Merge "mmc: core: Suspend cqe during sdhci_msm_reset"
diff --git a/Android.bp b/Android.bp
index 7567737..1fd921a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,12 +6,12 @@
},
}
-gensrcs {
+genrule {
name: "gen-headers_install.sh",
srcs: ["scripts/headers_install.sh"],
tools: ["unifdef"],
+ out: ["headers_install.sh"],
cmd: "sed 's+scripts/unifdef+$(location unifdef)+g' $(in) > $(out)",
- output_extension: "sh",
}
cc_prebuilt_binary {
@@ -21,38 +21,25 @@
srcs: [":gen-headers_install.sh"],
}
-gensrcs {
- name: "qcom-kernel-includes",
- cmd: "$(location headers_install.sh) `dirname $(out)` `dirname $(in)` `basename $(in)`",
- tools: ["headers_install.sh"],
- export_include_dirs: ["include/uapi"],
- srcs: [
- "include/uapi/**/*.h",
- ],
- output_extension: "h",
-}
+// Use the following for verbose output from kernel_headers.py.
+// kernel_headers_verbose = "--verbose "
+// Use the following for minimal output from kernel_headers.py.
+kernel_headers_verbose = ""
-gensrcs {
- name: "qseecom-kernel-includes",
- cmd: "$(location headers_install.sh) `dirname $(out)` `dirname $(in)` `basename $(in)`",
- tools: ["headers_install.sh"],
- export_include_dirs: ["include/uapi"],
- srcs: [
- "include/uapi/linux/qseecom.h",
- ],
- output_extension: "h",
-}
+build = ["gen_headers_arm.bp", "gen_headers_arm64.bp"]
cc_library_headers {
- name: "qseecom-kernel-headers",
- generated_headers: ["qseecom-kernel-includes"],
- export_generated_headers: ["qseecom-kernel-includes"],
-}
-
-cc_library_headers {
- name: "qcom_kernel_headers",
- generated_headers: ["qcom-kernel-includes"],
- export_generated_headers: ["qcom-kernel-includes"],
+ name: "qti_kernel_headers",
+ arch: {
+ arm: {
+ generated_headers: ["qti_generate_kernel_headers_arm"],
+ export_generated_headers: ["qti_generate_kernel_headers_arm"],
+ },
+ arm64: {
+ generated_headers: ["qti_generate_kernel_headers_arm64"],
+ export_generated_headers: ["qti_generate_kernel_headers_arm64"],
+ },
+ },
vendor: true,
recovery_available: true,
}
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 8718d4a..b492fb6 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -478,6 +478,8 @@
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass
/sys/devices/system/cpu/vulnerabilities/l1tf
/sys/devices/system/cpu/vulnerabilities/mds
+ /sys/devices/system/cpu/vulnerabilities/tsx_async_abort
+ /sys/devices/system/cpu/vulnerabilities/itlb_multihit
Date: January 2018
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Description: Information about CPU vulnerabilities
diff --git a/Documentation/admin-guide/hw-vuln/index.rst b/Documentation/admin-guide/hw-vuln/index.rst
index 49311f3..0795e3c 100644
--- a/Documentation/admin-guide/hw-vuln/index.rst
+++ b/Documentation/admin-guide/hw-vuln/index.rst
@@ -12,3 +12,5 @@
spectre
l1tf
mds
+ tsx_async_abort
+ multihit.rst
diff --git a/Documentation/admin-guide/hw-vuln/mds.rst b/Documentation/admin-guide/hw-vuln/mds.rst
index e3a796c..2d19c9f 100644
--- a/Documentation/admin-guide/hw-vuln/mds.rst
+++ b/Documentation/admin-guide/hw-vuln/mds.rst
@@ -265,8 +265,11 @@
============ =============================================================
-Not specifying this option is equivalent to "mds=full".
-
+Not specifying this option is equivalent to "mds=full". For processors
+that are affected by both TAA (TSX Asynchronous Abort) and MDS,
+specifying just "mds=off" without an accompanying "tsx_async_abort=off"
+will have no effect as the same mitigation is used for both
+vulnerabilities.
Mitigation selection guide
--------------------------
diff --git a/Documentation/admin-guide/hw-vuln/multihit.rst b/Documentation/admin-guide/hw-vuln/multihit.rst
new file mode 100644
index 0000000..ba9988d
--- /dev/null
+++ b/Documentation/admin-guide/hw-vuln/multihit.rst
@@ -0,0 +1,163 @@
+iTLB multihit
+=============
+
+iTLB multihit is an erratum where some processors may incur a machine check
+error, possibly resulting in an unrecoverable CPU lockup, when an
+instruction fetch hits multiple entries in the instruction TLB. This can
+occur when the page size is changed along with either the physical address
+or cache type. A malicious guest running on a virtualized system can
+exploit this erratum to perform a denial of service attack.
+
+
+Affected processors
+-------------------
+
+Variations of this erratum are present on most Intel Core and Xeon processor
+models. The erratum is not present on:
+
+ - non-Intel processors
+
+ - Some Atoms (Airmont, Bonnell, Goldmont, GoldmontPlus, Saltwell, Silvermont)
+
+ - Intel processors that have the PSCHANGE_MC_NO bit set in the
+ IA32_ARCH_CAPABILITIES MSR.
+
+
+Related CVEs
+------------
+
+The following CVE entry is related to this issue:
+
+ ============== =================================================
+ CVE-2018-12207 Machine Check Error Avoidance on Page Size Change
+ ============== =================================================
+
+
+Problem
+-------
+
+Privileged software, including OS and virtual machine managers (VMM), are in
+charge of memory management. A key component in memory management is the control
+of the page tables. Modern processors use virtual memory, a technique that creates
+the illusion of a very large memory for processors. This virtual space is split
+into pages of a given size. Page tables translate virtual addresses to physical
+addresses.
+
+To reduce latency when performing a virtual to physical address translation,
+processors include a structure, called TLB, that caches recent translations.
+There are separate TLBs for instruction (iTLB) and data (dTLB).
+
+Under this errata, instructions are fetched from a linear address translated
+using a 4 KB translation cached in the iTLB. Privileged software modifies the
+paging structure so that the same linear address using large page size (2 MB, 4
+MB, 1 GB) with a different physical address or memory type. After the page
+structure modification but before the software invalidates any iTLB entries for
+the linear address, a code fetch that happens on the same linear address may
+cause a machine-check error which can result in a system hang or shutdown.
+
+
+Attack scenarios
+----------------
+
+Attacks against the iTLB multihit erratum can be mounted from malicious
+guests in a virtualized system.
+
+
+iTLB multihit system information
+--------------------------------
+
+The Linux kernel provides a sysfs interface to enumerate the current iTLB
+multihit status of the system:whether the system is vulnerable and which
+mitigations are active. The relevant sysfs file is:
+
+/sys/devices/system/cpu/vulnerabilities/itlb_multihit
+
+The possible values in this file are:
+
+.. list-table::
+
+ * - Not affected
+ - The processor is not vulnerable.
+ * - KVM: Mitigation: Split huge pages
+ - Software changes mitigate this issue.
+ * - KVM: Vulnerable
+ - The processor is vulnerable, but no mitigation enabled
+
+
+Enumeration of the erratum
+--------------------------------
+
+A new bit has been allocated in the IA32_ARCH_CAPABILITIES (PSCHANGE_MC_NO) msr
+and will be set on CPU's which are mitigated against this issue.
+
+ ======================================= =========== ===============================
+ IA32_ARCH_CAPABILITIES MSR Not present Possibly vulnerable,check model
+ IA32_ARCH_CAPABILITIES[PSCHANGE_MC_NO] '0' Likely vulnerable,check model
+ IA32_ARCH_CAPABILITIES[PSCHANGE_MC_NO] '1' Not vulnerable
+ ======================================= =========== ===============================
+
+
+Mitigation mechanism
+-------------------------
+
+This erratum can be mitigated by restricting the use of large page sizes to
+non-executable pages. This forces all iTLB entries to be 4K, and removes
+the possibility of multiple hits.
+
+In order to mitigate the vulnerability, KVM initially marks all huge pages
+as non-executable. If the guest attempts to execute in one of those pages,
+the page is broken down into 4K pages, which are then marked executable.
+
+If EPT is disabled or not available on the host, KVM is in control of TLB
+flushes and the problematic situation cannot happen. However, the shadow
+EPT paging mechanism used by nested virtualization is vulnerable, because
+the nested guest can trigger multiple iTLB hits by modifying its own
+(non-nested) page tables. For simplicity, KVM will make large pages
+non-executable in all shadow paging modes.
+
+Mitigation control on the kernel command line and KVM - module parameter
+------------------------------------------------------------------------
+
+The KVM hypervisor mitigation mechanism for marking huge pages as
+non-executable can be controlled with a module parameter "nx_huge_pages=".
+The kernel command line allows to control the iTLB multihit mitigations at
+boot time with the option "kvm.nx_huge_pages=".
+
+The valid arguments for these options are:
+
+ ========== ================================================================
+ force Mitigation is enabled. In this case, the mitigation implements
+ non-executable huge pages in Linux kernel KVM module. All huge
+ pages in the EPT are marked as non-executable.
+ If a guest attempts to execute in one of those pages, the page is
+ broken down into 4K pages, which are then marked executable.
+
+ off Mitigation is disabled.
+
+ auto Enable mitigation only if the platform is affected and the kernel
+ was not booted with the "mitigations=off" command line parameter.
+ This is the default option.
+ ========== ================================================================
+
+
+Mitigation selection guide
+--------------------------
+
+1. No virtualization in use
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ The system is protected by the kernel unconditionally and no further
+ action is required.
+
+2. Virtualization with trusted guests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ If the guest comes from a trusted source, you may assume that the guest will
+ not attempt to maliciously exploit these errata and no further action is
+ required.
+
+3. Virtualization with untrusted guests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ If the guest comes from an untrusted source, the guest host kernel will need
+ to apply iTLB multihit mitigation via the kernel command line or kvm
+ module parameter.
diff --git a/Documentation/admin-guide/hw-vuln/tsx_async_abort.rst b/Documentation/admin-guide/hw-vuln/tsx_async_abort.rst
new file mode 100644
index 0000000..af6865b
--- /dev/null
+++ b/Documentation/admin-guide/hw-vuln/tsx_async_abort.rst
@@ -0,0 +1,279 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+TAA - TSX Asynchronous Abort
+======================================
+
+TAA is a hardware vulnerability that allows unprivileged speculative access to
+data which is available in various CPU internal buffers by using asynchronous
+aborts within an Intel TSX transactional region.
+
+Affected processors
+-------------------
+
+This vulnerability only affects Intel processors that support Intel
+Transactional Synchronization Extensions (TSX) when the TAA_NO bit (bit 8)
+is 0 in the IA32_ARCH_CAPABILITIES MSR. On processors where the MDS_NO bit
+(bit 5) is 0 in the IA32_ARCH_CAPABILITIES MSR, the existing MDS mitigations
+also mitigate against TAA.
+
+Whether a processor is affected or not can be read out from the TAA
+vulnerability file in sysfs. See :ref:`tsx_async_abort_sys_info`.
+
+Related CVEs
+------------
+
+The following CVE entry is related to this TAA issue:
+
+ ============== ===== ===================================================
+ CVE-2019-11135 TAA TSX Asynchronous Abort (TAA) condition on some
+ microprocessors utilizing speculative execution may
+ allow an authenticated user to potentially enable
+ information disclosure via a side channel with
+ local access.
+ ============== ===== ===================================================
+
+Problem
+-------
+
+When performing store, load or L1 refill operations, processors write
+data into temporary microarchitectural structures (buffers). The data in
+those buffers can be forwarded to load operations as an optimization.
+
+Intel TSX is an extension to the x86 instruction set architecture that adds
+hardware transactional memory support to improve performance of multi-threaded
+software. TSX lets the processor expose and exploit concurrency hidden in an
+application due to dynamically avoiding unnecessary synchronization.
+
+TSX supports atomic memory transactions that are either committed (success) or
+aborted. During an abort, operations that happened within the transactional region
+are rolled back. An asynchronous abort takes place, among other options, when a
+different thread accesses a cache line that is also used within the transactional
+region when that access might lead to a data race.
+
+Immediately after an uncompleted asynchronous abort, certain speculatively
+executed loads may read data from those internal buffers and pass it to dependent
+operations. This can be then used to infer the value via a cache side channel
+attack.
+
+Because the buffers are potentially shared between Hyper-Threads cross
+Hyper-Thread attacks are possible.
+
+The victim of a malicious actor does not need to make use of TSX. Only the
+attacker needs to begin a TSX transaction and raise an asynchronous abort
+which in turn potenitally leaks data stored in the buffers.
+
+More detailed technical information is available in the TAA specific x86
+architecture section: :ref:`Documentation/x86/tsx_async_abort.rst <tsx_async_abort>`.
+
+
+Attack scenarios
+----------------
+
+Attacks against the TAA vulnerability can be implemented from unprivileged
+applications running on hosts or guests.
+
+As for MDS, the attacker has no control over the memory addresses that can
+be leaked. Only the victim is responsible for bringing data to the CPU. As
+a result, the malicious actor has to sample as much data as possible and
+then postprocess it to try to infer any useful information from it.
+
+A potential attacker only has read access to the data. Also, there is no direct
+privilege escalation by using this technique.
+
+
+.. _tsx_async_abort_sys_info:
+
+TAA system information
+-----------------------
+
+The Linux kernel provides a sysfs interface to enumerate the current TAA status
+of mitigated systems. The relevant sysfs file is:
+
+/sys/devices/system/cpu/vulnerabilities/tsx_async_abort
+
+The possible values in this file are:
+
+.. list-table::
+
+ * - 'Vulnerable'
+ - The CPU is affected by this vulnerability and the microcode and kernel mitigation are not applied.
+ * - 'Vulnerable: Clear CPU buffers attempted, no microcode'
+ - The system tries to clear the buffers but the microcode might not support the operation.
+ * - 'Mitigation: Clear CPU buffers'
+ - The microcode has been updated to clear the buffers. TSX is still enabled.
+ * - 'Mitigation: TSX disabled'
+ - TSX is disabled.
+ * - 'Not affected'
+ - The CPU is not affected by this issue.
+
+.. _ucode_needed:
+
+Best effort mitigation mode
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If the processor is vulnerable, but the availability of the microcode-based
+mitigation mechanism is not advertised via CPUID the kernel selects a best
+effort mitigation mode. This mode invokes the mitigation instructions
+without a guarantee that they clear the CPU buffers.
+
+This is done to address virtualization scenarios where the host has the
+microcode update applied, but the hypervisor is not yet updated to expose the
+CPUID to the guest. If the host has updated microcode the protection takes
+effect; otherwise a few CPU cycles are wasted pointlessly.
+
+The state in the tsx_async_abort sysfs file reflects this situation
+accordingly.
+
+
+Mitigation mechanism
+--------------------
+
+The kernel detects the affected CPUs and the presence of the microcode which is
+required. If a CPU is affected and the microcode is available, then the kernel
+enables the mitigation by default.
+
+
+The mitigation can be controlled at boot time via a kernel command line option.
+See :ref:`taa_mitigation_control_command_line`.
+
+.. _virt_mechanism:
+
+Virtualization mitigation
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Affected systems where the host has TAA microcode and TAA is mitigated by
+having disabled TSX previously, are not vulnerable regardless of the status
+of the VMs.
+
+In all other cases, if the host either does not have the TAA microcode or
+the kernel is not mitigated, the system might be vulnerable.
+
+
+.. _taa_mitigation_control_command_line:
+
+Mitigation control on the kernel command line
+---------------------------------------------
+
+The kernel command line allows to control the TAA mitigations at boot time with
+the option "tsx_async_abort=". The valid arguments for this option are:
+
+ ============ =============================================================
+ off This option disables the TAA mitigation on affected platforms.
+ If the system has TSX enabled (see next parameter) and the CPU
+ is affected, the system is vulnerable.
+
+ full TAA mitigation is enabled. If TSX is enabled, on an affected
+ system it will clear CPU buffers on ring transitions. On
+ systems which are MDS-affected and deploy MDS mitigation,
+ TAA is also mitigated. Specifying this option on those
+ systems will have no effect.
+
+ full,nosmt The same as tsx_async_abort=full, with SMT disabled on
+ vulnerable CPUs that have TSX enabled. This is the complete
+ mitigation. When TSX is disabled, SMT is not disabled because
+ CPU is not vulnerable to cross-thread TAA attacks.
+ ============ =============================================================
+
+Not specifying this option is equivalent to "tsx_async_abort=full". For
+processors that are affected by both TAA and MDS, specifying just
+"tsx_async_abort=off" without an accompanying "mds=off" will have no
+effect as the same mitigation is used for both vulnerabilities.
+
+The kernel command line also allows to control the TSX feature using the
+parameter "tsx=" on CPUs which support TSX control. MSR_IA32_TSX_CTRL is used
+to control the TSX feature and the enumeration of the TSX feature bits (RTM
+and HLE) in CPUID.
+
+The valid options are:
+
+ ============ =============================================================
+ off Disables TSX on the system.
+
+ Note that this option takes effect only on newer CPUs which are
+ not vulnerable to MDS, i.e., have MSR_IA32_ARCH_CAPABILITIES.MDS_NO=1
+ and which get the new IA32_TSX_CTRL MSR through a microcode
+ update. This new MSR allows for the reliable deactivation of
+ the TSX functionality.
+
+ on Enables TSX.
+
+ Although there are mitigations for all known security
+ vulnerabilities, TSX has been known to be an accelerator for
+ several previous speculation-related CVEs, and so there may be
+ unknown security risks associated with leaving it enabled.
+
+ auto Disables TSX if X86_BUG_TAA is present, otherwise enables TSX
+ on the system.
+ ============ =============================================================
+
+Not specifying this option is equivalent to "tsx=off".
+
+The following combinations of the "tsx_async_abort" and "tsx" are possible. For
+affected platforms tsx=auto is equivalent to tsx=off and the result will be:
+
+ ========= ========================== =========================================
+ tsx=on tsx_async_abort=full The system will use VERW to clear CPU
+ buffers. Cross-thread attacks are still
+ possible on SMT machines.
+ tsx=on tsx_async_abort=full,nosmt As above, cross-thread attacks on SMT
+ mitigated.
+ tsx=on tsx_async_abort=off The system is vulnerable.
+ tsx=off tsx_async_abort=full TSX might be disabled if microcode
+ provides a TSX control MSR. If so,
+ system is not vulnerable.
+ tsx=off tsx_async_abort=full,nosmt Ditto
+ tsx=off tsx_async_abort=off ditto
+ ========= ========================== =========================================
+
+
+For unaffected platforms "tsx=on" and "tsx_async_abort=full" does not clear CPU
+buffers. For platforms without TSX control (MSR_IA32_ARCH_CAPABILITIES.MDS_NO=0)
+"tsx" command line argument has no effect.
+
+For the affected platforms below table indicates the mitigation status for the
+combinations of CPUID bit MD_CLEAR and IA32_ARCH_CAPABILITIES MSR bits MDS_NO
+and TSX_CTRL_MSR.
+
+ ======= ========= ============= ========================================
+ MDS_NO MD_CLEAR TSX_CTRL_MSR Status
+ ======= ========= ============= ========================================
+ 0 0 0 Vulnerable (needs microcode)
+ 0 1 0 MDS and TAA mitigated via VERW
+ 1 1 0 MDS fixed, TAA vulnerable if TSX enabled
+ because MD_CLEAR has no meaning and
+ VERW is not guaranteed to clear buffers
+ 1 X 1 MDS fixed, TAA can be mitigated by
+ VERW or TSX_CTRL_MSR
+ ======= ========= ============= ========================================
+
+Mitigation selection guide
+--------------------------
+
+1. Trusted userspace and guests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If all user space applications are from a trusted source and do not execute
+untrusted code which is supplied externally, then the mitigation can be
+disabled. The same applies to virtualized environments with trusted guests.
+
+
+2. Untrusted userspace and guests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If there are untrusted applications or guests on the system, enabling TSX
+might allow a malicious actor to leak data from the host or from other
+processes running on the same physical core.
+
+If the microcode is available and the TSX is disabled on the host, attacks
+are prevented in a virtualized environment as well, even if the VMs do not
+explicitly enable the mitigation.
+
+
+.. _taa_default_mitigations:
+
+Default mitigations
+-------------------
+
+The kernel's default action for vulnerable processors is:
+
+ - Deploy TSX disable mitigation (tsx_async_abort=full tsx=off).
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 4834753..bef7d96 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -113,7 +113,7 @@
the GPE dispatcher.
This facility can be used to prevent such uncontrolled
GPE floodings.
- Format: <int>
+ Format: <byte>
acpi_no_auto_serialize [HW,ACPI]
Disable auto-serialization of AML methods
@@ -1963,6 +1963,25 @@
KVM MMU at runtime.
Default is 0 (off)
+ kvm.nx_huge_pages=
+ [KVM] Controls the software workaround for the
+ X86_BUG_ITLB_MULTIHIT bug.
+ force : Always deploy workaround.
+ off : Never deploy workaround.
+ auto : Deploy workaround based on the presence of
+ X86_BUG_ITLB_MULTIHIT.
+
+ Default is 'auto'.
+
+ If the software workaround is enabled for the host,
+ guests do need not to enable it for nested guests.
+
+ kvm.nx_huge_pages_recovery_ratio=
+ [KVM] Controls how many 4KiB pages are periodically zapped
+ back to huge pages. 0 disables the recovery, otherwise if
+ the value is N KVM will zap 1/Nth of the 4KiB pages every
+ minute. The default is 60.
+
kvm-amd.nested= [KVM,AMD] Allow nested virtualization in KVM/SVM.
Default is 1 (enabled)
@@ -2347,6 +2366,12 @@
SMT on vulnerable CPUs
off - Unconditionally disable MDS mitigation
+ On TAA-affected machines, mds=off can be prevented by
+ an active TAA mitigation as both vulnerabilities are
+ mitigated with the same mechanism so in order to disable
+ this mitigation, you need to specify tsx_async_abort=off
+ too.
+
Not specifying this option is equivalent to
mds=full.
@@ -2530,6 +2555,13 @@
ssbd=force-off [ARM64]
l1tf=off [X86]
mds=off [X86]
+ tsx_async_abort=off [X86]
+ kvm.nx_huge_pages=off [X86]
+
+ Exceptions:
+ This does not have any effect on
+ kvm.nx_huge_pages when
+ kvm.nx_huge_pages=force.
auto (default)
Mitigate all CPU vulnerabilities, but leave SMT
@@ -2545,6 +2577,7 @@
be fully mitigated, even if it means losing SMT.
Equivalent to: l1tf=flush,nosmt [X86]
mds=full,nosmt [X86]
+ tsx_async_abort=full,nosmt [X86]
mminit_loglevel=
[KNL] When CONFIG_DEBUG_MEMORY_INIT is set, this
@@ -4703,6 +4736,76 @@
marks the TSC unconditionally unstable at bootup and
avoids any further wobbles once the TSC watchdog notices.
+ tsx= [X86] Control Transactional Synchronization
+ Extensions (TSX) feature in Intel processors that
+ support TSX control.
+
+ This parameter controls the TSX feature. The options are:
+
+ on - Enable TSX on the system. Although there are
+ mitigations for all known security vulnerabilities,
+ TSX has been known to be an accelerator for
+ several previous speculation-related CVEs, and
+ so there may be unknown security risks associated
+ with leaving it enabled.
+
+ off - Disable TSX on the system. (Note that this
+ option takes effect only on newer CPUs which are
+ not vulnerable to MDS, i.e., have
+ MSR_IA32_ARCH_CAPABILITIES.MDS_NO=1 and which get
+ the new IA32_TSX_CTRL MSR through a microcode
+ update. This new MSR allows for the reliable
+ deactivation of the TSX functionality.)
+
+ auto - Disable TSX if X86_BUG_TAA is present,
+ otherwise enable TSX on the system.
+
+ Not specifying this option is equivalent to tsx=off.
+
+ See Documentation/admin-guide/hw-vuln/tsx_async_abort.rst
+ for more details.
+
+ tsx_async_abort= [X86,INTEL] Control mitigation for the TSX Async
+ Abort (TAA) vulnerability.
+
+ Similar to Micro-architectural Data Sampling (MDS)
+ certain CPUs that support Transactional
+ Synchronization Extensions (TSX) are vulnerable to an
+ exploit against CPU internal buffers which can forward
+ information to a disclosure gadget under certain
+ conditions.
+
+ In vulnerable processors, the speculatively forwarded
+ data can be used in a cache side channel attack, to
+ access data to which the attacker does not have direct
+ access.
+
+ This parameter controls the TAA mitigation. The
+ options are:
+
+ full - Enable TAA mitigation on vulnerable CPUs
+ if TSX is enabled.
+
+ full,nosmt - Enable TAA mitigation and disable SMT on
+ vulnerable CPUs. If TSX is disabled, SMT
+ is not disabled because CPU is not
+ vulnerable to cross-thread TAA attacks.
+ off - Unconditionally disable TAA mitigation
+
+ On MDS-affected machines, tsx_async_abort=off can be
+ prevented by an active MDS mitigation as both vulnerabilities
+ are mitigated with the same mechanism so in order to disable
+ this mitigation, you need to specify mds=off too.
+
+ Not specifying this option is equivalent to
+ tsx_async_abort=full. On CPUs which are MDS affected
+ and deploy MDS mitigation, TAA mitigation is not
+ required and doesn't provide any additional
+ mitigation.
+
+ For details see:
+ Documentation/admin-guide/hw-vuln/tsx_async_abort.rst
+
turbografx.map[2|3]= [HW,JOY]
TurboGraFX parallel port interface
Format:
@@ -4851,13 +4954,13 @@
Flags is a set of characters, each corresponding
to a common usb-storage quirk flag as follows:
a = SANE_SENSE (collect more than 18 bytes
- of sense data);
+ of sense data, not on uas);
b = BAD_SENSE (don't collect more than 18
- bytes of sense data);
+ bytes of sense data, not on uas);
c = FIX_CAPACITY (decrease the reported
device capacity by one sector);
d = NO_READ_DISC_INFO (don't use
- READ_DISC_INFO command);
+ READ_DISC_INFO command, not on uas);
e = NO_READ_CAPACITY_16 (don't use
READ_CAPACITY_16 command);
f = NO_REPORT_OPCODES (don't use report opcodes
@@ -4872,17 +4975,18 @@
j = NO_REPORT_LUNS (don't use report luns
command, uas only);
l = NOT_LOCKABLE (don't try to lock and
- unlock ejectable media);
+ unlock ejectable media, not on uas);
m = MAX_SECTORS_64 (don't transfer more
- than 64 sectors = 32 KB at a time);
+ than 64 sectors = 32 KB at a time,
+ not on uas);
n = INITIAL_READ10 (force a retry of the
- initial READ(10) command);
+ initial READ(10) command, not on uas);
o = CAPACITY_OK (accept the capacity
- reported by the device);
+ reported by the device, not on uas);
p = WRITE_CACHE (the device cache is ON
- by default);
+ by default, not on uas);
r = IGNORE_RESIDUE (the device reports
- bogus residue values);
+ bogus residue values, not on uas);
s = SINGLE_LUN (the device has only one
Logical Unit);
t = NO_ATA_1X (don't allow ATA(12) and ATA(16)
@@ -4891,7 +4995,8 @@
w = NO_WP_DETECT (don't test whether the
medium is write-protected).
y = ALWAYS_SYNC (issue a SYNCHRONIZE_CACHE
- even if the device claims no cache)
+ even if the device claims no cache,
+ not on uas)
Example: quirks=0419:aaf5:rl,0421:0433:rc
user_debug= [KNL,ARM]
@@ -5130,6 +5235,10 @@
the unplug protocol
never -- do not unplug even if version check succeeds
+ xen_legacy_crash [X86,XEN]
+ Crash from Xen panic notifier, without executing late
+ panic() code such as dumping handler.
+
xen_nopvspin [X86,XEN]
Disables the ticketlock slowpath using Xen PV
optimizations.
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index eeb3fc9..16be5e2 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -59,6 +59,7 @@
| ARM | Cortex-A73 | #858921 | ARM64_ERRATUM_858921 |
| ARM | Cortex-A55 | #1024718 | ARM64_ERRATUM_1024718 |
| ARM | Cortex-A76 | #1463225 | ARM64_ERRATUM_1463225 |
+| ARM | Cortex-A77 | #1542418 | ARM64_ERRATUM_1542418 |
| ARM | MMU-500 | #841119,#826419 | N/A |
| | | | |
| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 |
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index 52c7e85..c371b45 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -428,6 +428,7 @@
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
+THPeligible: 0
VmFlags: rd ex mr mw me dw
Name: name from userspace
@@ -466,6 +467,8 @@
"SwapPss" shows proportional swap share of this mapping. Unlike "Swap", this
does not take into account swapped out page of underlying shmem objects.
"Locked" indicates whether the mapping is locked in memory or not.
+"THPeligible" indicates whether the mapping is eligible for THP pages - 1 if
+true, 0 otherwise.
"VmFlags" field deserves a separate description. This member represents the kernel
flags associated with the particular virtual memory area in two letter encoded
diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt
index c8656dd..958fff9 100644
--- a/Documentation/hid/uhid.txt
+++ b/Documentation/hid/uhid.txt
@@ -160,7 +160,7 @@
UHID_OUTPUT:
This is sent if the HID device driver wants to send raw data to the I/O
device on the interrupt channel. You should read the payload and forward it to
- the device. The payload is of type "struct uhid_data_req".
+ the device. The payload is of type "struct uhid_output_req".
This may be received even though you haven't received UHID_OPEN, yet.
UHID_GET_REPORT:
diff --git a/Documentation/scheduler/sched-bwc.txt b/Documentation/scheduler/sched-bwc.txt
index f6b1873..de583fb 100644
--- a/Documentation/scheduler/sched-bwc.txt
+++ b/Documentation/scheduler/sched-bwc.txt
@@ -90,6 +90,51 @@
In case b) above, even though the child may have runtime remaining it will not
be allowed to until the parent's runtime is refreshed.
+CFS Bandwidth Quota Caveats
+---------------------------
+Once a slice is assigned to a cpu it does not expire. However all but 1ms of
+the slice may be returned to the global pool if all threads on that cpu become
+unrunnable. This is configured at compile time by the min_cfs_rq_runtime
+variable. This is a performance tweak that helps prevent added contention on
+the global lock.
+
+The fact that cpu-local slices do not expire results in some interesting corner
+cases that should be understood.
+
+For cgroup cpu constrained applications that are cpu limited this is a
+relatively moot point because they will naturally consume the entirety of their
+quota as well as the entirety of each cpu-local slice in each period. As a
+result it is expected that nr_periods roughly equal nr_throttled, and that
+cpuacct.usage will increase roughly equal to cfs_quota_us in each period.
+
+For highly-threaded, non-cpu bound applications this non-expiration nuance
+allows applications to briefly burst past their quota limits by the amount of
+unused slice on each cpu that the task group is running on (typically at most
+1ms per cpu or as defined by min_cfs_rq_runtime). This slight burst only
+applies if quota had been assigned to a cpu and then not fully used or returned
+in previous periods. This burst amount will not be transferred between cores.
+As a result, this mechanism still strictly limits the task group to quota
+average usage, albeit over a longer time window than a single period. This
+also limits the burst ability to no more than 1ms per cpu. This provides
+better more predictable user experience for highly threaded applications with
+small quota limits on high core count machines. It also eliminates the
+propensity to throttle these applications while simultanously using less than
+quota amounts of cpu. Another way to say this, is that by allowing the unused
+portion of a slice to remain valid across periods we have decreased the
+possibility of wastefully expiring quota on cpu-local silos that don't need a
+full slice's amount of cpu time.
+
+The interaction between cpu-bound and non-cpu-bound-interactive applications
+should also be considered, especially when single core usage hits 100%. If you
+gave each of these applications half of a cpu-core and they both got scheduled
+on the same CPU it is theoretically possible that the non-cpu bound application
+will use up to 1ms additional quota in some periods, thereby preventing the
+cpu-bound application from fully using its quota by that same amount. In these
+instances it will be up to the CFS algorithm (see sched-design-CFS.rst) to
+decide which application is chosen to run, as they will both be runnable and
+have remaining quota. This runtime discrepancy will be made up in the following
+periods when the interactive application idles.
+
Examples
--------
1. Limit a group to 1 CPU worth of runtime.
diff --git a/Documentation/virtual/kvm/locking.txt b/Documentation/virtual/kvm/locking.txt
index 1bb8bca..635cd6e 100644
--- a/Documentation/virtual/kvm/locking.txt
+++ b/Documentation/virtual/kvm/locking.txt
@@ -15,8 +15,6 @@
On x86, vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock.
-For spinlocks, kvm_lock is taken outside kvm->mmu_lock.
-
Everything else is a leaf: no other lock is taken inside the critical
sections.
@@ -169,7 +167,7 @@
------------
Name: kvm_lock
-Type: spinlock_t
+Type: mutex
Arch: any
Protects: - vm_list
diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst
index ef389dc..0780d55 100644
--- a/Documentation/x86/index.rst
+++ b/Documentation/x86/index.rst
@@ -6,3 +6,4 @@
:maxdepth: 1
mds
+ tsx_async_abort
diff --git a/Documentation/x86/tsx_async_abort.rst b/Documentation/x86/tsx_async_abort.rst
new file mode 100644
index 0000000..583ddc1
--- /dev/null
+++ b/Documentation/x86/tsx_async_abort.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+TSX Async Abort (TAA) mitigation
+================================
+
+.. _tsx_async_abort:
+
+Overview
+--------
+
+TSX Async Abort (TAA) is a side channel attack on internal buffers in some
+Intel processors similar to Microachitectural Data Sampling (MDS). In this
+case certain loads may speculatively pass invalid data to dependent operations
+when an asynchronous abort condition is pending in a Transactional
+Synchronization Extensions (TSX) transaction. This includes loads with no
+fault or assist condition. Such loads may speculatively expose stale data from
+the same uarch data structures as in MDS, with same scope of exposure i.e.
+same-thread and cross-thread. This issue affects all current processors that
+support TSX.
+
+Mitigation strategy
+-------------------
+
+a) TSX disable - one of the mitigations is to disable TSX. A new MSR
+IA32_TSX_CTRL will be available in future and current processors after
+microcode update which can be used to disable TSX. In addition, it
+controls the enumeration of the TSX feature bits (RTM and HLE) in CPUID.
+
+b) Clear CPU buffers - similar to MDS, clearing the CPU buffers mitigates this
+vulnerability. More details on this approach can be found in
+:ref:`Documentation/admin-guide/hw-vuln/mds.rst <mds>`.
+
+Kernel internal mitigation modes
+--------------------------------
+
+ ============= ============================================================
+ off Mitigation is disabled. Either the CPU is not affected or
+ tsx_async_abort=off is supplied on the kernel command line.
+
+ tsx disabled Mitigation is enabled. TSX feature is disabled by default at
+ bootup on processors that support TSX control.
+
+ verw Mitigation is enabled. CPU is affected and MD_CLEAR is
+ advertised in CPUID.
+
+ ucode needed Mitigation is enabled. CPU is affected and MD_CLEAR is not
+ advertised in CPUID. That is mainly for virtualization
+ scenarios where the host has the updated microcode but the
+ hypervisor does not expose MD_CLEAR in CPUID. It's a best
+ effort approach without guarantee.
+ ============= ============================================================
+
+If the CPU is affected and the "tsx_async_abort" kernel command line parameter is
+not provided then the kernel selects an appropriate mitigation depending on the
+status of RTM and MD_CLEAR CPUID bits.
+
+Below tables indicate the impact of tsx=on|off|auto cmdline options on state of
+TAA mitigation, VERW behavior and TSX feature for various combinations of
+MSR_IA32_ARCH_CAPABILITIES bits.
+
+1. "tsx=off"
+
+========= ========= ============ ============ ============== =================== ======================
+MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=off
+---------------------------------- -------------------------------------------------------------------------
+TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
+ after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
+========= ========= ============ ============ ============== =================== ======================
+ 0 0 0 HW default Yes Same as MDS Same as MDS
+ 0 0 1 Invalid case Invalid case Invalid case Invalid case
+ 0 1 0 HW default No Need ucode update Need ucode update
+ 0 1 1 Disabled Yes TSX disabled TSX disabled
+ 1 X 1 Disabled X None needed None needed
+========= ========= ============ ============ ============== =================== ======================
+
+2. "tsx=on"
+
+========= ========= ============ ============ ============== =================== ======================
+MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=on
+---------------------------------- -------------------------------------------------------------------------
+TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
+ after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
+========= ========= ============ ============ ============== =================== ======================
+ 0 0 0 HW default Yes Same as MDS Same as MDS
+ 0 0 1 Invalid case Invalid case Invalid case Invalid case
+ 0 1 0 HW default No Need ucode update Need ucode update
+ 0 1 1 Enabled Yes None Same as MDS
+ 1 X 1 Enabled X None needed None needed
+========= ========= ============ ============ ============== =================== ======================
+
+3. "tsx=auto"
+
+========= ========= ============ ============ ============== =================== ======================
+MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=auto
+---------------------------------- -------------------------------------------------------------------------
+TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
+ after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
+========= ========= ============ ============ ============== =================== ======================
+ 0 0 0 HW default Yes Same as MDS Same as MDS
+ 0 0 1 Invalid case Invalid case Invalid case Invalid case
+ 0 1 0 HW default No Need ucode update Need ucode update
+ 0 1 1 Disabled Yes TSX disabled TSX disabled
+ 1 X 1 Enabled X None needed None needed
+========= ========= ============ ============ ============== =================== ======================
+
+In the tables, TSX_CTRL_MSR is a new bit in MSR_IA32_ARCH_CAPABILITIES that
+indicates whether MSR_IA32_TSX_CTRL is supported.
+
+There are two control bits in IA32_TSX_CTRL MSR:
+
+ Bit 0: When set it disables the Restricted Transactional Memory (RTM)
+ sub-feature of TSX (will force all transactions to abort on the
+ XBEGIN instruction).
+
+ Bit 1: When set it disables the enumeration of the RTM and HLE feature
+ (i.e. it will make CPUID(EAX=7).EBX{bit4} and
+ CPUID(EAX=7).EBX{bit11} read as 0).
diff --git a/Makefile b/Makefile
index 37589dd..3bb6239 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 4
PATCHLEVEL = 19
-SUBLEVEL = 81
+SUBLEVEL = 95
EXTRAVERSION =
NAME = "People's Front"
@@ -926,6 +926,12 @@
# change __FILE__ to the relative path from the srctree
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
+# ensure -fcf-protection is disabled when using retpoline as it is
+# incompatible with -mindirect-branch=thunk-extern
+ifdef CONFIG_RETPOLINE
+KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
+endif
+
# use the deterministic mode of AR if available
KBUILD_ARFLAGS := $(call ar-option,D)
@@ -1600,9 +1606,6 @@
# We are always building modules
KBUILD_MODULES := 1
-PHONY += crmodverdir
-crmodverdir:
- $(cmd_crmodverdir)
PHONY += $(objtree)/Module.symvers
$(objtree)/Module.symvers:
@@ -1614,7 +1617,7 @@
module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
PHONY += $(module-dirs) modules
-$(module-dirs): crmodverdir $(objtree)/Module.symvers
+$(module-dirs): prepare $(objtree)/Module.symvers
$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
modules: $(module-dirs)
@@ -1655,7 +1658,8 @@
# Dummies...
PHONY += prepare scripts
-prepare: ;
+prepare:
+ $(cmd_crmodverdir)
scripts: ;
endif # KBUILD_EXTMOD
@@ -1783,17 +1787,14 @@
# Modules
/: prepare scripts FORCE
- $(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir)
# Make sure the latest headers are built for Documentation
Documentation/ samples/: headers_install
%/: prepare scripts FORCE
- $(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir)
%.ko: prepare scripts FORCE
- $(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir) $(@:.ko=.o)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h
index db681cf..2ad77fb 100644
--- a/arch/arc/include/asm/cache.h
+++ b/arch/arc/include/asm/cache.h
@@ -124,7 +124,9 @@ extern unsigned long perip_base, perip_end;
/* IO coherency related Auxiliary registers */
#define ARC_REG_IO_COH_ENABLE 0x500
+#define ARC_IO_COH_ENABLE_BIT BIT(0)
#define ARC_REG_IO_COH_PARTIAL 0x501
+#define ARC_IO_COH_PARTIAL_BIT BIT(0)
#define ARC_REG_IO_COH_AP0_BASE 0x508
#define ARC_REG_IO_COH_AP0_SIZE 0x509
diff --git a/arch/arc/kernel/perf_event.c b/arch/arc/kernel/perf_event.c
index 8aec462..30f66b1 100644
--- a/arch/arc/kernel/perf_event.c
+++ b/arch/arc/kernel/perf_event.c
@@ -490,8 +490,8 @@ static int arc_pmu_device_probe(struct platform_device *pdev)
/* loop thru all available h/w condition indexes */
for (j = 0; j < cc_bcr.c; j++) {
write_aux_reg(ARC_REG_CC_INDEX, j);
- cc_name.indiv.word0 = read_aux_reg(ARC_REG_CC_NAME0);
- cc_name.indiv.word1 = read_aux_reg(ARC_REG_CC_NAME1);
+ cc_name.indiv.word0 = le32_to_cpu(read_aux_reg(ARC_REG_CC_NAME0));
+ cc_name.indiv.word1 = le32_to_cpu(read_aux_reg(ARC_REG_CC_NAME1));
/* See if it has been mapped to a perf event_id */
for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) {
diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c
index f2701c1..cf9619d 100644
--- a/arch/arc/mm/cache.c
+++ b/arch/arc/mm/cache.c
@@ -1145,6 +1145,20 @@ noinline void __init arc_ioc_setup(void)
unsigned int ioc_base, mem_sz;
/*
+ * If IOC was already enabled (due to bootloader) it technically needs to
+ * be reconfigured with aperture base,size corresponding to Linux memory map
+ * which will certainly be different than uboot's. But disabling and
+ * reenabling IOC when DMA might be potentially active is tricky business.
+ * To avoid random memory issues later, just panic here and ask user to
+ * upgrade bootloader to one which doesn't enable IOC
+ */
+ if (read_aux_reg(ARC_REG_IO_COH_ENABLE) & ARC_IO_COH_ENABLE_BIT)
+ panic("IOC already enabled, please upgrade bootloader!\n");
+
+ if (!ioc_enable)
+ return;
+
+ /*
* As for today we don't support both IOC and ZONE_HIGHMEM enabled
* simultaneously. This happens because as of today IOC aperture covers
* only ZONE_NORMAL (low mem) and any dma transactions outside this
@@ -1187,8 +1201,8 @@ noinline void __init arc_ioc_setup(void)
panic("IOC Aperture start must be aligned to the size of the aperture");
write_aux_reg(ARC_REG_IO_COH_AP0_BASE, ioc_base >> 12);
- write_aux_reg(ARC_REG_IO_COH_PARTIAL, 1);
- write_aux_reg(ARC_REG_IO_COH_ENABLE, 1);
+ write_aux_reg(ARC_REG_IO_COH_PARTIAL, ARC_IO_COH_PARTIAL_BIT);
+ write_aux_reg(ARC_REG_IO_COH_ENABLE, ARC_IO_COH_ENABLE_BIT);
/* Re-enable L1 dcache */
__dc_enable();
@@ -1265,7 +1279,7 @@ void __init arc_cache_init_master(void)
if (is_isa_arcv2() && l2_line_sz && !slc_enable)
arc_slc_disable();
- if (is_isa_arcv2() && ioc_enable)
+ if (is_isa_arcv2() && ioc_exists)
arc_ioc_setup();
if (is_isa_arcv2() && l2_line_sz && slc_enable) {
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 8c47ddb..0b5e8ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -25,6 +25,7 @@
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_WANT_IPC_PARSE_VERSION
+ select ARM_PSCI_FW if PM
select BUILDTIME_EXTABLE_SORT if MMU
select CLONE_BACKWARDS
select CPU_PM if (SUSPEND || CPU_IDLE)
@@ -540,6 +541,31 @@
help
Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+config ARCH_QCOM
+ bool "Qualcomm MSM (non-multiplatform)"
+ select ARCH_REQUIRE_GPIOLIB
+ select CPU_V7
+ select AUTO_ZRELADDR
+ select HAVE_SMP
+ select CLKDEV_LOOKUP
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_ALLOCATOR
+ select ARM_PATCH_PHYS_VIRT
+ select ARM_HAS_SG_CHAIN
+ select ARCH_HAS_OPP
+ select SOC_BUS
+ select MULTI_IRQ_HANDLER
+ select PM_OPP
+ select SPARSE_IRQ
+ select USE_OF
+ select PINCTRL
+ help
+ Support for Qualcomm MSM/QSD based systems. This runs on the
+ apps processor of the MSM/QSD and depends on a shared memory
+ interface to the modem processor which runs the baseband
+ stack and controls some vital subsystems
+ (clock and power control, etc).
+
config ARCH_RPC
bool "RiscPC"
depends on MMU
@@ -1469,7 +1495,7 @@
int
default 2048 if ARCH_SOCFPGA
default 1024 if ARCH_BRCMSTB || ARCH_RENESAS || ARCH_TEGRA || \
- ARCH_ZYNQ
+ ARCH_ZYNQ || ARCH_QCOM
default 512 if ARCH_EXYNOS || ARCH_KEYSTONE || SOC_OMAP5 || \
SOC_DRA7XX || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210
default 416 if ARCH_SUNXI
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 908022d..c341f10 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1094,14 +1094,21 @@
Say Y here if you want kernel low-level debugging support
on SOCFPGA(Cyclone 5 and Arria 5) based platforms.
- config DEBUG_SOCFPGA_UART1
+ config DEBUG_SOCFPGA_ARRIA10_UART1
depends on ARCH_SOCFPGA
- bool "Use SOCFPGA UART1 for low-level debug"
+ bool "Use SOCFPGA Arria10 UART1 for low-level debug"
select DEBUG_UART_8250
help
Say Y here if you want kernel low-level debugging support
on SOCFPGA(Arria 10) based platforms.
+ config DEBUG_SOCFPGA_CYCLONE5_UART1
+ depends on ARCH_SOCFPGA
+ bool "Use SOCFPGA Cyclone 5 UART1 for low-level debug"
+ select DEBUG_UART_8250
+ help
+ Say Y here if you want kernel low-level debugging support
+ on SOCFPGA(Cyclone 5 and Arria 5) based platforms.
config DEBUG_SUN9I_UART0
bool "Kernel low-level debugging messages via sun9i UART0"
@@ -1447,21 +1454,21 @@
depends on ARCH_OMAP2PLUS
config DEBUG_IMX_UART_PORT
- int "i.MX Debug UART Port Selection" if DEBUG_IMX1_UART || \
- DEBUG_IMX25_UART || \
- DEBUG_IMX21_IMX27_UART || \
- DEBUG_IMX31_UART || \
- DEBUG_IMX35_UART || \
- DEBUG_IMX50_UART || \
- DEBUG_IMX51_UART || \
- DEBUG_IMX53_UART || \
- DEBUG_IMX6Q_UART || \
- DEBUG_IMX6SL_UART || \
- DEBUG_IMX6SX_UART || \
- DEBUG_IMX6UL_UART || \
- DEBUG_IMX7D_UART
+ int "i.MX Debug UART Port Selection"
+ depends on DEBUG_IMX1_UART || \
+ DEBUG_IMX25_UART || \
+ DEBUG_IMX21_IMX27_UART || \
+ DEBUG_IMX31_UART || \
+ DEBUG_IMX35_UART || \
+ DEBUG_IMX50_UART || \
+ DEBUG_IMX51_UART || \
+ DEBUG_IMX53_UART || \
+ DEBUG_IMX6Q_UART || \
+ DEBUG_IMX6SL_UART || \
+ DEBUG_IMX6SX_UART || \
+ DEBUG_IMX6UL_UART || \
+ DEBUG_IMX7D_UART
default 1
- depends on ARCH_MXC
help
Choose UART port on which kernel low-level debug messages
should be output.
@@ -1662,7 +1669,8 @@
default 0xfe800000 if ARCH_IOP32X
default 0xff690000 if DEBUG_RK32_UART2
default 0xffc02000 if DEBUG_SOCFPGA_UART0
- default 0xffc02100 if DEBUG_SOCFPGA_UART1
+ default 0xffc02100 if DEBUG_SOCFPGA_ARRIA10_UART1
+ default 0xffc03000 if DEBUG_SOCFPGA_CYCLONE5_UART1
default 0xffd82340 if ARCH_IOP13XX
default 0xffe40000 if DEBUG_RCAR_GEN1_SCIF0
default 0xffe42000 if DEBUG_RCAR_GEN1_SCIF2
@@ -1769,7 +1777,8 @@
default 0xfeb30c00 if DEBUG_KEYSTONE_UART0
default 0xfeb31000 if DEBUG_KEYSTONE_UART1
default 0xfec02000 if DEBUG_SOCFPGA_UART0
- default 0xfec02100 if DEBUG_SOCFPGA_UART1
+ default 0xfec02100 if DEBUG_SOCFPGA_ARRIA10_UART1
+ default 0xfec03000 if DEBUG_SOCFPGA_CYCLONE5_UART1
default 0xfec12000 if (DEBUG_MVEBU_UART0 || DEBUG_MVEBU_UART0_ALTERNATE) && ARCH_MVEBU
default 0xfec12100 if DEBUG_MVEBU_UART1_ALTERNATE
default 0xfec10000 if DEBUG_SIRFATLAS7_UART0
@@ -1818,9 +1827,9 @@
depends on DEBUG_LL_UART_8250 || DEBUG_UART_8250
depends on DEBUG_UART_8250_SHIFT >= 2
default y if DEBUG_PICOXCELL_UART || \
- DEBUG_SOCFPGA_UART0 || DEBUG_SOCFPGA_UART1 || \
- DEBUG_KEYSTONE_UART0 || DEBUG_KEYSTONE_UART1 || \
- DEBUG_ALPINE_UART0 || \
+ DEBUG_SOCFPGA_UART0 || DEBUG_SOCFPGA_ARRIA10_UART1 || \
+ DEBUG_SOCFPGA_CYCLONE5_UART1 || DEBUG_KEYSTONE_UART0 || \
+ DEBUG_KEYSTONE_UART1 || DEBUG_ALPINE_UART0 || \
DEBUG_DAVINCI_DMx_UART0 || DEBUG_DAVINCI_DA8XX_UART1 || \
DEBUG_DAVINCI_DA8XX_UART2 || DEBUG_BCM_IPROC_UART3 || \
DEBUG_BCM_KONA_UART || DEBUG_RK32_UART2
@@ -1858,7 +1867,7 @@
config UNCOMPRESS_INCLUDE
string
default "debug/uncompress.h" if ARCH_MULTIPLATFORM || ARCH_MSM || \
- PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
+ ARCH_QCOM || PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
default "mach/uncompress.h"
config EARLY_PRINTK
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ded1724..6d02008 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -30,6 +30,10 @@
# Never generate .eh_frame
KBUILD_CFLAGS += $(call cc-option,-fno-dwarf2-cfi-asm)
+ifeq ($(cc-name),clang)
+KBUILD_CFLAGS += -Wno-vectorizer-no-neon
+endif
+
# This should work on most of the modern platforms
KBUILD_DEFCONFIG := multi_v7_defconfig
diff --git a/arch/arm/boot/compressed/libfdt_env.h b/arch/arm/boot/compressed/libfdt_env.h
index 0743781..6a0f1f5 100644
--- a/arch/arm/boot/compressed/libfdt_env.h
+++ b/arch/arm/boot/compressed/libfdt_env.h
@@ -2,10 +2,14 @@
#ifndef _ARM_LIBFDT_ENV_H
#define _ARM_LIBFDT_ENV_H
+#include <linux/limits.h>
#include <linux/types.h>
#include <linux/string.h>
#include <asm/byteorder.h>
+#define INT32_MAX S32_MAX
+#define UINT32_MAX U32_MAX
+
typedef __be16 fdt16_t;
typedef __be32 fdt32_t;
typedef __be64 fdt64_t;
diff --git a/arch/arm/boot/dts/am335x-boneblack-common.dtsi b/arch/arm/boot/dts/am335x-boneblack-common.dtsi
index 325daae4..21bc117 100644
--- a/arch/arm/boot/dts/am335x-boneblack-common.dtsi
+++ b/arch/arm/boot/dts/am335x-boneblack-common.dtsi
@@ -88,7 +88,7 @@
};
&i2c0 {
- tda19988: tda19988 {
+ tda19988: tda19988@70 {
compatible = "nxp,tda998x";
reg = <0x70>;
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index 20bbb89..cc59e42 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -731,6 +731,7 @@
pinctrl-0 = <&cpsw_default>;
pinctrl-1 = <&cpsw_sleep>;
status = "okay";
+ slaves = <1>;
};
&davinci_mdio {
@@ -738,15 +739,14 @@
pinctrl-0 = <&davinci_mdio_default>;
pinctrl-1 = <&davinci_mdio_sleep>;
status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ reg = <0>;
+ };
};
&cpsw_emac0 {
- phy_id = <&davinci_mdio>, <0>;
- phy-mode = "rgmii-txid";
-};
-
-&cpsw_emac1 {
- phy_id = <&davinci_mdio>, <1>;
+ phy-handle = <ðphy0>;
phy-mode = "rgmii-txid";
};
diff --git a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts
index 4d96901..d9e9267 100644
--- a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts
+++ b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts
@@ -161,7 +161,7 @@
invensense,key = [4e cc 7e eb f6 1e 35 22 00 34 0d 65 32 e9 94 89];*/
};
- bmp280: pressure@78 {
+ bmp280: pressure@76 {
compatible = "bosch,bmp280";
reg = <0x76>;
};
diff --git a/arch/arm/boot/dts/am335x-pdu001.dts b/arch/arm/boot/dts/am335x-pdu001.dts
index 1ad530a..f56798e 100644
--- a/arch/arm/boot/dts/am335x-pdu001.dts
+++ b/arch/arm/boot/dts/am335x-pdu001.dts
@@ -373,7 +373,7 @@
ti,pindir-d0-out-d1-in;
status = "okay";
- cfaf240320a032t {
+ display-controller@0 {
compatible = "orisetech,otm3225a";
reg = <0>;
spi-max-frequency = <1000000>;
@@ -577,7 +577,7 @@
bus-width = <4>;
pinctrl-names = "default";
pinctrl-0 = <&mmc2_pins>;
- cd-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
+ cd-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
};
&sham {
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index cf1e4f7..09e58fb 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -1101,7 +1101,7 @@
};
};
- qspi: qspi@47900000 {
+ qspi: spi@47900000 {
compatible = "ti,am4372-qspi";
reg = <0x47900000 0x100>,
<0x30000000 0x4000000>;
diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index 5b97c20..8a17eca 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -83,7 +83,7 @@
};
lcd0: display {
- compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
+ compatible = "osddisplays,osd070t1718-19ts", "panel-dpi";
label = "lcd";
backlight = <&lcd_bl>;
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index 6502d33..12735cf 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -45,7 +45,7 @@
};
lcd0: display {
- compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
+ compatible = "osddisplays,osd070t1718-19ts", "panel-dpi";
label = "lcd";
backlight = <&lcd_bl>;
diff --git a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts
index 203266f..52ae8ee 100644
--- a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts
+++ b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts
@@ -518,7 +518,7 @@
};
/* touch controller */
- ads7846@0 {
+ touchscreen@1 {
pinctrl-names = "default";
pinctrl-0 = <&ads7846_pins>;
diff --git a/arch/arm/boot/dts/arm-realview-eb.dtsi b/arch/arm/boot/dts/arm-realview-eb.dtsi
index a917cf8..0e4c7c4 100644
--- a/arch/arm/boot/dts/arm-realview-eb.dtsi
+++ b/arch/arm/boot/dts/arm-realview-eb.dtsi
@@ -371,7 +371,7 @@
clock-names = "uartclk", "apb_pclk";
};
- ssp: ssp@1000d000 {
+ ssp: spi@1000d000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x1000d000 0x1000>;
clocks = <&sspclk>, <&pclk>;
diff --git a/arch/arm/boot/dts/arm-realview-pb1176.dts b/arch/arm/boot/dts/arm-realview-pb1176.dts
index f935b72..83e0fbc 100644
--- a/arch/arm/boot/dts/arm-realview-pb1176.dts
+++ b/arch/arm/boot/dts/arm-realview-pb1176.dts
@@ -45,7 +45,7 @@
};
/* The voltage to the MMC card is hardwired at 3.3V */
- vmmc: fixedregulator@0 {
+ vmmc: regulator-vmmc {
compatible = "regulator-fixed";
regulator-name = "vmmc";
regulator-min-microvolt = <3300000>;
@@ -53,7 +53,7 @@
regulator-boot-on;
};
- veth: fixedregulator@0 {
+ veth: regulator-veth {
compatible = "regulator-fixed";
regulator-name = "veth";
regulator-min-microvolt = <3300000>;
@@ -380,7 +380,7 @@
clock-names = "apb_pclk";
};
- pb1176_ssp: ssp@1010b000 {
+ pb1176_ssp: spi@1010b000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x1010b000 0x1000>;
interrupt-parent = <&intc_dc1176>;
diff --git a/arch/arm/boot/dts/arm-realview-pb11mp.dts b/arch/arm/boot/dts/arm-realview-pb11mp.dts
index 3620328..2f6aa24 100644
--- a/arch/arm/boot/dts/arm-realview-pb11mp.dts
+++ b/arch/arm/boot/dts/arm-realview-pb11mp.dts
@@ -145,7 +145,7 @@
};
/* The voltage to the MMC card is hardwired at 3.3V */
- vmmc: fixedregulator@0 {
+ vmmc: regulator-vmmc {
compatible = "regulator-fixed";
regulator-name = "vmmc";
regulator-min-microvolt = <3300000>;
@@ -153,7 +153,7 @@
regulator-boot-on;
};
- veth: fixedregulator@0 {
+ veth: regulator-veth {
compatible = "regulator-fixed";
regulator-name = "veth";
regulator-min-microvolt = <3300000>;
@@ -523,7 +523,7 @@
clock-names = "uartclk", "apb_pclk";
};
- ssp@1000d000 {
+ spi@1000d000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x1000d000 0x1000>;
interrupt-parent = <&intc_pb11mp>;
diff --git a/arch/arm/boot/dts/arm-realview-pbx.dtsi b/arch/arm/boot/dts/arm-realview-pbx.dtsi
index 10868ba3..916a977 100644
--- a/arch/arm/boot/dts/arm-realview-pbx.dtsi
+++ b/arch/arm/boot/dts/arm-realview-pbx.dtsi
@@ -44,7 +44,7 @@
};
/* The voltage to the MMC card is hardwired at 3.3V */
- vmmc: fixedregulator@0 {
+ vmmc: regulator-vmmc {
compatible = "regulator-fixed";
regulator-name = "vmmc";
regulator-min-microvolt = <3300000>;
@@ -52,7 +52,7 @@
regulator-boot-on;
};
- veth: fixedregulator@0 {
+ veth: regulator-veth {
compatible = "regulator-fixed";
regulator-name = "veth";
regulator-min-microvolt = <3300000>;
@@ -362,7 +362,7 @@
clock-names = "uartclk", "apb_pclk";
};
- ssp: ssp@1000d000 {
+ ssp: spi@1000d000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x1000d000 0x1000>;
clocks = <&sspclk>, <&pclk>;
@@ -567,4 +567,3 @@
};
};
};
-
diff --git a/arch/arm/boot/dts/armada-388-clearfog.dtsi b/arch/arm/boot/dts/armada-388-clearfog.dtsi
index 7c6ad2a..1b0d068 100644
--- a/arch/arm/boot/dts/armada-388-clearfog.dtsi
+++ b/arch/arm/boot/dts/armada-388-clearfog.dtsi
@@ -48,7 +48,7 @@
&clearfog_sdhci_cd_pins>;
pinctrl-names = "default";
status = "okay";
- vmmc = <®_3p3v>;
+ vmmc-supply = <®_3p3v>;
wp-inverted;
};
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index b23a983..69f6b9d 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -350,7 +350,7 @@
status = "disabled";
};
- i2c: i2c@1e78a000 {
+ i2c: bus@1e78a000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 87fdc14..d107459 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -410,7 +410,7 @@
status = "disabled";
};
- i2c: i2c@1e78a000 {
+ i2c: bus@1e78a000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm/boot/dts/at91-dvk_su60_somc.dtsi b/arch/arm/boot/dts/at91-dvk_su60_somc.dtsi
index bb86f17..21876da 100644
--- a/arch/arm/boot/dts/at91-dvk_su60_somc.dtsi
+++ b/arch/arm/boot/dts/at91-dvk_su60_somc.dtsi
@@ -70,9 +70,9 @@
&i2c1 {
status = "okay";
- eeprom@87 {
+ eeprom@57 {
compatible = "giantec,gt24c32a", "atmel,24c32";
- reg = <87>;
+ reg = <0x57>;
pagesize = <32>;
};
};
diff --git a/arch/arm/boot/dts/at91-dvk_su60_somc_lcm.dtsi b/arch/arm/boot/dts/at91-dvk_su60_somc_lcm.dtsi
index 4b9176d..df0f0cc 100644
--- a/arch/arm/boot/dts/at91-dvk_su60_somc_lcm.dtsi
+++ b/arch/arm/boot/dts/at91-dvk_su60_somc_lcm.dtsi
@@ -59,9 +59,9 @@
&i2c1 {
status = "okay";
- ft5426@56 {
+ ft5426@38 {
compatible = "focaltech,ft5426", "edt,edt-ft5406";
- reg = <56>;
+ reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcd_ctp_int>;
diff --git a/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts b/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts
index 3b1baa8..2214bfe7 100644
--- a/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts
@@ -92,13 +92,13 @@
reg = <0x40000 0xc0000>;
};
- bootloaderenv@0x100000 {
- label = "bootloader env";
+ bootloaderenvred@0x100000 {
+ label = "bootloader env redundant";
reg = <0x100000 0x40000>;
};
- bootloaderenvred@0x140000 {
- label = "bootloader env redundant";
+ bootloaderenv@0x140000 {
+ label = "bootloader env";
reg = <0x140000 0x40000>;
};
diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
index 4b7c762..7d554b9 100644
--- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
@@ -252,7 +252,7 @@
rootfs@800000 {
label = "rootfs";
- reg = <0x800000 0x0f800000>;
+ reg = <0x800000 0x1f800000>;
};
};
};
diff --git a/arch/arm/boot/dts/at91-vinco.dts b/arch/arm/boot/dts/at91-vinco.dts
index 1be9889..4302772 100644
--- a/arch/arm/boot/dts/at91-vinco.dts
+++ b/arch/arm/boot/dts/at91-vinco.dts
@@ -128,7 +128,7 @@
i2c2: i2c@f8024000 {
status = "okay";
- rtc1: rtc@64 {
+ rtc1: rtc@32 {
compatible = "epson,rx8900";
reg = <0x32>;
};
diff --git a/arch/arm/boot/dts/at91sam9260ek.dts b/arch/arm/boot/dts/at91sam9260ek.dts
index d2b865f..07d1b57 100644
--- a/arch/arm/boot/dts/at91sam9260ek.dts
+++ b/arch/arm/boot/dts/at91sam9260ek.dts
@@ -127,7 +127,7 @@
spi0: spi@fffc8000 {
cs-gpios = <0>, <&pioC 11 0>, <0>, <0>;
- mtd_dataflash@0 {
+ mtd_dataflash@1 {
compatible = "atmel,at45", "atmel,dataflash";
spi-max-frequency = <50000000>;
reg = <1>;
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index a29fc04..a57f2d4 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -160,7 +160,7 @@
spi-max-frequency = <15000000>;
};
- tsc2046@0 {
+ tsc2046@2 {
reg = <2>;
compatible = "ti,ads7843";
interrupts-extended = <&pioC 2 IRQ_TYPE_EDGE_BOTH>;
diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
index 71df3ad..ec1f17a 100644
--- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
+++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
@@ -109,7 +109,7 @@
spi0: spi@fffc8000 {
cs-gpios = <0>, <&pioC 11 0>, <0>, <0>;
- mtd_dataflash@0 {
+ mtd_dataflash@1 {
compatible = "atmel,at45", "atmel,dataflash";
spi-max-frequency = <50000000>;
reg = <1>;
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index 1ee25a4..d16db1f 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -570,7 +570,7 @@
};
};
- uart1 {
+ usart1 {
pinctrl_usart1: usart1-0 {
atmel,pins =
<AT91_PIOB 4 AT91_PERIPH_A AT91_PINCTRL_NONE
diff --git a/arch/arm/boot/dts/at91sam9x5cm.dtsi b/arch/arm/boot/dts/at91sam9x5cm.dtsi
index 4908ee0..993eabe 100644
--- a/arch/arm/boot/dts/at91sam9x5cm.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5cm.dtsi
@@ -100,7 +100,7 @@
rootfs@800000 {
label = "rootfs";
- reg = <0x800000 0x1f800000>;
+ reg = <0x800000 0x0f800000>;
};
};
};
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 253df71..887a60c 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -169,8 +169,8 @@
mdio: mdio@18002000 {
compatible = "brcm,iproc-mdio";
reg = <0x18002000 0x8>;
- #size-cells = <1>;
- #address-cells = <0>;
+ #size-cells = <0>;
+ #address-cells = <1>;
status = "disabled";
gphy0: ethernet-phy@0 {
diff --git a/arch/arm/boot/dts/bcm-hr2.dtsi b/arch/arm/boot/dts/bcm-hr2.dtsi
index 3084a7c..e4d4973 100644
--- a/arch/arm/boot/dts/bcm-hr2.dtsi
+++ b/arch/arm/boot/dts/bcm-hr2.dtsi
@@ -216,7 +216,7 @@
reg = <0x33000 0x14>;
};
- qspi: qspi@27200 {
+ qspi: spi@27200 {
compatible = "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi";
reg = <0x027200 0x184>,
<0x027000 0x124>,
diff --git a/arch/arm/boot/dts/bcm-nsp.dtsi b/arch/arm/boot/dts/bcm-nsp.dtsi
index 09ba850..2b219ad 100644
--- a/arch/arm/boot/dts/bcm-nsp.dtsi
+++ b/arch/arm/boot/dts/bcm-nsp.dtsi
@@ -273,7 +273,7 @@
brcm,nand-has-wp;
};
- qspi: qspi@27200 {
+ qspi: spi@27200 {
compatible = "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi";
reg = <0x027200 0x184>,
<0x027000 0x124>,
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index 31b2964..c9322a5 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -39,7 +39,7 @@
trips {
cpu-crit {
- temperature = <80000>;
+ temperature = <90000>;
hysteresis = <0>;
type = "critical";
};
diff --git a/arch/arm/boot/dts/bcm5301x.dtsi b/arch/arm/boot/dts/bcm5301x.dtsi
index bc607d1..a678fb7 100644
--- a/arch/arm/boot/dts/bcm5301x.dtsi
+++ b/arch/arm/boot/dts/bcm5301x.dtsi
@@ -350,8 +350,8 @@
mdio: mdio@18003000 {
compatible = "brcm,iproc-mdio";
reg = <0x18003000 0x8>;
- #size-cells = <1>;
- #address-cells = <0>;
+ #size-cells = <0>;
+ #address-cells = <1>;
};
mdio-bus-mux {
diff --git a/arch/arm/boot/dts/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts
index 580e3cb..3e1584e 100644
--- a/arch/arm/boot/dts/dove-cubox.dts
+++ b/arch/arm/boot/dts/dove-cubox.dts
@@ -87,7 +87,7 @@
status = "okay";
clock-frequency = <100000>;
- si5351: clock-generator {
+ si5351: clock-generator@60 {
compatible = "silabs,si5351a-msop";
reg = <0x60>;
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 4a0a511..250ad05 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -155,7 +155,7 @@
0xffffe000 MBUS_ID(0x03, 0x01) 0 0x0000800 /* CESA SRAM 2k */
0xfffff000 MBUS_ID(0x0d, 0x00) 0 0x0000800>; /* PMU SRAM 2k */
- spi0: spi-ctrl@10600 {
+ spi0: spi@10600 {
compatible = "marvell,orion-spi";
#address-cells = <1>;
#size-cells = <0>;
@@ -168,7 +168,7 @@
status = "disabled";
};
- i2c: i2c-ctrl@11000 {
+ i2c: i2c@11000 {
compatible = "marvell,mv64xxx-i2c";
reg = <0x11000 0x20>;
#address-cells = <1>;
@@ -218,7 +218,7 @@
status = "disabled";
};
- spi1: spi-ctrl@14600 {
+ spi1: spi@14600 {
compatible = "marvell,orion-spi";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 2cb45dd..7ce24b2 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -336,6 +336,7 @@
<0 0 0 2 &pcie1_intc 2>,
<0 0 0 3 &pcie1_intc 3>,
<0 0 0 4 &pcie1_intc 4>;
+ ti,syscon-unaligned-access = <&scm_conf1 0x14 1>;
status = "disabled";
pcie1_intc: interrupt-controller {
interrupt-controller;
@@ -387,6 +388,7 @@
<0 0 0 2 &pcie2_intc 2>,
<0 0 0 3 &pcie2_intc 3>,
<0 0 0 4 &pcie2_intc 4>;
+ ti,syscon-unaligned-access = <&scm_conf1 0x14 2>;
pcie2_intc: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
@@ -1369,7 +1371,7 @@
status = "disabled";
};
- qspi: qspi@4b300000 {
+ qspi: spi@4b300000 {
compatible = "ti,dra7xxx-qspi";
reg = <0x4b300000 0x100>,
<0x5c000000 0x4000000>;
diff --git a/arch/arm/boot/dts/exynos3250-artik5.dtsi b/arch/arm/boot/dts/exynos3250-artik5.dtsi
index 620b50c..7c22cbf 100644
--- a/arch/arm/boot/dts/exynos3250-artik5.dtsi
+++ b/arch/arm/boot/dts/exynos3250-artik5.dtsi
@@ -69,6 +69,8 @@
compatible = "samsung,s2mps14-pmic";
interrupt-parent = <&gpx3>;
interrupts = <5 IRQ_TYPE_NONE>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&s2mps14_irq>;
reg = <0x66>;
s2mps14_osc: clocks {
@@ -350,6 +352,11 @@
samsung,pin-drv = <EXYNOS4_PIN_DRV_LV3>;
samsung,pin-val = <1>;
};
+
+ s2mps14_irq: s2mps14-irq {
+ samsung,pins = "gpx3-5";
+ samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+ };
};
&rtc {
diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi
index 94efca7..5892a9f7 100644
--- a/arch/arm/boot/dts/exynos3250.dtsi
+++ b/arch/arm/boot/dts/exynos3250.dtsi
@@ -360,7 +360,7 @@
};
hsotg: hsotg@12480000 {
- compatible = "snps,dwc2";
+ compatible = "samsung,s3c6400-hsotg", "snps,dwc2";
reg = <0x12480000 0x20000>;
interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cmu CLK_USBOTG>;
diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index 7a8a5c5..4ab1f1c 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -149,9 +149,11 @@
};
&hdmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_hpd>;
status = "okay";
- ddc = <&i2c_2>;
- hpd-gpios = <&gpx3 7 GPIO_ACTIVE_LOW>;
+ ddc = <&i2c_ddc>;
+ hpd-gpios = <&gpx3 7 GPIO_ACTIVE_HIGH>;
vdd_osc-supply = <&ldo10_reg>;
vdd_pll-supply = <&ldo8_reg>;
vdd-supply = <&ldo8_reg>;
@@ -168,6 +170,8 @@
reg = <0x66>;
interrupt-parent = <&gpx3>;
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&s5m8767_irq>;
vinb1-supply = <&main_dc_reg>;
vinb2-supply = <&main_dc_reg>;
@@ -452,13 +456,6 @@
};
};
-&i2c_2 {
- status = "okay";
- /* used by HDMI DDC */
- samsung,i2c-sda-delay = <100>;
- samsung,i2c-max-bus-freq = <66000>;
-};
-
&i2c_3 {
status = "okay";
@@ -535,6 +532,13 @@
cap-sd-highspeed;
};
+&pinctrl_0 {
+ s5m8767_irq: s5m8767-irq {
+ samsung,pins = "gpx3-2";
+ samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+ };
+};
+
&rtc {
status = "okay";
};
@@ -547,3 +551,22 @@
status = "okay";
samsung,exynos-sataphy-i2c-phandle = <&sata_phy_i2c>;
};
+
+&soc {
+ /*
+ * For unknown reasons HDMI-DDC does not work with Exynos I2C
+ * controllers. Lets use software I2C over GPIO pins as a workaround.
+ */
+ i2c_ddc: i2c-gpio {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_gpio_bus>;
+ status = "okay";
+ compatible = "i2c-gpio";
+ gpios = <&gpa0 6 0 /* sda */
+ &gpa0 7 0 /* scl */
+ >;
+ i2c-gpio,delay-us = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/exynos5250-pinctrl.dtsi b/arch/arm/boot/dts/exynos5250-pinctrl.dtsi
index 6ff6dea..d31a686 100644
--- a/arch/arm/boot/dts/exynos5250-pinctrl.dtsi
+++ b/arch/arm/boot/dts/exynos5250-pinctrl.dtsi
@@ -225,6 +225,12 @@
samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
};
+ i2c2_gpio_bus: i2c2-gpio-bus {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+ samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
+ };
+
uart2_data: uart2-data {
samsung,pins = "gpa1-0", "gpa1-1";
samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
@@ -593,6 +599,11 @@
samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
};
+
+ hdmi_hpd: hdmi-hpd {
+ samsung,pins = "gpx3-7";
+ samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+ };
};
&pinctrl_1 {
diff --git a/arch/arm/boot/dts/exynos5250-snow-rev5.dts b/arch/arm/boot/dts/exynos5250-snow-rev5.dts
index 0348b1c..7cbfc6f 100644
--- a/arch/arm/boot/dts/exynos5250-snow-rev5.dts
+++ b/arch/arm/boot/dts/exynos5250-snow-rev5.dts
@@ -20,6 +20,14 @@
samsung,model = "Snow-I2S-MAX98090";
samsung,audio-codec = <&max98090>;
+
+ cpu {
+ sound-dai = <&i2s0 0>;
+ };
+
+ codec {
+ sound-dai = <&max98090 0>, <&hdmi>;
+ };
};
};
@@ -31,6 +39,9 @@
interrupt-parent = <&gpx0>;
pinctrl-names = "default";
pinctrl-0 = <&max98090_irq>;
+ clocks = <&pmu_system_controller 0>;
+ clock-names = "mclk";
+ #sound-dai-cells = <1>;
};
};
diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index 25bdc9d..9eb48ca 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -153,7 +153,7 @@
&clock_audss {
assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>;
- assigned-clock-parents = <&clock CLK_FOUT_EPLL>;
+ assigned-clock-parents = <&clock CLK_MAU_EPLL>;
};
&cpu0 {
@@ -312,6 +312,7 @@
regulator-name = "vdd_1v35";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
@@ -333,6 +334,7 @@
regulator-name = "vdd_2v";
regulator-min-microvolt = <2000000>;
regulator-max-microvolt = <2000000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
@@ -343,6 +345,7 @@
regulator-name = "vdd_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
diff --git a/arch/arm/boot/dts/exynos5422-odroid-core.dtsi b/arch/arm/boot/dts/exynos5422-odroid-core.dtsi
index 27214e6..d476ba0 100644
--- a/arch/arm/boot/dts/exynos5422-odroid-core.dtsi
+++ b/arch/arm/boot/dts/exynos5422-odroid-core.dtsi
@@ -224,7 +224,7 @@
ldo13_reg: LDO13 {
regulator-name = "vddq_mmc2";
- regulator-min-microvolt = <2800000>;
+ regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2800000>;
};
diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts
index 7989631..4398f2d 100644
--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts
+++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts
@@ -153,7 +153,7 @@
&clock_audss {
assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>;
- assigned-clock-parents = <&clock CLK_FOUT_EPLL>;
+ assigned-clock-parents = <&clock CLK_MAU_EPLL>;
};
&cpu0 {
@@ -312,6 +312,7 @@
regulator-name = "vdd_1v35";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
@@ -333,6 +334,7 @@
regulator-name = "vdd_2v";
regulator-min-microvolt = <2000000>;
regulator-max-microvolt = <2000000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
@@ -343,6 +345,7 @@
regulator-name = "vdd_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
diff --git a/arch/arm/boot/dts/gemini-sq201.dts b/arch/arm/boot/dts/gemini-sq201.dts
index 3787cf3..e9e4a8a 100644
--- a/arch/arm/boot/dts/gemini-sq201.dts
+++ b/arch/arm/boot/dts/gemini-sq201.dts
@@ -20,7 +20,7 @@
};
chosen {
- bootargs = "console=ttyS0,115200n8";
+ bootargs = "console=ttyS0,115200n8 root=/dev/mtdblock2 rw rootfstype=squashfs,jffs2 rootwait";
stdout-path = &uart0;
};
@@ -138,37 +138,10 @@
/* 16MB of flash */
reg = <0x30000000 0x01000000>;
- partition@0 {
- label = "RedBoot";
- reg = <0x00000000 0x00120000>;
- read-only;
- };
- partition@120000 {
- label = "Kernel";
- reg = <0x00120000 0x00200000>;
- };
- partition@320000 {
- label = "Ramdisk";
- reg = <0x00320000 0x00600000>;
- };
- partition@920000 {
- label = "Application";
- reg = <0x00920000 0x00600000>;
- };
- partition@f20000 {
- label = "VCTL";
- reg = <0x00f20000 0x00020000>;
- read-only;
- };
- partition@f40000 {
- label = "CurConf";
- reg = <0x00f40000 0x000a0000>;
- read-only;
- };
- partition@fe0000 {
- label = "FIS directory";
- reg = <0x00fe0000 0x00020000>;
- read-only;
+ partitions {
+ compatible = "redboot-fis";
+ /* Eraseblock at 0xfe0000 */
+ fis-index-block = <0x1fc>;
};
};
diff --git a/arch/arm/boot/dts/imx1-ads.dts b/arch/arm/boot/dts/imx1-ads.dts
index a1d81ba..119b19b 100644
--- a/arch/arm/boot/dts/imx1-ads.dts
+++ b/arch/arm/boot/dts/imx1-ads.dts
@@ -21,6 +21,7 @@
};
memory@8000000 {
+ device_type = "memory";
reg = <0x08000000 0x04000000>;
};
};
diff --git a/arch/arm/boot/dts/imx1-apf9328.dts b/arch/arm/boot/dts/imx1-apf9328.dts
index 11515c0..ee4b1b1 100644
--- a/arch/arm/boot/dts/imx1-apf9328.dts
+++ b/arch/arm/boot/dts/imx1-apf9328.dts
@@ -21,6 +21,7 @@
};
memory@8000000 {
+ device_type = "memory";
reg = <0x08000000 0x00800000>;
};
};
diff --git a/arch/arm/boot/dts/imx1.dtsi b/arch/arm/boot/dts/imx1.dtsi
index 3edc7b5..2b6e770 100644
--- a/arch/arm/boot/dts/imx1.dtsi
+++ b/arch/arm/boot/dts/imx1.dtsi
@@ -15,10 +15,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
gpio0 = &gpio1;
diff --git a/arch/arm/boot/dts/imx23-evk.dts b/arch/arm/boot/dts/imx23-evk.dts
index ad2ae25..aca27aa 100644
--- a/arch/arm/boot/dts/imx23-evk.dts
+++ b/arch/arm/boot/dts/imx23-evk.dts
@@ -10,6 +10,7 @@
compatible = "fsl,imx23-evk", "fsl,imx23";
memory@40000000 {
+ device_type = "memory";
reg = <0x40000000 0x08000000>;
};
diff --git a/arch/arm/boot/dts/imx23-olinuxino.dts b/arch/arm/boot/dts/imx23-olinuxino.dts
index e935177..109f516 100644
--- a/arch/arm/boot/dts/imx23-olinuxino.dts
+++ b/arch/arm/boot/dts/imx23-olinuxino.dts
@@ -20,6 +20,7 @@
compatible = "olimex,imx23-olinuxino", "fsl,imx23";
memory@40000000 {
+ device_type = "memory";
reg = <0x40000000 0x04000000>;
};
diff --git a/arch/arm/boot/dts/imx23-sansa.dts b/arch/arm/boot/dts/imx23-sansa.dts
index 67de786..fa22fd9 100644
--- a/arch/arm/boot/dts/imx23-sansa.dts
+++ b/arch/arm/boot/dts/imx23-sansa.dts
@@ -50,6 +50,7 @@
compatible = "sandisk,sansa_fuze_plus", "fsl,imx23";
memory@40000000 {
+ device_type = "memory";
reg = <0x40000000 0x04000000>;
};
diff --git a/arch/arm/boot/dts/imx23-stmp378x_devb.dts b/arch/arm/boot/dts/imx23-stmp378x_devb.dts
index 95c7b91..aab0293 100644
--- a/arch/arm/boot/dts/imx23-stmp378x_devb.dts
+++ b/arch/arm/boot/dts/imx23-stmp378x_devb.dts
@@ -17,6 +17,7 @@
compatible = "fsl,stmp378x-devb", "fsl,imx23";
memory@40000000 {
+ device_type = "memory";
reg = <0x40000000 0x04000000>;
};
diff --git a/arch/arm/boot/dts/imx23-xfi3.dts b/arch/arm/boot/dts/imx23-xfi3.dts
index 9616e50..2b5df8d 100644
--- a/arch/arm/boot/dts/imx23-xfi3.dts
+++ b/arch/arm/boot/dts/imx23-xfi3.dts
@@ -49,6 +49,7 @@
compatible = "creative,x-fi3", "fsl,imx23";
memory@40000000 {
+ device_type = "memory";
reg = <0x40000000 0x04000000>;
};
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index 71bfd2b..aaaa987 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -13,10 +13,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
gpio0 = &gpio0;
diff --git a/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi
index e316fe0..e4d7da2 100644
--- a/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi
+++ b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi
@@ -18,6 +18,7 @@
compatible = "eukrea,cpuimx25", "fsl,imx25";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x4000000>; /* 64M */
};
};
diff --git a/arch/arm/boot/dts/imx25-karo-tx25.dts b/arch/arm/boot/dts/imx25-karo-tx25.dts
index 5cb6967..f37e9a7 100644
--- a/arch/arm/boot/dts/imx25-karo-tx25.dts
+++ b/arch/arm/boot/dts/imx25-karo-tx25.dts
@@ -37,6 +37,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x02000000 0x90000000 0x02000000>;
};
};
diff --git a/arch/arm/boot/dts/imx25-pdk.dts b/arch/arm/boot/dts/imx25-pdk.dts
index a5626b4..f8544a9 100644
--- a/arch/arm/boot/dts/imx25-pdk.dts
+++ b/arch/arm/boot/dts/imx25-pdk.dts
@@ -12,6 +12,7 @@
compatible = "fsl,imx25-pdk", "fsl,imx25";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x4000000>;
};
diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index 85c15ee..8c8ad80 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -12,10 +12,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx27-apf27.dts b/arch/arm/boot/dts/imx27-apf27.dts
index 3eddd80..f635d5c 100644
--- a/arch/arm/boot/dts/imx27-apf27.dts
+++ b/arch/arm/boot/dts/imx27-apf27.dts
@@ -20,6 +20,7 @@
compatible = "armadeus,imx27-apf27", "fsl,imx27";
memory@a0000000 {
+ device_type = "memory";
reg = <0xa0000000 0x04000000>;
};
};
diff --git a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi
index 9c455dc..c85f9d0 100644
--- a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi
+++ b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi
@@ -17,6 +17,7 @@
compatible = "eukrea,cpuimx27", "fsl,imx27";
memory@a0000000 {
+ device_type = "memory";
reg = <0xa0000000 0x04000000>;
};
diff --git a/arch/arm/boot/dts/imx27-pdk.dts b/arch/arm/boot/dts/imx27-pdk.dts
index f9a882d..35123b7 100644
--- a/arch/arm/boot/dts/imx27-pdk.dts
+++ b/arch/arm/boot/dts/imx27-pdk.dts
@@ -10,6 +10,7 @@
compatible = "fsl,imx27-pdk", "fsl,imx27";
memory@a0000000 {
+ device_type = "memory";
reg = <0xa0000000 0x08000000>;
};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi
index cbad7c8..b0b4f7c 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi
+++ b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi
@@ -18,6 +18,7 @@
compatible = "phytec,imx27-pca100", "fsl,imx27";
memory@a0000000 {
+ device_type = "memory";
reg = <0xa0000000 0x08000000>; /* 128MB */
};
};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi
index ec466b4..0935e14 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi
+++ b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi
@@ -17,6 +17,7 @@
compatible = "phytec,imx27-pcm038", "fsl,imx27";
memory@a0000000 {
+ device_type = "memory";
reg = <0xa0000000 0x08000000>;
};
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index 753d88d..39e75b9 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -16,10 +16,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx31-bug.dts b/arch/arm/boot/dts/imx31-bug.dts
index 6ee4ff8..9eb960c 100644
--- a/arch/arm/boot/dts/imx31-bug.dts
+++ b/arch/arm/boot/dts/imx31-bug.dts
@@ -17,6 +17,7 @@
compatible = "buglabs,imx31-bug", "fsl,imx31";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x8000000>; /* 128M */
};
};
diff --git a/arch/arm/boot/dts/imx31-lite.dts b/arch/arm/boot/dts/imx31-lite.dts
index db52ddc..d17abdfb 100644
--- a/arch/arm/boot/dts/imx31-lite.dts
+++ b/arch/arm/boot/dts/imx31-lite.dts
@@ -18,6 +18,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x8000000>;
};
diff --git a/arch/arm/boot/dts/imx31.dtsi b/arch/arm/boot/dts/imx31.dtsi
index ca1419c..2fc64d2 100644
--- a/arch/arm/boot/dts/imx31.dtsi
+++ b/arch/arm/boot/dts/imx31.dtsi
@@ -10,10 +10,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
gpio0 = &gpio1;
diff --git a/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi
index ba39d93..5f8a47a 100644
--- a/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi
+++ b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi
@@ -18,6 +18,7 @@
compatible = "eukrea,cpuimx35", "fsl,imx35";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x8000000>; /* 128M */
};
};
diff --git a/arch/arm/boot/dts/imx35-pdk.dts b/arch/arm/boot/dts/imx35-pdk.dts
index df613e8..ddce0a8 100644
--- a/arch/arm/boot/dts/imx35-pdk.dts
+++ b/arch/arm/boot/dts/imx35-pdk.dts
@@ -11,6 +11,7 @@
compatible = "fsl,imx35-pdk", "fsl,imx35";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x8000000>,
<0x90000000 0x8000000>;
};
diff --git a/arch/arm/boot/dts/imx35.dtsi b/arch/arm/boot/dts/imx35.dtsi
index 1c50b78..b36b97b 100644
--- a/arch/arm/boot/dts/imx35.dtsi
+++ b/arch/arm/boot/dts/imx35.dtsi
@@ -13,10 +13,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx50-evk.dts b/arch/arm/boot/dts/imx50-evk.dts
index 682a997..a25da41 100644
--- a/arch/arm/boot/dts/imx50-evk.dts
+++ b/arch/arm/boot/dts/imx50-evk.dts
@@ -12,6 +12,7 @@
compatible = "fsl,imx50-evk", "fsl,imx50";
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x80000000>;
};
};
diff --git a/arch/arm/boot/dts/imx50.dtsi b/arch/arm/boot/dts/imx50.dtsi
index ab522c2..9e9e92a 100644
--- a/arch/arm/boot/dts/imx50.dtsi
+++ b/arch/arm/boot/dts/imx50.dtsi
@@ -22,10 +22,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx51-apf51.dts b/arch/arm/boot/dts/imx51-apf51.dts
index 79d8003..1eddf29 100644
--- a/arch/arm/boot/dts/imx51-apf51.dts
+++ b/arch/arm/boot/dts/imx51-apf51.dts
@@ -22,6 +22,7 @@
compatible = "armadeus,imx51-apf51", "fsl,imx51";
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts
index ba60b0c..9919146 100644
--- a/arch/arm/boot/dts/imx51-babbage.dts
+++ b/arch/arm/boot/dts/imx51-babbage.dts
@@ -15,6 +15,7 @@
};
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi b/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi
index 5761a66..82d8df0 100644
--- a/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi
+++ b/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi
@@ -17,6 +17,7 @@
compatible = "digi,connectcore-ccxmx51-som", "fsl,imx51";
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0x08000000>;
};
};
diff --git a/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi
index f8902a3..2e31253 100644
--- a/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi
+++ b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi
@@ -23,6 +23,7 @@
compatible = "eukrea,cpuimx51", "fsl,imx51";
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0x10000000>; /* 256M */
};
};
diff --git a/arch/arm/boot/dts/imx51-ts4800.dts b/arch/arm/boot/dts/imx51-ts4800.dts
index 39eb067..4344632 100644
--- a/arch/arm/boot/dts/imx51-ts4800.dts
+++ b/arch/arm/boot/dts/imx51-ts4800.dts
@@ -18,6 +18,7 @@
};
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0x10000000>;
};
diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts
index 6e80254..9235fd4 100644
--- a/arch/arm/boot/dts/imx51-zii-rdu1.dts
+++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts
@@ -53,6 +53,7 @@
/* Will be filled by the bootloader */
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0>;
};
@@ -514,7 +515,7 @@
};
ds1341: rtc@68 {
- compatible = "maxim,ds1341";
+ compatible = "dallas,ds1341";
reg = <0x68>;
};
diff --git a/arch/arm/boot/dts/imx51-zii-scu2-mezz.dts b/arch/arm/boot/dts/imx51-zii-scu2-mezz.dts
index 26cf085..f5b2d76 100644
--- a/arch/arm/boot/dts/imx51-zii-scu2-mezz.dts
+++ b/arch/arm/boot/dts/imx51-zii-scu2-mezz.dts
@@ -18,6 +18,7 @@
/* Will be filled by the bootloader */
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0>;
};
diff --git a/arch/arm/boot/dts/imx51-zii-scu3-esb.dts b/arch/arm/boot/dts/imx51-zii-scu3-esb.dts
index e6ebac8..ad90d66 100644
--- a/arch/arm/boot/dts/imx51-zii-scu3-esb.dts
+++ b/arch/arm/boot/dts/imx51-zii-scu3-esb.dts
@@ -18,6 +18,7 @@
/* Will be filled by the bootloader */
memory@90000000 {
+ device_type = "memory";
reg = <0x90000000 0>;
};
diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi
index ef2abc0..81f60c9 100644
--- a/arch/arm/boot/dts/imx51.dtsi
+++ b/arch/arm/boot/dts/imx51.dtsi
@@ -16,10 +16,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts
index 117bd00..7d5a482 100644
--- a/arch/arm/boot/dts/imx53-ard.dts
+++ b/arch/arm/boot/dts/imx53-ard.dts
@@ -19,6 +19,7 @@
compatible = "fsl,imx53-ard", "fsl,imx53";
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx53-cx9020.dts b/arch/arm/boot/dts/imx53-cx9020.dts
index cf70ebc..c875e23 100644
--- a/arch/arm/boot/dts/imx53-cx9020.dts
+++ b/arch/arm/boot/dts/imx53-cx9020.dts
@@ -22,6 +22,7 @@
};
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x20000000>,
<0xb0000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx53-m53.dtsi b/arch/arm/boot/dts/imx53-m53.dtsi
index ce45f08..db2e5bc 100644
--- a/arch/arm/boot/dts/imx53-m53.dtsi
+++ b/arch/arm/boot/dts/imx53-m53.dtsi
@@ -16,6 +16,7 @@
compatible = "aries,imx53-m53", "denx,imx53-m53", "fsl,imx53";
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x20000000>,
<0xb0000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi
index 50dde84..f00dda3 100644
--- a/arch/arm/boot/dts/imx53-qsb-common.dtsi
+++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi
@@ -11,6 +11,7 @@
};
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x20000000>,
<0xb0000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx53-smd.dts b/arch/arm/boot/dts/imx53-smd.dts
index 462071c..09071ca 100644
--- a/arch/arm/boot/dts/imx53-smd.dts
+++ b/arch/arm/boot/dts/imx53-smd.dts
@@ -12,6 +12,7 @@
compatible = "fsl,imx53-smd", "fsl,imx53";
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx53-tqma53.dtsi b/arch/arm/boot/dts/imx53-tqma53.dtsi
index a72b898..c77d58f 100644
--- a/arch/arm/boot/dts/imx53-tqma53.dtsi
+++ b/arch/arm/boot/dts/imx53-tqma53.dtsi
@@ -17,6 +17,7 @@
compatible = "tq,tqma53", "fsl,imx53";
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x40000000>; /* Up to 1GiB */
};
diff --git a/arch/arm/boot/dts/imx53-tx53.dtsi b/arch/arm/boot/dts/imx53-tx53.dtsi
index 54cf3e6..4ab1359 100644
--- a/arch/arm/boot/dts/imx53-tx53.dtsi
+++ b/arch/arm/boot/dts/imx53-tx53.dtsi
@@ -51,6 +51,7 @@
/* Will be filled by the bootloader */
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0>;
};
diff --git a/arch/arm/boot/dts/imx53-usbarmory.dts b/arch/arm/boot/dts/imx53-usbarmory.dts
index f6268d0..ee6263d 100644
--- a/arch/arm/boot/dts/imx53-usbarmory.dts
+++ b/arch/arm/boot/dts/imx53-usbarmory.dts
@@ -58,6 +58,7 @@
};
memory@70000000 {
+ device_type = "memory";
reg = <0x70000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi b/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
index f83a8c6..d595034 100644
--- a/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
+++ b/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
@@ -17,12 +17,8 @@
memory@70000000 {
device_type = "memory";
- reg = <0x70000000 0x20000000>;
- };
-
- memory@b0000000 {
- device_type = "memory";
- reg = <0xb0000000 0x20000000>;
+ reg = <0x70000000 0x20000000>,
+ <0xb0000000 0x20000000>;
};
regulators {
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index b6b0818..8accbe1 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -23,10 +23,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 9f11f1f..9d086a3 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -177,6 +177,8 @@
accelerometer@1c {
compatible = "fsl,mma8451";
reg = <0x1c>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mma8451_int>;
interrupt-parent = <&gpio6>;
interrupts = <31 IRQ_TYPE_LEVEL_LOW>;
};
@@ -522,6 +524,12 @@
>;
};
+ pinctrl_mma8451_int: mma8451intgrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_BCLK__GPIO6_IO31 0xb0b1
+ >;
+ };
+
pinctrl_pwm3: pwm1grp {
fsl,pins = <
MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1
diff --git a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi
index 7fff371..315d0e7 100644
--- a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi
@@ -609,13 +609,14 @@
};
touchscreen@2a {
- compatible = "eeti,egalax_ts";
+ compatible = "eeti,exc3000";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ts>;
reg = <0x2a>;
interrupt-parent = <&gpio1>;
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
- wakeup-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+ touchscreen-inverted-x;
+ touchscreen-swapped-x-y;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts
index 679b448..f7a48e4 100644
--- a/arch/arm/boot/dts/imx6sl-evk.dts
+++ b/arch/arm/boot/dts/imx6sl-evk.dts
@@ -17,6 +17,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx6sl-warp.dts b/arch/arm/boot/dts/imx6sl-warp.dts
index 404e602..408da70 100644
--- a/arch/arm/boot/dts/imx6sl-warp.dts
+++ b/arch/arm/boot/dts/imx6sl-warp.dts
@@ -55,6 +55,7 @@
compatible = "warp,imx6sl-warp", "fsl,imx6sl";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index 2fa88c6..55d1872 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -13,10 +13,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec;
diff --git a/arch/arm/boot/dts/imx6sll-evk.dts b/arch/arm/boot/dts/imx6sll-evk.dts
index c8e1155..0c2406a 100644
--- a/arch/arm/boot/dts/imx6sll-evk.dts
+++ b/arch/arm/boot/dts/imx6sll-evk.dts
@@ -20,6 +20,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x80000000>;
};
diff --git a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
index adb5cc7..832b5c5 100644
--- a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
+++ b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
@@ -12,6 +12,7 @@
compatible = "boundary,imx6sx-nitrogen6sx", "fsl,imx6sx";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx6sx-sabreauto.dts b/arch/arm/boot/dts/imx6sx-sabreauto.dts
index 841a27f..48aede5 100644
--- a/arch/arm/boot/dts/imx6sx-sabreauto.dts
+++ b/arch/arm/boot/dts/imx6sx-sabreauto.dts
@@ -11,6 +11,7 @@
compatible = "fsl,imx6sx-sabreauto", "fsl,imx6sx";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x80000000>;
};
diff --git a/arch/arm/boot/dts/imx6sx-sdb.dtsi b/arch/arm/boot/dts/imx6sx-sdb.dtsi
index f8f3187..91f809e 100644
--- a/arch/arm/boot/dts/imx6sx-sdb.dtsi
+++ b/arch/arm/boot/dts/imx6sx-sdb.dtsi
@@ -21,6 +21,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
@@ -115,7 +116,9 @@
regulator-name = "enet_3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
- gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
+ gpio = <&gpio2 6 GPIO_ACTIVE_LOW>;
+ regulator-boot-on;
+ regulator-always-on;
};
reg_pcie_gpio: regulator-pcie-gpio {
@@ -178,6 +181,7 @@
phy-supply = <®_enet_3v3>;
phy-mode = "rgmii";
phy-handle = <ðphy1>;
+ phy-reset-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
status = "okay";
mdio {
@@ -371,6 +375,8 @@
MX6SX_PAD_RGMII1_RD3__ENET1_RX_DATA_3 0x3081
MX6SX_PAD_RGMII1_RX_CTL__ENET1_RX_EN 0x3081
MX6SX_PAD_ENET2_RX_CLK__ENET2_REF_CLK_25M 0x91
+ /* phy reset */
+ MX6SX_PAD_ENET2_CRS__GPIO2_IO_7 0x10b0
>;
};
diff --git a/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts b/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts
index 252175b..2bc5162 100644
--- a/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts
+++ b/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts
@@ -21,6 +21,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts
index 40ccdf4..db0feb9 100644
--- a/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts
+++ b/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts
@@ -49,6 +49,7 @@
compatible = "udoo,neobasic", "fsl,imx6sx";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
};
diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts
index 42bfc8f..5c7a2bb 100644
--- a/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts
+++ b/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts
@@ -49,6 +49,7 @@
compatible = "udoo,neoextended", "fsl,imx6sx";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
};
diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts
index c84c877f..13dfe2a 100644
--- a/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts
+++ b/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts
@@ -49,6 +49,7 @@
compatible = "udoo,neofull", "fsl,imx6sx";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
};
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index 7b62e6f..ae0728d 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -15,10 +15,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
can0 = &flexcan1;
diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi
index 32a0723..6953034 100644
--- a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi
+++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi
@@ -12,6 +12,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
@@ -174,7 +175,7 @@
flash0: n25q256a@0 {
#address-cells = <1>;
#size-cells = <1>;
- compatible = "micron,n25q256a";
+ compatible = "micron,n25q256a", "jedec,spi-nor";
spi-max-frequency = <29000000>;
reg = <0>;
};
diff --git a/arch/arm/boot/dts/imx6ul-geam.dts b/arch/arm/boot/dts/imx6ul-geam.dts
index d81d20f..85cfad0 100644
--- a/arch/arm/boot/dts/imx6ul-geam.dts
+++ b/arch/arm/boot/dts/imx6ul-geam.dts
@@ -51,6 +51,7 @@
compatible = "engicam,imx6ul-geam", "fsl,imx6ul";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x08000000>;
};
diff --git a/arch/arm/boot/dts/imx6ul-isiot.dtsi b/arch/arm/boot/dts/imx6ul-isiot.dtsi
index cd99285..1cb5274 100644
--- a/arch/arm/boot/dts/imx6ul-isiot.dtsi
+++ b/arch/arm/boot/dts/imx6ul-isiot.dtsi
@@ -46,6 +46,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx6ul-litesom.dtsi b/arch/arm/boot/dts/imx6ul-litesom.dtsi
index 8f775f6..8d68932 100644
--- a/arch/arm/boot/dts/imx6ul-litesom.dtsi
+++ b/arch/arm/boot/dts/imx6ul-litesom.dtsi
@@ -48,6 +48,7 @@
compatible = "grinn,imx6ul-litesom", "fsl,imx6ul";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
};
diff --git a/arch/arm/boot/dts/imx6ul-opos6ul.dtsi b/arch/arm/boot/dts/imx6ul-opos6ul.dtsi
index a031bee..cf7faf4 100644
--- a/arch/arm/boot/dts/imx6ul-opos6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul-opos6ul.dtsi
@@ -49,6 +49,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0>; /* will be filled by U-Boot */
};
diff --git a/arch/arm/boot/dts/imx6ul-pico-hobbit.dts b/arch/arm/boot/dts/imx6ul-pico-hobbit.dts
index 0c09420f..797262d 100644
--- a/arch/arm/boot/dts/imx6ul-pico-hobbit.dts
+++ b/arch/arm/boot/dts/imx6ul-pico-hobbit.dts
@@ -53,6 +53,7 @@
/* Will be filled by the bootloader */
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0>;
};
diff --git a/arch/arm/boot/dts/imx6ul-tx6ul.dtsi b/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
index 02b5ba4..bb6dbfd 100644
--- a/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
@@ -71,6 +71,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0>; /* will be filled by U-Boot */
};
diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
index 336cdea..adecd6e 100644
--- a/arch/arm/boot/dts/imx6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul.dtsi
@@ -15,10 +15,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
ethernet0 = &fec1;
@@ -89,6 +87,8 @@
"pll1_sys";
arm-supply = <®_arm>;
soc-supply = <®_soc>;
+ nvmem-cells = <&cpu_speed_grade>;
+ nvmem-cell-names = "speed_grade";
};
};
@@ -932,6 +932,10 @@
tempmon_temp_grade: temp-grade@20 {
reg = <0x20 4>;
};
+
+ cpu_speed_grade: speed-grade@10 {
+ reg = <0x10 4>;
+ };
};
lcdif: lcdif@21c8000 {
diff --git a/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi
index 10ab469..fb213be 100644
--- a/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi
+++ b/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi
@@ -7,6 +7,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x10000000>;
};
};
diff --git a/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
index 183193e..038d8c90 100644
--- a/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
+++ b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
@@ -7,6 +7,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx6ull.dtsi b/arch/arm/boot/dts/imx6ull.dtsi
index cd1776a..796ed35 100644
--- a/arch/arm/boot/dts/imx6ull.dtsi
+++ b/arch/arm/boot/dts/imx6ull.dtsi
@@ -22,7 +22,7 @@
>;
fsl,soc-operating-points = <
/* KHz uV */
- 900000 1175000
+ 900000 1250000
792000 1175000
528000 1175000
396000 1175000
diff --git a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
index 584418f..62d5e9a4a 100644
--- a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
+++ b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
@@ -19,6 +19,7 @@
compatible = "compulab,cl-som-imx7", "fsl,imx7d";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB - minimal configuration */
};
@@ -284,4 +285,4 @@
MX7D_PAD_LPSR_GPIO1_IO05__GPIO1_IO5 0x14 /* OTG PWREN */
>;
};
-};
\ No newline at end of file
+};
diff --git a/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi b/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi
index 04d24ee..898f4b8 100644
--- a/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi
+++ b/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi
@@ -8,6 +8,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
};
diff --git a/arch/arm/boot/dts/imx7d-colibri.dtsi b/arch/arm/boot/dts/imx7d-colibri.dtsi
index d9f8fb6..e2e327f 100644
--- a/arch/arm/boot/dts/imx7d-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7d-colibri.dtsi
@@ -45,6 +45,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
};
diff --git a/arch/arm/boot/dts/imx7d-nitrogen7.dts b/arch/arm/boot/dts/imx7d-nitrogen7.dts
index 177d21f..6b4acea 100644
--- a/arch/arm/boot/dts/imx7d-nitrogen7.dts
+++ b/arch/arm/boot/dts/imx7d-nitrogen7.dts
@@ -12,6 +12,7 @@
compatible = "boundary,imx7d-nitrogen7", "fsl,imx7d";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x40000000>;
};
diff --git a/arch/arm/boot/dts/imx7d-pico.dtsi b/arch/arm/boot/dts/imx7d-pico.dtsi
index f27b384..934a019 100644
--- a/arch/arm/boot/dts/imx7d-pico.dtsi
+++ b/arch/arm/boot/dts/imx7d-pico.dtsi
@@ -49,6 +49,7 @@
compatible = "technexion,imx7d-pico", "fsl,imx7d";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x80000000>;
};
diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
index c9b3c60..317f1bc 100644
--- a/arch/arm/boot/dts/imx7d-sdb.dts
+++ b/arch/arm/boot/dts/imx7d-sdb.dts
@@ -15,6 +15,7 @@
};
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x80000000>;
};
diff --git a/arch/arm/boot/dts/imx7s-colibri.dtsi b/arch/arm/boot/dts/imx7s-colibri.dtsi
index fe8344c..1fb1ec5 100644
--- a/arch/arm/boot/dts/imx7s-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7s-colibri.dtsi
@@ -45,6 +45,7 @@
/ {
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x10000000>;
};
};
diff --git a/arch/arm/boot/dts/imx7s-warp.dts b/arch/arm/boot/dts/imx7s-warp.dts
index fa390da..97d5c71 100644
--- a/arch/arm/boot/dts/imx7s-warp.dts
+++ b/arch/arm/boot/dts/imx7s-warp.dts
@@ -51,6 +51,7 @@
compatible = "warp,imx7s-warp", "fsl,imx7s";
memory@80000000 {
+ device_type = "memory";
reg = <0x80000000 0x20000000>;
};
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index a7f697b..7eaf96b 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -17,10 +17,8 @@
* The decompressor and also some bootloaders rely on a
* pre-existing /chosen node to be available to insert the
* command line and merge other ATAGS info.
- * Also for U-Boot there must be a pre-existing /memory node.
*/
chosen {};
- memory { device_type = "memory"; };
aliases {
gpio0 = &gpio1;
@@ -443,7 +441,7 @@
compatible = "fsl,imx7d-gpt", "fsl,imx6sx-gpt";
reg = <0x302d0000 0x10000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX7D_CLK_DUMMY>,
+ clocks = <&clks IMX7D_GPT1_ROOT_CLK>,
<&clks IMX7D_GPT1_ROOT_CLK>;
clock-names = "ipg", "per";
};
@@ -452,7 +450,7 @@
compatible = "fsl,imx7d-gpt", "fsl,imx6sx-gpt";
reg = <0x302e0000 0x10000>;
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX7D_CLK_DUMMY>,
+ clocks = <&clks IMX7D_GPT2_ROOT_CLK>,
<&clks IMX7D_GPT2_ROOT_CLK>;
clock-names = "ipg", "per";
status = "disabled";
@@ -462,7 +460,7 @@
compatible = "fsl,imx7d-gpt", "fsl,imx6sx-gpt";
reg = <0x302f0000 0x10000>;
interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX7D_CLK_DUMMY>,
+ clocks = <&clks IMX7D_GPT3_ROOT_CLK>,
<&clks IMX7D_GPT3_ROOT_CLK>;
clock-names = "ipg", "per";
status = "disabled";
@@ -472,7 +470,7 @@
compatible = "fsl,imx7d-gpt", "fsl,imx6sx-gpt";
reg = <0x30300000 0x10000>;
interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX7D_CLK_DUMMY>,
+ clocks = <&clks IMX7D_GPT4_ROOT_CLK>,
<&clks IMX7D_GPT4_ROOT_CLK>;
clock-names = "ipg", "per";
status = "disabled";
diff --git a/arch/arm/boot/dts/keystone-k2g.dtsi b/arch/arm/boot/dts/keystone-k2g.dtsi
index 738b44c..1c83310 100644
--- a/arch/arm/boot/dts/keystone-k2g.dtsi
+++ b/arch/arm/boot/dts/keystone-k2g.dtsi
@@ -416,7 +416,7 @@
clock-names = "fck", "mmchsdb_fck";
};
- qspi: qspi@2940000 {
+ qspi: spi@2940000 {
compatible = "ti,k2g-qspi", "cdns,qspi-nor";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
index 7d2302e..9354da4 100644
--- a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
+++ b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
@@ -196,3 +196,7 @@
&twl_gpio {
ti,use-leds;
};
+
+&twl_keypad {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/lpc32xx.dtsi b/arch/arm/boot/dts/lpc32xx.dtsi
index 4981741..ed0d6fb 100644
--- a/arch/arm/boot/dts/lpc32xx.dtsi
+++ b/arch/arm/boot/dts/lpc32xx.dtsi
@@ -179,7 +179,7 @@
* ssp0 and spi1 are shared pins;
* enable one in your board dts, as needed.
*/
- ssp0: ssp@20084000 {
+ ssp0: spi@20084000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x20084000 0x1000>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
@@ -199,7 +199,7 @@
* ssp1 and spi2 are shared pins;
* enable one in your board dts, as needed.
*/
- ssp1: ssp@2008c000 {
+ ssp1: spi@2008c000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x2008c000 0x1000>;
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm/boot/dts/meson8.dtsi b/arch/arm/boot/dts/meson8.dtsi
index d77dcf8..7162e0c 100644
--- a/arch/arm/boot/dts/meson8.dtsi
+++ b/arch/arm/boot/dts/meson8.dtsi
@@ -194,7 +194,7 @@
#clock-cells = <1>;
#reset-cells = <1>;
compatible = "amlogic,meson8-clkc";
- reg = <0x8000 0x4>, <0x4000 0x460>;
+ reg = <0x8000 0x4>, <0x4000 0x400>;
};
reset: reset-controller@4404 {
diff --git a/arch/arm/boot/dts/meson8b.dtsi b/arch/arm/boot/dts/meson8b.dtsi
index 5b3e5c5..4293047 100644
--- a/arch/arm/boot/dts/meson8b.dtsi
+++ b/arch/arm/boot/dts/meson8b.dtsi
@@ -163,7 +163,7 @@
#clock-cells = <1>;
#reset-cells = <1>;
compatible = "amlogic,meson8b-clkc";
- reg = <0x8000 0x4>, <0x4000 0x460>;
+ reg = <0x8000 0x4>, <0x4000 0x400>;
};
reset: reset-controller@4404 {
diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi
index 47e5b63..e95deed 100644
--- a/arch/arm/boot/dts/mmp2.dtsi
+++ b/arch/arm/boot/dts/mmp2.dtsi
@@ -180,7 +180,7 @@
clocks = <&soc_clocks MMP2_CLK_GPIO>;
resets = <&soc_clocks MMP2_CLK_GPIO>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
ranges;
gcb0: gpio@d4019000 {
diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
index f1d6de8..000bf16 100644
--- a/arch/arm/boot/dts/omap2.dtsi
+++ b/arch/arm/boot/dts/omap2.dtsi
@@ -114,7 +114,7 @@
dma-names = "tx", "rx";
};
- mcspi1: mcspi@48098000 {
+ mcspi1: spi@48098000 {
compatible = "ti,omap2-mcspi";
ti,hwmods = "mcspi1";
reg = <0x48098000 0x100>;
@@ -125,7 +125,7 @@
"tx2", "rx2", "tx3", "rx3";
};
- mcspi2: mcspi@4809a000 {
+ mcspi2: spi@4809a000 {
compatible = "ti,omap2-mcspi";
ti,hwmods = "mcspi2";
reg = <0x4809a000 0x100>;
diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi
index 84635ee..7f57af2 100644
--- a/arch/arm/boot/dts/omap2430.dtsi
+++ b/arch/arm/boot/dts/omap2430.dtsi
@@ -285,7 +285,7 @@
ti,timer-alwon;
};
- mcspi3: mcspi@480b8000 {
+ mcspi3: spi@480b8000 {
compatible = "ti,omap2-mcspi";
ti,hwmods = "mcspi3";
reg = <0x480b8000 0x100>;
diff --git a/arch/arm/boot/dts/omap3-gta04.dtsi b/arch/arm/boot/dts/omap3-gta04.dtsi
index ac830b9..0c39a23 100644
--- a/arch/arm/boot/dts/omap3-gta04.dtsi
+++ b/arch/arm/boot/dts/omap3-gta04.dtsi
@@ -28,6 +28,7 @@
aliases {
display0 = &lcd;
+ display1 = &tv0;
};
/* fixed 26MHz oscillator */
@@ -78,7 +79,7 @@
#sound-dai-cells = <0>;
};
- spi_lcd {
+ spi_lcd: spi_lcd {
compatible = "spi-gpio";
#address-cells = <0x1>;
#size-cells = <0x0>;
@@ -131,7 +132,7 @@
};
tv0: connector {
- compatible = "svideo-connector";
+ compatible = "composite-video-connector";
label = "tv";
port {
@@ -143,7 +144,7 @@
tv_amp: opa362 {
compatible = "ti,opa362";
- enable-gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; /* GPIO_23 to enable video out amplifier */
ports {
#address-cells = <1>;
@@ -282,6 +283,13 @@
OMAP3_CORE1_IOPAD(0x2134, PIN_INPUT_PULLUP | MUX_MODE4) /* gpio112 */
>;
};
+
+ penirq_pins: pinmux_penirq_pins {
+ pinctrl-single,pins = <
+ /* here we could enable to wakeup the cpu from suspend by a pen touch */
+ OMAP3_CORE1_IOPAD(0x2194, PIN_INPUT_PULLUP | MUX_MODE4) /* gpio160 */
+ >;
+ };
};
&omap3_pmx_core2 {
@@ -422,10 +430,19 @@
tsc2007@48 {
compatible = "ti,tsc2007";
reg = <0x48>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&penirq_pins>;
interrupt-parent = <&gpio6>;
interrupts = <0 IRQ_TYPE_EDGE_FALLING>; /* GPIO_160 */
- gpios = <&gpio6 0 GPIO_ACTIVE_LOW>;
+ gpios = <&gpio6 0 GPIO_ACTIVE_LOW>; /* GPIO_160 */
ti,x-plate-ohms = <600>;
+ touchscreen-size-x = <480>;
+ touchscreen-size-y = <640>;
+ touchscreen-max-pressure = <1000>;
+ touchscreen-fuzz-x = <3>;
+ touchscreen-fuzz-y = <8>;
+ touchscreen-fuzz-pressure = <10>;
+ touchscreen-inverted-y;
};
/* RFID EEPROM */
@@ -531,6 +548,12 @@
regulator-max-microvolt = <3150000>;
};
+/* Needed to power the DPI pins */
+
+&vpll2 {
+ regulator-always-on;
+};
+
&dss {
pinctrl-names = "default";
pinctrl-0 = < &dss_dpi_pins >;
@@ -551,10 +574,14 @@
vdda-supply = <&vdac>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
port {
+ reg = <0>;
venc_out: endpoint {
remote-endpoint = <&opa_in>;
- ti,channels = <2>;
+ ti,channels = <1>;
ti,invert-polarity;
};
};
@@ -598,22 +625,22 @@
bootloaders@80000 {
label = "U-Boot";
- reg = <0x80000 0x1e0000>;
+ reg = <0x80000 0x1c0000>;
};
- bootloaders_env@260000 {
+ bootloaders_env@240000 {
label = "U-Boot Env";
- reg = <0x260000 0x20000>;
+ reg = <0x240000 0x40000>;
};
kernel@280000 {
label = "Kernel";
- reg = <0x280000 0x400000>;
+ reg = <0x280000 0x600000>;
};
- filesystem@680000 {
+ filesystem@880000 {
label = "File System";
- reg = <0x680000 0xf980000>;
+ reg = <0x880000 0>; /* 0 = MTDPART_SIZ_FULL */
};
};
};
diff --git a/arch/arm/boot/dts/omap3-n9.dts b/arch/arm/boot/dts/omap3-n9.dts
index ded5fcf..1f91646 100644
--- a/arch/arm/boot/dts/omap3-n9.dts
+++ b/arch/arm/boot/dts/omap3-n9.dts
@@ -40,7 +40,7 @@
};
&i2c3 {
- ak8975@0f {
+ ak8975@f {
compatible = "asahi-kasei,ak8975";
reg = <0x0f>;
};
diff --git a/arch/arm/boot/dts/omap3-pandora-common.dtsi b/arch/arm/boot/dts/omap3-pandora-common.dtsi
index 90c98f9..a51081d 100644
--- a/arch/arm/boot/dts/omap3-pandora-common.dtsi
+++ b/arch/arm/boot/dts/omap3-pandora-common.dtsi
@@ -229,6 +229,17 @@
gpio = <&gpio6 4 GPIO_ACTIVE_HIGH>; /* GPIO_164 */
};
+ /* wl1251 wifi+bt module */
+ wlan_en: fixed-regulator-wg7210_en {
+ compatible = "regulator-fixed";
+ regulator-name = "vwlan";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ startup-delay-us = <50000>;
+ enable-active-high;
+ gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
+ };
+
/* wg7210 (wifi+bt module) 32k clock buffer */
wg7210_32k: fixed-regulator-wg7210_32k {
compatible = "regulator-fixed";
@@ -525,9 +536,30 @@
/*wp-gpios = <&gpio4 31 GPIO_ACTIVE_HIGH>;*/ /* GPIO_127 */
};
-/* mmc3 is probed using pdata-quirks to pass wl1251 card data */
&mmc3 {
- status = "disabled";
+ vmmc-supply = <&wlan_en>;
+
+ bus-width = <4>;
+ non-removable;
+ ti,non-removable;
+ cap-power-off-card;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc3_pins>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ wlan: wifi@1 {
+ compatible = "ti,wl1251";
+
+ reg = <1>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; /* GPIO_21 */
+
+ ti,wl1251-has-eeprom;
+ };
};
/* bluetooth*/
diff --git a/arch/arm/boot/dts/omap3-tao3530.dtsi b/arch/arm/boot/dts/omap3-tao3530.dtsi
index 6f5bd02..7b4ec2c 100644
--- a/arch/arm/boot/dts/omap3-tao3530.dtsi
+++ b/arch/arm/boot/dts/omap3-tao3530.dtsi
@@ -225,7 +225,7 @@
pinctrl-0 = <&mmc1_pins>;
vmmc-supply = <&vmmc1>;
vqmmc-supply = <&vsim>;
- cd-gpios = <&twl_gpio 0 GPIO_ACTIVE_HIGH>;
+ cd-gpios = <&twl_gpio 0 GPIO_ACTIVE_LOW>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap4-l4.dtsi b/arch/arm/boot/dts/omap4-l4.dtsi
index 6eb26b8..5059eca 100644
--- a/arch/arm/boot/dts/omap4-l4.dtsi
+++ b/arch/arm/boot/dts/omap4-l4.dtsi
@@ -196,12 +196,12 @@
clock-names = "fck";
#address-cells = <1>;
#size-cells = <1>;
- ranges = <0x0 0x58000 0x4000>;
+ ranges = <0x0 0x58000 0x5000>;
hsi: hsi@0 {
compatible = "ti,omap4-hsi";
reg = <0x0 0x4000>,
- <0x4a05c000 0x1000>;
+ <0x5000 0x1000>;
reg-names = "sys", "gdd";
clocks = <&l3_init_clkctrl OMAP4_HSI_CLKCTRL 0>;
diff --git a/arch/arm/boot/dts/omap5-board-common.dtsi b/arch/arm/boot/dts/omap5-board-common.dtsi
index 8b8db9d..61a06f6 100644
--- a/arch/arm/boot/dts/omap5-board-common.dtsi
+++ b/arch/arm/boot/dts/omap5-board-common.dtsi
@@ -703,6 +703,11 @@
vbus-supply = <&smps10_out1_reg>;
};
+&dwc3 {
+ extcon = <&extcon_usb3>;
+ dr_mode = "otg";
+};
+
&mcspi1 {
};
diff --git a/arch/arm/boot/dts/orion5x-linkstation.dtsi b/arch/arm/boot/dts/orion5x-linkstation.dtsi
index ebd93df..b6c9b85 100644
--- a/arch/arm/boot/dts/orion5x-linkstation.dtsi
+++ b/arch/arm/boot/dts/orion5x-linkstation.dtsi
@@ -156,7 +156,7 @@
&i2c {
status = "okay";
- rtc {
+ rtc@32 {
compatible = "ricoh,rs5c372a";
reg = <0x32>;
};
diff --git a/arch/arm/boot/dts/pxa25x.dtsi b/arch/arm/boot/dts/pxa25x.dtsi
index 95d59be..8494b57 100644
--- a/arch/arm/boot/dts/pxa25x.dtsi
+++ b/arch/arm/boot/dts/pxa25x.dtsi
@@ -80,6 +80,10 @@
#pwm-cells = <1>;
clocks = <&clks CLK_PWM1>;
};
+
+ rtc@40900000 {
+ clocks = <&clks CLK_OSC32k768>;
+ };
};
timer@40a00000 {
diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi
index 747f750f..ccbecad 100644
--- a/arch/arm/boot/dts/pxa27x.dtsi
+++ b/arch/arm/boot/dts/pxa27x.dtsi
@@ -35,7 +35,7 @@
clocks = <&clks CLK_NONE>;
};
- pxa27x_ohci: usb@4c000000 {
+ usb0: usb@4c000000 {
compatible = "marvell,pxa-ohci";
reg = <0x4c000000 0x10000>;
interrupts = <3>;
@@ -71,7 +71,7 @@
clocks = <&clks CLK_PWM1>;
};
- pwri2c: i2c@40f000180 {
+ pwri2c: i2c@40f00180 {
compatible = "mrvl,pxa-i2c";
reg = <0x40f00180 0x24>;
interrupts = <6>;
@@ -113,6 +113,10 @@
status = "disabled";
};
+
+ rtc@40900000 {
+ clocks = <&clks CLK_OSC32k768>;
+ };
};
clocks {
diff --git a/arch/arm/boot/dts/pxa2xx.dtsi b/arch/arm/boot/dts/pxa2xx.dtsi
index a520b4c..0a0e837 100644
--- a/arch/arm/boot/dts/pxa2xx.dtsi
+++ b/arch/arm/boot/dts/pxa2xx.dtsi
@@ -117,13 +117,6 @@
status = "disabled";
};
- usb0: ohci@4c000000 {
- compatible = "marvell,pxa-ohci";
- reg = <0x4c000000 0x10000>;
- interrupts = <3>;
- status = "disabled";
- };
-
mmc0: mmc@41100000 {
compatible = "marvell,pxa-mmc";
reg = <0x41100000 0x1000>;
diff --git a/arch/arm/boot/dts/pxa3xx.dtsi b/arch/arm/boot/dts/pxa3xx.dtsi
index 3a8f0ed..53009db 100644
--- a/arch/arm/boot/dts/pxa3xx.dtsi
+++ b/arch/arm/boot/dts/pxa3xx.dtsi
@@ -204,7 +204,7 @@
status = "disabled";
};
- pxa3xx_ohci: usb@4c000000 {
+ usb0: usb@4c000000 {
compatible = "marvell,pxa-ohci";
reg = <0x4c000000 0x10000>;
interrupts = <3>;
diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
index 54d056b..8328ad5 100644
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
@@ -313,7 +313,7 @@
saw0: regulator@b089000 {
compatible = "qcom,saw2";
- reg = <0x02089000 0x1000>, <0x0b009000 0x1000>;
+ reg = <0x0b089000 0x1000>, <0x0b009000 0x1000>;
regulator;
};
diff --git a/arch/arm/boot/dts/r8a7779.dtsi b/arch/arm/boot/dts/r8a7779.dtsi
index 6b997bc..0391971 100644
--- a/arch/arm/boot/dts/r8a7779.dtsi
+++ b/arch/arm/boot/dts/r8a7779.dtsi
@@ -344,7 +344,7 @@
sata: sata@fc600000 {
compatible = "renesas,sata-r8a7779", "renesas,rcar-sata";
- reg = <0xfc600000 0x2000>;
+ reg = <0xfc600000 0x200000>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp1_clks R8A7779_CLK_SATA>;
power-domains = <&sysc R8A7779_PD_ALWAYS_ON>;
diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts
index 50312e7..7b9508e 100644
--- a/arch/arm/boot/dts/r8a7790-lager.dts
+++ b/arch/arm/boot/dts/r8a7790-lager.dts
@@ -489,8 +489,6 @@
};
&lvds1 {
- status = "okay";
-
ports {
port@1 {
lvds_connector: endpoint {
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index 0925bdc..52a757f 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1559,7 +1559,7 @@
sata0: sata@ee300000 {
compatible = "renesas,sata-r8a7790",
"renesas,rcar-gen2-sata";
- reg = <0 0xee300000 0 0x2000>;
+ reg = <0 0xee300000 0 0x200000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 815>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
@@ -1570,7 +1570,7 @@
sata1: sata@ee500000 {
compatible = "renesas,sata-r8a7790",
"renesas,rcar-gen2-sata";
- reg = <0 0xee500000 0 0x2000>;
+ reg = <0 0xee500000 0 0x200000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 814>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index ce22db0..e6580aa 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -479,8 +479,6 @@
};
&lvds0 {
- status = "okay";
-
ports {
port@1 {
lvds_connector: endpoint {
diff --git a/arch/arm/boot/dts/r8a7791-porter.dts b/arch/arm/boot/dts/r8a7791-porter.dts
index f02036e..fefdf82 100644
--- a/arch/arm/boot/dts/r8a7791-porter.dts
+++ b/arch/arm/boot/dts/r8a7791-porter.dts
@@ -482,8 +482,6 @@
};
&lvds0 {
- status = "okay";
-
ports {
port@1 {
lvds_connector: endpoint {
diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi
index 991ac6fe..25b6a99 100644
--- a/arch/arm/boot/dts/r8a7791.dtsi
+++ b/arch/arm/boot/dts/r8a7791.dtsi
@@ -1543,7 +1543,7 @@
sata0: sata@ee300000 {
compatible = "renesas,sata-r8a7791",
"renesas,rcar-gen2-sata";
- reg = <0 0xee300000 0 0x2000>;
+ reg = <0 0xee300000 0 0x200000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 815>;
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
@@ -1554,7 +1554,7 @@
sata1: sata@ee500000 {
compatible = "renesas,sata-r8a7791",
"renesas,rcar-gen2-sata";
- reg = <0 0xee500000 0 0x2000>;
+ reg = <0 0xee500000 0 0x200000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 814>;
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 67f5720..d560fc4 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -733,7 +733,7 @@
/* no rts / cts for uart2 */
};
- spi {
+ spi-pins {
spi_txd:spi-txd {
rockchip,pins = <1 29 RK_FUNC_3 &pcfg_pull_default>;
};
diff --git a/arch/arm/boot/dts/rk3188-radxarock.dts b/arch/arm/boot/dts/rk3188-radxarock.dts
index 45fd2b3..4a28906 100644
--- a/arch/arm/boot/dts/rk3188-radxarock.dts
+++ b/arch/arm/boot/dts/rk3188-radxarock.dts
@@ -93,6 +93,8 @@
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc_pwr>;
startup-delay-us = <100000>;
vin-supply = <&vcc_io>;
};
@@ -315,6 +317,12 @@
};
};
+ sd0 {
+ sdmmc_pwr: sdmmc-pwr {
+ rockchip,pins = <RK_GPIO3 1 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
usb {
host_vbus_drv: host-vbus-drv {
rockchip,pins = <0 3 RK_FUNC_GPIO &pcfg_pull_none>;
diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 5032548..32e1ab3 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -25,7 +25,7 @@
vcc_flash: flash-regulator {
compatible = "regulator-fixed";
- regulator-name = "vcc_sys";
+ regulator-name = "vcc_flash";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
startup-delay-us = <150>;
diff --git a/arch/arm/boot/dts/rv1108.dtsi b/arch/arm/boot/dts/rv1108.dtsi
index ed8f6ca..a9f053d 100644
--- a/arch/arm/boot/dts/rv1108.dtsi
+++ b/arch/arm/boot/dts/rv1108.dtsi
@@ -66,7 +66,7 @@
arm-pmu {
compatible = "arm,cortex-a7-pmu";
- interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
};
timer {
@@ -541,7 +541,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x20030000 0x100>;
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&xin24m>;
+ clocks = <&cru PCLK_GPIO0_PMU>;
gpio-controller;
#gpio-cells = <2>;
@@ -554,7 +554,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x10310000 0x100>;
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&xin24m>;
+ clocks = <&cru PCLK_GPIO1>;
gpio-controller;
#gpio-cells = <2>;
@@ -567,7 +567,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x10320000 0x100>;
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&xin24m>;
+ clocks = <&cru PCLK_GPIO2>;
gpio-controller;
#gpio-cells = <2>;
@@ -580,7 +580,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x10330000 0x100>;
interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&xin24m>;
+ clocks = <&cru PCLK_GPIO3>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/arch/arm/boot/dts/s3c6410-mini6410.dts b/arch/arm/boot/dts/s3c6410-mini6410.dts
index 0e159c8..1aeac33 100644
--- a/arch/arm/boot/dts/s3c6410-mini6410.dts
+++ b/arch/arm/boot/dts/s3c6410-mini6410.dts
@@ -165,6 +165,10 @@
};
};
+&clocks {
+ clocks = <&fin_pll>;
+};
+
&sdhci0 {
pinctrl-names = "default";
pinctrl-0 = <&sd0_clk>, <&sd0_cmd>, <&sd0_cd>, <&sd0_bus4>;
diff --git a/arch/arm/boot/dts/s3c6410-smdk6410.dts b/arch/arm/boot/dts/s3c6410-smdk6410.dts
index a9a5689..3bf6c45 100644
--- a/arch/arm/boot/dts/s3c6410-smdk6410.dts
+++ b/arch/arm/boot/dts/s3c6410-smdk6410.dts
@@ -69,6 +69,10 @@
};
};
+&clocks {
+ clocks = <&fin_pll>;
+};
+
&sdhci0 {
pinctrl-names = "default";
pinctrl-0 = <&sd0_clk>, <&sd0_cmd>, <&sd0_cd>, <&sd0_bus4>;
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dts
index b280e649..31b01a9 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dts
+++ b/arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dts
@@ -88,7 +88,7 @@
status = "okay";
clock-frequency = <100000>;
- adxl345: adxl345@0 {
+ adxl345: adxl345@53 {
compatible = "adi,adxl345";
reg = <0x53>;
diff --git a/arch/arm/boot/dts/ste-dbx5x0.dtsi b/arch/arm/boot/dts/ste-dbx5x0.dtsi
index 2310a4e..9867677 100644
--- a/arch/arm/boot/dts/ste-dbx5x0.dtsi
+++ b/arch/arm/boot/dts/ste-dbx5x0.dtsi
@@ -197,7 +197,7 @@
<0xa0410100 0x100>;
};
- scu@a04100000 {
+ scu@a0410000 {
compatible = "arm,cortex-a9-scu";
reg = <0xa0410000 0x100>;
};
@@ -878,7 +878,7 @@
power-domains = <&pm_domains DOMAIN_VAPE>;
};
- ssp@80002000 {
+ spi@80002000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x80002000 0x1000>;
interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
@@ -892,7 +892,7 @@
power-domains = <&pm_domains DOMAIN_VAPE>;
};
- ssp@80003000 {
+ spi@80003000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x80003000 0x1000>;
interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi b/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi
index 5c5cea2..1ec193b 100644
--- a/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi
+++ b/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi
@@ -607,16 +607,20 @@
mcde {
lcd_default_mode: lcd_default {
- default_mux {
+ default_mux1 {
/* Mux in VSI0 and all the data lines */
function = "lcd";
groups =
"lcdvsi0_a_1", /* VSI0 for LCD */
"lcd_d0_d7_a_1", /* Data lines */
"lcd_d8_d11_a_1", /* TV-out */
- "lcdaclk_b_1", /* Clock line for TV-out */
"lcdvsi1_a_1"; /* VSI1 for HDMI */
};
+ default_mux2 {
+ function = "lcda";
+ groups =
+ "lcdaclk_b_1"; /* Clock line for TV-out */
+ };
default_cfg1 {
pins =
"GPIO68_E1", /* VSI0 */
diff --git a/arch/arm/boot/dts/ste-hrefprev60.dtsi b/arch/arm/boot/dts/ste-hrefprev60.dtsi
index 3f14b4d..94eeb7f 100644
--- a/arch/arm/boot/dts/ste-hrefprev60.dtsi
+++ b/arch/arm/boot/dts/ste-hrefprev60.dtsi
@@ -57,7 +57,7 @@
};
};
- ssp@80002000 {
+ spi@80002000 {
/*
* On the first generation boards, this SSP/SPI port was connected
* to the AB8500.
diff --git a/arch/arm/boot/dts/ste-snowball.dts b/arch/arm/boot/dts/ste-snowball.dts
index b0b94d0..6038904 100644
--- a/arch/arm/boot/dts/ste-snowball.dts
+++ b/arch/arm/boot/dts/ste-snowball.dts
@@ -376,7 +376,7 @@
pinctrl-1 = <&i2c3_sleep_mode>;
};
- ssp@80002000 {
+ spi@80002000 {
pinctrl-names = "default";
pinctrl-0 = <&ssp0_snowball_mode>;
};
diff --git a/arch/arm/boot/dts/ste-u300.dts b/arch/arm/boot/dts/ste-u300.dts
index 62ecb6a..1bd1aba 100644
--- a/arch/arm/boot/dts/ste-u300.dts
+++ b/arch/arm/boot/dts/ste-u300.dts
@@ -442,7 +442,7 @@
dma-names = "rx";
};
- spi: ssp@c0006000 {
+ spi: spi@c0006000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0xc0006000 0x1000>;
interrupt-parent = <&vica>;
diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts
index 372bc2e..063ee8a 100644
--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts
@@ -6,6 +6,7 @@
/dts-v1/;
#include "stm32mp157c-ed1.dts"
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "STMicroelectronics STM32MP157C eval daughter on eval mother";
@@ -19,6 +20,58 @@
serial0 = &uart4;
ethernet0 = ðernet0;
};
+
+ panel_backlight: panel-backlight {
+ compatible = "gpio-backlight";
+ gpios = <&gpiod 13 GPIO_ACTIVE_LOW>;
+ default-on;
+ status = "okay";
+ };
+};
+
+&cec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&cec_pins_a>;
+ status = "okay";
+};
+
+&dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ dsi_in: endpoint {
+ remote-endpoint = <<dc_ep0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ dsi_out: endpoint {
+ remote-endpoint = <&dsi_panel_in>;
+ };
+ };
+ };
+
+ panel-dsi@0 {
+ compatible = "raydium,rm68200";
+ reg = <0>;
+ reset-gpios = <&gpiof 15 GPIO_ACTIVE_LOW>;
+ backlight = <&panel_backlight>;
+ status = "okay";
+
+ port {
+ dsi_panel_in: endpoint {
+ remote-endpoint = <&dsi_out>;
+ };
+ };
+ };
};
ðernet0 {
@@ -40,12 +93,6 @@
};
};
-&cec {
- pinctrl-names = "default";
- pinctrl-0 = <&cec_pins_a>;
- status = "okay";
-};
-
&i2c2 {
pinctrl-names = "default";
pinctrl-0 = <&i2c2_pins_a>;
@@ -62,6 +109,20 @@
status = "okay";
};
+<dc {
+ status = "okay";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ltdc_ep0_out: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&dsi_in>;
+ };
+ };
+};
+
&m_can1 {
pinctrl-names = "default";
pinctrl-0 = <&m_can1_pins_a>;
diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index 185541a..c50c36b 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -947,7 +947,7 @@
dma-requests = <48>;
};
- qspi: qspi@58003000 {
+ qspi: spi@58003000 {
compatible = "st,stm32f469-qspi";
reg = <0x58003000 0x1000>, <0x70000000 0x10000000>;
reg-names = "qspi", "qspi_mm";
diff --git a/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts b/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts
index 221acd10..2f0d966 100644
--- a/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts
+++ b/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts
@@ -63,8 +63,6 @@
compatible = "gpio-keys-polled";
pinctrl-names = "default";
pinctrl-0 = <&key_pins_inet9f>;
- #address-cells = <1>;
- #size-cells = <0>;
poll-interval = <20>;
left-joystick-left {
diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
index b97a0f2..d82a604 100644
--- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts
+++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
@@ -76,8 +76,6 @@
gpio-keys {
compatible = "gpio-keys";
- #address-cells = <1>;
- #size-cells = <0>;
back {
label = "Key Back";
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 3d62a89..5d46bb0 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -530,8 +530,6 @@
};
hdmi_out: port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
reg = <1>;
};
};
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 316cb8b..a66d9f9 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -104,8 +104,6 @@
};
hdmi_out: port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
reg = <1>;
};
};
diff --git a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
index 8acbaab..d2a2eb8 100644
--- a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
+++ b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
@@ -92,7 +92,8 @@
*/
clock-frequency = <400000>;
- touchscreen: touchscreen {
+ touchscreen: touchscreen@40 {
+ reg = <0x40>;
interrupt-parent = <&pio>;
interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>; /* EINT11 (PG11) */
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index debc0bf2..76924fa 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -201,7 +201,7 @@
};
pmu {
- compatible = "arm,cortex-a7-pmu", "arm,cortex-a15-pmu";
+ compatible = "arm,cortex-a7-pmu";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 9c52712..355619dc 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -183,7 +183,7 @@
};
pmu {
- compatible = "arm,cortex-a7-pmu", "arm,cortex-a15-pmu";
+ compatible = "arm,cortex-a7-pmu";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
};
@@ -639,8 +639,6 @@
};
hdmi_out: port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
reg = <1>;
};
};
diff --git a/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
index 1537ce1..49547a4 100644
--- a/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
+++ b/arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts
@@ -171,6 +171,7 @@
vqmmc-supply = <®_dldo1>;
non-removable;
wakeup-source;
+ keep-power-in-suspend;
status = "okay";
brcmf: wifi@1 {
diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
index 30540dc..bdda0d9 100644
--- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
+++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
@@ -140,7 +140,7 @@
&external_mdio {
ext_rgmii_phy: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
- reg = <0>;
+ reg = <1>;
};
};
diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index f0096074..9233ba3 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -47,19 +47,19 @@
compatible = "operating-points-v2";
opp-shared;
- opp@648000000 {
+ opp-648000000 {
opp-hz = /bits/ 64 <648000000>;
opp-microvolt = <1040000 1040000 1300000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
- opp@816000000 {
+ opp-816000000 {
opp-hz = /bits/ 64 <816000000>;
opp-microvolt = <1100000 1100000 1300000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
- opp@1008000000 {
+ opp-1008000000 {
opp-hz = /bits/ 64 <1008000000>;
opp-microvolt = <1200000 1200000 1300000>;
clock-latency-ns = <244144>; /* 8 32k periods */
@@ -122,7 +122,7 @@
soc {
system-control@1c00000 {
compatible = "allwinner,sun8i-h3-system-control";
- reg = <0x01c00000 0x30>;
+ reg = <0x01c00000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
diff --git a/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts b/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts
index 0dbdb29..ee7ce37 100644
--- a/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts
+++ b/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts
@@ -103,13 +103,13 @@
};
&cpu0_opp_table {
- opp@1104000000 {
+ opp-1104000000 {
opp-hz = /bits/ 64 <1104000000>;
opp-microvolt = <1320000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
- opp@1200000000 {
+ opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1320000>;
clock-latency-ns = <244144>; /* 8 32k periods */
diff --git a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
index 880096c7..5e8a95a 100644
--- a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
+++ b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
@@ -69,7 +69,8 @@
*/
clock-frequency = <400000>;
- touchscreen: touchscreen@0 {
+ touchscreen: touchscreen@40 {
+ reg = <0x40>;
interrupt-parent = <&pio>;
interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>; /* PB5 */
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
index 387fc2a..333df90 100644
--- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
+++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
@@ -78,7 +78,7 @@
};
&mmc0 {
- pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-0 = <&mmc0_pins>;
pinctrl-names = "default";
broken-cd;
bus-width = <4>;
@@ -87,7 +87,7 @@
};
&uart0 {
- pinctrl-0 = <&uart0_pins_a>;
+ pinctrl-0 = <&uart0_pb_pins>;
pinctrl-names = "default";
status = "okay";
};
diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 443b083..92fcb75 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -292,17 +292,17 @@
interrupt-controller;
#interrupt-cells = <3>;
- i2c0_pins: i2c0 {
+ i2c0_pins: i2c0-pins {
pins = "PB6", "PB7";
function = "i2c0";
};
- uart0_pins_a: uart0@0 {
+ uart0_pb_pins: uart0-pb-pins {
pins = "PB8", "PB9";
function = "uart0";
};
- mmc0_pins_a: mmc0@0 {
+ mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3",
"PF4", "PF5";
function = "mmc0";
@@ -310,7 +310,7 @@
bias-pull-up;
};
- mmc1_pins: mmc1 {
+ mmc1_pins: mmc1-pins {
pins = "PG0", "PG1", "PG2", "PG3",
"PG4", "PG5";
function = "mmc1";
@@ -318,7 +318,7 @@
bias-pull-up;
};
- spi0_pins: spi0 {
+ spi0_pins: spi0-pins {
pins = "PC0", "PC1", "PC2", "PC3";
function = "spi0";
};
diff --git a/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts b/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts
index 35859d8..bf97f62 100644
--- a/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts
+++ b/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts
@@ -95,7 +95,7 @@
&i2c0 {
status = "okay";
- axp22x: pmic@68 {
+ axp22x: pmic@34 {
compatible = "x-powers,axp221";
reg = <0x34>;
interrupt-parent = <&nmi_intc>;
diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi
index 25591d6..d9532fb 100644
--- a/arch/arm/boot/dts/sun9i-a80.dtsi
+++ b/arch/arm/boot/dts/sun9i-a80.dtsi
@@ -1196,7 +1196,7 @@
};
};
- r_rsb: i2c@8003400 {
+ r_rsb: rsb@8003400 {
compatible = "allwinner,sun8i-a23-rsb";
reg = <0x08003400 0x400>;
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index fc613131..4b1530e 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -816,7 +816,7 @@
clock-names = "apb", "ir";
resets = <&r_ccu RST_APB0_IR>;
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
- reg = <0x01f02000 0x40>;
+ reg = <0x01f02000 0x400>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index ef24529..4f9b4a8 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -524,10 +524,10 @@
gpio-keys {
compatible = "gpio-keys";
- power {
- label = "Power";
+ wakeup {
+ label = "Wakeup";
gpios = <&gpio TEGRA_GPIO(J, 7) GPIO_ACTIVE_LOW>;
- linux,code = <KEY_POWER>;
+ linux,code = <KEY_WAKEUP>;
wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 15b73bd..80854f7d 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -419,19 +419,6 @@
status = "disabled";
};
- gmi@70009000 {
- compatible = "nvidia,tegra20-gmi";
- reg = <0x70009000 0x1000>;
- #address-cells = <2>;
- #size-cells = <1>;
- ranges = <0 0 0xd0000000 0xfffffff>;
- clocks = <&tegra_car TEGRA20_CLK_NOR>;
- clock-names = "gmi";
- resets = <&tegra_car 42>;
- reset-names = "gmi";
- status = "disabled";
- };
-
nand-controller@70008000 {
compatible = "nvidia,tegra20-nand";
reg = <0x70008000 0x100>;
@@ -447,6 +434,19 @@
status = "disabled";
};
+ gmi@70009000 {
+ compatible = "nvidia,tegra20-gmi";
+ reg = <0x70009000 0x1000>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0xd0000000 0xfffffff>;
+ clocks = <&tegra_car TEGRA20_CLK_NOR>;
+ clock-names = "gmi";
+ resets = <&tegra_car 42>;
+ reset-names = "gmi";
+ status = "disabled";
+ };
+
pwm: pwm@7000a000 {
compatible = "nvidia,tegra20-pwm";
reg = <0x7000a000 0x100>;
diff --git a/arch/arm/boot/dts/tegra30-apalis.dtsi b/arch/arm/boot/dts/tegra30-apalis.dtsi
index 2f807d4..f810bbf 100644
--- a/arch/arm/boot/dts/tegra30-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra30-apalis.dtsi
@@ -171,14 +171,14 @@
/* Apalis MMC1 */
sdmmc3_clk_pa6 {
- nvidia,pins = "sdmmc3_clk_pa6",
- "sdmmc3_cmd_pa7";
+ nvidia,pins = "sdmmc3_clk_pa6";
nvidia,function = "sdmmc3";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
sdmmc3_dat0_pb7 {
- nvidia,pins = "sdmmc3_dat0_pb7",
+ nvidia,pins = "sdmmc3_cmd_pa7",
+ "sdmmc3_dat0_pb7",
"sdmmc3_dat1_pb6",
"sdmmc3_dat2_pb5",
"sdmmc3_dat3_pb4",
@@ -659,7 +659,7 @@
reg = <1>;
clocks = <&clk16m>;
interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_EDGE_RISING>;
+ interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <10000000>;
};
};
@@ -674,7 +674,7 @@
reg = <0>;
clocks = <&clk16m>;
interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(W, 2) IRQ_TYPE_EDGE_RISING>;
+ interrupts = <TEGRA_GPIO(W, 2) IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <10000000>;
};
};
diff --git a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts
index 16e1f38..a0c550e 100644
--- a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts
+++ b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts
@@ -79,7 +79,8 @@
reg = <0>;
clocks = <&clk16m>;
interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(S, 0) IRQ_TYPE_EDGE_RISING>;
+ /* CAN_INT */
+ interrupts = <TEGRA_GPIO(S, 0) IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <10000000>;
};
spidev0: spi@1 {
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index a6781f6..5a04dde 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -896,7 +896,7 @@
nvidia,elastic-limit = <16>;
nvidia,term-range-adj = <6>;
nvidia,xcvr-setup = <51>;
- nvidia.xcvr-setup-use-fuses;
+ nvidia,xcvr-setup-use-fuses;
nvidia,xcvr-lsfslew = <1>;
nvidia,xcvr-lsrslew = <1>;
nvidia,xcvr-hsslew = <32>;
@@ -933,7 +933,7 @@
nvidia,elastic-limit = <16>;
nvidia,term-range-adj = <6>;
nvidia,xcvr-setup = <51>;
- nvidia.xcvr-setup-use-fuses;
+ nvidia,xcvr-setup-use-fuses;
nvidia,xcvr-lsfslew = <2>;
nvidia,xcvr-lsrslew = <2>;
nvidia,xcvr-hsslew = <32>;
@@ -969,7 +969,7 @@
nvidia,elastic-limit = <16>;
nvidia,term-range-adj = <6>;
nvidia,xcvr-setup = <51>;
- nvidia.xcvr-setup-use-fuses;
+ nvidia,xcvr-setup-use-fuses;
nvidia,xcvr-lsfslew = <2>;
nvidia,xcvr-lsrslew = <2>;
nvidia,xcvr-hsslew = <32>;
diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
index 5f61d36..6f4f60b 100644
--- a/arch/arm/boot/dts/versatile-ab.dts
+++ b/arch/arm/boot/dts/versatile-ab.dts
@@ -373,7 +373,7 @@
clock-names = "apb_pclk";
};
- ssp@101f4000 {
+ spi@101f4000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x101f4000 0x1000>;
interrupts = <11>;
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts
index cc5a3dc..27cd6cb 100644
--- a/arch/arm/boot/dts/zynq-zc702.dts
+++ b/arch/arm/boot/dts/zynq-zc702.dts
@@ -174,17 +174,17 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <7>;
- hwmon@52 {
+ hwmon@34 {
compatible = "ti,ucd9248";
- reg = <52>;
+ reg = <0x34>;
};
- hwmon@53 {
+ hwmon@35 {
compatible = "ti,ucd9248";
- reg = <53>;
+ reg = <0x35>;
};
- hwmon@54 {
+ hwmon@36 {
compatible = "ti,ucd9248";
- reg = <54>;
+ reg = <0x36>;
};
};
};
diff --git a/arch/arm/boot/dts/zynq-zc770-xm010.dts b/arch/arm/boot/dts/zynq-zc770-xm010.dts
index 0e1bfdd..0dd3522 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm010.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm010.dts
@@ -68,7 +68,7 @@
status = "okay";
num-cs = <4>;
is-decoded-cs = <0>;
- flash@0 {
+ flash@1 {
compatible = "sst25wf080", "jedec,spi-nor";
reg = <1>;
spi-max-frequency = <1000000>;
diff --git a/arch/arm/boot/dts/zynq-zc770-xm013.dts b/arch/arm/boot/dts/zynq-zc770-xm013.dts
index 651913f..4ae2c85 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm013.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm013.dts
@@ -62,7 +62,7 @@
status = "okay";
num-cs = <4>;
is-decoded-cs = <0>;
- eeprom: eeprom@0 {
+ eeprom: eeprom@2 {
at25,byte-len = <8192>;
at25,addr-mode = <2>;
at25,page-size = <32>;
diff --git a/arch/arm/configs/vendor/bengal-perf_defconfig b/arch/arm/configs/vendor/bengal-perf_defconfig
index 599b59b..2d28360 100644
--- a/arch/arm/configs/vendor/bengal-perf_defconfig
+++ b/arch/arm/configs/vendor/bengal-perf_defconfig
@@ -1,6 +1,5 @@
CONFIG_LOCALVERSION="-perf"
# CONFIG_LOCALVERSION_AUTO is not set
-CONFIG_POSIX_MQUEUE=y
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
@@ -9,7 +8,6 @@
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_PSI=y
@@ -19,8 +17,6 @@
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_MEMCG=y
-CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_FREEZER=y
@@ -33,12 +29,10 @@
# CONFIG_PID_NS is not set
CONFIG_SCHED_AUTOGROUP=y
CONFIG_SCHED_TUNE=y
-CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_FHANDLE is not set
CONFIG_KALLSYMS_ALL=y
CONFIG_BPF_SYSCALL=y
@@ -50,8 +44,8 @@
CONFIG_PROFILING=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_BENGAL=y
+CONFIG_ARCH_SCUBA=y
# CONFIG_VDSO is not set
-CONFIG_PCI=y
CONFIG_SMP=y
CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=8
@@ -59,7 +53,7 @@
CONFIG_HIGHMEM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
-CONFIG_EFI=y
+CONFIG_CPU_FREQ_TIMES=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
@@ -67,7 +61,6 @@
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_ARM_QCOM_CPUFREQ_HW=y
CONFIG_CPU_IDLE=y
-CONFIG_ARM_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_KERNEL_MODE_NEON=y
@@ -215,11 +208,6 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
-CONFIG_BRIDGE_EBT_T_FILTER=y
-CONFIG_BRIDGE_EBT_T_NAT=y
-CONFIG_BRIDGE_EBT_ARPREPLY=y
-CONFIG_BRIDGE_EBT_DNAT=y
-CONFIG_BRIDGE_EBT_SNAT=y
CONFIG_IP_SCTP=y
CONFIG_L2TP=y
CONFIG_L2TP_V3=y
@@ -255,20 +243,14 @@
CONFIG_MSM_BT_POWER=y
CONFIG_BTFM_SLIM_WCN3990=y
CONFIG_CFG80211=y
-CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
CONFIG_DEVTMPFS=y
-CONFIG_DEVTMPFS_MOUNT=y
CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
CONFIG_REGMAP_WCD_IRQ=y
CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
-CONFIG_MTD=y
-CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_NAND=y
-CONFIG_MTD_UBI=y
CONFIG_ZRAM=y
CONFIG_ZRAM_DEDUP=y
CONFIG_BLK_DEV_LOOP=y
@@ -300,7 +282,6 @@
CONFIG_TUN=y
CONFIG_RMNET=y
CONFIG_PHYLIB=y
-CONFIG_AT803X_PHY=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -317,50 +298,41 @@
CONFIG_CLD_LL_CORE=y
CONFIG_CNSS_GENL=y
CONFIG_INPUT_EVDEV=y
-CONFIG_INPUT_KEYRESET=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_XPAD=y
-CONFIG_INPUT_TABLET=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_INPUT_TOUCHSCREEN=y
-CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
-CONFIG_INPUT_GPIO=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVMEM is not set
-CONFIG_SERIAL_MSM=y
-CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_MSM_GENI=y
CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING=y
-CONFIG_HVC_DCC=y
-CONFIG_HVC_DCC_SERIALIZE_SMP=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_DIAG_CHAR=y
CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QCOM_GENI=y
CONFIG_SPI=y
-CONFIG_SPI_DEBUG=y
-CONFIG_SPI_QUP=y
CONFIG_SPI_QCOM_GENI=y
CONFIG_SPI_SPIDEV=y
CONFIG_SPMI=y
-CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
-CONFIG_PTP_1588_CLOCK=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_PINCTRL_BENGAL=y
+CONFIG_PINCTRL_SCUBA=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_POWER_RESET_SYSCON=y
-CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_SMB5=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
@@ -386,7 +358,7 @@
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
-CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
CONFIG_REGULATOR_FAN53555=y
CONFIG_REGULATOR_QCOM_SMD_RPM=y
CONFIG_REGULATOR_QPNP_LCDB=y
@@ -395,22 +367,16 @@
CONFIG_REGULATOR_PM8008=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
-CONFIG_MEDIA_RADIO_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_VIDEO_ADV_DEBUG=y
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_SOC_CAMERA=y
-CONFIG_SOC_CAMERA_PLATFORM=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=y
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
-CONFIG_FB_VIRTUAL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
@@ -418,10 +384,10 @@
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_PCI is not set
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_USB_AUDIO_QMI=y
CONFIG_SND_SOC=y
-CONFIG_HIDRAW=y
CONFIG_UHID=y
CONFIG_HID_APPLE=y
CONFIG_HID_ELECOM=y
@@ -429,20 +395,15 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_PLANTRONICS=y
+CONFIG_HID_SONY=y
CONFIG_USB_HIDDEV=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
CONFIG_USB_DWC3_MSM=y
-CONFIG_USB_ISP1760=y
-CONFIG_USB_ISP1760_HOST_ROLE=y
-CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_NOP_USB_XCEIV=y
@@ -467,17 +428,18 @@
CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
+CONFIG_TYPEC=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
-CONFIG_MMC_IPC_LOGGING=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QTI_FLASH=y
CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -511,20 +473,17 @@
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_MAILBOX=y
CONFIG_QCOM_APCS_IPC=y
-CONFIG_MSM_QMP=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
-CONFIG_RPMSG_QCOM_SMD=y
CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_COMMAND_DB=y
-CONFIG_QCOM_CPUSS_DUMP=y
+CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_QCOM_MDT_LOADER=y
CONFIG_QPNP_PBS=y
CONFIG_QCOM_QMI_HELPERS=y
@@ -576,7 +535,6 @@
CONFIG_ARM_QCOM_DEVFREQ_FW=y
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
-CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_QCOM_SPMI_ADC5=y
CONFIG_PWM=y
@@ -586,13 +544,12 @@
CONFIG_RAS=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+# CONFIG_NVMEM_SYSFS is not set
CONFIG_QCOM_QFPROM=y
CONFIG_NVMEM_SPMI_SDAM=y
CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_QCOM_KGSL=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_FS_SECURITY=y
@@ -606,15 +563,9 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_EFIVAR_FS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
-CONFIG_UBIFS_FS=y
-CONFIG_UBIFS_FS_ADVANCED_COMPR=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PFK=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
@@ -635,17 +586,14 @@
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
-CONFIG_XZ_DEC=y
-CONFIG_STACK_HASH_ORDER_SHIFT=12
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_FRAME_WARN=2048
-CONFIG_PAGE_OWNER=y
# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
-CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=-1
CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_PREEMPT is not set
CONFIG_IPC_LOGGING=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
diff --git a/arch/arm/configs/vendor/bengal_defconfig b/arch/arm/configs/vendor/bengal_defconfig
index aa8db93..c47b22e 100644
--- a/arch/arm/configs/vendor/bengal_defconfig
+++ b/arch/arm/configs/vendor/bengal_defconfig
@@ -18,8 +18,6 @@
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_MEMCG=y
-CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_DEBUG_BLK_CGROUP=y
CONFIG_RT_GROUP_SCHED=y
@@ -49,8 +47,8 @@
CONFIG_PROFILING=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_BENGAL=y
+CONFIG_ARCH_SCUBA=y
# CONFIG_VDSO is not set
-CONFIG_PCI=y
CONFIG_SMP=y
CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=8
@@ -59,6 +57,7 @@
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_EFI=y
+CONFIG_CPU_FREQ_TIMES=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
@@ -66,7 +65,6 @@
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_ARM_QCOM_CPUFREQ_HW=y
CONFIG_CPU_IDLE=y
-CONFIG_ARM_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_KERNEL_MODE_NEON=y
@@ -259,6 +257,7 @@
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_FW_LOADER_USER_HELPER=y
@@ -266,11 +265,6 @@
CONFIG_REGMAP_WCD_IRQ=y
CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
-CONFIG_MTD=y
-CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_NAND=y
-CONFIG_MTD_UBI=y
CONFIG_ZRAM=y
CONFIG_ZRAM_DEDUP=y
CONFIG_BLK_DEV_LOOP=y
@@ -326,9 +320,12 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_INPUT_TABLET=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
@@ -343,8 +340,10 @@
CONFIG_TTY_PRINTK=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_DIAG_CHAR=y
CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QCOM_GENI=y
CONFIG_SPI=y
@@ -357,12 +356,12 @@
CONFIG_PTP_1588_CLOCK=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_PINCTRL_BENGAL=y
+CONFIG_PINCTRL_SCUBA=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_POWER_RESET_SYSCON=y
-CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_SMB5=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
@@ -405,12 +404,9 @@
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_PLATFORM=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=y
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
-CONFIG_FB_VIRTUAL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_GENERIC=m
@@ -420,6 +416,7 @@
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_PCI is not set
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_USB_AUDIO_QMI=y
CONFIG_SND_SOC=y
@@ -431,19 +428,15 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_PLANTRONICS=y
+CONFIG_HID_SONY=y
CONFIG_USB_HIDDEV=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
CONFIG_USB_DWC3_MSM=y
-CONFIG_USB_ISP1760=y
-CONFIG_USB_ISP1760_HOST_ROLE=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
@@ -469,10 +462,10 @@
CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
+CONFIG_TYPEC=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_IPC_LOGGING=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -480,6 +473,8 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QTI_FLASH=y
CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -515,22 +510,20 @@
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_MAILBOX=y
CONFIG_QCOM_APCS_IPC=y
-CONFIG_MSM_QMP=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
CONFIG_IOMMU_TLBSYNC_DEBUG=y
CONFIG_ARM_SMMU_TESTBUS_DUMP=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
-CONFIG_RPMSG_QCOM_SMD=y
CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_CPUSS_DUMP=y
+CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_QCOM_MDT_LOADER=y
CONFIG_QPNP_PBS=y
CONFIG_QCOM_QMI_HELPERS=y
@@ -602,12 +595,11 @@
CONFIG_NVMEM_SPMI_SDAM=y
CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_QCOM_KGSL=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_CHECK_FS=y
CONFIG_FS_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
@@ -619,11 +611,7 @@
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_EFIVAR_FS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
-CONFIG_UBIFS_FS=y
-CONFIG_UBIFS_FS_ADVANCED_COMPR=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
diff --git a/arch/arm/crypto/crc32-ce-glue.c b/arch/arm/crypto/crc32-ce-glue.c
index 96e62ec..cd9e93b 100644
--- a/arch/arm/crypto/crc32-ce-glue.c
+++ b/arch/arm/crypto/crc32-ce-glue.c
@@ -236,7 +236,7 @@ static void __exit crc32_pmull_mod_exit(void)
ARRAY_SIZE(crc32_pmull_algs));
}
-static const struct cpu_feature crc32_cpu_feature[] = {
+static const struct cpu_feature __maybe_unused crc32_cpu_feature[] = {
{ cpu_feature(CRC32) }, { cpu_feature(PMULL) }, { }
};
MODULE_DEVICE_TABLE(cpu, crc32_cpu_feature);
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 523c499..02e0425 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -366,6 +366,11 @@ static inline int hyp_map_aux_data(void)
#define kvm_phys_to_vttbr(addr) (addr)
+static inline void kvm_workaround_1542418_vmid_rollover(void)
+{
+ /* not affected */
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h
index a00288d7..3c534a5 100644
--- a/arch/arm/include/asm/traps.h
+++ b/arch/arm/include/asm/traps.h
@@ -28,6 +28,8 @@ static inline int __in_irqentry_text(unsigned long ptr)
ptr < (unsigned long)&__irqentry_text_end;
}
+extern void get_timer_freq_hook_init(void);
+extern void get_timer_count_hook_init(void);
extern void __init early_trap_init(void *);
extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index c136eef..6390a40 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -349,6 +349,13 @@ do { \
#define __get_user_asm_byte(x, addr, err) \
__get_user_asm(x, addr, err, ldrb)
+#if __LINUX_ARM_ARCH__ >= 6
+
+#define __get_user_asm_half(x, addr, err) \
+ __get_user_asm(x, addr, err, ldrh)
+
+#else
+
#ifndef __ARMEB__
#define __get_user_asm_half(x, __gu_addr, err) \
({ \
@@ -367,6 +374,8 @@ do { \
})
#endif
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
#define __get_user_asm_word(x, addr, err) \
__get_user_asm(x, addr, err, ldr)
#endif
@@ -442,6 +451,13 @@ do { \
#define __put_user_asm_byte(x, __pu_addr, err) \
__put_user_asm(x, __pu_addr, err, strb)
+#if __LINUX_ARM_ARCH__ >= 6
+
+#define __put_user_asm_half(x, __pu_addr, err) \
+ __put_user_asm(x, __pu_addr, err, strh)
+
+#else
+
#ifndef __ARMEB__
#define __put_user_asm_half(x, __pu_addr, err) \
({ \
@@ -458,6 +474,8 @@ do { \
})
#endif
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
#define __put_user_asm_word(x, __pu_addr, err) \
__put_user_asm(x, __pu_addr, err, str)
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 746565a..0465d65 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -296,16 +296,15 @@
cmp scno, #-1 @ skip the syscall?
bne 2b
add sp, sp, #S_OFF @ restore stack
- b ret_slow_syscall
-__sys_trace_return:
- str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
+__sys_trace_return_nosave:
+ enable_irq_notrace
mov r0, sp
bl syscall_trace_exit
b ret_slow_syscall
-__sys_trace_return_nosave:
- enable_irq_notrace
+__sys_trace_return:
+ str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
mov r0, sp
bl syscall_trace_exit
b ret_slow_syscall
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index 997b023..9328f20 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -72,7 +72,7 @@
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
- * r0 = cp#15 control register
+ * r0 = cp#15 control register (exc_ret for M-class)
* r1 = machine ID
* r2 = atags/dtb pointer
* r9 = processor ID
@@ -141,7 +141,8 @@
#ifdef CONFIG_CPU_CP15
.long cr_alignment @ r3
#else
- .long 0 @ r3
+M_CLASS(.long exc_ret) @ r3
+AR_CLASS(.long 0) @ r3
#endif
.size __mmap_switched_data, . - __mmap_switched_data
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
index cab8947..326a97a 100644
--- a/arch/arm/kernel/head-nommu.S
+++ b/arch/arm/kernel/head-nommu.S
@@ -205,6 +205,8 @@
bic r0, r0, #V7M_SCB_CCR_IC
#endif
str r0, [r12, V7M_SCB_CCR]
+ /* Pass exc_ret to __mmap_switched */
+ mov r0, r10
#endif /* CONFIG_CPU_CP15 elif CONFIG_CPU_V7M */
ret lr
ENDPROC(__after_proc_init)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index a172ff5..d20a4e4 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -620,7 +620,7 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs)
raw_spin_unlock(&stop_lock);
}
- set_cpu_online(cpu, false);
+ set_cpu_active(cpu, false);
local_fiq_disable();
local_irq_disable();
@@ -743,10 +743,10 @@ void smp_send_stop(void)
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
- while (num_online_cpus() > 1 && timeout--)
+ while (num_active_cpus() > 1 && timeout--)
udelay(1);
- if (num_online_cpus() > 1)
+ if (num_active_cpus() > 1)
pr_warn("SMP: failed to stop secondary CPUs\n");
}
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index badf02c..027bbd5 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -31,6 +31,7 @@
#include <linux/irq.h>
#include <linux/atomic.h>
+#include <asm/arch_timer.h>
#include <asm/cacheflush.h>
#include <asm/exception.h>
#include <asm/unistd.h>
@@ -736,6 +737,77 @@ late_initcall(arm_mrc_hook_init);
#endif
+static int get_timer_count_trap(struct pt_regs *regs, unsigned int instr)
+{
+ u64 cval;
+ unsigned int res;
+ int rd = (instr >> 12) & 0xF;
+ int rn = (instr >> 16) & 0xF;
+
+ res = arm_check_condition(instr, regs->ARM_cpsr);
+ if (res == ARM_OPCODE_CONDTEST_FAIL) {
+ regs->ARM_pc += 4;
+ return 0;
+ }
+
+ if (rd == 15 || rn == 15)
+ return 1;
+ cval = arch_counter_get_cntvct();
+ regs->uregs[rd] = cval;
+ regs->uregs[rn] = cval >> 32;
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+static struct undef_hook get_timer_count_hook = {
+ .instr_mask = 0x0ff00fff,
+ .instr_val = 0x0c500f1e,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = get_timer_count_trap,
+};
+
+void get_timer_count_hook_init(void)
+{
+ register_undef_hook(&get_timer_count_hook);
+}
+EXPORT_SYMBOL(get_timer_count_hook_init);
+
+static int get_freq_trap(struct pt_regs *regs, unsigned int instr)
+{
+ u32 fval;
+ unsigned int res;
+ int rd = (instr >> 12) & 0xF;
+
+ res = arm_check_condition(instr, regs->ARM_cpsr);
+ if (res == ARM_OPCODE_CONDTEST_FAIL) {
+ regs->ARM_pc += 4;
+ return 0;
+ }
+
+ if (rd == 15)
+ return 1;
+
+ fval = arch_timer_get_cntfrq();
+ regs->uregs[rd] = fval;
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+static struct undef_hook get_freq_hook = {
+ .instr_mask = 0x0fff0fff,
+ .instr_val = 0x0e1e0f10,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = get_freq_trap,
+};
+
+void get_timer_freq_hook_init(void)
+{
+ register_undef_hook(&get_freq_hook);
+}
+EXPORT_SYMBOL(get_timer_freq_hook_init);
+
/*
* A data abort trap was taken, but we did not handle the instruction.
* Try to abort the user program, or panic if it was the kernel.
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index 746e780..b2e4bc3a 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -42,6 +42,12 @@
ENTRY(__get_user_2)
check_uaccess r0, 2, r1, r2, __get_user_bad
+#if __LINUX_ARM_ARCH__ >= 6
+
+2: TUSER(ldrh) r2, [r0]
+
+#else
+
#ifdef CONFIG_CPU_USE_DOMAINS
rb .req ip
2: ldrbt r2, [r0], #1
@@ -56,6 +62,9 @@
#else
orr r2, rb, r2, lsl #8
#endif
+
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
mov r0, #0
ret lr
ENDPROC(__get_user_2)
@@ -145,7 +154,9 @@
.pushsection __ex_table, "a"
.long 1b, __get_user_bad
.long 2b, __get_user_bad
+#if __LINUX_ARM_ARCH__ < 6
.long 3b, __get_user_bad
+#endif
.long 4b, __get_user_bad
.long 5b, __get_user_bad8
.long 6b, __get_user_bad8
diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S
index 38d660d..515eeaa 100644
--- a/arch/arm/lib/putuser.S
+++ b/arch/arm/lib/putuser.S
@@ -41,16 +41,13 @@
ENTRY(__put_user_2)
check_uaccess r0, 2, r1, ip, __put_user_bad
- mov ip, r2, lsr #8
-#ifdef CONFIG_THUMB2_KERNEL
-#ifndef __ARMEB__
-2: TUSER(strb) r2, [r0]
-3: TUSER(strb) ip, [r0, #1]
+#if __LINUX_ARM_ARCH__ >= 6
+
+2: TUSER(strh) r2, [r0]
+
#else
-2: TUSER(strb) ip, [r0]
-3: TUSER(strb) r2, [r0, #1]
-#endif
-#else /* !CONFIG_THUMB2_KERNEL */
+
+ mov ip, r2, lsr #8
#ifndef __ARMEB__
2: TUSER(strb) r2, [r0], #1
3: TUSER(strb) ip, [r0]
@@ -58,7 +55,8 @@
2: TUSER(strb) ip, [r0], #1
3: TUSER(strb) r2, [r0]
#endif
-#endif /* CONFIG_THUMB2_KERNEL */
+
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
mov r0, #0
ret lr
ENDPROC(__put_user_2)
@@ -91,7 +89,9 @@
.pushsection __ex_table, "a"
.long 1b, __put_user_bad
.long 2b, __put_user_bad
+#if __LINUX_ARM_ARCH__ < 6
.long 3b, __put_user_bad
+#endif
.long 4b, __put_user_bad
.long 5b, __put_user_bad
.long 6b, __put_user_bad
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 0921e2c..e2e4df3 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -143,15 +143,15 @@ static int at91_pm_config_ws(unsigned int pm_mode, bool set)
/* Check if enabled on SHDWC. */
if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
- goto put_node;
+ goto put_device;
mode |= wsi->pmc_fsmr_bit;
if (wsi->set_polarity)
polarity |= wsi->pmc_fsmr_bit;
}
-put_node:
- of_node_put(np);
+put_device:
+ put_device(&pdev->dev);
}
if (mode) {
diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index 4266591..83ca89a 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -458,8 +458,8 @@ static s8 dm365_queue_priority_mapping[][2] = {
};
static const struct dma_slave_map dm365_edma_map[] = {
- { "davinci-mcbsp.0", "tx", EDMA_FILTER_PARAM(0, 2) },
- { "davinci-mcbsp.0", "rx", EDMA_FILTER_PARAM(0, 3) },
+ { "davinci-mcbsp", "tx", EDMA_FILTER_PARAM(0, 2) },
+ { "davinci-mcbsp", "rx", EDMA_FILTER_PARAM(0, 3) },
{ "davinci_voicecodec", "tx", EDMA_FILTER_PARAM(0, 2) },
{ "davinci_voicecodec", "rx", EDMA_FILTER_PARAM(0, 3) },
{ "spi_davinci.2", "tx", EDMA_FILTER_PARAM(0, 10) },
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index b08e407..529f4b5 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -618,6 +618,28 @@ static void __init imx6_pm_common_init(const struct imx6_pm_socdata
IMX6Q_GPR1_GINT);
}
+static void imx6_pm_stby_poweroff(void)
+{
+ imx6_set_lpm(STOP_POWER_OFF);
+ imx6q_suspend_finish(0);
+
+ mdelay(1000);
+
+ pr_emerg("Unable to poweroff system\n");
+}
+
+static int imx6_pm_stby_poweroff_probe(void)
+{
+ if (pm_power_off) {
+ pr_warn("%s: pm_power_off already claimed %p %pf!\n",
+ __func__, pm_power_off, pm_power_off);
+ return -EBUSY;
+ }
+
+ pm_power_off = imx6_pm_stby_poweroff;
+ return 0;
+}
+
void __init imx6_pm_ccm_init(const char *ccm_compat)
{
struct device_node *np;
@@ -634,6 +656,9 @@ void __init imx6_pm_ccm_init(const char *ccm_compat)
val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
writel_relaxed(val, ccm_base + CLPCR);
+
+ if (of_property_read_bool(np, "fsl,pmic-stby-poweroff"))
+ imx6_pm_stby_poweroff_probe();
}
void __init imx6q_pm_init(void)
diff --git a/arch/arm/mach-ks8695/board-acs5k.c b/arch/arm/mach-ks8695/board-acs5k.c
index ef835d8..5783062 100644
--- a/arch/arm/mach-ks8695/board-acs5k.c
+++ b/arch/arm/mach-ks8695/board-acs5k.c
@@ -100,7 +100,7 @@ static struct i2c_board_info acs5k_i2c_devs[] __initdata = {
},
};
-static void acs5k_i2c_init(void)
+static void __init acs5k_i2c_init(void)
{
/* The gpio interface */
gpiod_add_lookup_table(&acs5k_i2c_gpiod_table);
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile
index e8ccf51..ec02358 100644
--- a/arch/arm/mach-omap1/Makefile
+++ b/arch/arm/mach-omap1/Makefile
@@ -25,7 +25,7 @@
led-y := leds.o
-usb-fs-$(CONFIG_USB) := usb.o
+usb-fs-$(CONFIG_USB_SUPPORT) := usb.o
obj-y += $(usb-fs-m) $(usb-fs-y)
# Specific board support
diff --git a/arch/arm/mach-omap1/id.c b/arch/arm/mach-omap1/id.c
index 52de382..7e49dfd 100644
--- a/arch/arm/mach-omap1/id.c
+++ b/arch/arm/mach-omap1/id.c
@@ -200,10 +200,10 @@ void __init omap_check_revision(void)
printk(KERN_INFO "Unknown OMAP cpu type: 0x%02x\n", cpu_type);
}
- printk(KERN_INFO "OMAP%04x", omap_revision >> 16);
+ pr_info("OMAP%04x", omap_revision >> 16);
if ((omap_revision >> 8) & 0xff)
- printk(KERN_INFO "%x", (omap_revision >> 8) & 0xff);
- printk(KERN_INFO " revision %i handled as %02xxx id: %08x%08x\n",
+ pr_cont("%x", (omap_revision >> 8) & 0xff);
+ pr_cont(" revision %i handled as %02xxx id: %08x%08x\n",
die_rev, omap_revision & 0xff, system_serial_low,
system_serial_high);
}
diff --git a/arch/arm/mach-omap1/include/mach/usb.h b/arch/arm/mach-omap1/include/mach/usb.h
index 77867778..5429d86 100644
--- a/arch/arm/mach-omap1/include/mach/usb.h
+++ b/arch/arm/mach-omap1/include/mach/usb.h
@@ -11,7 +11,7 @@
#include <linux/platform_data/usb-omap1.h>
-#if IS_ENABLED(CONFIG_USB)
+#if IS_ENABLED(CONFIG_USB_SUPPORT)
void omap1_usb_init(struct omap_usb_config *pdata);
#else
static inline void omap1_usb_init(struct omap_usb_config *pdata)
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index 68ba5f4..859c71c 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -199,8 +199,8 @@ void __init omap2xxx_check_revision(void)
pr_info("%s", soc_name);
if ((omap_rev() >> 8) & 0x0f)
- pr_info("%s", soc_rev);
- pr_info("\n");
+ pr_cont("%s", soc_rev);
+ pr_cont("\n");
}
#define OMAP3_SHOW_FEATURE(feat) \
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 7f02743..dae7262 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -305,108 +305,15 @@ static void __init omap3_logicpd_torpedo_init(void)
}
/* omap3pandora legacy devices */
-#define PANDORA_WIFI_IRQ_GPIO 21
-#define PANDORA_WIFI_NRESET_GPIO 23
static struct platform_device pandora_backlight = {
.name = "pandora-backlight",
.id = -1,
};
-static struct regulator_consumer_supply pandora_vmmc3_supply[] = {
- REGULATOR_SUPPLY("vmmc", "omap_hsmmc.2"),
-};
-
-static struct regulator_init_data pandora_vmmc3 = {
- .constraints = {
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = ARRAY_SIZE(pandora_vmmc3_supply),
- .consumer_supplies = pandora_vmmc3_supply,
-};
-
-static struct fixed_voltage_config pandora_vwlan = {
- .supply_name = "vwlan",
- .microvolts = 1800000, /* 1.8V */
- .gpio = PANDORA_WIFI_NRESET_GPIO,
- .startup_delay = 50000, /* 50ms */
- .enable_high = 1,
- .init_data = &pandora_vmmc3,
-};
-
-static struct platform_device pandora_vwlan_device = {
- .name = "reg-fixed-voltage",
- .id = 1,
- .dev = {
- .platform_data = &pandora_vwlan,
- },
-};
-
-static void pandora_wl1251_init_card(struct mmc_card *card)
-{
- /*
- * We have TI wl1251 attached to MMC3. Pass this information to
- * SDIO core because it can't be probed by normal methods.
- */
- if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
- card->quirks |= MMC_QUIRK_NONSTD_SDIO;
- card->cccr.wide_bus = 1;
- card->cis.vendor = 0x104c;
- card->cis.device = 0x9066;
- card->cis.blksize = 512;
- card->cis.max_dtr = 24000000;
- card->ocr = 0x80;
- }
-}
-
-static struct omap2_hsmmc_info pandora_mmc3[] = {
- {
- .mmc = 3,
- .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
- .gpio_cd = -EINVAL,
- .gpio_wp = -EINVAL,
- .init_card = pandora_wl1251_init_card,
- },
- {} /* Terminator */
-};
-
-static void __init pandora_wl1251_init(void)
-{
- struct wl1251_platform_data pandora_wl1251_pdata;
- int ret;
-
- memset(&pandora_wl1251_pdata, 0, sizeof(pandora_wl1251_pdata));
-
- pandora_wl1251_pdata.power_gpio = -1;
-
- ret = gpio_request_one(PANDORA_WIFI_IRQ_GPIO, GPIOF_IN, "wl1251 irq");
- if (ret < 0)
- goto fail;
-
- pandora_wl1251_pdata.irq = gpio_to_irq(PANDORA_WIFI_IRQ_GPIO);
- if (pandora_wl1251_pdata.irq < 0)
- goto fail_irq;
-
- pandora_wl1251_pdata.use_eeprom = true;
- ret = wl1251_set_platform_data(&pandora_wl1251_pdata);
- if (ret < 0)
- goto fail_irq;
-
- return;
-
-fail_irq:
- gpio_free(PANDORA_WIFI_IRQ_GPIO);
-fail:
- pr_err("wl1251 board initialisation failed\n");
-}
-
static void __init omap3_pandora_legacy_init(void)
{
platform_device_register(&pandora_backlight);
- platform_device_register(&pandora_vwlan_device);
- omap_hsmmc_init(pandora_mmc3);
- omap_hsmmc_late_init(pandora_mmc3);
- pandora_wl1251_init();
}
#endif /* CONFIG_ARCH_OMAP3 */
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 7bb6192..c108485 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -1,56 +1,92 @@
-menuconfig ARCH_QCOM
- bool "Qualcomm Support"
- depends on ARCH_MULTI_V7
- select ARCH_SUPPORTS_BIG_ENDIAN
- select ARM_GIC
- select ARM_AMBA
- select PINCTRL
- select MFD_CORE
- select SND_SOC_COMPRESS
- select SND_HWDEP
- select QCOM_SCM if SMP
- help
- Support for Qualcomm's devicetree based systems.
-
if ARCH_QCOM
+menu "QCOM SoC Type"
config ARCH_MSM8X60
bool "Enable support for MSM8X60"
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
select CLKSRC_QCOM
+ select CLKSRC_OF
+ select COMMON_CLK
config ARCH_MSM8960
bool "Enable support for MSM8960"
select CLKSRC_QCOM
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
+
config ARCH_MSM8974
bool "Enable support for MSM8974"
select HAVE_ARM_ARCH_TIMER
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
config ARCH_MDM9615
bool "Enable support for MDM9615"
select CLKSRC_QCOM
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
config ARCH_BENGAL
bool "Enable Support for Qualcomm Technologies, Inc. BENGAL"
select COMMON_CLK_QCOM
- select CPU_V7
+ select ARCH_SUPPORT_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
select HAVE_CLK
select HAVE_CLK_PREPARE
- select PM_OPP
- select SOC_BUS
select THERMAL_WRITABLE_TRIPS
select ARM_GIC_V3
- select ARM_AMBA
- select SPARSE_IRQ
- select MULTI_IRQ_HANDLER
select HAVE_ARM_ARCH_TIMER
- select COMMON_CLK
select PINCTRL_MSM
select MSM_PM if PM
select CPU_FREQ
select PM_DEVFREQ
+ select PCI
help
This enables support for the BENGAL chipset. If you do not
wish to build a kernel that runs on this chipset, say 'N' here.
+config ARCH_SCUBA
+ bool "Enable Support for Qualcomm Technologies, Inc. SCUBA"
+ select COMMON_CLK_QCOM
+ select ARCH_SUPPORT_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
+ select HAVE_CLK
+ select HAVE_CLK_PREPARE
+ select THERMAL_WRITABLE_TRIPS
+ select ARM_GIC_V3
+ select HAVE_ARM_ARCH_TIMER
+ select PINCTRL_MSM
+ select MSM_PM if PM
+ select CPU_FREQ
+ select PM_DEVFREQ
+ select PCI
+ help
+ This enables support for the SCUBA chipset. If you do not
+ wish to build a kernel that runs on this chipset, say 'N' here.
+
+endmenu
endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index 685e7b8..f6658a2 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_USE_OF) += board-dt.o
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_ARCH_BENGAL) += board-bengal.o
+obj-$(CONFIG_ARCH_SCUBA) += board-scuba.o
diff --git a/arch/arm/mach-qcom/Makefile.boot b/arch/arm/mach-qcom/Makefile.boot
new file mode 100644
index 0000000..4c0a039
--- /dev/null
+++ b/arch/arm/mach-qcom/Makefile.boot
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Empty file waiting for deletion once Makefile.boot isn't needed any more.
diff --git a/arch/arm/mach-qcom/board-scuba.c b/arch/arm/mach-qcom/board-scuba.c
new file mode 100644
index 0000000..137765a
--- /dev/null
+++ b/arch/arm/mach-qcom/board-scuba.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include "board-dt.h"
+
+static const char *scuba_dt_match[] __initconst = {
+ "qcom,scuba",
+ NULL
+};
+
+static void __init scuba_init(void)
+{
+ board_dt_populate(NULL);
+}
+
+DT_MACHINE_START(BENGAL,
+ "Qualcomm Technologies, Inc. SCUBA (Flattened Device Tree)")
+ .init_machine = scuba_init,
+ .dt_compat = scuba_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-sunxi/mc_smp.c b/arch/arm/mach-sunxi/mc_smp.c
index b4037b6..ff173e6 100644
--- a/arch/arm/mach-sunxi/mc_smp.c
+++ b/arch/arm/mach-sunxi/mc_smp.c
@@ -478,14 +478,18 @@ static void sunxi_mc_smp_cpu_die(unsigned int l_cpu)
static int sunxi_cpu_powerdown(unsigned int cpu, unsigned int cluster)
{
u32 reg;
+ int gating_bit = cpu;
pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu);
if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS)
return -EINVAL;
+ if (is_a83t && cpu == 0)
+ gating_bit = 4;
+
/* gate processor power */
reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
- reg |= PRCM_PWROFF_GATING_REG_CORE(cpu);
+ reg |= PRCM_PWROFF_GATING_REG_CORE(gating_bit);
writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
udelay(20);
diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S
index 805f306..e31f167a 100644
--- a/arch/arm/mach-tegra/reset-handler.S
+++ b/arch/arm/mach-tegra/reset-handler.S
@@ -56,16 +56,16 @@
cmp r6, #TEGRA20
beq 1f @ Yes
/* Clear the flow controller flags for this CPU. */
- cpu_to_csr_reg r1, r0
+ cpu_to_csr_reg r3, r0
mov32 r2, TEGRA_FLOW_CTRL_BASE
- ldr r1, [r2, r1]
+ ldr r1, [r2, r3]
/* Clear event & intr flag */
orr r1, r1, \
#FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
movw r0, #0x3FFD @ enable, cluster_switch, immed, bitmaps
@ & ext flags for CPU power mgnt
bic r1, r1, r0
- str r1, [r2]
+ str r1, [r2, r3]
1:
mov32 r9, 0xc09
diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c
index 0f5381d..55bbbc3 100644
--- a/arch/arm/mach-vexpress/spc.c
+++ b/arch/arm/mach-vexpress/spc.c
@@ -551,8 +551,9 @@ static struct clk *ve_spc_clk_register(struct device *cpu_dev)
static int __init ve_spc_clk_init(void)
{
- int cpu;
+ int cpu, cluster;
struct clk *clk;
+ bool init_opp_table[MAX_CLUSTERS] = { false };
if (!info)
return 0; /* Continue only if SPC is initialised */
@@ -578,8 +579,17 @@ static int __init ve_spc_clk_init(void)
continue;
}
+ cluster = topology_physical_package_id(cpu_dev->id);
+ if (init_opp_table[cluster])
+ continue;
+
if (ve_init_opp_table(cpu_dev))
pr_warn("failed to initialise cpu%d opp table\n", cpu);
+ else if (dev_pm_opp_set_sharing_cpus(cpu_dev,
+ topology_core_cpumask(cpu_dev->id)))
+ pr_warn("failed to mark OPPs shared for cpu%d\n", cpu);
+ else
+ init_opp_table[cluster] = true;
}
platform_device_register_simple("vexpress-spc-cpufreq", -1, NULL, 0);
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index bd2c739..84a6bba 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -768,6 +768,36 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
return NULL;
}
+static int alignment_get_arm(struct pt_regs *regs, u32 *ip, unsigned long *inst)
+{
+ u32 instr = 0;
+ int fault;
+
+ if (user_mode(regs))
+ fault = get_user(instr, ip);
+ else
+ fault = probe_kernel_address(ip, instr);
+
+ *inst = __mem_to_opcode_arm(instr);
+
+ return fault;
+}
+
+static int alignment_get_thumb(struct pt_regs *regs, u16 *ip, u16 *inst)
+{
+ u16 instr = 0;
+ int fault;
+
+ if (user_mode(regs))
+ fault = get_user(instr, ip);
+ else
+ fault = probe_kernel_address(ip, instr);
+
+ *inst = __mem_to_opcode_thumb16(instr);
+
+ return fault;
+}
+
static int
do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
@@ -775,10 +805,10 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
unsigned long instr = 0, instrptr;
int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
unsigned int type;
- unsigned int fault;
u16 tinstr = 0;
int isize = 4;
int thumb2_32b = 0;
+ int fault;
if (interrupts_enabled(regs))
local_irq_enable();
@@ -787,15 +817,14 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
if (thumb_mode(regs)) {
u16 *ptr = (u16 *)(instrptr & ~1);
- fault = probe_kernel_address(ptr, tinstr);
- tinstr = __mem_to_opcode_thumb16(tinstr);
+
+ fault = alignment_get_thumb(regs, ptr, &tinstr);
if (!fault) {
if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
IS_T32(tinstr)) {
/* Thumb-2 32-bit */
- u16 tinst2 = 0;
- fault = probe_kernel_address(ptr + 1, tinst2);
- tinst2 = __mem_to_opcode_thumb16(tinst2);
+ u16 tinst2;
+ fault = alignment_get_thumb(regs, ptr + 1, &tinst2);
instr = __opcode_thumb32_compose(tinstr, tinst2);
thumb2_32b = 1;
} else {
@@ -804,8 +833,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
}
}
} else {
- fault = probe_kernel_address((void *)instrptr, instr);
- instr = __mem_to_opcode_arm(instr);
+ fault = alignment_get_arm(regs, (void *)instrptr, &instr);
}
if (fault) {
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7c41669..92c3199 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1811,7 +1811,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
int ret = 0;
unsigned int count;
struct scatterlist *s;
- int prot;
+ int prot = 0;
size = PAGE_ALIGN(size);
*handle = ARM_MAPPING_ERROR;
@@ -1820,6 +1820,11 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
if (iova == ARM_MAPPING_ERROR)
return -ENOMEM;
+ /*
+ * Check for coherency.
+ */
+ prot |= is_coherent ? IOMMU_CACHE : 0;
+
for (count = 0, s = sg; count < (size >> PAGE_SHIFT); s = sg_next(s)) {
phys_addr_t phys = page_to_phys(sg_page(s));
unsigned int len = PAGE_ALIGN(s->offset + s->length);
@@ -1827,7 +1832,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
- prot = __dma_info_to_prot(dir, attrs);
+ prot |= __dma_info_to_prot(dir, attrs);
ret = iommu_map(mapping->domain, iova, phys, len, prot);
if (ret < 0)
@@ -1931,9 +1936,23 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
dma_addr_t iova;
int prot = __dma_info_to_prot(dir, attrs);
bool coherent;
+ /*
+ * This is used to check if there are any unaligned offset/size
+ * given in the scatter list.
+ */
+ bool unaligned_offset_size = false;
- for_each_sg(sg, s, nents, i)
+ for_each_sg(sg, s, nents, i) {
total_length += s->length;
+ if ((s->offset & ~PAGE_MASK) || (s->length & ~PAGE_MASK)) {
+ unaligned_offset_size = true;
+ break;
+ }
+ }
+
+ if (unaligned_offset_size)
+ return __iommu_map_sg(dev, sg, nents, dir, attrs,
+ is_dma_coherent(dev, attrs, false));
iova = __alloc_iova(mapping, total_length);
if (iova == ARM_MAPPING_ERROR)
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 047ee14..9bef174 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -1195,6 +1195,9 @@ void __init adjust_lowmem_bounds(void)
phys_addr_t block_start = reg->base;
phys_addr_t block_end = reg->base + reg->size;
+ if (memblock_is_nomap(reg))
+ continue;
+
if (reg->base < vmalloc_limit) {
if (block_end > lowmem_limit)
/*
diff --git a/arch/arm/mm/proc-v7-bugs.c b/arch/arm/mm/proc-v7-bugs.c
index 9a07916..a6554fd 100644
--- a/arch/arm/mm/proc-v7-bugs.c
+++ b/arch/arm/mm/proc-v7-bugs.c
@@ -65,6 +65,9 @@ static void cpu_v7_spectre_init(void)
break;
#ifdef CONFIG_ARM_PSCI
+ case ARM_CPU_PART_BRAHMA_B53:
+ /* Requires no workaround */
+ break;
default:
/* Other ARM CPUs require no workaround */
if (read_cpuid_implementor() == ARM_CPU_IMP_ARM)
diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S
index 92e8418..9c2978c 100644
--- a/arch/arm/mm/proc-v7m.S
+++ b/arch/arm/mm/proc-v7m.S
@@ -135,13 +135,11 @@
dsb
mov r6, lr @ save LR
ldr sp, =init_thread_union + THREAD_START_SP
- stmia sp, {r0-r3, r12}
cpsie i
svc #0
1: cpsid i
- ldr r0, =exc_ret
- orr lr, lr, #EXC_RET_THREADMODE_PROCESSSTACK
- str lr, [r0]
+ /* Calculate exc_ret */
+ orr r10, lr, #EXC_RET_THREADMODE_PROCESSSTACK
ldmia sp, {r0-r3, r12}
str r5, [r12, #11 * 4] @ restore the original SVC vector entry
mov lr, r6 @ restore LR
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a465396..e8c8e63 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -524,6 +524,22 @@
If unsure, say Y.
+config ARM64_ERRATUM_1542418
+ bool "Cortex-A77: The core might fetch a stale instuction, violating the ordering of instruction fetches"
+ default y
+ help
+ This option adds a workaround for Arm Cortex-A77 erratum 1542418.
+
+ On the affected Cortex-A77 cores (r0p0 and r1p0), software relying
+ on the prefetch-speculation-protection instead of explicit
+ synchronisation may fetch a stale instruction from a CPU-specific
+ cache. This violates the ordering rules for instruction fetches.
+
+ Work around the erratum by ensuring that 60 ASIDs are selected
+ before any ASID is reused.
+
+ If unsure, say Y.
+
config CAVIUM_ERRATUM_22375
bool "Cavium erratum 22375, 24313"
default y
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index aed1941..d06b9f6 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -176,6 +176,7 @@
$(Q)$(MAKE) $(clean)=$(boot)
$(Q)$(MAKE) $(clean)=$(boot)/dts
+ifeq ($(KBUILD_EXTMOD),)
# We need to generate vdso-offsets.h before compiling certain files in kernel/.
# In order to do that, we should use the archprepare target, but we can't since
# asm-offsets.h is included in some files used to generate vdso-offsets.h, and
@@ -185,6 +186,7 @@
prepare: vdso_prepare
vdso_prepare: prepare0
$(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h
+endif
define archhelp
echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)'
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
index 98dbff1..5caba22 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
@@ -125,9 +125,9 @@
®_dcdc1 {
regulator-always-on;
- regulator-min-microvolt = <3000000>;
- regulator-max-microvolt = <3000000>;
- regulator-name = "vcc-3v";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-3v3";
};
®_dcdc2 {
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
index 3f53139..b3f1864 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
@@ -142,10 +142,14 @@
/* DCDC3 is polyphased with DCDC2 */
+/*
+ * The board uses DDR3L DRAM chips. 1.36V is the closest to the nominal
+ * 1.35V that the PMIC can drive.
+ */
®_dcdc5 {
regulator-always-on;
- regulator-min-microvolt = <1500000>;
- regulator-max-microvolt = <1500000>;
+ regulator-min-microvolt = <1360000>;
+ regulator-max-microvolt = <1360000>;
regulator-name = "vcc-ddr3";
};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
index 1221764..6670168 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
@@ -67,7 +67,9 @@
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
vmmc-supply = <®_dcdc1>;
- cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
+ disable-wp;
+ bus-width = <4>;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
index 24f1aac..d5b6e81 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
@@ -63,3 +63,12 @@
reg = <1>;
};
};
+
+®_dc1sw {
+ /*
+ * Ethernet PHY needs 30ms to properly power up and some more
+ * to initialize. 100ms should be plenty of time to finish
+ * whole process.
+ */
+ regulator-enable-ramp-delay = <100000>;
+};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts
index c21f233..285cb71 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts
@@ -113,6 +113,12 @@
};
®_dc1sw {
+ /*
+ * Ethernet PHY needs 30ms to properly power up and some more
+ * to initialize. 100ms should be plenty of time to finish
+ * whole process.
+ */
+ regulator-enable-ramp-delay = <100000>;
regulator-name = "vcc-phy";
};
diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
index 7c66175..faa017d 100644
--- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
@@ -124,6 +124,8 @@
&i2c1 {
status = "okay";
clock-frequency = <100000>;
+ i2c-sda-falling-time-ns = <890>; /* hcnt */
+ i2c-sdl-falling-time-ns = <890>; /* lcnt */
adc@14 {
compatible = "lltc,ltc2497";
diff --git a/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi b/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
index 125f4de..b664e7a 100644
--- a/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
+++ b/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
@@ -107,7 +107,7 @@
clock-names = "uartclk", "apb_pclk";
};
- spi0: ssp@e1020000 {
+ spi0: spi@e1020000 {
status = "disabled";
compatible = "arm,pl022", "arm,primecell";
reg = <0 0xe1020000 0 0x1000>;
@@ -117,7 +117,7 @@
clock-names = "apb_pclk";
};
- spi1: ssp@e1030000 {
+ spi1: spi@e1030000 {
status = "disabled";
compatible = "arm,pl022", "arm,primecell";
reg = <0 0xe1030000 0 0x1000>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
index c518130..3c34f14 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
@@ -458,7 +458,7 @@
};
ethmac: ethernet@ff3f0000 {
- compatible = "amlogic,meson-gxbb-dwmac", "snps,dwmac";
+ compatible = "amlogic,meson-axg-dwmac", "snps,dwmac";
reg = <0x0 0xff3f0000 0x0 0x10000
0x0 0xff634540 0x0 0x8>;
interrupts = <GIC_SPI 8 IRQ_TYPE_EDGE_RISING>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
index cbe99bd..8cd50b7 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
@@ -191,7 +191,7 @@
pinctrl-names = "default";
};
-&pinctrl_aobus {
+&gpio_ao {
gpio-line-names = "UART TX", "UART RX", "Power Control", "Power Key In",
"VCCK En", "CON1 Header Pin31",
"I2S Header Pin6", "IR In", "I2S Header Pin7",
@@ -201,7 +201,7 @@
"";
};
-&pinctrl_periphs {
+&gpio {
gpio-line-names = /* Bank GPIOZ */
"Eth MDIO", "Eth MDC", "Eth RGMII RX Clk",
"Eth RX DV", "Eth RX D0", "Eth RX D1", "Eth RX D2",
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 54954b3..09a27e0 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -187,7 +187,7 @@
pinctrl-names = "default";
};
-&pinctrl_aobus {
+&gpio_ao {
gpio-line-names = "UART TX", "UART RX", "VCCK En", "TF 3V3/1V8 En",
"USB HUB nRESET", "USB OTG Power En",
"J7 Header Pin2", "IR In", "J7 Header Pin4",
@@ -197,7 +197,7 @@
"";
};
-&pinctrl_periphs {
+&gpio {
gpio-line-names = /* Bank GPIOZ */
"Eth MDIO", "Eth MDC", "Eth RGMII RX Clk",
"Eth RX DV", "Eth RX D0", "Eth RX D1", "Eth RX D2",
@@ -293,7 +293,7 @@
};
&usb0_phy {
- status = "okay";
+ status = "disabled";
phy-supply = <&usb_otg_pwr>;
};
@@ -303,7 +303,7 @@
};
&usb0 {
- status = "okay";
+ status = "disabled";
};
&usb1 {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index 98cbba6..1ade7e4 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -390,7 +390,7 @@
};
};
- spi_pins: spi {
+ spi_pins: spi-pins {
mux {
groups = "spi_miso",
"spi_mosi",
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
index d32cf38..864ef01 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
@@ -112,7 +112,7 @@
linux,rc-map-name = "rc-geekbox";
};
-&pinctrl_aobus {
+&gpio_ao {
gpio-line-names = "UART TX",
"UART RX",
"Power Key In",
@@ -127,7 +127,7 @@
"";
};
-&pinctrl_periphs {
+&gpio {
gpio-line-names = /* Bank GPIOZ */
"", "", "", "", "", "", "",
"", "", "", "", "", "", "",
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
index f63bceb..b4dfb9a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
@@ -13,7 +13,7 @@
/ {
compatible = "libretech,cc", "amlogic,s905x", "amlogic,meson-gxl";
- model = "Libre Technology CC";
+ model = "Libre Computer Board AML-S905X-CC";
aliases {
serial0 = &uart_AO;
@@ -163,7 +163,7 @@
};
};
-&pinctrl_aobus {
+&gpio_ao {
gpio-line-names = "UART TX",
"UART RX",
"Blue LED",
@@ -178,7 +178,7 @@
"7J1 Header Pin15";
};
-&pinctrl_periphs {
+&gpio {
gpio-line-names = /* Bank GPIOZ */
"", "", "", "", "", "", "",
"", "", "", "", "", "", "",
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index c87a80e..8f0bb3c 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -337,7 +337,7 @@
};
};
- spi_pins: spi {
+ spi_pins: spi-pins {
mux {
groups = "spi_miso",
"spi_mosi",
diff --git a/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi b/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi
index 1a406a7..ea854f6 100644
--- a/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi
+++ b/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi
@@ -639,7 +639,7 @@
status = "disabled";
};
- ssp0: ssp@66180000 {
+ ssp0: spi@66180000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x66180000 0x1000>;
interrupts = <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>;
@@ -650,7 +650,7 @@
status = "disabled";
};
- ssp1: ssp@66190000 {
+ ssp1: spi@66190000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x66190000 0x1000>;
interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi b/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
index bc299c3..a9b92e5 100644
--- a/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
+++ b/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
@@ -138,7 +138,7 @@
&i2c1 {
status = "okay";
- pcf8574: pcf8574@20 {
+ pcf8574: pcf8574@27 {
compatible = "nxp,pcf8574a";
gpio-controller;
#gpio-cells = <2>;
diff --git a/arch/arm64/boot/dts/broadcom/stingray/stingray-pinctrl.dtsi b/arch/arm64/boot/dts/broadcom/stingray/stingray-pinctrl.dtsi
index 8a3a770..56789cc 100644
--- a/arch/arm64/boot/dts/broadcom/stingray/stingray-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/broadcom/stingray/stingray-pinctrl.dtsi
@@ -42,13 +42,14 @@
pinmux: pinmux@14029c {
compatible = "pinctrl-single";
- reg = <0x0014029c 0x250>;
+ reg = <0x0014029c 0x26c>;
#address-cells = <1>;
#size-cells = <1>;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0xf>;
pinctrl-single,gpio-range = <
- &range 0 154 MODE_GPIO
+ &range 0 91 MODE_GPIO
+ &range 95 60 MODE_GPIO
>;
range: gpio-range {
#pinctrl-single,gpio-range-cells = <3>;
diff --git a/arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi b/arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi
index e283480..ff714fc 100644
--- a/arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi
+++ b/arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi
@@ -463,8 +463,7 @@
<&pinmux 108 16 27>,
<&pinmux 135 77 6>,
<&pinmux 141 67 4>,
- <&pinmux 145 149 6>,
- <&pinmux 151 91 4>;
+ <&pinmux 145 149 6>;
};
i2c1: i2c@e0000 {
@@ -521,7 +520,7 @@
status = "disabled";
};
- ssp0: ssp@180000 {
+ ssp0: spi@180000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x00180000 0x1000>;
interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
@@ -533,7 +532,7 @@
status = "disabled";
};
- ssp1: ssp@190000 {
+ ssp1: spi@190000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x00190000 0x1000>;
interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
index 2131f12..6e20415 100644
--- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi
+++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
@@ -18,8 +18,8 @@
/ {
compatible = "samsung,exynos5433";
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
interrupt-parent = <&gic>;
@@ -235,7 +235,7 @@
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
- ranges;
+ ranges = <0x0 0x0 0x0 0x18000000>;
arm_a53_pmu {
compatible = "arm,cortex-a53-pmu", "arm,armv8-pmuv3";
diff --git a/arch/arm64/boot/dts/exynos/exynos7.dtsi b/arch/arm64/boot/dts/exynos/exynos7.dtsi
index 75ad724..31b1a60 100644
--- a/arch/arm64/boot/dts/exynos/exynos7.dtsi
+++ b/arch/arm64/boot/dts/exynos/exynos7.dtsi
@@ -12,8 +12,8 @@
/ {
compatible = "samsung,exynos7";
interrupt-parent = <&gic>;
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
aliases {
pinctrl0 = &pinctrl_alive;
@@ -70,7 +70,7 @@
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
- ranges;
+ ranges = <0 0 0 0x18000000>;
chipid@10000000 {
compatible = "samsung,exynos4210-chipid";
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
index 68ac78c..5da732f 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
@@ -337,7 +337,7 @@
status = "disabled";
};
- dspi: dspi@2100000 {
+ dspi: spi@2100000 {
compatible = "fsl,ls1012a-dspi", "fsl,ls1021a-v1.0-dspi";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
index 7881e3d..b9c0f2d 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
@@ -284,7 +284,7 @@
interrupts = <0 43 0x4>;
};
- qspi: quadspi@1550000 {
+ qspi: spi@1550000 {
compatible = "fsl,ls1043a-qspi", "fsl,ls1021a-qspi";
#address-cells = <1>;
#size-cells = <0>;
@@ -382,7 +382,7 @@
ranges = <0x0 0x5 0x00000000 0x8000000>;
};
- dspi0: dspi@2100000 {
+ dspi0: spi@2100000 {
compatible = "fsl,ls1043a-dspi", "fsl,ls1021a-v1.0-dspi";
#address-cells = <1>;
#size-cells = <0>;
@@ -395,7 +395,7 @@
status = "disabled";
};
- dspi1: dspi@2110000 {
+ dspi1: spi@2110000 {
compatible = "fsl,ls1043a-dspi", "fsl,ls1021a-v1.0-dspi";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
index 440e111..a59b482 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
@@ -57,12 +57,12 @@
reg = <0x4c>;
};
- eeprom@56 {
+ eeprom@52 {
compatible = "atmel,24c512";
reg = <0x52>;
};
- eeprom@57 {
+ eeprom@53 {
compatible = "atmel,24c512";
reg = <0x53>;
};
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index ef83786..de6af45 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -202,7 +202,7 @@
interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
};
- qspi: quadspi@1550000 {
+ qspi: spi@1550000 {
compatible = "fsl,ls1021a-qspi";
#address-cells = <1>;
#size-cells = <0>;
@@ -361,7 +361,7 @@
#thermal-sensor-cells = <1>;
};
- dspi: dspi@2100000 {
+ dspi: spi@2100000 {
compatible = "fsl,ls1021a-v1.0-dspi";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
index 8cb78dd..ebe0cd4 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
@@ -469,7 +469,7 @@
mmu-masters = <&fsl_mc 0x300 0>;
};
- dspi: dspi@2100000 {
+ dspi: spi@2100000 {
status = "disabled";
compatible = "fsl,ls2080a-dspi", "fsl,ls2085a-dspi";
#address-cells = <1>;
@@ -595,7 +595,7 @@
3 0 0x5 0x20000000 0x00010000>;
};
- qspi: quadspi@20c0000 {
+ qspi: spi@20c0000 {
status = "disabled";
compatible = "fsl,ls2080a-qspi", "fsl,ls1021a-qspi";
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/lg/lg1312.dtsi b/arch/arm64/boot/dts/lg/lg1312.dtsi
index 860c8fb..4bde7b6f 100644
--- a/arch/arm64/boot/dts/lg/lg1312.dtsi
+++ b/arch/arm64/boot/dts/lg/lg1312.dtsi
@@ -168,14 +168,14 @@
clock-names = "apb_pclk";
status="disabled";
};
- spi0: ssp@fe800000 {
+ spi0: spi@fe800000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x0 0xfe800000 0x1000>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_bus>;
clock-names = "apb_pclk";
};
- spi1: ssp@fe900000 {
+ spi1: spi@fe900000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x0 0xfe900000 0x1000>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm64/boot/dts/lg/lg1313.dtsi b/arch/arm64/boot/dts/lg/lg1313.dtsi
index 1887af6..16ced1f 100644
--- a/arch/arm64/boot/dts/lg/lg1313.dtsi
+++ b/arch/arm64/boot/dts/lg/lg1313.dtsi
@@ -168,14 +168,14 @@
clock-names = "apb_pclk";
status="disabled";
};
- spi0: ssp@fe800000 {
+ spi0: spi@fe800000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x0 0xfe800000 0x1000>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_bus>;
clock-names = "apb_pclk";
};
- spi1: ssp@fe900000 {
+ spi1: spi@fe900000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x0 0xfe900000 0x1000>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index a4dfcd1..9fc14bb 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -118,7 +118,7 @@
};
gen1_i2c: i2c@3160000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x03160000 0x10000>;
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -143,7 +143,7 @@
};
cam_i2c: i2c@3180000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x03180000 0x10000>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -157,7 +157,7 @@
/* shares pads with dpaux1 */
dp_aux_ch1_i2c: i2c@3190000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x03190000 0x10000>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -171,7 +171,7 @@
/* shares pads with dpaux0 */
dp_aux_ch0_i2c: i2c@31b0000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x031b0000 0x10000>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -184,7 +184,7 @@
};
gen7_i2c: i2c@31c0000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x031c0000 0x10000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -197,7 +197,7 @@
};
gen9_i2c: i2c@31e0000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x031e0000 0x10000>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -264,7 +264,7 @@
};
gen2_i2c: i2c@c240000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x0c240000 0x10000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -277,7 +277,7 @@
};
gen8_i2c: i2c@c250000 {
- compatible = "nvidia,tegra194-i2c", "nvidia,tegra114-i2c";
+ compatible = "nvidia,tegra194-i2c";
reg = <0x0c250000 0x10000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 7398ae8..ccaa555 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -282,6 +282,7 @@
status = "okay";
bus-width = <8>;
non-removable;
+ vqmmc-supply = <&vdd_1v8>;
};
clocks {
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index 9d5a0e6..68af663 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1589,7 +1589,7 @@
regulator-name = "VDD_HDMI_5V0";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
- gpio = <&exp1 12 GPIO_ACTIVE_LOW>;
+ gpio = <&exp1 12 GPIO_ACTIVE_HIGH>;
enable-active-high;
vin-supply = <&vdd_5v0_sys>;
};
diff --git a/arch/arm64/boot/dts/renesas/r8a77965.dtsi b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
index f60f08b..f1dfd174 100644
--- a/arch/arm64/boot/dts/renesas/r8a77965.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
@@ -545,7 +545,7 @@
};
hsusb: usb@e6590000 {
- compatible = "renesas,usbhs-r8a7796",
+ compatible = "renesas,usbhs-r8a77965",
"renesas,rcar-gen3-usbhs";
reg = <0 0xe6590000 0 0x100>;
interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
@@ -634,6 +634,14 @@
resets = <&cpg 219>;
#dma-cells = <1>;
dma-channels = <16>;
+ iommus = <&ipmmu_ds0 0>, <&ipmmu_ds0 1>,
+ <&ipmmu_ds0 2>, <&ipmmu_ds0 3>,
+ <&ipmmu_ds0 4>, <&ipmmu_ds0 5>,
+ <&ipmmu_ds0 6>, <&ipmmu_ds0 7>,
+ <&ipmmu_ds0 8>, <&ipmmu_ds0 9>,
+ <&ipmmu_ds0 10>, <&ipmmu_ds0 11>,
+ <&ipmmu_ds0 12>, <&ipmmu_ds0 13>,
+ <&ipmmu_ds0 14>, <&ipmmu_ds0 15>;
};
dmac1: dma-controller@e7300000 {
@@ -668,6 +676,14 @@
resets = <&cpg 218>;
#dma-cells = <1>;
dma-channels = <16>;
+ iommus = <&ipmmu_ds1 0>, <&ipmmu_ds1 1>,
+ <&ipmmu_ds1 2>, <&ipmmu_ds1 3>,
+ <&ipmmu_ds1 4>, <&ipmmu_ds1 5>,
+ <&ipmmu_ds1 6>, <&ipmmu_ds1 7>,
+ <&ipmmu_ds1 8>, <&ipmmu_ds1 9>,
+ <&ipmmu_ds1 10>, <&ipmmu_ds1 11>,
+ <&ipmmu_ds1 12>, <&ipmmu_ds1 13>,
+ <&ipmmu_ds1 14>, <&ipmmu_ds1 15>;
};
dmac2: dma-controller@e7310000 {
@@ -702,6 +718,14 @@
resets = <&cpg 217>;
#dma-cells = <1>;
dma-channels = <16>;
+ iommus = <&ipmmu_ds1 16>, <&ipmmu_ds1 17>,
+ <&ipmmu_ds1 18>, <&ipmmu_ds1 19>,
+ <&ipmmu_ds1 20>, <&ipmmu_ds1 21>,
+ <&ipmmu_ds1 22>, <&ipmmu_ds1 23>,
+ <&ipmmu_ds1 24>, <&ipmmu_ds1 25>,
+ <&ipmmu_ds1 26>, <&ipmmu_ds1 27>,
+ <&ipmmu_ds1 28>, <&ipmmu_ds1 29>,
+ <&ipmmu_ds1 30>, <&ipmmu_ds1 31>;
};
ipmmu_ds0: mmu@e6740000 {
@@ -1455,9 +1479,9 @@
compatible = "renesas,usb2-phy-r8a77965",
"renesas,rcar-gen3-usb2-phy";
reg = <0 0xee0a0200 0 0x700>;
- clocks = <&cpg CPG_MOD 703>;
+ clocks = <&cpg CPG_MOD 702>;
power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
- resets = <&cpg 703>;
+ resets = <&cpg 702>;
#phy-cells = <0>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
index a8e8f26..1b8f19e 100644
--- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
+++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
@@ -188,7 +188,7 @@
compatible = "adi,adv7180cp";
reg = <0x20>;
- port {
+ ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
index 7d3d866..3b90f81 100644
--- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi
+++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
@@ -420,7 +420,10 @@
video-receiver@70 {
compatible = "adi,adv7482";
- reg = <0x70>;
+ reg = <0x70 0x71 0x72 0x73 0x74 0x75
+ 0x60 0x61 0x62 0x63 0x64 0x65>;
+ reg-names = "main", "dpll", "cp", "hdmi", "edid", "repeater",
+ "infoframe", "cbus", "cec", "sdp", "txa", "txb" ;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts
index 8ce4a79..1e6a710 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts
@@ -131,7 +131,7 @@
status = "okay";
clock-frequency = <400000>;
- sgtl5000: codec@0a {
+ sgtl5000: codec@a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
clocks = <&sgtl5000_clk>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
index 36b6079..6062cc8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
@@ -93,6 +93,19 @@
vin-supply = <&vcc_1v8>;
};
+ vcc3v0_sd: vcc3v0-sd {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpio = <&gpio0 RK_PA1 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc0_pwr_h>;
+ regulator-always-on;
+ regulator-max-microvolt = <3000000>;
+ regulator-min-microvolt = <3000000>;
+ regulator-name = "vcc3v0_sd";
+ vin-supply = <&vcc3v3_sys>;
+ };
+
vcc3v3_sys: vcc3v3-sys {
compatible = "regulator-fixed";
regulator-name = "vcc3v3_sys";
@@ -116,7 +129,7 @@
vcc5v0_host: vcc5v0-host-regulator {
compatible = "regulator-fixed";
enable-active-high;
- gpio = <&gpio1 RK_PD1 GPIO_ACTIVE_HIGH>;
+ gpio = <&gpio4 RK_PD1 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_host_en>;
regulator-name = "vcc5v0_host";
@@ -310,7 +323,7 @@
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <3000000>;
+ regulator-max-microvolt = <3300000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
@@ -469,6 +482,13 @@
};
};
+ sd {
+ sdmmc0_pwr_h: sdmmc0-pwr-h {
+ rockchip,pins =
+ <RK_GPIO0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
usb2 {
vcc5v0_host_en: vcc5v0-host-en {
rockchip,pins =
@@ -499,6 +519,7 @@
};
&sdmmc {
+ broken-cd;
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
@@ -507,6 +528,7 @@
max-frequency = <150000000>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
+ vmmc-supply = <&vcc3v0_sd>;
vqmmc-supply = <&vcc_sdio>;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi
index 2409344..2e39171 100644
--- a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi
@@ -8,22 +8,22 @@
&cbass_main {
gic500: interrupt-controller@1800000 {
compatible = "arm,gic-v3";
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
ranges;
#interrupt-cells = <3>;
interrupt-controller;
- reg = <0x01800000 0x10000>, /* GICD */
- <0x01880000 0x90000>; /* GICR */
+ reg = <0x00 0x01800000 0x00 0x10000>, /* GICD */
+ <0x00 0x01880000 0x00 0x90000>; /* GICR */
/*
* vcpumntirq:
* virtual CPU interface maintenance interrupt
*/
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
- gic_its: gic-its@18200000 {
+ gic_its: gic-its@1820000 {
compatible = "arm,gic-v3-its";
- reg = <0x01820000 0x10000>;
+ reg = <0x00 0x01820000 0x00 0x10000>;
msi-controller;
#msi-cells = <1>;
};
diff --git a/arch/arm64/boot/dts/ti/k3-am65.dtsi b/arch/arm64/boot/dts/ti/k3-am65.dtsi
index cede1fa..ded364d 100644
--- a/arch/arm64/boot/dts/ti/k3-am65.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am65.dtsi
@@ -46,38 +46,38 @@
cbass_main: interconnect@100000 {
compatible = "simple-bus";
- #address-cells = <1>;
- #size-cells = <1>;
- ranges = <0x00100000 0x00 0x00100000 0x00020000>, /* ctrl mmr */
- <0x00600000 0x00 0x00600000 0x00001100>, /* GPIO */
- <0x00900000 0x00 0x00900000 0x00012000>, /* serdes */
- <0x01000000 0x00 0x01000000 0x0af02400>, /* Most peripherals */
- <0x30800000 0x00 0x30800000 0x0bc00000>, /* MAIN NAVSS */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x00 0x00100000 0x00 0x00100000 0x00 0x00020000>, /* ctrl mmr */
+ <0x00 0x00600000 0x00 0x00600000 0x00 0x00001100>, /* GPIO */
+ <0x00 0x00900000 0x00 0x00900000 0x00 0x00012000>, /* serdes */
+ <0x00 0x01000000 0x00 0x01000000 0x00 0x0af02400>, /* Most peripherals */
+ <0x00 0x30800000 0x00 0x30800000 0x00 0x0bc00000>, /* MAIN NAVSS */
/* MCUSS Range */
- <0x28380000 0x00 0x28380000 0x03880000>,
- <0x40200000 0x00 0x40200000 0x00900100>,
- <0x42040000 0x00 0x42040000 0x03ac2400>,
- <0x45100000 0x00 0x45100000 0x00c24000>,
- <0x46000000 0x00 0x46000000 0x00200000>,
- <0x47000000 0x00 0x47000000 0x00068400>;
+ <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>,
+ <0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>,
+ <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>,
+ <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>,
+ <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>,
+ <0x00 0x47000000 0x00 0x47000000 0x00 0x00068400>;
cbass_mcu: interconnect@28380000 {
compatible = "simple-bus";
- #address-cells = <1>;
- #size-cells = <1>;
- ranges = <0x28380000 0x28380000 0x03880000>, /* MCU NAVSS*/
- <0x40200000 0x40200000 0x00900100>, /* First peripheral window */
- <0x42040000 0x42040000 0x03ac2400>, /* WKUP */
- <0x45100000 0x45100000 0x00c24000>, /* MMRs, remaining NAVSS */
- <0x46000000 0x46000000 0x00200000>, /* CPSW */
- <0x47000000 0x47000000 0x00068400>; /* OSPI space 1 */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, /* MCU NAVSS*/
+ <0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>, /* First peripheral window */
+ <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, /* WKUP */
+ <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, /* MMRs, remaining NAVSS */
+ <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, /* CPSW */
+ <0x00 0x47000000 0x00 0x47000000 0x00 0x00068400>; /* OSPI space 1 */
cbass_wakeup: interconnect@42040000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
/* WKUP Basic peripherals */
- ranges = <0x42040000 0x42040000 0x03ac2400>;
+ ranges = <0x42040000 0x00 0x42040000 0x03ac2400>;
};
};
};
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-clk.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp-clk.dtsi
index 9c09bac..306ad21 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-clk.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-clk.dtsi
@@ -58,13 +58,13 @@
clock-accuracy = <100>;
};
- dpdma_clk: dpdma_clk {
+ dpdma_clk: dpdma-clk {
compatible = "fixed-clock";
#clock-cells = <0x0>;
clock-frequency = <533000000>;
};
- drm_clock: drm_clock {
+ drm_clock: drm-clock {
compatible = "fixed-clock";
#clock-cells = <0x0>;
clock-frequency = <262750000>;
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
index 8954c8c..14062b4 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
@@ -82,7 +82,7 @@
linux,default-trigger = "bluetooth-power";
};
- vbus_det { /* U5 USB5744 VBUS detection via MIO25 */
+ vbus-det { /* U5 USB5744 VBUS detection via MIO25 */
label = "vbus_det";
gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
default-state = "on";
@@ -98,7 +98,7 @@
regulator-boot-on;
};
- sdio_pwrseq: sdio_pwrseq {
+ sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio 7 GPIO_ACTIVE_LOW>; /* WIFI_EN */
post-power-on-delay-ms = <10>;
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
index 25dd574..d3b8e1a 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
@@ -53,7 +53,7 @@
leds {
compatible = "gpio-leds";
- heartbeat_led {
+ heartbeat-led {
label = "heartbeat";
gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
@@ -139,25 +139,25 @@
* 7, 10 - 17 - not connected
*/
- gtr_sel0 {
+ gtr-sel0 {
gpio-hog;
gpios = <0 0>;
output-low; /* PCIE = 0, DP = 1 */
line-name = "sel0";
};
- gtr_sel1 {
+ gtr-sel1 {
gpio-hog;
gpios = <1 0>;
output-high; /* PCIE = 0, DP = 1 */
line-name = "sel1";
};
- gtr_sel2 {
+ gtr-sel2 {
gpio-hog;
gpios = <2 0>;
output-high; /* PCIE = 0, USB0 = 1 */
line-name = "sel2";
};
- gtr_sel3 {
+ gtr-sel3 {
gpio-hog;
gpios = <3 0>;
output-high; /* PCIE = 0, SATA = 1 */
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
index 259f21b..28dee4d 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
@@ -53,7 +53,7 @@
leds {
compatible = "gpio-leds";
- heartbeat_led {
+ heartbeat-led {
label = "heartbeat";
gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
index a61b3cc..47b5989 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
@@ -53,7 +53,7 @@
leds {
compatible = "gpio-leds";
- heartbeat_led {
+ heartbeat-led {
label = "heartbeat";
gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 29ce234..a516c0e 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -71,7 +71,7 @@
};
};
- cpu_opp_table: cpu_opp_table {
+ cpu_opp_table: cpu-opp-table {
compatible = "operating-points-v2";
opp-shared;
opp00 {
@@ -124,7 +124,7 @@
<1 10 0xf08>;
};
- amba_apu: amba_apu@0 {
+ amba_apu: amba-apu@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
diff --git a/arch/arm64/configs/cuttlefish_defconfig b/arch/arm64/configs/cuttlefish_defconfig
index 713be11..22789da 100644
--- a/arch/arm64/configs/cuttlefish_defconfig
+++ b/arch/arm64/configs/cuttlefish_defconfig
@@ -80,7 +80,6 @@
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_SPARSEMEM_VMEMMAP is not set
CONFIG_KSM=y
-CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_ZSMALLOC=y
CONFIG_NET=y
CONFIG_PACKET=y
diff --git a/arch/arm64/configs/vendor/bengal-perf_defconfig b/arch/arm64/configs/vendor/bengal-perf_defconfig
index c587713..a3d6f5b 100644
--- a/arch/arm64/configs/vendor/bengal-perf_defconfig
+++ b/arch/arm64/configs/vendor/bengal-perf_defconfig
@@ -11,14 +11,13 @@
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_PSI=y
+CONFIG_PSI_FTRACE=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_MEMCG=y
-CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_FREEZER=y
@@ -32,6 +31,8 @@
CONFIG_SCHED_AUTOGROUP=y
CONFIG_SCHED_TUNE=y
CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
@@ -43,6 +44,7 @@
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_PROFILING=y
+# CONFIG_ZONE_DMA32 is not set
CONFIG_HOTPLUG_SIZE_BITS=29
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_BENGAL=y
@@ -156,6 +158,7 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
@@ -294,7 +297,37 @@
CONFIG_BONDING=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AURORA is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HP is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
CONFIG_RMNET=y
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
CONFIG_PHYLIB=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
@@ -316,7 +349,10 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
@@ -328,6 +364,8 @@
CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_HW_RANDOM_CAVIUM is not set
+# CONFIG_DEVPORT is not set
CONFIG_DIAG_CHAR=y
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
@@ -386,9 +424,7 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_SW=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=y
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
@@ -399,6 +435,7 @@
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_PCI is not set
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_USB_AUDIO_QMI=y
CONFIG_SND_SOC=y
@@ -409,18 +446,14 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_PLANTRONICS=y
+CONFIG_HID_SONY=y
CONFIG_USB_HIDDEV=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
CONFIG_USB_DWC3_MSM=y
-CONFIG_USB_ISP1760=y
-CONFIG_USB_ISP1760_HOST_ROLE=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_NOP_USB_XCEIV=y
@@ -449,14 +482,14 @@
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
-CONFIG_MMC_IPC_LOGGING=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QTI_FLASH=y
CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -490,12 +523,10 @@
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_MAILBOX=y
CONFIG_QCOM_APCS_IPC=y
-CONFIG_MSM_QMP=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
@@ -589,8 +620,6 @@
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_EFIVAR_FS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -613,7 +642,6 @@
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
-CONFIG_XZ_DEC=y
CONFIG_STACK_HASH_ORDER_SHIFT=12
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
@@ -622,6 +650,7 @@
CONFIG_MAGIC_SYSRQ=y
CONFIG_PANIC_TIMEOUT=-1
CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_PREEMPT is not set
CONFIG_IPC_LOGGING=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
diff --git a/arch/arm64/configs/vendor/bengal_defconfig b/arch/arm64/configs/vendor/bengal_defconfig
index 6817649..014763b 100644
--- a/arch/arm64/configs/vendor/bengal_defconfig
+++ b/arch/arm64/configs/vendor/bengal_defconfig
@@ -10,14 +10,13 @@
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_PSI=y
+CONFIG_PSI_FTRACE=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_MEMCG=y
-CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_DEBUG_BLK_CGROUP=y
CONFIG_RT_GROUP_SCHED=y
@@ -33,6 +32,8 @@
CONFIG_SCHED_AUTOGROUP=y
CONFIG_SCHED_TUNE=y
CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
@@ -44,6 +45,7 @@
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_PROFILING=y
+# CONFIG_ZONE_DMA32 is not set
CONFIG_HOTPLUG_SIZE_BITS=29
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_BENGAL=y
@@ -88,6 +90,7 @@
CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
+CONFIG_KPROBES=y
CONFIG_PANIC_ON_REFCOUNT_ERROR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -163,6 +166,7 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
@@ -304,7 +308,36 @@
CONFIG_BONDING=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AURORA is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HP is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
CONFIG_RMNET=y
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
CONFIG_PHYLIB=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
@@ -326,7 +359,10 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
@@ -340,6 +376,8 @@
CONFIG_TTY_PRINTK=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_HW_RANDOM_CAVIUM is not set
+# CONFIG_DEVPORT is not set
CONFIG_DIAG_CHAR=y
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
@@ -399,9 +437,7 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_SW=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=y
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
@@ -412,6 +448,7 @@
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_PCI is not set
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_USB_AUDIO_QMI=y
CONFIG_SND_SOC=y
@@ -422,18 +459,14 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_PLANTRONICS=y
+CONFIG_HID_SONY=y
CONFIG_USB_HIDDEV=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
CONFIG_USB_DWC3_MSM=y
-CONFIG_USB_ISP1760=y
-CONFIG_USB_ISP1760_HOST_ROLE=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_NOP_USB_XCEIV=y
@@ -462,7 +495,6 @@
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_IPC_LOGGING=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -470,6 +502,8 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QTI_FLASH=y
CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -508,14 +542,12 @@
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_MAILBOX=y
CONFIG_QCOM_APCS_IPC=y
-CONFIG_MSM_QMP=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
CONFIG_IOMMU_TLBSYNC_DEBUG=y
CONFIG_ARM_SMMU_TESTBUS_DUMP=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
@@ -603,6 +635,7 @@
CONFIG_EXT4_FS_SECURITY=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_CHECK_FS=y
CONFIG_FS_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
@@ -614,8 +647,6 @@
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_EFIVAR_FS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -638,7 +669,6 @@
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
-CONFIG_XZ_DEC=y
CONFIG_PRINTK_TIME=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS=y
diff --git a/arch/arm64/configs/vendor/kona-perf_defconfig b/arch/arm64/configs/vendor/kona-perf_defconfig
index c622396..452f7a0 100644
--- a/arch/arm64/configs/vendor/kona-perf_defconfig
+++ b/arch/arm64/configs/vendor/kona-perf_defconfig
@@ -1,6 +1,5 @@
CONFIG_LOCALVERSION="-perf"
# CONFIG_LOCALVERSION_AUTO is not set
-CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT=y
@@ -60,6 +59,7 @@
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_LSE_ATOMICS=y
# CONFIG_ARM64_VHE is not set
CONFIG_RANDOMIZE_BASE=y
# CONFIG_EFI is not set
@@ -158,6 +158,7 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
@@ -282,6 +283,7 @@
CONFIG_UID_SYS_STATS=y
CONFIG_OKL4_USER_VIRQ=y
CONFIG_WIGIG_SENSING_SPI=m
+CONFIG_QTI_XR_SMRTVWR_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -297,6 +299,7 @@
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y
+CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
@@ -325,6 +328,8 @@
CONFIG_BUS_AUTO_SUSPEND=y
CONFIG_CNSS_QCA6390=y
CONFIG_CNSS_GENL=y
+CONFIG_NVM=y
+CONFIG_NVM_PBLK=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -422,6 +427,7 @@
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_TSPP1=y
CONFIG_TSPP=m
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_I2C_RTC6226_QCA=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
@@ -453,11 +459,14 @@
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_MSM=y
CONFIG_USB_ISP1760=y
CONFIG_USB_ISP1760_HOST_ROLE=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_USB_REDRIVER_NB7VPQ904M=y
@@ -543,7 +552,6 @@
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
@@ -666,6 +674,8 @@
CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=0
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_XCBC=y
diff --git a/arch/arm64/configs/vendor/kona_defconfig b/arch/arm64/configs/vendor/kona_defconfig
index 70db727..2bca627 100644
--- a/arch/arm64/configs/vendor/kona_defconfig
+++ b/arch/arm64/configs/vendor/kona_defconfig
@@ -1,5 +1,4 @@
# CONFIG_LOCALVERSION_AUTO is not set
-CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT=y
@@ -61,6 +60,7 @@
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_LSE_ATOMICS=y
# CONFIG_ARM64_VHE is not set
CONFIG_RANDOMIZE_BASE=y
CONFIG_BUILD_ARM64_UNCOMPRESSED_KERNEL=y
@@ -165,6 +165,7 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
@@ -268,6 +269,8 @@
CONFIG_MSM_BT_POWER=y
CONFIG_BT_SLIM_QCA6390=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_NFC_NQ=y
@@ -292,6 +295,7 @@
CONFIG_UID_SYS_STATS=y
CONFIG_OKL4_USER_VIRQ=y
CONFIG_WIGIG_SENSING_SPI=m
+CONFIG_QTI_XR_SMRTVWR_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -308,6 +312,7 @@
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y
+CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
@@ -335,6 +340,8 @@
CONFIG_BUS_AUTO_SUSPEND=y
CONFIG_CNSS_QCA6390=y
CONFIG_CNSS_GENL=y
+CONFIG_NVM=y
+CONFIG_NVM_PBLK=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -434,6 +441,7 @@
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_TSPP1=y
CONFIG_TSPP=m
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_I2C_RTC6226_QCA=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
@@ -466,11 +474,14 @@
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_MSM=y
CONFIG_USB_ISP1760=y
CONFIG_USB_ISP1760_HOST_ROLE=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_USB_REDRIVER_NB7VPQ904M=y
@@ -564,7 +575,6 @@
CONFIG_IOMMU_TLBSYNC_DEBUG=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
@@ -697,6 +707,8 @@
CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=0
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_XCBC=y
diff --git a/arch/arm64/configs/vendor/lito-perf_defconfig b/arch/arm64/configs/vendor/lito-perf_defconfig
index ea0977a..85c42a1 100644
--- a/arch/arm64/configs/vendor/lito-perf_defconfig
+++ b/arch/arm64/configs/vendor/lito-perf_defconfig
@@ -51,7 +51,6 @@
CONFIG_PCI=y
CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=8
-CONFIG_HZ_100=y
CONFIG_SECCOMP=y
# CONFIG_UNMAP_KERNEL_AT_EL0 is not set
CONFIG_ARM64_SSBD=y
@@ -79,6 +78,7 @@
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_ARM_QCOM_CPUFREQ_HW=y
+CONFIG_ARM_QCOM_CPUFREQ_HW_DEBUG=y
CONFIG_MSM_TZ_LOG=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
@@ -294,6 +294,7 @@
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y
+CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
@@ -333,6 +334,9 @@
CONFIG_TABLET_USB_KBTAB=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_SECURE_TOUCH_SYNAPTICS_DSX=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY=y
+CONFIG_TOUCHSCREEN_FTS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
@@ -367,6 +371,7 @@
CONFIG_SMB1390_CHARGE_PUMP_PSY=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
+CONFIG_SMB1398_CHARGER=y
CONFIG_THERMAL=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_THERMAL_WRITABLE_TRIPS=y
@@ -387,6 +392,7 @@
CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y
CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE=y
CONFIG_QTI_LIMITS_ISENSE_CDSP=y
+CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
@@ -412,9 +418,7 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_SW=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=m
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
@@ -512,6 +516,7 @@
CONFIG_RMNET_IPA3=y
CONFIG_RNDIS_IPA=y
CONFIG_IPA_UT=y
+CONFIG_USB_BAM=y
CONFIG_QCOM_GENI_SE=y
# CONFIG_QCOM_A53PLL is not set
CONFIG_QCOM_CLK_RPMH=y
@@ -525,7 +530,10 @@
CONFIG_SM_NPUCC_LITO=y
CONFIG_SM_DEBUGCC_LITO=y
CONFIG_SDM_CAMCC_LAGOON=y
+CONFIG_SDM_DEBUGCC_LAGOON=y
+CONFIG_SDM_DISPCC_LAGOON=y
CONFIG_SDM_GPUCC_LAGOON=y
+CONFIG_SDM_NPUCC_LAGOON=y
CONFIG_SDM_VIDEOCC_LAGOON=y
CONFIG_HWSPINLOCK=y
CONFIG_HWSPINLOCK_QCOM=y
@@ -535,7 +543,6 @@
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
@@ -548,6 +555,7 @@
CONFIG_QCOM_IPCC=y
CONFIG_QCOM_LLCC=y
CONFIG_QCOM_LITO_LLCC=y
+CONFIG_QCOM_LAGOON_LLCC=y
CONFIG_QCOM_LLCC_PERFMON=m
CONFIG_QCOM_MDT_LOADER=y
CONFIG_QCOM_QMI_HELPERS=y
diff --git a/arch/arm64/configs/vendor/lito_defconfig b/arch/arm64/configs/vendor/lito_defconfig
index 7fee6bc..bf6b60d 100644
--- a/arch/arm64/configs/vendor/lito_defconfig
+++ b/arch/arm64/configs/vendor/lito_defconfig
@@ -51,7 +51,6 @@
CONFIG_PCI=y
CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=8
-CONFIG_HZ_100=y
CONFIG_SECCOMP=y
# CONFIG_UNMAP_KERNEL_AT_EL0 is not set
CONFIG_ARM64_SSBD=y
@@ -80,6 +79,7 @@
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_ARM_QCOM_CPUFREQ_HW=y
+CONFIG_ARM_QCOM_CPUFREQ_HW_DEBUG=y
CONFIG_MSM_TZ_LOG=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
@@ -301,6 +301,7 @@
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y
+CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
@@ -340,6 +341,9 @@
CONFIG_TABLET_USB_KBTAB=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_SECURE_TOUCH_SYNAPTICS_DSX=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY=y
+CONFIG_TOUCHSCREEN_FTS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
@@ -375,6 +379,7 @@
CONFIG_SMB1390_CHARGE_PUMP_PSY=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
+CONFIG_SMB1398_CHARGER=y
CONFIG_THERMAL=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_THERMAL_WRITABLE_TRIPS=y
@@ -395,6 +400,7 @@
CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y
CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE=y
CONFIG_QTI_LIMITS_ISENSE_CDSP=y
+CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
@@ -420,9 +426,7 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_SW=y
-CONFIG_V4L_TEST_DRIVERS=y
-CONFIG_VIDEO_VIM2M=y
-CONFIG_VIDEO_VICODEC=y
+CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y
CONFIG_DRM=y
# CONFIG_DRM_MSM is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
@@ -526,6 +530,7 @@
CONFIG_RMNET_IPA3=y
CONFIG_RNDIS_IPA=y
CONFIG_IPA_UT=y
+CONFIG_USB_BAM=y
CONFIG_QCOM_GENI_SE=y
# CONFIG_QCOM_A53PLL is not set
CONFIG_QCOM_CLK_RPMH=y
@@ -539,7 +544,10 @@
CONFIG_SM_NPUCC_LITO=y
CONFIG_SM_DEBUGCC_LITO=y
CONFIG_SDM_CAMCC_LAGOON=y
+CONFIG_SDM_DEBUGCC_LAGOON=y
+CONFIG_SDM_DISPCC_LAGOON=y
CONFIG_SDM_GPUCC_LAGOON=y
+CONFIG_SDM_NPUCC_LAGOON=y
CONFIG_SDM_VIDEOCC_LAGOON=y
CONFIG_HWSPINLOCK=y
CONFIG_HWSPINLOCK_QCOM=y
@@ -551,7 +559,6 @@
CONFIG_ARM_SMMU_TESTBUS_DUMP=y
CONFIG_QCOM_LAZY_MAPPING=y
CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
@@ -565,6 +572,7 @@
CONFIG_QCOM_IPCC=y
CONFIG_QCOM_LLCC=y
CONFIG_QCOM_LITO_LLCC=y
+CONFIG_QCOM_LAGOON_LLCC=y
CONFIG_QCOM_LLCC_PERFMON=m
CONFIG_QCOM_MDT_LOADER=y
CONFIG_QCOM_QMI_HELPERS=y
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index ba45a37..5b7c3f7 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -54,7 +54,7 @@
#define ARM64_WORKAROUND_1463225 33
#define ARM64_SSBS 34
#define ARM64_WORKAROUND_1188873 35
-
-#define ARM64_NCAPS 36
+#define ARM64_WORKAROUND_1542418 36
+#define ARM64_NCAPS 37
#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index dda6e50..bf57104 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -509,6 +509,11 @@ static inline bool system_supports_sve(void)
cpus_have_const_cap(ARM64_SVE);
}
+static inline bool system_supports_cnp(void)
+{
+ return false;
+}
+
#define ARM64_SSBD_UNKNOWN -1
#define ARM64_SSBD_FORCE_DISABLE 0
#define ARM64_SSBD_KERNEL 1
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 06da284..1e04fdb 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -68,6 +68,7 @@
#define ARM_CPU_IMP_BRCM 0x42
#define ARM_CPU_IMP_QCOM 0x51
#define ARM_CPU_IMP_NVIDIA 0x4E
+#define ARM_CPU_IMP_HISI 0x48
#define ARM_CPU_PART_AEM_V8 0xD0F
#define ARM_CPU_PART_FOUNDATION 0xD00
@@ -101,6 +102,8 @@
#define NVIDIA_CPU_PART_DENVER 0x003
#define NVIDIA_CPU_PART_CARMEL 0x004
+#define HISI_CPU_PART_TSV110 0xD01
+
#define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
#define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
#define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72)
@@ -122,6 +125,7 @@
#define MIDR_QCOM_KRYO MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO)
#define MIDR_NVIDIA_DENVER MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_DENVER)
#define MIDR_NVIDIA_CARMEL MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_CARMEL)
+#define MIDR_HISI_TSV110 MIDR_CPU_MODEL(ARM_CPU_IMP_HISI, HISI_CPU_PART_TSV110)
#define MIDR_KRYO2XX_GOLD \
MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO2XX_GOLD)
#define MIDR_KRYO2XX_SILVER \
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index b255844..ce678f9 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -20,6 +20,7 @@
#include <asm/page.h>
#include <asm/memory.h>
+#include <asm/mmu_context.h>
#include <asm/cpufeature.h>
/*
@@ -528,5 +529,19 @@ static inline int hyp_map_aux_data(void)
#define kvm_phys_to_vttbr(addr) phys_to_ttbr(addr)
+static inline void kvm_workaround_1542418_vmid_rollover(void)
+{
+ unsigned long flags;
+
+ if (!IS_ENABLED(CONFIG_ARM64_ERRATUM_1542418) ||
+ !cpus_have_const_cap(ARM64_WORKAROUND_1542418))
+ return;
+
+ local_irq_save(flags);
+ arm64_workaround_1542418_asid_rollover();
+ local_irq_restore(flags);
+
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index a1e5168de..b5c2d7a 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -246,6 +246,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
void verify_cpu_asid_bits(void);
void post_ttbr_update_workaround(void);
+void arm64_workaround_1542418_asid_rollover(void);
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h
index 78b942c..5be015e 100644
--- a/arch/arm64/include/asm/pgtable-prot.h
+++ b/arch/arm64/include/asm/pgtable-prot.h
@@ -43,11 +43,11 @@
#define PROT_DEFAULT (_PROT_DEFAULT | PTE_MAYBE_NG)
#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)
-#define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
-#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
-#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
-#define PROT_NORMAL_WT (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
-#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
+#define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
+#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
+#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
+#define PROT_NORMAL_WT (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
+#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
@@ -91,17 +91,17 @@
#define PAGE_S2_DEVICE __pgprot(_PROT_DEFAULT | PAGE_S2_MEMATTR(DEVICE_nGnRE) | PTE_S2_RDONLY | PAGE_S2_XN)
#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
-#define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
-#define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE)
+/* shared+writable pages are clean by default, hence PTE_RDONLY|PTE_WRITE */
+#define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
+#define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_WRITE)
#define PAGE_READONLY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
#define PAGE_READONLY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
-#define PAGE_EXECONLY __pgprot(_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
#define __P000 PAGE_NONE
#define __P001 PAGE_READONLY
#define __P010 PAGE_READONLY
#define __P011 PAGE_READONLY
-#define __P100 PAGE_EXECONLY
+#define __P100 PAGE_READONLY_EXEC
#define __P101 PAGE_READONLY_EXEC
#define __P110 PAGE_READONLY_EXEC
#define __P111 PAGE_READONLY_EXEC
@@ -110,7 +110,7 @@
#define __S001 PAGE_READONLY
#define __S010 PAGE_SHARED
#define __S011 PAGE_SHARED
-#define __S100 PAGE_EXECONLY
+#define __S100 PAGE_READONLY_EXEC
#define __S101 PAGE_READONLY_EXEC
#define __S110 PAGE_SHARED_EXEC
#define __S111 PAGE_SHARED_EXEC
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index f2aa655..45705d1 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -106,12 +106,8 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
#define pte_dirty(pte) (pte_sw_dirty(pte) || pte_hw_dirty(pte))
#define pte_valid(pte) (!!(pte_val(pte) & PTE_VALID))
-/*
- * Execute-only user mappings do not have the PTE_USER bit set. All valid
- * kernel mappings have the PTE_UXN bit set.
- */
#define pte_valid_not_user(pte) \
- ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_UXN)) == (PTE_VALID | PTE_UXN))
+ ((pte_val(pte) & (PTE_VALID | PTE_USER)) == PTE_VALID)
#define pte_valid_young(pte) \
((pte_val(pte) & (PTE_VALID | PTE_AF)) == (PTE_VALID | PTE_AF))
#define pte_valid_user(pte) \
@@ -127,8 +123,8 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
/*
* p??_access_permitted() is true for valid user mappings (subject to the
- * write permission check) other than user execute-only which do not have the
- * PTE_USER bit set. PROT_NONE mappings do not have the PTE_VALID bit set.
+ * write permission check). PROT_NONE mappings do not have the PTE_VALID bit
+ * set.
*/
#define pte_access_permitted(pte, write) \
(pte_valid_user(pte) && (!(write) || pte_write(pte)))
@@ -303,23 +299,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
set_pte(ptep, pte);
}
-#define __HAVE_ARCH_PTE_SAME
-static inline int pte_same(pte_t pte_a, pte_t pte_b)
-{
- pteval_t lhs, rhs;
-
- lhs = pte_val(pte_a);
- rhs = pte_val(pte_b);
-
- if (pte_present(pte_a))
- lhs &= ~PTE_RDONLY;
-
- if (pte_present(pte_b))
- rhs &= ~PTE_RDONLY;
-
- return (lhs == rhs);
-}
-
/*
* Huge pte definitions.
*/
diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
index c320f3b..b0d4bfc 100644
--- a/arch/arm64/include/asm/traps.h
+++ b/arch/arm64/include/asm/traps.h
@@ -120,4 +120,6 @@ static inline u32 arm64_ras_serror_get_severity(u32 esr)
bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr);
void __noreturn arm64_serror_panic(struct pt_regs *regs, u32 esr);
+static inline void get_timer_count_hook_init(void) {}
+static inline void get_timer_freq_hook_init(void) {}
#endif
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index 92be1d1..39dc98d 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -177,6 +177,9 @@ static void __init register_insn_emulation(struct insn_emulation_ops *ops)
struct insn_emulation *insn;
insn = kzalloc(sizeof(*insn), GFP_KERNEL);
+ if (!insn)
+ return;
+
insn->ops = ops;
insn->min = INSN_UNDEF;
@@ -236,6 +239,8 @@ static void __init register_insn_emulation_sysctl(void)
insns_sysctl = kcalloc(nr_insn_emulated + 1, sizeof(*sysctl),
GFP_KERNEL);
+ if (!insns_sysctl)
+ return;
raw_spin_lock_irqsave(&insn_emulation_lock, flags);
list_for_each_entry(insn, &insn_emulation, node) {
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index cd250ec..dda0989 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -23,6 +23,7 @@
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/cpufeature.h>
+#include <asm/mmu_context.h>
#include <asm/smp_plat.h>
static bool __maybe_unused
@@ -643,6 +644,18 @@ needs_tx2_tvm_workaround(const struct arm64_cpu_capabilities *entry,
return false;
}
+#ifdef CONFIG_ARM64_ERRATUM_1542418
+static void run_workaround_1542418_asid_rollover(const struct arm64_cpu_capabilities *c)
+{
+ /*
+ * If this CPU is affected by the erratum, run the workaround
+ * to protect us in case we are running on a kexec'ed kernel.
+ */
+ if (c->matches(c, SCOPE_LOCAL_CPU))
+ arm64_workaround_1542418_asid_rollover();
+}
+#endif
+
#ifdef CONFIG_HARDEN_EL2_VECTORS
static const struct midr_range arm64_harden_el2_vectors[] = {
@@ -876,6 +889,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
.matches = needs_tx2_tvm_workaround,
},
#endif
+#ifdef CONFIG_ARM64_ERRATUM_1542418
+ {
+ .desc = "ARM erratum 1542418",
+ .capability = ARM64_WORKAROUND_1542418,
+ ERRATA_MIDR_RANGE(MIDR_CORTEX_A77, 0, 0, 1, 0),
+ .cpu_enable = run_workaround_1542418_asid_rollover,
+ },
+#endif
{
}
};
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index d6fb0be..c6324c2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -906,6 +906,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A73),
+ MIDR_ALL_VERSIONS(MIDR_HISI_TSV110),
{ /* sentinel */ }
};
char const *str = "kpti command line option";
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index 7eff8af..b661839 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -119,10 +119,16 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
/*
* Ensure updated trampoline is visible to instruction
- * fetch before we patch in the branch.
+ * fetch before we patch in the branch. Although the
+ * architecture doesn't require an IPI in this case,
+ * Neoverse-N1 erratum #1542419 does require one
+ * if the TLB maintenance in module_enable_ro() is
+ * skipped due to rodata_enabled. It doesn't seem worth
+ * it to make it conditional given that this is
+ * certainly not a fast-path.
*/
- __flush_icache_range((unsigned long)&dst[0],
- (unsigned long)&dst[1]);
+ flush_icache_range((unsigned long)&dst[0],
+ (unsigned long)&dst[1]);
}
addr = (unsigned long)dst;
#else /* CONFIG_ARM64_MODULE_PLTS */
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 77ca595..06058fb 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -703,6 +703,7 @@
/*
* Common entry point for secondary CPUs.
*/
+ bl __cpu_secondary_check52bitva
bl __cpu_setup // initialise processor
bl __enable_mmu
ldr x8, =__secondary_switched
@@ -779,6 +780,31 @@
ret
ENDPROC(__enable_mmu)
+ENTRY(__cpu_secondary_check52bitva)
+#ifdef CONFIG_ARM64_52BIT_VA
+ ldr_l x0, vabits_user
+ cmp x0, #52
+ b.ne 2f
+
+ mrs_s x0, SYS_ID_AA64MMFR2_EL1
+ and x0, x0, #(0xf << ID_AA64MMFR2_LVA_SHIFT)
+ cbnz x0, 2f
+
+ adr_l x0, va52mismatch
+ mov w1, #1
+ strb w1, [x0]
+ dmb sy
+ dc ivac, x0 // Invalidate potentially stale cache line
+
+ update_early_cpu_boot_status CPU_STUCK_IN_KERNEL, x0, x1
+1: wfe
+ wfi
+ b 1b
+
+#endif
+2: ret
+ENDPROC(__cpu_secondary_check52bitva)
+
__no_granule_support:
/* Indicate that this CPU can't boot and is stuck in the kernel */
update_early_cpu_boot_status CPU_STUCK_IN_KERNEL, x1, x2
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index e8edbf1..3856d51 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -84,7 +84,8 @@ static void cpu_psci_cpu_die(unsigned int cpu)
static int cpu_psci_cpu_kill(unsigned int cpu)
{
- int err, i;
+ int err;
+ unsigned long start, end;
if (!psci_ops.affinity_info)
return 0;
@@ -94,16 +95,18 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
* while it is dying. So, try again a few times.
*/
- for (i = 0; i < 10; i++) {
+ start = jiffies;
+ end = start + msecs_to_jiffies(100);
+ do {
err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
- pr_info("CPU%d killed.\n", cpu);
+ pr_info("CPU%d killed (polled %d ms)\n", cpu,
+ jiffies_to_msecs(jiffies - start));
return 0;
}
- msleep(10);
- pr_info("Retrying again to check for CPU kill\n");
- }
+ usleep_range(100, 1000);
+ } while (time_before(jiffies, end));
pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
cpu, err);
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 583c4fa..81d22c8 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -113,6 +113,7 @@ static int boot_secondary(unsigned int cpu, struct task_struct *idle)
}
static DECLARE_COMPLETION(cpu_running);
+bool va52mismatch __ro_after_init;
int __cpu_up(unsigned int cpu, struct task_struct *idle)
{
@@ -142,10 +143,15 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
if (!cpu_online(cpu)) {
pr_crit("CPU%u: failed to come online\n", cpu);
+
+ if (IS_ENABLED(CONFIG_ARM64_52BIT_VA) && va52mismatch)
+ pr_crit("CPU%u: does not support 52-bit VAs\n", cpu);
+
ret = -EIO;
}
} else {
pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
+ return ret;
}
secondary_data.task = NULL;
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index c65a823..4aded5b 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -768,7 +768,6 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
handler[reason], smp_processor_id(), esr,
esr_get_class_string(esr));
- die("Oops - bad mode", regs, 0);
local_daif_mask();
panic("bad mode");
}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6da2bbd..0c073f3 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2174,8 +2174,11 @@ static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu,
if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM64_SYSREG)
return NULL;
+ if (!index_to_params(id, ¶ms))
+ return NULL;
+
table = get_target_table(vcpu->arch.target, true, &num);
- r = find_reg_by_id(id, ¶ms, table, num);
+ r = find_reg(¶ms, table, num);
if (!r)
r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index 21ba0b2..4374020 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -57,5 +57,6 @@
.section .fixup,"ax"
.align 2
9: mov x0, x2 // return the original size
+ uaccess_disable_not_uao x2, x3
ret
.previous
diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S
index 20305d4..96b22c0 100644
--- a/arch/arm64/lib/copy_from_user.S
+++ b/arch/arm64/lib/copy_from_user.S
@@ -75,5 +75,6 @@
.section .fixup,"ax"
.align 2
9998: sub x0, end, dst // bytes not copied
+ uaccess_disable_not_uao x3, x4
ret
.previous
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
index 54b75de..e56c705 100644
--- a/arch/arm64/lib/copy_in_user.S
+++ b/arch/arm64/lib/copy_in_user.S
@@ -77,5 +77,6 @@
.section .fixup,"ax"
.align 2
9998: sub x0, end, dst // bytes not copied
+ uaccess_disable_not_uao x3, x4
ret
.previous
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
index fda6172..6b99b93 100644
--- a/arch/arm64/lib/copy_to_user.S
+++ b/arch/arm64/lib/copy_to_user.S
@@ -74,5 +74,6 @@
.section .fixup,"ax"
.align 2
9998: sub x0, end, dst // bytes not copied
+ uaccess_disable_not_uao x3, x4
ret
.previous
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index c127f94..7294200 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -88,6 +88,75 @@ void verify_cpu_asid_bits(void)
}
}
+
+/*
+ * When the CnP is active, the caller must have set the ttbr0 to reserved
+ * before calling this function.
+ * Upon completion, the caller must ensure to:
+ * - restore the ttbr0
+ * - execute isb() to synchronize the change.
+ */
+static void __arm64_workaround_1542418_asid_rollover(void)
+{
+ phys_addr_t ttbr1_baddr;
+ u64 idx, ttbr1; /* ASID is in ttbr1 due to TCR_EL1.A1 */
+
+ if (!IS_ENABLED(CONFIG_ARM64_ERRATUM_1542418) ||
+ !cpus_have_const_cap(ARM64_WORKAROUND_1542418) ||
+ !this_cpu_has_cap(ARM64_WORKAROUND_1542418))
+ return;
+
+ /*
+ * We're about to use an arbitrary set of ASIDs, which may have
+ * live entries in the TLB (and on other CPUs with CnP). Ensure
+ * that we can't allocate conflicting entries using this task's
+ * TTBR0.
+ */
+ if (!system_supports_cnp())
+ cpu_set_reserved_ttbr0();
+ /* else: the caller must have already set this */
+
+ ttbr1 = read_sysreg(ttbr1_el1);
+ ttbr1_baddr = ttbr1 & ~TTBR_ASID_MASK;
+
+ /*
+ * Select 60 asids to invalidate the branch history for this generation.
+ * If kpti is in use we avoid selecting a user asid as
+ * __sdei_asm_entry_trampoline() uses USER_ASID_FLAG to determine if
+ * the NMI interrupted the kpti trampoline. Avoid using the reserved
+ * asid 0.
+ */
+ for (idx = 1; idx <= 61; idx++) {
+ write_sysreg((idx2asid(idx) << 48) | ttbr1_baddr, ttbr1_el1);
+ isb();
+ }
+
+ /* restore the current ASID */
+ write_sysreg(ttbr1, ttbr1_el1);
+
+ /*
+ * Rely on local_flush_tlb_all()'s isb to complete the ASID restore.
+ * check_and_switch_context() will call cpu_switch_mm() to (re)set ttbr0_el1.
+ */
+}
+
+void arm64_workaround_1542418_asid_rollover(void)
+{
+ u64 ttbr0 = read_sysreg(ttbr0_el1);
+
+ lockdep_assert_irqs_disabled();
+
+ /* Mirror check_and_switch_context() */
+ if (system_supports_cnp())
+ cpu_set_reserved_ttbr0();
+
+ __arm64_workaround_1542418_asid_rollover();
+ isb();
+
+ write_sysreg(ttbr0, ttbr0_el1);
+ isb();
+}
+
static void flush_context(unsigned int cpu)
{
int i;
@@ -227,8 +296,10 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
atomic64_set(&mm->context.id, asid);
}
- if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending))
+ if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) {
+ __arm64_workaround_1542418_asid_rollover();
local_flush_tlb_all();
+ }
atomic64_set(&per_cpu(active_asids, cpu), asid);
raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 9c94715..296e04b 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -427,7 +427,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
struct mm_struct *mm;
struct siginfo si;
vm_fault_t fault, major = 0;
- unsigned long vm_flags = VM_READ | VM_WRITE;
+ unsigned long vm_flags = VM_READ | VM_WRITE | VM_EXEC;
unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
struct vm_area_struct *vma = NULL;
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 146c04c..54529b4 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -432,7 +432,7 @@ static int __init dummy_numa_init(void)
if (numa_off)
pr_info("NUMA disabled\n"); /* Forced off on command line. */
pr_info("Faking a node at [mem %#018Lx-%#018Lx]\n",
- 0LLU, PFN_PHYS(max_pfn) - 1);
+ memblock_start_of_DRAM(), memblock_end_of_DRAM() - 1);
for_each_memblock(memory, mblk) {
ret = numa_add_memblk(0, mblk->base, mblk->base + mblk->size);
diff --git a/arch/m68k/kernel/uboot.c b/arch/m68k/kernel/uboot.c
index b29c3b2..1070828 100644
--- a/arch/m68k/kernel/uboot.c
+++ b/arch/m68k/kernel/uboot.c
@@ -102,5 +102,5 @@ __init void process_uboot_commandline(char *commandp, int size)
}
parse_uboot_commandline(commandp, len);
- commandp[size - 1] = 0;
+ commandp[len - 1] = 0;
}
diff --git a/arch/microblaze/Makefile b/arch/microblaze/Makefile
index 4f3ab57..548bac6 100644
--- a/arch/microblaze/Makefile
+++ b/arch/microblaze/Makefile
@@ -83,19 +83,21 @@
linux.bin linux.bin.gz linux.bin.ub: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+ @echo 'Kernel: $(boot)/$@ is ready' ' (#'`cat .version`')'
simpleImage.%: vmlinux
- $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+ $(Q)$(MAKE) $(build)=$(boot) $(addprefix $(boot)/$@., ub unstrip strip)
+ @echo 'Kernel: $(boot)/$@ is ready' ' (#'`cat .version`')'
define archhelp
echo '* linux.bin - Create raw binary'
echo ' linux.bin.gz - Create compressed raw binary'
echo ' linux.bin.ub - Create U-Boot wrapped raw binary'
- echo ' simpleImage.<dt> - ELF image with $(arch)/boot/dts/<dt>.dts linked in'
- echo ' - stripped elf with fdt blob'
- echo ' simpleImage.<dt>.unstrip - full ELF image with fdt blob'
- echo ' *_defconfig - Select default config from arch/microblaze/configs'
- echo ''
+ echo ' simpleImage.<dt> - Create the following images with <dt>.dtb linked in'
+ echo ' simpleImage.<dt> : raw image'
+ echo ' simpleImage.<dt>.ub : raw image with U-Boot header'
+ echo ' simpleImage.<dt>.unstrip: ELF (identical to vmlinux)'
+ echo ' simpleImage.<dt>.strip : stripped ELF'
echo ' Targets with <dt> embed a device tree blob inside the image'
echo ' These targets support board with firmware that does not'
echo ' support passing a device tree directly. Replace <dt> with the'
diff --git a/arch/microblaze/boot/Makefile b/arch/microblaze/boot/Makefile
index 600e5a1..cff570a 100644
--- a/arch/microblaze/boot/Makefile
+++ b/arch/microblaze/boot/Makefile
@@ -3,38 +3,33 @@
# arch/microblaze/boot/Makefile
#
-targets := linux.bin linux.bin.gz linux.bin.ub simpleImage.%
+targets := linux.bin linux.bin.gz linux.bin.ub simpleImage.*
OBJCOPYFLAGS := -R .note -R .comment -R .note.gnu.build-id -O binary
$(obj)/linux.bin: vmlinux FORCE
$(call if_changed,objcopy)
- @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'
$(obj)/linux.bin.ub: $(obj)/linux.bin FORCE
$(call if_changed,uimage)
- @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'
$(obj)/linux.bin.gz: $(obj)/linux.bin FORCE
$(call if_changed,gzip)
- @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'
-
-quiet_cmd_cp = CP $< $@$2
- cmd_cp = cat $< >$@$2 || (rm -f $@ && echo false)
quiet_cmd_strip = STRIP $< $@$2
cmd_strip = $(STRIP) -K microblaze_start -K _end -K __log_buf \
-K _fdt_start $< -o $@$2
UIMAGE_LOADADDR = $(CONFIG_KERNEL_BASE_ADDR)
-UIMAGE_IN = $@
-UIMAGE_OUT = $@.ub
-$(obj)/simpleImage.%: vmlinux FORCE
- $(call if_changed,cp,.unstrip)
+$(obj)/simpleImage.$(DTB): vmlinux FORCE
$(call if_changed,objcopy)
- $(call if_changed,uimage)
- $(call if_changed,strip,.strip)
- @echo 'Kernel: $(UIMAGE_OUT) is ready' ' (#'`cat .version`')'
-clean-files += simpleImage.*.unstrip linux.bin.ub
+$(obj)/simpleImage.$(DTB).ub: $(obj)/simpleImage.$(DTB) FORCE
+ $(call if_changed,uimage)
+
+$(obj)/simpleImage.$(DTB).unstrip: vmlinux FORCE
+ $(call if_changed,shipped)
+
+$(obj)/simpleImage.$(DTB).strip: vmlinux FORCE
+ $(call if_changed,strip)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 201caf2..a830a97 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -806,6 +806,7 @@
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_LITTLE_ENDIAN
+ select ZONE_DMA32 if 64BIT
config SIBYTE_SENTOSA
bool "Sibyte BCM91250E-Sentosa"
diff --git a/arch/mips/bcm47xx/workarounds.c b/arch/mips/bcm47xx/workarounds.c
index 1a8a07e..46eddbe 100644
--- a/arch/mips/bcm47xx/workarounds.c
+++ b/arch/mips/bcm47xx/workarounds.c
@@ -5,9 +5,8 @@
#include <bcm47xx_board.h>
#include <bcm47xx.h>
-static void __init bcm47xx_workarounds_netgear_wnr3500l(void)
+static void __init bcm47xx_workarounds_enable_usb_power(int usb_power)
{
- const int usb_power = 12;
int err;
err = gpio_request_one(usb_power, GPIOF_OUT_INIT_HIGH, "usb_power");
@@ -23,7 +22,10 @@ void __init bcm47xx_workarounds(void)
switch (board) {
case BCM47XX_BOARD_NETGEAR_WNR3500L:
- bcm47xx_workarounds_netgear_wnr3500l();
+ bcm47xx_workarounds_enable_usb_power(12);
+ break;
+ case BCM47XX_BOARD_NETGEAR_WNDR3400_V3:
+ bcm47xx_workarounds_enable_usb_power(21);
break;
default:
/* No workaround(s) needed */
diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index 7019e29..bbbf805 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -84,7 +84,7 @@ void __init prom_init(void)
* Here we will start up CPU1 in the background and ask it to
* reconfigure itself then go back to sleep.
*/
- memcpy((void *)0xa0000200, &bmips_smp_movevec, 0x20);
+ memcpy((void *)0xa0000200, bmips_smp_movevec, 0x20);
__sync();
set_c0_cause(C_SW0);
cpumask_set_cpu(1, &bmips_booted_mask);
diff --git a/arch/mips/bcm63xx/reset.c b/arch/mips/bcm63xx/reset.c
index a2af38c..64574e7 100644
--- a/arch/mips/bcm63xx/reset.c
+++ b/arch/mips/bcm63xx/reset.c
@@ -120,7 +120,7 @@
#define BCM6368_RESET_DSL 0
#define BCM6368_RESET_SAR SOFTRESET_6368_SAR_MASK
#define BCM6368_RESET_EPHY SOFTRESET_6368_EPHY_MASK
-#define BCM6368_RESET_ENETSW 0
+#define BCM6368_RESET_ENETSW SOFTRESET_6368_ENETSW_MASK
#define BCM6368_RESET_PCM SOFTRESET_6368_PCM_MASK
#define BCM6368_RESET_MPI SOFTRESET_6368_MPI_MASK
#define BCM6368_RESET_PCIE 0
diff --git a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
index 8241fc6..3839feb 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
@@ -266,7 +266,7 @@ int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id)
} else {
union cvmx_pko_mem_debug8 debug8;
debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8);
- return debug8.cn58xx.doorbell;
+ return debug8.cn50xx.doorbell;
}
case CVMX_CMD_QUEUE_ZIP:
case CVMX_CMD_QUEUE_DFA:
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c
index 807cada..5ba181e 100644
--- a/arch/mips/cavium-octeon/octeon-platform.c
+++ b/arch/mips/cavium-octeon/octeon-platform.c
@@ -501,7 +501,7 @@ static void __init octeon_fdt_set_phy(int eth, int phy_addr)
if (phy_addr >= 256 && alt_phy > 0) {
const struct fdt_property *phy_prop;
struct fdt_property *alt_prop;
- u32 phy_handle_name;
+ fdt32_t phy_handle_name;
/* Use the alt phy node instead.*/
phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL);
diff --git a/arch/mips/fw/sni/sniprom.c b/arch/mips/fw/sni/sniprom.c
index 8772617..80112f2 100644
--- a/arch/mips/fw/sni/sniprom.c
+++ b/arch/mips/fw/sni/sniprom.c
@@ -43,7 +43,7 @@
/* O32 stack has to be 8-byte aligned. */
static u64 o32_stk[4096];
-#define O32_STK &o32_stk[sizeof(o32_stk)]
+#define O32_STK (&o32_stk[ARRAY_SIZE(o32_stk)])
#define __PROM_O32(fun, arg) fun arg __asm__(#fun); \
__asm__(#fun " = call_o32")
diff --git a/arch/mips/include/asm/bmips.h b/arch/mips/include/asm/bmips.h
index bf6a8af..581a6a3 100644
--- a/arch/mips/include/asm/bmips.h
+++ b/arch/mips/include/asm/bmips.h
@@ -75,11 +75,11 @@ static inline int register_bmips_smp_ops(void)
#endif
}
-extern char bmips_reset_nmi_vec;
-extern char bmips_reset_nmi_vec_end;
-extern char bmips_smp_movevec;
-extern char bmips_smp_int_vec;
-extern char bmips_smp_int_vec_end;
+extern char bmips_reset_nmi_vec[];
+extern char bmips_reset_nmi_vec_end[];
+extern char bmips_smp_movevec[];
+extern char bmips_smp_int_vec[];
+extern char bmips_smp_int_vec_end[];
extern int bmips_smp_enabled;
extern int bmips_cpu_offset;
diff --git a/arch/mips/include/asm/cmpxchg.h b/arch/mips/include/asm/cmpxchg.h
index 89e9fb7..520ca16 100644
--- a/arch/mips/include/asm/cmpxchg.h
+++ b/arch/mips/include/asm/cmpxchg.h
@@ -73,8 +73,8 @@ extern unsigned long __xchg_called_with_bad_pointer(void)
extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
unsigned int size);
-static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
- int size)
+static __always_inline
+unsigned long __xchg(volatile void *ptr, unsigned long x, int size)
{
switch (size) {
case 1:
@@ -146,8 +146,9 @@ static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
unsigned long new, unsigned int size);
-static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
- unsigned long new, unsigned int size)
+static __always_inline
+unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+ unsigned long new, unsigned int size)
{
switch (size) {
case 1:
diff --git a/arch/mips/include/asm/kexec.h b/arch/mips/include/asm/kexec.h
index 493a3cc..cfdbe665 100644
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -12,11 +12,11 @@
#include <asm/stacktrace.h>
/* Maximum physical address we can use pages from */
-#define KEXEC_SOURCE_MEMORY_LIMIT (0x20000000)
+#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
/* Maximum address we can reach in physical address mode */
-#define KEXEC_DESTINATION_MEMORY_LIMIT (0x20000000)
+#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
/* Maximum address we can use for the control code buffer */
-#define KEXEC_CONTROL_MEMORY_LIMIT (0x20000000)
+#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
/* Reserve 3*4096 bytes for board-specific info */
#define KEXEC_CONTROL_PAGE_SIZE (4096 + 3*4096)
diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h
index 5f47f76..20eb9c4 100644
--- a/arch/mips/include/asm/octeon/cvmx-pko.h
+++ b/arch/mips/include/asm/octeon/cvmx-pko.h
@@ -611,7 +611,7 @@ static inline void cvmx_pko_get_port_status(uint64_t port_num, uint64_t clear,
pko_reg_read_idx.s.index = cvmx_pko_get_base_queue(port_num);
cvmx_write_csr(CVMX_PKO_REG_READ_IDX, pko_reg_read_idx.u64);
debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8);
- status->doorbell = debug8.cn58xx.doorbell;
+ status->doorbell = debug8.cn50xx.doorbell;
}
}
diff --git a/arch/mips/include/asm/pgtable-64.h b/arch/mips/include/asm/pgtable-64.h
index 93a9dce..813dfe5 100644
--- a/arch/mips/include/asm/pgtable-64.h
+++ b/arch/mips/include/asm/pgtable-64.h
@@ -18,10 +18,12 @@
#include <asm/fixmap.h>
#define __ARCH_USE_5LEVEL_HACK
-#if defined(CONFIG_PAGE_SIZE_64KB) && !defined(CONFIG_MIPS_VA_BITS_48)
+#if CONFIG_PGTABLE_LEVELS == 2
#include <asm-generic/pgtable-nopmd.h>
-#elif !(defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_MIPS_VA_BITS_48))
+#elif CONFIG_PGTABLE_LEVELS == 3
#include <asm-generic/pgtable-nopud.h>
+#else
+#include <asm-generic/5level-fixup.h>
#endif
/*
@@ -216,6 +218,9 @@ static inline unsigned long pgd_page_vaddr(pgd_t pgd)
return pgd_val(pgd);
}
+#define pgd_phys(pgd) virt_to_phys((void *)pgd_val(pgd))
+#define pgd_page(pgd) (pfn_to_page(pgd_phys(pgd) >> PAGE_SHIFT))
+
static inline pud_t *pud_offset(pgd_t *pgd, unsigned long address)
{
return (pud_t *)pgd_page_vaddr(*pgd) + pud_index(address);
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 4993db4..ee26f9a 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -49,8 +49,26 @@ struct thread_info {
.addr_limit = KERNEL_DS, \
}
-/* How to get the thread information struct from C. */
+/*
+ * A pointer to the struct thread_info for the currently executing thread is
+ * held in register $28/$gp.
+ *
+ * We declare __current_thread_info as a global register variable rather than a
+ * local register variable within current_thread_info() because clang doesn't
+ * support explicit local register variables.
+ *
+ * When building the VDSO we take care not to declare the global register
+ * variable because this causes GCC to not preserve the value of $28/$gp in
+ * functions that change its value (which is common in the PIC VDSO when
+ * accessing the GOT). Since the VDSO shouldn't be accessing
+ * __current_thread_info anyway we declare it extern in order to cause a link
+ * failure if it's referenced.
+ */
+#ifdef __VDSO__
+extern struct thread_info *__current_thread_info;
+#else
register struct thread_info *__current_thread_info __asm__("$28");
+#endif
static inline struct thread_info *current_thread_info(void)
{
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 159e83a..5ec546b 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -457,10 +457,10 @@ static void bmips_wr_vec(unsigned long dst, char *start, char *end)
static inline void bmips_nmi_handler_setup(void)
{
- bmips_wr_vec(BMIPS_NMI_RESET_VEC, &bmips_reset_nmi_vec,
- &bmips_reset_nmi_vec_end);
- bmips_wr_vec(BMIPS_WARM_RESTART_VEC, &bmips_smp_int_vec,
- &bmips_smp_int_vec_end);
+ bmips_wr_vec(BMIPS_NMI_RESET_VEC, bmips_reset_nmi_vec,
+ bmips_reset_nmi_vec_end);
+ bmips_wr_vec(BMIPS_WARM_RESTART_VEC, bmips_smp_int_vec,
+ bmips_smp_int_vec_end);
}
struct reset_vec_info {
diff --git a/arch/mips/net/ebpf_jit.c b/arch/mips/net/ebpf_jit.c
index 9bda82ed..3832c46 100644
--- a/arch/mips/net/ebpf_jit.c
+++ b/arch/mips/net/ebpf_jit.c
@@ -586,6 +586,7 @@ static void emit_const_to_reg(struct jit_ctx *ctx, int dst, u64 value)
static int emit_bpf_tail_call(struct jit_ctx *ctx, int this_idx)
{
int off, b_off;
+ int tcc_reg;
ctx->flags |= EBPF_SEEN_TC;
/*
@@ -598,14 +599,14 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int this_idx)
b_off = b_imm(this_idx + 1, ctx);
emit_instr(ctx, bne, MIPS_R_AT, MIPS_R_ZERO, b_off);
/*
- * if (--TCC < 0)
+ * if (TCC-- < 0)
* goto out;
*/
/* Delay slot */
- emit_instr(ctx, daddiu, MIPS_R_T5,
- (ctx->flags & EBPF_TCC_IN_V1) ? MIPS_R_V1 : MIPS_R_S4, -1);
+ tcc_reg = (ctx->flags & EBPF_TCC_IN_V1) ? MIPS_R_V1 : MIPS_R_S4;
+ emit_instr(ctx, daddiu, MIPS_R_T5, tcc_reg, -1);
b_off = b_imm(this_idx + 1, ctx);
- emit_instr(ctx, bltz, MIPS_R_T5, b_off);
+ emit_instr(ctx, bltz, tcc_reg, b_off);
/*
* prog = array->ptrs[index];
* if (prog == NULL)
diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c
index f6d9182..70a1ab6 100644
--- a/arch/mips/txx9/generic/setup.c
+++ b/arch/mips/txx9/generic/setup.c
@@ -960,12 +960,11 @@ void __init txx9_sramc_init(struct resource *r)
goto exit_put;
err = sysfs_create_bin_file(&dev->dev.kobj, &dev->bindata_attr);
if (err) {
- device_unregister(&dev->dev);
iounmap(dev->base);
- kfree(dev);
+ device_unregister(&dev->dev);
}
return;
exit_put:
+ iounmap(dev->base);
put_device(&dev->dev);
- return;
}
diff --git a/arch/nds32/include/asm/bitfield.h b/arch/nds32/include/asm/bitfield.h
index 8e84fc3..19b2841 100644
--- a/arch/nds32/include/asm/bitfield.h
+++ b/arch/nds32/include/asm/bitfield.h
@@ -692,8 +692,8 @@
#define PFM_CTL_offKU1 13 /* Enable user mode event counting for PFMC1 */
#define PFM_CTL_offKU2 14 /* Enable user mode event counting for PFMC2 */
#define PFM_CTL_offSEL0 15 /* The event selection for PFMC0 */
-#define PFM_CTL_offSEL1 21 /* The event selection for PFMC1 */
-#define PFM_CTL_offSEL2 27 /* The event selection for PFMC2 */
+#define PFM_CTL_offSEL1 16 /* The event selection for PFMC1 */
+#define PFM_CTL_offSEL2 22 /* The event selection for PFMC2 */
/* bit 28:31 reserved */
#define PFM_CTL_mskEN0 ( 0x01 << PFM_CTL_offEN0 )
diff --git a/arch/nds32/kernel/setup.c b/arch/nds32/kernel/setup.c
index 63a1a5e..8768358 100644
--- a/arch/nds32/kernel/setup.c
+++ b/arch/nds32/kernel/setup.c
@@ -71,8 +71,9 @@ static const char *hwcap_str[] = {
"div",
"mac",
"l2c",
- "dx_regs",
+ "fpu_dp",
"v2",
+ "dx_regs",
NULL,
};
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index 0c826ad..ee6159d 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -240,7 +240,7 @@
* occured. in fact they never do. if you need them use
* values saved on stack (for SPR_EPC, SPR_ESR) or content
* of r4 (for SPR_EEAR). for details look at EXCEPTION_HANDLE()
- * in 'arch/or32/kernel/head.S'
+ * in 'arch/openrisc/kernel/head.S'
*/
/* =====================================================[ exceptions] === */
diff --git a/arch/openrisc/kernel/head.S b/arch/openrisc/kernel/head.S
index 9fc6b60..31ed257 100644
--- a/arch/openrisc/kernel/head.S
+++ b/arch/openrisc/kernel/head.S
@@ -1728,7 +1728,7 @@
/*
* .data section should be page aligned
- * (look into arch/or32/kernel/vmlinux.lds)
+ * (look into arch/openrisc/kernel/vmlinux.lds.S)
*/
.section .data,"aw"
.align 8192
diff --git a/arch/parisc/include/asm/cmpxchg.h b/arch/parisc/include/asm/cmpxchg.h
index f627c37..ab5c215 100644
--- a/arch/parisc/include/asm/cmpxchg.h
+++ b/arch/parisc/include/asm/cmpxchg.h
@@ -44,8 +44,14 @@ __xchg(unsigned long x, __volatile__ void *ptr, int size)
** if (((unsigned long)p & 0xf) == 0)
** return __ldcw(p);
*/
-#define xchg(ptr, x) \
- ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
+#define xchg(ptr, x) \
+({ \
+ __typeof__(*(ptr)) __ret; \
+ __typeof__(*(ptr)) _x_ = (x); \
+ __ret = (__typeof__(*(ptr))) \
+ __xchg((unsigned long)_x_, (ptr), sizeof(*(ptr))); \
+ __ret; \
+})
/* bug catcher for when unsupported size is used - won't link */
extern void __cmpxchg_called_with_bad_pointer(void);
diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c
index 5eb979d..a1a5e4c 100644
--- a/arch/parisc/kernel/drivers.c
+++ b/arch/parisc/kernel/drivers.c
@@ -789,7 +789,7 @@ EXPORT_SYMBOL(device_to_hwpath);
static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high,
struct device *parent);
-static void walk_lower_bus(struct parisc_device *dev)
+static void __init walk_lower_bus(struct parisc_device *dev)
{
unsigned long io_io_low, io_io_high;
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index c4c0399..e43321f 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -90,11 +90,13 @@
endif
ifdef CONFIG_PPC64
+ifndef CONFIG_CC_IS_CLANG
cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mcall-aixdesc)
aflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
aflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -mabi=elfv2
endif
+endif
ifneq ($(cc-name),clang)
cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -mno-strict-align
@@ -134,6 +136,7 @@
endif
CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
+ifndef CONFIG_CC_IS_CLANG
ifdef CONFIG_CPU_LITTLE_ENDIAN
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,$(call cc-option,-mcall-aixdesc))
AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
@@ -142,10 +145,18 @@
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcall-aixdesc)
AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
endif
+endif
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,$(call cc-option,-mminimal-toc))
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)
-CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 $(MULTIPLEWORD)
+# Clang unconditionally reserves r2 on ppc32 and does not support the flag
+# https://bugs.llvm.org/show_bug.cgi?id=39555
+CFLAGS-$(CONFIG_PPC32) := $(call cc-option, -ffixed-r2)
+
+# Clang doesn't support -mmultiple / -mno-multiple
+# https://bugs.llvm.org/show_bug.cgi?id=39556
+CFLAGS-$(CONFIG_PPC32) += $(call cc-option, $(MULTIPLEWORD))
+
CFLAGS-$(CONFIG_PPC32) += $(call cc-option,-mno-readonly-in-sdata)
ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 25e3184..7d5ddf5 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -32,8 +32,8 @@
endif
BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
- -fno-strict-aliasing -Os -msoft-float -pipe \
- -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \
+ -fno-strict-aliasing -Os -msoft-float -mno-altivec -mno-vsx \
+ -pipe -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \
-D$(compress-y)
ifdef CONFIG_PPC64_BOOT_WRAPPER
diff --git a/arch/powerpc/boot/dts/bamboo.dts b/arch/powerpc/boot/dts/bamboo.dts
index 538e42b..b5861fa 100644
--- a/arch/powerpc/boot/dts/bamboo.dts
+++ b/arch/powerpc/boot/dts/bamboo.dts
@@ -268,8 +268,10 @@
/* Outbound ranges, one memory and one IO,
* later cannot be changed. Chip supports a second
* IO range but we don't use it for now
+ * The chip also supports a larger memory range but
+ * it's not naturally aligned, so our code will break
*/
- ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x40000000
+ ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x20000000
0x02000000 0x00000000 0x00000000 0x00000000 0xe0000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x00000000 0xe8000000 0x00000000 0x00010000>;
diff --git a/arch/powerpc/boot/libfdt_env.h b/arch/powerpc/boot/libfdt_env.h
index 2a0c8b1..9757d4f 100644
--- a/arch/powerpc/boot/libfdt_env.h
+++ b/arch/powerpc/boot/libfdt_env.h
@@ -5,6 +5,10 @@
#include <types.h>
#include <string.h>
+#define INT_MAX ((int)(~0U>>1))
+#define UINT32_MAX ((u32)~0U)
+#define INT32_MAX ((s32)(UINT32_MAX >> 1))
+
#include "of.h"
typedef unsigned long uintptr_t;
diff --git a/arch/powerpc/boot/opal.c b/arch/powerpc/boot/opal.c
index 0272570..dfb199e 100644
--- a/arch/powerpc/boot/opal.c
+++ b/arch/powerpc/boot/opal.c
@@ -13,8 +13,6 @@
#include <libfdt.h>
#include "../include/asm/opal-api.h"
-#ifdef CONFIG_PPC64_BOOT_WRAPPER
-
/* Global OPAL struct used by opal-call.S */
struct opal {
u64 base;
@@ -101,9 +99,3 @@ int opal_console_init(void *devp, struct serial_console_data *scdp)
return 0;
}
-#else
-int opal_console_init(void *devp, struct serial_console_data *scdp)
-{
- return -1;
-}
-#endif /* __powerpc64__ */
diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h
index e398173..d0609c1 100644
--- a/arch/powerpc/include/asm/asm-prototypes.h
+++ b/arch/powerpc/include/asm/asm-prototypes.h
@@ -146,8 +146,11 @@ void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
/* Patch sites */
extern s32 patch__call_flush_count_cache;
extern s32 patch__flush_count_cache_return;
+extern s32 patch__flush_link_stack_return;
+extern s32 patch__call_kvm_flush_link_stack;
extern s32 patch__memset_nocache, patch__memcpy_nocache;
extern long flush_count_cache;
+extern long kvm_flush_link_stack;
#endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index 6a6804c..59b35b9 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -44,6 +44,7 @@ extern int machine_check_e500(struct pt_regs *regs);
extern int machine_check_e200(struct pt_regs *regs);
extern int machine_check_47x(struct pt_regs *regs);
int machine_check_8xx(struct pt_regs *regs);
+int machine_check_83xx(struct pt_regs *regs);
extern void cpu_down_flush_e500v2(void);
extern void cpu_down_flush_e500mc(void);
@@ -214,6 +215,7 @@ static inline void cpu_feature_keys_init(void) { }
#define CPU_FTR_P9_TM_XER_SO_BUG LONG_ASM_CONST(0x0000200000000000)
#define CPU_FTR_P9_TLBIE_STQ_BUG LONG_ASM_CONST(0x0000400000000000)
#define CPU_FTR_P9_TIDR LONG_ASM_CONST(0x0000800000000000)
+#define CPU_FTR_P9_TLBIE_ERAT_BUG LONG_ASM_CONST(0x0001000000000000)
#ifndef __ASSEMBLY__
@@ -460,7 +462,7 @@ static inline void cpu_feature_keys_init(void) { }
CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \
CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \
CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_PKEY | \
- CPU_FTR_P9_TLBIE_STQ_BUG | CPU_FTR_P9_TIDR)
+ CPU_FTR_P9_TLBIE_STQ_BUG | CPU_FTR_P9_TLBIE_ERAT_BUG | CPU_FTR_P9_TIDR)
#define CPU_FTRS_POWER9_DD2_0 CPU_FTRS_POWER9
#define CPU_FTRS_POWER9_DD2_1 (CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD2_1)
#define CPU_FTRS_POWER9_DD2_2 (CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD2_1 | \
diff --git a/arch/powerpc/include/asm/drmem.h b/arch/powerpc/include/asm/drmem.h
index ce242b9..7c1d8e7 100644
--- a/arch/powerpc/include/asm/drmem.h
+++ b/arch/powerpc/include/asm/drmem.h
@@ -99,4 +99,9 @@ void __init walk_drmem_lmbs_early(unsigned long node,
void (*func)(struct drmem_lmb *, const __be32 **));
#endif
+static inline void invalidate_lmb_associativity_index(struct drmem_lmb *lmb)
+{
+ lmb->aa_index = 0xffffffff;
+}
+
#endif /* _ASM_POWERPC_LMB_H */
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index e991821..a061c3d 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -458,9 +458,100 @@ static inline u32 kvmppc_get_xics_latch(void)
return xirr;
}
-static inline void kvmppc_set_host_ipi(int cpu, u8 host_ipi)
+/*
+ * To avoid the need to unnecessarily exit fully to the host kernel, an IPI to
+ * a CPU thread that's running/napping inside of a guest is by default regarded
+ * as a request to wake the CPU (if needed) and continue execution within the
+ * guest, potentially to process new state like externally-generated
+ * interrupts or IPIs sent from within the guest itself (e.g. H_PROD/H_IPI).
+ *
+ * To force an exit to the host kernel, kvmppc_set_host_ipi() must be called
+ * prior to issuing the IPI to set the corresponding 'host_ipi' flag in the
+ * target CPU's PACA. To avoid unnecessary exits to the host, this flag should
+ * be immediately cleared via kvmppc_clear_host_ipi() by the IPI handler on
+ * the receiving side prior to processing the IPI work.
+ *
+ * NOTE:
+ *
+ * We currently issue an smp_mb() at the beginning of kvmppc_set_host_ipi().
+ * This is to guard against sequences such as the following:
+ *
+ * CPU
+ * X: smp_muxed_ipi_set_message():
+ * X: smp_mb()
+ * X: message[RESCHEDULE] = 1
+ * X: doorbell_global_ipi(42):
+ * X: kvmppc_set_host_ipi(42)
+ * X: ppc_msgsnd_sync()/smp_mb()
+ * X: ppc_msgsnd() -> 42
+ * 42: doorbell_exception(): // from CPU X
+ * 42: ppc_msgsync()
+ * 105: smp_muxed_ipi_set_message():
+ * 105: smb_mb()
+ * // STORE DEFERRED DUE TO RE-ORDERING
+ * --105: message[CALL_FUNCTION] = 1
+ * | 105: doorbell_global_ipi(42):
+ * | 105: kvmppc_set_host_ipi(42)
+ * | 42: kvmppc_clear_host_ipi(42)
+ * | 42: smp_ipi_demux_relaxed()
+ * | 42: // returns to executing guest
+ * | // RE-ORDERED STORE COMPLETES
+ * ->105: message[CALL_FUNCTION] = 1
+ * 105: ppc_msgsnd_sync()/smp_mb()
+ * 105: ppc_msgsnd() -> 42
+ * 42: local_paca->kvm_hstate.host_ipi == 0 // IPI ignored
+ * 105: // hangs waiting on 42 to process messages/call_single_queue
+ *
+ * We also issue an smp_mb() at the end of kvmppc_clear_host_ipi(). This is
+ * to guard against sequences such as the following (as well as to create
+ * a read-side pairing with the barrier in kvmppc_set_host_ipi()):
+ *
+ * CPU
+ * X: smp_muxed_ipi_set_message():
+ * X: smp_mb()
+ * X: message[RESCHEDULE] = 1
+ * X: doorbell_global_ipi(42):
+ * X: kvmppc_set_host_ipi(42)
+ * X: ppc_msgsnd_sync()/smp_mb()
+ * X: ppc_msgsnd() -> 42
+ * 42: doorbell_exception(): // from CPU X
+ * 42: ppc_msgsync()
+ * // STORE DEFERRED DUE TO RE-ORDERING
+ * -- 42: kvmppc_clear_host_ipi(42)
+ * | 42: smp_ipi_demux_relaxed()
+ * | 105: smp_muxed_ipi_set_message():
+ * | 105: smb_mb()
+ * | 105: message[CALL_FUNCTION] = 1
+ * | 105: doorbell_global_ipi(42):
+ * | 105: kvmppc_set_host_ipi(42)
+ * | // RE-ORDERED STORE COMPLETES
+ * -> 42: kvmppc_clear_host_ipi(42)
+ * 42: // returns to executing guest
+ * 105: ppc_msgsnd_sync()/smp_mb()
+ * 105: ppc_msgsnd() -> 42
+ * 42: local_paca->kvm_hstate.host_ipi == 0 // IPI ignored
+ * 105: // hangs waiting on 42 to process messages/call_single_queue
+ */
+static inline void kvmppc_set_host_ipi(int cpu)
{
- paca_ptrs[cpu]->kvm_hstate.host_ipi = host_ipi;
+ /*
+ * order stores of IPI messages vs. setting of host_ipi flag
+ *
+ * pairs with the barrier in kvmppc_clear_host_ipi()
+ */
+ smp_mb();
+ paca_ptrs[cpu]->kvm_hstate.host_ipi = 1;
+}
+
+static inline void kvmppc_clear_host_ipi(int cpu)
+{
+ paca_ptrs[cpu]->kvm_hstate.host_ipi = 0;
+ /*
+ * order clearing of host_ipi flag vs. processing of IPI messages
+ *
+ * pairs with the barrier in kvmppc_set_host_ipi()
+ */
+ smp_mb();
}
static inline void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu)
@@ -489,7 +580,10 @@ static inline u32 kvmppc_get_xics_latch(void)
return 0;
}
-static inline void kvmppc_set_host_ipi(int cpu, u8 host_ipi)
+static inline void kvmppc_set_host_ipi(int cpu)
+{}
+
+static inline void kvmppc_clear_host_ipi(int cpu)
{}
static inline void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu)
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 640a4d8..af99716 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -768,6 +768,8 @@
#define SRR1_PROGTRAP 0x00020000 /* Trap */
#define SRR1_PROGADDR 0x00010000 /* SRR0 contains subsequent addr */
+#define SRR1_MCE_MCP 0x00080000 /* Machine check signal caused interrupt */
+
#define SPRN_HSRR0 0x13A /* Save/Restore Register 0 */
#define SPRN_HSRR1 0x13B /* Save/Restore Register 1 */
#define HSRR1_DENORM 0x00100000 /* Denorm exception */
diff --git a/arch/powerpc/include/asm/security_features.h b/arch/powerpc/include/asm/security_features.h
index 759597b..ccf44c1 100644
--- a/arch/powerpc/include/asm/security_features.h
+++ b/arch/powerpc/include/asm/security_features.h
@@ -81,6 +81,9 @@ static inline bool security_ftr_enabled(unsigned long feature)
// Software required to flush count cache on context switch
#define SEC_FTR_FLUSH_COUNT_CACHE 0x0000000000000400ull
+// Software required to flush link stack on context switch
+#define SEC_FTR_FLUSH_LINK_STACK 0x0000000000001000ull
+
// Features enabled by default
#define SEC_FTR_DEFAULT \
diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h
index d89beab..8b957aa 100644
--- a/arch/powerpc/include/asm/sfp-machine.h
+++ b/arch/powerpc/include/asm/sfp-machine.h
@@ -213,30 +213,18 @@
* respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
* (i.e. carry out) is not stored anywhere, and is lost.
*/
-#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
do { \
if (__builtin_constant_p (bh) && (bh) == 0) \
- __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "%r" ((USItype)(ah)), \
- "%r" ((USItype)(al)), \
- "rI" ((USItype)(bl))); \
- else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
- __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "%r" ((USItype)(ah)), \
- "%r" ((USItype)(al)), \
- "rI" ((USItype)(bl))); \
+ __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
+ __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
else \
- __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "%r" ((USItype)(ah)), \
- "r" ((USItype)(bh)), \
- "%r" ((USItype)(al)), \
- "rI" ((USItype)(bl))); \
+ __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \
} while (0)
/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to
@@ -248,44 +236,24 @@
* and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
* and is lost.
*/
-#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
do { \
if (__builtin_constant_p (ah) && (ah) == 0) \
- __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "r" ((USItype)(bh)), \
- "rI" ((USItype)(al)), \
- "r" ((USItype)(bl))); \
- else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0) \
- __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "r" ((USItype)(bh)), \
- "rI" ((USItype)(al)), \
- "r" ((USItype)(bl))); \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+ else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
else if (__builtin_constant_p (bh) && (bh) == 0) \
- __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "r" ((USItype)(ah)), \
- "rI" ((USItype)(al)), \
- "r" ((USItype)(bl))); \
- else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
- __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "r" ((USItype)(ah)), \
- "rI" ((USItype)(al)), \
- "r" ((USItype)(bl))); \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
else \
- __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \
- : "=r" ((USItype)(sh)), \
- "=&r" ((USItype)(sl)) \
- : "r" ((USItype)(ah)), \
- "r" ((USItype)(bh)), \
- "rI" ((USItype)(al)), \
- "r" ((USItype)(bl))); \
+ __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \
} while (0)
/* asm fragments for mul and div */
@@ -294,13 +262,10 @@
* UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
* word product in HIGH_PROD and LOW_PROD.
*/
-#define umul_ppmm(ph, pl, m0, m1) \
+#define umul_ppmm(ph, pl, m0, m1) \
do { \
USItype __m0 = (m0), __m1 = (m1); \
- __asm__ ("mulhwu %0,%1,%2" \
- : "=r" ((USItype)(ph)) \
- : "%r" (__m0), \
- "r" (__m1)); \
+ __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
(pl) = __m0 * __m1; \
} while (0)
@@ -312,9 +277,10 @@
* significant bit of DENOMINATOR must be 1, then the pre-processor symbol
* UDIV_NEEDS_NORMALIZATION is defined to 1.
*/
-#define udiv_qrnnd(q, r, n1, n0, d) \
+#define udiv_qrnnd(q, r, n1, n0, d) \
do { \
- UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
+ UWtype __d1, __d0, __q1, __q0; \
+ UWtype __r1, __r0, __m; \
__d1 = __ll_highpart (d); \
__d0 = __ll_lowpart (d); \
\
@@ -325,7 +291,7 @@
if (__r1 < __m) \
{ \
__q1--, __r1 += (d); \
- if (__r1 >= (d)) /* we didn't get carry when adding to __r1 */ \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
if (__r1 < __m) \
__q1--, __r1 += (d); \
} \
diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h
index 685c723..3198dde 100644
--- a/arch/powerpc/include/asm/spinlock.h
+++ b/arch/powerpc/include/asm/spinlock.h
@@ -19,6 +19,7 @@
*
* (the type definitions are in asm/spinlock_types.h)
*/
+#include <linux/jump_label.h>
#include <linux/irqflags.h>
#ifdef CONFIG_PPC64
#include <asm/paca.h>
@@ -53,10 +54,12 @@
#endif
#ifdef CONFIG_PPC_PSERIES
+DECLARE_STATIC_KEY_FALSE(shared_processor);
+
#define vcpu_is_preempted vcpu_is_preempted
static inline bool vcpu_is_preempted(int cpu)
{
- if (!firmware_has_feature(FW_FEATURE_SPLPAR))
+ if (!static_branch_unlikely(&shared_processor))
return false;
return !!(be32_to_cpu(lppaca_of(cpu).yield_count) & 1);
}
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 1ca9e37..38a25ff 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -260,7 +260,7 @@ do { \
({ \
long __gu_err; \
__long_type(*(ptr)) __gu_val; \
- const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
+ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
__chk_user_ptr(ptr); \
if (!is_kernel_addr((unsigned long)__gu_addr)) \
might_fault(); \
@@ -274,7 +274,7 @@ do { \
({ \
long __gu_err = -EFAULT; \
__long_type(*(ptr)) __gu_val = 0; \
- const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
+ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
might_fault(); \
if (access_ok(VERIFY_READ, __gu_addr, (size))) { \
barrier_nospec(); \
@@ -288,7 +288,7 @@ do { \
({ \
long __gu_err; \
__long_type(*(ptr)) __gu_val; \
- const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
+ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
__chk_user_ptr(ptr); \
barrier_nospec(); \
__get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
diff --git a/arch/powerpc/include/asm/vdso_datapage.h b/arch/powerpc/include/asm/vdso_datapage.h
index bbc06bd..4333b9a 100644
--- a/arch/powerpc/include/asm/vdso_datapage.h
+++ b/arch/powerpc/include/asm/vdso_datapage.h
@@ -86,6 +86,7 @@ struct vdso_data {
__s32 wtom_clock_nsec; /* Wall to monotonic clock nsec */
__s64 wtom_clock_sec; /* Wall to monotonic clock sec */
struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */
+ __u32 hrtimer_res; /* hrtimer resolution */
__u32 syscall_map_64[SYSCALL_MAP_SIZE]; /* map of syscalls */
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
};
@@ -107,6 +108,7 @@ struct vdso_data {
__s32 wtom_clock_nsec;
struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */
__u32 stamp_sec_fraction; /* fractional seconds of stamp_xtime */
+ __u32 hrtimer_res; /* hrtimer resolution */
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
__u32 dcache_block_size; /* L1 d-cache block size */
__u32 icache_block_size; /* L1 i-cache block size */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index eac1879..d450280 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -5,8 +5,8 @@
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
-# Disable clang warning for using setjmp without setjmp.h header
-CFLAGS_crash.o += $(call cc-disable-warning, builtin-requires-header)
+# Avoid clang warnings around longjmp/setjmp declarations
+CFLAGS_crash.o += -ffreestanding
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 7c3738d..50400f2 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -379,6 +379,7 @@ int main(void)
OFFSET(WTOM_CLOCK_NSEC, vdso_data, wtom_clock_nsec);
OFFSET(STAMP_XTIME, vdso_data, stamp_xtime);
OFFSET(STAMP_SEC_FRAC, vdso_data, stamp_sec_fraction);
+ OFFSET(CLOCK_HRTIMER_RES, vdso_data, hrtimer_res);
OFFSET(CFG_ICACHE_BLOCKSZ, vdso_data, icache_block_size);
OFFSET(CFG_DCACHE_BLOCKSZ, vdso_data, dcache_block_size);
OFFSET(CFG_ICACHE_LOGBLOCKSZ, vdso_data, icache_log_block_size);
@@ -409,7 +410,6 @@ int main(void)
DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
DEFINE(CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_COARSE);
DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
- DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
#ifdef CONFIG_BUG
DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry));
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 2da0134..1eab54b 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -1141,6 +1141,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.machine_check = machine_check_generic,
.platform = "ppc603",
},
+#ifdef CONFIG_PPC_83xx
{ /* e300c1 (a 603e core, plus some) on 83xx */
.pvr_mask = 0x7fff0000,
.pvr_value = 0x00830000,
@@ -1151,7 +1152,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
- .machine_check = machine_check_generic,
+ .machine_check = machine_check_83xx,
.platform = "ppc603",
},
{ /* e300c2 (an e300c1 core, plus some, minus FPU) on 83xx */
@@ -1165,7 +1166,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
- .machine_check = machine_check_generic,
+ .machine_check = machine_check_83xx,
.platform = "ppc603",
},
{ /* e300c3 (e300c1, plus one IU, half cache size) on 83xx */
@@ -1179,7 +1180,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
- .machine_check = machine_check_generic,
+ .machine_check = machine_check_83xx,
.num_pmcs = 4,
.oprofile_cpu_type = "ppc/e300",
.oprofile_type = PPC_OPROFILE_FSL_EMB,
@@ -1196,12 +1197,13 @@ static struct cpu_spec __initdata cpu_specs[] = {
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
- .machine_check = machine_check_generic,
+ .machine_check = machine_check_83xx,
.num_pmcs = 4,
.oprofile_cpu_type = "ppc/e300",
.oprofile_type = PPC_OPROFILE_FSL_EMB,
.platform = "ppc603",
},
+#endif
{ /* default match, we assume split I/D cache & TB (non-601)... */
.pvr_mask = 0x00000000,
.pvr_value = 0x00000000,
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index b6fe883..5828144 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -36,7 +36,7 @@ void doorbell_global_ipi(int cpu)
{
u32 tag = get_hard_smp_processor_id(cpu);
- kvmppc_set_host_ipi(cpu, 1);
+ kvmppc_set_host_ipi(cpu);
/* Order previous accesses vs. msgsnd, which is treated as a store */
ppc_msgsnd_sync();
ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
@@ -51,7 +51,7 @@ void doorbell_core_ipi(int cpu)
{
u32 tag = cpu_thread_in_core(cpu);
- kvmppc_set_host_ipi(cpu, 1);
+ kvmppc_set_host_ipi(cpu);
/* Order previous accesses vs. msgsnd, which is treated as a store */
ppc_msgsnd_sync();
ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
@@ -86,7 +86,7 @@ void doorbell_exception(struct pt_regs *regs)
may_hard_irq_enable();
- kvmppc_set_host_ipi(smp_processor_id(), 0);
+ kvmppc_clear_host_ipi(smp_processor_id());
__this_cpu_inc(irq_stat.doorbell_irqs);
smp_ipi_demux_relaxed(); /* already performed the barrier */
diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c
index f3b8e04..c6f41907f 100644
--- a/arch/powerpc/kernel/dt_cpu_ftrs.c
+++ b/arch/powerpc/kernel/dt_cpu_ftrs.c
@@ -717,6 +717,8 @@ static __init void update_tlbie_feature_flag(unsigned long pvr)
WARN_ONCE(1, "Unknown PVR");
cur_cpu_spec->cpu_features |= CPU_FTR_P9_TLBIE_STQ_BUG;
}
+
+ cur_cpu_spec->cpu_features |= CPU_FTR_P9_TLBIE_ERAT_BUG;
}
}
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 110eba4..af1f3d5 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -281,6 +281,10 @@ static void eeh_pe_report_edev(struct eeh_dev *edev, eeh_report_fn fn,
struct pci_driver *driver;
enum pci_ers_result new_result;
+ if (!edev->pdev) {
+ eeh_edev_info(edev, "no device");
+ return;
+ }
device_lock(&edev->pdev->dev);
if (eeh_edev_actionable(edev)) {
driver = eeh_pcid_get(edev->pdev);
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 1b238ec..210d239 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -379,7 +379,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
while (parent) {
if (!(parent->type & EEH_PE_INVALID))
break;
- parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
+ parent->type &= ~EEH_PE_INVALID;
parent = parent->parent;
}
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 7a46e0e..58b5096 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -533,6 +533,7 @@
/* Save LR into r9 */
mflr r9
+ // Flush the link stack
.rept 64
bl .+4
.endr
@@ -542,6 +543,11 @@
.balign 32
/* Restore LR */
1: mtlr r9
+
+ // If we're just flushing the link stack, return here
+3: nop
+ patch_site 3b patch__flush_link_stack_return
+
li r9,0x7fff
mtctr r9
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 90af86f..e1dab9b 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -1123,7 +1123,7 @@
EXCEPTION_PROLOG_COMMON_2(PACA_EXGEN)
EXCEPTION_PROLOG_COMMON_3(0xe60)
addi r3,r1,STACK_FRAME_OVERHEAD
- BRANCH_LINK_TO_FAR(hmi_exception_realmode) /* Function call ABI */
+ BRANCH_LINK_TO_FAR(DOTSYM(hmi_exception_realmode)) /* Function call ABI */
cmpdi cr0,r3,0
/* Windup the stack. */
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 19b4c62..f0dc680 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -785,9 +785,9 @@ dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
vaddr = page_address(page) + offset;
uaddr = (unsigned long)vaddr;
- npages = iommu_num_pages(uaddr, size, IOMMU_PAGE_SIZE(tbl));
if (tbl) {
+ npages = iommu_num_pages(uaddr, size, IOMMU_PAGE_SIZE(tbl));
align = 0;
if (tbl->it_page_shift < PAGE_SHIFT && size >= PAGE_SIZE &&
((unsigned long)vaddr & ~PAGE_MASK) == 0)
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 916ddc4..d37704e 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -634,8 +634,6 @@ void __do_irq(struct pt_regs *regs)
trace_irq_entry(regs);
- check_stack_overflow();
-
/*
* Query the platform PIC for the interrupt & ack it.
*
@@ -667,6 +665,8 @@ void do_IRQ(struct pt_regs *regs)
irqtp = hardirq_ctx[raw_smp_processor_id()];
sirqtp = softirq_ctx[raw_smp_processor_id()];
+ check_stack_overflow();
+
/* Already there ? */
if (unlikely(curtp == irqtp || curtp == sirqtp)) {
__do_irq(regs);
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index 1bf6aae..facc029 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S
@@ -87,7 +87,7 @@
subf r8,r6,r4 /* compute length */
add r8,r8,r5 /* ensure we get enough */
lwz r9,DCACHEL1LOGBLOCKSIZE(r10) /* Get log-2 of cache block size */
- srw. r8,r8,r9 /* compute line count */
+ srd. r8,r8,r9 /* compute line count */
beqlr /* nothing to do? */
mtctr r8
1: dcbst 0,r6
@@ -103,7 +103,7 @@
subf r8,r6,r4 /* compute length */
add r8,r8,r5
lwz r9,ICACHEL1LOGBLOCKSIZE(r10) /* Get log-2 of Icache block size */
- srw. r8,r8,r9 /* compute line count */
+ srd. r8,r8,r9 /* compute line count */
beqlr /* nothing to do? */
mtctr r8
2: icbi 0,r6
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 909c940..02b69a6 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -575,12 +575,11 @@ void flush_all_to_thread(struct task_struct *tsk)
if (tsk->thread.regs) {
preempt_disable();
BUG_ON(tsk != current);
- save_all(tsk);
-
#ifdef CONFIG_SPE
if (tsk->thread.regs->msr & MSR_SPE)
tsk->thread.spefscr = mfspr(SPRN_SPEFSCR);
#endif
+ save_all(tsk);
preempt_enable();
}
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index c4d7078..8e88f78 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -129,7 +129,7 @@ static void __init move_device_tree(void)
p = __va(memblock_alloc(size, PAGE_SIZE));
memcpy(p, initial_boot_params, size);
initial_boot_params = p;
- DBG("Moved device tree to 0x%p\n", p);
+ DBG("Moved device tree to 0x%px\n", p);
}
DBG("<- move_device_tree\n");
@@ -689,7 +689,7 @@ void __init early_init_devtree(void *params)
{
phys_addr_t limit;
- DBG(" -> early_init_devtree(%p)\n", params);
+ DBG(" -> early_init_devtree(%px)\n", params);
/* Too early to BUG_ON(), do it by hand */
if (!early_init_dt_verify(params))
@@ -749,7 +749,7 @@ void __init early_init_devtree(void *params)
memblock_allow_resize();
memblock_dump_all();
- DBG("Phys. mem: %llx\n", memblock_phys_mem_size());
+ DBG("Phys. mem: %llx\n", (unsigned long long)memblock_phys_mem_size());
/* We may need to relocate the flat tree, do it now.
* FIXME .. and the initrd too? */
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 9e41a9d..95d1264 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -985,6 +985,7 @@ int rtas_ibm_suspend_me(u64 handle)
goto out;
}
+ cpu_hotplug_disable();
stop_topology_update();
/* Call function on all CPUs. One of us will make the
@@ -999,6 +1000,7 @@ int rtas_ibm_suspend_me(u64 handle)
printk(KERN_ERR "Error doing global join\n");
start_topology_update();
+ cpu_hotplug_enable();
/* Take down CPUs not online prior to suspend */
cpuret = rtas_offline_cpus_mask(offline_mask);
diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c
index 70568cc..6a3dde9 100644
--- a/arch/powerpc/kernel/security.c
+++ b/arch/powerpc/kernel/security.c
@@ -24,11 +24,12 @@ enum count_cache_flush_type {
COUNT_CACHE_FLUSH_HW = 0x4,
};
static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
+static bool link_stack_flush_enabled;
bool barrier_nospec_enabled;
static bool no_nospec;
static bool btb_flush_enabled;
-#ifdef CONFIG_PPC_FSL_BOOK3E
+#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
static bool no_spectrev2;
#endif
@@ -106,7 +107,7 @@ static __init int barrier_nospec_debugfs_init(void)
device_initcall(barrier_nospec_debugfs_init);
#endif /* CONFIG_DEBUG_FS */
-#ifdef CONFIG_PPC_FSL_BOOK3E
+#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
static int __init handle_nospectre_v2(char *p)
{
no_spectrev2 = true;
@@ -114,6 +115,9 @@ static int __init handle_nospectre_v2(char *p)
return 0;
}
early_param("nospectre_v2", handle_nospectre_v2);
+#endif /* CONFIG_PPC_FSL_BOOK3E || CONFIG_PPC_BOOK3S_64 */
+
+#ifdef CONFIG_PPC_FSL_BOOK3E
void setup_spectre_v2(void)
{
if (no_spectrev2 || cpu_mitigations_off())
@@ -130,32 +134,33 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, cha
thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
- if (rfi_flush || thread_priv) {
+ if (rfi_flush) {
struct seq_buf s;
seq_buf_init(&s, buf, PAGE_SIZE - 1);
- seq_buf_printf(&s, "Mitigation: ");
-
- if (rfi_flush)
- seq_buf_printf(&s, "RFI Flush");
-
- if (rfi_flush && thread_priv)
- seq_buf_printf(&s, ", ");
-
+ seq_buf_printf(&s, "Mitigation: RFI Flush");
if (thread_priv)
- seq_buf_printf(&s, "L1D private per thread");
+ seq_buf_printf(&s, ", L1D private per thread");
seq_buf_printf(&s, "\n");
return s.len;
}
+ if (thread_priv)
+ return sprintf(buf, "Vulnerable: L1D private per thread\n");
+
if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
!security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
return sprintf(buf, "Not affected\n");
return sprintf(buf, "Vulnerable\n");
}
+
+ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return cpu_show_meltdown(dev, attr, buf);
+}
#endif
ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
@@ -201,11 +206,19 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c
if (ccd)
seq_buf_printf(&s, "Indirect branch cache disabled");
+
+ if (link_stack_flush_enabled)
+ seq_buf_printf(&s, ", Software link stack flush");
+
} else if (count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) {
seq_buf_printf(&s, "Mitigation: Software count cache flush");
if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW)
seq_buf_printf(&s, " (hardware accelerated)");
+
+ if (link_stack_flush_enabled)
+ seq_buf_printf(&s, ", Software link stack flush");
+
} else if (btb_flush_enabled) {
seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
} else {
@@ -366,18 +379,49 @@ static __init int stf_barrier_debugfs_init(void)
device_initcall(stf_barrier_debugfs_init);
#endif /* CONFIG_DEBUG_FS */
+static void no_count_cache_flush(void)
+{
+ count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
+ pr_info("count-cache-flush: software flush disabled.\n");
+}
+
static void toggle_count_cache_flush(bool enable)
{
- if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
+ if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE) &&
+ !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK))
+ enable = false;
+
+ if (!enable) {
patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP);
- count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
- pr_info("count-cache-flush: software flush disabled.\n");
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+ patch_instruction_site(&patch__call_kvm_flush_link_stack, PPC_INST_NOP);
+#endif
+ pr_info("link-stack-flush: software flush disabled.\n");
+ link_stack_flush_enabled = false;
+ no_count_cache_flush();
return;
}
+ // This enables the branch from _switch to flush_count_cache
patch_branch_site(&patch__call_flush_count_cache,
(u64)&flush_count_cache, BRANCH_SET_LINK);
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+ // This enables the branch from guest_exit_cont to kvm_flush_link_stack
+ patch_branch_site(&patch__call_kvm_flush_link_stack,
+ (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
+#endif
+
+ pr_info("link-stack-flush: software flush enabled.\n");
+ link_stack_flush_enabled = true;
+
+ // If we just need to flush the link stack, patch an early return
+ if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
+ patch_instruction_site(&patch__flush_link_stack_return, PPC_INST_BLR);
+ no_count_cache_flush();
+ return;
+ }
+
if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
count_cache_flush_type = COUNT_CACHE_FLUSH_SW;
pr_info("count-cache-flush: full software flush sequence enabled.\n");
@@ -391,7 +435,26 @@ static void toggle_count_cache_flush(bool enable)
void setup_count_cache_flush(void)
{
- toggle_count_cache_flush(true);
+ bool enable = true;
+
+ if (no_spectrev2 || cpu_mitigations_off()) {
+ if (security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED) ||
+ security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED))
+ pr_warn("Spectre v2 mitigations not fully under software control, can't disable\n");
+
+ enable = false;
+ }
+
+ /*
+ * There's no firmware feature flag/hypervisor bit to tell us we need to
+ * flush the link stack on context switch. So we set it here if we see
+ * either of the Spectre v2 mitigations that aim to protect userspace.
+ */
+ if (security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED) ||
+ security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE))
+ security_ftr_set(SEC_FTR_FLUSH_LINK_STACK);
+
+ toggle_count_cache_flush(enable);
}
#ifdef CONFIG_DEBUG_FS
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 70f145e..5449e76 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -235,7 +235,7 @@ static u64 scan_dispatch_log(u64 stop_tb)
* Accumulate stolen time by scanning the dispatch trace log.
* Called on entry from user mode.
*/
-void accumulate_stolen_time(void)
+void notrace accumulate_stolen_time(void)
{
u64 sst, ust;
unsigned long save_irq_soft_mask = irq_soft_mask_return();
@@ -929,6 +929,7 @@ void update_vsyscall(struct timekeeper *tk)
vdso_data->wtom_clock_nsec = tk->wall_to_monotonic.tv_nsec;
vdso_data->stamp_xtime = xt;
vdso_data->stamp_sec_fraction = frac_sec;
+ vdso_data->hrtimer_res = hrtimer_resolution;
smp_wmb();
++(vdso_data->tb_update_count);
}
@@ -984,10 +985,14 @@ static void register_decrementer_clockevent(int cpu)
*dec = decrementer_clockevent;
dec->cpumask = cpumask_of(cpu);
+ clockevents_config_and_register(dec, ppc_tb_freq, 2, decrementer_max);
+
printk_once(KERN_DEBUG "clockevent: %s mult[%x] shift[%d] cpu[%d]\n",
dec->name, dec->mult, dec->shift, cpu);
- clockevents_register_device(dec);
+ /* Set values for KVM, see kvm_emulate_dec() */
+ decrementer_clockevent.mult = dec->mult;
+ decrementer_clockevent.shift = dec->shift;
}
static void enable_large_decrementer(void)
@@ -1035,18 +1040,7 @@ static void __init set_decrementer_max(void)
static void __init init_decrementer_clockevent(void)
{
- int cpu = smp_processor_id();
-
- clockevents_calc_mult_shift(&decrementer_clockevent, ppc_tb_freq, 4);
-
- decrementer_clockevent.max_delta_ns =
- clockevent_delta2ns(decrementer_max, &decrementer_clockevent);
- decrementer_clockevent.max_delta_ticks = decrementer_max;
- decrementer_clockevent.min_delta_ns =
- clockevent_delta2ns(2, &decrementer_clockevent);
- decrementer_clockevent.min_delta_ticks = 2;
-
- register_decrementer_clockevent(cpu);
+ register_decrementer_clockevent(smp_processor_id());
}
void secondary_cpu_time_init(void)
diff --git a/arch/powerpc/kernel/vdso32/datapage.S b/arch/powerpc/kernel/vdso32/datapage.S
index 3745113..2a7eb54 100644
--- a/arch/powerpc/kernel/vdso32/datapage.S
+++ b/arch/powerpc/kernel/vdso32/datapage.S
@@ -37,6 +37,7 @@
mtlr r0
addi r3, r3, __kernel_datapage_offset-data_page_branch
lwz r0,0(r3)
+ .cfi_restore lr
add r3,r0,r3
blr
.cfi_endproc
diff --git a/arch/powerpc/kernel/vdso32/gettimeofday.S b/arch/powerpc/kernel/vdso32/gettimeofday.S
index 75cff3f..49eecd2 100644
--- a/arch/powerpc/kernel/vdso32/gettimeofday.S
+++ b/arch/powerpc/kernel/vdso32/gettimeofday.S
@@ -139,6 +139,7 @@
*/
99:
li r0,__NR_clock_gettime
+ .cfi_restore lr
sc
blr
.cfi_endproc
@@ -159,12 +160,15 @@
cror cr0*4+eq,cr0*4+eq,cr1*4+eq
bne cr0,99f
+ mflr r12
+ .cfi_register lr,r12
+ bl __get_datapage@local /* get data page */
+ lwz r5, CLOCK_HRTIMER_RES(r3)
+ mtlr r12
li r3,0
cmpli cr0,r4,0
crclr cr0*4+so
beqlr
- lis r5,CLOCK_REALTIME_RES@h
- ori r5,r5,CLOCK_REALTIME_RES@l
stw r3,TSPC32_TV_SEC(r4)
stw r5,TSPC32_TV_NSEC(r4)
blr
diff --git a/arch/powerpc/kernel/vdso64/cacheflush.S b/arch/powerpc/kernel/vdso64/cacheflush.S
index 69c5af2..228a4a2 100644
--- a/arch/powerpc/kernel/vdso64/cacheflush.S
+++ b/arch/powerpc/kernel/vdso64/cacheflush.S
@@ -39,7 +39,7 @@
subf r8,r6,r4 /* compute length */
add r8,r8,r5 /* ensure we get enough */
lwz r9,CFG_DCACHE_LOGBLOCKSZ(r10)
- srw. r8,r8,r9 /* compute line count */
+ srd. r8,r8,r9 /* compute line count */
crclr cr0*4+so
beqlr /* nothing to do? */
mtctr r8
@@ -56,7 +56,7 @@
subf r8,r6,r4 /* compute length */
add r8,r8,r5
lwz r9,CFG_ICACHE_LOGBLOCKSZ(r10)
- srw. r8,r8,r9 /* compute line count */
+ srd. r8,r8,r9 /* compute line count */
crclr cr0*4+so
beqlr /* nothing to do? */
mtctr r8
diff --git a/arch/powerpc/kernel/vdso64/datapage.S b/arch/powerpc/kernel/vdso64/datapage.S
index abf17fe..bf96686 100644
--- a/arch/powerpc/kernel/vdso64/datapage.S
+++ b/arch/powerpc/kernel/vdso64/datapage.S
@@ -37,6 +37,7 @@
mtlr r0
addi r3, r3, __kernel_datapage_offset-data_page_branch
lwz r0,0(r3)
+ .cfi_restore lr
add r3,r0,r3
blr
.cfi_endproc
diff --git a/arch/powerpc/kernel/vdso64/gettimeofday.S b/arch/powerpc/kernel/vdso64/gettimeofday.S
index afbad2a..020e90d 100644
--- a/arch/powerpc/kernel/vdso64/gettimeofday.S
+++ b/arch/powerpc/kernel/vdso64/gettimeofday.S
@@ -169,6 +169,7 @@
*/
99:
li r0,__NR_clock_gettime
+ .cfi_restore lr
sc
blr
.cfi_endproc
@@ -189,12 +190,15 @@
cror cr0*4+eq,cr0*4+eq,cr1*4+eq
bne cr0,99f
+ mflr r12
+ .cfi_register lr,r12
+ bl V_LOCAL_FUNC(__get_datapage)
+ lwz r5, CLOCK_HRTIMER_RES(r3)
+ mtlr r12
li r3,0
cmpldi cr0,r4,0
crclr cr0*4+so
beqlr
- lis r5,CLOCK_REALTIME_RES@h
- ori r5,r5,CLOCK_REALTIME_RES@l
std r3,TSPC64_TV_SEC(r4)
std r5,TSPC64_TV_NSEC(r4)
blr
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
index 281f074..cc05f34 100644
--- a/arch/powerpc/kvm/book3s.c
+++ b/arch/powerpc/kvm/book3s.c
@@ -78,8 +78,11 @@ void kvmppc_unfixup_split_real(struct kvm_vcpu *vcpu)
{
if (vcpu->arch.hflags & BOOK3S_HFLAG_SPLIT_HACK) {
ulong pc = kvmppc_get_pc(vcpu);
+ ulong lr = kvmppc_get_lr(vcpu);
if ((pc & SPLIT_HACK_MASK) == SPLIT_HACK_OFFS)
kvmppc_set_pc(vcpu, pc & ~SPLIT_HACK_MASK);
+ if ((lr & SPLIT_HACK_MASK) == SPLIT_HACK_OFFS)
+ kvmppc_set_lr(vcpu, lr & ~SPLIT_HACK_MASK);
vcpu->arch.hflags &= ~BOOK3S_HFLAG_SPLIT_HACK;
}
}
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index 07a8004..65486c3 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -401,7 +401,7 @@ static long kvmppc_tce_iommu_do_unmap(struct kvm *kvm,
long ret;
if (WARN_ON_ONCE(iommu_tce_xchg(tbl, entry, &hpa, &dir)))
- return H_HARDWARE;
+ return H_TOO_HARD;
if (dir == DMA_NONE)
return H_SUCCESS;
@@ -449,15 +449,15 @@ long kvmppc_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl,
return H_TOO_HARD;
if (WARN_ON_ONCE(mm_iommu_ua_to_hpa(mem, ua, tbl->it_page_shift, &hpa)))
- return H_HARDWARE;
+ return H_TOO_HARD;
if (mm_iommu_mapped_inc(mem))
- return H_CLOSED;
+ return H_TOO_HARD;
ret = iommu_tce_xchg(tbl, entry, &hpa, &dir);
if (WARN_ON_ONCE(ret)) {
mm_iommu_mapped_dec(mem);
- return H_HARDWARE;
+ return H_TOO_HARD;
}
if (dir != DMA_NONE)
diff --git a/arch/powerpc/kvm/book3s_64_vio_hv.c b/arch/powerpc/kvm/book3s_64_vio_hv.c
index eb8b115..d258ed4 100644
--- a/arch/powerpc/kvm/book3s_64_vio_hv.c
+++ b/arch/powerpc/kvm/book3s_64_vio_hv.c
@@ -300,10 +300,10 @@ static long kvmppc_rm_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl,
if (WARN_ON_ONCE_RM(mm_iommu_ua_to_hpa_rm(mem, ua, tbl->it_page_shift,
&hpa)))
- return H_HARDWARE;
+ return H_TOO_HARD;
if (WARN_ON_ONCE_RM(mm_iommu_mapped_inc(mem)))
- return H_CLOSED;
+ return H_TOO_HARD;
ret = iommu_tce_xchg_rm(kvm->mm, tbl, entry, &hpa, &dir);
if (ret) {
@@ -501,7 +501,7 @@ long kvmppc_rm_h_put_tce_indirect(struct kvm_vcpu *vcpu,
rmap = (void *) vmalloc_to_phys(rmap);
if (WARN_ON_ONCE_RM(!rmap))
- return H_HARDWARE;
+ return H_TOO_HARD;
/*
* Synchronize with the MMU notifier callbacks in
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 7c68d83..02ab86b 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -434,6 +434,37 @@ static inline int is_mmio_hpte(unsigned long v, unsigned long r)
(HPTE_R_KEY_HI | HPTE_R_KEY_LO));
}
+static inline void fixup_tlbie_lpid(unsigned long rb_value, unsigned long lpid)
+{
+
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ /* Radix flush for a hash guest */
+
+ unsigned long rb,rs,prs,r,ric;
+
+ rb = PPC_BIT(52); /* IS = 2 */
+ rs = 0; /* lpid = 0 */
+ prs = 0; /* partition scoped */
+ r = 1; /* radix format */
+ ric = 0; /* RIC_FLSUH_TLB */
+
+ /*
+ * Need the extra ptesync to make sure we don't
+ * re-order the tlbie
+ */
+ asm volatile("ptesync": : :"memory");
+ asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
+ : : "r"(rb), "i"(r), "i"(prs),
+ "i"(ric), "r"(rs) : "memory");
+ }
+
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ asm volatile(PPC_TLBIE_5(%0,%1,0,0,0) : :
+ "r" (rb_value), "r" (lpid));
+ }
+}
+
static void do_tlbies(struct kvm *kvm, unsigned long *rbvalues,
long npages, int global, bool need_sync)
{
@@ -452,16 +483,7 @@ static void do_tlbies(struct kvm *kvm, unsigned long *rbvalues,
"r" (rbvalues[i]), "r" (kvm->arch.lpid));
}
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- /*
- * Need the extra ptesync to make sure we don't
- * re-order the tlbie
- */
- asm volatile("ptesync": : :"memory");
- asm volatile(PPC_TLBIE_5(%0,%1,0,0,0) : :
- "r" (rbvalues[0]), "r" (kvm->arch.lpid));
- }
-
+ fixup_tlbie_lpid(rbvalues[i - 1], kvm->arch.lpid);
asm volatile("eieio; tlbsync; ptesync" : : : "memory");
} else {
if (need_sync)
diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c
index 758d1d2..aaafb9f 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_xics.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c
@@ -61,7 +61,7 @@ static inline void icp_send_hcore_msg(int hcore, struct kvm_vcpu *vcpu)
hcpu = hcore << threads_shift;
kvmppc_host_rm_ops_hv->rm_core[hcore].rm_data = vcpu;
smp_muxed_ipi_set_message(hcpu, PPC_MSG_RM_HOST_ACTION);
- kvmppc_set_host_ipi(hcpu, 1);
+ kvmppc_set_host_ipi(hcpu);
smp_mb();
kvmhv_rm_send_ipi(hcpu);
}
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index f1878e1..7fe3077 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -18,6 +18,7 @@
*/
#include <asm/ppc_asm.h>
+#include <asm/code-patching-asm.h>
#include <asm/kvm_asm.h>
#include <asm/reg.h>
#include <asm/mmu.h>
@@ -1559,6 +1560,10 @@
1:
#endif /* CONFIG_KVM_XICS */
+ /* Possibly flush the link stack here. */
+1: nop
+ patch_site 1b patch__call_kvm_flush_link_stack
+
/* For hash guest, read the guest SLB and save it away */
ld r5, VCPU_KVM(r9)
lbz r0, KVM_RADIX(r5)
@@ -2107,6 +2112,29 @@
mtlr r0
blr
+.balign 32
+.global kvm_flush_link_stack
+kvm_flush_link_stack:
+ /* Save LR into r0 */
+ mflr r0
+
+ /* Flush the link stack. On Power8 it's up to 32 entries in size. */
+ .rept 32
+ bl .+4
+ .endr
+
+ /* And on Power9 it's up to 64. */
+BEGIN_FTR_SECTION
+ .rept 32
+ bl .+4
+ .endr
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
+
+ /* Restore LR */
+ mtlr r0
+ blr
+
+
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
/*
* Softpatch interrupt for transactional memory emulation cases
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 365526e..6e0ff8b 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -633,21 +633,22 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
switch (TRAP(regs)) {
case 0x300:
case 0x380:
- printk(KERN_ALERT "Unable to handle kernel paging request for "
- "data at address 0x%08lx\n", regs->dar);
+ pr_alert("BUG: %s at 0x%08lx\n",
+ regs->dar < PAGE_SIZE ? "Kernel NULL pointer dereference" :
+ "Unable to handle kernel data access", regs->dar);
break;
case 0x400:
case 0x480:
- printk(KERN_ALERT "Unable to handle kernel paging request for "
- "instruction fetch\n");
+ pr_alert("BUG: Unable to handle kernel instruction fetch%s",
+ regs->nip < PAGE_SIZE ? " (NULL pointer?)\n" : "\n");
break;
case 0x600:
- printk(KERN_ALERT "Unable to handle kernel paging request for "
- "unaligned access at address 0x%08lx\n", regs->dar);
+ pr_alert("BUG: Unable to handle kernel unaligned access at 0x%08lx\n",
+ regs->dar);
break;
default:
- printk(KERN_ALERT "Unable to handle kernel paging request for "
- "unknown fault\n");
+ pr_alert("BUG: Unable to handle unknown paging fault at 0x%08lx\n",
+ regs->dar);
break;
}
printk(KERN_ALERT "Faulting instruction address: 0x%08lx\n",
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
index 0c13561..42a48c5 100644
--- a/arch/powerpc/mm/hash_native_64.c
+++ b/arch/powerpc/mm/hash_native_64.c
@@ -201,8 +201,31 @@ static inline unsigned long ___tlbie(unsigned long vpn, int psize,
return va;
}
-static inline void fixup_tlbie(unsigned long vpn, int psize, int apsize, int ssize)
+static inline void fixup_tlbie_vpn(unsigned long vpn, int psize,
+ int apsize, int ssize)
{
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ /* Radix flush for a hash guest */
+
+ unsigned long rb,rs,prs,r,ric;
+
+ rb = PPC_BIT(52); /* IS = 2 */
+ rs = 0; /* lpid = 0 */
+ prs = 0; /* partition scoped */
+ r = 1; /* radix format */
+ ric = 0; /* RIC_FLSUH_TLB */
+
+ /*
+ * Need the extra ptesync to make sure we don't
+ * re-order the tlbie
+ */
+ asm volatile("ptesync": : :"memory");
+ asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
+ : : "r"(rb), "i"(r), "i"(prs),
+ "i"(ric), "r"(rs) : "memory");
+ }
+
+
if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
/* Need the extra ptesync to ensure we don't reorder tlbie*/
asm volatile("ptesync": : :"memory");
@@ -287,7 +310,7 @@ static inline void tlbie(unsigned long vpn, int psize, int apsize,
asm volatile("ptesync": : :"memory");
} else {
__tlbie(vpn, psize, apsize, ssize);
- fixup_tlbie(vpn, psize, apsize, ssize);
+ fixup_tlbie_vpn(vpn, psize, apsize, ssize);
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
if (lock_tlbie && !use_local)
@@ -860,7 +883,7 @@ static void native_flush_hash_range(unsigned long number, int local)
/*
* Just do one more with the last used values.
*/
- fixup_tlbie(vpn, psize, psize, ssize);
+ fixup_tlbie_vpn(vpn, psize, psize, ssize);
asm volatile("eieio; tlbsync; ptesync":::"memory");
if (lock_tlbie)
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index b1007e9..8894c8f 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -296,10 +296,18 @@ int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
ret = mmu_hash_ops.hpte_insert(hpteg, vpn, paddr, tprot,
HPTE_V_BOLTED, psize, psize,
ssize);
-
+ if (ret == -1) {
+ /* Try to remove a non bolted entry */
+ ret = mmu_hash_ops.hpte_remove(hpteg);
+ if (ret != -1)
+ ret = mmu_hash_ops.hpte_insert(hpteg, vpn, paddr, tprot,
+ HPTE_V_BOLTED, psize, psize,
+ ssize);
+ }
if (ret < 0)
break;
+ cond_resched();
#ifdef CONFIG_DEBUG_PAGEALLOC
if (debug_pagealloc_enabled() &&
(paddr >> PAGE_SHIFT) < linear_map_hash_count)
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 04ccb27..9a6afd9 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -344,6 +344,14 @@ void __init mem_init(void)
BUILD_BUG_ON(MMU_PAGE_COUNT > 16);
#ifdef CONFIG_SWIOTLB
+ /*
+ * Some platforms (e.g. 85xx) limit DMA-able memory way below
+ * 4G. We force memblock to bottom-up mode to ensure that the
+ * memory allocated in swiotlb_init() is DMA-able.
+ * As it's the last memblock allocation, no need to reset it
+ * back to to-down.
+ */
+ memblock_set_bottom_up(true);
swiotlb_init(0);
#endif
diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index 3ea4c1f..69caeb5 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -294,15 +294,15 @@ static int __meminit create_physical_mapping(unsigned long start,
}
if (split_text_mapping && (mapping_size == PUD_SIZE) &&
- (addr <= __pa_symbol(__init_begin)) &&
- (addr + mapping_size) >= __pa_symbol(_stext)) {
+ (addr < __pa_symbol(__init_begin)) &&
+ (addr + mapping_size) > __pa_symbol(__init_begin)) {
max_mapping_size = PMD_SIZE;
goto retry;
}
if (split_text_mapping && (mapping_size == PMD_SIZE) &&
- (addr <= __pa_symbol(__init_begin)) &&
- (addr + mapping_size) >= __pa_symbol(_stext)) {
+ (addr < __pa_symbol(__init_begin)) &&
+ (addr + mapping_size) > __pa_symbol(__init_begin)) {
mapping_size = PAGE_SIZE;
psize = mmu_virtual_psize;
}
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index bea6c54..0678327 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -52,7 +52,7 @@ struct batrange { /* stores address ranges mapped by BATs */
phys_addr_t v_block_mapped(unsigned long va)
{
int b;
- for (b = 0; b < 4; ++b)
+ for (b = 0; b < ARRAY_SIZE(bat_addrs); ++b)
if (va >= bat_addrs[b].start && va < bat_addrs[b].limit)
return bat_addrs[b].phys + (va - bat_addrs[b].start);
return 0;
@@ -64,7 +64,7 @@ phys_addr_t v_block_mapped(unsigned long va)
unsigned long p_block_mapped(phys_addr_t pa)
{
int b;
- for (b = 0; b < 4; ++b)
+ for (b = 0; b < ARRAY_SIZE(bat_addrs); ++b)
if (pa >= bat_addrs[b].phys
&& pa < (bat_addrs[b].limit-bat_addrs[b].start)
+bat_addrs[b].phys)
diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c
index 9f574e5..2f162c6 100644
--- a/arch/powerpc/mm/slb.c
+++ b/arch/powerpc/mm/slb.c
@@ -355,7 +355,7 @@ void slb_initialize(void)
#endif
}
- get_paca()->stab_rr = SLB_NUM_BOLTED;
+ get_paca()->stab_rr = SLB_NUM_BOLTED - 1;
lflags = SLB_VSID_KERNEL | linear_llp;
vflags = SLB_VSID_KERNEL | vmalloc_llp;
diff --git a/arch/powerpc/mm/tlb-radix.c b/arch/powerpc/mm/tlb-radix.c
index 0cddae4..1749f15f 100644
--- a/arch/powerpc/mm/tlb-radix.c
+++ b/arch/powerpc/mm/tlb-radix.c
@@ -215,21 +215,82 @@ static inline void __tlbie_lpid_va(unsigned long va, unsigned long lpid,
trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
}
-static inline void fixup_tlbie(void)
+
+static inline void fixup_tlbie_va(unsigned long va, unsigned long pid,
+ unsigned long ap)
{
- unsigned long pid = 0;
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_va(va, 0, ap, RIC_FLUSH_TLB);
+ }
+
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
+ }
+}
+
+static inline void fixup_tlbie_va_range(unsigned long va, unsigned long pid,
+ unsigned long ap)
+{
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_pid(0, RIC_FLUSH_TLB);
+ }
+
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
+ }
+}
+
+static inline void fixup_tlbie_pid(unsigned long pid)
+{
+ /*
+ * We can use any address for the invalidation, pick one which is
+ * probably unused as an optimisation.
+ */
unsigned long va = ((1UL << 52) - 1);
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_pid(0, RIC_FLUSH_TLB);
+ }
+
if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
asm volatile("ptesync": : :"memory");
__tlbie_va(va, pid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
}
}
+
+static inline void fixup_tlbie_lpid_va(unsigned long va, unsigned long lpid,
+ unsigned long ap)
+{
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_lpid_va(va, 0, ap, RIC_FLUSH_TLB);
+ }
+
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_lpid_va(va, lpid, ap, RIC_FLUSH_TLB);
+ }
+}
+
static inline void fixup_tlbie_lpid(unsigned long lpid)
{
+ /*
+ * We can use any address for the invalidation, pick one which is
+ * probably unused as an optimisation.
+ */
unsigned long va = ((1UL << 52) - 1);
+ if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
+ asm volatile("ptesync": : :"memory");
+ __tlbie_lpid(0, RIC_FLUSH_TLB);
+ }
+
if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
asm volatile("ptesync": : :"memory");
__tlbie_lpid_va(va, lpid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
@@ -277,6 +338,7 @@ static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
switch (ric) {
case RIC_FLUSH_TLB:
__tlbie_pid(pid, RIC_FLUSH_TLB);
+ fixup_tlbie_pid(pid);
break;
case RIC_FLUSH_PWC:
__tlbie_pid(pid, RIC_FLUSH_PWC);
@@ -284,8 +346,8 @@ static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
case RIC_FLUSH_ALL:
default:
__tlbie_pid(pid, RIC_FLUSH_ALL);
+ fixup_tlbie_pid(pid);
}
- fixup_tlbie();
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
@@ -329,6 +391,7 @@ static inline void _tlbie_lpid(unsigned long lpid, unsigned long ric)
switch (ric) {
case RIC_FLUSH_TLB:
__tlbie_lpid(lpid, RIC_FLUSH_TLB);
+ fixup_tlbie_lpid(lpid);
break;
case RIC_FLUSH_PWC:
__tlbie_lpid(lpid, RIC_FLUSH_PWC);
@@ -336,8 +399,8 @@ static inline void _tlbie_lpid(unsigned long lpid, unsigned long ric)
case RIC_FLUSH_ALL:
default:
__tlbie_lpid(lpid, RIC_FLUSH_ALL);
+ fixup_tlbie_lpid(lpid);
}
- fixup_tlbie_lpid(lpid);
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
@@ -366,6 +429,7 @@ static inline void _tlbiel_lpid_guest(unsigned long lpid, unsigned long ric)
__tlbiel_lpid_guest(lpid, set, RIC_FLUSH_TLB);
asm volatile("ptesync": : :"memory");
+ asm volatile(PPC_INVALIDATE_ERAT : : :"memory");
}
@@ -410,6 +474,8 @@ static inline void __tlbie_va_range(unsigned long start, unsigned long end,
for (addr = start; addr < end; addr += page_size)
__tlbie_va(addr, pid, ap, RIC_FLUSH_TLB);
+
+ fixup_tlbie_va_range(addr - page_size, pid, ap);
}
static inline void _tlbie_va(unsigned long va, unsigned long pid,
@@ -419,7 +485,7 @@ static inline void _tlbie_va(unsigned long va, unsigned long pid,
asm volatile("ptesync": : :"memory");
__tlbie_va(va, pid, ap, ric);
- fixup_tlbie();
+ fixup_tlbie_va(va, pid, ap);
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
@@ -430,7 +496,7 @@ static inline void _tlbie_lpid_va(unsigned long va, unsigned long lpid,
asm volatile("ptesync": : :"memory");
__tlbie_lpid_va(va, lpid, ap, ric);
- fixup_tlbie_lpid(lpid);
+ fixup_tlbie_lpid_va(va, lpid, ap);
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
@@ -442,7 +508,6 @@ static inline void _tlbie_va_range(unsigned long start, unsigned long end,
if (also_pwc)
__tlbie_pid(pid, RIC_FLUSH_PWC);
__tlbie_va_range(start, end, pid, page_size, psize);
- fixup_tlbie();
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
@@ -773,7 +838,7 @@ static inline void __radix__flush_tlb_range(struct mm_struct *mm,
if (gflush)
__tlbie_va_range(gstart, gend, pid,
PUD_SIZE, MMU_PAGE_1G);
- fixup_tlbie();
+
asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
}
@@ -1007,7 +1072,6 @@ void radix__flush_tlb_collapsed_pmd(struct mm_struct *mm, unsigned long addr)
goto local;
}
_tlbie_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
- goto local;
} else {
local:
_tlbiel_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index 279a51b..7e3ab47 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -950,6 +950,19 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
}
/*
+ * If we have seen a tail call, we need a second pass.
+ * This is because bpf_jit_emit_common_epilogue() is called
+ * from bpf_jit_emit_tail_call() with a not yet stable ctx->seen.
+ */
+ if (cgctx.seen & SEEN_TAILCALL) {
+ cgctx.idx = 0;
+ if (bpf_jit_build_body(fp, 0, &cgctx, addrs, false)) {
+ fp = org_fp;
+ goto out_addrs;
+ }
+ }
+
+ /*
* Pretend to build prologue, given the features we've seen. This will
* update ctgtx.idx as it pretends to output instructions, then we can
* calculate total size from idx.
diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c
index 6a2f65d..053b8e9 100644
--- a/arch/powerpc/perf/isa207-common.c
+++ b/arch/powerpc/perf/isa207-common.c
@@ -148,6 +148,14 @@ static bool is_thresh_cmp_valid(u64 event)
return true;
}
+static unsigned int dc_ic_rld_quad_l1_sel(u64 event)
+{
+ unsigned int cache;
+
+ cache = (event >> EVENT_CACHE_SEL_SHIFT) & MMCR1_DC_IC_QUAL_MASK;
+ return cache;
+}
+
static inline u64 isa207_find_source(u64 idx, u32 sub_idx)
{
u64 ret = PERF_MEM_NA;
@@ -288,10 +296,10 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp)
* have a cache selector of zero. The bank selector (bit 3) is
* irrelevant, as long as the rest of the value is 0.
*/
- if (cache & 0x7)
+ if (!cpu_has_feature(CPU_FTR_ARCH_300) && (cache & 0x7))
return -1;
- } else if (event & EVENT_IS_L1) {
+ } else if (cpu_has_feature(CPU_FTR_ARCH_300) || (event & EVENT_IS_L1)) {
mask |= CNST_L1_QUAL_MASK;
value |= CNST_L1_QUAL_VAL(cache);
}
@@ -394,11 +402,14 @@ int isa207_compute_mmcr(u64 event[], int n_ev,
/* In continuous sampling mode, update SDAR on TLB miss */
mmcra_sdar_mode(event[i], &mmcra);
- if (event[i] & EVENT_IS_L1) {
- cache = event[i] >> EVENT_CACHE_SEL_SHIFT;
- mmcr1 |= (cache & 1) << MMCR1_IC_QUAL_SHIFT;
- cache >>= 1;
- mmcr1 |= (cache & 1) << MMCR1_DC_QUAL_SHIFT;
+ if (cpu_has_feature(CPU_FTR_ARCH_300)) {
+ cache = dc_ic_rld_quad_l1_sel(event[i]);
+ mmcr1 |= (cache) << MMCR1_DC_IC_QUAL_SHIFT;
+ } else {
+ if (event[i] & EVENT_IS_L1) {
+ cache = dc_ic_rld_quad_l1_sel(event[i]);
+ mmcr1 |= (cache) << MMCR1_DC_IC_QUAL_SHIFT;
+ }
}
if (is_event_marked(event[i])) {
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index 0028f4b..e5a6216 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -163,8 +163,8 @@
#define MMCR1_COMBINE_SHIFT(pmc) (35 - ((pmc) - 1))
#define MMCR1_PMCSEL_SHIFT(pmc) (24 - (((pmc) - 1)) * 8)
#define MMCR1_FAB_SHIFT 36
-#define MMCR1_DC_QUAL_SHIFT 47
-#define MMCR1_IC_QUAL_SHIFT 46
+#define MMCR1_DC_IC_QUAL_MASK 0x3
+#define MMCR1_DC_IC_QUAL_SHIFT 46
/* MMCR1 Combine bits macro for power9 */
#define p9_MMCR1_COMBINE_SHIFT(pmc) (38 - ((pmc - 1) * 2))
diff --git a/arch/powerpc/platforms/83xx/misc.c b/arch/powerpc/platforms/83xx/misc.c
index d75c981..2b6589f 100644
--- a/arch/powerpc/platforms/83xx/misc.c
+++ b/arch/powerpc/platforms/83xx/misc.c
@@ -14,6 +14,7 @@
#include <linux/of_platform.h>
#include <linux/pci.h>
+#include <asm/debug.h>
#include <asm/io.h>
#include <asm/hw_irq.h>
#include <asm/ipic.h>
@@ -150,3 +151,19 @@ void __init mpc83xx_setup_arch(void)
mpc83xx_setup_pci();
}
+
+int machine_check_83xx(struct pt_regs *regs)
+{
+ u32 mask = 1 << (31 - IPIC_MCP_WDT);
+
+ if (!(regs->msr & SRR1_MCE_MCP) || !(ipic_get_mcp_status() & mask))
+ return machine_check_generic(regs);
+ ipic_clear_mcp_status(mask);
+
+ if (debugger_fault_handler(regs))
+ return 1;
+
+ die("Watchdog NMI Reset", regs, 0);
+
+ return 1;
+}
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 3c1beae..9dd5b89 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -578,8 +578,8 @@ static void pnv_eeh_get_phb_diag(struct eeh_pe *pe)
static int pnv_eeh_get_phb_state(struct eeh_pe *pe)
{
struct pnv_phb *phb = pe->phb->private_data;
- u8 fstate;
- __be16 pcierr;
+ u8 fstate = 0;
+ __be16 pcierr = 0;
s64 rc;
int result = 0;
@@ -617,8 +617,8 @@ static int pnv_eeh_get_phb_state(struct eeh_pe *pe)
static int pnv_eeh_get_pe_state(struct eeh_pe *pe)
{
struct pnv_phb *phb = pe->phb->private_data;
- u8 fstate;
- __be16 pcierr;
+ u8 fstate = 0;
+ __be16 pcierr = 0;
s64 rc;
int result;
diff --git a/arch/powerpc/platforms/powernv/memtrace.c b/arch/powerpc/platforms/powernv/memtrace.c
index a29fdf8..dd3cc46 100644
--- a/arch/powerpc/platforms/powernv/memtrace.c
+++ b/arch/powerpc/platforms/powernv/memtrace.c
@@ -70,6 +70,7 @@ static int change_memblock_state(struct memory_block *mem, void *arg)
return 0;
}
+/* called with device_hotplug_lock held */
static bool memtrace_offline_pages(u32 nid, u64 start_pfn, u64 nr_pages)
{
u64 end_pfn = start_pfn + nr_pages - 1;
@@ -110,6 +111,7 @@ static u64 memtrace_alloc_node(u32 nid, u64 size)
/* Trace memory needs to be aligned to the size */
end_pfn = round_down(end_pfn - nr_pages, nr_pages);
+ lock_device_hotplug();
for (base_pfn = end_pfn; base_pfn > start_pfn; base_pfn -= nr_pages) {
if (memtrace_offline_pages(nid, base_pfn, nr_pages) == true) {
/*
@@ -118,7 +120,6 @@ static u64 memtrace_alloc_node(u32 nid, u64 size)
* we never try to remove memory that spans two iomem
* resources.
*/
- lock_device_hotplug();
end_pfn = base_pfn + nr_pages;
for (pfn = base_pfn; pfn < end_pfn; pfn += bytes>> PAGE_SHIFT) {
remove_memory(nid, pfn << PAGE_SHIFT, bytes);
@@ -127,6 +128,7 @@ static u64 memtrace_alloc_node(u32 nid, u64 size)
return base_pfn << PAGE_SHIFT;
}
}
+ unlock_device_hotplug();
return 0;
}
@@ -242,9 +244,11 @@ static int memtrace_online(void)
* we need to online the memory ourselves.
*/
if (!memhp_auto_online) {
+ lock_device_hotplug();
walk_memory_range(PFN_DOWN(ent->start),
PFN_UP(ent->start + ent->size - 1),
NULL, online_mem_block);
+ unlock_device_hotplug();
}
/*
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 326ca62..ee63749 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -605,8 +605,8 @@ static int pnv_ioda_unfreeze_pe(struct pnv_phb *phb, int pe_no, int opt)
static int pnv_ioda_get_pe_state(struct pnv_phb *phb, int pe_no)
{
struct pnv_ioda_pe *slave, *pe;
- u8 fstate, state;
- __be16 pcierr;
+ u8 fstate = 0, state;
+ __be16 pcierr = 0;
s64 rc;
/* Sanity check on PE number */
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 13aef23..db230a35 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -602,8 +602,8 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no)
static void pnv_pci_config_check_eeh(struct pci_dn *pdn)
{
struct pnv_phb *phb = pdn->phb->private_data;
- u8 fstate;
- __be16 pcierr;
+ u8 fstate = 0;
+ __be16 pcierr = 0;
unsigned int pe_no;
s64 rc;
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index db09c70..3d3c989 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -150,20 +150,25 @@ static int pnv_smp_cpu_disable(void)
return 0;
}
+static void pnv_flush_interrupts(void)
+{
+ if (cpu_has_feature(CPU_FTR_ARCH_300)) {
+ if (xive_enabled())
+ xive_flush_interrupt();
+ else
+ icp_opal_flush_interrupt();
+ } else {
+ icp_native_flush_interrupt();
+ }
+}
+
static void pnv_smp_cpu_kill_self(void)
{
+ unsigned long srr1, unexpected_mask, wmask;
unsigned int cpu;
- unsigned long srr1, wmask;
u64 lpcr_val;
/* Standard hot unplug procedure */
- /*
- * This hard disables local interurpts, ensuring we have no lazy
- * irqs pending.
- */
- WARN_ON(irqs_disabled());
- hard_irq_disable();
- WARN_ON(lazy_irq_pending());
idle_task_exit();
current->active_mm = NULL; /* for sanity */
@@ -177,6 +182,27 @@ static void pnv_smp_cpu_kill_self(void)
wmask = SRR1_WAKEMASK_P8;
/*
+ * This turns the irq soft-disabled state we're called with, into a
+ * hard-disabled state with pending irq_happened interrupts cleared.
+ *
+ * PACA_IRQ_DEC - Decrementer should be ignored.
+ * PACA_IRQ_HMI - Can be ignored, processing is done in real mode.
+ * PACA_IRQ_DBELL, EE, PMI - Unexpected.
+ */
+ hard_irq_disable();
+ if (generic_check_cpu_restart(cpu))
+ goto out;
+
+ unexpected_mask = ~(PACA_IRQ_DEC | PACA_IRQ_HMI | PACA_IRQ_HARD_DIS);
+ if (local_paca->irq_happened & unexpected_mask) {
+ if (local_paca->irq_happened & PACA_IRQ_EE)
+ pnv_flush_interrupts();
+ DBG("CPU%d Unexpected exit while offline irq_happened=%lx!\n",
+ cpu, local_paca->irq_happened);
+ }
+ local_paca->irq_happened = PACA_IRQ_HARD_DIS;
+
+ /*
* We don't want to take decrementer interrupts while we are
* offline, so clear LPCR:PECE1. We keep PECE2 (and
* LPCR_PECE_HVEE on P9) enabled so as to let IPIs in.
@@ -197,10 +223,11 @@ static void pnv_smp_cpu_kill_self(void)
* for coming online, which are handled via
* generic_check_cpu_restart() calls.
*/
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
srr1 = pnv_cpu_offline(cpu);
+ WARN_ON_ONCE(!irqs_disabled());
WARN_ON(lazy_irq_pending());
/*
@@ -216,13 +243,7 @@ static void pnv_smp_cpu_kill_self(void)
*/
if (((srr1 & wmask) == SRR1_WAKEEE) ||
((srr1 & wmask) == SRR1_WAKEHVI)) {
- if (cpu_has_feature(CPU_FTR_ARCH_300)) {
- if (xive_enabled())
- xive_flush_interrupt();
- else
- icp_opal_flush_interrupt();
- } else
- icp_native_flush_interrupt();
+ pnv_flush_interrupts();
} else if ((srr1 & wmask) == SRR1_WAKEHDBELL) {
unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER);
asm volatile(PPC_MSGCLR(%0) : : "r" (msg));
@@ -270,7 +291,7 @@ static void pnv_smp_cpu_kill_self(void)
*/
lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1;
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
-
+out:
DBG("CPU%d coming online...\n", cpu);
}
diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c
index cdbfc5c..f5387ad 100644
--- a/arch/powerpc/platforms/ps3/os-area.c
+++ b/arch/powerpc/platforms/ps3/os-area.c
@@ -664,7 +664,7 @@ static int update_flash_db(void)
db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
- if (count < sizeof(struct os_area_db)) {
+ if (count < 0 || count < sizeof(struct os_area_db)) {
pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
count);
error = count < 0 ? count : -EIO;
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index 25427a4..502ebcc 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -425,6 +425,10 @@ static struct bus_type cmm_subsys = {
.dev_name = "cmm",
};
+static void cmm_release_device(struct device *dev)
+{
+}
+
/**
* cmm_sysfs_register - Register with sysfs
*
@@ -440,6 +444,7 @@ static int cmm_sysfs_register(struct device *dev)
dev->id = 0;
dev->bus = &cmm_subsys;
+ dev->release = cmm_release_device;
if ((rc = device_register(dev)))
goto subsys_unregister;
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index e3010b1..c5ffcad 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -63,6 +63,10 @@ static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
prop->name = kstrdup(name, GFP_KERNEL);
+ if (!prop->name) {
+ dlpar_free_cc_property(prop);
+ return NULL;
+ }
prop->length = be32_to_cpu(ccwa->prop_length);
value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index 18014cd..ef65951 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -149,7 +149,7 @@ static int dtl_start(struct dtl *dtl)
/* Register our dtl buffer with the hypervisor. The HV expects the
* buffer size to be passed in the second word of the buffer */
- ((u32 *)dtl->buf)[1] = DISPATCH_LOG_BYTES;
+ ((u32 *)dtl->buf)[1] = cpu_to_be32(DISPATCH_LOG_BYTES);
hwcpu = get_hard_smp_processor_id(dtl->cpu);
addr = __pa(dtl->buf);
@@ -184,7 +184,7 @@ static void dtl_stop(struct dtl *dtl)
static u64 dtl_current_index(struct dtl *dtl)
{
- return lppaca_of(dtl->cpu).dtl_idx;
+ return be64_to_cpu(lppaca_of(dtl->cpu).dtl_idx);
}
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
index f99cd31..7f86bc3 100644
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
@@ -163,7 +163,7 @@ static u32 find_aa_index(struct device_node *dr_node,
return aa_index;
}
-static u32 lookup_lmb_associativity_index(struct drmem_lmb *lmb)
+static int update_lmb_associativity_index(struct drmem_lmb *lmb)
{
struct device_node *parent, *lmb_node, *dr_node;
struct property *ala_prop;
@@ -202,44 +202,16 @@ static u32 lookup_lmb_associativity_index(struct drmem_lmb *lmb)
aa_index = find_aa_index(dr_node, ala_prop, lmb_assoc);
+ of_node_put(dr_node);
dlpar_free_cc_nodes(lmb_node);
- return aa_index;
-}
-static int dlpar_add_device_tree_lmb(struct drmem_lmb *lmb)
-{
- int rc, aa_index;
-
- lmb->flags |= DRCONF_MEM_ASSIGNED;
-
- aa_index = lookup_lmb_associativity_index(lmb);
if (aa_index < 0) {
- pr_err("Couldn't find associativity index for drc index %x\n",
- lmb->drc_index);
- return aa_index;
+ pr_err("Could not find LMB associativity\n");
+ return -1;
}
lmb->aa_index = aa_index;
-
- rtas_hp_event = true;
- rc = drmem_update_dt();
- rtas_hp_event = false;
-
- return rc;
-}
-
-static int dlpar_remove_device_tree_lmb(struct drmem_lmb *lmb)
-{
- int rc;
-
- lmb->flags &= ~DRCONF_MEM_ASSIGNED;
- lmb->aa_index = 0xffffffff;
-
- rtas_hp_event = true;
- rc = drmem_update_dt();
- rtas_hp_event = false;
-
- return rc;
+ return 0;
}
static struct memory_block *lmb_to_memblock(struct drmem_lmb *lmb)
@@ -431,7 +403,9 @@ static int dlpar_remove_lmb(struct drmem_lmb *lmb)
/* Update memory regions for memory remove */
memblock_remove(lmb->base_addr, block_sz);
- dlpar_remove_device_tree_lmb(lmb);
+ invalidate_lmb_associativity_index(lmb);
+ lmb->flags &= ~DRCONF_MEM_ASSIGNED;
+
return 0;
}
@@ -691,10 +665,8 @@ static int dlpar_add_lmb(struct drmem_lmb *lmb)
if (lmb->flags & DRCONF_MEM_ASSIGNED)
return -EINVAL;
- rc = dlpar_add_device_tree_lmb(lmb);
+ rc = update_lmb_associativity_index(lmb);
if (rc) {
- pr_err("Couldn't update device tree for drc index %x\n",
- lmb->drc_index);
dlpar_release_drc(lmb->drc_index);
return rc;
}
@@ -705,16 +677,16 @@ static int dlpar_add_lmb(struct drmem_lmb *lmb)
nid = memory_add_physaddr_to_nid(lmb->base_addr);
/* Add the memory */
- rc = add_memory(nid, lmb->base_addr, block_sz);
+ rc = __add_memory(nid, lmb->base_addr, block_sz);
if (rc) {
- dlpar_remove_device_tree_lmb(lmb);
+ invalidate_lmb_associativity_index(lmb);
return rc;
}
rc = dlpar_online_lmb(lmb);
if (rc) {
remove_memory(nid, lmb->base_addr, block_sz);
- dlpar_remove_device_tree_lmb(lmb);
+ invalidate_lmb_associativity_index(lmb);
} else {
lmb->flags |= DRCONF_MEM_ASSIGNED;
}
@@ -961,6 +933,12 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
break;
}
+ if (!rc) {
+ rtas_hp_event = true;
+ rc = drmem_update_dt();
+ rtas_hp_event = false;
+ }
+
unlock_device_hotplug();
return rc;
}
diff --git a/arch/powerpc/platforms/pseries/hvconsole.c b/arch/powerpc/platforms/pseries/hvconsole.c
index 74da18d..73ec15c 100644
--- a/arch/powerpc/platforms/pseries/hvconsole.c
+++ b/arch/powerpc/platforms/pseries/hvconsole.c
@@ -62,7 +62,7 @@ EXPORT_SYMBOL(hvc_get_chars);
* @vtermno: The vtermno or unit_address of the adapter from which the data
* originated.
* @buf: The character buffer that contains the character data to send to
- * firmware.
+ * firmware. Must be at least 16 bytes, even if count is less than 16.
* @count: Send this number of characters.
*/
int hvc_put_chars(uint32_t vtermno, const char *buf, int count)
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index ea602f7..49e3a88 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -48,6 +48,7 @@
#include <asm/kexec.h>
#include <asm/fadump.h>
#include <asm/asm-prototypes.h>
+#include <asm/debugfs.h>
#include "pseries.h"
@@ -1032,3 +1033,56 @@ static int __init reserve_vrma_context_id(void)
return 0;
}
machine_device_initcall(pseries, reserve_vrma_context_id);
+
+#ifdef CONFIG_DEBUG_FS
+/* debugfs file interface for vpa data */
+static ssize_t vpa_file_read(struct file *filp, char __user *buf, size_t len,
+ loff_t *pos)
+{
+ int cpu = (long)filp->private_data;
+ struct lppaca *lppaca = &lppaca_of(cpu);
+
+ return simple_read_from_buffer(buf, len, pos, lppaca,
+ sizeof(struct lppaca));
+}
+
+static const struct file_operations vpa_fops = {
+ .open = simple_open,
+ .read = vpa_file_read,
+ .llseek = default_llseek,
+};
+
+static int __init vpa_debugfs_init(void)
+{
+ char name[16];
+ long i;
+ static struct dentry *vpa_dir;
+
+ if (!firmware_has_feature(FW_FEATURE_SPLPAR))
+ return 0;
+
+ vpa_dir = debugfs_create_dir("vpa", powerpc_debugfs_root);
+ if (!vpa_dir) {
+ pr_warn("%s: can't create vpa root dir\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* set up the per-cpu vpa file*/
+ for_each_possible_cpu(i) {
+ struct dentry *d;
+
+ sprintf(name, "cpu-%ld", i);
+
+ d = debugfs_create_file(name, 0400, vpa_dir, (void *)i,
+ &vpa_fops);
+ if (!d) {
+ pr_warn("%s: can't create per-cpu vpa file\n",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+machine_arch_initcall(pseries, vpa_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 67f4915..c2d318d 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -75,6 +75,9 @@
#include "pseries.h"
#include "../../../../drivers/pci/pci.h"
+DEFINE_STATIC_KEY_FALSE(shared_processor);
+EXPORT_SYMBOL_GPL(shared_processor);
+
int CMO_PrPSP = -1;
int CMO_SecPSP = -1;
unsigned long CMO_PageSize = (ASM_CONST(1) << IOMMU_PAGE_SHIFT_4K);
@@ -761,6 +764,10 @@ static void __init pSeries_setup_arch(void)
if (firmware_has_feature(FW_FEATURE_LPAR)) {
vpa_init(boot_cpuid);
+
+ if (lppaca_shared_proc(get_lppaca()))
+ static_branch_enable(&shared_processor);
+
ppc_md.power_save = pseries_lpar_idle;
ppc_md.enable_pmcs = pseries_lpar_enable_pmcs;
#ifdef CONFIG_PCI_IOV
diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c
index 37bfbc5..340de58 100644
--- a/arch/powerpc/sysdev/xics/icp-native.c
+++ b/arch/powerpc/sysdev/xics/icp-native.c
@@ -145,7 +145,7 @@ static unsigned int icp_native_get_irq(void)
static void icp_native_cause_ipi(int cpu)
{
- kvmppc_set_host_ipi(cpu, 1);
+ kvmppc_set_host_ipi(cpu);
icp_native_set_qirr(cpu, IPI_PRIORITY);
}
@@ -184,7 +184,7 @@ void icp_native_flush_interrupt(void)
if (vec == XICS_IPI) {
/* Clear pending IPI */
int cpu = smp_processor_id();
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
icp_native_set_qirr(cpu, 0xff);
} else {
pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",
@@ -205,7 +205,7 @@ static irqreturn_t icp_native_ipi_action(int irq, void *dev_id)
{
int cpu = smp_processor_id();
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
icp_native_set_qirr(cpu, 0xff);
return smp_ipi_demux();
diff --git a/arch/powerpc/sysdev/xics/icp-opal.c b/arch/powerpc/sysdev/xics/icp-opal.c
index c71d2ea..e3e52cf 100644
--- a/arch/powerpc/sysdev/xics/icp-opal.c
+++ b/arch/powerpc/sysdev/xics/icp-opal.c
@@ -130,7 +130,7 @@ static void icp_opal_cause_ipi(int cpu)
{
int hw_cpu = get_hard_smp_processor_id(cpu);
- kvmppc_set_host_ipi(cpu, 1);
+ kvmppc_set_host_ipi(cpu);
opal_int_set_mfrr(hw_cpu, IPI_PRIORITY);
}
@@ -138,7 +138,7 @@ static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id)
{
int cpu = smp_processor_id();
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff);
return smp_ipi_demux();
@@ -161,7 +161,7 @@ void icp_opal_flush_interrupt(void)
if (vec == XICS_IPI) {
/* Clear pending IPI */
int cpu = smp_processor_id();
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff);
} else {
pr_err("XICS: hw interrupt 0x%x to offline cpu, "
diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c
index 0b24b10..3c939b9 100644
--- a/arch/powerpc/sysdev/xive/common.c
+++ b/arch/powerpc/sysdev/xive/common.c
@@ -968,6 +968,15 @@ static int xive_irq_alloc_data(unsigned int virq, irq_hw_number_t hw)
xd->target = XIVE_INVALID_TARGET;
irq_set_handler_data(virq, xd);
+ /*
+ * Turn OFF by default the interrupt being mapped. A side
+ * effect of this check is the mapping the ESB page of the
+ * interrupt in the Linux address space. This prevents page
+ * fault issues in the crash handler which masks all
+ * interrupts.
+ */
+ xive_esb_read(xd, XIVE_ESB_SET_PQ_01);
+
return 0;
}
@@ -1009,12 +1018,13 @@ static void xive_ipi_eoi(struct irq_data *d)
{
struct xive_cpu *xc = __this_cpu_read(xive_cpu);
- DBG_VERBOSE("IPI eoi: irq=%d [0x%lx] (HW IRQ 0x%x) pending=%02x\n",
- d->irq, irqd_to_hwirq(d), xc->hw_ipi, xc->pending_prio);
-
/* Handle possible race with unplug and drop stale IPIs */
if (!xc)
return;
+
+ DBG_VERBOSE("IPI eoi: irq=%d [0x%lx] (HW IRQ 0x%x) pending=%02x\n",
+ d->irq, irqd_to_hwirq(d), xc->hw_ipi, xc->pending_prio);
+
xive_do_source_eoi(xc->hw_ipi, &xc->ipi_data);
xive_do_queue_eoi(xc);
}
diff --git a/arch/powerpc/sysdev/xive/spapr.c b/arch/powerpc/sysdev/xive/spapr.c
index 575db3b..e3ebf64 100644
--- a/arch/powerpc/sysdev/xive/spapr.c
+++ b/arch/powerpc/sysdev/xive/spapr.c
@@ -359,20 +359,28 @@ static int xive_spapr_populate_irq_data(u32 hw_irq, struct xive_irq_data *data)
data->esb_shift = esb_shift;
data->trig_page = trig_page;
+ data->hw_irq = hw_irq;
+
/*
* No chip-id for the sPAPR backend. This has an impact how we
* pick a target. See xive_pick_irq_target().
*/
data->src_chip = XIVE_INVALID_CHIP_ID;
+ /*
+ * When the H_INT_ESB flag is set, the H_INT_ESB hcall should
+ * be used for interrupt management. Skip the remapping of the
+ * ESB pages which are not available.
+ */
+ if (data->flags & XIVE_IRQ_FLAG_H_INT_ESB)
+ return 0;
+
data->eoi_mmio = ioremap(data->eoi_page, 1u << data->esb_shift);
if (!data->eoi_mmio) {
pr_err("Failed to map EOI page for irq 0x%x\n", hw_irq);
return -ENOMEM;
}
- data->hw_irq = hw_irq;
-
/* Full function page supports trigger */
if (flags & XIVE_SRC_TRIGGER) {
data->trig_mmio = data->eoi_mmio;
diff --git a/arch/powerpc/tools/relocs_check.sh b/arch/powerpc/tools/relocs_check.sh
index ec2d5c83..d6c16e7 100755
--- a/arch/powerpc/tools/relocs_check.sh
+++ b/arch/powerpc/tools/relocs_check.sh
@@ -23,7 +23,7 @@
vmlinux="$2"
bad_relocs=$(
-"$objdump" -R "$vmlinux" |
+$objdump -R "$vmlinux" |
# Only look at relocation lines.
grep -E '\<R_' |
# These relocations are okay
diff --git a/arch/powerpc/tools/unrel_branch_check.sh b/arch/powerpc/tools/unrel_branch_check.sh
index 1e972df..7711475 100755
--- a/arch/powerpc/tools/unrel_branch_check.sh
+++ b/arch/powerpc/tools/unrel_branch_check.sh
@@ -18,14 +18,14 @@
#__end_interrupts should be located within the first 64K
end_intr=0x$(
-"$objdump" -R "$vmlinux" -d --start-address=0xc000000000000000 \
+$objdump -R "$vmlinux" -d --start-address=0xc000000000000000 \
--stop-address=0xc000000000010000 |
grep '\<__end_interrupts>:' |
awk '{print $1}'
)
BRANCHES=$(
-"$objdump" -R "$vmlinux" -D --start-address=0xc000000000000000 \
+$objdump -R "$vmlinux" -D --start-address=0xc000000000000000 \
--stop-address=${end_intr} |
grep -e "^c[0-9a-f]*:[[:space:]]*\([0-9a-f][0-9a-f][[:space:]]\)\{4\}[[:space:]]*b" |
grep -v '\<__start_initialization_multiplatform>' |
diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile
index 9d7d8e6..365e711 100644
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for xmon
-# Disable clang warning for using setjmp without setjmp.h header
-subdir-ccflags-y := $(call cc-disable-warning, builtin-requires-header)
+# Avoid clang warnings around longjmp/setjmp declarations
+subdir-ccflags-y := -ffreestanding
subdir-ccflags-$(CONFIG_PPC_WERROR) += -Werror
@@ -13,6 +13,12 @@
ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst $(CC_FLAGS_FTRACE),,$(ORIG_CFLAGS))
+ifdef CONFIG_CC_IS_CLANG
+# clang stores addresses on the stack causing the frame size to blow
+# out. See https://github.com/ClangBuiltLinux/linux/issues/252
+KBUILD_CFLAGS += -Wframe-larger-than=4096
+endif
+
ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
obj-y += xmon.o nonstdio.o spr_access.o
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index bb5db7b..f0fa22e 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -3493,7 +3493,7 @@ void dump_segments(void)
printf("sr0-15 =");
for (i = 0; i < 16; ++i)
- printf(" %x", mfsrin(i));
+ printf(" %x", mfsrin(i << 28));
printf("\n");
}
#endif
diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
index a840b7d..6d39f64 100644
--- a/arch/riscv/kernel/ftrace.c
+++ b/arch/riscv/kernel/ftrace.c
@@ -142,7 +142,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
*/
old = *parent;
- if (function_graph_enter(old, self_addr, frame_pointer, parent))
+ if (!function_graph_enter(old, self_addr, frame_pointer, parent))
*parent = return_hooker;
}
diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c
index 70ef272..bd2f2db 100644
--- a/arch/riscv/mm/ioremap.c
+++ b/arch/riscv/mm/ioremap.c
@@ -42,7 +42,7 @@ static void __iomem *__ioremap_caller(phys_addr_t addr, size_t size,
/* Page-align mappings */
offset = addr & (~PAGE_MASK);
- addr &= PAGE_MASK;
+ addr -= offset;
size = PAGE_ALIGN(size + offset);
area = get_vm_area_caller(size, VM_IOREMAP, caller);
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
index 9e6668e..f6a9b0c2 100644
--- a/arch/s390/boot/Makefile
+++ b/arch/s390/boot/Makefile
@@ -6,6 +6,7 @@
KCOV_INSTRUMENT := n
GCOV_PROFILE := n
UBSAN_SANITIZE := n
+KASAN_SANITIZE := n
KBUILD_AFLAGS := $(KBUILD_AFLAGS_DECOMPRESSOR)
KBUILD_CFLAGS := $(KBUILD_CFLAGS_DECOMPRESSOR)
diff --git a/arch/s390/boot/compressed/Makefile b/arch/s390/boot/compressed/Makefile
index b375c6c..9b3d821 100644
--- a/arch/s390/boot/compressed/Makefile
+++ b/arch/s390/boot/compressed/Makefile
@@ -8,6 +8,7 @@
KCOV_INSTRUMENT := n
GCOV_PROFILE := n
UBSAN_SANITIZE := n
+KASAN_SANITIZE := n
obj-y := $(if $(CONFIG_KERNEL_UNCOMPRESSED),,head.o misc.o) piggy.o
targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2
diff --git a/arch/s390/include/asm/mmu.h b/arch/s390/include/asm/mmu.h
index a8418e1..bcfb637 100644
--- a/arch/s390/include/asm/mmu.h
+++ b/arch/s390/include/asm/mmu.h
@@ -32,6 +32,8 @@ typedef struct {
unsigned int uses_cmm:1;
/* The gmaps associated with this context are allowed to use huge pages. */
unsigned int allow_gmap_hpage_1m:1;
+ /* The mmu context is for compat task */
+ unsigned int compat_mm:1;
} mm_context_t;
#define INIT_MM_CONTEXT(name) \
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index 09b61d0..8d04e6f 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -25,6 +25,7 @@ static inline int init_new_context(struct task_struct *tsk,
atomic_set(&mm->context.flush_count, 0);
mm->context.gmap_asce = 0;
mm->context.flush_mm = 0;
+ mm->context.compat_mm = test_thread_flag(TIF_31BIT);
#ifdef CONFIG_PGSTE
mm->context.alloc_pgste = page_table_allocate_pgste ||
test_thread_flag(TIF_PGSTE) ||
diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h
index 5ee7337..67838df 100644
--- a/arch/s390/include/asm/pgalloc.h
+++ b/arch/s390/include/asm/pgalloc.h
@@ -56,7 +56,12 @@ static inline p4d_t *p4d_alloc_one(struct mm_struct *mm, unsigned long address)
crst_table_init(table, _REGION2_ENTRY_EMPTY);
return (p4d_t *) table;
}
-#define p4d_free(mm, p4d) crst_table_free(mm, (unsigned long *) p4d)
+
+static inline void p4d_free(struct mm_struct *mm, p4d_t *p4d)
+{
+ if (!mm_p4d_folded(mm))
+ crst_table_free(mm, (unsigned long *) p4d);
+}
static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
{
@@ -65,7 +70,12 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
crst_table_init(table, _REGION3_ENTRY_EMPTY);
return (pud_t *) table;
}
-#define pud_free(mm, pud) crst_table_free(mm, (unsigned long *) pud)
+
+static inline void pud_free(struct mm_struct *mm, pud_t *pud)
+{
+ if (!mm_pud_folded(mm))
+ crst_table_free(mm, (unsigned long *) pud);
+}
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
{
@@ -83,6 +93,8 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
{
+ if (mm_pmd_folded(mm))
+ return;
pgtable_pmd_page_dtor(virt_to_page(pmd));
crst_table_free(mm, (unsigned long *) pmd);
}
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index de05466..0a326da 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -1150,8 +1150,6 @@ void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr);
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t entry)
{
- if (!MACHINE_HAS_NX)
- pte_val(entry) &= ~_PAGE_NOEXEC;
if (pte_present(entry))
pte_val(entry) &= ~_PAGE_UNUSED;
if (mm_has_pgste(mm))
@@ -1168,6 +1166,8 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
{
pte_t __pte;
pte_val(__pte) = physpage + pgprot_val(pgprot);
+ if (!MACHINE_HAS_NX)
+ pte_val(__pte) &= ~_PAGE_NOEXEC;
return pte_mkyoung(__pte);
}
diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h
index 64539c2..2dc9eb4 100644
--- a/arch/s390/include/asm/timex.h
+++ b/arch/s390/include/asm/timex.h
@@ -10,8 +10,9 @@
#ifndef _ASM_S390_TIMEX_H
#define _ASM_S390_TIMEX_H
-#include <asm/lowcore.h>
+#include <linux/preempt.h>
#include <linux/time64.h>
+#include <asm/lowcore.h>
/* The value of the TOD clock for 1.1.1970. */
#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
@@ -186,15 +187,18 @@ extern unsigned char tod_clock_base[16] __aligned(8);
/**
* get_clock_monotonic - returns current time in clock rate units
*
- * The caller must ensure that preemption is disabled.
* The clock and tod_clock_base get changed via stop_machine.
- * Therefore preemption must be disabled when calling this
- * function, otherwise the returned value is not guaranteed to
- * be monotonic.
+ * Therefore preemption must be disabled, otherwise the returned
+ * value is not guaranteed to be monotonic.
*/
static inline unsigned long long get_tod_clock_monotonic(void)
{
- return get_tod_clock() - *(unsigned long long *) &tod_clock_base[1];
+ unsigned long long tod;
+
+ preempt_disable_notrace();
+ tod = get_tod_clock() - *(unsigned long long *) &tod_clock_base[1];
+ preempt_enable_notrace();
+ return tod;
}
/**
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index 5332f62..40194f8 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -84,7 +84,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n);
__rc; \
})
-static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size)
+static __always_inline int __put_user_fn(void *x, void __user *ptr, unsigned long size)
{
unsigned long spec = 0x010000UL;
int rc;
@@ -114,7 +114,7 @@ static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size)
return rc;
}
-static inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size)
+static __always_inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size)
{
unsigned long spec = 0x01UL;
int rc;
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index b205c0f..762fc453 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -23,6 +23,8 @@
UBSAN_SANITIZE_early.o := n
UBSAN_SANITIZE_early_nobss.o := n
+KASAN_SANITIZE_early_nobss.o := n
+
#
# Passing null pointers is ok for smp code, since we access the lowcore here.
#
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index b2c68fb..41925f2 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -462,10 +462,11 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr)
ptr += sprintf(ptr, "%%c%i", value);
else if (operand->flags & OPERAND_VR)
ptr += sprintf(ptr, "%%v%i", value);
- else if (operand->flags & OPERAND_PCREL)
- ptr += sprintf(ptr, "%lx", (signed int) value
- + addr);
- else if (operand->flags & OPERAND_SIGNED)
+ else if (operand->flags & OPERAND_PCREL) {
+ void *pcrel = (void *)((int)value + addr);
+
+ ptr += sprintf(ptr, "%px", pcrel);
+ } else if (operand->flags & OPERAND_SIGNED)
ptr += sprintf(ptr, "%i", value);
else
ptr += sprintf(ptr, "%u", value);
@@ -537,7 +538,7 @@ void show_code(struct pt_regs *regs)
else
*ptr++ = ' ';
addr = regs->psw.addr + start - 32;
- ptr += sprintf(ptr, "%016lx: ", addr);
+ ptr += sprintf(ptr, "%px: ", (void *)addr);
if (start + opsize >= end)
break;
for (i = 0; i < opsize; i++)
@@ -565,7 +566,7 @@ void print_fn_code(unsigned char *code, unsigned long len)
opsize = insn_length(*code);
if (opsize > len)
break;
- ptr += sprintf(ptr, "%p: ", code);
+ ptr += sprintf(ptr, "%px: ", code);
for (i = 0; i < opsize; i++)
ptr += sprintf(ptr, "%02x", code[i]);
*ptr++ = '\t';
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c
index b9d8fe4..8f84568 100644
--- a/arch/s390/kernel/idle.c
+++ b/arch/s390/kernel/idle.c
@@ -69,18 +69,26 @@ DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
static ssize_t show_idle_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ unsigned long long now, idle_time, idle_enter, idle_exit, in_idle;
struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
- unsigned long long now, idle_time, idle_enter, idle_exit;
unsigned int seq;
do {
- now = get_tod_clock();
seq = read_seqcount_begin(&idle->seqcount);
idle_time = READ_ONCE(idle->idle_time);
idle_enter = READ_ONCE(idle->clock_idle_enter);
idle_exit = READ_ONCE(idle->clock_idle_exit);
} while (read_seqcount_retry(&idle->seqcount, seq));
- idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0;
+ in_idle = 0;
+ now = get_tod_clock();
+ if (idle_enter) {
+ if (idle_exit) {
+ in_idle = idle_exit - idle_enter;
+ } else if (now > idle_enter) {
+ in_idle = now - idle_enter;
+ }
+ }
+ idle_time += in_idle;
return sprintf(buf, "%llu\n", idle_time >> 12);
}
DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
@@ -88,17 +96,24 @@ DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
u64 arch_cpu_idle_time(int cpu)
{
struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
- unsigned long long now, idle_enter, idle_exit;
+ unsigned long long now, idle_enter, idle_exit, in_idle;
unsigned int seq;
do {
- now = get_tod_clock();
seq = read_seqcount_begin(&idle->seqcount);
idle_enter = READ_ONCE(idle->clock_idle_enter);
idle_exit = READ_ONCE(idle->clock_idle_exit);
} while (read_seqcount_retry(&idle->seqcount, seq));
-
- return cputime_to_nsecs(idle_enter ? ((idle_exit ?: now) - idle_enter) : 0);
+ in_idle = 0;
+ now = get_tod_clock();
+ if (idle_enter) {
+ if (idle_exit) {
+ in_idle = idle_exit - idle_enter;
+ } else if (now > idle_enter) {
+ in_idle = now - idle_enter;
+ }
+ }
+ return cputime_to_nsecs(in_idle);
}
void arch_cpu_idle_enter(void)
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index 4440483..5bfb1ce 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -193,7 +193,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
unsigned long num_sdb, gfp_t gfp_flags)
{
int i, rc;
- unsigned long *new, *tail;
+ unsigned long *new, *tail, *tail_prev = NULL;
if (!sfb->sdbt || !sfb->tail)
return -EINVAL;
@@ -232,6 +232,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
sfb->num_sdbt++;
/* Link current page to tail of chain */
*tail = (unsigned long)(void *) new + 1;
+ tail_prev = tail;
tail = new;
}
@@ -241,10 +242,22 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
* issue, a new realloc call (if required) might succeed.
*/
rc = alloc_sample_data_block(tail, gfp_flags);
- if (rc)
+ if (rc) {
+ /* Undo last SDBT. An SDBT with no SDB at its first
+ * entry but with an SDBT entry instead can not be
+ * handled by the interrupt handler code.
+ * Avoid this situation.
+ */
+ if (tail_prev) {
+ sfb->num_sdbt--;
+ free_page((unsigned long) new);
+ tail = tail_prev;
+ }
break;
+ }
sfb->num_sdb++;
tail++;
+ tail_prev = new = NULL; /* Allocated at least one SBD */
}
/* Link sampling buffer to its origin */
@@ -1248,18 +1261,28 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
*/
if (flush_all && done)
break;
-
- /* If an event overflow happened, discard samples by
- * processing any remaining sample-data-blocks.
- */
- if (event_overflow)
- flush_all = 1;
}
/* Account sample overflows in the event hardware structure */
if (sampl_overflow)
OVERFLOW_REG(hwc) = DIV_ROUND_UP(OVERFLOW_REG(hwc) +
sampl_overflow, 1 + num_sdb);
+
+ /* Perf_event_overflow() and perf_event_account_interrupt() limit
+ * the interrupt rate to an upper limit. Roughly 1000 samples per
+ * task tick.
+ * Hitting this limit results in a large number
+ * of throttled REF_REPORT_THROTTLE entries and the samples
+ * are dropped.
+ * Slightly increase the interval to avoid hitting this limit.
+ */
+ if (event_overflow) {
+ SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10);
+ debug_sprintf_event(sfdbg, 1, "%s: rate adjustment %ld\n",
+ __func__,
+ DIV_ROUND_UP(SAMPL_RATE(hwc), 10));
+ }
+
if (sampl_overflow || event_overflow)
debug_sprintf_event(sfdbg, 4, "hw_perf_event_update: "
"overflow stats: sample=%llu event=%llu\n",
@@ -2045,14 +2068,17 @@ static int __init init_cpum_sampling_pmu(void)
}
sfdbg = debug_register(KMSG_COMPONENT, 2, 1, 80);
- if (!sfdbg)
+ if (!sfdbg) {
pr_err("Registering for s390dbf failed\n");
+ return -ENOMEM;
+ }
debug_register_view(sfdbg, &debug_sprintf_view);
err = register_external_irq(EXT_IRQ_MEASURE_ALERT,
cpumf_measurement_alert);
if (err) {
pr_cpumsf_err(RS_INIT_FAILURE_ALRT);
+ debug_unregister(sfdbg);
goto out;
}
@@ -2061,6 +2087,7 @@ static int __init init_cpum_sampling_pmu(void)
pr_cpumsf_err(RS_INIT_FAILURE_PERF);
unregister_external_irq(EXT_IRQ_MEASURE_ALERT,
cpumf_measurement_alert);
+ debug_unregister(sfdbg);
goto out;
}
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index da02f40..ecd2471 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -261,9 +261,12 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
lc->spinlock_index = 0;
lc->percpu_offset = __per_cpu_offset[cpu];
lc->kernel_asce = S390_lowcore.kernel_asce;
+ lc->user_asce = S390_lowcore.kernel_asce;
lc->machine_flags = S390_lowcore.machine_flags;
lc->user_timer = lc->system_timer = lc->steal_timer = 0;
__ctl_store(lc->cregs_save_area, 0, 15);
+ lc->cregs_save_area[1] = lc->kernel_asce;
+ lc->cregs_save_area[7] = lc->vdso_asce;
save_access_regs((unsigned int *) lc->access_regs_save_area);
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
sizeof(lc->stfle_fac_list));
@@ -712,39 +715,67 @@ static void __ref smp_get_core_info(struct sclp_core_info *info, int early)
static int smp_add_present_cpu(int cpu);
-static int __smp_rescan_cpus(struct sclp_core_info *info, int sysfs_add)
+static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail,
+ bool configured, bool early)
{
struct pcpu *pcpu;
- cpumask_t avail;
- int cpu, nr, i, j;
+ int cpu, nr, i;
u16 address;
nr = 0;
- cpumask_xor(&avail, cpu_possible_mask, cpu_present_mask);
- cpu = cpumask_first(&avail);
- for (i = 0; (i < info->combined) && (cpu < nr_cpu_ids); i++) {
- if (sclp.has_core_type && info->core[i].type != boot_core_type)
+ if (sclp.has_core_type && core->type != boot_core_type)
+ return nr;
+ cpu = cpumask_first(avail);
+ address = core->core_id << smp_cpu_mt_shift;
+ for (i = 0; (i <= smp_cpu_mtid) && (cpu < nr_cpu_ids); i++) {
+ if (pcpu_find_address(cpu_present_mask, address + i))
continue;
- address = info->core[i].core_id << smp_cpu_mt_shift;
- for (j = 0; j <= smp_cpu_mtid; j++) {
- if (pcpu_find_address(cpu_present_mask, address + j))
- continue;
- pcpu = pcpu_devices + cpu;
- pcpu->address = address + j;
- pcpu->state =
- (cpu >= info->configured*(smp_cpu_mtid + 1)) ?
- CPU_STATE_STANDBY : CPU_STATE_CONFIGURED;
- smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
- set_cpu_present(cpu, true);
- if (sysfs_add && smp_add_present_cpu(cpu) != 0)
- set_cpu_present(cpu, false);
- else
- nr++;
- cpu = cpumask_next(cpu, &avail);
- if (cpu >= nr_cpu_ids)
+ pcpu = pcpu_devices + cpu;
+ pcpu->address = address + i;
+ if (configured)
+ pcpu->state = CPU_STATE_CONFIGURED;
+ else
+ pcpu->state = CPU_STATE_STANDBY;
+ smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ set_cpu_present(cpu, true);
+ if (!early && smp_add_present_cpu(cpu) != 0)
+ set_cpu_present(cpu, false);
+ else
+ nr++;
+ cpumask_clear_cpu(cpu, avail);
+ cpu = cpumask_next(cpu, avail);
+ }
+ return nr;
+}
+
+static int __smp_rescan_cpus(struct sclp_core_info *info, bool early)
+{
+ struct sclp_core_entry *core;
+ cpumask_t avail;
+ bool configured;
+ u16 core_id;
+ int nr, i;
+
+ nr = 0;
+ cpumask_xor(&avail, cpu_possible_mask, cpu_present_mask);
+ /*
+ * Add IPL core first (which got logical CPU number 0) to make sure
+ * that all SMT threads get subsequent logical CPU numbers.
+ */
+ if (early) {
+ core_id = pcpu_devices[0].address >> smp_cpu_mt_shift;
+ for (i = 0; i < info->configured; i++) {
+ core = &info->core[i];
+ if (core->core_id == core_id) {
+ nr += smp_add_core(core, &avail, true, early);
break;
+ }
}
}
+ for (i = 0; i < info->combined; i++) {
+ configured = i < info->configured;
+ nr += smp_add_core(&info->core[i], &avail, configured, early);
+ }
return nr;
}
@@ -790,7 +821,7 @@ void __init smp_detect_cpus(void)
/* Add CPUs present at boot */
get_online_cpus();
- __smp_rescan_cpus(info, 0);
+ __smp_rescan_cpus(info, true);
put_online_cpus();
memblock_free_early((unsigned long)info, sizeof(*info));
}
@@ -810,6 +841,8 @@ static void smp_start_secondary(void *cpuvoid)
restore_access_regs(S390_lowcore.access_regs_save_area);
__ctl_load(S390_lowcore.cregs_save_area, 0, 15);
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
+ set_cpu_flag(CIF_ASCE_PRIMARY);
+ set_cpu_flag(CIF_ASCE_SECONDARY);
cpu_init();
preempt_disable();
init_cpu_timer();
@@ -1140,7 +1173,7 @@ int __ref smp_rescan_cpus(void)
smp_get_core_info(info, 0);
get_online_cpus();
mutex_lock(&smp_cpu_state_mutex);
- nr = __smp_rescan_cpus(info, 1);
+ nr = __smp_rescan_cpus(info, false);
mutex_unlock(&smp_cpu_state_mutex);
put_online_cpus();
kfree(info);
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 3031cc6..7ab7d25 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -56,7 +56,7 @@ static vm_fault_t vdso_fault(const struct vm_special_mapping *sm,
vdso_pagelist = vdso64_pagelist;
vdso_pages = vdso64_pages;
#ifdef CONFIG_COMPAT
- if (is_compat_task()) {
+ if (vma->vm_mm->context.compat_mm) {
vdso_pagelist = vdso32_pagelist;
vdso_pages = vdso32_pages;
}
@@ -77,7 +77,7 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
vdso_pages = vdso64_pages;
#ifdef CONFIG_COMPAT
- if (is_compat_task())
+ if (vma->vm_mm->context.compat_mm)
vdso_pages = vdso32_pages;
#endif
@@ -224,7 +224,8 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
vdso_pages = vdso64_pages;
#ifdef CONFIG_COMPAT
- if (is_compat_task())
+ mm->context.compat_mm = is_compat_task();
+ if (mm->context.compat_mm)
vdso_pages = vdso32_pages;
#endif
/*
diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile
index 04dd3e2c..e76309f 100644
--- a/arch/s390/kernel/vdso32/Makefile
+++ b/arch/s390/kernel/vdso32/Makefile
@@ -28,9 +28,10 @@
extra-y += vdso32.lds
CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
-# Disable gcov profiling and ubsan for VDSO code
+# Disable gcov profiling, ubsan and kasan for VDSO code
GCOV_PROFILE := n
UBSAN_SANITIZE := n
+KASAN_SANITIZE := n
# Force dependency (incbin is bad)
$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
diff --git a/arch/s390/kernel/vdso32/clock_gettime.S b/arch/s390/kernel/vdso32/clock_gettime.S
index a9418bf..ada5c11 100644
--- a/arch/s390/kernel/vdso32/clock_gettime.S
+++ b/arch/s390/kernel/vdso32/clock_gettime.S
@@ -10,6 +10,7 @@
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/dwarf.h>
+#include <asm/ptrace.h>
.text
.align 4
@@ -18,8 +19,8 @@
__kernel_clock_gettime:
CFI_STARTPROC
ahi %r15,-16
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
basr %r5,0
0: al %r5,21f-0b(%r5) /* get &_vdso_data */
chi %r2,__CLOCK_REALTIME_COARSE
@@ -72,13 +73,13 @@
st %r1,4(%r3) /* store tp->tv_nsec */
lhi %r2,0
ahi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
/* CLOCK_MONOTONIC_COARSE */
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
9: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
tml %r4,0x0001 /* pending update ? loop */
jnz 9b
@@ -158,17 +159,17 @@
st %r1,4(%r3) /* store tp->tv_nsec */
lhi %r2,0
ahi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
/* Fallback to system call */
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
19: lhi %r1,__NR_clock_gettime
svc 0
ahi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
CFI_ENDPROC
diff --git a/arch/s390/kernel/vdso32/gettimeofday.S b/arch/s390/kernel/vdso32/gettimeofday.S
index 3c0db0f..b23063f 100644
--- a/arch/s390/kernel/vdso32/gettimeofday.S
+++ b/arch/s390/kernel/vdso32/gettimeofday.S
@@ -10,6 +10,7 @@
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/dwarf.h>
+#include <asm/ptrace.h>
.text
.align 4
@@ -19,7 +20,7 @@
CFI_STARTPROC
ahi %r15,-16
CFI_ADJUST_CFA_OFFSET 16
- CFI_VAL_OFFSET 15, -160
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
basr %r5,0
0: al %r5,13f-0b(%r5) /* get &_vdso_data */
1: ltr %r3,%r3 /* check if tz is NULL */
diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile
index ddebc26..f849ac6 100644
--- a/arch/s390/kernel/vdso64/Makefile
+++ b/arch/s390/kernel/vdso64/Makefile
@@ -28,9 +28,10 @@
extra-y += vdso64.lds
CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
-# Disable gcov profiling and ubsan for VDSO code
+# Disable gcov profiling, ubsan and kasan for VDSO code
GCOV_PROFILE := n
UBSAN_SANITIZE := n
+KASAN_SANITIZE := n
# Force dependency (incbin is bad)
$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S
index fac3ab5..9d2ee79 100644
--- a/arch/s390/kernel/vdso64/clock_gettime.S
+++ b/arch/s390/kernel/vdso64/clock_gettime.S
@@ -10,6 +10,7 @@
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/dwarf.h>
+#include <asm/ptrace.h>
.text
.align 4
@@ -18,8 +19,8 @@
__kernel_clock_gettime:
CFI_STARTPROC
aghi %r15,-16
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
larl %r5,_vdso_data
cghi %r2,__CLOCK_REALTIME_COARSE
je 4f
@@ -56,13 +57,13 @@
stg %r1,8(%r3) /* store tp->tv_nsec */
lghi %r2,0
aghi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
/* CLOCK_MONOTONIC_COARSE */
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
3: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
tmll %r4,0x0001 /* pending update ? loop */
jnz 3b
@@ -115,13 +116,13 @@
stg %r1,8(%r3) /* store tp->tv_nsec */
lghi %r2,0
aghi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
/* CPUCLOCK_VIRT for this thread */
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
9: lghi %r4,0
icm %r0,15,__VDSO_ECTG_OK(%r5)
jz 12f
@@ -142,17 +143,17 @@
stg %r4,8(%r3)
lghi %r2,0
aghi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
/* Fallback to system call */
- CFI_DEF_CFA_OFFSET 176
- CFI_VAL_OFFSET 15, -160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
12: lghi %r1,__NR_clock_gettime
svc 0
aghi %r15,16
- CFI_DEF_CFA_OFFSET 160
+ CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
CFI_RESTORE 15
br %r14
CFI_ENDPROC
diff --git a/arch/s390/kernel/vdso64/gettimeofday.S b/arch/s390/kernel/vdso64/gettimeofday.S
index 6e1f0b4..aebe10d 100644
--- a/arch/s390/kernel/vdso64/gettimeofday.S
+++ b/arch/s390/kernel/vdso64/gettimeofday.S
@@ -10,6 +10,7 @@
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/dwarf.h>
+#include <asm/ptrace.h>
.text
.align 4
@@ -19,7 +20,7 @@
CFI_STARTPROC
aghi %r15,-16
CFI_ADJUST_CFA_OFFSET 16
- CFI_VAL_OFFSET 15, -160
+ CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
larl %r5,_vdso_data
0: ltgr %r3,%r3 /* check if tz is NULL */
je 1f
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index fac1d4e..db3196a 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -416,19 +416,30 @@ static void kvm_s390_cpu_feat_init(void)
int kvm_arch_init(void *opaque)
{
+ int rc;
+
kvm_s390_dbf = debug_register("kvm-trace", 32, 1, 7 * sizeof(long));
if (!kvm_s390_dbf)
return -ENOMEM;
if (debug_register_view(kvm_s390_dbf, &debug_sprintf_view)) {
- debug_unregister(kvm_s390_dbf);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out_debug_unreg;
}
kvm_s390_cpu_feat_init();
/* Register floating interrupt controller interface. */
- return kvm_register_device_ops(&kvm_flic_ops, KVM_DEV_TYPE_FLIC);
+ rc = kvm_register_device_ops(&kvm_flic_ops, KVM_DEV_TYPE_FLIC);
+ if (rc) {
+ pr_err("Failed to register FLIC rc=%d\n", rc);
+ goto out_debug_unreg;
+ }
+ return 0;
+
+out_debug_unreg:
+ debug_unregister(kvm_s390_dbf);
+ return rc;
}
void kvm_arch_exit(void)
@@ -2110,13 +2121,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.sca = (struct bsca_block *) get_zeroed_page(alloc_flags);
if (!kvm->arch.sca)
goto out_err;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
sca_offset += 16;
if (sca_offset + sizeof(struct bsca_block) > PAGE_SIZE)
sca_offset = 0;
kvm->arch.sca = (struct bsca_block *)
((char *) kvm->arch.sca + sca_offset);
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
sprintf(debug_name, "kvm-%u", current->pid);
diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
index 57ab401..5418d10 100644
--- a/arch/s390/lib/Makefile
+++ b/arch/s390/lib/Makefile
@@ -9,5 +9,9 @@
lib-$(CONFIG_KPROBES) += probes.o
lib-$(CONFIG_UPROBES) += probes.o
+# Instrumenting memory accesses to __user data (in different address space)
+# produce false positives
+KASAN_SANITIZE_uaccess.o := n
+
chkbss := mem.o
include $(srctree)/arch/s390/scripts/Makefile.chkbss
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index 510a182..a51c892 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -298,16 +298,16 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
}
if (write) {
- len = *lenp;
- if (copy_from_user(buf, buffer,
- len > sizeof(buf) ? sizeof(buf) : len))
+ len = min(*lenp, sizeof(buf));
+ if (copy_from_user(buf, buffer, len))
return -EFAULT;
- buf[sizeof(buf) - 1] = '\0';
+ buf[len - 1] = '\0';
cmm_skip_blanks(buf, &p);
nr = simple_strtoul(p, &p, 0);
cmm_skip_blanks(p, &p);
seconds = simple_strtoul(p, &p, 0);
cmm_set_timeout(nr, seconds);
+ *ppos += *lenp;
} else {
len = sprintf(buf, "%ld %ld\n",
cmm_timeout_pages, cmm_timeout_seconds);
@@ -315,9 +315,9 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
len = *lenp;
if (copy_to_user(buffer, buf, len))
return -EFAULT;
+ *lenp = len;
+ *ppos += len;
}
- *lenp = len;
- *ppos += len;
return 0;
}
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 2809d11..9b5b866 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -39,7 +39,8 @@ static inline int gup_pte_range(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
page = pte_page(pte);
head = compound_head(page);
- if (!page_cache_get_speculative(head))
+ if (unlikely(WARN_ON_ONCE(page_ref_count(head) < 0)
+ || !page_cache_get_speculative(head)))
return 0;
if (unlikely(pte_val(pte) != pte_val(*ptep))) {
put_page(head);
@@ -77,7 +78,8 @@ static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
refs++;
} while (addr += PAGE_SIZE, addr != end);
- if (!page_cache_add_speculative(head, refs)) {
+ if (unlikely(WARN_ON_ONCE(page_ref_count(head) < 0)
+ || !page_cache_add_speculative(head, refs))) {
*nr -= refs;
return 0;
}
@@ -151,7 +153,8 @@ static int gup_huge_pud(pud_t *pudp, pud_t pud, unsigned long addr,
refs++;
} while (addr += PAGE_SIZE, addr != end);
- if (!page_cache_add_speculative(head, refs)) {
+ if (unlikely(WARN_ON_ONCE(page_ref_count(head) < 0)
+ || !page_cache_add_speculative(head, refs))) {
*nr -= refs;
return 0;
}
diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile
index ce6a3f7..fdccb76 100644
--- a/arch/s390/purgatory/Makefile
+++ b/arch/s390/purgatory/Makefile
@@ -13,8 +13,10 @@
$(obj)/mem.o: $(srctree)/arch/s390/lib/mem.S FORCE
$(call if_changed_rule,as_o_S)
-$(obj)/string.o: $(srctree)/arch/s390/lib/string.c FORCE
- $(call if_changed_rule,cc_o_c)
+KCOV_INSTRUMENT := n
+GCOV_PROFILE := n
+UBSAN_SANITIZE := n
+KASAN_SANITIZE := n
LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostdlib
LDFLAGS_purgatory.ro += -z nodefaultlib
diff --git a/arch/s390/purgatory/string.c b/arch/s390/purgatory/string.c
new file mode 100644
index 0000000..c98c22a
--- /dev/null
+++ b/arch/s390/purgatory/string.c
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+#define __HAVE_ARCH_MEMCMP /* arch function */
+#include "../lib/string.c"
diff --git a/arch/sh/include/cpu-sh4/cpu/sh7734.h b/arch/sh/include/cpu-sh4/cpu/sh7734.h
index 96f0246..82b6320 100644
--- a/arch/sh/include/cpu-sh4/cpu/sh7734.h
+++ b/arch/sh/include/cpu-sh4/cpu/sh7734.h
@@ -134,7 +134,7 @@ enum {
GPIO_FN_EX_WAIT1, GPIO_FN_SD1_DAT0_A, GPIO_FN_DREQ2, GPIO_FN_CAN1_TX_C,
GPIO_FN_ET0_LINK_C, GPIO_FN_ET0_ETXD5_A,
GPIO_FN_EX_WAIT0, GPIO_FN_TCLK1_B,
- GPIO_FN_RD_WR, GPIO_FN_TCLK0,
+ GPIO_FN_RD_WR, GPIO_FN_TCLK0, GPIO_FN_CAN_CLK_B, GPIO_FN_ET0_ETXD4,
GPIO_FN_EX_CS5, GPIO_FN_SD1_CMD_A, GPIO_FN_ATADIR, GPIO_FN_QSSL_B,
GPIO_FN_ET0_ETXD3_A,
GPIO_FN_EX_CS4, GPIO_FN_SD1_WP_A, GPIO_FN_ATAWR, GPIO_FN_QMI_QIO1_B,
diff --git a/arch/sparc/include/asm/cmpxchg_64.h b/arch/sparc/include/asm/cmpxchg_64.h
index f71ef37..316faa0 100644
--- a/arch/sparc/include/asm/cmpxchg_64.h
+++ b/arch/sparc/include/asm/cmpxchg_64.h
@@ -52,7 +52,12 @@ static inline unsigned long xchg64(__volatile__ unsigned long *m, unsigned long
return val;
}
-#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+#define xchg(ptr,x) \
+({ __typeof__(*(ptr)) __ret; \
+ __ret = (__typeof__(*(ptr))) \
+ __xchg((unsigned long)(x), (ptr), sizeof(*(ptr))); \
+ __ret; \
+})
void __xchg_called_with_bad_pointer(void);
diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h
index b162c23..7a836d2 100644
--- a/arch/sparc/include/asm/io_64.h
+++ b/arch/sparc/include/asm/io_64.h
@@ -409,6 +409,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
}
#define ioremap_nocache(X,Y) ioremap((X),(Y))
+#define ioremap_uc(X,Y) ioremap((X),(Y))
#define ioremap_wc(X,Y) ioremap((X),(Y))
#define ioremap_wt(X,Y) ioremap((X),(Y))
diff --git a/arch/sparc/include/asm/parport.h b/arch/sparc/include/asm/parport.h
index 05df5f0..3c5a1c6 100644
--- a/arch/sparc/include/asm/parport.h
+++ b/arch/sparc/include/asm/parport.h
@@ -21,6 +21,7 @@
*/
#define HAS_DMA
+#ifdef CONFIG_PARPORT_PC_FIFO
static DEFINE_SPINLOCK(dma_spin_lock);
#define claim_dma_lock() \
@@ -31,6 +32,7 @@ static DEFINE_SPINLOCK(dma_spin_lock);
#define release_dma_lock(__flags) \
spin_unlock_irqrestore(&dma_spin_lock, __flags);
+#endif
static struct sparc_ebus_info {
struct ebus_dma_info info;
diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
index 222785a..ec4da4d 100644
--- a/arch/sparc/net/bpf_jit_comp_64.c
+++ b/arch/sparc/net/bpf_jit_comp_64.c
@@ -1270,6 +1270,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
const u8 tmp2 = bpf2sparc[TMP_REG_2];
u32 opcode = 0, rs2;
+ if (insn->dst_reg == BPF_REG_FP)
+ ctx->saw_frame_pointer = true;
+
ctx->tmp_2_used = true;
emit_loadimm(imm, tmp2, ctx);
@@ -1308,6 +1311,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
const u8 tmp = bpf2sparc[TMP_REG_1];
u32 opcode = 0, rs2;
+ if (insn->dst_reg == BPF_REG_FP)
+ ctx->saw_frame_pointer = true;
+
switch (BPF_SIZE(code)) {
case BPF_W:
opcode = ST32;
@@ -1340,6 +1346,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
const u8 tmp2 = bpf2sparc[TMP_REG_2];
const u8 tmp3 = bpf2sparc[TMP_REG_3];
+ if (insn->dst_reg == BPF_REG_FP)
+ ctx->saw_frame_pointer = true;
+
ctx->tmp_1_used = true;
ctx->tmp_2_used = true;
ctx->tmp_3_used = true;
@@ -1360,6 +1369,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
const u8 tmp2 = bpf2sparc[TMP_REG_2];
const u8 tmp3 = bpf2sparc[TMP_REG_3];
+ if (insn->dst_reg == BPF_REG_FP)
+ ctx->saw_frame_pointer = true;
+
ctx->tmp_1_used = true;
ctx->tmp_2_used = true;
ctx->tmp_3_used = true;
@@ -1425,12 +1437,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *tmp, *orig_prog = prog;
struct sparc64_jit_data *jit_data;
struct bpf_binary_header *header;
+ u32 prev_image_size, image_size;
bool tmp_blinded = false;
bool extra_pass = false;
struct jit_ctx ctx;
- u32 image_size;
u8 *image_ptr;
- int pass;
+ int pass, i;
if (!prog->jit_requested)
return orig_prog;
@@ -1461,27 +1473,52 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
header = jit_data->header;
extra_pass = true;
image_size = sizeof(u32) * ctx.idx;
+ prev_image_size = image_size;
+ pass = 1;
goto skip_init_ctx;
}
memset(&ctx, 0, sizeof(ctx));
ctx.prog = prog;
- ctx.offset = kcalloc(prog->len, sizeof(unsigned int), GFP_KERNEL);
+ ctx.offset = kmalloc_array(prog->len, sizeof(unsigned int), GFP_KERNEL);
if (ctx.offset == NULL) {
prog = orig_prog;
goto out_off;
}
- /* Fake pass to detect features used, and get an accurate assessment
- * of what the final image size will be.
+ /* Longest sequence emitted is for bswap32, 12 instructions. Pre-cook
+ * the offset array so that we converge faster.
*/
- if (build_body(&ctx)) {
- prog = orig_prog;
- goto out_off;
+ for (i = 0; i < prog->len; i++)
+ ctx.offset[i] = i * (12 * 4);
+
+ prev_image_size = ~0U;
+ for (pass = 1; pass < 40; pass++) {
+ ctx.idx = 0;
+
+ build_prologue(&ctx);
+ if (build_body(&ctx)) {
+ prog = orig_prog;
+ goto out_off;
+ }
+ build_epilogue(&ctx);
+
+ if (bpf_jit_enable > 1)
+ pr_info("Pass %d: size = %u, seen = [%c%c%c%c%c%c]\n", pass,
+ ctx.idx * 4,
+ ctx.tmp_1_used ? '1' : ' ',
+ ctx.tmp_2_used ? '2' : ' ',
+ ctx.tmp_3_used ? '3' : ' ',
+ ctx.saw_frame_pointer ? 'F' : ' ',
+ ctx.saw_call ? 'C' : ' ',
+ ctx.saw_tail_call ? 'T' : ' ');
+
+ if (ctx.idx * 4 == prev_image_size)
+ break;
+ prev_image_size = ctx.idx * 4;
+ cond_resched();
}
- build_prologue(&ctx);
- build_epilogue(&ctx);
/* Now we know the actual image size. */
image_size = sizeof(u32) * ctx.idx;
@@ -1494,28 +1531,24 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.image = (u32 *)image_ptr;
skip_init_ctx:
- for (pass = 1; pass < 3; pass++) {
- ctx.idx = 0;
+ ctx.idx = 0;
- build_prologue(&ctx);
+ build_prologue(&ctx);
- if (build_body(&ctx)) {
- bpf_jit_binary_free(header);
- prog = orig_prog;
- goto out_off;
- }
+ if (build_body(&ctx)) {
+ bpf_jit_binary_free(header);
+ prog = orig_prog;
+ goto out_off;
+ }
- build_epilogue(&ctx);
+ build_epilogue(&ctx);
- if (bpf_jit_enable > 1)
- pr_info("Pass %d: shrink = %d, seen = [%c%c%c%c%c%c]\n", pass,
- image_size - (ctx.idx * 4),
- ctx.tmp_1_used ? '1' : ' ',
- ctx.tmp_2_used ? '2' : ' ',
- ctx.tmp_3_used ? '3' : ' ',
- ctx.saw_frame_pointer ? 'F' : ' ',
- ctx.saw_call ? 'C' : ' ',
- ctx.saw_tail_call ? 'T' : ' ');
+ if (ctx.idx * 4 != prev_image_size) {
+ pr_err("bpf_jit: Failed to converge, prev_size=%u size=%d\n",
+ prev_image_size, ctx.idx * 4);
+ bpf_jit_binary_free(header);
+ prog = orig_prog;
+ goto out_off;
}
if (bpf_jit_enable > 1)
diff --git a/arch/um/Kconfig.debug b/arch/um/Kconfig.debug
index 2014597..85726ee 100644
--- a/arch/um/Kconfig.debug
+++ b/arch/um/Kconfig.debug
@@ -16,6 +16,7 @@
config GCOV
bool "Enable gcov support"
depends on DEBUG_INFO
+ depends on !KCOV
help
This option allows developers to retrieve coverage data from a UML
session.
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 8d80b27..7e524ef 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -261,7 +261,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
if (err == 0) {
spin_unlock(&line->lock);
return IRQ_NONE;
- } else if (err < 0) {
+ } else if ((err < 0) && (err != -EAGAIN)) {
line->head = line->buffer;
line->tail = line->buffer;
}
@@ -284,7 +284,7 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
if (err)
return err;
if (output)
- err = um_request_irq(driver->write_irq, fd, IRQ_NONE,
+ err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
line_write_interrupt, IRQF_SHARED,
driver->write_irq_name, data);
return err;
diff --git a/arch/um/drivers/vector_user.c b/arch/um/drivers/vector_user.c
index 4d6a78e..00c4c27 100644
--- a/arch/um/drivers/vector_user.c
+++ b/arch/um/drivers/vector_user.c
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <os.h>
#include <um_malloc.h>
+#include <sys/uio.h>
#include "vector_user.h"
#define ID_GRE 0
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f85253f..d9ba533 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1904,6 +1904,51 @@
If unsure, say y.
+choice
+ prompt "TSX enable mode"
+ depends on CPU_SUP_INTEL
+ default X86_INTEL_TSX_MODE_OFF
+ help
+ Intel's TSX (Transactional Synchronization Extensions) feature
+ allows to optimize locking protocols through lock elision which
+ can lead to a noticeable performance boost.
+
+ On the other hand it has been shown that TSX can be exploited
+ to form side channel attacks (e.g. TAA) and chances are there
+ will be more of those attacks discovered in the future.
+
+ Therefore TSX is not enabled by default (aka tsx=off). An admin
+ might override this decision by tsx=on the command line parameter.
+ Even with TSX enabled, the kernel will attempt to enable the best
+ possible TAA mitigation setting depending on the microcode available
+ for the particular machine.
+
+ This option allows to set the default tsx mode between tsx=on, =off
+ and =auto. See Documentation/admin-guide/kernel-parameters.txt for more
+ details.
+
+ Say off if not sure, auto if TSX is in use but it should be used on safe
+ platforms or on if TSX is in use and the security aspect of tsx is not
+ relevant.
+
+config X86_INTEL_TSX_MODE_OFF
+ bool "off"
+ help
+ TSX is disabled if possible - equals to tsx=off command line parameter.
+
+config X86_INTEL_TSX_MODE_ON
+ bool "on"
+ help
+ TSX is always enabled on TSX capable HW - equals the tsx=on command
+ line parameter.
+
+config X86_INTEL_TSX_MODE_AUTO
+ bool "auto"
+ help
+ TSX is enabled on TSX capable HW that is believed to be safe against
+ side channel attacks- equals the tsx=auto command line parameter.
+endchoice
+
config EFI
bool "EFI runtime service support"
depends on ACPI
@@ -2727,8 +2772,7 @@
config OLPC_XO1_PM
bool "OLPC XO-1 Power Management"
- depends on OLPC && MFD_CS5535 && PM_SLEEP
- select MFD_CORE
+ depends on OLPC && MFD_CS5535=y && PM_SLEEP
---help---
Add support for poweroff and suspend of the OLPC XO-1 laptop.
diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig
index e2b132a..0d86eadb 100644
--- a/arch/x86/configs/x86_64_cuttlefish_defconfig
+++ b/arch/x86/configs/x86_64_cuttlefish_defconfig
@@ -80,7 +80,6 @@
CONFIG_BINFMT_MISC=y
CONFIG_KSM=y
CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
-CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_ZSMALLOC=y
CONFIG_NET=y
CONFIG_PACKET=y
diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c
index 27ade3cb..defb536 100644
--- a/arch/x86/events/amd/core.c
+++ b/arch/x86/events/amd/core.c
@@ -4,12 +4,14 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/jiffies.h>
#include <asm/apicdef.h>
#include <asm/nmi.h>
#include "../perf_event.h"
-static DEFINE_PER_CPU(unsigned int, perf_nmi_counter);
+static DEFINE_PER_CPU(unsigned long, perf_nmi_tstamp);
+static unsigned long perf_nmi_window;
static __initconst const u64 amd_hw_cache_event_ids
[PERF_COUNT_HW_CACHE_MAX]
@@ -640,11 +642,12 @@ static void amd_pmu_disable_event(struct perf_event *event)
* handler when multiple PMCs are active or PMC overflow while handling some
* other source of an NMI.
*
- * Attempt to mitigate this by using the number of active PMCs to determine
- * whether to return NMI_HANDLED if the perf NMI handler did not handle/reset
- * any PMCs. The per-CPU perf_nmi_counter variable is set to a minimum of the
- * number of active PMCs or 2. The value of 2 is used in case an NMI does not
- * arrive at the LAPIC in time to be collapsed into an already pending NMI.
+ * Attempt to mitigate this by creating an NMI window in which un-handled NMIs
+ * received during this window will be claimed. This prevents extending the
+ * window past when it is possible that latent NMIs should be received. The
+ * per-CPU perf_nmi_tstamp will be set to the window end time whenever perf has
+ * handled a counter. When an un-handled NMI is received, it will be claimed
+ * only if arriving within that window.
*/
static int amd_pmu_handle_irq(struct pt_regs *regs)
{
@@ -662,21 +665,19 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
handled = x86_pmu_handle_irq(regs);
/*
- * If a counter was handled, record the number of possible remaining
- * NMIs that can occur.
+ * If a counter was handled, record a timestamp such that un-handled
+ * NMIs will be claimed if arriving within that window.
*/
if (handled) {
- this_cpu_write(perf_nmi_counter,
- min_t(unsigned int, 2, active));
+ this_cpu_write(perf_nmi_tstamp,
+ jiffies + perf_nmi_window);
return handled;
}
- if (!this_cpu_read(perf_nmi_counter))
+ if (time_after(jiffies, this_cpu_read(perf_nmi_tstamp)))
return NMI_DONE;
- this_cpu_dec(perf_nmi_counter);
-
return NMI_HANDLED;
}
@@ -908,6 +909,9 @@ static int __init amd_core_pmu_init(void)
if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
return 0;
+ /* Avoid calulating the value each time in the NMI handler */
+ perf_nmi_window = msecs_to_jiffies(100);
+
switch (boot_cpu_data.x86) {
case 0x15:
pr_cont("Fam15h ");
diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c
index 80c6d84..07bf551 100644
--- a/arch/x86/events/amd/ibs.c
+++ b/arch/x86/events/amd/ibs.c
@@ -389,7 +389,8 @@ static inline void perf_ibs_disable_event(struct perf_ibs *perf_ibs,
struct hw_perf_event *hwc, u64 config)
{
config &= ~perf_ibs->cnt_mask;
- wrmsrl(hwc->config_base, config);
+ if (boot_cpu_data.x86 == 0x10)
+ wrmsrl(hwc->config_base, config);
config &= ~perf_ibs->enable_mask;
wrmsrl(hwc->config_base, config);
}
@@ -564,7 +565,8 @@ static struct perf_ibs perf_ibs_op = {
},
.msr = MSR_AMD64_IBSOPCTL,
.config_mask = IBS_OP_CONFIG_MASK,
- .cnt_mask = IBS_OP_MAX_CNT,
+ .cnt_mask = IBS_OP_MAX_CNT | IBS_OP_CUR_CNT |
+ IBS_OP_CUR_CNT_RAND,
.enable_mask = IBS_OP_ENABLE,
.valid_mask = IBS_OP_VAL,
.max_period = IBS_OP_MAX_CNT << 4,
@@ -625,7 +627,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
if (event->attr.sample_type & PERF_SAMPLE_RAW)
offset_max = perf_ibs->offset_max;
else if (check_rip)
- offset_max = 2;
+ offset_max = 3;
else
offset_max = 1;
do {
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index c9625bf..4293894 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -375,7 +375,7 @@ int x86_add_exclusive(unsigned int what)
* LBR and BTS are still mutually exclusive.
*/
if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
- return 0;
+ goto out;
if (!atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) {
mutex_lock(&pmc_reserve_mutex);
@@ -387,6 +387,7 @@ int x86_add_exclusive(unsigned int what)
mutex_unlock(&pmc_reserve_mutex);
}
+out:
atomic_inc(&active_events);
return 0;
@@ -397,11 +398,15 @@ int x86_add_exclusive(unsigned int what)
void x86_del_exclusive(unsigned int what)
{
+ atomic_dec(&active_events);
+
+ /*
+ * See the comment in x86_add_exclusive().
+ */
if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
return;
atomic_dec(&x86_pmu.lbr_exclusive[what]);
- atomic_dec(&active_events);
}
int x86_setup_perfctr(struct perf_event *event)
diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c
index 7139f6b..510f946 100644
--- a/arch/x86/events/intel/bts.c
+++ b/arch/x86/events/intel/bts.c
@@ -71,9 +71,17 @@ struct bts_buffer {
static struct pmu bts_pmu;
+static int buf_nr_pages(struct page *page)
+{
+ if (!PagePrivate(page))
+ return 1;
+
+ return 1 << page_private(page);
+}
+
static size_t buf_size(struct page *page)
{
- return 1 << (PAGE_SHIFT + page_private(page));
+ return buf_nr_pages(page) * PAGE_SIZE;
}
static void *
@@ -91,9 +99,7 @@ bts_buffer_setup_aux(struct perf_event *event, void **pages,
/* count all the high order buffers */
for (pg = 0, nbuf = 0; pg < nr_pages;) {
page = virt_to_page(pages[pg]);
- if (WARN_ON_ONCE(!PagePrivate(page) && nr_pages > 1))
- return NULL;
- pg += 1 << page_private(page);
+ pg += buf_nr_pages(page);
nbuf++;
}
@@ -117,7 +123,7 @@ bts_buffer_setup_aux(struct perf_event *event, void **pages,
unsigned int __nr_pages;
page = virt_to_page(pages[pg]);
- __nr_pages = PagePrivate(page) ? 1 << page_private(page) : 1;
+ __nr_pages = buf_nr_pages(page);
buf->buf[nbuf].page = page;
buf->buf[nbuf].offset = offset;
buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0);
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 2690135..7098b9b 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -485,10 +485,8 @@ void uncore_pmu_event_start(struct perf_event *event, int flags)
local64_set(&event->hw.prev_count, uncore_read_counter(box, event));
uncore_enable_event(box, event);
- if (box->n_active == 1) {
- uncore_enable_box(box);
+ if (box->n_active == 1)
uncore_pmu_start_hrtimer(box);
- }
}
void uncore_pmu_event_stop(struct perf_event *event, int flags)
@@ -512,10 +510,8 @@ void uncore_pmu_event_stop(struct perf_event *event, int flags)
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
hwc->state |= PERF_HES_STOPPED;
- if (box->n_active == 0) {
- uncore_disable_box(box);
+ if (box->n_active == 0)
uncore_pmu_cancel_hrtimer(box);
- }
}
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
@@ -769,6 +765,40 @@ static int uncore_pmu_event_init(struct perf_event *event)
return ret;
}
+static void uncore_pmu_enable(struct pmu *pmu)
+{
+ struct intel_uncore_pmu *uncore_pmu;
+ struct intel_uncore_box *box;
+
+ uncore_pmu = container_of(pmu, struct intel_uncore_pmu, pmu);
+ if (!uncore_pmu)
+ return;
+
+ box = uncore_pmu_to_box(uncore_pmu, smp_processor_id());
+ if (!box)
+ return;
+
+ if (uncore_pmu->type->ops->enable_box)
+ uncore_pmu->type->ops->enable_box(box);
+}
+
+static void uncore_pmu_disable(struct pmu *pmu)
+{
+ struct intel_uncore_pmu *uncore_pmu;
+ struct intel_uncore_box *box;
+
+ uncore_pmu = container_of(pmu, struct intel_uncore_pmu, pmu);
+ if (!uncore_pmu)
+ return;
+
+ box = uncore_pmu_to_box(uncore_pmu, smp_processor_id());
+ if (!box)
+ return;
+
+ if (uncore_pmu->type->ops->disable_box)
+ uncore_pmu->type->ops->disable_box(box);
+}
+
static ssize_t uncore_get_attr_cpumask(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -794,6 +824,8 @@ static int uncore_pmu_register(struct intel_uncore_pmu *pmu)
pmu->pmu = (struct pmu) {
.attr_groups = pmu->type->attr_groups,
.task_ctx_nr = perf_invalid_context,
+ .pmu_enable = uncore_pmu_enable,
+ .pmu_disable = uncore_pmu_disable,
.event_init = uncore_pmu_event_init,
.add = uncore_pmu_event_add,
.del = uncore_pmu_event_del,
diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h
index 42fa397..40e040e 100644
--- a/arch/x86/events/intel/uncore.h
+++ b/arch/x86/events/intel/uncore.h
@@ -412,18 +412,6 @@ static inline int uncore_freerunning_hw_config(struct intel_uncore_box *box,
return -EINVAL;
}
-static inline void uncore_disable_box(struct intel_uncore_box *box)
-{
- if (box->pmu->type->ops->disable_box)
- box->pmu->type->ops->disable_box(box);
-}
-
-static inline void uncore_enable_box(struct intel_uncore_box *box)
-{
- if (box->pmu->type->ops->enable_box)
- box->pmu->type->ops->enable_box(box);
-}
-
static inline void uncore_disable_event(struct intel_uncore_box *box,
struct perf_event *event)
{
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index 3fb8551..8a9cff1 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -17,6 +17,7 @@
*
*/
+#include <linux/efi.h>
#include <linux/types.h>
#include <asm/apic.h>
#include <asm/desc.h>
@@ -257,6 +258,22 @@ static int hv_cpu_die(unsigned int cpu)
return 0;
}
+static int __init hv_pci_init(void)
+{
+ int gen2vm = efi_enabled(EFI_BOOT);
+
+ /*
+ * For Generation-2 VM, we exit from pci_arch_init() by returning 0.
+ * The purpose is to suppress the harmless warning:
+ * "PCI: Fatal: No config space access function found"
+ */
+ if (gen2vm)
+ return 0;
+
+ /* For Generation-1 VM, we'll proceed in pci_arch_init(). */
+ return 1;
+}
+
/*
* This function is to be invoked early in the boot sequence after the
* hypervisor has been detected.
@@ -333,6 +350,8 @@ void __init hyperv_init(void)
hv_apic_init();
+ x86_init.pci.arch_init = hv_pci_init;
+
/*
* Register Hyper-V specific clocksource.
*/
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 759f0a1..8c13b99 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -389,5 +389,7 @@
#define X86_BUG_MDS X86_BUG(19) /* CPU is affected by Microarchitectural data sampling */
#define X86_BUG_MSBDS_ONLY X86_BUG(20) /* CPU is only affected by the MSDBS variant of BUG_MDS */
#define X86_BUG_SWAPGS X86_BUG(21) /* CPU is affected by speculation through SWAPGS */
+#define X86_BUG_TAA X86_BUG(22) /* CPU is affected by TSX Async Abort(TAA) */
+#define X86_BUG_ITLB_MULTIHIT X86_BUG(23) /* CPU may incur MCE during certain page attribute changes */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/crash.h b/arch/x86/include/asm/crash.h
index a7adb2bfb..6b8ad6f 100644
--- a/arch/x86/include/asm/crash.h
+++ b/arch/x86/include/asm/crash.h
@@ -2,6 +2,8 @@
#ifndef _ASM_X86_CRASH_H
#define _ASM_X86_CRASH_H
+struct kimage;
+
int crash_load_segments(struct kimage *image);
int crash_copy_backup_region(struct kimage *image);
int crash_setup_memmap_entries(struct kimage *image,
diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h
index 5d0b72f..2a8e5f7 100644
--- a/arch/x86/include/asm/intel-family.h
+++ b/arch/x86/include/asm/intel-family.h
@@ -6,7 +6,7 @@
* "Big Core" Processors (Branded as Core, Xeon, etc...)
*
* The "_X" parts are generally the EP and EX Xeons, or the
- * "Extreme" ones, like Broadwell-E.
+ * "Extreme" ones, like Broadwell-E, or Atom microserver.
*
* While adding a new CPUID for a new microarchitecture, add a new
* group to keep logically sorted out in chronological order. Within
@@ -61,6 +61,9 @@
#define INTEL_FAM6_TIGERLAKE_L 0x8C
#define INTEL_FAM6_TIGERLAKE 0x8D
+#define INTEL_FAM6_COMETLAKE 0xA5
+#define INTEL_FAM6_COMETLAKE_L 0xA6
+
/* "Small Core" Processors (Atom) */
#define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */
@@ -80,6 +83,7 @@
#define INTEL_FAM6_ATOM_GOLDMONT 0x5C /* Apollo Lake */
#define INTEL_FAM6_ATOM_GOLDMONT_X 0x5F /* Denverton */
#define INTEL_FAM6_ATOM_GOLDMONT_PLUS 0x7A /* Gemini Lake */
+#define INTEL_FAM6_ATOM_TREMONT_X 0x86 /* Jacobsville */
/* Xeon Phi */
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index f327236f..5125fca 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -67,7 +67,7 @@ struct kimage;
/* Memory to backup during crash kdump */
#define KEXEC_BACKUP_SRC_START (0UL)
-#define KEXEC_BACKUP_SRC_END (640 * 1024UL) /* 640K */
+#define KEXEC_BACKUP_SRC_END (640 * 1024UL - 1) /* 640K */
/*
* CPU does not save ss and sp on stack if execution is already
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 0d3f5cf..155be8a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -281,6 +281,7 @@ struct kvm_rmap_head {
struct kvm_mmu_page {
struct list_head link;
struct hlist_node hash_link;
+ struct list_head lpage_disallowed_link;
/*
* The following two entries are used to key the shadow page in the
@@ -293,6 +294,7 @@ struct kvm_mmu_page {
/* hold the gfn of each spte inside spt */
gfn_t *gfns;
bool unsync;
+ bool lpage_disallowed; /* Can't be replaced by an equiv large page */
int root_count; /* Currently serving as active root */
unsigned int unsync_children;
struct kvm_rmap_head parent_ptes; /* rmap pointers to parent sptes */
@@ -807,6 +809,7 @@ struct kvm_arch {
*/
struct list_head active_mmu_pages;
struct list_head zapped_obsolete_pages;
+ struct list_head lpage_disallowed_mmu_pages;
struct kvm_page_track_notifier_node mmu_sp_tracker;
struct kvm_page_track_notifier_head track_notifier_head;
@@ -877,6 +880,8 @@ struct kvm_arch {
bool x2apic_broadcast_quirk_disabled;
bool guest_can_read_msr_platform_info;
+
+ struct task_struct *nx_lpage_recovery_thread;
};
struct kvm_vm_stat {
@@ -890,6 +895,7 @@ struct kvm_vm_stat {
ulong mmu_unsync;
ulong remote_tlb_flush;
ulong lpages;
+ ulong nx_lpage_splits;
ulong max_mmu_page_hash_collisions;
};
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index a1d22e4..0f4feee 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -84,6 +84,18 @@
* Microarchitectural Data
* Sampling (MDS) vulnerabilities.
*/
+#define ARCH_CAP_PSCHANGE_MC_NO BIT(6) /*
+ * The processor is not susceptible to a
+ * machine check error due to modifying the
+ * code page size along with either the
+ * physical address or cache type
+ * without TLB invalidation.
+ */
+#define ARCH_CAP_TSX_CTRL_MSR BIT(7) /* MSR for TSX control is available. */
+#define ARCH_CAP_TAA_NO BIT(8) /*
+ * Not susceptible to
+ * TSX Async Abort (TAA) vulnerabilities.
+ */
#define MSR_IA32_FLUSH_CMD 0x0000010b
#define L1D_FLUSH BIT(0) /*
@@ -94,6 +106,10 @@
#define MSR_IA32_BBL_CR_CTL 0x00000119
#define MSR_IA32_BBL_CR_CTL3 0x0000011e
+#define MSR_IA32_TSX_CTRL 0x00000122
+#define TSX_CTRL_RTM_DISABLE BIT(0) /* Disable RTM feature */
+#define TSX_CTRL_CPUID_CLEAR BIT(1) /* Disable TSX enumeration */
+
#define MSR_IA32_SYSENTER_CS 0x00000174
#define MSR_IA32_SYSENTER_ESP 0x00000175
#define MSR_IA32_SYSENTER_EIP 0x00000176
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index 28cb2b3..09c7466 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -323,7 +323,7 @@ DECLARE_STATIC_KEY_FALSE(mds_idle_clear);
#include <asm/segment.h>
/**
- * mds_clear_cpu_buffers - Mitigation for MDS vulnerability
+ * mds_clear_cpu_buffers - Mitigation for MDS and TAA vulnerability
*
* This uses the otherwise unused and obsolete VERW instruction in
* combination with microcode which triggers a CPU buffer flush when the
@@ -346,7 +346,7 @@ static inline void mds_clear_cpu_buffers(void)
}
/**
- * mds_user_clear_cpu_buffers - Mitigation for MDS vulnerability
+ * mds_user_clear_cpu_buffers - Mitigation for MDS and TAA vulnerability
*
* Clear CPU buffers if the corresponding static key is enabled
*/
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index b54f256..efb44bd 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -1003,4 +1003,11 @@ enum mds_mitigations {
MDS_MITIGATION_VMWERV,
};
+enum taa_mitigations {
+ TAA_MITIGATION_OFF,
+ TAA_MITIGATION_UCODE_NEEDED,
+ TAA_MITIGATION_VERW,
+ TAA_MITIGATION_TSX_DISABLED,
+};
+
#endif /* _ASM_X86_PROCESSOR_H */
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 6de1fd3..ee696ef 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -237,23 +237,51 @@ static inline int regs_within_kernel_stack(struct pt_regs *regs,
}
/**
+ * regs_get_kernel_stack_nth_addr() - get the address of the Nth entry on stack
+ * @regs: pt_regs which contains kernel stack pointer.
+ * @n: stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns the address of the @n th entry of the
+ * kernel stack which is specified by @regs. If the @n th entry is NOT in
+ * the kernel stack, this returns NULL.
+ */
+static inline unsigned long *regs_get_kernel_stack_nth_addr(struct pt_regs *regs, unsigned int n)
+{
+ unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
+
+ addr += n;
+ if (regs_within_kernel_stack(regs, (unsigned long)addr))
+ return addr;
+ else
+ return NULL;
+}
+
+/* To avoid include hell, we can't include uaccess.h */
+extern long probe_kernel_read(void *dst, const void *src, size_t size);
+
+/**
* regs_get_kernel_stack_nth() - get Nth entry of the stack
* @regs: pt_regs which contains kernel stack pointer.
* @n: stack entry number.
*
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
- * is specified by @regs. If the @n th entry is NOT in the kernel stack,
+ * is specified by @regs. If the @n th entry is NOT in the kernel stack
* this returns 0.
*/
static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
unsigned int n)
{
- unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
- addr += n;
- if (regs_within_kernel_stack(regs, (unsigned long)addr))
- return *addr;
- else
- return 0;
+ unsigned long *addr;
+ unsigned long val;
+ long ret;
+
+ addr = regs_get_kernel_stack_nth_addr(regs, n);
+ if (addr) {
+ ret = probe_kernel_read(&val, addr, sizeof(val));
+ if (!ret)
+ return val;
+ }
+ return 0;
}
#define arch_has_single_step() (1)
diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h
index 7eb936b..74053d6 100644
--- a/arch/x86/include/asm/syscall_wrapper.h
+++ b/arch/x86/include/asm/syscall_wrapper.h
@@ -196,10 +196,10 @@ struct pt_regs;
* macros to work correctly.
*/
#ifndef SYSCALL_DEFINE0
-#define SYSCALL_DEFINE0(sname) \
- SYSCALL_METADATA(_##sname, 0); \
+#define SYSCALL_DEFINE0(sname) \
+ SYSCALL_METADATA(_##sname, 0); \
asmlinkage long __x64_sys_##sname(const struct pt_regs *__unused);\
- ALLOW_ERROR_INJECTION(__x64_sys_##sname, ERRNO); \
+ ALLOW_ERROR_INJECTION(__x64_sys_##sname, ERRNO); \
asmlinkage long __x64_sys_##sname(const struct pt_regs *__unused)
#endif
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index dfdd1ca..1ca76ca 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1528,9 +1528,6 @@ static void setup_local_APIC(void)
{
int cpu = smp_processor_id();
unsigned int value;
-#ifdef CONFIG_X86_32
- int logical_apicid, ldr_apicid;
-#endif
if (disable_apic) {
@@ -1571,16 +1568,21 @@ static void setup_local_APIC(void)
apic->init_apic_ldr();
#ifdef CONFIG_X86_32
- /*
- * APIC LDR is initialized. If logical_apicid mapping was
- * initialized during get_smp_config(), make sure it matches the
- * actual value.
- */
- logical_apicid = early_per_cpu(x86_cpu_to_logical_apicid, cpu);
- ldr_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));
- WARN_ON(logical_apicid != BAD_APICID && logical_apicid != ldr_apicid);
- /* always use the value from LDR */
- early_per_cpu(x86_cpu_to_logical_apicid, cpu) = ldr_apicid;
+ if (apic->dest_logical) {
+ int logical_apicid, ldr_apicid;
+
+ /*
+ * APIC LDR is initialized. If logical_apicid mapping was
+ * initialized during get_smp_config(), make sure it matches
+ * the actual value.
+ */
+ logical_apicid = early_per_cpu(x86_cpu_to_logical_apicid, cpu);
+ ldr_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));
+ if (logical_apicid != BAD_APICID)
+ WARN_ON(logical_apicid != ldr_apicid);
+ /* Always use the value from LDR. */
+ early_per_cpu(x86_cpu_to_logical_apicid, cpu) = ldr_apicid;
+ }
#endif
/*
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index ab22ede..fa3b85b 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1724,9 +1724,10 @@ static bool io_apic_level_ack_pending(struct mp_chip_data *data)
static inline bool ioapic_irqd_mask(struct irq_data *data)
{
- /* If we are moving the irq we need to mask it */
+ /* If we are moving the IRQ we need to mask it */
if (unlikely(irqd_is_setaffinity_pending(data))) {
- mask_ioapic_irq(data);
+ if (!irqd_irq_masked(data))
+ mask_ioapic_irq(data);
return true;
}
return false;
@@ -1763,7 +1764,9 @@ static inline void ioapic_irqd_unmask(struct irq_data *data, bool masked)
*/
if (!io_apic_level_ack_pending(data->chip_data))
irq_move_masked_irq(data);
- unmask_ioapic_irq(data);
+ /* If the IRQ is masked in the core, leave it: */
+ if (!irqd_irq_masked(data))
+ unmask_ioapic_irq(data);
}
}
#else
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 347137e..320769b 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -28,7 +28,7 @@
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
-obj-$(CONFIG_CPU_SUP_INTEL) += intel.o intel_pconfig.o
+obj-$(CONFIG_CPU_SUP_INTEL) += intel.o intel_pconfig.o tsx.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index ee7d176..2d23a44 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -32,11 +32,15 @@
#include <asm/e820/api.h>
#include <asm/hypervisor.h>
+#include "cpu.h"
+
static void __init spectre_v1_select_mitigation(void);
static void __init spectre_v2_select_mitigation(void);
static void __init ssb_select_mitigation(void);
static void __init l1tf_select_mitigation(void);
static void __init mds_select_mitigation(void);
+static void __init mds_print_mitigation(void);
+static void __init taa_select_mitigation(void);
/* The base value of the SPEC_CTRL MSR that always has to be preserved. */
u64 x86_spec_ctrl_base;
@@ -103,6 +107,13 @@ void __init check_bugs(void)
ssb_select_mitigation();
l1tf_select_mitigation();
mds_select_mitigation();
+ taa_select_mitigation();
+
+ /*
+ * As MDS and TAA mitigations are inter-related, print MDS
+ * mitigation until after TAA mitigation selection is done.
+ */
+ mds_print_mitigation();
arch_smt_update();
@@ -241,6 +252,12 @@ static void __init mds_select_mitigation(void)
(mds_nosmt || cpu_mitigations_auto_nosmt()))
cpu_smt_disable(false);
}
+}
+
+static void __init mds_print_mitigation(void)
+{
+ if (!boot_cpu_has_bug(X86_BUG_MDS) || cpu_mitigations_off())
+ return;
pr_info("%s\n", mds_strings[mds_mitigation]);
}
@@ -267,6 +284,113 @@ static int __init mds_cmdline(char *str)
early_param("mds", mds_cmdline);
#undef pr_fmt
+#define pr_fmt(fmt) "TAA: " fmt
+
+/* Default mitigation for TAA-affected CPUs */
+static enum taa_mitigations taa_mitigation __ro_after_init = TAA_MITIGATION_VERW;
+static bool taa_nosmt __ro_after_init;
+
+static const char * const taa_strings[] = {
+ [TAA_MITIGATION_OFF] = "Vulnerable",
+ [TAA_MITIGATION_UCODE_NEEDED] = "Vulnerable: Clear CPU buffers attempted, no microcode",
+ [TAA_MITIGATION_VERW] = "Mitigation: Clear CPU buffers",
+ [TAA_MITIGATION_TSX_DISABLED] = "Mitigation: TSX disabled",
+};
+
+static void __init taa_select_mitigation(void)
+{
+ u64 ia32_cap;
+
+ if (!boot_cpu_has_bug(X86_BUG_TAA)) {
+ taa_mitigation = TAA_MITIGATION_OFF;
+ return;
+ }
+
+ /* TSX previously disabled by tsx=off */
+ if (!boot_cpu_has(X86_FEATURE_RTM)) {
+ taa_mitigation = TAA_MITIGATION_TSX_DISABLED;
+ goto out;
+ }
+
+ if (cpu_mitigations_off()) {
+ taa_mitigation = TAA_MITIGATION_OFF;
+ return;
+ }
+
+ /*
+ * TAA mitigation via VERW is turned off if both
+ * tsx_async_abort=off and mds=off are specified.
+ */
+ if (taa_mitigation == TAA_MITIGATION_OFF &&
+ mds_mitigation == MDS_MITIGATION_OFF)
+ goto out;
+
+ if (boot_cpu_has(X86_FEATURE_MD_CLEAR))
+ taa_mitigation = TAA_MITIGATION_VERW;
+ else
+ taa_mitigation = TAA_MITIGATION_UCODE_NEEDED;
+
+ /*
+ * VERW doesn't clear the CPU buffers when MD_CLEAR=1 and MDS_NO=1.
+ * A microcode update fixes this behavior to clear CPU buffers. It also
+ * adds support for MSR_IA32_TSX_CTRL which is enumerated by the
+ * ARCH_CAP_TSX_CTRL_MSR bit.
+ *
+ * On MDS_NO=1 CPUs if ARCH_CAP_TSX_CTRL_MSR is not set, microcode
+ * update is required.
+ */
+ ia32_cap = x86_read_arch_cap_msr();
+ if ( (ia32_cap & ARCH_CAP_MDS_NO) &&
+ !(ia32_cap & ARCH_CAP_TSX_CTRL_MSR))
+ taa_mitigation = TAA_MITIGATION_UCODE_NEEDED;
+
+ /*
+ * TSX is enabled, select alternate mitigation for TAA which is
+ * the same as MDS. Enable MDS static branch to clear CPU buffers.
+ *
+ * For guests that can't determine whether the correct microcode is
+ * present on host, enable the mitigation for UCODE_NEEDED as well.
+ */
+ static_branch_enable(&mds_user_clear);
+
+ if (taa_nosmt || cpu_mitigations_auto_nosmt())
+ cpu_smt_disable(false);
+
+ /*
+ * Update MDS mitigation, if necessary, as the mds_user_clear is
+ * now enabled for TAA mitigation.
+ */
+ if (mds_mitigation == MDS_MITIGATION_OFF &&
+ boot_cpu_has_bug(X86_BUG_MDS)) {
+ mds_mitigation = MDS_MITIGATION_FULL;
+ mds_select_mitigation();
+ }
+out:
+ pr_info("%s\n", taa_strings[taa_mitigation]);
+}
+
+static int __init tsx_async_abort_parse_cmdline(char *str)
+{
+ if (!boot_cpu_has_bug(X86_BUG_TAA))
+ return 0;
+
+ if (!str)
+ return -EINVAL;
+
+ if (!strcmp(str, "off")) {
+ taa_mitigation = TAA_MITIGATION_OFF;
+ } else if (!strcmp(str, "full")) {
+ taa_mitigation = TAA_MITIGATION_VERW;
+ } else if (!strcmp(str, "full,nosmt")) {
+ taa_mitigation = TAA_MITIGATION_VERW;
+ taa_nosmt = true;
+ }
+
+ return 0;
+}
+early_param("tsx_async_abort", tsx_async_abort_parse_cmdline);
+
+#undef pr_fmt
#define pr_fmt(fmt) "Spectre V1 : " fmt
enum spectre_v1_mitigation {
@@ -772,13 +896,10 @@ static void update_mds_branch_idle(void)
}
#define MDS_MSG_SMT "MDS CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html for more details.\n"
+#define TAA_MSG_SMT "TAA CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/tsx_async_abort.html for more details.\n"
void arch_smt_update(void)
{
- /* Enhanced IBRS implies STIBP. No update required. */
- if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
- return;
-
mutex_lock(&spec_ctrl_mutex);
switch (spectre_v2_user) {
@@ -804,6 +925,17 @@ void arch_smt_update(void)
break;
}
+ switch (taa_mitigation) {
+ case TAA_MITIGATION_VERW:
+ case TAA_MITIGATION_UCODE_NEEDED:
+ if (sched_smt_active())
+ pr_warn_once(TAA_MSG_SMT);
+ break;
+ case TAA_MITIGATION_TSX_DISABLED:
+ case TAA_MITIGATION_OFF:
+ break;
+ }
+
mutex_unlock(&spec_ctrl_mutex);
}
@@ -1119,6 +1251,9 @@ void x86_spec_ctrl_setup_ap(void)
x86_amd_ssb_disable();
}
+bool itlb_multihit_kvm_mitigation;
+EXPORT_SYMBOL_GPL(itlb_multihit_kvm_mitigation);
+
#undef pr_fmt
#define pr_fmt(fmt) "L1TF: " fmt
@@ -1274,11 +1409,24 @@ static ssize_t l1tf_show_state(char *buf)
l1tf_vmx_states[l1tf_vmx_mitigation],
sched_smt_active() ? "vulnerable" : "disabled");
}
+
+static ssize_t itlb_multihit_show_state(char *buf)
+{
+ if (itlb_multihit_kvm_mitigation)
+ return sprintf(buf, "KVM: Mitigation: Split huge pages\n");
+ else
+ return sprintf(buf, "KVM: Vulnerable\n");
+}
#else
static ssize_t l1tf_show_state(char *buf)
{
return sprintf(buf, "%s\n", L1TF_DEFAULT_MSG);
}
+
+static ssize_t itlb_multihit_show_state(char *buf)
+{
+ return sprintf(buf, "Processor vulnerable\n");
+}
#endif
static ssize_t mds_show_state(char *buf)
@@ -1298,6 +1446,21 @@ static ssize_t mds_show_state(char *buf)
sched_smt_active() ? "vulnerable" : "disabled");
}
+static ssize_t tsx_async_abort_show_state(char *buf)
+{
+ if ((taa_mitigation == TAA_MITIGATION_TSX_DISABLED) ||
+ (taa_mitigation == TAA_MITIGATION_OFF))
+ return sprintf(buf, "%s\n", taa_strings[taa_mitigation]);
+
+ if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
+ return sprintf(buf, "%s; SMT Host state unknown\n",
+ taa_strings[taa_mitigation]);
+ }
+
+ return sprintf(buf, "%s; SMT %s\n", taa_strings[taa_mitigation],
+ sched_smt_active() ? "vulnerable" : "disabled");
+}
+
static char *stibp_state(void)
{
if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
@@ -1366,6 +1529,12 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
case X86_BUG_MDS:
return mds_show_state(buf);
+ case X86_BUG_TAA:
+ return tsx_async_abort_show_state(buf);
+
+ case X86_BUG_ITLB_MULTIHIT:
+ return itlb_multihit_show_state(buf);
+
default:
break;
}
@@ -1402,4 +1571,14 @@ ssize_t cpu_show_mds(struct device *dev, struct device_attribute *attr, char *bu
{
return cpu_show_common(dev, attr, buf, X86_BUG_MDS);
}
+
+ssize_t cpu_show_tsx_async_abort(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return cpu_show_common(dev, attr, buf, X86_BUG_TAA);
+}
+
+ssize_t cpu_show_itlb_multihit(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return cpu_show_common(dev, attr, buf, X86_BUG_ITLB_MULTIHIT);
+}
#endif
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index b33fdfa..a6458ab 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -946,13 +946,14 @@ static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c)
#endif
}
-#define NO_SPECULATION BIT(0)
-#define NO_MELTDOWN BIT(1)
-#define NO_SSB BIT(2)
-#define NO_L1TF BIT(3)
-#define NO_MDS BIT(4)
-#define MSBDS_ONLY BIT(5)
-#define NO_SWAPGS BIT(6)
+#define NO_SPECULATION BIT(0)
+#define NO_MELTDOWN BIT(1)
+#define NO_SSB BIT(2)
+#define NO_L1TF BIT(3)
+#define NO_MDS BIT(4)
+#define MSBDS_ONLY BIT(5)
+#define NO_SWAPGS BIT(6)
+#define NO_ITLB_MULTIHIT BIT(7)
#define VULNWL(_vendor, _family, _model, _whitelist) \
{ X86_VENDOR_##_vendor, _family, _model, X86_FEATURE_ANY, _whitelist }
@@ -970,26 +971,26 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = {
VULNWL(NSC, 5, X86_MODEL_ANY, NO_SPECULATION),
/* Intel Family 6 */
- VULNWL_INTEL(ATOM_SALTWELL, NO_SPECULATION),
- VULNWL_INTEL(ATOM_SALTWELL_TABLET, NO_SPECULATION),
- VULNWL_INTEL(ATOM_SALTWELL_MID, NO_SPECULATION),
- VULNWL_INTEL(ATOM_BONNELL, NO_SPECULATION),
- VULNWL_INTEL(ATOM_BONNELL_MID, NO_SPECULATION),
+ VULNWL_INTEL(ATOM_SALTWELL, NO_SPECULATION | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_SALTWELL_TABLET, NO_SPECULATION | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_SALTWELL_MID, NO_SPECULATION | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_BONNELL, NO_SPECULATION | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_BONNELL_MID, NO_SPECULATION | NO_ITLB_MULTIHIT),
- VULNWL_INTEL(ATOM_SILVERMONT, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
- VULNWL_INTEL(ATOM_SILVERMONT_X, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
- VULNWL_INTEL(ATOM_SILVERMONT_MID, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
- VULNWL_INTEL(ATOM_AIRMONT, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
- VULNWL_INTEL(XEON_PHI_KNL, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
- VULNWL_INTEL(XEON_PHI_KNM, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
+ VULNWL_INTEL(ATOM_SILVERMONT, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_SILVERMONT_X, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_SILVERMONT_MID, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_AIRMONT, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(XEON_PHI_KNL, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(XEON_PHI_KNM, NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
VULNWL_INTEL(CORE_YONAH, NO_SSB),
- VULNWL_INTEL(ATOM_AIRMONT_MID, NO_L1TF | MSBDS_ONLY | NO_SWAPGS),
+ VULNWL_INTEL(ATOM_AIRMONT_MID, NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
- VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF | NO_SWAPGS),
- VULNWL_INTEL(ATOM_GOLDMONT_X, NO_MDS | NO_L1TF | NO_SWAPGS),
- VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF | NO_SWAPGS),
+ VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_GOLDMONT_X, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT),
/*
* Technically, swapgs isn't serializing on AMD (despite it previously
@@ -999,14 +1000,16 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = {
* good enough for our purposes.
*/
+ VULNWL_INTEL(ATOM_TREMONT_X, NO_ITLB_MULTIHIT),
+
/* AMD Family 0xf - 0x12 */
- VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS),
- VULNWL_AMD(0x10, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS),
- VULNWL_AMD(0x11, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS),
- VULNWL_AMD(0x12, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS),
+ VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_AMD(0x10, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_AMD(0x11, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT),
+ VULNWL_AMD(0x12, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT),
/* FAMILY_ANY must be last, otherwise 0x0f - 0x12 matches won't work */
- VULNWL_AMD(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS | NO_SWAPGS),
+ VULNWL_AMD(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT),
{}
};
@@ -1017,19 +1020,30 @@ static bool __init cpu_matches(unsigned long which)
return m && !!(m->driver_data & which);
}
-static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
+u64 x86_read_arch_cap_msr(void)
{
u64 ia32_cap = 0;
+ if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
+ rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap);
+
+ return ia32_cap;
+}
+
+static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
+{
+ u64 ia32_cap = x86_read_arch_cap_msr();
+
+ /* Set ITLB_MULTIHIT bug if cpu is not in the whitelist and not mitigated */
+ if (!cpu_matches(NO_ITLB_MULTIHIT) && !(ia32_cap & ARCH_CAP_PSCHANGE_MC_NO))
+ setup_force_cpu_bug(X86_BUG_ITLB_MULTIHIT);
+
if (cpu_matches(NO_SPECULATION))
return;
setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
- if (cpu_has(c, X86_FEATURE_ARCH_CAPABILITIES))
- rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap);
-
if (!cpu_matches(NO_SSB) && !(ia32_cap & ARCH_CAP_SSB_NO) &&
!cpu_has(c, X86_FEATURE_AMD_SSB_NO))
setup_force_cpu_bug(X86_BUG_SPEC_STORE_BYPASS);
@@ -1046,6 +1060,21 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
if (!cpu_matches(NO_SWAPGS))
setup_force_cpu_bug(X86_BUG_SWAPGS);
+ /*
+ * When the CPU is not mitigated for TAA (TAA_NO=0) set TAA bug when:
+ * - TSX is supported or
+ * - TSX_CTRL is present
+ *
+ * TSX_CTRL check is needed for cases when TSX could be disabled before
+ * the kernel boot e.g. kexec.
+ * TSX_CTRL check alone is not sufficient for cases when the microcode
+ * update is not present or running as guest that don't get TSX_CTRL.
+ */
+ if (!(ia32_cap & ARCH_CAP_TAA_NO) &&
+ (cpu_has(c, X86_FEATURE_RTM) ||
+ (ia32_cap & ARCH_CAP_TSX_CTRL_MSR)))
+ setup_force_cpu_bug(X86_BUG_TAA);
+
if (cpu_matches(NO_MELTDOWN))
return;
@@ -1104,6 +1133,9 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
memset(&c->x86_capability, 0, sizeof c->x86_capability);
c->extended_cpuid_level = 0;
+ if (!have_cpuid_p())
+ identify_cpu_without_cpuid(c);
+
/* cyrix could have cpuid enabled via c_identify()*/
if (have_cpuid_p()) {
cpu_detect(c);
@@ -1121,7 +1153,6 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
if (this_cpu->c_bsp_init)
this_cpu->c_bsp_init(c);
} else {
- identify_cpu_without_cpuid(c);
setup_clear_cpu_cap(X86_FEATURE_CPUID);
}
@@ -1475,6 +1506,7 @@ void __init identify_boot_cpu(void)
enable_sep_cpu();
#endif
cpu_detect_tlb(&boot_cpu_data);
+ tsx_init();
}
void identify_secondary_cpu(struct cpuinfo_x86 *c)
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index 7b229af..236582c 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -45,6 +45,22 @@ struct _tlb_table {
extern const struct cpu_dev *const __x86_cpu_dev_start[],
*const __x86_cpu_dev_end[];
+#ifdef CONFIG_CPU_SUP_INTEL
+enum tsx_ctrl_states {
+ TSX_CTRL_ENABLE,
+ TSX_CTRL_DISABLE,
+ TSX_CTRL_NOT_SUPPORTED,
+};
+
+extern __ro_after_init enum tsx_ctrl_states tsx_ctrl_state;
+
+extern void __init tsx_init(void);
+extern void tsx_enable(void);
+extern void tsx_disable(void);
+#else
+static inline void tsx_init(void) { }
+#endif /* CONFIG_CPU_SUP_INTEL */
+
extern void get_cpu_cap(struct cpuinfo_x86 *c);
extern void get_cpu_address_sizes(struct cpuinfo_x86 *c);
extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c);
@@ -65,4 +81,6 @@ unsigned int aperfmperf_get_khz(int cpu);
extern void x86_spec_ctrl_setup_ap(void);
+extern u64 x86_read_arch_cap_msr(void);
+
#endif /* ARCH_X86_CPU_H */
diff --git a/arch/x86/kernel/cpu/cyrix.c b/arch/x86/kernel/cpu/cyrix.c
index fa61c87..1d9b8aa 100644
--- a/arch/x86/kernel/cpu/cyrix.c
+++ b/arch/x86/kernel/cpu/cyrix.c
@@ -437,7 +437,7 @@ static void cyrix_identify(struct cpuinfo_x86 *c)
/* enable MAPEN */
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
/* enable cpuid */
- setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x80);
+ setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80);
/* disable MAPEN */
setCx86(CX86_CCR3, ccr3);
local_irq_restore(flags);
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index fc3c07f..a5287b1 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -766,6 +766,11 @@ static void init_intel(struct cpuinfo_x86 *c)
init_intel_energy_perf(c);
init_intel_misc_features(c);
+
+ if (tsx_ctrl_state == TSX_CTRL_ENABLE)
+ tsx_enable();
+ if (tsx_ctrl_state == TSX_CTRL_DISABLE)
+ tsx_disable();
}
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/cpu/intel_rdt.c b/arch/x86/kernel/cpu/intel_rdt.c
index abb71ac..b99a04d 100644
--- a/arch/x86/kernel/cpu/intel_rdt.c
+++ b/arch/x86/kernel/cpu/intel_rdt.c
@@ -421,7 +421,7 @@ struct rdt_domain *rdt_find_domain(struct rdt_resource *r, int id,
struct list_head *l;
if (id < 0)
- return ERR_PTR(id);
+ return ERR_PTR(-ENODEV);
list_for_each(l, &r->domains) {
d = list_entry(l, struct rdt_domain, list);
@@ -610,6 +610,13 @@ static void domain_remove_cpu(int cpu, struct rdt_resource *r)
cancel_delayed_work(&d->cqm_limbo);
}
+ /*
+ * rdt_domain "d" is going to be freed below, so clear
+ * its pointer from pseudo_lock_region struct.
+ */
+ if (d->plr)
+ d->plr->d = NULL;
+
kfree(d->ctrl_val);
kfree(d->mbps_val);
kfree(d->rmid_busy_llc);
diff --git a/arch/x86/kernel/cpu/intel_rdt_ctrlmondata.c b/arch/x86/kernel/cpu/intel_rdt_ctrlmondata.c
index 627e5c8..2052e1e 100644
--- a/arch/x86/kernel/cpu/intel_rdt_ctrlmondata.c
+++ b/arch/x86/kernel/cpu/intel_rdt_ctrlmondata.c
@@ -408,8 +408,16 @@ int rdtgroup_schemata_show(struct kernfs_open_file *of,
for_each_alloc_enabled_rdt_resource(r)
seq_printf(s, "%s:uninitialized\n", r->name);
} else if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
- seq_printf(s, "%s:%d=%x\n", rdtgrp->plr->r->name,
- rdtgrp->plr->d->id, rdtgrp->plr->cbm);
+ if (!rdtgrp->plr->d) {
+ rdt_last_cmd_clear();
+ rdt_last_cmd_puts("Cache domain offline\n");
+ ret = -ENODEV;
+ } else {
+ seq_printf(s, "%s:%d=%x\n",
+ rdtgrp->plr->r->name,
+ rdtgrp->plr->d->id,
+ rdtgrp->plr->cbm);
+ }
} else {
closid = rdtgrp->closid;
for_each_alloc_enabled_rdt_resource(r) {
@@ -451,6 +459,10 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg)
int ret = 0;
rdtgrp = rdtgroup_kn_lock_live(of->kn);
+ if (!rdtgrp) {
+ ret = -ENOENT;
+ goto out;
+ }
md.priv = of->kn->priv;
resid = md.u.rid;
@@ -459,7 +471,7 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg)
r = &rdt_resources_all[resid];
d = rdt_find_domain(r, domid, NULL);
- if (!d) {
+ if (IS_ERR_OR_NULL(d)) {
ret = -ENOENT;
goto out;
}
diff --git a/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c b/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
index 912d539..a999a58 100644
--- a/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
+++ b/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
@@ -1116,6 +1116,11 @@ static int pseudo_lock_measure_cycles(struct rdtgroup *rdtgrp, int sel)
goto out;
}
+ if (!plr->d) {
+ ret = -ENODEV;
+ goto out;
+ }
+
plr->thread_done = 0;
cpu = cpumask_first(&plr->d->cpu_mask);
if (!cpu_online(cpu)) {
@@ -1429,6 +1434,11 @@ static int pseudo_lock_dev_mmap(struct file *filp, struct vm_area_struct *vma)
plr = rdtgrp->plr;
+ if (!plr->d) {
+ mutex_unlock(&rdtgroup_mutex);
+ return -ENODEV;
+ }
+
/*
* Task is required to run with affinity to the cpus associated
* with the pseudo-locked region. If this is not the case the task
diff --git a/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c b/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c
index 2013699..a2d7e66 100644
--- a/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c
+++ b/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c
@@ -268,17 +268,27 @@ static int rdtgroup_cpus_show(struct kernfs_open_file *of,
struct seq_file *s, void *v)
{
struct rdtgroup *rdtgrp;
+ struct cpumask *mask;
int ret = 0;
rdtgrp = rdtgroup_kn_lock_live(of->kn);
if (rdtgrp) {
- if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED)
- seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n",
- cpumask_pr_args(&rdtgrp->plr->d->cpu_mask));
- else
+ if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
+ if (!rdtgrp->plr->d) {
+ rdt_last_cmd_clear();
+ rdt_last_cmd_puts("Cache domain offline\n");
+ ret = -ENODEV;
+ } else {
+ mask = &rdtgrp->plr->d->cpu_mask;
+ seq_printf(s, is_cpu_list(of) ?
+ "%*pbl\n" : "%*pb\n",
+ cpumask_pr_args(mask));
+ }
+ } else {
seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n",
cpumask_pr_args(&rdtgrp->cpu_mask));
+ }
} else {
ret = -ENOENT;
}
@@ -965,7 +975,78 @@ static int rdtgroup_mode_show(struct kernfs_open_file *of,
}
/**
- * rdtgroup_cbm_overlaps - Does CBM for intended closid overlap with other
+ * rdt_cdp_peer_get - Retrieve CDP peer if it exists
+ * @r: RDT resource to which RDT domain @d belongs
+ * @d: Cache instance for which a CDP peer is requested
+ * @r_cdp: RDT resource that shares hardware with @r (RDT resource peer)
+ * Used to return the result.
+ * @d_cdp: RDT domain that shares hardware with @d (RDT domain peer)
+ * Used to return the result.
+ *
+ * RDT resources are managed independently and by extension the RDT domains
+ * (RDT resource instances) are managed independently also. The Code and
+ * Data Prioritization (CDP) RDT resources, while managed independently,
+ * could refer to the same underlying hardware. For example,
+ * RDT_RESOURCE_L2CODE and RDT_RESOURCE_L2DATA both refer to the L2 cache.
+ *
+ * When provided with an RDT resource @r and an instance of that RDT
+ * resource @d rdt_cdp_peer_get() will return if there is a peer RDT
+ * resource and the exact instance that shares the same hardware.
+ *
+ * Return: 0 if a CDP peer was found, <0 on error or if no CDP peer exists.
+ * If a CDP peer was found, @r_cdp will point to the peer RDT resource
+ * and @d_cdp will point to the peer RDT domain.
+ */
+static int rdt_cdp_peer_get(struct rdt_resource *r, struct rdt_domain *d,
+ struct rdt_resource **r_cdp,
+ struct rdt_domain **d_cdp)
+{
+ struct rdt_resource *_r_cdp = NULL;
+ struct rdt_domain *_d_cdp = NULL;
+ int ret = 0;
+
+ switch (r->rid) {
+ case RDT_RESOURCE_L3DATA:
+ _r_cdp = &rdt_resources_all[RDT_RESOURCE_L3CODE];
+ break;
+ case RDT_RESOURCE_L3CODE:
+ _r_cdp = &rdt_resources_all[RDT_RESOURCE_L3DATA];
+ break;
+ case RDT_RESOURCE_L2DATA:
+ _r_cdp = &rdt_resources_all[RDT_RESOURCE_L2CODE];
+ break;
+ case RDT_RESOURCE_L2CODE:
+ _r_cdp = &rdt_resources_all[RDT_RESOURCE_L2DATA];
+ break;
+ default:
+ ret = -ENOENT;
+ goto out;
+ }
+
+ /*
+ * When a new CPU comes online and CDP is enabled then the new
+ * RDT domains (if any) associated with both CDP RDT resources
+ * are added in the same CPU online routine while the
+ * rdtgroup_mutex is held. It should thus not happen for one
+ * RDT domain to exist and be associated with its RDT CDP
+ * resource but there is no RDT domain associated with the
+ * peer RDT CDP resource. Hence the WARN.
+ */
+ _d_cdp = rdt_find_domain(_r_cdp, d->id, NULL);
+ if (WARN_ON(IS_ERR_OR_NULL(_d_cdp))) {
+ _r_cdp = NULL;
+ ret = -EINVAL;
+ }
+
+out:
+ *r_cdp = _r_cdp;
+ *d_cdp = _d_cdp;
+
+ return ret;
+}
+
+/**
+ * __rdtgroup_cbm_overlaps - Does CBM for intended closid overlap with other
* @r: Resource to which domain instance @d belongs.
* @d: The domain instance for which @closid is being tested.
* @cbm: Capacity bitmask being tested.
@@ -984,8 +1065,8 @@ static int rdtgroup_mode_show(struct kernfs_open_file *of,
*
* Return: false if CBM does not overlap, true if it does.
*/
-bool rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_domain *d,
- unsigned long cbm, int closid, bool exclusive)
+static bool __rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_domain *d,
+ unsigned long cbm, int closid, bool exclusive)
{
enum rdtgrp_mode mode;
unsigned long ctrl_b;
@@ -1021,6 +1102,41 @@ bool rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_domain *d,
}
/**
+ * rdtgroup_cbm_overlaps - Does CBM overlap with other use of hardware
+ * @r: Resource to which domain instance @d belongs.
+ * @d: The domain instance for which @closid is being tested.
+ * @cbm: Capacity bitmask being tested.
+ * @closid: Intended closid for @cbm.
+ * @exclusive: Only check if overlaps with exclusive resource groups
+ *
+ * Resources that can be allocated using a CBM can use the CBM to control
+ * the overlap of these allocations. rdtgroup_cmb_overlaps() is the test
+ * for overlap. Overlap test is not limited to the specific resource for
+ * which the CBM is intended though - when dealing with CDP resources that
+ * share the underlying hardware the overlap check should be performed on
+ * the CDP resource sharing the hardware also.
+ *
+ * Refer to description of __rdtgroup_cbm_overlaps() for the details of the
+ * overlap test.
+ *
+ * Return: true if CBM overlap detected, false if there is no overlap
+ */
+bool rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_domain *d,
+ unsigned long cbm, int closid, bool exclusive)
+{
+ struct rdt_resource *r_cdp;
+ struct rdt_domain *d_cdp;
+
+ if (__rdtgroup_cbm_overlaps(r, d, cbm, closid, exclusive))
+ return true;
+
+ if (rdt_cdp_peer_get(r, d, &r_cdp, &d_cdp) < 0)
+ return false;
+
+ return __rdtgroup_cbm_overlaps(r_cdp, d_cdp, cbm, closid, exclusive);
+}
+
+/**
* rdtgroup_mode_test_exclusive - Test if this resource group can be exclusive
*
* An exclusive resource group implies that there should be no sharing of
@@ -1180,6 +1296,7 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
struct rdt_resource *r;
struct rdt_domain *d;
unsigned int size;
+ int ret = 0;
bool sep;
u32 ctrl;
@@ -1190,11 +1307,18 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
}
if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
- seq_printf(s, "%*s:", max_name_width, rdtgrp->plr->r->name);
- size = rdtgroup_cbm_to_size(rdtgrp->plr->r,
- rdtgrp->plr->d,
- rdtgrp->plr->cbm);
- seq_printf(s, "%d=%u\n", rdtgrp->plr->d->id, size);
+ if (!rdtgrp->plr->d) {
+ rdt_last_cmd_clear();
+ rdt_last_cmd_puts("Cache domain offline\n");
+ ret = -ENODEV;
+ } else {
+ seq_printf(s, "%*s:", max_name_width,
+ rdtgrp->plr->r->name);
+ size = rdtgroup_cbm_to_size(rdtgrp->plr->r,
+ rdtgrp->plr->d,
+ rdtgrp->plr->cbm);
+ seq_printf(s, "%d=%u\n", rdtgrp->plr->d->id, size);
+ }
goto out;
}
@@ -1224,7 +1348,7 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
out:
rdtgroup_kn_unlock(of->kn);
- return 0;
+ return ret;
}
/* rdtgroup information files for one cache resource. */
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index ff1c00b..1ceccc4 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -106,6 +106,9 @@ static void setup_inj_struct(struct mce *m)
memset(m, 0, sizeof(struct mce));
m->cpuvendor = boot_cpu_data.x86_vendor;
+ m->time = ktime_get_real_seconds();
+ m->cpuid = cpuid_eax(1);
+ m->microcode = boot_cpu_data.microcode;
}
/* Update fake mce registers on current CPU. */
@@ -580,6 +583,9 @@ static int inj_bank_set(void *data, u64 val)
m->bank = val;
do_inject();
+ /* Reset injection struct */
+ setup_inj_struct(&i_mce);
+
return 0;
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index fee118b..1f69b12 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -812,8 +812,8 @@ static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp,
if (quirk_no_way_out)
quirk_no_way_out(i, m, regs);
+ m->bank = i;
if (mce_severity(m, mca_cfg.tolerant, &tmp, true) >= MCE_PANIC_SEVERITY) {
- m->bank = i;
mce_read_aux(m, i);
*msg = tmp;
return 1;
@@ -1631,36 +1631,6 @@ static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
if (c->x86 == 0x15 && c->x86_model <= 0xf)
mce_flags.overflow_recov = 1;
- /*
- * Turn off MC4_MISC thresholding banks on those models since
- * they're not supported there.
- */
- if (c->x86 == 0x15 &&
- (c->x86_model >= 0x10 && c->x86_model <= 0x1f)) {
- int i;
- u64 hwcr;
- bool need_toggle;
- u32 msrs[] = {
- 0x00000413, /* MC4_MISC0 */
- 0xc0000408, /* MC4_MISC1 */
- };
-
- rdmsrl(MSR_K7_HWCR, hwcr);
-
- /* McStatusWrEn has to be set */
- need_toggle = !(hwcr & BIT(18));
-
- if (need_toggle)
- wrmsrl(MSR_K7_HWCR, hwcr | BIT(18));
-
- /* Clear CntP bit safely */
- for (i = 0; i < ARRAY_SIZE(msrs); i++)
- msr_clear_bit(msrs[i], 62);
-
- /* restore old settings */
- if (need_toggle)
- wrmsrl(MSR_K7_HWCR, hwcr);
- }
}
if (c->x86_vendor == X86_VENDOR_INTEL) {
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 9f915a8..da0b696 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -228,10 +228,10 @@ static void smca_configure(unsigned int bank, unsigned int cpu)
}
/* Return early if this bank was already initialized. */
- if (smca_banks[bank].hwid)
+ if (smca_banks[bank].hwid && smca_banks[bank].hwid->hwid_mcatype != 0)
return;
- if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_IPID(bank), &low, &high)) {
+ if (rdmsr_safe(MSR_AMD64_SMCA_MCx_IPID(bank), &low, &high)) {
pr_warn("Failed to read MCA_IPID for bank %d\n", bank);
return;
}
@@ -545,6 +545,40 @@ prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
return offset;
}
+/*
+ * Turn off MC4_MISC thresholding banks on all family 0x15 models since
+ * they're not supported there.
+ */
+void disable_err_thresholding(struct cpuinfo_x86 *c)
+{
+ int i;
+ u64 hwcr;
+ bool need_toggle;
+ u32 msrs[] = {
+ 0x00000413, /* MC4_MISC0 */
+ 0xc0000408, /* MC4_MISC1 */
+ };
+
+ if (c->x86 != 0x15)
+ return;
+
+ rdmsrl(MSR_K7_HWCR, hwcr);
+
+ /* McStatusWrEn has to be set */
+ need_toggle = !(hwcr & BIT(18));
+
+ if (need_toggle)
+ wrmsrl(MSR_K7_HWCR, hwcr | BIT(18));
+
+ /* Clear CntP bit safely */
+ for (i = 0; i < ARRAY_SIZE(msrs); i++)
+ msr_clear_bit(msrs[i], 62);
+
+ /* restore old settings */
+ if (need_toggle)
+ wrmsrl(MSR_K7_HWCR, hwcr);
+}
+
/* cpu init entry point, called from mce.c with preempt off */
void mce_amd_feature_init(struct cpuinfo_x86 *c)
{
@@ -552,6 +586,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
unsigned int bank, block, cpu = smp_processor_id();
int offset = -1;
+ disable_err_thresholding(c);
+
for (bank = 0; bank < mca_cfg.banks; ++bank) {
if (mce_flags.smca)
smca_configure(bank, cpu);
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index ee229ce..ec6a07b 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -185,7 +185,7 @@ static void therm_throt_process(bool new_event, int event, int level)
/* if we just entered the thermal event */
if (new_event) {
if (event == THERMAL_THROTTLING_EVENT)
- pr_crit("CPU%d: %s temperature above threshold, cpu clock throttled (total events = %lu)\n",
+ pr_warn("CPU%d: %s temperature above threshold, cpu clock throttled (total events = %lu)\n",
this_cpu,
level == CORE_LEVEL ? "Core" : "Package",
state->count);
diff --git a/arch/x86/kernel/cpu/tsx.c b/arch/x86/kernel/cpu/tsx.c
new file mode 100644
index 0000000..3e20d32
--- /dev/null
+++ b/arch/x86/kernel/cpu/tsx.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Transactional Synchronization Extensions (TSX) control.
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Author:
+ * Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+ */
+
+#include <linux/cpufeature.h>
+
+#include <asm/cmdline.h>
+
+#include "cpu.h"
+
+enum tsx_ctrl_states tsx_ctrl_state __ro_after_init = TSX_CTRL_NOT_SUPPORTED;
+
+void tsx_disable(void)
+{
+ u64 tsx;
+
+ rdmsrl(MSR_IA32_TSX_CTRL, tsx);
+
+ /* Force all transactions to immediately abort */
+ tsx |= TSX_CTRL_RTM_DISABLE;
+
+ /*
+ * Ensure TSX support is not enumerated in CPUID.
+ * This is visible to userspace and will ensure they
+ * do not waste resources trying TSX transactions that
+ * will always abort.
+ */
+ tsx |= TSX_CTRL_CPUID_CLEAR;
+
+ wrmsrl(MSR_IA32_TSX_CTRL, tsx);
+}
+
+void tsx_enable(void)
+{
+ u64 tsx;
+
+ rdmsrl(MSR_IA32_TSX_CTRL, tsx);
+
+ /* Enable the RTM feature in the cpu */
+ tsx &= ~TSX_CTRL_RTM_DISABLE;
+
+ /*
+ * Ensure TSX support is enumerated in CPUID.
+ * This is visible to userspace and will ensure they
+ * can enumerate and use the TSX feature.
+ */
+ tsx &= ~TSX_CTRL_CPUID_CLEAR;
+
+ wrmsrl(MSR_IA32_TSX_CTRL, tsx);
+}
+
+static bool __init tsx_ctrl_is_supported(void)
+{
+ u64 ia32_cap = x86_read_arch_cap_msr();
+
+ /*
+ * TSX is controlled via MSR_IA32_TSX_CTRL. However, support for this
+ * MSR is enumerated by ARCH_CAP_TSX_MSR bit in MSR_IA32_ARCH_CAPABILITIES.
+ *
+ * TSX control (aka MSR_IA32_TSX_CTRL) is only available after a
+ * microcode update on CPUs that have their MSR_IA32_ARCH_CAPABILITIES
+ * bit MDS_NO=1. CPUs with MDS_NO=0 are not planned to get
+ * MSR_IA32_TSX_CTRL support even after a microcode update. Thus,
+ * tsx= cmdline requests will do nothing on CPUs without
+ * MSR_IA32_TSX_CTRL support.
+ */
+ return !!(ia32_cap & ARCH_CAP_TSX_CTRL_MSR);
+}
+
+static enum tsx_ctrl_states x86_get_tsx_auto_mode(void)
+{
+ if (boot_cpu_has_bug(X86_BUG_TAA))
+ return TSX_CTRL_DISABLE;
+
+ return TSX_CTRL_ENABLE;
+}
+
+void __init tsx_init(void)
+{
+ char arg[5] = {};
+ int ret;
+
+ if (!tsx_ctrl_is_supported())
+ return;
+
+ ret = cmdline_find_option(boot_command_line, "tsx", arg, sizeof(arg));
+ if (ret >= 0) {
+ if (!strcmp(arg, "on")) {
+ tsx_ctrl_state = TSX_CTRL_ENABLE;
+ } else if (!strcmp(arg, "off")) {
+ tsx_ctrl_state = TSX_CTRL_DISABLE;
+ } else if (!strcmp(arg, "auto")) {
+ tsx_ctrl_state = x86_get_tsx_auto_mode();
+ } else {
+ tsx_ctrl_state = TSX_CTRL_DISABLE;
+ pr_err("tsx: invalid option, defaulting to off\n");
+ }
+ } else {
+ /* tsx= not provided */
+ if (IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_AUTO))
+ tsx_ctrl_state = x86_get_tsx_auto_mode();
+ else if (IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_OFF))
+ tsx_ctrl_state = TSX_CTRL_DISABLE;
+ else
+ tsx_ctrl_state = TSX_CTRL_ENABLE;
+ }
+
+ if (tsx_ctrl_state == TSX_CTRL_DISABLE) {
+ tsx_disable();
+
+ /*
+ * tsx_disable() will change the state of the
+ * RTM CPUID bit. Clear it here since it is now
+ * expected to be not set.
+ */
+ setup_clear_cpu_cap(X86_FEATURE_RTM);
+ } else if (tsx_ctrl_state == TSX_CTRL_ENABLE) {
+
+ /*
+ * HW defaults TSX to be enabled at bootup.
+ * We may still need the TSX enable support
+ * during init for special cases like
+ * kexec after TSX is disabled.
+ */
+ tsx_enable();
+
+ /*
+ * tsx_enable() will change the state of the
+ * RTM CPUID bit. Force it here since it is now
+ * expected to be set.
+ */
+ setup_force_cpu_cap(X86_FEATURE_RTM);
+ }
+}
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index e83a057..173e915 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -1140,6 +1140,12 @@ bool arch_within_kprobe_blacklist(unsigned long addr)
is_in_entry_trampoline_section;
}
+int __init arch_populate_kprobe_blacklist(void)
+{
+ return kprobe_add_area_blacklist((unsigned long)__entry_text_start,
+ (unsigned long)__entry_text_end);
+}
+
int __init arch_init_kprobes(void)
{
return 0;
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 516ec75..8d4d506 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -40,6 +40,7 @@
#include <asm/hw_breakpoint.h>
#include <asm/traps.h>
#include <asm/syscall.h>
+#include <asm/mmu_context.h>
#include "tls.h"
@@ -343,6 +344,49 @@ static int set_segment_reg(struct task_struct *task,
return 0;
}
+static unsigned long task_seg_base(struct task_struct *task,
+ unsigned short selector)
+{
+ unsigned short idx = selector >> 3;
+ unsigned long base;
+
+ if (likely((selector & SEGMENT_TI_MASK) == 0)) {
+ if (unlikely(idx >= GDT_ENTRIES))
+ return 0;
+
+ /*
+ * There are no user segments in the GDT with nonzero bases
+ * other than the TLS segments.
+ */
+ if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
+ return 0;
+
+ idx -= GDT_ENTRY_TLS_MIN;
+ base = get_desc_base(&task->thread.tls_array[idx]);
+ } else {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
+ struct ldt_struct *ldt;
+
+ /*
+ * If performance here mattered, we could protect the LDT
+ * with RCU. This is a slow path, though, so we can just
+ * take the mutex.
+ */
+ mutex_lock(&task->mm->context.lock);
+ ldt = task->mm->context.ldt;
+ if (unlikely(idx >= ldt->nr_entries))
+ base = 0;
+ else
+ base = get_desc_base(ldt->entries + idx);
+ mutex_unlock(&task->mm->context.lock);
+#else
+ base = 0;
+#endif
+ }
+
+ return base;
+}
+
#endif /* CONFIG_X86_32 */
static unsigned long get_flags(struct task_struct *task)
@@ -436,18 +480,16 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset)
#ifdef CONFIG_X86_64
case offsetof(struct user_regs_struct, fs_base): {
- /*
- * XXX: This will not behave as expected if called on
- * current or if fsindex != 0.
- */
- return task->thread.fsbase;
+ if (task->thread.fsindex == 0)
+ return task->thread.fsbase;
+ else
+ return task_seg_base(task, task->thread.fsindex);
}
case offsetof(struct user_regs_struct, gs_base): {
- /*
- * XXX: This will not behave as expected if called on
- * current or if fsindex != 0.
- */
- return task->thread.gsbase;
+ if (task->thread.gsindex == 0)
+ return task->thread.gsbase;
+ else
+ return task_seg_base(task, task->thread.gsindex);
}
#endif
}
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 9119859..420aa7d 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -1089,7 +1089,7 @@ arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs
pr_err("return address clobbered: pid=%d, %%sp=%#lx, %%ip=%#lx\n",
current->pid, regs->sp, regs->ip);
- force_sig_info(SIGSEGV, SEND_SIG_FORCED, current);
+ force_sig(SIGSEGV, current);
}
return -1;
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index b810102..48c24d0 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -420,7 +420,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
r = -E2BIG;
- if (*nent >= maxnent)
+ if (WARN_ON(*nent >= maxnent))
goto out;
do_cpuid_1_ent(entry, function, index);
@@ -501,8 +501,16 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
/* PKU is not yet implemented for shadow paging. */
if (!tdp_enabled || !boot_cpu_has(X86_FEATURE_OSPKE))
entry->ecx &= ~F(PKU);
+
entry->edx &= kvm_cpuid_7_0_edx_x86_features;
cpuid_mask(&entry->edx, CPUID_7_EDX);
+ if (boot_cpu_has(X86_FEATURE_IBPB) &&
+ boot_cpu_has(X86_FEATURE_IBRS))
+ entry->edx |= F(SPEC_CTRL);
+ if (boot_cpu_has(X86_FEATURE_STIBP))
+ entry->edx |= F(INTEL_STIBP);
+ if (boot_cpu_has(X86_FEATURE_SSBD))
+ entry->edx |= F(SPEC_CTRL_SSBD);
/*
* We emulate ARCH_CAPABILITIES in software even
* if the host doesn't support it.
@@ -721,6 +729,9 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 func,
u32 idx, int *nent, int maxnent, unsigned int type)
{
+ if (*nent >= maxnent)
+ return -E2BIG;
+
if (type == KVM_GET_EMULATED_CPUID)
return __do_cpuid_ent_emulated(entry, func, idx, nent, maxnent);
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 8894026..eddf91a 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -40,6 +40,7 @@
#include <linux/uaccess.h>
#include <linux/hash.h>
#include <linux/kern_levels.h>
+#include <linux/kthread.h>
#include <asm/page.h>
#include <asm/pat.h>
@@ -49,6 +50,30 @@
#include <asm/kvm_page_track.h>
#include "trace.h"
+extern bool itlb_multihit_kvm_mitigation;
+
+static int __read_mostly nx_huge_pages = -1;
+static uint __read_mostly nx_huge_pages_recovery_ratio = 60;
+
+static int set_nx_huge_pages(const char *val, const struct kernel_param *kp);
+static int set_nx_huge_pages_recovery_ratio(const char *val, const struct kernel_param *kp);
+
+static struct kernel_param_ops nx_huge_pages_ops = {
+ .set = set_nx_huge_pages,
+ .get = param_get_bool,
+};
+
+static struct kernel_param_ops nx_huge_pages_recovery_ratio_ops = {
+ .set = set_nx_huge_pages_recovery_ratio,
+ .get = param_get_uint,
+};
+
+module_param_cb(nx_huge_pages, &nx_huge_pages_ops, &nx_huge_pages, 0644);
+__MODULE_PARM_TYPE(nx_huge_pages, "bool");
+module_param_cb(nx_huge_pages_recovery_ratio, &nx_huge_pages_recovery_ratio_ops,
+ &nx_huge_pages_recovery_ratio, 0644);
+__MODULE_PARM_TYPE(nx_huge_pages_recovery_ratio, "uint");
+
/*
* When setting this variable to true it enables Two-Dimensional-Paging
* where the hardware walks 2 page tables:
@@ -140,9 +165,6 @@ module_param(dbg, bool, 0644);
#include <trace/events/kvm.h>
-#define CREATE_TRACE_POINTS
-#include "mmutrace.h"
-
#define SPTE_HOST_WRITEABLE (1ULL << PT_FIRST_AVAIL_BITS_SHIFT)
#define SPTE_MMU_WRITEABLE (1ULL << (PT_FIRST_AVAIL_BITS_SHIFT + 1))
@@ -261,9 +283,14 @@ static u64 __read_mostly shadow_nonpresent_or_rsvd_lower_gfn_mask;
static void mmu_spte_set(u64 *sptep, u64 spte);
+static bool is_executable_pte(u64 spte);
static union kvm_mmu_page_role
kvm_mmu_calc_root_page_role(struct kvm_vcpu *vcpu);
+#define CREATE_TRACE_POINTS
+#include "mmutrace.h"
+
+
void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask, u64 mmio_value)
{
BUG_ON((mmio_mask & mmio_value) != mmio_value);
@@ -283,6 +310,11 @@ static inline bool spte_ad_enabled(u64 spte)
return !(spte & shadow_acc_track_value);
}
+static bool is_nx_huge_page_enabled(void)
+{
+ return READ_ONCE(nx_huge_pages);
+}
+
static inline u64 spte_shadow_accessed_mask(u64 spte)
{
MMU_WARN_ON((spte & shadow_mmio_mask) == shadow_mmio_value);
@@ -1027,10 +1059,16 @@ static gfn_t kvm_mmu_page_get_gfn(struct kvm_mmu_page *sp, int index)
static void kvm_mmu_page_set_gfn(struct kvm_mmu_page *sp, int index, gfn_t gfn)
{
- if (sp->role.direct)
- BUG_ON(gfn != kvm_mmu_page_get_gfn(sp, index));
- else
+ if (!sp->role.direct) {
sp->gfns[index] = gfn;
+ return;
+ }
+
+ if (WARN_ON(gfn != kvm_mmu_page_get_gfn(sp, index)))
+ pr_err_ratelimited("gfn mismatch under direct page %llx "
+ "(expected %llx, got %llx)\n",
+ sp->gfn,
+ kvm_mmu_page_get_gfn(sp, index), gfn);
}
/*
@@ -1089,6 +1127,17 @@ static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
kvm_mmu_gfn_disallow_lpage(slot, gfn);
}
+static void account_huge_nx_page(struct kvm *kvm, struct kvm_mmu_page *sp)
+{
+ if (sp->lpage_disallowed)
+ return;
+
+ ++kvm->stat.nx_lpage_splits;
+ list_add_tail(&sp->lpage_disallowed_link,
+ &kvm->arch.lpage_disallowed_mmu_pages);
+ sp->lpage_disallowed = true;
+}
+
static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
{
struct kvm_memslots *slots;
@@ -1106,6 +1155,13 @@ static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
kvm_mmu_gfn_allow_lpage(slot, gfn);
}
+static void unaccount_huge_nx_page(struct kvm *kvm, struct kvm_mmu_page *sp)
+{
+ --kvm->stat.nx_lpage_splits;
+ sp->lpage_disallowed = false;
+ list_del(&sp->lpage_disallowed_link);
+}
+
static bool __mmu_gfn_lpage_is_disallowed(gfn_t gfn, int level,
struct kvm_memory_slot *slot)
{
@@ -2658,6 +2714,9 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp,
kvm_reload_remote_mmus(kvm);
}
+ if (sp->lpage_disallowed)
+ unaccount_huge_nx_page(kvm, sp);
+
sp->role.invalid = 1;
return ret;
}
@@ -2866,6 +2925,11 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
if (!speculative)
spte |= spte_shadow_accessed_mask(spte);
+ if (level > PT_PAGE_TABLE_LEVEL && (pte_access & ACC_EXEC_MASK) &&
+ is_nx_huge_page_enabled()) {
+ pte_access &= ~ACC_EXEC_MASK;
+ }
+
if (pte_access & ACC_EXEC_MASK)
spte |= shadow_x_mask;
else
@@ -2986,10 +3050,7 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
ret = RET_PF_EMULATE;
pgprintk("%s: setting spte %llx\n", __func__, *sptep);
- pgprintk("instantiating %s PTE (%s) at %llx (%llx) addr %p\n",
- is_large_pte(*sptep)? "2MB" : "4kB",
- *sptep & PT_WRITABLE_MASK ? "RW" : "R", gfn,
- *sptep, sptep);
+ trace_kvm_mmu_set_spte(level, gfn, sptep);
if (!was_rmapped && is_large_pte(*sptep))
++vcpu->kvm->stat.lpages;
@@ -3001,8 +3062,6 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
}
}
- kvm_release_pfn_clean(pfn);
-
return ret;
}
@@ -3037,9 +3096,11 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
if (ret <= 0)
return -1;
- for (i = 0; i < ret; i++, gfn++, start++)
+ for (i = 0; i < ret; i++, gfn++, start++) {
mmu_set_spte(vcpu, start, access, 0, sp->role.level, gfn,
page_to_pfn(pages[i]), true, true);
+ put_page(pages[i]);
+ }
return 0;
}
@@ -3087,40 +3148,71 @@ static void direct_pte_prefetch(struct kvm_vcpu *vcpu, u64 *sptep)
__direct_pte_prefetch(vcpu, sp, sptep);
}
-static int __direct_map(struct kvm_vcpu *vcpu, int write, int map_writable,
- int level, gfn_t gfn, kvm_pfn_t pfn, bool prefault)
+static void disallowed_hugepage_adjust(struct kvm_shadow_walk_iterator it,
+ gfn_t gfn, kvm_pfn_t *pfnp, int *levelp)
{
- struct kvm_shadow_walk_iterator iterator;
+ int level = *levelp;
+ u64 spte = *it.sptep;
+
+ if (it.level == level && level > PT_PAGE_TABLE_LEVEL &&
+ is_nx_huge_page_enabled() &&
+ is_shadow_present_pte(spte) &&
+ !is_large_pte(spte)) {
+ /*
+ * A small SPTE exists for this pfn, but FNAME(fetch)
+ * and __direct_map would like to create a large PTE
+ * instead: just force them to go down another level,
+ * patching back for them into pfn the next 9 bits of
+ * the address.
+ */
+ u64 page_mask = KVM_PAGES_PER_HPAGE(level) - KVM_PAGES_PER_HPAGE(level - 1);
+ *pfnp |= gfn & page_mask;
+ (*levelp)--;
+ }
+}
+
+static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, int write,
+ int map_writable, int level, kvm_pfn_t pfn,
+ bool prefault, bool lpage_disallowed)
+{
+ struct kvm_shadow_walk_iterator it;
struct kvm_mmu_page *sp;
- int emulate = 0;
- gfn_t pseudo_gfn;
+ int ret;
+ gfn_t gfn = gpa >> PAGE_SHIFT;
+ gfn_t base_gfn = gfn;
if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
- return 0;
+ return RET_PF_RETRY;
- for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
- if (iterator.level == level) {
- emulate = mmu_set_spte(vcpu, iterator.sptep, ACC_ALL,
- write, level, gfn, pfn, prefault,
- map_writable);
- direct_pte_prefetch(vcpu, iterator.sptep);
- ++vcpu->stat.pf_fixed;
+ trace_kvm_mmu_spte_requested(gpa, level, pfn);
+ for_each_shadow_entry(vcpu, gpa, it) {
+ /*
+ * We cannot overwrite existing page tables with an NX
+ * large page, as the leaf could be executable.
+ */
+ disallowed_hugepage_adjust(it, gfn, &pfn, &level);
+
+ base_gfn = gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1);
+ if (it.level == level)
break;
- }
- drop_large_spte(vcpu, iterator.sptep);
- if (!is_shadow_present_pte(*iterator.sptep)) {
- u64 base_addr = iterator.addr;
+ drop_large_spte(vcpu, it.sptep);
+ if (!is_shadow_present_pte(*it.sptep)) {
+ sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr,
+ it.level - 1, true, ACC_ALL);
- base_addr &= PT64_LVL_ADDR_MASK(iterator.level);
- pseudo_gfn = base_addr >> PAGE_SHIFT;
- sp = kvm_mmu_get_page(vcpu, pseudo_gfn, iterator.addr,
- iterator.level - 1, 1, ACC_ALL);
-
- link_shadow_page(vcpu, iterator.sptep, sp);
+ link_shadow_page(vcpu, it.sptep, sp);
+ if (lpage_disallowed)
+ account_huge_nx_page(vcpu->kvm, sp);
}
}
- return emulate;
+
+ ret = mmu_set_spte(vcpu, it.sptep, ACC_ALL,
+ write, level, base_gfn, pfn, prefault,
+ map_writable);
+ direct_pte_prefetch(vcpu, it.sptep);
+ ++vcpu->stat.pf_fixed;
+ return ret;
}
static void kvm_send_hwpoison_signal(unsigned long address, struct task_struct *tsk)
@@ -3156,11 +3248,10 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn)
}
static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
- gfn_t *gfnp, kvm_pfn_t *pfnp,
+ gfn_t gfn, kvm_pfn_t *pfnp,
int *levelp)
{
kvm_pfn_t pfn = *pfnp;
- gfn_t gfn = *gfnp;
int level = *levelp;
/*
@@ -3170,7 +3261,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
* here.
*/
if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
- level == PT_PAGE_TABLE_LEVEL &&
+ !kvm_is_zone_device_pfn(pfn) && level == PT_PAGE_TABLE_LEVEL &&
PageTransCompoundMap(pfn_to_page(pfn)) &&
!mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
unsigned long mask;
@@ -3187,8 +3278,6 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
mask = KVM_PAGES_PER_HPAGE(level) - 1;
VM_BUG_ON((gfn & mask) != (pfn & mask));
if (pfn & mask) {
- gfn &= ~mask;
- *gfnp = gfn;
kvm_release_pfn_clean(pfn);
pfn &= ~mask;
kvm_get_pfn(pfn);
@@ -3415,11 +3504,14 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code,
{
int r;
int level;
- bool force_pt_level = false;
+ bool force_pt_level;
kvm_pfn_t pfn;
unsigned long mmu_seq;
bool map_writable, write = error_code & PFERR_WRITE_MASK;
+ bool lpage_disallowed = (error_code & PFERR_FETCH_MASK) &&
+ is_nx_huge_page_enabled();
+ force_pt_level = lpage_disallowed;
level = mapping_level(vcpu, gfn, &force_pt_level);
if (likely(!force_pt_level)) {
/*
@@ -3445,22 +3537,20 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code,
if (handle_abnormal_pfn(vcpu, v, gfn, pfn, ACC_ALL, &r))
return r;
+ r = RET_PF_RETRY;
spin_lock(&vcpu->kvm->mmu_lock);
if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
goto out_unlock;
if (make_mmu_pages_available(vcpu) < 0)
goto out_unlock;
if (likely(!force_pt_level))
- transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
- r = __direct_map(vcpu, write, map_writable, level, gfn, pfn, prefault);
- spin_unlock(&vcpu->kvm->mmu_lock);
-
- return r;
-
+ transparent_hugepage_adjust(vcpu, gfn, &pfn, &level);
+ r = __direct_map(vcpu, v, write, map_writable, level, pfn,
+ prefault, false);
out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn);
- return RET_PF_RETRY;
+ return r;
}
static void mmu_free_root_page(struct kvm *kvm, hpa_t *root_hpa,
@@ -4050,6 +4140,8 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
unsigned long mmu_seq;
int write = error_code & PFERR_WRITE_MASK;
bool map_writable;
+ bool lpage_disallowed = (error_code & PFERR_FETCH_MASK) &&
+ is_nx_huge_page_enabled();
MMU_WARN_ON(!VALID_PAGE(vcpu->arch.mmu.root_hpa));
@@ -4060,8 +4152,9 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
if (r)
return r;
- force_pt_level = !check_hugepage_cache_consistency(vcpu, gfn,
- PT_DIRECTORY_LEVEL);
+ force_pt_level =
+ lpage_disallowed ||
+ !check_hugepage_cache_consistency(vcpu, gfn, PT_DIRECTORY_LEVEL);
level = mapping_level(vcpu, gfn, &force_pt_level);
if (likely(!force_pt_level)) {
if (level > PT_DIRECTORY_LEVEL &&
@@ -4082,22 +4175,20 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
if (handle_abnormal_pfn(vcpu, 0, gfn, pfn, ACC_ALL, &r))
return r;
+ r = RET_PF_RETRY;
spin_lock(&vcpu->kvm->mmu_lock);
if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
goto out_unlock;
if (make_mmu_pages_available(vcpu) < 0)
goto out_unlock;
if (likely(!force_pt_level))
- transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
- r = __direct_map(vcpu, write, map_writable, level, gfn, pfn, prefault);
- spin_unlock(&vcpu->kvm->mmu_lock);
-
- return r;
-
+ transparent_hugepage_adjust(vcpu, gfn, &pfn, &level);
+ r = __direct_map(vcpu, gpa, write, map_writable, level, pfn,
+ prefault, lpage_disallowed);
out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn);
- return RET_PF_RETRY;
+ return r;
}
static void nonpaging_init_context(struct kvm_vcpu *vcpu,
@@ -5618,9 +5709,9 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
* the guest, and the guest page table is using 4K page size
* mapping if the indirect sp has level = 1.
*/
- if (sp->role.direct &&
- !kvm_is_reserved_pfn(pfn) &&
- PageTransCompoundMap(pfn_to_page(pfn))) {
+ if (sp->role.direct && !kvm_is_reserved_pfn(pfn) &&
+ !kvm_is_zone_device_pfn(pfn) &&
+ PageTransCompoundMap(pfn_to_page(pfn))) {
drop_spte(kvm, sptep);
need_tlb_flush = 1;
goto restart;
@@ -5819,7 +5910,7 @@ mmu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
int nr_to_scan = sc->nr_to_scan;
unsigned long freed = 0;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
int idx;
@@ -5869,7 +5960,7 @@ mmu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
break;
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
return freed;
}
@@ -5891,10 +5982,60 @@ static void mmu_destroy_caches(void)
kmem_cache_destroy(mmu_page_header_cache);
}
+static bool get_nx_auto_mode(void)
+{
+ /* Return true when CPU has the bug, and mitigations are ON */
+ return boot_cpu_has_bug(X86_BUG_ITLB_MULTIHIT) && !cpu_mitigations_off();
+}
+
+static void __set_nx_huge_pages(bool val)
+{
+ nx_huge_pages = itlb_multihit_kvm_mitigation = val;
+}
+
+static int set_nx_huge_pages(const char *val, const struct kernel_param *kp)
+{
+ bool old_val = nx_huge_pages;
+ bool new_val;
+
+ /* In "auto" mode deploy workaround only if CPU has the bug. */
+ if (sysfs_streq(val, "off"))
+ new_val = 0;
+ else if (sysfs_streq(val, "force"))
+ new_val = 1;
+ else if (sysfs_streq(val, "auto"))
+ new_val = get_nx_auto_mode();
+ else if (strtobool(val, &new_val) < 0)
+ return -EINVAL;
+
+ __set_nx_huge_pages(new_val);
+
+ if (new_val != old_val) {
+ struct kvm *kvm;
+ int idx;
+
+ mutex_lock(&kvm_lock);
+
+ list_for_each_entry(kvm, &vm_list, vm_list) {
+ idx = srcu_read_lock(&kvm->srcu);
+ kvm_mmu_invalidate_zap_all_pages(kvm);
+ srcu_read_unlock(&kvm->srcu, idx);
+
+ wake_up_process(kvm->arch.nx_lpage_recovery_thread);
+ }
+ mutex_unlock(&kvm_lock);
+ }
+
+ return 0;
+}
+
int kvm_mmu_module_init(void)
{
int ret = -ENOMEM;
+ if (nx_huge_pages == -1)
+ __set_nx_huge_pages(get_nx_auto_mode());
+
kvm_mmu_reset_all_pte_masks();
pte_list_desc_cache = kmem_cache_create("pte_list_desc",
@@ -5961,3 +6102,116 @@ void kvm_mmu_module_exit(void)
unregister_shrinker(&mmu_shrinker);
mmu_audit_disable();
}
+
+static int set_nx_huge_pages_recovery_ratio(const char *val, const struct kernel_param *kp)
+{
+ unsigned int old_val;
+ int err;
+
+ old_val = nx_huge_pages_recovery_ratio;
+ err = param_set_uint(val, kp);
+ if (err)
+ return err;
+
+ if (READ_ONCE(nx_huge_pages) &&
+ !old_val && nx_huge_pages_recovery_ratio) {
+ struct kvm *kvm;
+
+ mutex_lock(&kvm_lock);
+
+ list_for_each_entry(kvm, &vm_list, vm_list)
+ wake_up_process(kvm->arch.nx_lpage_recovery_thread);
+
+ mutex_unlock(&kvm_lock);
+ }
+
+ return err;
+}
+
+static void kvm_recover_nx_lpages(struct kvm *kvm)
+{
+ int rcu_idx;
+ struct kvm_mmu_page *sp;
+ unsigned int ratio;
+ LIST_HEAD(invalid_list);
+ ulong to_zap;
+
+ rcu_idx = srcu_read_lock(&kvm->srcu);
+ spin_lock(&kvm->mmu_lock);
+
+ ratio = READ_ONCE(nx_huge_pages_recovery_ratio);
+ to_zap = ratio ? DIV_ROUND_UP(kvm->stat.nx_lpage_splits, ratio) : 0;
+ while (to_zap && !list_empty(&kvm->arch.lpage_disallowed_mmu_pages)) {
+ /*
+ * We use a separate list instead of just using active_mmu_pages
+ * because the number of lpage_disallowed pages is expected to
+ * be relatively small compared to the total.
+ */
+ sp = list_first_entry(&kvm->arch.lpage_disallowed_mmu_pages,
+ struct kvm_mmu_page,
+ lpage_disallowed_link);
+ WARN_ON_ONCE(!sp->lpage_disallowed);
+ kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list);
+ WARN_ON_ONCE(sp->lpage_disallowed);
+
+ if (!--to_zap || need_resched() || spin_needbreak(&kvm->mmu_lock)) {
+ kvm_mmu_commit_zap_page(kvm, &invalid_list);
+ if (to_zap)
+ cond_resched_lock(&kvm->mmu_lock);
+ }
+ }
+
+ spin_unlock(&kvm->mmu_lock);
+ srcu_read_unlock(&kvm->srcu, rcu_idx);
+}
+
+static long get_nx_lpage_recovery_timeout(u64 start_time)
+{
+ return READ_ONCE(nx_huge_pages) && READ_ONCE(nx_huge_pages_recovery_ratio)
+ ? start_time + 60 * HZ - get_jiffies_64()
+ : MAX_SCHEDULE_TIMEOUT;
+}
+
+static int kvm_nx_lpage_recovery_worker(struct kvm *kvm, uintptr_t data)
+{
+ u64 start_time;
+ long remaining_time;
+
+ while (true) {
+ start_time = get_jiffies_64();
+ remaining_time = get_nx_lpage_recovery_timeout(start_time);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop() && remaining_time > 0) {
+ schedule_timeout(remaining_time);
+ remaining_time = get_nx_lpage_recovery_timeout(start_time);
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ set_current_state(TASK_RUNNING);
+
+ if (kthread_should_stop())
+ return 0;
+
+ kvm_recover_nx_lpages(kvm);
+ }
+}
+
+int kvm_mmu_post_init_vm(struct kvm *kvm)
+{
+ int err;
+
+ err = kvm_vm_create_worker_thread(kvm, kvm_nx_lpage_recovery_worker, 0,
+ "kvm-nx-lpage-recovery",
+ &kvm->arch.nx_lpage_recovery_thread);
+ if (!err)
+ kthread_unpark(kvm->arch.nx_lpage_recovery_thread);
+
+ return err;
+}
+
+void kvm_mmu_pre_destroy_vm(struct kvm *kvm)
+{
+ if (kvm->arch.nx_lpage_recovery_thread)
+ kthread_stop(kvm->arch.nx_lpage_recovery_thread);
+}
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index 6589228..f7b2de7 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -216,4 +216,8 @@ void kvm_mmu_gfn_allow_lpage(struct kvm_memory_slot *slot, gfn_t gfn);
bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm,
struct kvm_memory_slot *slot, u64 gfn);
int kvm_arch_write_log_dirty(struct kvm_vcpu *vcpu);
+
+int kvm_mmu_post_init_vm(struct kvm *kvm);
+void kvm_mmu_pre_destroy_vm(struct kvm *kvm);
+
#endif
diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h
index c73bf4e..918b0d5 100644
--- a/arch/x86/kvm/mmutrace.h
+++ b/arch/x86/kvm/mmutrace.h
@@ -325,6 +325,65 @@ TRACE_EVENT(
__entry->kvm_gen == __entry->spte_gen
)
);
+
+TRACE_EVENT(
+ kvm_mmu_set_spte,
+ TP_PROTO(int level, gfn_t gfn, u64 *sptep),
+ TP_ARGS(level, gfn, sptep),
+
+ TP_STRUCT__entry(
+ __field(u64, gfn)
+ __field(u64, spte)
+ __field(u64, sptep)
+ __field(u8, level)
+ /* These depend on page entry type, so compute them now. */
+ __field(bool, r)
+ __field(bool, x)
+ __field(u8, u)
+ ),
+
+ TP_fast_assign(
+ __entry->gfn = gfn;
+ __entry->spte = *sptep;
+ __entry->sptep = virt_to_phys(sptep);
+ __entry->level = level;
+ __entry->r = shadow_present_mask || (__entry->spte & PT_PRESENT_MASK);
+ __entry->x = is_executable_pte(__entry->spte);
+ __entry->u = shadow_user_mask ? !!(__entry->spte & shadow_user_mask) : -1;
+ ),
+
+ TP_printk("gfn %llx spte %llx (%s%s%s%s) level %d at %llx",
+ __entry->gfn, __entry->spte,
+ __entry->r ? "r" : "-",
+ __entry->spte & PT_WRITABLE_MASK ? "w" : "-",
+ __entry->x ? "x" : "-",
+ __entry->u == -1 ? "" : (__entry->u ? "u" : "-"),
+ __entry->level, __entry->sptep
+ )
+);
+
+TRACE_EVENT(
+ kvm_mmu_spte_requested,
+ TP_PROTO(gpa_t addr, int level, kvm_pfn_t pfn),
+ TP_ARGS(addr, level, pfn),
+
+ TP_STRUCT__entry(
+ __field(u64, gfn)
+ __field(u64, pfn)
+ __field(u8, level)
+ ),
+
+ TP_fast_assign(
+ __entry->gfn = addr >> PAGE_SHIFT;
+ __entry->pfn = pfn | (__entry->gfn & (KVM_PAGES_PER_HPAGE(level) - 1));
+ __entry->level = level;
+ ),
+
+ TP_printk("gfn %llx pfn %llx level %d",
+ __entry->gfn, __entry->pfn, __entry->level
+ )
+);
+
#endif /* _TRACE_KVMMMU_H */
#undef TRACE_INCLUDE_PATH
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index 14ffd97..adf42dc 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -522,6 +522,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
mmu_set_spte(vcpu, spte, pte_access, 0, PT_PAGE_TABLE_LEVEL, gfn, pfn,
true, true);
+ kvm_release_pfn_clean(pfn);
return true;
}
@@ -595,12 +596,14 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
struct guest_walker *gw,
int write_fault, int hlevel,
- kvm_pfn_t pfn, bool map_writable, bool prefault)
+ kvm_pfn_t pfn, bool map_writable, bool prefault,
+ bool lpage_disallowed)
{
struct kvm_mmu_page *sp = NULL;
struct kvm_shadow_walk_iterator it;
unsigned direct_access, access = gw->pt_access;
int top_level, ret;
+ gfn_t gfn, base_gfn;
direct_access = gw->pte_access;
@@ -645,35 +648,48 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
link_shadow_page(vcpu, it.sptep, sp);
}
- for (;
- shadow_walk_okay(&it) && it.level > hlevel;
- shadow_walk_next(&it)) {
- gfn_t direct_gfn;
+ /*
+ * FNAME(page_fault) might have clobbered the bottom bits of
+ * gw->gfn, restore them from the virtual address.
+ */
+ gfn = gw->gfn | ((addr & PT_LVL_OFFSET_MASK(gw->level)) >> PAGE_SHIFT);
+ base_gfn = gfn;
+ trace_kvm_mmu_spte_requested(addr, gw->level, pfn);
+
+ for (; shadow_walk_okay(&it); shadow_walk_next(&it)) {
clear_sp_write_flooding_count(it.sptep);
+
+ /*
+ * We cannot overwrite existing page tables with an NX
+ * large page, as the leaf could be executable.
+ */
+ disallowed_hugepage_adjust(it, gfn, &pfn, &hlevel);
+
+ base_gfn = gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1);
+ if (it.level == hlevel)
+ break;
+
validate_direct_spte(vcpu, it.sptep, direct_access);
drop_large_spte(vcpu, it.sptep);
- if (is_shadow_present_pte(*it.sptep))
- continue;
-
- direct_gfn = gw->gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1);
-
- sp = kvm_mmu_get_page(vcpu, direct_gfn, addr, it.level-1,
- true, direct_access);
- link_shadow_page(vcpu, it.sptep, sp);
+ if (!is_shadow_present_pte(*it.sptep)) {
+ sp = kvm_mmu_get_page(vcpu, base_gfn, addr,
+ it.level - 1, true, direct_access);
+ link_shadow_page(vcpu, it.sptep, sp);
+ if (lpage_disallowed)
+ account_huge_nx_page(vcpu->kvm, sp);
+ }
}
- clear_sp_write_flooding_count(it.sptep);
ret = mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault,
- it.level, gw->gfn, pfn, prefault, map_writable);
+ it.level, base_gfn, pfn, prefault, map_writable);
FNAME(pte_prefetch)(vcpu, gw, it.sptep);
-
+ ++vcpu->stat.pf_fixed;
return ret;
out_gpte_changed:
- kvm_release_pfn_clean(pfn);
return RET_PF_RETRY;
}
@@ -740,9 +756,11 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
int r;
kvm_pfn_t pfn;
int level = PT_PAGE_TABLE_LEVEL;
- bool force_pt_level = false;
unsigned long mmu_seq;
bool map_writable, is_self_change_mapping;
+ bool lpage_disallowed = (error_code & PFERR_FETCH_MASK) &&
+ is_nx_huge_page_enabled();
+ bool force_pt_level = lpage_disallowed;
pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code);
@@ -821,6 +839,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
walker.pte_access &= ~ACC_EXEC_MASK;
}
+ r = RET_PF_RETRY;
spin_lock(&vcpu->kvm->mmu_lock);
if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
goto out_unlock;
@@ -829,19 +848,15 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
if (make_mmu_pages_available(vcpu) < 0)
goto out_unlock;
if (!force_pt_level)
- transparent_hugepage_adjust(vcpu, &walker.gfn, &pfn, &level);
+ transparent_hugepage_adjust(vcpu, walker.gfn, &pfn, &level);
r = FNAME(fetch)(vcpu, addr, &walker, write_fault,
- level, pfn, map_writable, prefault);
- ++vcpu->stat.pf_fixed;
+ level, pfn, map_writable, prefault, lpage_disallowed);
kvm_mmu_audit(vcpu, AUDIT_POST_PAGE_FAULT);
- spin_unlock(&vcpu->kvm->mmu_lock);
-
- return r;
out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn);
- return RET_PF_RETRY;
+ return r;
}
static gpa_t FNAME(get_level1_sp_gpa)(struct kvm_mmu_page *sp)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index ac2cc2e..7657dcd 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -736,8 +736,14 @@ static int get_npt_level(struct kvm_vcpu *vcpu)
static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
{
vcpu->arch.efer = efer;
- if (!npt_enabled && !(efer & EFER_LMA))
- efer &= ~EFER_LME;
+
+ if (!npt_enabled) {
+ /* Shadow paging assumes NX to be available. */
+ efer |= EFER_NX;
+
+ if (!(efer & EFER_LMA))
+ efer &= ~EFER_LME;
+ }
to_svm(vcpu)->vmcb->save.efer = efer | EFER_SVME;
mark_dirty(to_svm(vcpu)->vmcb, VMCB_CR);
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 6f7b3ac..fa2abed 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2079,7 +2079,7 @@ static int __find_msr_index(struct vcpu_vmx *vmx, u32 msr)
return -1;
}
-static inline void __invvpid(int ext, u16 vpid, gva_t gva)
+static inline void __invvpid(unsigned long ext, u16 vpid, gva_t gva)
{
struct {
u64 vpid : 16;
@@ -2094,7 +2094,7 @@ static inline void __invvpid(int ext, u16 vpid, gva_t gva)
BUG_ON(error);
}
-static inline void __invept(int ext, u64 eptp, gpa_t gpa)
+static inline void __invept(unsigned long ext, u64 eptp, gpa_t gpa)
{
struct {
u64 eptp, gpa;
@@ -2785,17 +2785,9 @@ static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset)
u64 guest_efer = vmx->vcpu.arch.efer;
u64 ignore_bits = 0;
- if (!enable_ept) {
- /*
- * NX is needed to handle CR0.WP=1, CR4.SMEP=1. Testing
- * host CPUID is more efficient than testing guest CPUID
- * or CR4. Host SMEP is anyway a requirement for guest SMEP.
- */
- if (boot_cpu_has(X86_FEATURE_SMEP))
- guest_efer |= EFER_NX;
- else if (!(guest_efer & EFER_NX))
- ignore_bits |= EFER_NX;
- }
+ /* Shadow paging assumes NX to be available. */
+ if (!enable_ept)
+ guest_efer |= EFER_NX;
/*
* LMA and LME handled by hardware; SCE meaningless outside long mode.
@@ -3412,9 +3404,6 @@ static void setup_msrs(struct vcpu_vmx *vmx)
index = __find_msr_index(vmx, MSR_CSTAR);
if (index >= 0)
move_msr_up(vmx, index, save_nmsrs++);
- index = __find_msr_index(vmx, MSR_TSC_AUX);
- if (index >= 0 && guest_cpuid_has(&vmx->vcpu, X86_FEATURE_RDTSCP))
- move_msr_up(vmx, index, save_nmsrs++);
/*
* MSR_STAR is only needed on long mode guests, and only
* if efer.sce is enabled.
@@ -3427,6 +3416,9 @@ static void setup_msrs(struct vcpu_vmx *vmx)
index = __find_msr_index(vmx, MSR_EFER);
if (index >= 0 && update_transition_efer(vmx, index))
move_msr_up(vmx, index, save_nmsrs++);
+ index = __find_msr_index(vmx, MSR_TSC_AUX);
+ if (index >= 0 && guest_cpuid_has(&vmx->vcpu, X86_FEATURE_RDTSCP))
+ move_msr_up(vmx, index, save_nmsrs++);
vmx->save_nmsrs = save_nmsrs;
vmx->guest_msrs_dirty = true;
@@ -5181,7 +5173,7 @@ static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
(unsigned long *)&vcpu->arch.regs_dirty))
return;
- if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
+ if (is_pae_paging(vcpu)) {
vmcs_write64(GUEST_PDPTR0, mmu->pdptrs[0]);
vmcs_write64(GUEST_PDPTR1, mmu->pdptrs[1]);
vmcs_write64(GUEST_PDPTR2, mmu->pdptrs[2]);
@@ -5193,7 +5185,7 @@ static void ept_save_pdptrs(struct kvm_vcpu *vcpu)
{
struct kvm_mmu *mmu = vcpu->arch.walk_mmu;
- if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
+ if (is_pae_paging(vcpu)) {
mmu->pdptrs[0] = vmcs_read64(GUEST_PDPTR0);
mmu->pdptrs[1] = vmcs_read64(GUEST_PDPTR1);
mmu->pdptrs[2] = vmcs_read64(GUEST_PDPTR2);
@@ -12021,8 +12013,7 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne
* If PAE paging and EPT are both on, CR3 is not used by the CPU and
* must not be dereferenced.
*/
- if (!is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu) &&
- !nested_ept) {
+ if (is_pae_paging(vcpu) && !nested_ept) {
if (!load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3)) {
*entry_failure_code = ENTRY_FAIL_PDPTE;
return 1;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 6ae8a01..353f63f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -92,8 +92,8 @@ u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA));
static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
#endif
-#define VM_STAT(x) offsetof(struct kvm, stat.x), KVM_STAT_VM
-#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
+#define VM_STAT(x, ...) offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__
+#define VCPU_STAT(x, ...) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__
#define KVM_X2APIC_API_VALID_FLAGS (KVM_X2APIC_API_USE_32BIT_IDS | \
KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK)
@@ -205,7 +205,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "mmu_cache_miss", VM_STAT(mmu_cache_miss) },
{ "mmu_unsync", VM_STAT(mmu_unsync) },
{ "remote_tlb_flush", VM_STAT(remote_tlb_flush) },
- { "largepages", VM_STAT(lpages) },
+ { "largepages", VM_STAT(lpages, .mode = 0444) },
+ { "nx_largepages_splitted", VM_STAT(nx_lpage_splits, .mode = 0444) },
{ "max_mmu_page_hash_collisions",
VM_STAT(max_mmu_page_hash_collisions) },
{ NULL }
@@ -289,13 +290,14 @@ int kvm_set_shared_msr(unsigned slot, u64 value, u64 mask)
struct kvm_shared_msrs *smsr = per_cpu_ptr(shared_msrs, cpu);
int err;
- if (((value ^ smsr->values[slot].curr) & mask) == 0)
+ value = (value & mask) | (smsr->values[slot].host & ~mask);
+ if (value == smsr->values[slot].curr)
return 0;
- smsr->values[slot].curr = value;
err = wrmsrl_safe(shared_msrs_global.msrs[slot], value);
if (err)
return 1;
+ smsr->values[slot].curr = value;
if (!smsr->registered) {
smsr->urn.on_user_return = kvm_on_user_return;
user_return_notifier_register(&smsr->urn);
@@ -633,7 +635,7 @@ bool pdptrs_changed(struct kvm_vcpu *vcpu)
gfn_t gfn;
int r;
- if (is_long_mode(vcpu) || !is_pae(vcpu) || !is_paging(vcpu))
+ if (!is_pae_paging(vcpu))
return false;
if (!test_bit(VCPU_EXREG_PDPTR,
@@ -884,8 +886,8 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
if (is_long_mode(vcpu) &&
(cr3 & rsvd_bits(cpuid_maxphyaddr(vcpu), 63)))
return 1;
- else if (is_pae(vcpu) && is_paging(vcpu) &&
- !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3))
+ else if (is_pae_paging(vcpu) &&
+ !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3))
return 1;
kvm_mmu_new_cr3(vcpu, cr3, skip_tlb_flush);
@@ -1131,6 +1133,14 @@ u64 kvm_get_arch_capabilities(void)
rdmsrl_safe(MSR_IA32_ARCH_CAPABILITIES, &data);
/*
+ * If nx_huge_pages is enabled, KVM's shadow paging will ensure that
+ * the nested hypervisor runs with NX huge pages. If it is not,
+ * L1 is anyway vulnerable to ITLB_MULTIHIT explots from other
+ * L1 guests, so it need not worry about its own (L2) guests.
+ */
+ data |= ARCH_CAP_PSCHANGE_MC_NO;
+
+ /*
* If we're doing cache flushes (either "always" or "cond")
* we will do one whenever the guest does a vmlaunch/vmresume.
* If an outer hypervisor is doing the cache flush for us
@@ -1142,8 +1152,40 @@ u64 kvm_get_arch_capabilities(void)
if (l1tf_vmx_mitigation != VMENTER_L1D_FLUSH_NEVER)
data |= ARCH_CAP_SKIP_VMENTRY_L1DFLUSH;
+ if (!boot_cpu_has_bug(X86_BUG_CPU_MELTDOWN))
+ data |= ARCH_CAP_RDCL_NO;
+ if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS))
+ data |= ARCH_CAP_SSB_NO;
+ if (!boot_cpu_has_bug(X86_BUG_MDS))
+ data |= ARCH_CAP_MDS_NO;
+
+ /*
+ * On TAA affected systems, export MDS_NO=0 when:
+ * - TSX is enabled on the host, i.e. X86_FEATURE_RTM=1.
+ * - Updated microcode is present. This is detected by
+ * the presence of ARCH_CAP_TSX_CTRL_MSR and ensures
+ * that VERW clears CPU buffers.
+ *
+ * When MDS_NO=0 is exported, guests deploy clear CPU buffer
+ * mitigation and don't complain:
+ *
+ * "Vulnerable: Clear CPU buffers attempted, no microcode"
+ *
+ * If TSX is disabled on the system, guests are also mitigated against
+ * TAA and clear CPU buffer mitigation is not required for guests.
+ */
+ if (!boot_cpu_has(X86_FEATURE_RTM))
+ data &= ~ARCH_CAP_TAA_NO;
+ else if (!boot_cpu_has_bug(X86_BUG_TAA))
+ data |= ARCH_CAP_TAA_NO;
+ else if (data & ARCH_CAP_TSX_CTRL_MSR)
+ data &= ~ARCH_CAP_MDS_NO;
+
+ /* KVM does not emulate MSR_IA32_TSX_CTRL. */
+ data &= ~ARCH_CAP_TSX_CTRL_MSR;
return data;
}
+
EXPORT_SYMBOL_GPL(kvm_get_arch_capabilities);
static int kvm_get_msr_feature(struct kvm_msr_entry *msr)
@@ -4075,6 +4117,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
case KVM_SET_NESTED_STATE: {
struct kvm_nested_state __user *user_kvm_nested_state = argp;
struct kvm_nested_state kvm_state;
+ int idx;
r = -EINVAL;
if (!kvm_x86_ops->set_nested_state)
@@ -4096,7 +4139,9 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
if (kvm_state.flags == KVM_STATE_NESTED_RUN_PENDING)
break;
+ idx = srcu_read_lock(&vcpu->kvm->srcu);
r = kvm_x86_ops->set_nested_state(vcpu, user_kvm_nested_state, &kvm_state);
+ srcu_read_unlock(&vcpu->kvm->srcu, idx);
break;
}
default:
@@ -6502,7 +6547,7 @@ static void kvm_hyperv_tsc_notifier(void)
struct kvm_vcpu *vcpu;
int cpu;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list)
kvm_make_mclock_inprogress_request(kvm);
@@ -6528,7 +6573,7 @@ static void kvm_hyperv_tsc_notifier(void)
spin_unlock(&ka->pvclock_gtod_sync_lock);
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
}
#endif
@@ -6586,17 +6631,17 @@ static int kvmclock_cpufreq_notifier(struct notifier_block *nb, unsigned long va
smp_call_function_single(freq->cpu, tsc_khz_changed, freq, 1);
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
kvm_for_each_vcpu(i, vcpu, kvm) {
if (vcpu->cpu != freq->cpu)
continue;
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
- if (vcpu->cpu != smp_processor_id())
+ if (vcpu->cpu != raw_smp_processor_id())
send_ipi = 1;
}
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
if (freq->old < freq->new && send_ipi) {
/*
@@ -6722,12 +6767,12 @@ static void pvclock_gtod_update_fn(struct work_struct *work)
struct kvm_vcpu *vcpu;
int i;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list)
kvm_for_each_vcpu(i, vcpu, kvm)
kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);
atomic_set(&kvm_guest_has_master_clock, 0);
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
}
static DECLARE_WORK(pvclock_gtod_work, pvclock_gtod_update_fn);
@@ -8312,7 +8357,7 @@ static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
kvm_update_cpuid(vcpu);
idx = srcu_read_lock(&vcpu->kvm->srcu);
- if (!is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu)) {
+ if (is_pae_paging(vcpu)) {
load_pdptrs(vcpu, vcpu->arch.walk_mmu, kvm_read_cr3(vcpu));
mmu_reset_needed = 1;
}
@@ -8949,6 +8994,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
INIT_HLIST_HEAD(&kvm->arch.mask_notifier_list);
INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages);
+ INIT_LIST_HEAD(&kvm->arch.lpage_disallowed_mmu_pages);
INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
atomic_set(&kvm->arch.noncoherent_dma_count, 0);
@@ -8980,6 +9026,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
return 0;
}
+int kvm_arch_post_init_vm(struct kvm *kvm)
+{
+ return kvm_mmu_post_init_vm(kvm);
+}
+
static void kvm_unload_vcpu_mmu(struct kvm_vcpu *vcpu)
{
vcpu_load(vcpu);
@@ -9081,6 +9132,11 @@ int x86_set_memory_region(struct kvm *kvm, int id, gpa_t gpa, u32 size)
}
EXPORT_SYMBOL_GPL(x86_set_memory_region);
+void kvm_arch_pre_destroy_vm(struct kvm *kvm)
+{
+ kvm_mmu_pre_destroy_vm(kvm);
+}
+
void kvm_arch_destroy_vm(struct kvm *kvm)
{
if (current->mm == kvm->mm) {
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 3a91ea7..608e5f8c 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -139,6 +139,11 @@ static inline int is_paging(struct kvm_vcpu *vcpu)
return likely(kvm_read_cr0_bits(vcpu, X86_CR0_PG));
}
+static inline bool is_pae_paging(struct kvm_vcpu *vcpu)
+{
+ return !is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu);
+}
+
static inline u32 bit(int bitno)
{
return 1 << (bitno & 31);
diff --git a/arch/x86/lib/x86-opcode-map.txt b/arch/x86/lib/x86-opcode-map.txt
index e0b8593..0a0e911 100644
--- a/arch/x86/lib/x86-opcode-map.txt
+++ b/arch/x86/lib/x86-opcode-map.txt
@@ -333,7 +333,7 @@
06: CLTS
07: SYSRET (o64)
08: INVD
-09: WBINVD
+09: WBINVD | WBNOINVD (F3)
0a:
0b: UD2 (1B)
0c:
@@ -364,7 +364,7 @@
# a ModR/M byte.
1a: BNDCL Gv,Ev (F3) | BNDCU Gv,Ev (F2) | BNDMOV Gv,Ev (66) | BNDLDX Gv,Ev
1b: BNDCN Gv,Ev (F2) | BNDMOV Ev,Gv (66) | BNDMK Gv,Ev (F3) | BNDSTX Ev,Gv
-1c:
+1c: Grp20 (1A),(1C)
1d:
1e:
1f: NOP Ev
@@ -792,6 +792,8 @@
f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v)
f6: ADCX Gy,Ey (66) | ADOX Gy,Ey (F3) | MULX By,Gy,rDX,Ey (F2),(v)
f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v)
+f8: MOVDIR64B Gv,Mdqq (66) | ENQCMD Gv,Mdqq (F2) | ENQCMDS Gv,Mdqq (F3)
+f9: MOVDIRI My,Gy
EndTable
Table: 3-byte opcode 2 (0x0f 0x3a)
@@ -943,9 +945,9 @@
EndTable
GrpTable: Grp7
-0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B)
-1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001),(11B) | CLAC (010),(11B) | STAC (011),(11B)
-2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B)
+0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B) | PCONFIG (101),(11B) | ENCLV (000),(11B)
+1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001),(11B) | CLAC (010),(11B) | STAC (011),(11B) | ENCLS (111),(11B)
+2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B) | ENCLU (111),(11B)
3: LIDT Ms
4: SMSW Mw/Rv
5: rdpkru (110),(11B) | wrpkru (111),(11B)
@@ -1020,7 +1022,7 @@
3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
4: XSAVE | ptwrite Ey (F3),(11B)
5: XRSTOR | lfence (11B)
-6: XSAVEOPT | clwb (66) | mfence (11B)
+6: XSAVEOPT | clwb (66) | mfence (11B) | TPAUSE Rd (66),(11B) | UMONITOR Rv (F3),(11B) | UMWAIT Rd (F2),(11B)
7: clflush | clflushopt (66) | sfence (11B)
EndTable
@@ -1051,6 +1053,10 @@
6: vscatterpf1qps/d Wx (66),(ev)
EndTable
+GrpTable: Grp20
+0: cldemote Mb
+EndTable
+
# AMD's Prefetch Group
GrpTable: GrpP
0: PREFETCH
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index c05a818..abcb8d0 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -19,7 +19,9 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <asm/e820/types.h>
#include <asm/pgtable.h>
/*
@@ -238,6 +240,29 @@ static unsigned long normalize_addr(unsigned long u)
return (signed long)(u << shift) >> shift;
}
+static void note_wx(struct pg_state *st)
+{
+ unsigned long npages;
+
+ npages = (st->current_address - st->start_address) / PAGE_SIZE;
+
+#ifdef CONFIG_PCI_BIOS
+ /*
+ * If PCI BIOS is enabled, the PCI BIOS area is forced to WX.
+ * Inform about it, but avoid the warning.
+ */
+ if (pcibios_enabled && st->start_address >= PAGE_OFFSET + BIOS_BEGIN &&
+ st->current_address <= PAGE_OFFSET + BIOS_END) {
+ pr_warn_once("x86/mm: PCI BIOS W+X mapping %lu pages\n", npages);
+ return;
+ }
+#endif
+ /* Account the WX pages */
+ st->wx_pages += npages;
+ WARN_ONCE(1, "x86/mm: Found insecure W+X mapping at address %pS\n",
+ (void *)st->start_address);
+}
+
/*
* This function gets called on a break in a continuous series
* of PTE entries; the next one is different so we need to
@@ -273,14 +298,8 @@ static void note_page(struct seq_file *m, struct pg_state *st,
unsigned long delta;
int width = sizeof(unsigned long) * 2;
- if (st->check_wx && (eff & _PAGE_RW) && !(eff & _PAGE_NX)) {
- WARN_ONCE(1,
- "x86/mm: Found insecure W+X mapping at address %p/%pS\n",
- (void *)st->start_address,
- (void *)st->start_address);
- st->wx_pages += (st->current_address -
- st->start_address) / PAGE_SIZE;
- }
+ if (st->check_wx && (eff & _PAGE_RW) && !(eff & _PAGE_NX))
+ note_wx(st);
/*
* Now print the actual finished series
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 1bcb724..72e6fa1 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -281,7 +281,7 @@ void vmalloc_sync_all(void)
return;
for (address = VMALLOC_START & PMD_MASK;
- address >= TASK_SIZE_MAX && address < FIXADDR_TOP;
+ address >= TASK_SIZE_MAX && address < VMALLOC_END;
address += PMD_SIZE) {
struct page *page;
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 619101a..bf52106 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -660,8 +660,8 @@ void __native_set_fixmap(enum fixed_addresses idx, pte_t pte)
fixmaps_set++;
}
-void native_set_fixmap(unsigned /* enum fixed_addresses */ idx, phys_addr_t phys,
- pgprot_t flags)
+void native_set_fixmap(unsigned /* enum fixed_addresses */ idx,
+ phys_addr_t phys, pgprot_t flags)
{
/* Sanitize 'prot' against any unsupported bits: */
pgprot_val(flags) &= __default_kernel_pte_mask;
diff --git a/arch/x86/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c
index 8f6cc71..24d573b 100644
--- a/arch/x86/net/bpf_jit_comp32.c
+++ b/arch/x86/net/bpf_jit_comp32.c
@@ -117,6 +117,8 @@ static bool is_simm32(s64 value)
#define IA32_JLE 0x7E
#define IA32_JG 0x7F
+#define COND_JMP_OPCODE_INVALID (0xFF)
+
/*
* Map eBPF registers to IA32 32bit registers or stack scratch space.
*
@@ -698,19 +700,12 @@ static inline void emit_ia32_neg64(const u8 dst[], bool dstk, u8 **pprog)
STACK_VAR(dst_hi));
}
- /* xor ecx,ecx */
- EMIT2(0x31, add_2reg(0xC0, IA32_ECX, IA32_ECX));
- /* sub dreg_lo,ecx */
- EMIT2(0x2B, add_2reg(0xC0, dreg_lo, IA32_ECX));
- /* mov dreg_lo,ecx */
- EMIT2(0x89, add_2reg(0xC0, dreg_lo, IA32_ECX));
-
- /* xor ecx,ecx */
- EMIT2(0x31, add_2reg(0xC0, IA32_ECX, IA32_ECX));
- /* sbb dreg_hi,ecx */
- EMIT2(0x19, add_2reg(0xC0, dreg_hi, IA32_ECX));
- /* mov dreg_hi,ecx */
- EMIT2(0x89, add_2reg(0xC0, dreg_hi, IA32_ECX));
+ /* neg dreg_lo */
+ EMIT2(0xF7, add_1reg(0xD8, dreg_lo));
+ /* adc dreg_hi,0x0 */
+ EMIT3(0x83, add_1reg(0xD0, dreg_hi), 0x00);
+ /* neg dreg_hi */
+ EMIT2(0xF7, add_1reg(0xD8, dreg_hi));
if (dstk) {
/* mov dword ptr [ebp+off],dreg_lo */
@@ -729,9 +724,6 @@ static inline void emit_ia32_lsh_r64(const u8 dst[], const u8 src[],
{
u8 *prog = *pprog;
int cnt = 0;
- static int jmp_label1 = -1;
- static int jmp_label2 = -1;
- static int jmp_label3 = -1;
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
@@ -750,79 +742,23 @@ static inline void emit_ia32_lsh_r64(const u8 dst[], const u8 src[],
/* mov ecx,src_lo */
EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
+ /* shld dreg_hi,dreg_lo,cl */
+ EMIT3(0x0F, 0xA5, add_2reg(0xC0, dreg_hi, dreg_lo));
+ /* shl dreg_lo,cl */
+ EMIT2(0xD3, add_1reg(0xE0, dreg_lo));
+
+ /* if ecx >= 32, mov dreg_lo into dreg_hi and clear dreg_lo */
+
/* cmp ecx,32 */
EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
- /* Jumps when >= 32 */
- if (is_imm8(jmp_label(jmp_label1, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
+ /* skip the next two instructions (4 bytes) when < 32 */
+ EMIT2(IA32_JB, 4);
- /* < 32 */
- /* shl dreg_hi,cl */
- EMIT2(0xD3, add_1reg(0xE0, dreg_hi));
- /* mov ebx,dreg_lo */
- EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX));
- /* shl dreg_lo,cl */
- EMIT2(0xD3, add_1reg(0xE0, dreg_lo));
-
- /* IA32_ECX = -IA32_ECX + 32 */
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shr ebx,cl */
- EMIT2(0xD3, add_1reg(0xE8, IA32_EBX));
- /* or dreg_hi,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX));
-
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 32 */
- if (jmp_label1 == -1)
- jmp_label1 = cnt;
-
- /* cmp ecx,64 */
- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64);
- /* Jumps when >= 64 */
- if (is_imm8(jmp_label(jmp_label2, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
-
- /* >= 32 && < 64 */
- /* sub ecx,32 */
- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
- /* shl dreg_lo,cl */
- EMIT2(0xD3, add_1reg(0xE0, dreg_lo));
/* mov dreg_hi,dreg_lo */
EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo));
-
/* xor dreg_lo,dreg_lo */
EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 64 */
- if (jmp_label2 == -1)
- jmp_label2 = cnt;
- /* xor dreg_lo,dreg_lo */
- EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
- /* xor dreg_hi,dreg_hi */
- EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
-
- if (jmp_label3 == -1)
- jmp_label3 = cnt;
-
if (dstk) {
/* mov dword ptr [ebp+off],dreg_lo */
EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo),
@@ -841,9 +777,6 @@ static inline void emit_ia32_arsh_r64(const u8 dst[], const u8 src[],
{
u8 *prog = *pprog;
int cnt = 0;
- static int jmp_label1 = -1;
- static int jmp_label2 = -1;
- static int jmp_label3 = -1;
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
@@ -862,79 +795,23 @@ static inline void emit_ia32_arsh_r64(const u8 dst[], const u8 src[],
/* mov ecx,src_lo */
EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
+ /* shrd dreg_lo,dreg_hi,cl */
+ EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi));
+ /* sar dreg_hi,cl */
+ EMIT2(0xD3, add_1reg(0xF8, dreg_hi));
+
+ /* if ecx >= 32, mov dreg_hi to dreg_lo and set/clear dreg_hi depending on sign */
+
/* cmp ecx,32 */
EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
- /* Jumps when >= 32 */
- if (is_imm8(jmp_label(jmp_label1, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
+ /* skip the next two instructions (5 bytes) when < 32 */
+ EMIT2(IA32_JB, 5);
- /* < 32 */
- /* lshr dreg_lo,cl */
- EMIT2(0xD3, add_1reg(0xE8, dreg_lo));
- /* mov ebx,dreg_hi */
- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
- /* ashr dreg_hi,cl */
- EMIT2(0xD3, add_1reg(0xF8, dreg_hi));
-
- /* IA32_ECX = -IA32_ECX + 32 */
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shl ebx,cl */
- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
- /* or dreg_lo,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
-
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 32 */
- if (jmp_label1 == -1)
- jmp_label1 = cnt;
-
- /* cmp ecx,64 */
- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64);
- /* Jumps when >= 64 */
- if (is_imm8(jmp_label(jmp_label2, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
-
- /* >= 32 && < 64 */
- /* sub ecx,32 */
- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
- /* ashr dreg_hi,cl */
- EMIT2(0xD3, add_1reg(0xF8, dreg_hi));
/* mov dreg_lo,dreg_hi */
EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
-
- /* ashr dreg_hi,imm8 */
+ /* sar dreg_hi,31 */
EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31);
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 64 */
- if (jmp_label2 == -1)
- jmp_label2 = cnt;
- /* ashr dreg_hi,imm8 */
- EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31);
- /* mov dreg_lo,dreg_hi */
- EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
-
- if (jmp_label3 == -1)
- jmp_label3 = cnt;
-
if (dstk) {
/* mov dword ptr [ebp+off],dreg_lo */
EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo),
@@ -953,9 +830,6 @@ static inline void emit_ia32_rsh_r64(const u8 dst[], const u8 src[], bool dstk,
{
u8 *prog = *pprog;
int cnt = 0;
- static int jmp_label1 = -1;
- static int jmp_label2 = -1;
- static int jmp_label3 = -1;
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
@@ -974,77 +848,23 @@ static inline void emit_ia32_rsh_r64(const u8 dst[], const u8 src[], bool dstk,
/* mov ecx,src_lo */
EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
+ /* shrd dreg_lo,dreg_hi,cl */
+ EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi));
+ /* shr dreg_hi,cl */
+ EMIT2(0xD3, add_1reg(0xE8, dreg_hi));
+
+ /* if ecx >= 32, mov dreg_hi to dreg_lo and clear dreg_hi */
+
/* cmp ecx,32 */
EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
- /* Jumps when >= 32 */
- if (is_imm8(jmp_label(jmp_label1, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
+ /* skip the next two instructions (4 bytes) when < 32 */
+ EMIT2(IA32_JB, 4);
- /* < 32 */
- /* lshr dreg_lo,cl */
- EMIT2(0xD3, add_1reg(0xE8, dreg_lo));
- /* mov ebx,dreg_hi */
- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
- /* shr dreg_hi,cl */
- EMIT2(0xD3, add_1reg(0xE8, dreg_hi));
-
- /* IA32_ECX = -IA32_ECX + 32 */
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shl ebx,cl */
- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
- /* or dreg_lo,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
-
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 32 */
- if (jmp_label1 == -1)
- jmp_label1 = cnt;
- /* cmp ecx,64 */
- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64);
- /* Jumps when >= 64 */
- if (is_imm8(jmp_label(jmp_label2, 2)))
- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
- else
- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
-
- /* >= 32 && < 64 */
- /* sub ecx,32 */
- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
- /* shr dreg_hi,cl */
- EMIT2(0xD3, add_1reg(0xE8, dreg_hi));
/* mov dreg_lo,dreg_hi */
EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
/* xor dreg_hi,dreg_hi */
EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
- /* goto out; */
- if (is_imm8(jmp_label(jmp_label3, 2)))
- EMIT2(0xEB, jmp_label(jmp_label3, 2));
- else
- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
-
- /* >= 64 */
- if (jmp_label2 == -1)
- jmp_label2 = cnt;
- /* xor dreg_lo,dreg_lo */
- EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
- /* xor dreg_hi,dreg_hi */
- EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
-
- if (jmp_label3 == -1)
- jmp_label3 = cnt;
-
if (dstk) {
/* mov dword ptr [ebp+off],dreg_lo */
EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo),
@@ -1074,27 +894,10 @@ static inline void emit_ia32_lsh_i64(const u8 dst[], const u32 val,
}
/* Do LSH operation */
if (val < 32) {
- /* shl dreg_hi,imm8 */
- EMIT3(0xC1, add_1reg(0xE0, dreg_hi), val);
- /* mov ebx,dreg_lo */
- EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX));
+ /* shld dreg_hi,dreg_lo,imm8 */
+ EMIT4(0x0F, 0xA4, add_2reg(0xC0, dreg_hi, dreg_lo), val);
/* shl dreg_lo,imm8 */
EMIT3(0xC1, add_1reg(0xE0, dreg_lo), val);
-
- /* IA32_ECX = 32 - val */
- /* mov ecx,val */
- EMIT2(0xB1, val);
- /* movzx ecx,ecx */
- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX));
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shr ebx,cl */
- EMIT2(0xD3, add_1reg(0xE8, IA32_EBX));
- /* or dreg_hi,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX));
} else if (val >= 32 && val < 64) {
u32 value = val - 32;
@@ -1140,27 +943,10 @@ static inline void emit_ia32_rsh_i64(const u8 dst[], const u32 val,
/* Do RSH operation */
if (val < 32) {
- /* shr dreg_lo,imm8 */
- EMIT3(0xC1, add_1reg(0xE8, dreg_lo), val);
- /* mov ebx,dreg_hi */
- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
+ /* shrd dreg_lo,dreg_hi,imm8 */
+ EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val);
/* shr dreg_hi,imm8 */
EMIT3(0xC1, add_1reg(0xE8, dreg_hi), val);
-
- /* IA32_ECX = 32 - val */
- /* mov ecx,val */
- EMIT2(0xB1, val);
- /* movzx ecx,ecx */
- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX));
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shl ebx,cl */
- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
- /* or dreg_lo,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
} else if (val >= 32 && val < 64) {
u32 value = val - 32;
@@ -1205,27 +991,10 @@ static inline void emit_ia32_arsh_i64(const u8 dst[], const u32 val,
}
/* Do RSH operation */
if (val < 32) {
- /* shr dreg_lo,imm8 */
- EMIT3(0xC1, add_1reg(0xE8, dreg_lo), val);
- /* mov ebx,dreg_hi */
- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
+ /* shrd dreg_lo,dreg_hi,imm8 */
+ EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val);
/* ashr dreg_hi,imm8 */
EMIT3(0xC1, add_1reg(0xF8, dreg_hi), val);
-
- /* IA32_ECX = 32 - val */
- /* mov ecx,val */
- EMIT2(0xB1, val);
- /* movzx ecx,ecx */
- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX));
- /* neg ecx */
- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
- /* add ecx,32 */
- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
-
- /* shl ebx,cl */
- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
- /* or dreg_lo,ebx */
- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
} else if (val >= 32 && val < 64) {
u32 value = val - 32;
@@ -1613,6 +1382,75 @@ static inline void emit_push_r64(const u8 src[], u8 **pprog)
*pprog = prog;
}
+static u8 get_cond_jmp_opcode(const u8 op, bool is_cmp_lo)
+{
+ u8 jmp_cond;
+
+ /* Convert BPF opcode to x86 */
+ switch (op) {
+ case BPF_JEQ:
+ jmp_cond = IA32_JE;
+ break;
+ case BPF_JSET:
+ case BPF_JNE:
+ jmp_cond = IA32_JNE;
+ break;
+ case BPF_JGT:
+ /* GT is unsigned '>', JA in x86 */
+ jmp_cond = IA32_JA;
+ break;
+ case BPF_JLT:
+ /* LT is unsigned '<', JB in x86 */
+ jmp_cond = IA32_JB;
+ break;
+ case BPF_JGE:
+ /* GE is unsigned '>=', JAE in x86 */
+ jmp_cond = IA32_JAE;
+ break;
+ case BPF_JLE:
+ /* LE is unsigned '<=', JBE in x86 */
+ jmp_cond = IA32_JBE;
+ break;
+ case BPF_JSGT:
+ if (!is_cmp_lo)
+ /* Signed '>', GT in x86 */
+ jmp_cond = IA32_JG;
+ else
+ /* GT is unsigned '>', JA in x86 */
+ jmp_cond = IA32_JA;
+ break;
+ case BPF_JSLT:
+ if (!is_cmp_lo)
+ /* Signed '<', LT in x86 */
+ jmp_cond = IA32_JL;
+ else
+ /* LT is unsigned '<', JB in x86 */
+ jmp_cond = IA32_JB;
+ break;
+ case BPF_JSGE:
+ if (!is_cmp_lo)
+ /* Signed '>=', GE in x86 */
+ jmp_cond = IA32_JGE;
+ else
+ /* GE is unsigned '>=', JAE in x86 */
+ jmp_cond = IA32_JAE;
+ break;
+ case BPF_JSLE:
+ if (!is_cmp_lo)
+ /* Signed '<=', LE in x86 */
+ jmp_cond = IA32_JLE;
+ else
+ /* LE is unsigned '<=', JBE in x86 */
+ jmp_cond = IA32_JBE;
+ break;
+ default: /* to silence GCC warning */
+ jmp_cond = COND_JMP_OPCODE_INVALID;
+ break;
+ }
+
+ return jmp_cond;
+}
+
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
int oldproglen, struct jit_context *ctx)
{
@@ -2068,11 +1906,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
case BPF_JMP | BPF_JGT | BPF_X:
case BPF_JMP | BPF_JLT | BPF_X:
case BPF_JMP | BPF_JGE | BPF_X:
- case BPF_JMP | BPF_JLE | BPF_X:
- case BPF_JMP | BPF_JSGT | BPF_X:
- case BPF_JMP | BPF_JSLE | BPF_X:
- case BPF_JMP | BPF_JSLT | BPF_X:
- case BPF_JMP | BPF_JSGE | BPF_X: {
+ case BPF_JMP | BPF_JLE | BPF_X: {
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
u8 sreg_lo = sstk ? IA32_ECX : src_lo;
@@ -2099,6 +1933,40 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo));
goto emit_cond_jmp;
}
+ case BPF_JMP | BPF_JSGT | BPF_X:
+ case BPF_JMP | BPF_JSLE | BPF_X:
+ case BPF_JMP | BPF_JSLT | BPF_X:
+ case BPF_JMP | BPF_JSGE | BPF_X: {
+ u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
+ u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
+ u8 sreg_lo = sstk ? IA32_ECX : src_lo;
+ u8 sreg_hi = sstk ? IA32_EBX : src_hi;
+
+ if (dstk) {
+ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX),
+ STACK_VAR(dst_lo));
+ EMIT3(0x8B,
+ add_2reg(0x40, IA32_EBP,
+ IA32_EDX),
+ STACK_VAR(dst_hi));
+ }
+
+ if (sstk) {
+ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX),
+ STACK_VAR(src_lo));
+ EMIT3(0x8B,
+ add_2reg(0x40, IA32_EBP,
+ IA32_EBX),
+ STACK_VAR(src_hi));
+ }
+
+ /* cmp dreg_hi,sreg_hi */
+ EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi));
+ EMIT2(IA32_JNE, 10);
+ /* cmp dreg_lo,sreg_lo */
+ EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo));
+ goto emit_cond_jmp_signed;
+ }
case BPF_JMP | BPF_JSET | BPF_X: {
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
@@ -2159,11 +2027,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
case BPF_JMP | BPF_JGT | BPF_K:
case BPF_JMP | BPF_JLT | BPF_K:
case BPF_JMP | BPF_JGE | BPF_K:
- case BPF_JMP | BPF_JLE | BPF_K:
- case BPF_JMP | BPF_JSGT | BPF_K:
- case BPF_JMP | BPF_JSLE | BPF_K:
- case BPF_JMP | BPF_JSLT | BPF_K:
- case BPF_JMP | BPF_JSGE | BPF_K: {
+ case BPF_JMP | BPF_JLE | BPF_K: {
u32 hi;
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
@@ -2189,50 +2053,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
/* cmp dreg_lo,sreg_lo */
EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo));
-emit_cond_jmp: /* Convert BPF opcode to x86 */
- switch (BPF_OP(code)) {
- case BPF_JEQ:
- jmp_cond = IA32_JE;
- break;
- case BPF_JSET:
- case BPF_JNE:
- jmp_cond = IA32_JNE;
- break;
- case BPF_JGT:
- /* GT is unsigned '>', JA in x86 */
- jmp_cond = IA32_JA;
- break;
- case BPF_JLT:
- /* LT is unsigned '<', JB in x86 */
- jmp_cond = IA32_JB;
- break;
- case BPF_JGE:
- /* GE is unsigned '>=', JAE in x86 */
- jmp_cond = IA32_JAE;
- break;
- case BPF_JLE:
- /* LE is unsigned '<=', JBE in x86 */
- jmp_cond = IA32_JBE;
- break;
- case BPF_JSGT:
- /* Signed '>', GT in x86 */
- jmp_cond = IA32_JG;
- break;
- case BPF_JSLT:
- /* Signed '<', LT in x86 */
- jmp_cond = IA32_JL;
- break;
- case BPF_JSGE:
- /* Signed '>=', GE in x86 */
- jmp_cond = IA32_JGE;
- break;
- case BPF_JSLE:
- /* Signed '<=', LE in x86 */
- jmp_cond = IA32_JLE;
- break;
- default: /* to silence GCC warning */
+emit_cond_jmp: jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false);
+ if (jmp_cond == COND_JMP_OPCODE_INVALID)
return -EFAULT;
- }
jmp_offset = addrs[i + insn->off] - addrs[i];
if (is_imm8(jmp_offset)) {
EMIT2(jmp_cond, jmp_offset);
@@ -2242,7 +2065,66 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
pr_err("cond_jmp gen bug %llx\n", jmp_offset);
return -EFAULT;
}
+ break;
+ }
+ case BPF_JMP | BPF_JSGT | BPF_K:
+ case BPF_JMP | BPF_JSLE | BPF_K:
+ case BPF_JMP | BPF_JSLT | BPF_K:
+ case BPF_JMP | BPF_JSGE | BPF_K: {
+ u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
+ u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
+ u8 sreg_lo = IA32_ECX;
+ u8 sreg_hi = IA32_EBX;
+ u32 hi;
+ if (dstk) {
+ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX),
+ STACK_VAR(dst_lo));
+ EMIT3(0x8B,
+ add_2reg(0x40, IA32_EBP,
+ IA32_EDX),
+ STACK_VAR(dst_hi));
+ }
+
+ /* mov ecx,imm32 */
+ EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), imm32);
+ hi = imm32 & (1 << 31) ? (u32)~0 : 0;
+ /* mov ebx,imm32 */
+ EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EBX), hi);
+ /* cmp dreg_hi,sreg_hi */
+ EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi));
+ EMIT2(IA32_JNE, 10);
+ /* cmp dreg_lo,sreg_lo */
+ EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo));
+
+ /*
+ * For simplicity of branch offset computation,
+ * let's use fixed jump coding here.
+ */
+emit_cond_jmp_signed: /* Check the condition for low 32-bit comparison */
+ jmp_cond = get_cond_jmp_opcode(BPF_OP(code), true);
+ if (jmp_cond == COND_JMP_OPCODE_INVALID)
+ return -EFAULT;
+ jmp_offset = addrs[i + insn->off] - addrs[i] + 8;
+ if (is_simm32(jmp_offset)) {
+ EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
+ } else {
+ pr_err("cond_jmp gen bug %llx\n", jmp_offset);
+ return -EFAULT;
+ }
+ EMIT2(0xEB, 6);
+
+ /* Check the condition for high 32-bit comparison */
+ jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false);
+ if (jmp_cond == COND_JMP_OPCODE_INVALID)
+ return -EFAULT;
+ jmp_offset = addrs[i + insn->off] - addrs[i];
+ if (is_simm32(jmp_offset)) {
+ EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
+ } else {
+ pr_err("cond_jmp gen bug %llx\n", jmp_offset);
+ return -EFAULT;
+ }
break;
}
case BPF_JMP | BPF_JA:
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
index bd372e8..e723559 100644
--- a/arch/x86/pci/fixup.c
+++ b/arch/x86/pci/fixup.c
@@ -589,6 +589,17 @@ static void pci_fixup_amd_ehci_pme(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x7808, pci_fixup_amd_ehci_pme);
/*
+ * Device [1022:7914]
+ * When in D0, PME# doesn't get asserted when plugging USB 2.0 device.
+ */
+static void pci_fixup_amd_fch_xhci_pme(struct pci_dev *dev)
+{
+ dev_info(&dev->dev, "PME# does not work under D0, disabling it\n");
+ dev->pme_support &= ~(PCI_PM_CAP_PME_D0 >> PCI_PM_CAP_PME_SHIFT);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x7914, pci_fixup_amd_fch_xhci_pme);
+
+/*
* Apple MacBook Pro: Avoid [mem 0x7fa00000-0x7fbfffff]
*
* Using the [mem 0x7fa00000-0x7fbfffff] region, e.g., by assigning it to
@@ -629,17 +640,11 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x8c10, quirk_apple_mbp_poweroff);
static void quirk_no_aersid(struct pci_dev *pdev)
{
/* VMD Domain */
- if (is_vmd(pdev->bus))
+ if (is_vmd(pdev->bus) && pci_is_root_bus(pdev->bus))
pdev->bus->bus_flags |= PCI_BUS_FLAGS_NO_AERSID;
}
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2030, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x334a, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x334b, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x334c, quirk_no_aersid);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x334d, quirk_no_aersid);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_PCI, 8, quirk_no_aersid);
static void quirk_intel_th_dnv(struct pci_dev *dev)
{
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 9061bab..335a62e 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -893,9 +893,6 @@ static void __init kexec_enter_virtual_mode(void)
if (efi_enabled(EFI_OLD_MEMMAP) && (__supported_pte_mask & _PAGE_NX))
runtime_code_page_mkexec();
-
- /* clean DUMMY object */
- efi_delete_dummy_variable();
#endif
}
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
index 844d31c..c9873c9 100644
--- a/arch/x86/platform/efi/quirks.c
+++ b/arch/x86/platform/efi/quirks.c
@@ -259,10 +259,6 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
return;
}
- /* No need to reserve regions that will never be freed. */
- if (md.attribute & EFI_MEMORY_RUNTIME)
- return;
-
size += addr % EFI_PAGE_SIZE;
size = round_up(size, EFI_PAGE_SIZE);
addr = round_down(addr, EFI_PAGE_SIZE);
@@ -292,6 +288,8 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
early_memunmap(new, new_size);
efi_memmap_install(new_phys, num_entries);
+ e820__range_update(addr, size, E820_TYPE_RAM, E820_TYPE_RESERVED);
+ e820__update_table(e820_table);
}
/*
diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c
index c9986041..6c3ec19 100644
--- a/arch/x86/power/hibernate_64.c
+++ b/arch/x86/power/hibernate_64.c
@@ -266,9 +266,9 @@ static int get_e820_md5(struct e820_table *table, void *buf)
return ret;
}
-static void hibernation_e820_save(void *buf)
+static int hibernation_e820_save(void *buf)
{
- get_e820_md5(e820_table_firmware, buf);
+ return get_e820_md5(e820_table_firmware, buf);
}
static bool hibernation_e820_mismatch(void *buf)
@@ -288,8 +288,9 @@ static bool hibernation_e820_mismatch(void *buf)
return memcmp(result, buf, MD5_DIGEST_SIZE) ? true : false;
}
#else
-static void hibernation_e820_save(void *buf)
+static int hibernation_e820_save(void *buf)
{
+ return 0;
}
static bool hibernation_e820_mismatch(void *buf)
@@ -334,9 +335,7 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
rdr->magic = RESTORE_MAGIC;
- hibernation_e820_save(rdr->e820_digest);
-
- return 0;
+ return hibernation_e820_save(rdr->e820_digest);
}
/**
diff --git a/arch/x86/tools/gen-insn-attr-x86.awk b/arch/x86/tools/gen-insn-attr-x86.awk
index b02a36b..a42015b 100644
--- a/arch/x86/tools/gen-insn-attr-x86.awk
+++ b/arch/x86/tools/gen-insn-attr-x86.awk
@@ -69,7 +69,7 @@
lprefix1_expr = "\\((66|!F3)\\)"
lprefix2_expr = "\\(F3\\)"
- lprefix3_expr = "\\((F2|!F3|66\\&F2)\\)"
+ lprefix3_expr = "\\((F2|!F3|66&F2)\\)"
lprefix_expr = "\\((66|F2|F3)\\)"
max_lprefix = 4
@@ -257,7 +257,7 @@
return add_flags(imm, mod)
}
-/^[0-9a-f]+\:/ {
+/^[0-9a-f]+:/ {
if (NR == 1)
next
# get index
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index c6c7c9b..2483ff3 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -266,19 +266,41 @@ void xen_reboot(int reason)
BUG();
}
+static int reboot_reason = SHUTDOWN_reboot;
+static bool xen_legacy_crash;
void xen_emergency_restart(void)
{
- xen_reboot(SHUTDOWN_reboot);
+ xen_reboot(reboot_reason);
}
static int
xen_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
{
- if (!kexec_crash_loaded())
- xen_reboot(SHUTDOWN_crash);
+ if (!kexec_crash_loaded()) {
+ if (xen_legacy_crash)
+ xen_reboot(SHUTDOWN_crash);
+
+ reboot_reason = SHUTDOWN_crash;
+
+ /*
+ * If panic_timeout==0 then we are supposed to wait forever.
+ * However, to preserve original dom0 behavior we have to drop
+ * into hypervisor. (domU behavior is controlled by its
+ * config file)
+ */
+ if (panic_timeout == 0)
+ panic_timeout = -1;
+ }
return NOTIFY_DONE;
}
+static int __init parse_xen_legacy_crash(char *arg)
+{
+ xen_legacy_crash = true;
+ return 0;
+}
+early_param("xen_legacy_crash", parse_xen_legacy_crash);
+
static struct notifier_block xen_panic_block = {
.notifier_call = xen_panic_event,
.priority = INT_MIN
diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S
index 3a6feed..a93d8a7 100644
--- a/arch/x86/xen/xen-asm_64.S
+++ b/arch/x86/xen/xen-asm_64.S
@@ -12,6 +12,7 @@
#include <asm/segment.h>
#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
+#include <asm/asm.h>
#include <xen/interface/xen.h>
@@ -24,6 +25,7 @@
pop %r11
jmp \name
END(xen_\name)
+_ASM_NOKPROBE(xen_\name)
.endm
xen_pv_trap divide_error
diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c
index 59153d0..b43f036 100644
--- a/arch/xtensa/mm/tlb.c
+++ b/arch/xtensa/mm/tlb.c
@@ -216,6 +216,8 @@ static int check_tlb_entry(unsigned w, unsigned e, bool dtlb)
unsigned tlbidx = w | (e << PAGE_SHIFT);
unsigned r0 = dtlb ?
read_dtlb_virtual(tlbidx) : read_itlb_virtual(tlbidx);
+ unsigned r1 = dtlb ?
+ read_dtlb_translation(tlbidx) : read_itlb_translation(tlbidx);
unsigned vpn = (r0 & PAGE_MASK) | (e << PAGE_SHIFT);
unsigned pte = get_pte_for_vaddr(vpn);
unsigned mm_asid = (get_rasid_register() >> 8) & ASID_MASK;
@@ -231,8 +233,6 @@ static int check_tlb_entry(unsigned w, unsigned e, bool dtlb)
}
if (tlb_asid == mm_asid) {
- unsigned r1 = dtlb ? read_dtlb_translation(tlbidx) :
- read_itlb_translation(tlbidx);
if ((pte ^ r1) & PAGE_MASK) {
pr_err("%cTLB: way: %u, entry: %u, mapping: %08x->%08x, PTE: %08x\n",
dtlb ? 'D' : 'I', w, e, r0, r1, pte);
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 64f4dcf..ef5a07b 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -3195,6 +3195,13 @@ static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd,
jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4);
}
+static bool bfq_bfqq_injectable(struct bfq_queue *bfqq)
+{
+ return BFQQ_SEEKY(bfqq) && bfqq->wr_coeff == 1 &&
+ blk_queue_nonrot(bfqq->bfqd->queue) &&
+ bfqq->bfqd->hw_tag;
+}
+
/**
* bfq_bfqq_expire - expire a queue.
* @bfqd: device owning the queue.
@@ -3304,6 +3311,8 @@ void bfq_bfqq_expire(struct bfq_data *bfqd,
if (ref == 1) /* bfqq is gone, no more actions on it */
return;
+ bfqq->injected_service = 0;
+
/* mark bfqq as waiting a request only if a bic still points to it */
if (!bfq_bfqq_busy(bfqq) &&
reason != BFQQE_BUDGET_TIMEOUT &&
@@ -3584,7 +3593,12 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq)
* whether bfqq is being weight-raised, because
* bfq_symmetric_scenario() does not take into account also
* weight-raised queues (see comments on
- * bfq_weights_tree_add()).
+ * bfq_weights_tree_add()). In particular, if bfqq is being
+ * weight-raised, it is important to idle only if there are
+ * other, non-weight-raised queues that may steal throughput
+ * to bfqq. Actually, we should be even more precise, and
+ * differentiate between interactive weight raising and
+ * soft real-time weight raising.
*
* As a side note, it is worth considering that the above
* device-idling countermeasures may however fail in the
@@ -3596,7 +3610,8 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq)
* to let requests be served in the desired order until all
* the requests already queued in the device have been served.
*/
- asymmetric_scenario = bfqq->wr_coeff > 1 ||
+ asymmetric_scenario = (bfqq->wr_coeff > 1 &&
+ bfqd->wr_busy_queues < bfqd->busy_queues) ||
!bfq_symmetric_scenario(bfqd);
/*
@@ -3642,6 +3657,30 @@ static bool bfq_bfqq_must_idle(struct bfq_queue *bfqq)
return RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_better_to_idle(bfqq);
}
+static struct bfq_queue *bfq_choose_bfqq_for_injection(struct bfq_data *bfqd)
+{
+ struct bfq_queue *bfqq;
+
+ /*
+ * A linear search; but, with a high probability, very few
+ * steps are needed to find a candidate queue, i.e., a queue
+ * with enough budget left for its next request. In fact:
+ * - BFQ dynamically updates the budget of every queue so as
+ * to accommodate the expected backlog of the queue;
+ * - if a queue gets all its requests dispatched as injected
+ * service, then the queue is removed from the active list
+ * (and re-added only if it gets new requests, but with
+ * enough budget for its new backlog).
+ */
+ list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list)
+ if (!RB_EMPTY_ROOT(&bfqq->sort_list) &&
+ bfq_serv_to_charge(bfqq->next_rq, bfqq) <=
+ bfq_bfqq_budget_left(bfqq))
+ return bfqq;
+
+ return NULL;
+}
+
/*
* Select a queue for service. If we have a current queue in service,
* check whether to continue servicing it, or retrieve and set a new one.
@@ -3723,10 +3762,19 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd)
* No requests pending. However, if the in-service queue is idling
* for a new request, or has requests waiting for a completion and
* may idle after their completion, then keep it anyway.
+ *
+ * Yet, to boost throughput, inject service from other queues if
+ * possible.
*/
if (bfq_bfqq_wait_request(bfqq) ||
(bfqq->dispatched != 0 && bfq_better_to_idle(bfqq))) {
- bfqq = NULL;
+ if (bfq_bfqq_injectable(bfqq) &&
+ bfqq->injected_service * bfqq->inject_coeff <
+ bfqq->entity.service * 10)
+ bfqq = bfq_choose_bfqq_for_injection(bfqd);
+ else
+ bfqq = NULL;
+
goto keep_queue;
}
@@ -3816,6 +3864,14 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd,
bfq_dispatch_remove(bfqd->queue, rq);
+ if (bfqq != bfqd->in_service_queue) {
+ if (likely(bfqd->in_service_queue))
+ bfqd->in_service_queue->injected_service +=
+ bfq_serv_to_charge(rq, bfqq);
+
+ goto return_rq;
+ }
+
/*
* If weight raising has to terminate for bfqq, then next
* function causes an immediate update of bfqq's weight,
@@ -3834,13 +3890,12 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd,
* belongs to CLASS_IDLE and other queues are waiting for
* service.
*/
- if (bfqd->busy_queues > 1 && bfq_class_idle(bfqq))
- goto expire;
+ if (!(bfqd->busy_queues > 1 && bfq_class_idle(bfqq)))
+ goto return_rq;
- return rq;
-
-expire:
bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED);
+
+return_rq:
return rq;
}
@@ -4242,6 +4297,13 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
bfq_mark_bfqq_has_short_ttime(bfqq);
bfq_mark_bfqq_sync(bfqq);
bfq_mark_bfqq_just_created(bfqq);
+ /*
+ * Aggressively inject a lot of service: up to 90%.
+ * This coefficient remains constant during bfqq life,
+ * but this behavior might be changed, after enough
+ * testing and tuning.
+ */
+ bfqq->inject_coeff = 1;
} else
bfq_clear_bfqq_sync(bfqq);
diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h
index d5e9e60..a41e988 100644
--- a/block/bfq-iosched.h
+++ b/block/bfq-iosched.h
@@ -351,6 +351,32 @@ struct bfq_queue {
unsigned long split_time; /* time of last split */
unsigned long first_IO_time; /* time of first I/O for this queue */
+
+ /* max service rate measured so far */
+ u32 max_service_rate;
+ /*
+ * Ratio between the service received by bfqq while it is in
+ * service, and the cumulative service (of requests of other
+ * queues) that may be injected while bfqq is empty but still
+ * in service. To increase precision, the coefficient is
+ * measured in tenths of unit. Here are some example of (1)
+ * ratios, (2) resulting percentages of service injected
+ * w.r.t. to the total service dispatched while bfqq is in
+ * service, and (3) corresponding values of the coefficient:
+ * 1 (50%) -> 10
+ * 2 (33%) -> 20
+ * 10 (9%) -> 100
+ * 9.9 (9%) -> 99
+ * 1.5 (40%) -> 15
+ * 0.5 (66%) -> 5
+ * 0.1 (90%) -> 1
+ *
+ * So, if the coefficient is lower than 10, then
+ * injected service is more than bfqq service.
+ */
+ unsigned int inject_coeff;
+ /* amount of service injected in current service slot */
+ unsigned int injected_service;
};
/**
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 5275241..a06547f 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -955,9 +955,14 @@ static int blkcg_print_stat(struct seq_file *sf, void *v)
int i;
bool has_stats = false;
+ spin_lock_irq(blkg->q->queue_lock);
+
+ if (!blkg->online)
+ goto skip;
+
dname = blkg_dev_name(blkg);
if (!dname)
- continue;
+ goto skip;
/*
* Hooray string manipulation, count is the size written NOT
@@ -967,8 +972,6 @@ static int blkcg_print_stat(struct seq_file *sf, void *v)
*/
off += scnprintf(buf+off, size-off, "%s ", dname);
- spin_lock_irq(blkg->q->queue_lock);
-
rwstat = blkg_rwstat_recursive_sum(blkg, NULL,
offsetof(struct blkcg_gq, stat_bytes));
rbytes = atomic64_read(&rwstat.aux_cnt[BLKG_RWSTAT_READ]);
@@ -981,8 +984,6 @@ static int blkcg_print_stat(struct seq_file *sf, void *v)
wios = atomic64_read(&rwstat.aux_cnt[BLKG_RWSTAT_WRITE]);
dios = atomic64_read(&rwstat.aux_cnt[BLKG_RWSTAT_DISCARD]);
- spin_unlock_irq(blkg->q->queue_lock);
-
if (rbytes || wbytes || rios || wios) {
has_stats = true;
off += scnprintf(buf+off, size-off,
@@ -1023,6 +1024,8 @@ static int blkcg_print_stat(struct seq_file *sf, void *v)
seq_commit(sf, -1);
}
}
+ skip:
+ spin_unlock_irq(blkg->q->queue_lock);
}
rcu_read_unlock();
diff --git a/block/blk-core.c b/block/blk-core.c
index 8f877ee..dca51ca 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -784,6 +784,9 @@ void blk_cleanup_queue(struct request_queue *q)
* prevent that q->request_fn() gets invoked after draining finished.
*/
blk_freeze_queue(q);
+
+ rq_qos_exit(q);
+
spin_lock_irq(lock);
queue_flag_set(QUEUE_FLAG_DEAD, q);
spin_unlock_irq(lock);
diff --git a/block/blk-map.c b/block/blk-map.c
index db9373b..9d8627a 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -145,7 +145,7 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
return 0;
unmap_rq:
- __blk_rq_unmap_user(bio);
+ blk_rq_unmap_user(bio);
fail:
rq->bio = NULL;
return ret;
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 74d3cb3..8d61477 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -673,6 +673,31 @@ static void blk_account_io_merge(struct request *req)
part_stat_unlock();
}
}
+/*
+ * Two cases of handling DISCARD merge:
+ * If max_discard_segments > 1, the driver takes every bio
+ * as a range and send them to controller together. The ranges
+ * needn't to be contiguous.
+ * Otherwise, the bios/requests will be handled as same as
+ * others which should be contiguous.
+ */
+static inline bool blk_discard_mergable(struct request *req)
+{
+ if (req_op(req) == REQ_OP_DISCARD &&
+ queue_max_discard_segments(req->q) > 1)
+ return true;
+ return false;
+}
+
+enum elv_merge blk_try_req_merge(struct request *req, struct request *next)
+{
+ if (blk_discard_mergable(req))
+ return ELEVATOR_DISCARD_MERGE;
+ else if (blk_rq_pos(req) + blk_rq_sectors(req) == blk_rq_pos(next))
+ return ELEVATOR_BACK_MERGE;
+
+ return ELEVATOR_NO_MERGE;
+}
static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt)
{
@@ -695,12 +720,6 @@ static struct request *attempt_merge(struct request_queue *q,
if (req_op(req) != req_op(next))
return NULL;
- /*
- * not contiguous
- */
- if (blk_rq_pos(req) + blk_rq_sectors(req) != blk_rq_pos(next))
- return NULL;
-
if (rq_data_dir(req) != rq_data_dir(next)
|| req->rq_disk != next->rq_disk
|| req_no_special_merge(next))
@@ -727,11 +746,19 @@ static struct request *attempt_merge(struct request_queue *q,
* counts here. Handle DISCARDs separately, as they
* have separate settings.
*/
- if (req_op(req) == REQ_OP_DISCARD) {
+
+ switch (blk_try_req_merge(req, next)) {
+ case ELEVATOR_DISCARD_MERGE:
if (!req_attempt_discard_merge(q, req, next))
return NULL;
- } else if (!ll_merge_requests_fn(q, req, next))
+ break;
+ case ELEVATOR_BACK_MERGE:
+ if (!ll_merge_requests_fn(q, req, next))
+ return NULL;
+ break;
+ default:
return NULL;
+ }
/*
* If failfast settings disagree or any of the two is already
@@ -759,7 +786,7 @@ static struct request *attempt_merge(struct request_queue *q,
req->__data_len += blk_rq_bytes(next);
- if (req_op(req) != REQ_OP_DISCARD)
+ if (!blk_discard_mergable(req))
elv_merge_requests(q, req, next);
/*
@@ -855,10 +882,9 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio)
enum elv_merge blk_try_merge(struct request *rq, struct bio *bio)
{
- if (req_op(rq) == REQ_OP_DISCARD &&
- queue_max_discard_segments(rq->q) > 1) {
+ if (blk_discard_mergable(rq))
return ELEVATOR_DISCARD_MERGE;
- } else if (blk_rq_pos(rq) + blk_rq_sectors(rq) ==
+ else if (blk_rq_pos(rq) + blk_rq_sectors(rq) ==
bio->bi_iter.bi_sector) {
if (crypto_not_mergeable(rq->bio, bio))
return ELEVATOR_NO_MERGE;
diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c
index 0b7297a..5006a0d 100644
--- a/block/blk-mq-sysfs.c
+++ b/block/blk-mq-sysfs.c
@@ -151,20 +151,25 @@ static ssize_t blk_mq_hw_sysfs_nr_reserved_tags_show(struct blk_mq_hw_ctx *hctx,
static ssize_t blk_mq_hw_sysfs_cpus_show(struct blk_mq_hw_ctx *hctx, char *page)
{
+ const size_t size = PAGE_SIZE - 1;
unsigned int i, first = 1;
- ssize_t ret = 0;
+ int ret = 0, pos = 0;
for_each_cpu(i, hctx->cpumask) {
if (first)
- ret += sprintf(ret + page, "%u", i);
+ ret = snprintf(pos + page, size - pos, "%u", i);
else
- ret += sprintf(ret + page, ", %u", i);
+ ret = snprintf(pos + page, size - pos, ", %u", i);
+
+ if (ret >= size - pos)
+ break;
first = 0;
+ pos += ret;
}
- ret += sprintf(ret + page, "\n");
- return ret;
+ ret = snprintf(pos + page, size + 1 - pos, "\n");
+ return pos + ret;
}
static struct attribute *default_ctx_attrs[] = {
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index bab47a1..8286640 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -997,8 +997,6 @@ void blk_unregister_queue(struct gendisk *disk)
kobject_del(&q->kobj);
blk_trace_remove_sysfs(disk_to_dev(disk));
- rq_qos_exit(q);
-
mutex_lock(&q->sysfs_lock);
if (q->request_fn || (q->mq_ops && q->elevator))
elv_unregister_queue(q);
diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c
index 6ca015f..6490b27 100644
--- a/block/compat_ioctl.c
+++ b/block/compat_ioctl.c
@@ -6,6 +6,7 @@
#include <linux/compat.h>
#include <linux/elevator.h>
#include <linux/hdreg.h>
+#include <linux/pr.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/types.h>
@@ -354,6 +355,8 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
* but we call blkdev_ioctl, which gets the lock for us
*/
case BLKRRPART:
+ case BLKREPORTZONE:
+ case BLKRESETZONE:
return blkdev_ioctl(bdev, mode, cmd,
(unsigned long)compat_ptr(arg));
case BLKBSZSET_32:
@@ -401,6 +404,14 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
case BLKTRACETEARDOWN: /* compatible */
ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
return ret;
+ case IOC_PR_REGISTER:
+ case IOC_PR_RESERVE:
+ case IOC_PR_RELEASE:
+ case IOC_PR_PREEMPT:
+ case IOC_PR_PREEMPT_ABORT:
+ case IOC_PR_CLEAR:
+ return blkdev_ioctl(bdev, mode, cmd,
+ (unsigned long)compat_ptr(arg));
default:
if (disk->fops->compat_ioctl)
ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index ec78a04..ed643ce 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -1058,7 +1058,7 @@ void af_alg_async_cb(struct crypto_async_request *_req, int err)
af_alg_free_resources(areq);
sock_put(sk);
- iocb->ki_complete(iocb, err ? err : resultlen, 0);
+ iocb->ki_complete(iocb, err ? err : (int)resultlen, 0);
}
EXPORT_SYMBOL_GPL(af_alg_async_cb);
diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c
index 3cca814..f847c18 100644
--- a/crypto/crypto_user.c
+++ b/crypto/crypto_user.c
@@ -288,38 +288,43 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
drop_alg:
crypto_mod_put(alg);
- if (err)
+ if (err) {
+ kfree_skb(skb);
return err;
+ }
return nlmsg_unicast(crypto_nlsk, skb, NETLINK_CB(in_skb).portid);
}
static int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct crypto_alg *alg;
+ const size_t start_pos = cb->args[0];
+ size_t pos = 0;
struct crypto_dump_info info;
- int err;
-
- if (cb->args[0])
- goto out;
-
- cb->args[0] = 1;
+ struct crypto_alg *alg;
+ int res;
info.in_skb = cb->skb;
info.out_skb = skb;
info.nlmsg_seq = cb->nlh->nlmsg_seq;
info.nlmsg_flags = NLM_F_MULTI;
+ down_read(&crypto_alg_sem);
list_for_each_entry(alg, &crypto_alg_list, cra_list) {
- err = crypto_report_alg(alg, &info);
- if (err)
- goto out_err;
+ if (pos >= start_pos) {
+ res = crypto_report_alg(alg, &info);
+ if (res == -EMSGSIZE)
+ break;
+ if (res)
+ goto out;
+ }
+ pos++;
}
-
+ cb->args[0] = pos;
+ res = skb->len;
out:
- return skb->len;
-out_err:
- return err;
+ up_read(&crypto_alg_sem);
+ return res;
}
static int crypto_dump_report_done(struct netlink_callback *cb)
@@ -503,7 +508,7 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) &&
(nlh->nlmsg_flags & NLM_F_DUMP))) {
struct crypto_alg *alg;
- u16 dump_alloc = 0;
+ unsigned long dump_alloc = 0;
if (link->dump == NULL)
return -EINVAL;
@@ -511,16 +516,16 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
down_read(&crypto_alg_sem);
list_for_each_entry(alg, &crypto_alg_list, cra_list)
dump_alloc += CRYPTO_REPORT_MAXSIZE;
+ up_read(&crypto_alg_sem);
{
struct netlink_dump_control c = {
.dump = link->dump,
.done = link->done,
- .min_dump_alloc = dump_alloc,
+ .min_dump_alloc = min(dump_alloc, 65535UL),
};
err = netlink_dump_start(crypto_nlsk, skb, nlh, &c);
}
- up_read(&crypto_alg_sem);
return err;
}
diff --git a/crypto/ecc.c b/crypto/ecc.c
index adcce31..ad73925 100644
--- a/crypto/ecc.c
+++ b/crypto/ecc.c
@@ -906,10 +906,34 @@ static void ecc_point_mult(struct ecc_point *result,
static inline void ecc_swap_digits(const u64 *in, u64 *out,
unsigned int ndigits)
{
+ const __be64 *src = (__force __be64 *)in;
int i;
for (i = 0; i < ndigits; i++)
- out[i] = __swab64(in[ndigits - 1 - i]);
+ out[i] = be64_to_cpu(src[ndigits - 1 - i]);
+}
+
+static int __ecc_is_key_valid(const struct ecc_curve *curve,
+ const u64 *private_key, unsigned int ndigits)
+{
+ u64 one[ECC_MAX_DIGITS] = { 1, };
+ u64 res[ECC_MAX_DIGITS];
+
+ if (!private_key)
+ return -EINVAL;
+
+ if (curve->g.ndigits != ndigits)
+ return -EINVAL;
+
+ /* Make sure the private key is in the range [2, n-3]. */
+ if (vli_cmp(one, private_key, ndigits) != -1)
+ return -EINVAL;
+ vli_sub(res, curve->n, one, ndigits);
+ vli_sub(res, res, one, ndigits);
+ if (vli_cmp(res, private_key, ndigits) != 1)
+ return -EINVAL;
+
+ return 0;
}
int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
@@ -918,22 +942,12 @@ int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
int nbytes;
const struct ecc_curve *curve = ecc_get_curve(curve_id);
- if (!private_key)
- return -EINVAL;
-
nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
if (private_key_len != nbytes)
return -EINVAL;
- if (vli_is_zero(private_key, ndigits))
- return -EINVAL;
-
- /* Make sure the private key is in the range [1, n-1]. */
- if (vli_cmp(curve->n, private_key, ndigits) != 1)
- return -EINVAL;
-
- return 0;
+ return __ecc_is_key_valid(curve, private_key, ndigits);
}
/*
@@ -979,11 +993,8 @@ int ecc_gen_privkey(unsigned int curve_id, unsigned int ndigits, u64 *privkey)
if (err)
return err;
- if (vli_is_zero(priv, ndigits))
- return -EINVAL;
-
- /* Make sure the private key is in the range [1, n-1]. */
- if (vli_cmp(curve->n, priv, ndigits) != 1)
+ /* Make sure the private key is in the valid range. */
+ if (__ecc_is_key_valid(curve, priv, ndigits))
return -EINVAL;
ecc_swap_digits(priv, privkey, ndigits);
diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
index 9893dbf..812476e 100644
--- a/crypto/rsa-pkcs1pad.c
+++ b/crypto/rsa-pkcs1pad.c
@@ -261,15 +261,6 @@ static int pkcs1pad_encrypt(struct akcipher_request *req)
pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf,
ctx->key_size - 1 - req->src_len, req->src);
- req_ctx->out_buf = kmalloc(ctx->key_size, GFP_KERNEL);
- if (!req_ctx->out_buf) {
- kfree(req_ctx->in_buf);
- return -ENOMEM;
- }
-
- pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
- ctx->key_size, NULL);
-
akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
pkcs1pad_encrypt_sign_complete_cb, req);
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 828e3b0..8cc4cf8 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -1400,8 +1400,8 @@ static int test_comp(struct crypto_comp *tfm,
int ilen;
unsigned int dlen = COMP_BUF_SIZE;
- memset(output, 0, sizeof(COMP_BUF_SIZE));
- memset(decomp_output, 0, sizeof(COMP_BUF_SIZE));
+ memset(output, 0, COMP_BUF_SIZE);
+ memset(decomp_output, 0, COMP_BUF_SIZE);
ilen = ctemplate[i].inlen;
ret = crypto_comp_compress(tfm, ctemplate[i].input,
@@ -1445,7 +1445,7 @@ static int test_comp(struct crypto_comp *tfm,
int ilen;
unsigned int dlen = COMP_BUF_SIZE;
- memset(decomp_output, 0, sizeof(COMP_BUF_SIZE));
+ memset(decomp_output, 0, COMP_BUF_SIZE);
ilen = dtemplate[i].inlen;
ret = crypto_comp_decompress(tfm, dtemplate[i].input,
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index c651e20..30ccd94 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mutex.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/platform_data/clk-lpss.h>
#include <linux/platform_data/x86/pmc_atom.h>
@@ -83,6 +84,7 @@ struct lpss_device_desc {
size_t prv_size_override;
struct property_entry *properties;
void (*setup)(struct lpss_private_data *pdata);
+ bool resume_from_noirq;
};
static const struct lpss_device_desc lpss_dma_desc = {
@@ -99,6 +101,9 @@ struct lpss_private_data {
u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
};
+/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */
+static u32 pmc_atom_d3_mask = 0xfe000ffe;
+
/* LPSS run time quirks */
static unsigned int lpss_quirks;
@@ -175,6 +180,21 @@ static void byt_pwm_setup(struct lpss_private_data *pdata)
static void byt_i2c_setup(struct lpss_private_data *pdata)
{
+ const char *uid_str = acpi_device_uid(pdata->adev);
+ acpi_handle handle = pdata->adev->handle;
+ unsigned long long shared_host = 0;
+ acpi_status status;
+ long uid = 0;
+
+ /* Expected to always be true, but better safe then sorry */
+ if (uid_str)
+ uid = simple_strtol(uid_str, NULL, 10);
+
+ /* Detect I2C bus shared with PUNIT and ignore its d3 status */
+ status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
+ if (ACPI_SUCCESS(status) && shared_host && uid)
+ pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1));
+
lpss_deassert_reset(pdata);
if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
@@ -274,12 +294,14 @@ static const struct lpss_device_desc byt_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_SAVE_CTX,
.prv_offset = 0x800,
.setup = byt_i2c_setup,
+ .resume_from_noirq = true,
};
static const struct lpss_device_desc bsw_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
.prv_offset = 0x800,
.setup = byt_i2c_setup,
+ .resume_from_noirq = true,
};
static const struct lpss_device_desc bsw_spi_dev_desc = {
@@ -494,12 +516,18 @@ static int match_hid_uid(struct device *dev, void *data)
static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
{
+ struct device *dev;
+
struct hid_uid data = {
.hid = hid,
.uid = uid,
};
- return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+ dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+ if (dev)
+ return dev;
+
+ return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
}
static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
@@ -637,12 +665,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
* have _PS0 and _PS3 without _PSC (and no power resources), so
* acpi_bus_init_power() will assume that the BIOS has put them into D0.
*/
- ret = acpi_device_fix_up_power(adev);
- if (ret) {
- /* Skip the device, but continue the namespace scan. */
- ret = 0;
- goto err_out;
- }
+ acpi_device_fix_up_power(adev);
adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev, dev_desc->properties);
@@ -894,7 +917,7 @@ static void lpss_iosf_enter_d3_state(void)
* Here we read the values related to LPSS power island, i.e. LPSS
* devices, excluding both LPSS DMA controllers, along with SCC domain.
*/
- u32 func_dis, d3_sts_0, pmc_status, pmc_mask = 0xfe000ffe;
+ u32 func_dis, d3_sts_0, pmc_status;
int ret;
ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
@@ -912,7 +935,7 @@ static void lpss_iosf_enter_d3_state(void)
* Shutdown both LPSS DMA controllers if and only if all other devices
* are already in D3hot.
*/
- pmc_status = (~(d3_sts_0 | func_dis)) & pmc_mask;
+ pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
if (pmc_status)
goto exit;
@@ -1006,7 +1029,7 @@ static int acpi_lpss_resume(struct device *dev)
}
#ifdef CONFIG_PM_SLEEP
-static int acpi_lpss_suspend_late(struct device *dev)
+static int acpi_lpss_do_suspend_late(struct device *dev)
{
int ret;
@@ -1017,12 +1040,62 @@ static int acpi_lpss_suspend_late(struct device *dev)
return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
}
-static int acpi_lpss_resume_early(struct device *dev)
+static int acpi_lpss_suspend_late(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_suspend_late(dev);
+}
+
+static int acpi_lpss_suspend_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ if (pdata->dev_desc->resume_from_noirq) {
+ ret = acpi_lpss_do_suspend_late(dev);
+ if (ret)
+ return ret;
+ }
+
+ return acpi_subsys_suspend_noirq(dev);
+}
+
+static int acpi_lpss_do_resume_early(struct device *dev)
{
int ret = acpi_lpss_resume(dev);
return ret ? ret : pm_generic_resume_early(dev);
}
+
+static int acpi_lpss_resume_early(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_resume_early(dev);
+}
+
+static int acpi_lpss_resume_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ ret = acpi_subsys_resume_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (!dev_pm_may_skip_resume(dev) && pdata->dev_desc->resume_from_noirq)
+ ret = acpi_lpss_do_resume_early(dev);
+
+ return ret;
+}
+
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1052,8 +1125,8 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
.complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend,
.suspend_late = acpi_lpss_suspend_late,
- .suspend_noirq = acpi_subsys_suspend_noirq,
- .resume_noirq = acpi_subsys_resume_noirq,
+ .suspend_noirq = acpi_lpss_suspend_noirq,
+ .resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late,
@@ -1061,8 +1134,8 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
.thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_lpss_suspend_late,
- .poweroff_noirq = acpi_subsys_suspend_noirq,
- .restore_noirq = acpi_subsys_resume_noirq,
+ .poweroff_noirq = acpi_lpss_suspend_noirq,
+ .restore_noirq = acpi_lpss_resume_noirq,
.restore_early = acpi_lpss_resume_early,
#endif
.runtime_suspend = acpi_lpss_runtime_suspend,
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 6b0d3ef..2ccfbb6 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -228,7 +228,7 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
if (node < 0)
node = memory_add_physaddr_to_nid(info->start_addr);
- result = add_memory(node, info->start_addr, info->length);
+ result = __add_memory(node, info->start_addr, info->length);
/*
* If the memory block has been used by the kernel, add_memory()
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 298180b..bfcc68b 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -230,6 +230,8 @@ acpi_ev_default_region_setup(acpi_handle handle,
acpi_status acpi_ev_initialize_region(union acpi_operand_object *region_obj);
+u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node);
+
/*
* evsci - SCI (System Control Interrupt) handling/dispatch
*/
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 0f28a38..99b0da8 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -395,9 +395,9 @@ struct acpi_simple_repair_info {
/* Info for running the _REG methods */
struct acpi_reg_walk_info {
- acpi_adr_space_type space_id;
u32 function;
u32 reg_run_count;
+ acpi_adr_space_type space_id;
};
/*****************************************************************************
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 70c2bd1..49decca 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -653,6 +653,19 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
ACPI_FUNCTION_TRACE(ev_execute_reg_methods);
+ /*
+ * These address spaces do not need a call to _REG, since the ACPI
+ * specification defines them as: "must always be accessible". Since
+ * they never change state (never become unavailable), no need to ever
+ * call _REG on them. Also, a data_table is not a "real" address space,
+ * so do not call _REG. September 2018.
+ */
+ if ((space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) ||
+ (space_id == ACPI_ADR_SPACE_SYSTEM_IO) ||
+ (space_id == ACPI_ADR_SPACE_DATA_TABLE)) {
+ return_VOID;
+ }
+
info.space_id = space_id;
info.function = function;
info.reg_run_count = 0;
@@ -714,8 +727,8 @@ acpi_ev_reg_run(acpi_handle obj_handle,
}
/*
- * We only care about regions.and objects that are allowed to have address
- * space handlers
+ * We only care about regions and objects that are allowed to have
+ * address space handlers
*/
if ((node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) {
return (AE_OK);
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index 39284de..17df5da 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -16,9 +16,6 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evrgnini")
-/* Local prototypes */
-static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node);
-
/*******************************************************************************
*
* FUNCTION: acpi_ev_system_memory_region_setup
@@ -33,7 +30,6 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node);
* DESCRIPTION: Setup a system_memory operation region
*
******************************************************************************/
-
acpi_status
acpi_ev_system_memory_region_setup(acpi_handle handle,
u32 function,
@@ -313,7 +309,7 @@ acpi_ev_pci_config_region_setup(acpi_handle handle,
*
******************************************************************************/
-static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node)
+u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node)
{
acpi_status status;
struct acpi_pnp_device_id *hid;
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 091415b..3b3a25d 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -193,7 +193,6 @@ acpi_remove_address_space_handler(acpi_handle device,
*/
region_obj =
handler_obj->address_space.region_list;
-
}
/* Remove this Handler object from the list */
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index f008ba7..73177b8 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -33,7 +33,6 @@
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cper.h>
-#include <linux/kdebug.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/ratelimit.h>
@@ -171,40 +170,40 @@ static int ghes_estatus_pool_init(void)
return 0;
}
-static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
+static void ghes_estatus_pool_free_chunk(struct gen_pool *pool,
struct gen_pool_chunk *chunk,
void *data)
{
- free_page(chunk->start_addr);
+ vfree((void *)chunk->start_addr);
}
static void ghes_estatus_pool_exit(void)
{
gen_pool_for_each_chunk(ghes_estatus_pool,
- ghes_estatus_pool_free_chunk_page, NULL);
+ ghes_estatus_pool_free_chunk, NULL);
gen_pool_destroy(ghes_estatus_pool);
}
static int ghes_estatus_pool_expand(unsigned long len)
{
- unsigned long i, pages, size, addr;
- int ret;
+ unsigned long size, addr;
ghes_estatus_pool_size_request += PAGE_ALIGN(len);
size = gen_pool_size(ghes_estatus_pool);
if (size >= ghes_estatus_pool_size_request)
return 0;
- pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
- for (i = 0; i < pages; i++) {
- addr = __get_free_page(GFP_KERNEL);
- if (!addr)
- return -ENOMEM;
- ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
- if (ret)
- return ret;
- }
- return 0;
+ addr = (unsigned long)vmalloc(PAGE_ALIGN(len));
+ if (!addr)
+ return -ENOMEM;
+
+ /*
+ * New allocation must be visible in all pgd before it can be found by
+ * an NMI allocating from the pool.
+ */
+ vmalloc_sync_all();
+
+ return gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1);
}
static int map_gen_v2(struct ghes *ghes)
@@ -949,7 +948,6 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
sev = ghes_severity(ghes->estatus->error_severity);
if (sev >= GHES_SEV_PANIC) {
- oops_begin();
ghes_print_queued_estatus();
__ghes_panic(ghes);
}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index d2e29a1..92a1468 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -166,7 +166,7 @@ int acpi_bus_get_private_data(acpi_handle handle, void **data)
{
acpi_status status;
- if (!*data)
+ if (!data)
return -EINVAL;
status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index a19ff39..870eb5c 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -91,6 +91,17 @@ static const struct dmi_system_id lid_blacklst[] = {
DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
},
},
+ {
+ /*
+ * Medion Akoya E2215T, notification of the LID device only
+ * happens on close, not on open and _LID always returns closed.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E2215T MD60198"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
+ },
{}
};
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 1806260..e0927c5 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -1254,9 +1254,19 @@ static void acpi_dev_pm_detach(struct device *dev, bool power_off)
*/
int acpi_dev_pm_attach(struct device *dev, bool power_on)
{
+ /*
+ * Skip devices whose ACPI companions match the device IDs below,
+ * because they require special power management handling incompatible
+ * with the generic ACPI PM domain.
+ */
+ static const struct acpi_device_id special_pm_ids[] = {
+ {"PNP0C0B", }, /* Generic ACPI fan */
+ {"INT3404", }, /* Fan */
+ {}
+ };
struct acpi_device *adev = ACPI_COMPANION(dev);
- if (!adev)
+ if (!adev || !acpi_match_device_ids(adev, special_pm_ids))
return 0;
/*
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index ed73f6f..22617036 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -374,19 +374,21 @@ void *__ref acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
}
EXPORT_SYMBOL_GPL(acpi_os_map_memory);
-static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
+/* Must be called with mutex_lock(&acpi_ioremap_lock) */
+static unsigned long acpi_os_drop_map_ref(struct acpi_ioremap *map)
{
- if (!--map->refcount)
+ unsigned long refcount = --map->refcount;
+
+ if (!refcount)
list_del_rcu(&map->list);
+ return refcount;
}
static void acpi_os_map_cleanup(struct acpi_ioremap *map)
{
- if (!map->refcount) {
- synchronize_rcu_expedited();
- acpi_unmap(map->phys, map->virt);
- kfree(map);
- }
+ synchronize_rcu_expedited();
+ acpi_unmap(map->phys, map->virt);
+ kfree(map);
}
/**
@@ -406,6 +408,7 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map)
void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
{
struct acpi_ioremap *map;
+ unsigned long refcount;
if (!acpi_permanent_mmap) {
__acpi_unmap_table(virt, size);
@@ -419,10 +422,11 @@ void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
WARN(true, PREFIX "%s: bad address %p\n", __func__, virt);
return;
}
- acpi_os_drop_map_ref(map);
+ refcount = acpi_os_drop_map_ref(map);
mutex_unlock(&acpi_ioremap_lock);
- acpi_os_map_cleanup(map);
+ if (!refcount)
+ acpi_os_map_cleanup(map);
}
EXPORT_SYMBOL_GPL(acpi_os_unmap_iomem);
@@ -457,6 +461,7 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *gas)
{
u64 addr;
struct acpi_ioremap *map;
+ unsigned long refcount;
if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
return;
@@ -472,10 +477,11 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *gas)
mutex_unlock(&acpi_ioremap_lock);
return;
}
- acpi_os_drop_map_ref(map);
+ refcount = acpi_os_drop_map_ref(map);
mutex_unlock(&acpi_ioremap_lock);
- acpi_os_map_cleanup(map);
+ if (!refcount)
+ acpi_os_map_cleanup(map);
}
EXPORT_SYMBOL(acpi_os_unmap_generic_address);
@@ -1132,6 +1138,7 @@ void acpi_os_wait_events_complete(void)
flush_workqueue(kacpid_wq);
flush_workqueue(kacpi_notify_wq);
}
+EXPORT_SYMBOL(acpi_os_wait_events_complete);
struct acpi_hp_work {
struct work_struct work;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 7433035..e465e72 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -455,8 +455,9 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
decode_osc_support(root, "OS supports", support);
status = acpi_pci_osc_support(root, support);
if (ACPI_FAILURE(status)) {
- dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n",
- acpi_format_exception(status));
+ dev_info(&device->dev, "_OSC failed (%s)%s\n",
+ acpi_format_exception(status),
+ pcie_aspm_support_enabled() ? "; disabling ASPM" : "");
*no_aspm = 1;
return;
}
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
index 7a34310..5008ead 100644
--- a/drivers/acpi/sbshc.c
+++ b/drivers/acpi/sbshc.c
@@ -196,6 +196,7 @@ int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
hc->callback = NULL;
hc->context = NULL;
mutex_unlock(&hc->lock);
+ acpi_os_wait_events_complete();
return 0;
}
@@ -292,6 +293,7 @@ static int acpi_smbus_hc_remove(struct acpi_device *device)
hc = acpi_driver_data(device);
acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
+ acpi_os_wait_events_complete();
kfree(hc);
device->driver_data = NULL;
return 0;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e1b6231..1dcc48b 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1550,6 +1550,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
*/
static const struct acpi_device_id i2c_multi_instantiate_ids[] = {
{"BSG1160", },
+ {"INT33FE", },
{}
};
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 41324f0..f8150c7 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -816,14 +816,14 @@ static ssize_t counter_set(struct kobject *kobj,
* interface:
* echo unmask > /sys/firmware/acpi/interrupts/gpe00
*/
-#define ACPI_MASKABLE_GPE_MAX 0xFF
+#define ACPI_MASKABLE_GPE_MAX 0x100
static DECLARE_BITMAP(acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) __initdata;
static int __init acpi_gpe_set_masked_gpes(char *val)
{
u8 gpe;
- if (kstrtou8(val, 0, &gpe) || gpe > ACPI_MASKABLE_GPE_MAX)
+ if (kstrtou8(val, 0, &gpe))
return -EINVAL;
set_bit(gpe, acpi_masked_gpes_map);
@@ -835,7 +835,7 @@ void __init acpi_gpe_apply_masked_gpes(void)
{
acpi_handle handle;
acpi_status status;
- u8 gpe;
+ u16 gpe;
for_each_set_bit(gpe, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) {
status = acpi_get_gpe_device(gpe, &handle);
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 71e777d..c35d949 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -871,6 +871,7 @@ static void
binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread,
struct binder_work *work)
{
+ WARN_ON(!list_empty(&thread->waiting_thread_node));
binder_enqueue_work_ilocked(work, &thread->todo);
}
@@ -888,6 +889,7 @@ static void
binder_enqueue_thread_work_ilocked(struct binder_thread *thread,
struct binder_work *work)
{
+ WARN_ON(!list_empty(&thread->waiting_thread_node));
binder_enqueue_work_ilocked(work, &thread->todo);
thread->process_todo = true;
}
@@ -1448,19 +1450,12 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong,
} else
node->local_strong_refs++;
if (!node->has_strong_ref && target_list) {
+ struct binder_thread *thread = container_of(target_list,
+ struct binder_thread, todo);
binder_dequeue_work_ilocked(&node->work);
- /*
- * Note: this function is the only place where we queue
- * directly to a thread->todo without using the
- * corresponding binder_enqueue_thread_work() helper
- * functions; in this case it's ok to not set the
- * process_todo flag, since we know this node work will
- * always be followed by other work that starts queue
- * processing: in case of synchronous transactions, a
- * BR_REPLY or BR_ERROR; in case of oneway
- * transactions, a BR_TRANSACTION_COMPLETE.
- */
- binder_enqueue_work_ilocked(&node->work, target_list);
+ BUG_ON(&thread->todo != target_list);
+ binder_enqueue_deferred_thread_work_ilocked(thread,
+ &node->work);
}
} else {
if (!internal)
@@ -2997,6 +2992,7 @@ static void binder_transaction(struct binder_proc *proc,
{
int ret;
struct binder_transaction *t;
+ struct binder_work *w;
struct binder_work *tcomplete;
binder_size_t buffer_offset = 0;
binder_size_t off_start_offset, off_end_offset;
@@ -3140,6 +3136,29 @@ static void binder_transaction(struct binder_proc *proc,
goto err_invalid_target_handle;
}
binder_inner_proc_lock(proc);
+
+ w = list_first_entry_or_null(&thread->todo,
+ struct binder_work, entry);
+ if (!(tr->flags & TF_ONE_WAY) && w &&
+ w->type == BINDER_WORK_TRANSACTION) {
+ /*
+ * Do not allow new outgoing transaction from a
+ * thread that has a transaction at the head of
+ * its todo list. Only need to check the head
+ * because binder_select_thread_ilocked picks a
+ * thread from proc->waiting_threads to enqueue
+ * the transaction, and nothing is queued to the
+ * todo list while the thread is on waiting_threads.
+ */
+ binder_user_error("%d:%d new transaction not allowed when there is a transaction on thread todo\n",
+ proc->pid, thread->pid);
+ binder_inner_proc_unlock(proc);
+ return_error = BR_FAILED_REPLY;
+ return_error_param = -EPROTO;
+ return_error_line = __LINE__;
+ goto err_bad_todo_list;
+ }
+
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
@@ -3619,6 +3638,7 @@ static void binder_transaction(struct binder_proc *proc,
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
err_alloc_t_failed:
+err_bad_todo_list:
err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 27bd921..deb3797 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -285,8 +285,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
return 0;
free_range:
- for (page_addr = end - PAGE_SIZE; page_addr >= start;
- page_addr -= PAGE_SIZE) {
+ for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) {
bool ret;
size_t index;
@@ -299,6 +298,8 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
WARN_ON(!ret);
trace_binder_free_lru_end(alloc, index);
+ if (page_addr == start)
+ break;
continue;
err_vm_insert_page_failed:
@@ -306,7 +307,8 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
page->page_ptr = NULL;
err_alloc_page_failed:
err_page_ptr_cleared:
- ;
+ if (page_addr == start)
+ break;
}
err_no_vma:
if (mm) {
@@ -848,14 +850,20 @@ void binder_alloc_print_pages(struct seq_file *m,
int free = 0;
mutex_lock(&alloc->mutex);
- for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
- page = &alloc->pages[i];
- if (!page->page_ptr)
- free++;
- else if (list_empty(&page->lru))
- active++;
- else
- lru++;
+ /*
+ * Make sure the binder_alloc is fully initialized, otherwise we might
+ * read inconsistent state.
+ */
+ if (binder_alloc_get_vma(alloc) != NULL) {
+ for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
+ page = &alloc->pages[i];
+ if (!page->page_ptr)
+ free++;
+ else if (list_empty(&page->lru))
+ active++;
+ else
+ lru++;
+ }
}
mutex_unlock(&alloc->mutex);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 39b181d..99698d7 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -121,7 +121,8 @@
config AHCI_BRCM
tristate "Broadcom AHCI SATA support"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_NSP
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_NSP || \
+ ARCH_BCM_63XX
help
This option enables support for the AHCI SATA3 controller found on
Broadcom SoC's.
diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
index f3d55777..0192cab 100644
--- a/drivers/ata/ahci_brcm.c
+++ b/drivers/ata/ahci_brcm.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/string.h>
#include "ahci.h"
@@ -84,8 +85,7 @@ enum brcm_ahci_version {
};
enum brcm_ahci_quirks {
- BRCM_AHCI_QUIRK_NO_NCQ = BIT(0),
- BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(1),
+ BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(0),
};
struct brcm_ahci_priv {
@@ -94,6 +94,7 @@ struct brcm_ahci_priv {
u32 port_mask;
u32 quirks;
enum brcm_ahci_version version;
+ struct reset_control *rcdev;
};
static inline u32 brcm_sata_readreg(void __iomem *addr)
@@ -220,19 +221,12 @@ static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv)
brcm_sata_phy_disable(priv, i);
}
-static u32 brcm_ahci_get_portmask(struct platform_device *pdev,
+static u32 brcm_ahci_get_portmask(struct ahci_host_priv *hpriv,
struct brcm_ahci_priv *priv)
{
- void __iomem *ahci;
- struct resource *res;
u32 impl;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci");
- ahci = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ahci))
- return 0;
-
- impl = readl(ahci + HOST_PORTS_IMPL);
+ impl = readl(hpriv->mmio + HOST_PORTS_IMPL);
if (fls(impl) > SATA_TOP_MAX_PHYS)
dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n",
@@ -240,9 +234,6 @@ static u32 brcm_ahci_get_portmask(struct platform_device *pdev,
else if (!impl)
dev_info(priv->dev, "no ports found\n");
- devm_iounmap(&pdev->dev, ahci);
- devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
-
return impl;
}
@@ -292,6 +283,13 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
/* Perform the SATA PHY reset sequence */
brcm_sata_phy_disable(priv, ap->port_no);
+ /* Reset the SATA clock */
+ ahci_platform_disable_clks(hpriv);
+ msleep(10);
+
+ ahci_platform_enable_clks(hpriv);
+ msleep(10);
+
/* Bring the PHY back on */
brcm_sata_phy_enable(priv, ap->port_no);
@@ -354,11 +352,10 @@ static int brcm_ahci_suspend(struct device *dev)
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct brcm_ahci_priv *priv = hpriv->plat_data;
- int ret;
- ret = ahci_platform_suspend(dev);
brcm_sata_phys_disable(priv);
- return ret;
+
+ return ahci_platform_suspend(dev);
}
static int brcm_ahci_resume(struct device *dev)
@@ -366,11 +363,44 @@ static int brcm_ahci_resume(struct device *dev)
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct brcm_ahci_priv *priv = hpriv->plat_data;
+ int ret;
+
+ /* Make sure clocks are turned on before re-configuration */
+ ret = ahci_platform_enable_clks(hpriv);
+ if (ret)
+ return ret;
brcm_sata_init(priv);
brcm_sata_phys_enable(priv);
brcm_sata_alpm_init(hpriv);
- return ahci_platform_resume(dev);
+
+ /* Since we had to enable clocks earlier on, we cannot use
+ * ahci_platform_resume() as-is since a second call to
+ * ahci_platform_enable_resources() would bump up the resources
+ * (regulators, clocks, PHYs) count artificially so we copy the part
+ * after ahci_platform_enable_resources().
+ */
+ ret = ahci_platform_enable_phys(hpriv);
+ if (ret)
+ goto out_disable_phys;
+
+ ret = ahci_platform_resume_host(dev);
+ if (ret)
+ goto out_disable_platform_phys;
+
+ /* We resumed so update PM runtime state */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+out_disable_platform_phys:
+ ahci_platform_disable_phys(hpriv);
+out_disable_phys:
+ brcm_sata_phys_disable(priv);
+ ahci_platform_disable_clks(hpriv);
+ return ret;
}
#endif
@@ -411,44 +441,76 @@ static int brcm_ahci_probe(struct platform_device *pdev)
if (IS_ERR(priv->top_ctrl))
return PTR_ERR(priv->top_ctrl);
- if ((priv->version == BRCM_SATA_BCM7425) ||
- (priv->version == BRCM_SATA_NSP)) {
- priv->quirks |= BRCM_AHCI_QUIRK_NO_NCQ;
- priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
- }
-
- brcm_sata_init(priv);
-
- priv->port_mask = brcm_ahci_get_portmask(pdev, priv);
- if (!priv->port_mask)
- return -ENODEV;
-
- brcm_sata_phys_enable(priv);
+ /* Reset is optional depending on platform */
+ priv->rcdev = devm_reset_control_get(&pdev->dev, "ahci");
+ if (!IS_ERR_OR_NULL(priv->rcdev))
+ reset_control_deassert(priv->rcdev);
hpriv = ahci_platform_get_resources(pdev, 0);
- if (IS_ERR(hpriv))
- return PTR_ERR(hpriv);
+ if (IS_ERR(hpriv)) {
+ ret = PTR_ERR(hpriv);
+ goto out_reset;
+ }
+
hpriv->plat_data = priv;
- hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP;
+ hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP | AHCI_HFLAG_NO_WRITE_TO_RO;
+
+ switch (priv->version) {
+ case BRCM_SATA_BCM7425:
+ hpriv->flags |= AHCI_HFLAG_DELAY_ENGINE;
+ /* fall through */
+ case BRCM_SATA_NSP:
+ hpriv->flags |= AHCI_HFLAG_NO_NCQ;
+ priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
+ break;
+ default:
+ break;
+ }
+
+ ret = ahci_platform_enable_clks(hpriv);
+ if (ret)
+ goto out_reset;
+
+ /* Must be first so as to configure endianness including that
+ * of the standard AHCI register space.
+ */
+ brcm_sata_init(priv);
+
+ /* Initializes priv->port_mask which is used below */
+ priv->port_mask = brcm_ahci_get_portmask(hpriv, priv);
+ if (!priv->port_mask) {
+ ret = -ENODEV;
+ goto out_disable_clks;
+ }
+
+ /* Must be done before ahci_platform_enable_phys() */
+ brcm_sata_phys_enable(priv);
brcm_sata_alpm_init(hpriv);
- ret = ahci_platform_enable_resources(hpriv);
+ ret = ahci_platform_enable_phys(hpriv);
if (ret)
- return ret;
-
- if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ)
- hpriv->flags |= AHCI_HFLAG_NO_NCQ;
- hpriv->flags |= AHCI_HFLAG_NO_WRITE_TO_RO;
+ goto out_disable_phys;
ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
&ahci_platform_sht);
if (ret)
- return ret;
+ goto out_disable_platform_phys;
dev_info(dev, "Broadcom AHCI SATA3 registered\n");
return 0;
+
+out_disable_platform_phys:
+ ahci_platform_disable_phys(hpriv);
+out_disable_phys:
+ brcm_sata_phys_disable(priv);
+out_disable_clks:
+ ahci_platform_disable_clks(hpriv);
+out_reset:
+ if (!IS_ERR_OR_NULL(priv->rcdev))
+ reset_control_assert(priv->rcdev);
+ return ret;
}
static int brcm_ahci_remove(struct platform_device *pdev)
@@ -458,12 +520,12 @@ static int brcm_ahci_remove(struct platform_device *pdev)
struct brcm_ahci_priv *priv = hpriv->plat_data;
int ret;
+ brcm_sata_phys_disable(priv);
+
ret = ata_platform_remove_one(pdev);
if (ret)
return ret;
- brcm_sata_phys_disable(priv);
-
return 0;
}
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index f9cb51b..a542142 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -28,6 +28,10 @@
#define AHCI_WINDOW_BASE(win) (0x64 + ((win) << 4))
#define AHCI_WINDOW_SIZE(win) (0x68 + ((win) << 4))
+struct ahci_mvebu_plat_data {
+ int (*plat_config)(struct ahci_host_priv *hpriv);
+};
+
static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv,
const struct mbus_dram_target_info *dram)
{
@@ -62,6 +66,22 @@ static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv)
writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
}
+static int ahci_mvebu_armada_380_config(struct ahci_host_priv *hpriv)
+{
+ const struct mbus_dram_target_info *dram;
+ int rc = 0;
+
+ dram = mv_mbus_dram_info();
+ if (dram)
+ ahci_mvebu_mbus_config(hpriv, dram);
+ else
+ rc = -ENODEV;
+
+ ahci_mvebu_regret_option(hpriv);
+
+ return rc;
+}
+
/**
* ahci_mvebu_stop_engine
*
@@ -126,13 +146,10 @@ static int ahci_mvebu_resume(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct ahci_host_priv *hpriv = host->private_data;
- const struct mbus_dram_target_info *dram;
+ const struct ahci_mvebu_plat_data *pdata = hpriv->plat_data;
- dram = mv_mbus_dram_info();
- if (dram)
- ahci_mvebu_mbus_config(hpriv, dram);
-
- ahci_mvebu_regret_option(hpriv);
+ if (pdata->plat_config)
+ pdata->plat_config(hpriv);
return ahci_platform_resume_host(&pdev->dev);
}
@@ -154,28 +171,31 @@ static struct scsi_host_template ahci_platform_sht = {
static int ahci_mvebu_probe(struct platform_device *pdev)
{
+ const struct ahci_mvebu_plat_data *pdata;
struct ahci_host_priv *hpriv;
- const struct mbus_dram_target_info *dram;
int rc;
+ pdata = of_device_get_match_data(&pdev->dev);
+ if (!pdata)
+ return -EINVAL;
+
hpriv = ahci_platform_get_resources(pdev, 0);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
+ hpriv->plat_data = (void *)pdata;
+
rc = ahci_platform_enable_resources(hpriv);
if (rc)
return rc;
hpriv->stop_engine = ahci_mvebu_stop_engine;
- if (of_device_is_compatible(pdev->dev.of_node,
- "marvell,armada-380-ahci")) {
- dram = mv_mbus_dram_info();
- if (!dram)
- return -ENODEV;
-
- ahci_mvebu_mbus_config(hpriv, dram);
- ahci_mvebu_regret_option(hpriv);
+ pdata = hpriv->plat_data;
+ if (pdata->plat_config) {
+ rc = pdata->plat_config(hpriv);
+ if (rc)
+ goto disable_resources;
}
rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info,
@@ -190,9 +210,23 @@ static int ahci_mvebu_probe(struct platform_device *pdev)
return rc;
}
+static const struct ahci_mvebu_plat_data ahci_mvebu_armada_380_plat_data = {
+ .plat_config = ahci_mvebu_armada_380_config,
+};
+
+static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
+ .plat_config = NULL,
+};
+
static const struct of_device_id ahci_mvebu_of_match[] = {
- { .compatible = "marvell,armada-380-ahci", },
- { .compatible = "marvell,armada-3700-ahci", },
+ {
+ .compatible = "marvell,armada-380-ahci",
+ .data = &ahci_mvebu_armada_380_plat_data,
+ },
+ {
+ .compatible = "marvell,armada-3700-ahci",
+ .data = &ahci_mvebu_armada_3700_plat_data,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match);
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 46f0bd7..cf1e0e1 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -33,6 +33,13 @@ static const struct ata_port_info ahci_port_info = {
.port_ops = &ahci_platform_ops,
};
+static const struct ata_port_info ahci_port_info_nolpm = {
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_LPM,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_platform_ops,
+};
+
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -41,6 +48,7 @@ static int ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
+ const struct ata_port_info *port;
int rc;
hpriv = ahci_platform_get_resources(pdev,
@@ -58,7 +66,11 @@ static int ahci_probe(struct platform_device *pdev)
if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci"))
hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ;
- rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info,
+ port = acpi_device_get_match_data(dev);
+ if (!port)
+ port = &ahci_port_info;
+
+ rc = ahci_platform_init_host(pdev, hpriv, port,
&ahci_platform_sht);
if (rc)
goto disable_resources;
@@ -85,6 +97,7 @@ static const struct of_device_id ahci_of_match[] = {
MODULE_DEVICE_TABLE(of, ahci_of_match);
static const struct acpi_device_id ahci_acpi_match[] = {
+ { "APMC0D33", (unsigned long)&ahci_port_info_nolpm },
{ ACPI_DEVICE_CLASS(PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff) },
{},
};
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 5bece97..522b543 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -47,7 +47,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_ops);
* RETURNS:
* 0 on success otherwise a negative error code
*/
-static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
{
int rc, i;
@@ -72,6 +72,7 @@ static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
}
return rc;
}
+EXPORT_SYMBOL_GPL(ahci_platform_enable_phys);
/**
* ahci_platform_disable_phys - Disable PHYs
@@ -79,7 +80,7 @@ static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
*
* This function disables all PHYs found in hpriv->phys.
*/
-static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
{
int i;
@@ -88,6 +89,7 @@ static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
phy_exit(hpriv->phys[i]);
}
}
+EXPORT_SYMBOL_GPL(ahci_platform_disable_phys);
/**
* ahci_platform_enable_clks - Enable platform clocks
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 133fed8..b45b6f7 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5344,6 +5344,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
}
/**
+ * ata_qc_get_active - get bitmask of active qcs
+ * @ap: port in question
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ *
+ * RETURNS:
+ * Bitmask of active qcs
+ */
+u64 ata_qc_get_active(struct ata_port *ap)
+{
+ u64 qc_active = ap->qc_active;
+
+ /* ATA_TAG_INTERNAL is sent to hw as tag 0 */
+ if (qc_active & (1ULL << ATA_TAG_INTERNAL)) {
+ qc_active |= (1 << 0);
+ qc_active &= ~(1ULL << ATA_TAG_INTERNAL);
+ }
+
+ return qc_active;
+}
+EXPORT_SYMBOL_GPL(ata_qc_get_active);
+
+/**
* ata_qc_complete_multiple - Complete multiple qcs successfully
* @ap: port in question
* @qc_active: new qc_active mask
@@ -6726,6 +6750,9 @@ void ata_host_detach(struct ata_host *host)
{
int i;
+ /* Ensure ata_port probe has completed */
+ async_synchronize_full();
+
for (i = 0; i < host->n_ports; i++)
ata_port_detach(host->ports[i]);
diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
index 0a55019..cc6d06c 100644
--- a/drivers/ata/pata_ep93xx.c
+++ b/drivers/ata/pata_ep93xx.c
@@ -659,7 +659,7 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
* start of new transfer.
*/
drv_data->dma_rx_data.port = EP93XX_DMA_IDE;
- drv_data->dma_rx_data.direction = DMA_FROM_DEVICE;
+ drv_data->dma_rx_data.direction = DMA_DEV_TO_MEM;
drv_data->dma_rx_data.name = "ep93xx-pata-rx";
drv_data->dma_rx_channel = dma_request_channel(mask,
ep93xx_pata_dma_filter, &drv_data->dma_rx_data);
@@ -667,7 +667,7 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
return;
drv_data->dma_tx_data.port = EP93XX_DMA_IDE;
- drv_data->dma_tx_data.direction = DMA_TO_DEVICE;
+ drv_data->dma_tx_data.direction = DMA_MEM_TO_DEV;
drv_data->dma_tx_data.name = "ep93xx-pata-tx";
drv_data->dma_tx_channel = dma_request_channel(mask,
ep93xx_pata_dma_filter, &drv_data->dma_tx_data);
@@ -678,7 +678,7 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
/* Configure receive channel direction and source address */
memset(&conf, 0, sizeof(conf));
- conf.direction = DMA_FROM_DEVICE;
+ conf.direction = DMA_DEV_TO_MEM;
conf.src_addr = drv_data->udma_in_phys;
conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
if (dmaengine_slave_config(drv_data->dma_rx_channel, &conf)) {
@@ -689,7 +689,7 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
/* Configure transmit channel direction and destination address */
memset(&conf, 0, sizeof(conf));
- conf.direction = DMA_TO_DEVICE;
+ conf.direction = DMA_MEM_TO_DEV;
conf.dst_addr = drv_data->udma_out_phys;
conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
if (dmaengine_slave_config(drv_data->dma_tx_channel, &conf)) {
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 4dc528b..ae52a45 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -1283,7 +1283,7 @@ static void sata_fsl_host_intr(struct ata_port *ap)
i, ioread32(hcr_base + CC),
ioread32(hcr_base + CA));
}
- ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask);
+ ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask);
return;
} else if ((ap->qc_active & (1ULL << ATA_TAG_INTERNAL))) {
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 73ba8e1..ab2e9f6 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -2840,7 +2840,7 @@ static void mv_process_crpb_entries(struct ata_port *ap, struct mv_port_priv *pp
}
if (work_done) {
- ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask);
+ ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask);
/* Update the software queue position index in hardware */
writelfl((pp->crpb_dma & EDMA_RSP_Q_BASE_LO_MASK) |
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 72c9b92..761577d 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -1000,7 +1000,7 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
check_commands = 0;
check_commands &= ~(1 << pos);
}
- ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask);
+ ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask);
}
}
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index e89146d..d5c76b5 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -126,7 +126,7 @@ static unsigned long dummy[2] = {0,0};
#define zin_n(r) inl(zatm_dev->base+r*4)
#define zin(r) inl(zatm_dev->base+uPD98401_##r*4)
#define zout(v,r) outl(v,zatm_dev->base+uPD98401_##r*4)
-#define zwait while (zin(CMR) & uPD98401_BUSY)
+#define zwait() do {} while (zin(CMR) & uPD98401_BUSY)
/* RX0, RX1, TX0, TX1 */
static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };
@@ -140,7 +140,7 @@ static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */
static void zpokel(struct zatm_dev *zatm_dev,u32 value,u32 addr)
{
- zwait;
+ zwait();
zout(value,CER);
zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
(uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
@@ -149,10 +149,10 @@ static void zpokel(struct zatm_dev *zatm_dev,u32 value,u32 addr)
static u32 zpeekl(struct zatm_dev *zatm_dev,u32 addr)
{
- zwait;
+ zwait();
zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
(uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
- zwait;
+ zwait();
return zin(CER);
}
@@ -241,7 +241,7 @@ static void refill_pool(struct atm_dev *dev,int pool)
}
if (first) {
spin_lock_irqsave(&zatm_dev->lock, flags);
- zwait;
+ zwait();
zout(virt_to_bus(first),CER);
zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,
CMR);
@@ -508,9 +508,9 @@ static int open_rx_first(struct atm_vcc *vcc)
}
if (zatm_vcc->pool < 0) return -EMSGSIZE;
spin_lock_irqsave(&zatm_dev->lock, flags);
- zwait;
+ zwait();
zout(uPD98401_OPEN_CHAN,CMR);
- zwait;
+ zwait();
DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
spin_unlock_irqrestore(&zatm_dev->lock, flags);
@@ -571,21 +571,21 @@ static void close_rx(struct atm_vcc *vcc)
pos = vcc->vci >> 1;
shift = (1-(vcc->vci & 1)) << 4;
zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
- zwait;
+ zwait();
zout(uPD98401_NOP,CMR);
- zwait;
+ zwait();
zout(uPD98401_NOP,CMR);
spin_unlock_irqrestore(&zatm_dev->lock, flags);
}
spin_lock_irqsave(&zatm_dev->lock, flags);
- zwait;
+ zwait();
zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
uPD98401_CHAN_ADDR_SHIFT),CMR);
- zwait;
+ zwait();
udelay(10); /* why oh why ... ? */
zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
uPD98401_CHAN_ADDR_SHIFT),CMR);
- zwait;
+ zwait();
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
"%d\n",vcc->dev->number,zatm_vcc->rx_chan);
@@ -699,7 +699,7 @@ printk("NONONONOO!!!!\n");
skb_queue_tail(&zatm_vcc->tx_queue,skb);
DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
uPD98401_TXVC_QRP));
- zwait;
+ zwait();
zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
uPD98401_CHAN_ADDR_SHIFT),CMR);
spin_unlock_irqrestore(&zatm_dev->lock, flags);
@@ -891,12 +891,12 @@ static void close_tx(struct atm_vcc *vcc)
}
spin_lock_irqsave(&zatm_dev->lock, flags);
#if 0
- zwait;
+ zwait();
zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
#endif
- zwait;
+ zwait();
zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
- zwait;
+ zwait();
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
"%d\n",vcc->dev->number,chan);
@@ -926,9 +926,9 @@ static int open_tx_first(struct atm_vcc *vcc)
zatm_vcc->tx_chan = 0;
if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
spin_lock_irqsave(&zatm_dev->lock, flags);
- zwait;
+ zwait();
zout(uPD98401_OPEN_CHAN,CMR);
- zwait;
+ zwait();
DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
spin_unlock_irqrestore(&zatm_dev->lock, flags);
@@ -1557,7 +1557,7 @@ static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
struct zatm_dev *zatm_dev;
zatm_dev = ZATM_DEV(dev);
- zwait;
+ zwait();
zout(value,CER);
zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
@@ -1569,10 +1569,10 @@ static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
struct zatm_dev *zatm_dev;
zatm_dev = ZATM_DEV(dev);
- zwait;
+ zwait();
zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
- zwait;
+ zwait();
return zin(CER) & 0xff;
}
diff --git a/drivers/base/component.c b/drivers/base/component.c
index 8946dfe..e8d676f 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -536,9 +536,9 @@ int component_bind_all(struct device *master_dev, void *data)
}
if (ret != 0) {
- for (; i--; )
- if (!master->match->compare[i].duplicate) {
- c = master->match->compare[i].component;
+ for (; i > 0; i--)
+ if (!master->match->compare[i - 1].duplicate) {
+ c = master->match->compare[i - 1].component;
component_unbind(c, master, data);
}
}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 392932f..16de148 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -643,12 +643,27 @@ ssize_t __weak cpu_show_mds(struct device *dev,
return sprintf(buf, "Not affected\n");
}
+ssize_t __weak cpu_show_tsx_async_abort(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+ssize_t __weak cpu_show_itlb_multihit(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);
static DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL);
static DEVICE_ATTR(mds, 0444, cpu_show_mds, NULL);
+static DEVICE_ATTR(tsx_async_abort, 0444, cpu_show_tsx_async_abort, NULL);
+static DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL);
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
&dev_attr_meltdown.attr,
@@ -657,6 +672,8 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = {
&dev_attr_spec_store_bypass.attr,
&dev_attr_l1tf.attr,
&dev_attr_mds.attr,
+ &dev_attr_tsx_async_abort.attr,
+ &dev_attr_itlb_multihit.attr,
NULL
};
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 3fa026c..b964fe1 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -228,7 +228,6 @@ static bool pages_correctly_probed(unsigned long start_pfn)
/*
* MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
* OK to have direct references to sparsemem variables in here.
- * Must already be protected by mem_hotplug_begin().
*/
static int
memory_block_action(unsigned long phys_index, unsigned long action, int online_type)
@@ -294,7 +293,6 @@ static int memory_subsys_online(struct device *dev)
if (mem->online_type < 0)
mem->online_type = MMOP_ONLINE_KEEP;
- /* Already under protection of mem_hotplug_begin() */
ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
/* clear online_type */
@@ -341,19 +339,11 @@ store_mem_state(struct device *dev,
goto err;
}
- /*
- * Memory hotplug needs to hold mem_hotplug_begin() for probe to find
- * the correct memory block to online before doing device_online(dev),
- * which will take dev->mutex. Take the lock early to prevent an
- * inversion, memory_subsys_online() callbacks will be implemented by
- * assuming it's already protected.
- */
- mem_hotplug_begin();
-
switch (online_type) {
case MMOP_ONLINE_KERNEL:
case MMOP_ONLINE_MOVABLE:
case MMOP_ONLINE_KEEP:
+ /* mem->online_type is protected by device_hotplug_lock */
mem->online_type = online_type;
ret = device_online(&mem->dev);
break;
@@ -364,7 +354,6 @@ store_mem_state(struct device *dev,
ret = -EINVAL; /* should never happen */
}
- mem_hotplug_done();
err:
unlock_device_hotplug();
@@ -571,15 +560,20 @@ memory_probe_store(struct device *dev, struct device_attribute *attr,
if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
return -EINVAL;
+ ret = lock_device_hotplug_sysfs();
+ if (ret)
+ return ret;
+
nid = memory_add_physaddr_to_nid(phys_addr);
- ret = add_memory(nid, phys_addr,
- MIN_MEMORY_BLOCK_SIZE * sections_per_block);
+ ret = __add_memory(nid, phys_addr,
+ MIN_MEMORY_BLOCK_SIZE * sections_per_block);
if (ret)
goto out;
ret = count;
out:
+ unlock_device_hotplug();
return ret;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index dff82a3..e9be1f5 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -26,6 +26,7 @@
#include <linux/clk/clk-conf.h>
#include <linux/limits.h>
#include <linux/property.h>
+#include <linux/kmemleak.h>
#include "base.h"
#include "power/power.h"
@@ -525,6 +526,8 @@ struct platform_device *platform_device_register_full(
if (!pdev->dev.dma_mask)
goto err;
+ kmemleak_ignore(pdev->dev.dma_mask);
+
*pdev->dev.dma_mask = pdevinfo->dma_mask;
pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
}
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index bf5be0b..52c292d 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -467,6 +467,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
return -EAGAIN;
}
+ /* Default to shallowest state. */
+ if (!genpd->gov)
+ genpd->state_idx = 0;
+
if (genpd->power_off) {
int ret;
@@ -1686,6 +1690,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
ret = genpd_set_default_power_state(genpd);
if (ret)
return ret;
+ } else if (!gov) {
+ pr_warn("%s : no governor for states\n", genpd->name);
}
device_initialize(&genpd->dev);
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 3aaf6af..2158e13 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -1701,11 +1701,41 @@ static const struct block_device_operations floppy_fops = {
.check_events = amiga_check_events,
};
+static struct gendisk *fd_alloc_disk(int drive)
+{
+ struct gendisk *disk;
+
+ disk = alloc_disk(1);
+ if (!disk)
+ goto out;
+
+ disk->queue = blk_init_queue(do_fd_request, &amiflop_lock);
+ if (IS_ERR(disk->queue)) {
+ disk->queue = NULL;
+ goto out_put_disk;
+ }
+
+ unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL);
+ if (!unit[drive].trackbuf)
+ goto out_cleanup_queue;
+
+ return disk;
+
+out_cleanup_queue:
+ blk_cleanup_queue(disk->queue);
+ disk->queue = NULL;
+out_put_disk:
+ put_disk(disk);
+out:
+ unit[drive].type->code = FD_NODRIVE;
+ return NULL;
+}
+
static int __init fd_probe_drives(void)
{
int drive,drives,nomem;
- printk(KERN_INFO "FD: probing units\nfound ");
+ pr_info("FD: probing units\nfound");
drives=0;
nomem=0;
for(drive=0;drive<FD_MAX_UNITS;drive++) {
@@ -1713,27 +1743,17 @@ static int __init fd_probe_drives(void)
fd_probe(drive);
if (unit[drive].type->code == FD_NODRIVE)
continue;
- disk = alloc_disk(1);
+
+ disk = fd_alloc_disk(drive);
if (!disk) {
- unit[drive].type->code = FD_NODRIVE;
+ pr_cont(" no mem for fd%d", drive);
+ nomem = 1;
continue;
}
unit[drive].gendisk = disk;
-
- disk->queue = blk_init_queue(do_fd_request, &amiflop_lock);
- if (!disk->queue) {
- unit[drive].type->code = FD_NODRIVE;
- continue;
- }
-
drives++;
- if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
- printk("no mem for ");
- unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
- drives--;
- nomem = 1;
- }
- printk("fd%d ",drive);
+
+ pr_cont(" fd%d",drive);
disk->major = FLOPPY_MAJOR;
disk->first_minor = drive;
disk->fops = &floppy_fops;
@@ -1744,11 +1764,11 @@ static int __init fd_probe_drives(void)
}
if ((drives > 0) || (nomem == 0)) {
if (drives == 0)
- printk("no drives");
- printk("\n");
+ pr_cont(" no drives");
+ pr_cont("\n");
return drives;
}
- printk("\n");
+ pr_cont("\n");
return -ENOMEM;
}
@@ -1831,30 +1851,6 @@ static int __init amiga_floppy_probe(struct platform_device *pdev)
return ret;
}
-#if 0 /* not safe to unload */
-static int __exit amiga_floppy_remove(struct platform_device *pdev)
-{
- int i;
-
- for( i = 0; i < FD_MAX_UNITS; i++) {
- if (unit[i].type->code != FD_NODRIVE) {
- struct request_queue *q = unit[i].gendisk->queue;
- del_gendisk(unit[i].gendisk);
- put_disk(unit[i].gendisk);
- kfree(unit[i].trackbuf);
- if (q)
- blk_cleanup_queue(q);
- }
- }
- blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
- free_irq(IRQ_AMIGA_CIAA_TB, NULL);
- free_irq(IRQ_AMIGA_DSKBLK, NULL);
- custom.dmacon = DMAF_DISK; /* disable DMA */
- amiga_chip_free(raw_buf);
- unregister_blkdev(FLOPPY_MAJOR, "fd");
-}
-#endif
-
static struct platform_driver amiga_floppy_driver = {
.driver = {
.name = "amiga-floppy",
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 15a9ffc..19d3598 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -796,7 +796,6 @@ int __drbd_send_protocol(struct drbd_connection *connection, enum drbd_packet cm
if (nc->tentative && connection->agreed_pro_version < 92) {
rcu_read_unlock();
- mutex_unlock(&sock->mutex);
drbd_err(connection, "--dry-run is not supported by peer");
return -EOPNOTSUPP;
}
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 7145031..29f16ac 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1515,6 +1515,30 @@ static void sanitize_disk_conf(struct drbd_device *device, struct disk_conf *dis
}
}
+static int disk_opts_check_al_size(struct drbd_device *device, struct disk_conf *dc)
+{
+ int err = -EBUSY;
+
+ if (device->act_log &&
+ device->act_log->nr_elements == dc->al_extents)
+ return 0;
+
+ drbd_suspend_io(device);
+ /* If IO completion is currently blocked, we would likely wait
+ * "forever" for the activity log to become unused. So we don't. */
+ if (atomic_read(&device->ap_bio_cnt))
+ goto out;
+
+ wait_event(device->al_wait, lc_try_lock(device->act_log));
+ drbd_al_shrink(device);
+ err = drbd_check_al_size(device, dc);
+ lc_unlock(device->act_log);
+ wake_up(&device->al_wait);
+out:
+ drbd_resume_io(device);
+ return err;
+}
+
int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_config_context adm_ctx;
@@ -1577,15 +1601,12 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
}
}
- drbd_suspend_io(device);
- wait_event(device->al_wait, lc_try_lock(device->act_log));
- drbd_al_shrink(device);
- err = drbd_check_al_size(device, new_disk_conf);
- lc_unlock(device->act_log);
- wake_up(&device->al_wait);
- drbd_resume_io(device);
-
+ err = disk_opts_check_al_size(device, new_disk_conf);
if (err) {
+ /* Could be just "busy". Ignore?
+ * Introduce dedicated error code? */
+ drbd_msg_put_info(adm_ctx.reply_skb,
+ "Try again without changing current al-extents setting");
retcode = ERR_NOMEM;
goto fail_unlock;
}
@@ -1935,9 +1956,9 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
}
}
- if (device->state.conn < C_CONNECTED &&
- device->state.role == R_PRIMARY && device->ed_uuid &&
- (device->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) {
+ if (device->state.pdsk != D_UP_TO_DATE && device->ed_uuid &&
+ (device->state.role == R_PRIMARY || device->state.peer == R_PRIMARY) &&
+ (device->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) {
drbd_err(device, "Can only attach to data with current UUID=%016llX\n",
(unsigned long long)device->ed_uuid);
retcode = ERR_DATA_NOT_CURRENT;
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 8ebe99b..2bd488b 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -3981,6 +3981,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
struct o_qlim *o = (connection->agreed_features & DRBD_FF_WSAME) ? p->qlim : NULL;
enum determine_dev_size dd = DS_UNCHANGED;
sector_t p_size, p_usize, p_csize, my_usize;
+ sector_t new_size, cur_size;
int ldsc = 0; /* local disk size changed */
enum dds_flags ddsf;
@@ -3988,6 +3989,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
if (!peer_device)
return config_unknown_volume(connection, pi);
device = peer_device->device;
+ cur_size = drbd_get_capacity(device->this_bdev);
p_size = be64_to_cpu(p->d_size);
p_usize = be64_to_cpu(p->u_size);
@@ -3998,7 +4000,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
device->p_size = p_size;
if (get_ldev(device)) {
- sector_t new_size, cur_size;
rcu_read_lock();
my_usize = rcu_dereference(device->ldev->disk_conf)->disk_size;
rcu_read_unlock();
@@ -4016,7 +4017,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
/* Never shrink a device with usable data during connect.
But allow online shrinking if we are connected. */
new_size = drbd_new_dev_size(device, device->ldev, p_usize, 0);
- cur_size = drbd_get_capacity(device->this_bdev);
if (new_size < cur_size &&
device->state.disk >= D_OUTDATED &&
device->state.conn < C_CONNECTED) {
@@ -4081,9 +4081,36 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
*
* However, if he sends a zero current size,
* take his (user-capped or) backing disk size anyways.
+ *
+ * Unless of course he does not have a disk himself.
+ * In which case we ignore this completely.
*/
+ sector_t new_size = p_csize ?: p_usize ?: p_size;
drbd_reconsider_queue_parameters(device, NULL, o);
- drbd_set_my_capacity(device, p_csize ?: p_usize ?: p_size);
+ if (new_size == 0) {
+ /* Ignore, peer does not know nothing. */
+ } else if (new_size == cur_size) {
+ /* nothing to do */
+ } else if (cur_size != 0 && p_size == 0) {
+ drbd_warn(device, "Ignored diskless peer device size (peer:%llu != me:%llu sectors)!\n",
+ (unsigned long long)new_size, (unsigned long long)cur_size);
+ } else if (new_size < cur_size && device->state.role == R_PRIMARY) {
+ drbd_err(device, "The peer's device size is too small! (%llu < %llu sectors); demote me first!\n",
+ (unsigned long long)new_size, (unsigned long long)cur_size);
+ conn_request_state(peer_device->connection, NS(conn, C_DISCONNECTING), CS_HARD);
+ return -EIO;
+ } else {
+ /* I believe the peer, if
+ * - I don't have a current size myself
+ * - we agree on the size anyways
+ * - I do have a current size, am Secondary,
+ * and he has the only disk
+ * - I do have a current size, am Primary,
+ * and he has the only disk,
+ * which is larger than my current size
+ */
+ drbd_set_my_capacity(device, new_size);
+ }
}
if (get_ldev(device)) {
@@ -4369,6 +4396,25 @@ static int receive_state(struct drbd_connection *connection, struct packet_info
if (peer_state.conn == C_AHEAD)
ns.conn = C_BEHIND;
+ /* TODO:
+ * if (primary and diskless and peer uuid != effective uuid)
+ * abort attach on peer;
+ *
+ * If this node does not have good data, was already connected, but
+ * the peer did a late attach only now, trying to "negotiate" with me,
+ * AND I am currently Primary, possibly frozen, with some specific
+ * "effective" uuid, this should never be reached, really, because
+ * we first send the uuids, then the current state.
+ *
+ * In this scenario, we already dropped the connection hard
+ * when we received the unsuitable uuids (receive_uuids().
+ *
+ * Should we want to change this, that is: not drop the connection in
+ * receive_uuids() already, then we would need to add a branch here
+ * that aborts the attach of "unsuitable uuids" on the peer in case
+ * this node is currently Diskless Primary.
+ */
+
if (device->p_uuid && peer_state.disk >= D_NEGOTIATING &&
get_ldev_if_state(device, D_NEGOTIATING)) {
int cr; /* consider resync */
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
index 0813c65..b452359 100644
--- a/drivers/block/drbd/drbd_state.c
+++ b/drivers/block/drbd/drbd_state.c
@@ -688,11 +688,9 @@ request_detach(struct drbd_device *device)
CS_VERBOSE | CS_ORDERED | CS_INHIBIT_MD_IO);
}
-enum drbd_state_rv
-drbd_request_detach_interruptible(struct drbd_device *device)
+int drbd_request_detach_interruptible(struct drbd_device *device)
{
- enum drbd_state_rv rv;
- int ret;
+ int ret, rv;
drbd_suspend_io(device); /* so no-one is stuck in drbd_al_begin_io */
wait_event_interruptible(device->state_wait,
diff --git a/drivers/block/drbd/drbd_state.h b/drivers/block/drbd/drbd_state.h
index ea58301..f87371e 100644
--- a/drivers/block/drbd/drbd_state.h
+++ b/drivers/block/drbd/drbd_state.h
@@ -131,7 +131,7 @@ extern enum drbd_state_rv _drbd_set_state(struct drbd_device *, union drbd_state
enum chg_state_flags,
struct completion *done);
extern void print_st_err(struct drbd_device *, union drbd_state,
- union drbd_state, int);
+ union drbd_state, enum drbd_state_rv);
enum drbd_state_rv
_conn_request_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
@@ -162,8 +162,7 @@ static inline int drbd_request_state(struct drbd_device *device,
}
/* for use in adm_detach() (drbd_adm_detach(), drbd_adm_down()) */
-enum drbd_state_rv
-drbd_request_detach_interruptible(struct drbd_device *device);
+int drbd_request_detach_interruptible(struct drbd_device *device);
enum drbd_role conn_highest_role(struct drbd_connection *connection);
enum drbd_role conn_highest_peer(struct drbd_connection *connection);
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 126c2c5..9cd231a 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -416,18 +416,20 @@ static int lo_read_transfer(struct loop_device *lo, struct request *rq,
return ret;
}
-static int lo_discard(struct loop_device *lo, struct request *rq, loff_t pos)
+static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
+ int mode)
{
/*
- * We use punch hole to reclaim the free space used by the
- * image a.k.a. discard. However we do not support discard if
- * encryption is enabled, because it may give an attacker
- * useful information.
+ * We use fallocate to manipulate the space mappings used by the image
+ * a.k.a. discard/zerorange. However we do not support this if
+ * encryption is enabled, because it may give an attacker useful
+ * information.
*/
struct file *file = lo->lo_backing_file;
- int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
int ret;
+ mode |= FALLOC_FL_KEEP_SIZE;
+
if ((!file->f_op->fallocate) || lo->lo_encrypt_key_size) {
ret = -EOPNOTSUPP;
goto out;
@@ -596,9 +598,17 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
switch (req_op(rq)) {
case REQ_OP_FLUSH:
return lo_req_flush(lo, rq);
- case REQ_OP_DISCARD:
case REQ_OP_WRITE_ZEROES:
- return lo_discard(lo, rq, pos);
+ /*
+ * If the caller doesn't want deallocation, call zeroout to
+ * write zeroes the range. Otherwise, punch them out.
+ */
+ return lo_fallocate(lo, rq, pos,
+ (rq->cmd_flags & REQ_NOUNMAP) ?
+ FALLOC_FL_ZERO_RANGE :
+ FALLOC_FL_PUNCH_HOLE);
+ case REQ_OP_DISCARD:
+ return lo_fallocate(lo, rq, pos, FALLOC_FL_PUNCH_HOLE);
case REQ_OP_WRITE:
if (lo->transfer)
return lo_write_transfer(lo, rq, pos);
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index bc2fa4e..b9d321b 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -228,8 +228,8 @@ static void nbd_put(struct nbd_device *nbd)
if (refcount_dec_and_mutex_lock(&nbd->refs,
&nbd_index_mutex)) {
idr_remove(&nbd_index_idr, nbd->index);
- mutex_unlock(&nbd_index_mutex);
nbd_dev_remove(nbd);
+ mutex_unlock(&nbd_index_mutex);
}
}
@@ -349,17 +349,16 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req,
struct nbd_device *nbd = cmd->nbd;
struct nbd_config *config;
+ if (!mutex_trylock(&cmd->lock))
+ return BLK_EH_RESET_TIMER;
+
if (!refcount_inc_not_zero(&nbd->config_refs)) {
cmd->status = BLK_STS_TIMEOUT;
+ mutex_unlock(&cmd->lock);
goto done;
}
config = nbd->config;
- if (!mutex_trylock(&cmd->lock)) {
- nbd_config_put(nbd);
- return BLK_EH_RESET_TIMER;
- }
-
if (config->num_connections > 1) {
dev_err_ratelimited(nbd_to_dev(nbd),
"Connection timed out, retrying (%d/%d alive)\n",
@@ -664,6 +663,12 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
ret = -ENOENT;
goto out;
}
+ if (cmd->status != BLK_STS_OK) {
+ dev_err(disk_to_dev(nbd->disk), "Command already handled %p\n",
+ req);
+ ret = -ENOENT;
+ goto out;
+ }
if (test_bit(NBD_CMD_REQUEUED, &cmd->flags)) {
dev_err(disk_to_dev(nbd->disk), "Raced with timeout on req %p\n",
req);
@@ -745,7 +750,10 @@ static void nbd_clear_req(struct request *req, void *data, bool reserved)
{
struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req);
+ mutex_lock(&cmd->lock);
cmd->status = BLK_STS_IOERR;
+ mutex_unlock(&cmd->lock);
+
blk_mq_complete_request(req);
}
@@ -924,6 +932,26 @@ static blk_status_t nbd_queue_rq(struct blk_mq_hw_ctx *hctx,
return ret;
}
+static struct socket *nbd_get_socket(struct nbd_device *nbd, unsigned long fd,
+ int *err)
+{
+ struct socket *sock;
+
+ *err = 0;
+ sock = sockfd_lookup(fd, err);
+ if (!sock)
+ return NULL;
+
+ if (sock->ops->shutdown == sock_no_shutdown) {
+ dev_err(disk_to_dev(nbd->disk), "Unsupported socket: shutdown callout must be supported.\n");
+ *err = -EINVAL;
+ sockfd_put(sock);
+ return NULL;
+ }
+
+ return sock;
+}
+
static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
bool netlink)
{
@@ -933,7 +961,7 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
struct nbd_sock *nsock;
int err;
- sock = sockfd_lookup(arg, &err);
+ sock = nbd_get_socket(nbd, arg, &err);
if (!sock)
return err;
@@ -956,14 +984,15 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
sockfd_put(sock);
return -ENOMEM;
}
+
+ config->socks = socks;
+
nsock = kzalloc(sizeof(struct nbd_sock), GFP_KERNEL);
if (!nsock) {
sockfd_put(sock);
return -ENOMEM;
}
- config->socks = socks;
-
nsock->fallback_index = -1;
nsock->dead = false;
mutex_init(&nsock->tx_lock);
@@ -985,7 +1014,7 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
int i;
int err;
- sock = sockfd_lookup(arg, &err);
+ sock = nbd_get_socket(nbd, arg, &err);
if (!sock)
return err;
@@ -1218,10 +1247,10 @@ static int nbd_start_device_ioctl(struct nbd_device *nbd, struct block_device *b
mutex_unlock(&nbd->config_lock);
ret = wait_event_interruptible(config->recv_wq,
atomic_read(&config->recv_threads) == 0);
- if (ret) {
+ if (ret)
sock_shutdown(nbd);
- flush_workqueue(nbd->recv_workq);
- }
+ flush_workqueue(nbd->recv_workq);
+
mutex_lock(&nbd->config_lock);
nbd_bdev_reset(bdev);
/* user requested, ignore socket errors */
diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c
index f2c631c..14056dc 100644
--- a/drivers/block/rsxx/core.c
+++ b/drivers/block/rsxx/core.c
@@ -1014,8 +1014,10 @@ static void rsxx_pci_remove(struct pci_dev *dev)
cancel_work_sync(&card->event_work);
+ destroy_workqueue(card->event_wq);
rsxx_destroy_dev(card);
rsxx_dma_destroy(card);
+ destroy_workqueue(card->creg_ctrl.creg_wq);
spin_lock_irqsave(&card->irq_lock, flags);
rsxx_disable_ier_and_isr(card, CR_INTR_ALL);
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index 87b9e7f..27323fa 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -1416,7 +1416,7 @@ static void skd_resolve_req_exception(struct skd_device *skdev,
case SKD_CHECK_STATUS_BUSY_IMMINENT:
skd_log_skreq(skdev, skreq, "retry(busy)");
- blk_requeue_request(skdev->queue, req);
+ blk_mq_requeue_request(req, true);
dev_info(&skdev->pdev->dev, "drive BUSY imminent\n");
skdev->state = SKD_DRVR_STATE_BUSY_IMMINENT;
skdev->timer_countdown = SKD_TIMER_MINUTES(20);
@@ -1426,7 +1426,7 @@ static void skd_resolve_req_exception(struct skd_device *skdev,
case SKD_CHECK_STATUS_REQUEUE_REQUEST:
if ((unsigned long) ++req->special < SKD_MAX_RETRIES) {
skd_log_skreq(skdev, skreq, "retry");
- blk_requeue_request(skdev->queue, req);
+ blk_mq_requeue_request(req, true);
break;
}
/* fall through */
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index fd1e19f..3666afa 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -936,6 +936,8 @@ static int xen_blkbk_map(struct xen_blkif_ring *ring,
out_of_memory:
pr_alert("%s: out of memory\n", __func__);
put_free_pages(ring, pages_to_gnt, segs_to_map);
+ for (i = last_map; i < num; i++)
+ pages[i]->handle = BLKBACK_INVALID_HANDLE;
return -ENOMEM;
}
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index 55869b3..25c41ce 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -179,6 +179,15 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid)
blkif->domid = domid;
atomic_set(&blkif->refcnt, 1);
init_completion(&blkif->drain_complete);
+
+ /*
+ * Because freeing back to the cache may be deferred, it is not
+ * safe to unload the module (and hence destroy the cache) until
+ * this has completed. To prevent premature unloading, take an
+ * extra module reference here and release only when the object
+ * has been freed back to the cache.
+ */
+ __module_get(THIS_MODULE);
INIT_WORK(&blkif->free_work, xen_blkif_deferred_free);
return blkif;
@@ -328,6 +337,7 @@ static void xen_blkif_free(struct xen_blkif *blkif)
/* Make sure everything is drained before shutting down */
kmem_cache_free(xen_blkif_cachep, blkif);
+ module_put(THIS_MODULE);
}
int __init xen_blkif_interface_init(void)
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index b805afa..5cb7f10 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -424,13 +424,14 @@ static void reset_bdev(struct zram *zram)
static ssize_t backing_dev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct file *file;
struct zram *zram = dev_to_zram(dev);
- struct file *file = zram->backing_dev;
char *p;
ssize_t ret;
down_read(&zram->init_lock);
- if (!zram->backing_dev) {
+ file = zram->backing_dev;
+ if (!file) {
memcpy(buf, "none\n", 5);
up_read(&zram->init_lock);
return 5;
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index a0a55ed..612941f 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -600,7 +600,7 @@ static int bt_dt_parse_vreg_info(struct device *dev,
static int bt_dt_parse_clk_info(struct device *dev,
struct bt_power_clk_data **clk_data)
{
- int ret = -EINVAL;
+ int ret = 0;
struct bt_power_clk_data *clk = NULL;
struct device_node *np = dev->of_node;
@@ -637,7 +637,7 @@ static int bt_dt_parse_clk_info(struct device *dev,
*clk_data = clk;
} else {
- BT_PWR_ERR("clocks is not provided in device tree");
+ BT_PWR_INFO("clocks is not provided in device tree");
}
err:
@@ -658,7 +658,7 @@ static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
of_get_named_gpio(pdev->dev.of_node,
"qca,bt-reset-gpio", 0);
if (bt_power_pdata->bt_gpio_sys_rst < 0)
- BT_PWR_ERR("bt-reset-gpio not provided in device tree");
+ BT_PWR_INFO("bt-reset-gpio not provided in devicetree");
rc = bt_dt_parse_vreg_info(&pdev->dev,
&bt_power_pdata->bt_vdd_core,
diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c
index a69536d..bf886e6 100644
--- a/drivers/bluetooth/btfm_slim.c
+++ b/drivers/bluetooth/btfm_slim.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
@@ -313,8 +313,8 @@ static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
rx_chs = btfmslim->rx_chs;
tx_chs = btfmslim->tx_chs;
- if ((chipset_ver >= QCA_CHEROKEE_SOC_ID_0300) &&
- (chipset_ver <= QCA_CHEROKEE_SOC_ID_0320)) {
+ if ((chipset_ver >= QCA_CHEROKEE_SOC_ID_0310) &&
+ (chipset_ver <= QCA_CHEROKEE_SOC_ID_0320_UMC)) {
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
if (tx_chs->port == SLAVE_SB_PGD_PORT_TX1_FM)
diff --git a/drivers/bluetooth/btfm_slim_slave.h b/drivers/bluetooth/btfm_slim_slave.h
index 26d9c1d..88d1484 100644
--- a/drivers/bluetooth/btfm_slim_slave.h
+++ b/drivers/bluetooth/btfm_slim_slave.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef BTFM_SLIM_SLAVE_H
@@ -77,38 +77,29 @@
#define SLAVE_SB_PGD_PORT_RX_A2P 17
enum {
- QCA_CHEROKEE_SOC_ID_0100 = 0x40010100,
QCA_CHEROKEE_SOC_ID_0200 = 0x40010200,
QCA_CHEROKEE_SOC_ID_0201 = 0x40010201,
QCA_CHEROKEE_SOC_ID_0210 = 0x40010214,
QCA_CHEROKEE_SOC_ID_0211 = 0x40010224,
- QCA_CHEROKEE_SOC_ID_0300 = 0x40010300,
QCA_CHEROKEE_SOC_ID_0310 = 0x40010310,
QCA_CHEROKEE_SOC_ID_0320 = 0x40010320,
+ QCA_CHEROKEE_SOC_ID_0320_UMC = 0x40014320,
};
enum {
- QCA_APACHE_SOC_ID_0005 = 0x40020100,
- QCA_APACHE_SOC_ID_0006 = 0x40020110,
QCA_APACHE_SOC_ID_0100 = 0x40020120,
- QCA_APACHE_SOC_ID_0101 = 0x40020121,
- QCA_APACHE_SOC_ID_0102 = 0x40020122,
- QCA_APACHE_SOC_ID_0103 = 0x40020123,
QCA_APACHE_SOC_ID_0110 = 0x40020130,
QCA_APACHE_SOC_ID_0120 = 0x40020140,
QCA_APACHE_SOC_ID_0121 = 0x40020150,
};
enum {
- QCA_COMANCHE_SOC_ID_0100 = 0x40070100,
QCA_COMANCHE_SOC_ID_0101 = 0x40070101,
QCA_COMANCHE_SOC_ID_0110 = 0x40070110,
+ QCA_COMANCHE_SOC_ID_0120 = 0x40070120,
};
enum {
- QCA_HASTINGS_SOC_ID_0100 = 0x400A0100,
- QCA_HASTINGS_SOC_ID_0101 = 0x40040101,
- QCA_HASTINGS_SOC_ID_0110 = 0x400A0110,
QCA_HASTINGS_SOC_ID_0200 = 0x400A0200,
};
diff --git a/drivers/bluetooth/btrsi.c b/drivers/bluetooth/btrsi.c
index 60d1419..3951f7b 100644
--- a/drivers/bluetooth/btrsi.c
+++ b/drivers/bluetooth/btrsi.c
@@ -21,8 +21,9 @@
#include <net/rsi_91x.h>
#include <net/genetlink.h>
-#define RSI_HEADROOM_FOR_BT_HAL 16
+#define RSI_DMA_ALIGN 8
#define RSI_FRAME_DESC_SIZE 16
+#define RSI_HEADROOM_FOR_BT_HAL (RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN)
struct rsi_hci_adapter {
void *priv;
@@ -70,6 +71,16 @@ static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)
bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);
kfree_skb(skb);
skb = new_skb;
+ if (!IS_ALIGNED((unsigned long)skb->data, RSI_DMA_ALIGN)) {
+ u8 *skb_data = skb->data;
+ int skb_len = skb->len;
+
+ skb_push(skb, RSI_DMA_ALIGN);
+ skb_pull(skb, PTR_ALIGN(skb->data,
+ RSI_DMA_ALIGN) - skb->data);
+ memmove(skb->data, skb_data, skb_len);
+ skb_trim(skb, skb_len);
+ }
}
return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 08936bf..1b0adf5 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -1138,7 +1138,7 @@ static int btusb_open(struct hci_dev *hdev)
if (data->setup_on_usb) {
err = data->setup_on_usb(hdev);
if (err < 0)
- return err;
+ goto setup_fail;
}
data->intf->needs_remote_wakeup = 1;
@@ -1170,6 +1170,7 @@ static int btusb_open(struct hci_dev *hdev)
failed:
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+setup_fail:
usb_autopm_put_interface(data->intf);
return err;
}
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index aa6b7ed..59e5fc5 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -51,6 +51,12 @@
#define BCM_LM_DIAG_PKT 0x07
#define BCM_LM_DIAG_SIZE 63
+#define BCM_TYPE49_PKT 0x31
+#define BCM_TYPE49_SIZE 0
+
+#define BCM_TYPE52_PKT 0x34
+#define BCM_TYPE52_SIZE 0
+
#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */
/**
@@ -564,12 +570,28 @@ static int bcm_setup(struct hci_uart *hu)
.lsize = 0, \
.maxlen = BCM_NULL_SIZE
+#define BCM_RECV_TYPE49 \
+ .type = BCM_TYPE49_PKT, \
+ .hlen = BCM_TYPE49_SIZE, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = BCM_TYPE49_SIZE
+
+#define BCM_RECV_TYPE52 \
+ .type = BCM_TYPE52_PKT, \
+ .hlen = BCM_TYPE52_SIZE, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = BCM_TYPE52_SIZE
+
static const struct h4_recv_pkt bcm_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag },
{ BCM_RECV_NULL, .recv = hci_recv_diag },
+ { BCM_RECV_TYPE49, .recv = hci_recv_diag },
+ { BCM_RECV_TYPE52, .recv = hci_recv_diag },
};
static int bcm_recv(struct hci_uart *hu, const void *data, int count)
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 66fe1e6..2782927 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -606,6 +606,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
if (*ptr == 0xc0) {
BT_ERR("Short BCSP packet");
kfree_skb(bcsp->rx_skb);
+ bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_START;
bcsp->rx_count = 0;
} else
@@ -621,6 +622,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
BT_ERR("Error in BCSP hdr checksum");
kfree_skb(bcsp->rx_skb);
+ bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
@@ -645,6 +647,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
bscp_get_crc(bcsp));
kfree_skb(bcsp->rx_skb);
+ bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index aa2543b..46e2044 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -368,6 +368,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
+ clear_bit(HCI_UART_PROTO_READY, &hu->flags);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
diff --git a/drivers/bus/mhi/controllers/mhi_arch_qcom.c b/drivers/bus/mhi/controllers/mhi_arch_qcom.c
index a2f9bf7..1678f4c 100644
--- a/drivers/bus/mhi/controllers/mhi_arch_qcom.c
+++ b/drivers/bus/mhi/controllers/mhi_arch_qcom.c
@@ -27,7 +27,6 @@ struct arch_info {
u32 bus_client;
struct msm_pcie_register_event pcie_reg_event;
struct pci_saved_state *pcie_state;
- async_cookie_t cookie;
void *boot_ipc_log;
void *tsync_ipc_log;
struct mhi_device *boot_dev;
@@ -67,12 +66,15 @@ void mhi_reg_write_work(struct work_struct *w)
if (!info->valid)
return;
- if (mhi_is_active(mhi_cntrl->mhi_dev) && msm_pcie_prevent_l1(pci_dev))
+ if (!mhi_is_active(mhi_cntrl->mhi_dev))
+ return;
+
+ if (msm_pcie_prevent_l1(pci_dev))
return;
while (info->valid) {
if (!mhi_is_active(mhi_cntrl->mhi_dev))
- return;
+ break;
writel_relaxed(info->val, info->reg_addr);
info->valid = false;
@@ -200,7 +202,7 @@ static int mhi_arch_esoc_ops_power_on(void *priv, unsigned int flags)
return 0;
}
- MHI_LOG("Enter\n");
+ MHI_LOG("Enter: mdm_crashed:%d\n", flags & ESOC_HOOK_MDM_CRASH);
/* reset rpm state */
pm_runtime_set_active(&pci_dev->dev);
@@ -221,7 +223,6 @@ static int mhi_arch_esoc_ops_power_on(void *priv, unsigned int flags)
return ret;
}
- mhi_dev->mdm_state = (flags & ESOC_HOOK_MDM_CRASH);
return mhi_pci_probe(pci_dev, NULL);
}
@@ -282,9 +283,6 @@ static void mhi_arch_esoc_ops_power_off(void *priv, unsigned int flags)
mhi_deinit_pci_dev(mhi_cntrl);
mhi_arch_link_off(mhi_cntrl);
- /* wait for boot monitor to exit */
- async_synchronize_cookie(arch_info->cookie + 1);
-
mhi_arch_pcie_deinit(mhi_cntrl);
mhi_cntrl->dev = NULL;
@@ -310,11 +308,21 @@ static void mhi_bl_dl_cb(struct mhi_device *mhi_device,
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
char *buf = mhi_result->buf_addr;
+ char *token, *delim = "\n";
/* force a null at last character */
buf[mhi_result->bytes_xferd - 1] = 0;
- ipc_log_string(arch_info->boot_ipc_log, "%s %s", DLOG, buf);
+ if (mhi_result->bytes_xferd >= MAX_MSG_SIZE) {
+ do {
+ token = strsep((char **)&buf, delim);
+ if (token)
+ ipc_log_string(arch_info->boot_ipc_log, "%s %s",
+ DLOG, token);
+ } while (token);
+ } else {
+ ipc_log_string(arch_info->boot_ipc_log, "%s %s", DLOG, buf);
+ }
}
static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev,
@@ -333,50 +341,21 @@ static void mhi_bl_remove(struct mhi_device *mhi_device)
HLOG "Received Remove notif.\n");
}
-static void mhi_boot_monitor(void *data, async_cookie_t cookie)
-{
- struct mhi_controller *mhi_cntrl = data;
- struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
- struct arch_info *arch_info = mhi_dev->arch_info;
- /* 15 sec timeout for booting device */
- const u32 timeout = msecs_to_jiffies(15000);
-
- /* wait for device to enter boot stage */
- wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->ee == MHI_EE_AMSS
- || mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION
- || mhi_cntrl->power_down,
- timeout);
-
- ipc_log_string(arch_info->boot_ipc_log, HLOG "Device current ee = %s\n",
- TO_MHI_EXEC_STR(mhi_cntrl->ee));
-
- /* if we successfully booted to amss, enable runtime pm */
- if (mhi_cntrl->ee == MHI_EE_AMSS)
- if (!mhi_dev->drv_supported || arch_info->drv_connected)
- pm_runtime_allow(&mhi_dev->pci_dev->dev);
-}
-
-int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
-{
- struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
- struct arch_info *arch_info = mhi_dev->arch_info;
-
- /* start a boot monitor if not in crashed state */
- if (!mhi_dev->mdm_state)
- arch_info->cookie = async_schedule(mhi_boot_monitor, mhi_cntrl);
-
- return 0;
-}
-
void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
struct mhi_device *boot_dev = arch_info->boot_dev;
+ ipc_log_string(arch_info->boot_ipc_log,
+ HLOG "Device entered mission mode\n");
+
/* disable boot logger channel */
if (boot_dev)
mhi_unprepare_from_transfer(boot_dev);
+
+ if (!mhi_dev->drv_supported || arch_info->drv_connected)
+ pm_runtime_allow(&mhi_dev->pci_dev->dev);
}
static int mhi_arch_pcie_scale_bw(struct mhi_controller *mhi_cntrl,
@@ -611,7 +590,8 @@ static int mhi_arch_drv_suspend(struct mhi_controller *mhi_cntrl)
/* do a drv hand off */
ret = msm_pcie_pm_control(MSM_PCIE_DRV_SUSPEND, mhi_cntrl->bus,
- pci_dev, NULL, 0);
+ pci_dev, NULL, mhi_cntrl->wake_set ?
+ MSM_PCIE_CONFIG_NO_L1SS_TO : 0);
/*
* we failed to suspend and scaled down pcie bw.. need to scale up again
@@ -758,9 +738,24 @@ int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl)
}
msm_pcie_l1ss_timeout_enable(pci_dev);
- mhi_cntrl->force_m3_done = true;
MHI_LOG("Exited\n");
return 0;
}
+
+int mhi_arch_link_lpm_disable(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+ return msm_pcie_prevent_l1(mhi_dev->pci_dev);
+}
+
+int mhi_arch_link_lpm_enable(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+ msm_pcie_allow_l1(mhi_dev->pci_dev);
+
+ return 0;
+}
diff --git a/drivers/bus/mhi/controllers/mhi_qcom.c b/drivers/bus/mhi/controllers/mhi_qcom.c
index 292a4fc5..1257338 100644
--- a/drivers/bus/mhi/controllers/mhi_qcom.c
+++ b/drivers/bus/mhi/controllers/mhi_qcom.c
@@ -71,9 +71,6 @@ void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
pm_runtime_dont_use_autosuspend(&pci_dev->dev);
pm_runtime_disable(&pci_dev->dev);
- /* reset counter for lpm state changes */
- mhi_dev->lpm_disable_depth = 0;
-
pci_free_irq_vectors(pci_dev);
kfree(mhi_cntrl->irq);
mhi_cntrl->irq = NULL;
@@ -448,89 +445,13 @@ static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
/* disable PCIe L1 */
static int mhi_lpm_disable(struct mhi_controller *mhi_cntrl, void *priv)
{
- struct mhi_dev *mhi_dev = priv;
- struct pci_dev *pci_dev = mhi_dev->pci_dev;
- int lnkctl = pci_dev->pcie_cap + PCI_EXP_LNKCTL;
- u8 val;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&mhi_dev->lpm_lock, flags);
-
- /* L1 is already disabled */
- if (mhi_dev->lpm_disable_depth) {
- mhi_dev->lpm_disable_depth++;
- goto lpm_disable_exit;
- }
-
- ret = pci_read_config_byte(pci_dev, lnkctl, &val);
- if (ret) {
- MHI_ERR("Error reading LNKCTL, ret:%d\n", ret);
- goto lpm_disable_exit;
- }
-
- /* L1 is not supported, do not increment lpm_disable_depth */
- if (unlikely(!(val & PCI_EXP_LNKCTL_ASPM_L1)))
- goto lpm_disable_exit;
-
- val &= ~PCI_EXP_LNKCTL_ASPM_L1;
- ret = pci_write_config_byte(pci_dev, lnkctl, val);
- if (ret) {
- MHI_ERR("Error writing LNKCTL to disable LPM, ret:%d\n", ret);
- goto lpm_disable_exit;
- }
-
- mhi_dev->lpm_disable_depth++;
-
-lpm_disable_exit:
- spin_unlock_irqrestore(&mhi_dev->lpm_lock, flags);
-
- return ret;
+ return mhi_arch_link_lpm_disable(mhi_cntrl);
}
/* enable PCIe L1 */
static int mhi_lpm_enable(struct mhi_controller *mhi_cntrl, void *priv)
{
- struct mhi_dev *mhi_dev = priv;
- struct pci_dev *pci_dev = mhi_dev->pci_dev;
- int lnkctl = pci_dev->pcie_cap + PCI_EXP_LNKCTL;
- u8 val;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&mhi_dev->lpm_lock, flags);
-
- /*
- * Exit if L1 is not supported or is already disabled or
- * decrementing lpm_disable_depth still keeps it above 0
- */
- if (!mhi_dev->lpm_disable_depth)
- goto lpm_enable_exit;
-
- if (mhi_dev->lpm_disable_depth > 1) {
- mhi_dev->lpm_disable_depth--;
- goto lpm_enable_exit;
- }
-
- ret = pci_read_config_byte(pci_dev, lnkctl, &val);
- if (ret) {
- MHI_ERR("Error reading LNKCTL, ret:%d\n", ret);
- goto lpm_enable_exit;
- }
-
- val |= PCI_EXP_LNKCTL_ASPM_L1;
- ret = pci_write_config_byte(pci_dev, lnkctl, val);
- if (ret) {
- MHI_ERR("Error writing LNKCTL to enable LPM, ret:%d\n", ret);
- goto lpm_enable_exit;
- }
-
- mhi_dev->lpm_disable_depth = 0;
-
-lpm_enable_exit:
- spin_unlock_irqrestore(&mhi_dev->lpm_lock, flags);
-
- return ret;
+ return mhi_arch_link_lpm_enable(mhi_cntrl);
}
void mhi_qcom_store_hwinfo(struct mhi_controller *mhi_cntrl)
@@ -576,10 +497,6 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl)
mhi_cntrl->ee = 0;
mhi_cntrl->power_down = false;
- ret = mhi_arch_power_up(mhi_cntrl);
- if (ret)
- return ret;
-
ret = mhi_async_power_up(mhi_cntrl);
/* Update modem serial Info */
@@ -773,8 +690,6 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev)
mhi_cntrl->iova_start = memblock_start_of_DRAM();
mhi_cntrl->iova_stop = memblock_end_of_DRAM();
- mhi_cntrl->need_force_m3 = true;
-
/* setup host support for SFR retreival */
if (of_property_read_bool(of_node, "mhi,sfr-support"))
mhi_cntrl->sfr_len = MHI_MAX_SFR_LEN;
@@ -815,7 +730,6 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev)
}
mhi_dev->pci_dev = pci_dev;
- spin_lock_init(&mhi_dev->lpm_lock);
/* setup power management apis */
mhi_cntrl->status_cb = mhi_status_cb;
diff --git a/drivers/bus/mhi/controllers/mhi_qcom.h b/drivers/bus/mhi/controllers/mhi_qcom.h
index be756e4..e1d58a8 100644
--- a/drivers/bus/mhi/controllers/mhi_qcom.h
+++ b/drivers/bus/mhi/controllers/mhi_qcom.h
@@ -50,7 +50,6 @@ struct mhi_dev {
int resn;
void *arch_info;
bool powered_on;
- bool mdm_state;
dma_addr_t iova_start;
dma_addr_t iova_stop;
enum mhi_suspend_mode suspend_mode;
@@ -58,10 +57,6 @@ struct mhi_dev {
/* hardware info */
u32 serial_num;
u32 oem_pk_hash[MHI_BHI_OEMPKHASH_SEG];
-
- unsigned int lpm_disable_depth;
- /* lock to toggle low power modes */
- spinlock_t lpm_lock;
};
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
@@ -71,8 +66,9 @@ void mhi_reg_write_work(struct work_struct *w);
#ifdef CONFIG_ARCH_QCOM
+int mhi_arch_link_lpm_disable(struct mhi_controller *mhi_cntrl);
+int mhi_arch_link_lpm_enable(struct mhi_controller *mhi_cntrl);
void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl);
-int mhi_arch_power_up(struct mhi_controller *mhi_cntrl);
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl);
int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl);
@@ -99,13 +95,18 @@ static inline int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl)
return 0;
}
-static inline int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
+static inline void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline int mhi_arch_link_lpm_disable(struct mhi_controller *mhi_cntrl)
{
return 0;
}
-static inline void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl)
+static inline int mhi_arch_link_lpm_enable(struct mhi_controller *mhi_cntrl)
{
+ return 0;
}
#endif
diff --git a/drivers/bus/mhi/core/mhi_boot.c b/drivers/bus/mhi/core/mhi_boot.c
index 896d517..32eb4da 100644
--- a/drivers/bus/mhi/core/mhi_boot.c
+++ b/drivers/bus/mhi/core/mhi_boot.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -319,6 +319,9 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
mhi_buf->len);
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
+ if (unlikely(!mhi_cntrl->sequence_id))
+ mhi_cntrl->sequence_id = 1;
+
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
mhi_cntrl->sequence_id);
@@ -381,6 +384,9 @@ static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
lower_32_bits(dma_addr));
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
mhi_cntrl->session_id = prandom_u32() & BHI_TXDB_SEQNUM_BMSK;
+ if (unlikely(!mhi_cntrl->session_id))
+ mhi_cntrl->session_id = 1;
+
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGTXDB,
mhi_cntrl->session_id);
read_unlock_bh(pm_lock);
diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c
index b4e568c..20bb8f3 100644
--- a/drivers/bus/mhi/core/mhi_init.c
+++ b/drivers/bus/mhi/core/mhi_init.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -96,6 +96,57 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state)
return mhi_pm_state_str[index];
}
+static ssize_t time_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mhi_device *mhi_dev = to_mhi_device(dev);
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
+ u64 t_host, t_device;
+ int ret;
+
+ ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
+ if (ret) {
+ MHI_ERR("Failed to obtain time, ret:%d\n", ret);
+ return ret;
+ }
+
+ return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n",
+ t_host, t_device);
+}
+static DEVICE_ATTR_RO(time);
+
+static ssize_t time_us_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mhi_device *mhi_dev = to_mhi_device(dev);
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
+ u64 t_host, t_device;
+ int ret;
+
+ ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
+ if (ret) {
+ MHI_ERR("Failed to obtain time, ret:%d\n", ret);
+ return ret;
+ }
+
+ return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n",
+ LOCAL_TICKS_TO_US(t_host),
+ REMOTE_TICKS_TO_US(t_device));
+}
+static DEVICE_ATTR_RO(time_us);
+
+static struct attribute *mhi_tsync_attrs[] = {
+ &dev_attr_time.attr,
+ &dev_attr_time_us.attr,
+ NULL,
+};
+
+static const struct attribute_group mhi_tsync_group = {
+ .attrs = mhi_tsync_attrs,
+};
+
static ssize_t log_level_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -197,15 +248,36 @@ static const struct attribute_group mhi_sysfs_group = {
.attrs = mhi_sysfs_attrs,
};
-int mhi_create_sysfs(struct mhi_controller *mhi_cntrl)
+void mhi_create_sysfs(struct mhi_controller *mhi_cntrl)
{
- return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
- &mhi_sysfs_group);
+ sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group);
+ if (mhi_cntrl->mhi_tsync)
+ sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
+ &mhi_tsync_group);
}
void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl)
{
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
+ struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
+ struct tsync_node *tsync, *tmp;
+
+ if (mhi_tsync) {
+ mutex_lock(&mhi_cntrl->tsync_mutex);
+ sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj,
+ &mhi_tsync_group);
+
+ spin_lock(&mhi_tsync->lock);
+ list_for_each_entry_safe(tsync, tmp, &mhi_tsync->head, node) {
+ list_del(&tsync->node);
+ kfree(tsync);
+ }
+ spin_unlock(&mhi_tsync->lock);
+
+ kfree(mhi_cntrl->mhi_tsync);
+ mhi_cntrl->mhi_tsync = NULL;
+ mutex_unlock(&mhi_cntrl->tsync_mutex);
+ }
sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_sysfs_group);
@@ -234,6 +306,21 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
return 0;
}
+/* MHI protocol require transfer ring to be aligned to ring length */
+static int mhi_alloc_aligned_ring_uncached(
+ struct mhi_controller *mhi_cntrl, struct mhi_ring *ring, u64 len)
+{
+ ring->alloc_size = len + (len - 1);
+ ring->pre_aligned = mhi_alloc_uncached(mhi_cntrl, ring->alloc_size,
+ &ring->dma_handle, GFP_KERNEL);
+ if (!ring->pre_aligned)
+ return -ENOMEM;
+
+ ring->iommu_base = (ring->dma_handle + (len - 1)) & ~(len - 1);
+ ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle);
+ return 0;
+}
+
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
{
int i;
@@ -318,7 +405,11 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
continue;
ring = &mhi_event->ring;
- mhi_free_coherent(mhi_cntrl, ring->alloc_size,
+ if (mhi_event->force_uncached)
+ mhi_free_uncached(mhi_cntrl, ring->alloc_size,
+ ring->pre_aligned, ring->dma_handle);
+ else
+ mhi_free_coherent(mhi_cntrl, ring->alloc_size,
ring->pre_aligned, ring->dma_handle);
ring->base = NULL;
ring->iommu_base = 0;
@@ -352,6 +443,11 @@ static int mhi_init_debugfs_mhi_chan_open(struct inode *inode, struct file *fp)
return single_open(fp, mhi_debugfs_mhi_chan_show, inode->i_private);
}
+static int mhi_init_debugfs_mhi_vote_open(struct inode *inode, struct file *fp)
+{
+ return single_open(fp, mhi_debugfs_mhi_vote_show, inode->i_private);
+}
+
static const struct file_operations debugfs_state_ops = {
.open = mhi_init_debugfs_mhi_states_open,
.release = single_release,
@@ -370,6 +466,12 @@ static const struct file_operations debugfs_chan_ops = {
.read = seq_read,
};
+static const struct file_operations debugfs_vote_ops = {
+ .open = mhi_init_debugfs_mhi_vote_open,
+ .release = single_release,
+ .read = seq_read,
+};
+
DEFINE_DEBUGFS_ATTRIBUTE(debugfs_trigger_reset_fops, NULL,
mhi_debugfs_trigger_reset, "%llu\n");
@@ -395,6 +497,8 @@ void mhi_init_debugfs(struct mhi_controller *mhi_cntrl)
&debugfs_ev_ops);
debugfs_create_file_unsafe("chan", 0444, dentry, mhi_cntrl,
&debugfs_chan_ops);
+ debugfs_create_file_unsafe("vote", 0444, dentry, mhi_cntrl,
+ &debugfs_vote_ops);
debugfs_create_file_unsafe("reset", 0444, dentry, mhi_cntrl,
&debugfs_trigger_reset_fops);
mhi_cntrl->dentry = dentry;
@@ -474,7 +578,12 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
ring->el_size = sizeof(struct mhi_tre);
ring->len = ring->el_size * ring->elements;
- ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len);
+ if (mhi_event->force_uncached)
+ ret = mhi_alloc_aligned_ring_uncached(mhi_cntrl, ring,
+ ring->len);
+ else
+ ret = mhi_alloc_aligned_ring(mhi_cntrl, ring,
+ ring->len);
if (ret)
goto error_alloc_er;
@@ -535,7 +644,11 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
if (mhi_event->offload_ev)
continue;
- mhi_free_coherent(mhi_cntrl, ring->alloc_size,
+ if (mhi_event->force_uncached)
+ mhi_free_uncached(mhi_cntrl, ring->alloc_size,
+ ring->pre_aligned, ring->dma_handle);
+ else
+ mhi_free_coherent(mhi_cntrl, ring->alloc_size,
ring->pre_aligned, ring->dma_handle);
}
mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->er_ctxt) *
@@ -569,42 +682,32 @@ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl,
return -ENOENT;
}
-int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
+static int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
{
struct mhi_timesync *mhi_tsync;
- u32 time_offset, db_offset;
- int ret;
-
- read_lock_bh(&mhi_cntrl->pm_lock);
-
- if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
- ret = -EIO;
- goto exit_timesync;
- }
-
- ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID,
- &time_offset);
- if (ret) {
- MHI_LOG("No timesync capability found\n");
- goto exit_timesync;
- }
-
- read_unlock_bh(&mhi_cntrl->pm_lock);
+ u32 time_offset, time_cfg_offset;
+ int ret, er_index;
if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable ||
!mhi_cntrl->lpm_enable)
return -EINVAL;
+ ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID,
+ &time_offset);
+ if (ret) {
+ MHI_LOG("No timesync capability found\n");
+ return ret;
+ }
+
mhi_cntrl->local_timer_freq = arch_timer_get_cntfrq();
- /* register method supported */
- mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL);
+ /* register method is supported */
+ mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_ATOMIC);
if (!mhi_tsync)
return -ENOMEM;
spin_lock_init(&mhi_tsync->lock);
INIT_LIST_HEAD(&mhi_tsync->head);
- init_completion(&mhi_tsync->completion);
/* save time_offset for obtaining time */
MHI_LOG("TIME OFFS:0x%x\n", time_offset);
@@ -613,61 +716,20 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
mhi_cntrl->mhi_tsync = mhi_tsync;
- ret = mhi_create_timesync_sysfs(mhi_cntrl);
- if (unlikely(ret)) {
- /* kernel method still work */
- MHI_ERR("Failed to create timesync sysfs nodes\n");
- }
-
- read_lock_bh(&mhi_cntrl->pm_lock);
-
- if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
- ret = -EIO;
- goto exit_timesync;
- }
-
- /* get DB offset if supported, else return */
- ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,
- time_offset + TIMESYNC_DB_OFFSET, &db_offset);
- if (ret || !db_offset) {
- ret = 0;
- goto exit_timesync;
- }
-
- MHI_LOG("TIMESYNC_DB OFFS:0x%x\n", db_offset);
- mhi_tsync->db = mhi_cntrl->regs + db_offset;
-
- read_unlock_bh(&mhi_cntrl->pm_lock);
-
- /* get time-sync event ring configuration */
- ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE);
- if (ret < 0) {
+ /* get timesync event ring configuration */
+ er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE);
+ if (er_index < 0) {
MHI_LOG("Could not find timesync event ring\n");
- return ret;
+ return er_index;
}
- mhi_tsync->er_index = ret;
+ time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET;
- ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG);
- if (ret) {
- MHI_ERR("Failed to send time sync cfg cmd\n");
- return ret;
- }
-
- ret = wait_for_completion_timeout(&mhi_tsync->completion,
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
-
- if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) {
- MHI_ERR("Failed to get time cfg cmd completion\n");
- return -EIO;
- }
+ /* advertise host support */
+ mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, time_cfg_offset,
+ MHI_TIMESYNC_DB_SETUP(er_index));
return 0;
-
-exit_timesync:
- read_unlock_bh(&mhi_cntrl->pm_lock);
-
- return ret;
}
int mhi_init_sfr(struct mhi_controller *mhi_cntrl)
@@ -832,7 +894,8 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0);
mhi_cntrl->wake_set = false;
- /* setup bw scale db */
+ /* setup special purpose doorbells (timesync, bw scale) */
+ mhi_cntrl->tsync_db = base + val + (8 * MHI_TIMESYNC_CHAN_DB);
mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB);
/* setup channel db addresses */
@@ -865,8 +928,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
reg_info[i].mask, reg_info[i].shift,
reg_info[i].val);
- /* setup bandwidth scaling features */
+ /* setup special purpose features such as timesync or bw scaling */
mhi_init_bw_scale(mhi_cntrl);
+ mhi_init_timesync(mhi_cntrl);
return 0;
}
@@ -1018,7 +1082,7 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl,
if (!mhi_cntrl->mhi_event)
return -ENOMEM;
- INIT_LIST_HEAD(&mhi_cntrl->lp_ev_rings);
+ INIT_LIST_HEAD(&mhi_cntrl->sp_ev_rings);
/* populate ev ring */
mhi_event = mhi_cntrl->mhi_event;
@@ -1028,6 +1092,10 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl,
continue;
mhi_event->er_index = i++;
+
+ mhi_event->force_uncached = of_property_read_bool(child,
+ "mhi,force-uncached");
+
ret = of_property_read_u32(child, "mhi,num-elements",
(u32 *)&mhi_event->ring.elements);
if (ret)
@@ -1101,13 +1169,13 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl,
"mhi,offload");
/*
- * low priority events are handled in a separate worker thread
+ * special purpose events are handled in a separate kthread
* to allow for sleeping functions to be called.
*/
if (!mhi_event->offload_ev) {
- if (IS_MHI_ER_PRIORITY_LOW(mhi_event))
+ if (IS_MHI_ER_PRIORITY_SPECIAL(mhi_event))
list_add_tail(&mhi_event->node,
- &mhi_cntrl->lp_ev_rings);
+ &mhi_cntrl->sp_ev_rings);
else
mhi_event->request_irq = true;
}
@@ -1393,9 +1461,15 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
spin_lock_init(&mhi_cntrl->transition_lock);
spin_lock_init(&mhi_cntrl->wlock);
INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker);
- INIT_WORK(&mhi_cntrl->low_priority_worker, mhi_low_priority_worker);
init_waitqueue_head(&mhi_cntrl->state_event);
+ mhi_cntrl->special_wq = alloc_ordered_workqueue("mhi_special_w",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI);
+ if (!mhi_cntrl->special_wq)
+ goto error_alloc_cmd;
+
+ INIT_WORK(&mhi_cntrl->special_work, mhi_special_purpose_work);
+
mhi_cmd = mhi_cntrl->mhi_cmd;
for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++)
spin_lock_init(&mhi_cmd->lock);
@@ -1408,7 +1482,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
mhi_event->mhi_cntrl = mhi_cntrl;
spin_lock_init(&mhi_event->lock);
- if (IS_MHI_ER_PRIORITY_LOW(mhi_event))
+ if (IS_MHI_ER_PRIORITY_SPECIAL(mhi_event))
continue;
if (mhi_event->data_type == MHI_ER_CTRL_ELEMENT_TYPE)
@@ -1468,6 +1542,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
}
mhi_dev->dev_type = MHI_CONTROLLER_TYPE;
+ mhi_dev->chan_name = mhi_cntrl->name;
mhi_dev->mhi_cntrl = mhi_cntrl;
dev_set_name(&mhi_dev->dev, "%04x_%02u.%02u.%02u", mhi_dev->dev_id,
mhi_dev->domain, mhi_dev->bus, mhi_dev->slot);
@@ -1516,6 +1591,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
error_alloc_dev:
kfree(mhi_cntrl->mhi_cmd);
+ destroy_workqueue(mhi_cntrl->special_wq);
error_alloc_cmd:
vfree(mhi_cntrl->mhi_chan);
@@ -1829,10 +1905,6 @@ static int mhi_driver_remove(struct device *dev)
mutex_unlock(&mhi_chan->mutex);
}
-
- if (mhi_cntrl->tsync_dev == mhi_dev)
- mhi_cntrl->tsync_dev = NULL;
-
/* relinquish any pending votes for device */
while (atomic_read(&mhi_dev->dev_vote))
mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h
index e4c27fd2..d45bb85 100644
--- a/drivers/bus/mhi/core/mhi_internal.h
+++ b/drivers/bus/mhi/core/mhi_internal.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#ifndef _MHI_INT_H
#define _MHI_INT_H
@@ -141,14 +141,14 @@ extern struct bus_type mhi_bus_type;
#define CAP_NEXT_CAP_SHIFT (12)
/* MHI Timesync offsets */
-#define TIMESYNC_CFG_OFFSET (0x00)
-#define TIMESYNC_CFG_CAPID_MASK (CAP_CAPID_MASK)
-#define TIMESYNC_CFG_CAPID_SHIFT (CAP_CAPID_SHIFT)
-#define TIMESYNC_CFG_NEXT_OFF_MASK (CAP_NEXT_CAP_MASK)
-#define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT)
-#define TIMESYNC_CFG_NUMCMD_MASK (0xFF)
-#define TIMESYNC_CFG_NUMCMD_SHIFT (0)
-#define TIMESYNC_DB_OFFSET (0x4)
+#define TIMESYNC_CFG_OFFSET (0x04)
+#define TIMESYNC_CFG_ENABLED_MASK (0x80000000)
+#define TIMESYNC_CFG_ENABLED_SHIFT (31)
+#define TIMESYNC_CFG_CHAN_DB_ID_MASK (0x0000FF00)
+#define TIMESYNC_CFG_CHAN_DB_ID_SHIFT (8)
+#define TIMESYNC_CFG_ER_ID_MASK (0x000000FF)
+#define TIMESYNC_CFG_ER_ID_SHIFT (0)
+
#define TIMESYNC_TIME_LOW_OFFSET (0x8)
#define TIMESYNC_TIME_HIGH_OFFSET (0xC)
@@ -325,12 +325,6 @@ enum mhi_cmd_type {
#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \
(MHI_CMD_TYPE_START << 16))
-/* time sync cfg command */
-#define MHI_TRE_CMD_TSYNC_CFG_PTR (0)
-#define MHI_TRE_CMD_TSYNC_CFG_DWORD0 (0)
-#define MHI_TRE_CMD_TSYNC_CFG_DWORD1(er) ((MHI_CMD_TYPE_TSYNC << 16) | \
- (er << 24))
-
/* subsystem failure reason cfg command */
#define MHI_TRE_CMD_SFR_CFG_PTR(ptr) (ptr)
#define MHI_TRE_CMD_SFR_CFG_DWORD0(len) (len)
@@ -351,6 +345,7 @@ enum mhi_cmd_type {
#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0])
+#define MHI_TRE_GET_EV_TSYNC_UNIT(tre) (((tre)->dword[1] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr)
#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr)
#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF)
@@ -375,7 +370,6 @@ enum MHI_CMD {
MHI_CMD_RESET_CHAN,
MHI_CMD_START_CHAN,
MHI_CMD_STOP_CHAN,
- MHI_CMD_TIMSYNC_CFG,
MHI_CMD_SFR_CFG,
};
@@ -530,10 +524,16 @@ enum MHI_XFER_TYPE {
#define NR_OF_CMD_RINGS (1)
#define CMD_EL_PER_RING (128)
#define PRIMARY_CMD_RING (0)
+#define MHI_TIMESYNC_CHAN_DB (125)
#define MHI_BW_SCALE_CHAN_DB (126)
#define MHI_DEV_WAKE_DB (127)
#define MHI_MAX_MTU (0xffff)
+#define MHI_TIMESYNC_DB_SETUP(er_index) ((MHI_TIMESYNC_CHAN_DB << \
+ TIMESYNC_CFG_CHAN_DB_ID_SHIFT) & TIMESYNC_CFG_CHAN_DB_ID_MASK | \
+ (1 << TIMESYNC_CFG_ENABLED_SHIFT) & TIMESYNC_CFG_ENABLED_MASK | \
+ ((er_index) << TIMESYNC_CFG_ER_ID_SHIFT) & TIMESYNC_CFG_ER_ID_MASK)
+
#define MHI_BW_SCALE_SETUP(er_index) ((MHI_BW_SCALE_CHAN_DB << \
BW_SCALE_CFG_CHAN_DB_ID_SHIFT) & BW_SCALE_CFG_CHAN_DB_ID_MASK | \
(1 << BW_SCALE_CFG_ENABLED_SHIFT) & BW_SCALE_CFG_ENABLED_MASK | \
@@ -550,10 +550,10 @@ enum MHI_ER_TYPE {
enum mhi_er_priority {
MHI_ER_PRIORITY_HIGH,
MHI_ER_PRIORITY_MEDIUM,
- MHI_ER_PRIORITY_LOW,
+ MHI_ER_PRIORITY_SPECIAL,
};
-#define IS_MHI_ER_PRIORITY_LOW(ev) (ev->priority >= MHI_ER_PRIORITY_LOW)
+#define IS_MHI_ER_PRIORITY_SPECIAL(ev) (ev->priority >= MHI_ER_PRIORITY_SPECIAL)
#define IS_MHI_ER_PRIORITY_HIGH(ev) (ev->priority == MHI_ER_PRIORITY_HIGH)
enum mhi_er_data_type {
@@ -665,6 +665,9 @@ struct mhi_event {
struct mhi_event *mhi_event,
u32 event_quota);
struct mhi_controller *mhi_cntrl;
+ struct mhi_tre last_cached_tre;
+ u64 last_dev_rp;
+ bool force_uncached;
};
struct mhi_chan {
@@ -706,11 +709,15 @@ struct mhi_chan {
struct completion completion;
rwlock_t lock;
struct list_head node;
+
+ /* stats */
+ u64 mode_change;
};
struct tsync_node {
struct list_head node;
u32 sequence;
+ u32 int_sequence;
u64 local_time;
u64 remote_time;
struct mhi_device *mhi_dev;
@@ -719,11 +726,8 @@ struct tsync_node {
};
struct mhi_timesync {
- u32 er_index;
- void __iomem *db;
void __iomem *time_reg;
- enum MHI_EV_CCS ccs;
- struct completion completion;
+ u32 int_sequence;
spinlock_t lock; /* list protection */
struct list_head head;
};
@@ -749,6 +753,7 @@ extern struct mhi_bus mhi_bus;
struct mhi_controller *find_mhi_controller_by_name(const char *name);
/* debug fs related functions */
+int mhi_debugfs_mhi_vote_show(struct seq_file *m, void *d);
int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d);
int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d);
int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d);
@@ -769,8 +774,8 @@ int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
enum MHI_ST_TRANSITION state);
void mhi_pm_st_worker(struct work_struct *work);
void mhi_fw_load_worker(struct work_struct *work);
+void mhi_special_purpose_work(struct work_struct *work);
void mhi_process_sys_err(struct mhi_controller *mhi_cntrl);
-void mhi_low_priority_worker(struct work_struct *work);
int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
void mhi_ctrl_ev_task(unsigned long data);
int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
@@ -834,11 +839,8 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability,
u32 *offset);
void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr);
-int mhi_init_timesync(struct mhi_controller *mhi_cntrl);
int mhi_init_sfr(struct mhi_controller *mhi_cntrl);
-int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl);
-void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);
-int mhi_create_sysfs(struct mhi_controller *mhi_cntrl);
+void mhi_create_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl);
int mhi_early_notify_device(struct device *dev, void *data);
void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl,
@@ -876,6 +878,29 @@ static inline void mhi_free_coherent(struct mhi_controller *mhi_cntrl,
dma_free_coherent(mhi_cntrl->dev, size, vaddr, dma_handle);
}
+static inline void *mhi_alloc_uncached(struct mhi_controller *mhi_cntrl,
+ size_t size,
+ dma_addr_t *dma_handle,
+ gfp_t gfp)
+{
+ void *buf = dma_alloc_attrs(mhi_cntrl->dev, size, dma_handle, gfp,
+ DMA_ATTR_FORCE_NON_COHERENT);
+
+ if (buf)
+ atomic_add(size, &mhi_cntrl->alloc_size);
+
+ return buf;
+}
+static inline void mhi_free_uncached(struct mhi_controller *mhi_cntrl,
+ size_t size,
+ void *vaddr,
+ dma_addr_t dma_handle)
+{
+ atomic_sub(size, &mhi_cntrl->alloc_size);
+ dma_free_attrs(mhi_cntrl->dev, size, vaddr, dma_handle,
+ DMA_ATTR_FORCE_NON_COHERENT);
+}
+
static inline void *mhi_alloc_contig_coherent(
struct mhi_controller *mhi_cntrl,
size_t size, dma_addr_t *dma_handle,
@@ -946,22 +971,9 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev);
irqreturn_t mhi_intvec_handlr(int irq_number, void *dev);
void mhi_ev_task(unsigned long data);
-#ifdef CONFIG_MHI_DEBUG
-
#define MHI_ASSERT(cond, fmt, ...) do { \
if (cond) \
panic(fmt); \
} while (0)
-#else
-
-#define MHI_ASSERT(cond, fmt, ...) do { \
- if (cond) { \
- MHI_ERR(fmt); \
- WARN_ON(cond); \
- } \
-} while (0)
-
-#endif
-
#endif /* _MHI_INT_H */
diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c
index 0655924..c097c38 100644
--- a/drivers/bus/mhi/core/mhi_main.c
+++ b/drivers/bus/mhi/core/mhi_main.c
@@ -560,9 +560,9 @@ int mhi_queue_dma(struct mhi_device *mhi_dev,
mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(mhi_chan->bei, 1, 0, 0);
}
- MHI_VERB("chan:%d WP:0x%llx TRE:0x%llx 0x%08x 0x%08x\n", mhi_chan->chan,
- (u64)mhi_to_physical(tre_ring, mhi_tre), mhi_tre->ptr,
- mhi_tre->dword[0], mhi_tre->dword[1]);
+ MHI_VERB("chan:%d WP:0x%llx TRE:0x%llx 0x%08x 0x%08x rDB %d\n",
+ mhi_chan->chan, (u64)mhi_to_physical(tre_ring, mhi_tre),
+ mhi_tre->ptr, mhi_tre->dword[0], mhi_tre->dword[1], ring_db);
/* increment WP */
mhi_add_ring_element(mhi_cntrl, tre_ring);
@@ -852,38 +852,6 @@ int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl)
&mhi_tsync_group);
}
-static void mhi_create_time_sync_dev(struct mhi_controller *mhi_cntrl)
-{
- struct mhi_device *mhi_dev;
- int ret;
-
- if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee))
- return;
-
- mhi_dev = mhi_alloc_device(mhi_cntrl);
- if (!mhi_dev)
- return;
-
- mhi_dev->dev_type = MHI_TIMESYNC_TYPE;
- mhi_dev->chan_name = "TIME_SYNC";
- dev_set_name(&mhi_dev->dev, "%04x_%02u.%02u.%02u_%s", mhi_dev->dev_id,
- mhi_dev->domain, mhi_dev->bus, mhi_dev->slot,
- mhi_dev->chan_name);
-
- /* add if there is a matching DT node */
- mhi_assign_of_node(mhi_cntrl, mhi_dev);
-
- ret = device_add(&mhi_dev->dev);
- if (ret) {
- MHI_ERR("Failed to register dev for chan:%s\n",
- mhi_dev->chan_name);
- mhi_dealloc_device(mhi_cntrl, mhi_dev);
- return;
- }
-
- mhi_cntrl->tsync_dev = mhi_dev;
-}
-
/* bind mhi channels into mhi devices */
void mhi_create_devices(struct mhi_controller *mhi_cntrl)
{
@@ -892,13 +860,6 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl)
struct mhi_device *mhi_dev;
int ret;
- /*
- * we need to create time sync device before creating other
- * devices, because client may try to capture time during
- * clint probe.
- */
- mhi_create_time_sync_dev(mhi_cntrl);
-
mhi_chan = mhi_cntrl->mhi_chan;
for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
if (!mhi_chan->configured || mhi_chan->mhi_dev ||
@@ -990,6 +951,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
unsigned long flags = 0;
bool ring_db = true;
int n_free_tre, n_queued_tre;
+ unsigned long rflags;
ev_code = MHI_TRE_GET_EV_CODE(event);
buf_ring = &mhi_chan->buf_ring;
@@ -1048,7 +1010,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
result.buf_addr = buf_info->cb_buf;
- result.bytes_xferd = xfer_len;
+ result.bytes_xferd = min_t(u16, xfer_len,
+ buf_info->len);
mhi_del_ring_element(mhi_cntrl, buf_ring);
mhi_del_ring_element(mhi_cntrl, tre_ring);
local_rp = tre_ring->rp;
@@ -1078,12 +1041,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
break;
} /* CC_EOT */
case MHI_EV_CC_OOB:
- case MHI_EV_CC_DB_MODE:
- {
- unsigned long flags;
-
- MHI_VERB("DB_MODE/OOB Detected chan %d.\n", mhi_chan->chan);
mhi_chan->db_cfg.db_mode = true;
+ mhi_chan->mode_change++;
/*
* on RSC channel IPA HW has a minimum credit requirement before
@@ -1097,14 +1056,27 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
ring_db = false;
}
- read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
+ MHI_VERB("OOB_MODE chan %d ring_db %d\n", mhi_chan->chan,
+ ring_db);
+
+ read_lock_irqsave(&mhi_cntrl->pm_lock, rflags);
if (tre_ring->wp != tre_ring->rp &&
- MHI_DB_ACCESS_VALID(mhi_cntrl) && ring_db) {
+ MHI_DB_ACCESS_VALID(mhi_cntrl) && ring_db)
mhi_ring_chan_db(mhi_cntrl, mhi_chan);
- }
- read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags);
+ read_unlock_irqrestore(&mhi_cntrl->pm_lock, rflags);
break;
- }
+ case MHI_EV_CC_DB_MODE:
+ MHI_VERB("DB_MODE chan %d.\n", mhi_chan->chan);
+ mhi_chan->db_cfg.db_mode = true;
+ mhi_chan->mode_change++;
+
+ read_lock_irqsave(&mhi_cntrl->pm_lock, rflags);
+ if (tre_ring->wp != tre_ring->rp &&
+ MHI_DB_ACCESS_VALID(mhi_cntrl))
+ mhi_ring_chan_db(mhi_cntrl, mhi_chan);
+
+ read_unlock_irqrestore(&mhi_cntrl->pm_lock, rflags);
+ break;
case MHI_EV_CC_BAD_TRE:
MHI_ASSERT(1, "Received BAD TRE event for ring");
break;
@@ -1198,7 +1170,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
struct mhi_ring *mhi_ring = &cmd_ring->ring;
struct mhi_tre *cmd_pkt;
struct mhi_chan *mhi_chan;
- struct mhi_timesync *mhi_tsync;
struct mhi_sfr_info *sfr_info;
enum mhi_cmd_type type;
u32 chan;
@@ -1211,11 +1182,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
type = MHI_TRE_GET_CMD_TYPE(cmd_pkt);
switch (type) {
- case MHI_CMD_TYPE_TSYNC:
- mhi_tsync = mhi_cntrl->mhi_tsync;
- mhi_tsync->ccs = MHI_TRE_GET_EV_CODE(tre);
- complete(&mhi_tsync->completion);
- break;
case MHI_CMD_TYPE_SFR_CFG:
sfr_info = mhi_cntrl->mhi_sfr;
sfr_info->ccs = MHI_TRE_GET_EV_CODE(tre);
@@ -1223,6 +1189,10 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
break;
default:
chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
+ if (chan >= mhi_cntrl->max_chan) {
+ MHI_ERR("invalid channel id %u\n", chan);
+ break;
+ }
mhi_chan = &mhi_cntrl->mhi_chan[chan];
write_lock_bh(&mhi_chan->lock);
mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre);
@@ -1328,6 +1298,10 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
TO_MHI_EXEC_STR(event));
switch (event) {
case MHI_EE_SBL:
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ mhi_cntrl->ee = MHI_EE_SBL;
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ wake_up_all(&mhi_cntrl->state_event);
st = MHI_ST_TRANSITION_SBL;
break;
case MHI_EE_WFW:
@@ -1335,13 +1309,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
st = MHI_ST_TRANSITION_MISSION_MODE;
break;
case MHI_EE_RDDM:
- mhi_cntrl->status_cb(mhi_cntrl,
- mhi_cntrl->priv_data,
- MHI_CB_EE_RDDM);
- write_lock_irq(&mhi_cntrl->pm_lock);
- mhi_cntrl->ee = event;
- write_unlock_irq(&mhi_cntrl->pm_lock);
- wake_up_all(&mhi_cntrl->state_event);
break;
default:
MHI_ERR("Unhandled EE event:%s\n",
@@ -1400,7 +1367,16 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
MHI_VERB("Processing Event:0x%llx 0x%08x 0x%08x\n",
local_rp->ptr, local_rp->dword[0], local_rp->dword[1]);
+ mhi_event->last_cached_tre.ptr = local_rp->ptr;
+ mhi_event->last_cached_tre.dword[0] = local_rp->dword[0];
+ mhi_event->last_cached_tre.dword[1] = local_rp->dword[1];
+ mhi_event->last_dev_rp = (u64)dev_rp;
+
chan = MHI_TRE_GET_EV_CHID(local_rp);
+ if (chan >= mhi_cntrl->max_chan) {
+ MHI_ERR("invalid channel id %u\n", chan);
+ continue;
+ }
mhi_chan = &mhi_cntrl->mhi_chan[chan];
if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
@@ -1436,7 +1412,7 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl,
&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
int count = 0;
- u32 sequence;
+ u32 int_sequence, unit;
u64 remote_time;
if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) {
@@ -1458,28 +1434,29 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl,
MHI_ASSERT(type != MHI_PKT_TYPE_TSYNC_EVENT, "!TSYNC event");
- sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp);
+ int_sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp);
+ unit = MHI_TRE_GET_EV_TSYNC_UNIT(local_rp);
remote_time = MHI_TRE_GET_EV_TIME(local_rp);
do {
- spin_lock_irq(&mhi_tsync->lock);
+ spin_lock(&mhi_tsync->lock);
tsync_node = list_first_entry_or_null(&mhi_tsync->head,
struct tsync_node, node);
- MHI_ASSERT(!tsync_node, "Unexpected Event");
-
- if (unlikely(!tsync_node))
+ if (!tsync_node) {
+ spin_unlock(&mhi_tsync->lock);
break;
+ }
list_del(&tsync_node->node);
- spin_unlock_irq(&mhi_tsync->lock);
+ spin_unlock(&mhi_tsync->lock);
/*
* device may not able to process all time sync commands
* host issue and only process last command it receive
*/
- if (tsync_node->sequence == sequence) {
+ if (tsync_node->int_sequence == int_sequence) {
tsync_node->cb_func(tsync_node->mhi_dev,
- sequence,
+ tsync_node->sequence,
tsync_node->local_time,
remote_time);
kfree(tsync_node);
@@ -1522,9 +1499,6 @@ int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl,
goto exit_no_lock;
}
- if (mhi_cntrl->need_force_m3 && !mhi_cntrl->force_m3_done)
- goto exit_no_lock;
-
ret = __mhi_device_get_sync(mhi_cntrl);
if (ret)
goto exit_no_lock;
@@ -1699,14 +1673,21 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
MHI_VERB("Enter\n");
write_lock_irq(&mhi_cntrl->pm_lock);
- if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
- state = mhi_get_mhi_state(mhi_cntrl);
- ee = mhi_cntrl->ee;
- mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
- MHI_LOG("local ee: %s device ee:%s dev_state:%s\n",
- TO_MHI_EXEC_STR(ee),
- TO_MHI_EXEC_STR(mhi_cntrl->ee),
- TO_MHI_STATE_STR(state));
+ if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ goto exit_intvec;
+ }
+
+ state = mhi_get_mhi_state(mhi_cntrl);
+ ee = mhi_get_exec_env(mhi_cntrl);
+ MHI_LOG("local ee:%s device ee:%s dev_state:%s\n",
+ TO_MHI_EXEC_STR(mhi_cntrl->ee),
+ TO_MHI_EXEC_STR(ee),
+ TO_MHI_STATE_STR(state));
+
+ if (mhi_cntrl->power_down) {
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ goto exit_intvec;
}
if (state == MHI_STATE_SYS_ERR) {
@@ -1716,20 +1697,28 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
}
write_unlock_irq(&mhi_cntrl->pm_lock);
- /* if device in rddm don't bother processing sys error */
- if (mhi_cntrl->ee == MHI_EE_RDDM && ee != MHI_EE_DISABLE_TRANSITION) {
- if (mhi_cntrl->ee != ee) {
- mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
- MHI_CB_EE_RDDM);
- wake_up_all(&mhi_cntrl->state_event);
-
- /* notify critical clients with early notifications */
- mhi_control_error(mhi_cntrl);
+ if (ee == MHI_EE_RDDM) {
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ if (mhi_cntrl->ee == MHI_EE_RDDM) {
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ goto exit_intvec;
}
+ mhi_cntrl->ee = MHI_EE_RDDM;
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+
+ MHI_ERR("RDDM event occurred!\n");
+ mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
+ MHI_CB_EE_RDDM);
+ wake_up_all(&mhi_cntrl->state_event);
+
+ /* notify critical clients with early notifications */
+ mhi_control_error(mhi_cntrl);
+
goto exit_intvec;
}
- if (pm_state == MHI_PM_SYS_ERR_DETECT) {
+ /* if device is in RDDM, don't bother processing SYS_ERR */
+ if (ee != MHI_EE_RDDM && pm_state == MHI_PM_SYS_ERR_DETECT) {
wake_up_all(&mhi_cntrl->state_event);
/* for fatal errors, we let controller decide next step */
@@ -1750,14 +1739,20 @@ irqreturn_t mhi_intvec_handlr(int irq_number, void *dev)
{
struct mhi_controller *mhi_cntrl = dev;
+ u32 in_reset = -1;
/* wake up any events waiting for state change */
MHI_VERB("Enter\n");
+ if (unlikely(mhi_cntrl->initiate_mhi_reset)) {
+ mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+ MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, &in_reset);
+ mhi_cntrl->initiate_mhi_reset = !!in_reset;
+ }
wake_up_all(&mhi_cntrl->state_event);
MHI_VERB("Exit\n");
if (MHI_IN_MISSION_MODE(mhi_cntrl->ee))
- schedule_work(&mhi_cntrl->low_priority_worker);
+ queue_work(mhi_cntrl->special_wq, &mhi_cntrl->special_work);
return IRQ_WAKE_THREAD;
}
@@ -1805,12 +1800,6 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0;
cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan);
break;
- case MHI_CMD_TIMSYNC_CFG:
- cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR;
- cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0;
- cmd_tre->dword[1] = MHI_TRE_CMD_TSYNC_CFG_DWORD1
- (mhi_cntrl->mhi_tsync->er_index);
- break;
case MHI_CMD_SFR_CFG:
sfr_info = mhi_cntrl->mhi_sfr;
cmd_tre->ptr = MHI_TRE_CMD_SFR_CFG_PTR
@@ -2241,18 +2230,63 @@ int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d)
chan_ctxt->pollcfg, chan_ctxt->chtype,
chan_ctxt->erindex);
seq_printf(m,
- " base:0x%llx len:0x%llx wp:0x%llx local_rp:0x%llx local_wp:0x%llx db:0x%llx\n",
+ " base:0x%llx len:0x%llx wp:0x%llx local_rp:0x%llx local_wp:0x%llx db:0x%llx mode_change:0x%llx\n",
chan_ctxt->rbase, chan_ctxt->rlen,
chan_ctxt->wp,
mhi_to_physical(ring, ring->rp),
mhi_to_physical(ring, ring->wp),
- mhi_chan->db_cfg.db_val);
+ mhi_chan->db_cfg.db_val,
+ mhi_chan->mode_change);
+ mhi_chan->mode_change = 0;
}
}
return 0;
}
+/* show bus/device votes for a specific device */
+int mhi_device_vote_show(struct device *dev, void *data)
+{
+ struct mhi_device *mhi_dev;
+ struct mhi_controller *mhi_cntrl;
+
+ if (dev->bus != &mhi_bus_type)
+ return 0;
+
+ mhi_dev = to_mhi_device(dev);
+ mhi_cntrl = mhi_dev->mhi_cntrl;
+
+ /* we dont care about timesync or similar special devices */
+ if (mhi_dev->dev_type == MHI_TIMESYNC_TYPE)
+ return 0;
+
+ seq_printf((struct seq_file *)data, "%s: device:%u, bus:%u\n",
+ mhi_dev->chan_name, atomic_read(&mhi_dev->dev_vote),
+ atomic_read(&mhi_dev->bus_vote));
+
+ return 0;
+}
+
+int mhi_debugfs_mhi_vote_show(struct seq_file *m, void *d)
+{
+ struct mhi_controller *mhi_cntrl = m->private;
+ struct mhi_device *mhi_dev;
+
+ if (!mhi_cntrl)
+ return 0;
+
+ mhi_dev = mhi_cntrl->mhi_dev;
+
+ seq_printf(m, "At %llu ns:\n", sched_clock());
+ seq_printf(m, "%s: device:%u, bus:%u\n", mhi_dev->chan_name,
+ atomic_read(&mhi_dev->dev_vote),
+ atomic_read(&mhi_dev->bus_vote));
+
+ device_for_each_child(mhi_cntrl->dev, m, mhi_device_vote_show);
+
+ return 0;
+}
+
/* move channel to start state */
int mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
{
@@ -2502,13 +2536,13 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
/* not all devices support time feature */
if (!mhi_tsync) {
ret = -EIO;
- goto err_unlock;
+ goto error_unlock;
}
/* bring to M0 state */
ret = __mhi_device_get_sync(mhi_cntrl);
if (ret)
- goto err_unlock;
+ goto error_unlock;
read_lock_bh(&mhi_cntrl->pm_lock);
if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
@@ -2517,11 +2551,14 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
ret = -EIO;
goto error_invalid_state;
}
+ read_unlock_bh(&mhi_cntrl->pm_lock);
/* disable link level low power modes */
ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);
- if (ret)
+ if (ret) {
+ read_lock_bh(&mhi_cntrl->pm_lock);
goto error_invalid_state;
+ }
/*
* time critical code to fetch device times,
@@ -2539,10 +2576,11 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data);
+ read_lock_bh(&mhi_cntrl->pm_lock);
error_invalid_state:
mhi_cntrl->wake_put(mhi_cntrl, false);
read_unlock_bh(&mhi_cntrl->pm_lock);
-err_unlock:
+error_unlock:
mutex_unlock(&mhi_cntrl->tsync_mutex);
return ret;
}
@@ -2574,13 +2612,23 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
mutex_lock(&mhi_cntrl->tsync_mutex);
if (!mhi_tsync) {
ret = -EIO;
- goto err_unlock;
+ goto error_unlock;
}
/* tsync db can only be rung in M0 state */
ret = __mhi_device_get_sync(mhi_cntrl);
if (ret)
- goto err_unlock;
+ goto error_unlock;
+
+ read_lock_bh(&mhi_cntrl->pm_lock);
+ if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
+ MHI_ERR("MHI is not in active state, pm_state:%s\n",
+ to_mhi_pm_state_str(mhi_cntrl->pm_state));
+ ret = -EIO;
+ read_unlock_bh(&mhi_cntrl->pm_lock);
+ goto error_no_mem;
+ }
+ read_unlock_bh(&mhi_cntrl->pm_lock);
/*
* technically we can use GFP_KERNEL, but wants to avoid
@@ -2592,24 +2640,26 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
goto error_no_mem;
}
+ mhi_tsync->int_sequence++;
+ if (mhi_tsync->int_sequence == 0xFFFFFFFF)
+ mhi_tsync->int_sequence = 0;
+
tsync_node->sequence = sequence;
+ tsync_node->int_sequence = mhi_tsync->int_sequence;
tsync_node->cb_func = cb_func;
tsync_node->mhi_dev = mhi_dev;
/* disable link level low power modes */
- mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);
-
- read_lock_bh(&mhi_cntrl->pm_lock);
- if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
- MHI_ERR("MHI is not in active state, pm_state:%s\n",
- to_mhi_pm_state_str(mhi_cntrl->pm_state));
- ret = -EIO;
+ ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);
+ if (ret) {
+ MHI_ERR("LPM disable request failed for %s!\n",
+ mhi_dev->chan_name);
goto error_invalid_state;
}
- spin_lock_irq(&mhi_tsync->lock);
+ spin_lock(&mhi_tsync->lock);
list_add_tail(&tsync_node->node, &mhi_tsync->head);
- spin_unlock_irq(&mhi_tsync->lock);
+ spin_unlock(&mhi_tsync->lock);
/*
* time critical code, delay between these two steps should be
@@ -2620,26 +2670,25 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
tsync_node->local_time =
mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data);
- writel_relaxed_no_log(tsync_node->sequence, mhi_tsync->db);
+ writel_relaxed_no_log(tsync_node->int_sequence, mhi_cntrl->tsync_db);
/* write must go thru immediately */
wmb();
local_irq_enable();
preempt_enable();
+ mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data);
+
ret = 0;
error_invalid_state:
if (ret)
kfree(tsync_node);
- read_unlock_bh(&mhi_cntrl->pm_lock);
- mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data);
-
error_no_mem:
read_lock_bh(&mhi_cntrl->pm_lock);
mhi_cntrl->wake_put(mhi_cntrl, false);
read_unlock_bh(&mhi_cntrl->pm_lock);
-err_unlock:
+error_unlock:
mutex_unlock(&mhi_cntrl->tsync_mutex);
return ret;
}
diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c
index 5ea8a86..ab31083 100644
--- a/drivers/bus/mhi/core/mhi_pm.c
+++ b/drivers/bus/mhi/core/mhi_pm.c
@@ -15,6 +15,8 @@
#include <linux/mhi.h>
#include "mhi_internal.h"
+static void mhi_special_events_pending(struct mhi_controller *mhi_cntrl);
+
/*
* Not all MHI states transitions are sync transitions. Linkdown, SSR, and
* shutdown can happen anytime asynchronously. This function will transition to
@@ -165,6 +167,7 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
if (state == MHI_STATE_RESET) {
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1);
+ mhi_cntrl->initiate_mhi_reset = true;
} else {
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
(state << MHICTRL_MHISTATE_SHIFT));
@@ -469,12 +472,16 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
ee = mhi_get_exec_env(mhi_cntrl);
write_unlock_irq(&mhi_cntrl->pm_lock);
- if (!MHI_IN_MISSION_MODE(ee))
+ if (!MHI_IN_MISSION_MODE(ee)) {
+ MHI_ERR("Invalid EE:%s\n", TO_MHI_EXEC_STR(ee));
return -EIO;
+ }
mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
MHI_CB_EE_MISSION_MODE);
+ write_lock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->ee = ee;
+ write_unlock_irq(&mhi_cntrl->pm_lock);
wake_up_all(&mhi_cntrl->state_event);
@@ -518,21 +525,22 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
read_unlock_bh(&mhi_cntrl->pm_lock);
- /* setup support for additional features (SFR, timesync, etc.) */
+ /* setup support for additional features */
mhi_init_sfr(mhi_cntrl);
- mhi_init_timesync(mhi_cntrl);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
mhi_timesync_log(mhi_cntrl);
+ mhi_special_events_pending(mhi_cntrl);
+
+ /* setup sysfs nodes for userspace votes */
+ mhi_create_sysfs(mhi_cntrl);
+
MHI_LOG("Adding new devices\n");
/* add supported devices */
mhi_create_devices(mhi_cntrl);
- /* setup sysfs nodes for userspace votes */
- mhi_create_sysfs(mhi_cntrl);
-
read_lock_bh(&mhi_cntrl->pm_lock);
error_mission_mode:
@@ -580,8 +588,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
mhi_cntrl->ee = MHI_EE_DISABLE_TRANSITION;
mhi_cntrl->dev_state = MHI_STATE_RESET;
}
- /* notify controller of power down regardless of state transitions */
- mhi_cntrl->power_down = true;
write_unlock_irq(&mhi_cntrl->pm_lock);
/* wake up any threads waiting for state transitions */
@@ -597,20 +603,18 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
/* trigger MHI RESET so device will not access host ddr */
if (MHI_REG_ACCESS_VALID(prev_state)) {
- u32 in_reset = -1;
unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);
MHI_LOG("Trigger device into MHI_RESET\n");
+
+ write_lock_irq(&mhi_cntrl->pm_lock);
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
/* wait for reset to be cleared */
ret = wait_event_timeout(mhi_cntrl->state_event,
- mhi_read_reg_field(mhi_cntrl,
- mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT, &in_reset)
- || !in_reset, timeout);
- if ((!ret || in_reset) && cur_state == MHI_PM_SYS_ERR_PROCESS) {
+ !mhi_cntrl->initiate_mhi_reset, timeout);
+ if (!ret && cur_state == MHI_PM_SYS_ERR_PROCESS) {
MHI_CRITICAL("Device failed to exit RESET state\n");
mutex_unlock(&mhi_cntrl->pm_mutex);
return;
@@ -620,7 +624,11 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
* device cleares INTVEC as part of RESET processing,
* re-program it
*/
- mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
+ if (!mhi_cntrl->initiate_mhi_reset)
+ mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi,
+ BHI_INTVEC, 0);
+
+ mhi_cntrl->initiate_mhi_reset = false;
}
MHI_LOG("Waiting for all pending event ring processing to complete\n");
@@ -643,9 +651,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
MHI_LOG("Waiting for all pending threads to complete\n");
wake_up_all(&mhi_cntrl->state_event);
- flush_work(&mhi_cntrl->low_priority_worker);
-
- mhi_cntrl->force_m3_done = false;
+ flush_work(&mhi_cntrl->special_work);
if (sfr_info && sfr_info->buf_addr) {
mhi_free_coherent(mhi_cntrl, sfr_info->len, sfr_info->buf_addr,
@@ -653,9 +659,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
sfr_info->buf_addr = NULL;
}
- /* remove support for time sync */
- mhi_destroy_timesync(mhi_cntrl);
-
mutex_lock(&mhi_cntrl->pm_mutex);
MHI_ASSERT(atomic_read(&mhi_cntrl->dev_wake), "dev_wake != 0");
@@ -785,18 +788,19 @@ int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
return 0;
}
-static void mhi_low_priority_events_pending(struct mhi_controller *mhi_cntrl)
+static void mhi_special_events_pending(struct mhi_controller *mhi_cntrl)
{
struct mhi_event *mhi_event;
- list_for_each_entry(mhi_event, &mhi_cntrl->lp_ev_rings, node) {
+ list_for_each_entry(mhi_event, &mhi_cntrl->sp_ev_rings, node) {
struct mhi_event_ctxt *er_ctxt =
&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
struct mhi_ring *ev_ring = &mhi_event->ring;
spin_lock_bh(&mhi_event->lock);
if (ev_ring->rp != mhi_to_virtual(ev_ring, er_ctxt->rp)) {
- schedule_work(&mhi_cntrl->low_priority_worker);
+ queue_work(mhi_cntrl->special_wq,
+ &mhi_cntrl->special_work);
spin_unlock_bh(&mhi_event->lock);
break;
}
@@ -804,11 +808,11 @@ static void mhi_low_priority_events_pending(struct mhi_controller *mhi_cntrl)
}
}
-void mhi_low_priority_worker(struct work_struct *work)
+void mhi_special_purpose_work(struct work_struct *work)
{
struct mhi_controller *mhi_cntrl = container_of(work,
struct mhi_controller,
- low_priority_worker);
+ special_work);
struct mhi_event *mhi_event;
MHI_VERB("Enter with pm_state:%s MHI_STATE:%s ee:%s\n",
@@ -816,8 +820,8 @@ void mhi_low_priority_worker(struct work_struct *work)
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
- /* check low priority event rings and process events */
- list_for_each_entry(mhi_event, &mhi_cntrl->lp_ev_rings, node)
+ /* check special purpose event rings and process events */
+ list_for_each_entry(mhi_event, &mhi_cntrl->sp_ev_rings, node)
mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX);
}
@@ -832,6 +836,10 @@ void mhi_process_sys_err(struct mhi_controller *mhi_cntrl)
return;
}
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ mhi_cntrl->power_down = true;
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+
mhi_queue_disable_transition(mhi_cntrl, MHI_PM_SYS_ERR_PROCESS);
}
@@ -857,14 +865,9 @@ void mhi_pm_st_worker(struct work_struct *work)
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
write_unlock_irq(&mhi_cntrl->pm_lock);
- if (MHI_IN_PBL(mhi_cntrl->ee))
- mhi_fw_load_handler(mhi_cntrl);
+ mhi_fw_load_handler(mhi_cntrl);
break;
case MHI_ST_TRANSITION_SBL:
- write_lock_irq(&mhi_cntrl->pm_lock);
- mhi_cntrl->ee = MHI_EE_SBL;
- write_unlock_irq(&mhi_cntrl->pm_lock);
- wake_up_all(&mhi_cntrl->state_event);
mhi_create_devices(mhi_cntrl);
break;
case MHI_ST_TRANSITION_MISSION_MODE:
@@ -951,6 +954,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
}
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
+ mhi_cntrl->power_down = false;
mhi_cntrl->pm_state = MHI_PM_POR;
mhi_cntrl->ee = MHI_EE_MAX;
current_ee = mhi_get_exec_env(mhi_cntrl);
@@ -978,6 +982,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
return 0;
error_bhi_offset:
+ mhi_cntrl->power_down = true;
mhi_deinit_free_irq(mhi_cntrl);
error_setup_irq:
@@ -1044,21 +1049,23 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
enum MHI_PM_STATE cur_state;
enum MHI_PM_STATE transition_state = MHI_PM_SHUTDOWN_PROCESS;
+ mutex_lock(&mhi_cntrl->pm_mutex);
+
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ mhi_cntrl->power_down = true;
/* if it's not graceful shutdown, force MHI to a linkdown state */
if (!graceful) {
- mutex_lock(&mhi_cntrl->pm_mutex);
- write_lock_irq(&mhi_cntrl->pm_lock);
cur_state = mhi_tryset_pm_state(mhi_cntrl,
MHI_PM_LD_ERR_FATAL_DETECT);
- write_unlock_irq(&mhi_cntrl->pm_lock);
- mutex_unlock(&mhi_cntrl->pm_mutex);
if (cur_state != MHI_PM_LD_ERR_FATAL_DETECT)
MHI_ERR("Failed to move to state:%s from:%s\n",
to_mhi_pm_state_str(MHI_PM_LD_ERR_FATAL_DETECT),
to_mhi_pm_state_str(mhi_cntrl->pm_state));
-
transition_state = MHI_PM_SHUTDOWN_NO_ACCESS;
}
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+
+ mutex_unlock(&mhi_cntrl->pm_mutex);
mhi_queue_disable_transition(mhi_cntrl, transition_state);
@@ -1214,11 +1221,17 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client)
enum MHI_PM_STATE new_state;
struct mhi_chan *itr, *tmp;
- if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
+ read_lock_bh(&mhi_cntrl->pm_lock);
+ if (mhi_cntrl->pm_state == MHI_PM_DISABLE) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
return -EINVAL;
+ }
- if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
return -EIO;
+ }
+ read_unlock_bh(&mhi_cntrl->pm_lock);
/* do a quick check to see if any pending votes to keep us busy */
if (atomic_read(&mhi_cntrl->pending_pkts)) {
@@ -1246,6 +1259,12 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client)
goto error_suspend;
}
+ /* check pm state before saving it */
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ ret = -EIO;
+ goto error_suspend;
+ }
+
/* anytime after this, we will resume thru runtime pm framework */
MHI_LOG("Allowing Fast M3 transition\n");
@@ -1380,11 +1399,11 @@ int mhi_pm_resume(struct mhi_controller *mhi_cntrl)
/*
* If MHI on host is in suspending/suspended state, we do not process
- * any low priority requests, for example, bandwidth scaling events
- * from the device. Check for low priority event rings and handle the
- * pending events upon resume.
+ * any special purpose requests, for example, bandwidth scaling events
+ * from the device. Check for special purpose event rings and handle
+ * the pending events upon resume.
*/
- mhi_low_priority_events_pending(mhi_cntrl);
+ mhi_special_events_pending(mhi_cntrl);
return 0;
}
@@ -1395,17 +1414,23 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
struct mhi_event *mhi_event;
int i;
+ read_lock_bh(&mhi_cntrl->pm_lock);
MHI_LOG("Entered with pm_state:%s dev_state:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state));
- if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
+ if (mhi_cntrl->pm_state == MHI_PM_DISABLE) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
return 0;
+ }
- if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
return -EIO;
+ }
MHI_ASSERT(mhi_cntrl->pm_state != MHI_PM_M3, "mhi_pm_state != M3");
+ read_unlock_bh(&mhi_cntrl->pm_lock);
/* notify any clients we're about to exit lpm */
if (notify_client) {
@@ -1419,10 +1444,19 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
/* do not process control events */
tasklet_disable(&mhi_cntrl->mhi_event->task);
+ /* check if we entered error state again before doing the restore */
write_lock_irq(&mhi_cntrl->pm_lock);
+
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ tasklet_enable(&mhi_cntrl->mhi_event->task);
+ return -EIO;
+ }
+
/* restore the states */
mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state;
mhi_cntrl->dev_state = mhi_cntrl->saved_dev_state;
+
write_unlock_irq(&mhi_cntrl->pm_lock);
switch (mhi_cntrl->pm_state) {
@@ -1434,6 +1468,12 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
mhi_cntrl->wake_get(mhi_cntrl, true);
mhi_cntrl->wake_put(mhi_cntrl, true);
read_unlock_bh(&mhi_cntrl->pm_lock);
+ break;
+ default:
+ MHI_ERR("Unexpected PM state:%s after restore\n",
+ to_mhi_pm_state_str(mhi_cntrl->pm_state));
+ tasklet_enable(&mhi_cntrl->mhi_event->task);
+ return -EIO;
}
/* now it's safe to process ctrl events */
@@ -1453,8 +1493,8 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
mhi_msi_handlr(0, mhi_event);
}
- /* schedules worker if any low priority events need to be handled */
- mhi_low_priority_events_pending(mhi_cntrl);
+ /* schedules worker if any special purpose events need to be handled */
+ mhi_special_events_pending(mhi_cntrl);
MHI_LOG("Exit with pm_state:%s dev_state:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
@@ -1500,6 +1540,7 @@ void mhi_device_get(struct mhi_device *mhi_dev, int vote)
if (vote & MHI_VOTE_DEVICE) {
read_lock_bh(&mhi_cntrl->pm_lock);
mhi_cntrl->wake_get(mhi_cntrl, true);
+ MHI_LOG("dev_wake %d\n", atomic_read(&mhi_cntrl->dev_wake));
read_unlock_bh(&mhi_cntrl->pm_lock);
atomic_inc(&mhi_dev->dev_vote);
}
@@ -1553,6 +1594,7 @@ void mhi_device_put(struct mhi_device *mhi_dev, int vote)
mhi_trigger_resume(mhi_cntrl);
mhi_cntrl->wake_put(mhi_cntrl, false);
+ MHI_LOG("dev_wake %d\n", atomic_read(&mhi_cntrl->dev_wake));
read_unlock_bh(&mhi_cntrl->pm_lock);
}
diff --git a/drivers/bus/mhi/devices/mhi_netdev.c b/drivers/bus/mhi/devices/mhi_netdev.c
index b0e6d25..cd26e55 100644
--- a/drivers/bus/mhi/devices/mhi_netdev.c
+++ b/drivers/bus/mhi/devices/mhi_netdev.c
@@ -24,17 +24,10 @@
#ifdef CONFIG_MHI_DEBUG
-#define IPC_LOG_LVL (MHI_MSG_LVL_VERBOSE)
-
-#define MHI_ASSERT(cond, msg) do { \
- if (cond) \
- panic(msg); \
-} while (0)
-
#define MSG_VERB(fmt, ...) do { \
if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_VERBOSE) \
pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__);\
- if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \
+ if (mhi_netdev->ipc_log && (*mhi_netdev->ipc_log_lvl <= \
MHI_MSG_LVL_VERBOSE)) \
ipc_log_string(mhi_netdev->ipc_log, "[D][%s] " fmt, \
__func__, ##__VA_ARGS__); \
@@ -42,23 +35,19 @@
#else
-#define IPC_LOG_LVL (MHI_MSG_LVL_ERROR)
-
-#define MHI_ASSERT(cond, msg) do { \
- if (cond) { \
- MSG_ERR(msg); \
- WARN_ON(cond); \
- } \
+#define MSG_VERB(fmt, ...) do { \
+ if (mhi_netdev->ipc_log && (*mhi_netdev->ipc_log_lvl <= \
+ MHI_MSG_LVL_VERBOSE)) \
+ ipc_log_string(mhi_netdev->ipc_log, "[D][%s] " fmt, \
+ __func__, ##__VA_ARGS__); \
} while (0)
-#define MSG_VERB(fmt, ...)
-
#endif
#define MSG_LOG(fmt, ...) do { \
if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_INFO) \
pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__);\
- if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \
+ if (mhi_netdev->ipc_log && (*mhi_netdev->ipc_log_lvl <= \
MHI_MSG_LVL_INFO)) \
ipc_log_string(mhi_netdev->ipc_log, "[I][%s] " fmt, \
__func__, ##__VA_ARGS__); \
@@ -67,12 +56,17 @@
#define MSG_ERR(fmt, ...) do { \
if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_ERROR) \
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
- if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \
+ if (mhi_netdev->ipc_log && (*mhi_netdev->ipc_log_lvl <= \
MHI_MSG_LVL_ERROR)) \
ipc_log_string(mhi_netdev->ipc_log, "[E][%s] " fmt, \
__func__, ##__VA_ARGS__); \
} while (0)
+#define MHI_ASSERT(cond, msg) do { \
+ if (cond) \
+ panic(msg); \
+} while (0)
+
struct mhi_net_chain {
struct sk_buff *head, *tail; /* chained skb */
};
@@ -106,11 +100,12 @@ struct mhi_netdev {
struct dentry *dentry;
enum MHI_DEBUG_LEVEL msg_lvl;
- enum MHI_DEBUG_LEVEL ipc_log_lvl;
+ enum MHI_DEBUG_LEVEL *ipc_log_lvl;
void *ipc_log;
/* debug stats */
u32 abuffers, kbuffers, rbuffers;
+ bool napi_scheduled;
};
struct mhi_netdev_priv {
@@ -451,6 +446,7 @@ static int mhi_netdev_alloc_thread(void *data)
/* replenish the ring */
napi_schedule(mhi_netdev->napi);
+ mhi_netdev->napi_scheduled = true;
/* wait for buffers to run low or thread to stop */
wait_event_interruptible(mhi_netdev->alloc_event,
@@ -490,6 +486,7 @@ static int mhi_netdev_poll(struct napi_struct *napi, int budget)
if (rx_work < 0) {
MSG_ERR("Error polling ret:%d\n", rx_work);
napi_complete(napi);
+ mhi_netdev->napi_scheduled = false;
return 0;
}
@@ -500,8 +497,10 @@ static int mhi_netdev_poll(struct napi_struct *napi, int budget)
mhi_netdev_queue(mhi_netdev, rsc_dev->mhi_dev);
/* complete work if # of packet processed less than allocated budget */
- if (rx_work < budget)
+ if (rx_work < budget) {
napi_complete(napi);
+ mhi_netdev->napi_scheduled = false;
+ }
MSG_VERB("polled %d\n", rx_work);
@@ -837,6 +836,7 @@ static void mhi_netdev_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb)
return;
napi_schedule(mhi_netdev->napi);
+ mhi_netdev->napi_scheduled = true;
}
#ifdef CONFIG_DEBUG_FS
@@ -978,6 +978,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
int ret;
struct mhi_netdev *mhi_netdev, *p_netdev = NULL;
struct device_node *of_node = mhi_dev->dev.of_node;
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int nr_tre;
char node_name[32];
struct device_node *phandle;
@@ -1085,7 +1086,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
mhi_netdev->alias);
mhi_netdev->ipc_log = ipc_log_context_create(IPC_LOG_PAGES,
node_name, 0);
- mhi_netdev->ipc_log_lvl = IPC_LOG_LVL;
+ mhi_netdev->ipc_log_lvl = &mhi_cntrl->log_lvl;
mhi_netdev_create_debugfs(mhi_netdev);
}
@@ -1098,6 +1099,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
* by triggering a napi_poll
*/
napi_schedule(mhi_netdev->napi);
+ mhi_netdev->napi_scheduled = true;
return 0;
}
diff --git a/drivers/bus/mhi/devices/mhi_uci.c b/drivers/bus/mhi/devices/mhi_uci.c
index 7dbb4d7..d16ba5c 100644
--- a/drivers/bus/mhi/devices/mhi_uci.c
+++ b/drivers/bus/mhi/devices/mhi_uci.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.*/
#include <linux/cdev.h>
#include <linux/device.h>
@@ -50,6 +50,7 @@ struct uci_dev {
bool enabled;
u32 tiocm;
void *ipc_log;
+ enum MHI_DEBUG_LEVEL *ipc_log_lvl;
};
struct mhi_uci_drv {
@@ -64,36 +65,33 @@ enum MHI_DEBUG_LEVEL msg_lvl = MHI_MSG_LVL_ERROR;
#ifdef CONFIG_MHI_DEBUG
-#define IPC_LOG_LVL (MHI_MSG_LVL_VERBOSE)
#define MHI_UCI_IPC_LOG_PAGES (25)
-
-#else
-
-#define IPC_LOG_LVL (MHI_MSG_LVL_ERROR)
-#define MHI_UCI_IPC_LOG_PAGES (1)
-
-#endif
-
-#ifdef CONFIG_MHI_DEBUG
-
#define MSG_VERB(fmt, ...) do { \
if (msg_lvl <= MHI_MSG_LVL_VERBOSE) \
pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__); \
- if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_VERBOSE)) \
- ipc_log_string(uci_dev->ipc_log, "[D][%s] " fmt, \
- __func__, ##__VA_ARGS__); \
+ if (uci_dev->ipc_log && uci_dev->ipc_log_lvl && \
+ (*uci_dev->ipc_log_lvl <= MHI_MSG_LVL_VERBOSE)) \
+ ipc_log_string(uci_dev->ipc_log, \
+ "[D][%s] " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#else
-#define MSG_VERB(fmt, ...)
+#define MHI_UCI_IPC_LOG_PAGES (1)
+#define MSG_VERB(fmt, ...) do { \
+ if (uci_dev->ipc_log && uci_dev->ipc_log_lvl && \
+ (*uci_dev->ipc_log_lvl <= MHI_MSG_LVL_VERBOSE)) \
+ ipc_log_string(uci_dev->ipc_log, \
+ "[D][%s] " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
#endif
#define MSG_LOG(fmt, ...) do { \
if (msg_lvl <= MHI_MSG_LVL_INFO) \
pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__); \
- if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_INFO)) \
+ if (uci_dev->ipc_log && uci_dev->ipc_log_lvl && \
+ (*uci_dev->ipc_log_lvl <= MHI_MSG_LVL_INFO)) \
ipc_log_string(uci_dev->ipc_log, "[I][%s] " fmt, \
__func__, ##__VA_ARGS__); \
} while (0)
@@ -101,7 +99,8 @@ enum MHI_DEBUG_LEVEL msg_lvl = MHI_MSG_LVL_ERROR;
#define MSG_ERR(fmt, ...) do { \
if (msg_lvl <= MHI_MSG_LVL_ERROR) \
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
- if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_ERROR)) \
+ if (uci_dev->ipc_log && uci_dev->ipc_log_lvl && \
+ (*uci_dev->ipc_log_lvl <= MHI_MSG_LVL_ERROR)) \
ipc_log_string(uci_dev->ipc_log, "[E][%s] " fmt, \
__func__, ##__VA_ARGS__); \
} while (0)
@@ -571,6 +570,7 @@ static int mhi_uci_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
{
struct uci_dev *uci_dev;
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int minor;
char node_name[32];
int dir;
@@ -606,6 +606,7 @@ static int mhi_uci_probe(struct mhi_device *mhi_dev,
mhi_dev->ul_chan_id);
uci_dev->ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
node_name, 0);
+ uci_dev->ipc_log_lvl = &mhi_cntrl->log_lvl;
for (dir = 0; dir < 2; dir++) {
struct uci_chan *uci_chan = (dir) ?
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index e95b263..b6f63e7 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -91,6 +91,9 @@ struct sysc {
struct delayed_work idle_work;
};
+static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
+ bool is_child);
+
void sysc_write(struct sysc *ddata, int offset, u32 value)
{
writel_relaxed(value, ddata->module_va + offset);
@@ -214,8 +217,13 @@ static int sysc_get_clocks(struct sysc *ddata)
if (!ddata->clocks)
return -ENOMEM;
- for (i = 0; i < ddata->nr_clocks; i++) {
- error = sysc_get_one_clock(ddata, ddata->clock_roles[i]);
+ for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
+ const char *name = ddata->clock_roles[i];
+
+ if (!name)
+ continue;
+
+ error = sysc_get_one_clock(ddata, name);
if (error && error != -ENOENT)
return error;
}
@@ -374,6 +382,7 @@ static int sysc_check_one_child(struct sysc *ddata,
dev_warn(ddata->dev, "really a child ti,hwmods property?");
sysc_check_quirk_stdout(ddata, np);
+ sysc_parse_dts_quirks(ddata, np, true);
return 0;
}
@@ -1343,23 +1352,37 @@ static const struct sysc_dts_quirk sysc_dts_quirks[] = {
.mask = SYSC_QUIRK_NO_RESET_ON_INIT, },
};
-static int sysc_init_dts_quirks(struct sysc *ddata)
+static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
+ bool is_child)
{
- struct device_node *np = ddata->dev->of_node;
const struct property *prop;
- int i, len, error;
- u32 val;
-
- ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL);
+ int i, len;
for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) {
- prop = of_get_property(np, sysc_dts_quirks[i].name, &len);
+ const char *name = sysc_dts_quirks[i].name;
+
+ prop = of_get_property(np, name, &len);
if (!prop)
continue;
ddata->cfg.quirks |= sysc_dts_quirks[i].mask;
+ if (is_child) {
+ dev_warn(ddata->dev,
+ "dts flag should be at module level for %s\n",
+ name);
+ }
}
+}
+static int sysc_init_dts_quirks(struct sysc *ddata)
+{
+ struct device_node *np = ddata->dev->of_node;
+ int error;
+ u32 val;
+
+ ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL);
+
+ sysc_parse_dts_quirks(ddata, np, false);
error = of_property_read_u32(np, "ti,sysc-delay-us", &val);
if (!error) {
if (val > 255) {
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 27a82a5..d394738 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -411,10 +411,10 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi,
* hack to have the capability flags defined const, while we can still
* change it here without gcc complaining at every line.
*/
-#define ENSURE(call, bits) \
-do { \
- if (cdo->call == NULL) \
- *change_capability &= ~(bits); \
+#define ENSURE(cdo, call, bits) \
+do { \
+ if (cdo->call == NULL) \
+ WARN_ON_ONCE((cdo)->capability & (bits)); \
} while (0)
/*
@@ -590,7 +590,6 @@ int register_cdrom(struct cdrom_device_info *cdi)
{
static char banner_printed;
const struct cdrom_device_ops *cdo = cdi->ops;
- int *change_capability = (int *)&cdo->capability; /* hack */
cd_dbg(CD_OPEN, "entering register_cdrom\n");
@@ -602,16 +601,16 @@ int register_cdrom(struct cdrom_device_info *cdi)
cdrom_sysctl_register();
}
- ENSURE(drive_status, CDC_DRIVE_STATUS);
+ ENSURE(cdo, drive_status, CDC_DRIVE_STATUS);
if (cdo->check_events == NULL && cdo->media_changed == NULL)
- *change_capability = ~(CDC_MEDIA_CHANGED | CDC_SELECT_DISC);
- ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
- ENSURE(lock_door, CDC_LOCK);
- ENSURE(select_speed, CDC_SELECT_SPEED);
- ENSURE(get_last_session, CDC_MULTI_SESSION);
- ENSURE(get_mcn, CDC_MCN);
- ENSURE(reset, CDC_RESET);
- ENSURE(generic_packet, CDC_GENERIC_PACKET);
+ WARN_ON_ONCE(cdo->capability & (CDC_MEDIA_CHANGED | CDC_SELECT_DISC));
+ ENSURE(cdo, tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
+ ENSURE(cdo, lock_door, CDC_LOCK);
+ ENSURE(cdo, select_speed, CDC_SELECT_SPEED);
+ ENSURE(cdo, get_last_session, CDC_MULTI_SESSION);
+ ENSURE(cdo, get_mcn, CDC_MCN);
+ ENSURE(cdo, reset, CDC_RESET);
+ ENSURE(cdo, generic_packet, CDC_GENERIC_PACKET);
cdi->mc_flags = 0;
cdi->options = CDO_USE_FFLAGS;
@@ -997,6 +996,12 @@ static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks)
tracks->xa = 0;
tracks->error = 0;
cd_dbg(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n");
+
+ if (!CDROM_CAN(CDC_PLAY_AUDIO)) {
+ tracks->error = CDS_NO_INFO;
+ return;
+ }
+
/* Grab the TOC header so we can see how many tracks there are */
ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
if (ret) {
@@ -1163,7 +1168,8 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
ret = open_for_data(cdi);
if (ret)
goto err;
- cdrom_mmc3_profile(cdi);
+ if (CDROM_CAN(CDC_GENERIC_PACKET))
+ cdrom_mmc3_profile(cdi);
if (mode & FMODE_WRITE) {
ret = -EROFS;
if (cdrom_open_write(cdi))
@@ -2883,6 +2889,9 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
it doesn't give enough information or fails. then we return
the toc contents. */
use_toc:
+ if (!CDROM_CAN(CDC_PLAY_AUDIO))
+ return -ENOSYS;
+
toc.cdte_format = CDROM_MSF;
toc.cdte_track = CDROM_LEADOUT;
if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc)))
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 4385e26..ac55873 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
@@ -261,6 +261,7 @@ struct overlap {
uintptr_t mstart;
uintptr_t mend;
uintptr_t offset;
+ int do_cmo; /*used for cache maintenance of inrout buffers*/
};
struct smq_invoke_ctx {
@@ -1269,6 +1270,9 @@ static int context_build_overlap(struct smq_invoke_ctx *ctx)
if (ctx->overps[i]->end > max.end) {
max.end = ctx->overps[i]->end;
} else {
+ if (max.raix + 1 <= inbufs &&
+ ctx->overps[i]->raix + 1 > inbufs)
+ ctx->overps[i]->do_cmo = 1;
ctx->overps[i]->mend = 0;
ctx->overps[i]->mstart = 0;
}
@@ -1846,6 +1850,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
int i = ctx->overps[oix]->raix;
struct fastrpc_mmap *map = ctx->maps[i];
+ if (i+1 > inbufs) // Avoiding flush for outbufs
+ continue;
if (map && map->uncached)
continue;
if (ctx->fl->sctx->smmu.coherent &&
@@ -1856,13 +1862,41 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
if (map && (map->attr & FASTRPC_ATTR_FORCE_NOFLUSH))
continue;
- if (rpra && rpra[i].buf.len &&
- ctx->overps[oix]->mstart) {
+ if (rpra && rpra[i].buf.len && (ctx->overps[oix]->mstart ||
+ ctx->overps[oix]->do_cmo == 1)) {
if (map && map->buf) {
- dma_buf_begin_cpu_access(map->buf,
- DMA_TO_DEVICE);
- dma_buf_end_cpu_access(map->buf,
- DMA_TO_DEVICE);
+ if (((buf_page_size(ctx->overps[oix]->mend -
+ ctx->overps[oix]->mstart)) == map->size) ||
+ ctx->overps[oix]->do_cmo) {
+ dma_buf_begin_cpu_access(map->buf,
+ DMA_TO_DEVICE);
+ dma_buf_end_cpu_access(map->buf,
+ DMA_TO_DEVICE);
+ } else {
+ uintptr_t offset;
+ struct vm_area_struct *vma;
+
+ down_read(¤t->mm->mmap_sem);
+ VERIFY(err, NULL != (vma = find_vma(
+ current->mm,
+ ctx->overps[oix]->mstart)));
+ if (err) {
+ up_read(¤t->mm->mmap_sem);
+ goto bail;
+ }
+ offset = buf_page_start(
+ rpra[i].buf.pv) -
+ vma->vm_start;
+ up_read(¤t->mm->mmap_sem);
+ dma_buf_begin_cpu_access_partial(
+ map->buf, DMA_TO_DEVICE, offset,
+ ctx->overps[oix]->mend -
+ ctx->overps[oix]->mstart);
+ dma_buf_end_cpu_access_partial(
+ map->buf, DMA_TO_DEVICE, offset,
+ ctx->overps[oix]->mend -
+ ctx->overps[oix]->mstart);
+ }
} else
dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
uint64_to_ptr(rpra[i].buf.pv
@@ -1939,77 +1973,25 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
return err;
}
-static void inv_args_pre(struct smq_invoke_ctx *ctx)
-{
- int i, inbufs, outbufs;
- uint32_t sc = ctx->sc;
- remote_arg64_t *rpra = ctx->rpra;
- uintptr_t end;
-
- inbufs = REMOTE_SCALARS_INBUFS(sc);
- outbufs = REMOTE_SCALARS_OUTBUFS(sc);
- for (i = inbufs; i < inbufs + outbufs; ++i) {
- struct fastrpc_mmap *map = ctx->maps[i];
-
- if (map && map->uncached)
- continue;
- if (!rpra[i].buf.len)
- continue;
- if (ctx->fl->sctx->smmu.coherent &&
- !(map && (map->attr & FASTRPC_ATTR_NON_COHERENT)))
- continue;
- if (map && (map->attr & FASTRPC_ATTR_COHERENT))
- continue;
- if (map && (map->attr & FASTRPC_ATTR_FORCE_NOINVALIDATE))
- continue;
-
- if (buf_page_start(ptr_to_uint64((void *)rpra)) ==
- buf_page_start(rpra[i].buf.pv))
- continue;
- if (!IS_CACHE_ALIGNED((uintptr_t)
- uint64_to_ptr(rpra[i].buf.pv))) {
- if (map && map->buf) {
- dma_buf_begin_cpu_access(map->buf,
- DMA_BIDIRECTIONAL);
- dma_buf_end_cpu_access(map->buf,
- DMA_BIDIRECTIONAL);
- } else {
- dmac_flush_range(
- uint64_to_ptr(rpra[i].buf.pv), (char *)
- uint64_to_ptr(rpra[i].buf.pv + 1));
- }
- }
-
- end = (uintptr_t)uint64_to_ptr(rpra[i].buf.pv +
- rpra[i].buf.len);
- if (!IS_CACHE_ALIGNED(end)) {
- if (map && map->buf) {
- dma_buf_begin_cpu_access(map->buf,
- DMA_BIDIRECTIONAL);
- dma_buf_end_cpu_access(map->buf,
- DMA_BIDIRECTIONAL);
- } else {
- dmac_flush_range((char *)end,
- (char *)end + 1);
- }
- }
- }
-}
-
static void inv_args(struct smq_invoke_ctx *ctx)
{
int i, inbufs, outbufs;
uint32_t sc = ctx->sc;
remote_arg64_t *rpra = ctx->lrpra;
+ int err = 0;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
- for (i = inbufs; i < inbufs + outbufs; ++i) {
- struct fastrpc_mmap *map = ctx->maps[i];
+ for (i = 0; i < inbufs + outbufs; ++i) {
+ int over = ctx->overps[i]->raix;
+ struct fastrpc_mmap *map = ctx->maps[over];
+
+ if ((over + 1 <= inbufs))
+ continue;
if (map && map->uncached)
continue;
- if (!rpra[i].buf.len)
+ if (!rpra[over].buf.len)
continue;
if (ctx->fl->sctx->smmu.coherent &&
!(map && (map->attr & FASTRPC_ATTR_NON_COHERENT)))
@@ -2020,20 +2002,51 @@ static void inv_args(struct smq_invoke_ctx *ctx)
continue;
if (buf_page_start(ptr_to_uint64((void *)rpra)) ==
- buf_page_start(rpra[i].buf.pv)) {
+ buf_page_start(rpra[over].buf.pv)) {
continue;
}
- if (map && map->buf) {
- dma_buf_begin_cpu_access(map->buf,
- DMA_FROM_DEVICE);
- dma_buf_end_cpu_access(map->buf,
- DMA_FROM_DEVICE);
- } else
- dmac_inv_range((char *)uint64_to_ptr(rpra[i].buf.pv),
- (char *)uint64_to_ptr(rpra[i].buf.pv
- + rpra[i].buf.len));
- }
+ if (ctx->overps[i]->mstart || ctx->overps[i]->do_cmo == 1) {
+ if (map && map->buf) {
+ if (((buf_page_size(ctx->overps[i]->mend -
+ ctx->overps[i]->mstart)) == map->size) ||
+ ctx->overps[i]->do_cmo) {
+ dma_buf_begin_cpu_access(map->buf,
+ DMA_TO_DEVICE);
+ dma_buf_end_cpu_access(map->buf,
+ DMA_FROM_DEVICE);
+ } else {
+ uintptr_t offset;
+ struct vm_area_struct *vma;
+ down_read(¤t->mm->mmap_sem);
+ VERIFY(err, NULL != (vma = find_vma(
+ current->mm,
+ ctx->overps[i]->mstart)));
+ if (err) {
+ up_read(¤t->mm->mmap_sem);
+ goto bail;
+ }
+ offset = buf_page_start(
+ rpra[over].buf.pv) -
+ vma->vm_start;
+ up_read(¤t->mm->mmap_sem);
+ dma_buf_begin_cpu_access_partial(
+ map->buf, DMA_TO_DEVICE, offset,
+ ctx->overps[i]->mend -
+ ctx->overps[i]->mstart);
+ dma_buf_end_cpu_access_partial(map->buf,
+ DMA_FROM_DEVICE, offset,
+ ctx->overps[i]->mend -
+ ctx->overps[i]->mstart);
+ }
+ } else
+ dmac_inv_range((char *)uint64_to_ptr(rpra[over].buf.pv),
+ (char *)uint64_to_ptr(rpra[over].buf.pv
+ + rpra[over].buf.len));
+ }
+ }
+bail:
+ return;
}
static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx,
@@ -2291,10 +2304,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
}
PERF(fl->profile, GET_COUNTER(perf_counter, PERF_INVARGS),
- inv_args_pre(ctx);
- PERF_END);
-
- PERF(fl->profile, GET_COUNTER(perf_counter, PERF_INVARGS),
inv_args(ctx);
PERF_END);
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 64ecb3d..6177f2b 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/sched.h>
+#include <linux/sched/task.h>
#include <linux/ratelimit.h>
#include <linux/workqueue.h>
#include <linux/diagchar.h>
@@ -330,7 +331,10 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
int peripheral = 0;
struct diag_md_session_t *session_info = NULL;
struct pid *pid_struct = NULL;
+ struct task_struct *task_s = NULL;
+ if (!info)
+ return -EINVAL;
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
if (!ch->md_info_inited)
@@ -353,11 +357,11 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
if (!session_info)
goto drop_data;
- if (session_info && info &&
+ if (session_info &&
(session_info->pid != info->pid))
continue;
- if ((info && (info->peripheral_mask[i] &
- MD_PERIPHERAL_MASK(peripheral)) == 0))
+ if ((info->peripheral_mask[i] &
+ MD_PERIPHERAL_MASK(peripheral)) == 0)
goto drop_data;
pid_struct = find_get_pid(session_info->pid);
if (!pid_struct) {
@@ -386,35 +390,45 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
}
if (i > 0) {
remote_token = diag_get_remote(i);
- if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ task_s = get_pid_task(pid_struct, PIDTYPE_PID);
+ if (task_s) {
err = copy_to_user(buf + ret,
&remote_token,
sizeof(int));
- if (err)
+ if (err) {
+ put_task_struct(task_s);
goto drop_data;
+ }
ret += sizeof(int);
+ put_task_struct(task_s);
}
}
- /* Copy the length of data being passed */
- if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ task_s = get_pid_task(pid_struct, PIDTYPE_PID);
+ if (task_s) {
+
+ /* Copy the length of data being passed */
err = copy_to_user(buf + ret,
(void *)&(entry->len),
sizeof(int));
- if (err)
+ if (err) {
+ put_task_struct(task_s);
goto drop_data;
+ }
ret += sizeof(int);
- }
- /* Copy the actual data being passed */
- if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ /* Copy the actual data being passed */
err = copy_to_user(buf + ret,
(void *)entry->buf,
entry->len);
- if (err)
+ if (err) {
+ put_task_struct(task_s);
goto drop_data;
+ }
ret += entry->len;
+ put_task_struct(task_s);
}
+
/*
* The data is now copied to the user space client,
* Notify that the write is complete and delete its
@@ -432,14 +446,22 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
entry->len = 0;
entry->ctx = 0;
spin_unlock_irqrestore(&ch->lock, flags);
+
+ put_pid(pid_struct);
}
}
*pret = ret;
- if (pid_struct && get_pid_task(pid_struct, PIDTYPE_PID)) {
- err = copy_to_user(buf + sizeof(int),
+ pid_struct = find_get_pid(info->pid);
+ if (pid_struct) {
+ task_s = get_pid_task(pid_struct, PIDTYPE_PID);
+ if (task_s) {
+ err = copy_to_user(buf + sizeof(int),
(void *)&num_data,
sizeof(int));
+ put_task_struct(task_s);
+ }
+ put_pid(pid_struct);
}
diag_ws_on_copy_complete(DIAG_WS_MUX);
if (drain_again)
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 110e916..0516429 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2008-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2020, The Linux Foundation. All rights reserved.
*/
#ifndef DIAGCHAR_H
@@ -225,6 +225,8 @@
#define HDLC_CTXT 1
#define NON_HDLC_CTXT 2
+#define PKT_PROCESS_TIMEOUT 200
+
#define TYPE_DATA 0
#define TYPE_CNTL 1
#define TYPE_DCI 2
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 2237736..6638358 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2008-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/ratelimit.h>
#include <linux/timer.h>
+#include <linux/jiffies.h>
#include <linux/sched/task.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <linux/usb/usbdiag.h>
@@ -536,6 +537,7 @@ static int diag_remove_client_entry(struct file *file)
return -EINVAL;
}
+ mutex_lock(&driver->diagchar_mutex);
diagpriv_data = file->private_data;
for (i = 0; i < driver->num_clients; i++)
if (diagpriv_data && diagpriv_data->pid ==
@@ -545,11 +547,13 @@ static int diag_remove_client_entry(struct file *file)
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"pid %d, not present in client map\n",
diagpriv_data->pid);
+ mutex_unlock(&driver->diagchar_mutex);
mutex_unlock(&driver->diag_file_mutex);
return -EINVAL;
}
DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag: %s process exit with pid = %d\n",
driver->client_map[i].name, diagpriv_data->pid);
+ mutex_unlock(&driver->diagchar_mutex);
/*
* clean up any DCI registrations, if this is a DCI client
* This will specially help in case of ungraceful exit of any DCI client
@@ -3085,7 +3089,7 @@ long diagchar_ioctl(struct file *filp,
static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
int pkt_type)
{
- int err = 0;
+ int err = 0, wait_err = 0;
int ret = PKT_DROP;
struct diag_apps_data_t *data = &hdlc_data;
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
@@ -3116,8 +3120,15 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
send.terminate = 1;
wait_for_buffer:
- wait_event_interruptible(driver->hdlc_wait_q,
- (data->flushed == 0));
+ wait_err = wait_event_interruptible_timeout(driver->hdlc_wait_q,
+ (data->flushed == 0),
+ msecs_to_jiffies(PKT_PROCESS_TIMEOUT));
+ if (wait_err <= 0) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: Timeout while waiting for hdlc buffer to be flushed, err: %d\n",
+ wait_err);
+ return PKT_DROP;
+ }
spin_lock_irqsave(&driver->diagmem_lock, flags);
if (data->flushed) {
spin_unlock_irqrestore(&driver->diagmem_lock, flags);
@@ -3168,8 +3179,16 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
goto fail_free_buf;
}
wait_for_agg_buff:
- wait_event_interruptible(driver->hdlc_wait_q,
- (data->flushed == 0));
+ wait_err = wait_event_interruptible_timeout(driver->hdlc_wait_q,
+ (data->flushed == 0),
+ msecs_to_jiffies(PKT_PROCESS_TIMEOUT));
+ if (wait_err <= 0) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: Timeout while waiting for hdlc aggregation buffer to be flushed, err: %d\n",
+ wait_err);
+ return PKT_DROP;
+ }
+
spin_lock_irqsave(&driver->diagmem_lock, flags);
if (data->flushed) {
spin_unlock_irqrestore(&driver->diagmem_lock, flags);
@@ -3227,7 +3246,7 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
int pkt_type)
{
- int err = 0;
+ int err = 0, wait_err = 0;
int ret = PKT_DROP;
struct diag_pkt_frame_t header;
struct diag_apps_data_t *data = &non_hdlc_data;
@@ -3245,8 +3264,16 @@ static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
return -EIO;
}
wait_for_buffer:
- wait_event_interruptible(driver->hdlc_wait_q,
- (data->flushed == 0));
+ wait_err = wait_event_interruptible_timeout(driver->hdlc_wait_q,
+ (data->flushed == 0),
+ msecs_to_jiffies(PKT_PROCESS_TIMEOUT));
+ if (wait_err <= 0) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: Timeout while waiting for non-hdlc buffer to be flushed, err: %d\n",
+ wait_err);
+ return PKT_DROP;
+ }
+
spin_lock_irqsave(&driver->diagmem_lock, flags);
if (data->flushed) {
spin_unlock_irqrestore(&driver->diagmem_lock, flags);
@@ -4273,7 +4300,7 @@ static void diag_debug_init(void)
* to be logged to IPC
*/
diag_debug_mask = DIAG_DEBUG_PERIPHERALS | DIAG_DEBUG_DCI |
- DIAG_DEBUG_USERSPACE | DIAG_DEBUG_BRIDGE;
+ DIAG_DEBUG_MHI | DIAG_DEBUG_USERSPACE | DIAG_DEBUG_BRIDGE;
}
#else
static void diag_debug_init(void)
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
index d4af0e6..7635f6d 100644
--- a/drivers/char/diag/diagfwd_bridge.c
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -111,7 +111,7 @@ static int diagfwd_bridge_mux_write_done(unsigned char *buf, int len,
return -EINVAL;
ch = &bridge_info[buf_ctx];
if (ch->dev_ops && ch->dev_ops->fwd_complete) {
- DIAG_LOG(DIAG_DEBUG_MHI,
+ DIAG_LOG(DIAG_DEBUG_BRIDGE,
"Write done completion received for buf %pK len:%d\n",
buf, len);
ch->dev_ops->fwd_complete(ch->id, ch->ctxt, buf, len, 0);
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index 39eb9c1..4cdda50 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
@@ -192,9 +192,12 @@ static void mhi_buf_tbl_remove(struct diag_mhi_info *mhi_info, int type,
list_del(&item->link);
if (type == TYPE_MHI_READ_CH) {
DIAG_LOG(DIAG_DEBUG_MHI,
- "Callback received on buffer:%pK from mhi\n", buf);
+ "Freeing read channel buffer: %pK\n", buf);
diagmem_free(driver, item->buf, mhi_info->mempool);
}
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Removing %s channel item entry from table: %pK\n",
+ mhi_info->name, buf);
kfree(item);
found = 1;
}
@@ -232,7 +235,7 @@ static void mhi_buf_tbl_clear(struct diag_mhi_info *mhi_info)
&mhi_info->read_done_list, link) {
if (tp->buf == buf) {
DIAG_LOG(DIAG_DEBUG_MHI,
- "buffer:%pK removed from table for ch:%s\n",
+ "Read buffer:%pK removed from table for ch:%s\n",
buf, mhi_info->name);
list_del(&tp->link);
kfree(tp);
@@ -254,6 +257,9 @@ static void mhi_buf_tbl_clear(struct diag_mhi_info *mhi_info)
item = list_entry(start, struct diag_mhi_buf_tbl_t,
link);
list_del(&item->link);
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Write buffer %pK removed from table for ch: %s\n",
+ buf, mhi_info->name);
diag_remote_dev_write_done(mhi_info->dev_id, item->buf,
item->len, mhi_info->id);
kfree(item);
@@ -280,6 +286,9 @@ static int __mhi_close(struct diag_mhi_info *mhi_info, int close_flag)
if (close_flag == CLOSE_CHANNELS) {
mutex_lock(&mhi_info->ch_mutex);
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "diag: %s mhi channel closed, calling mhi unprepare\n",
+ mhi_info->name);
mhi_unprepare_from_transfer(mhi_info->mhi_dev);
mutex_unlock(&mhi_info->ch_mutex);
}
@@ -293,15 +302,20 @@ static int mhi_close(int token, int ch)
int dev_idx = get_id_from_token(token);
if (dev_idx < 0 || dev_idx >= NUM_MHI_DEV) {
- pr_err("diag: In %s, invalid index %d\n", __func__, dev_idx);
+ pr_err("diag: %s: invalid index %d\n", __func__, dev_idx);
return -EINVAL;
}
- if (ch < 0 || ch >= NUM_MHI_CHAN)
- pr_err("diag: In %s, invalid channel %d\n", __func__, ch);
+ if (ch < 0 || ch >= NUM_MHI_CHAN) {
+ pr_err("diag: %s: invalid channel %d\n", __func__, ch);
+ return -EINVAL;
+ }
- if (!diag_mhi[dev_idx][ch].enabled)
+ if (!diag_mhi[dev_idx][ch].enabled) {
+ pr_err("diag: %s: invalid device node for index: %d, ch: %d\n",
+ __func__, dev_idx, ch);
return -ENODEV;
+ }
/*
* This function is called whenever the channel needs to be closed
* explicitly by Diag. Close both the read and write channels (denoted
@@ -334,9 +348,16 @@ static int __mhi_open(struct diag_mhi_info *mhi_info, int token, int open_flag)
return -ENODEV;
if (open_flag == OPEN_CHANNELS) {
if ((atomic_read(&(mhi_info->read_ch.opened))) &&
- (atomic_read(&(mhi_info->write_ch.opened))))
+ (atomic_read(&(mhi_info->write_ch.opened)))) {
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Read and write channel already open: %s\n",
+ mhi_info->name);
return 0;
+ }
mutex_lock(&mhi_info->ch_mutex);
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Prepare mhi for transfer on port: %s\n",
+ mhi_info->name);
err = mhi_prepare_for_transfer(mhi_info->mhi_dev);
mutex_unlock(&mhi_info->ch_mutex);
if (err) {
@@ -346,9 +367,9 @@ static int __mhi_open(struct diag_mhi_info *mhi_info, int token, int open_flag)
}
atomic_set(&mhi_info->read_ch.opened, 1);
atomic_set(&mhi_info->write_ch.opened, 1);
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
- "opened mhi read/write channel, port: %d\n",
- mhi_info->id);
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "opened mhi read/write channel, port: %s\n",
+ mhi_info->name);
} else if (open_flag == CHANNELS_OPENED) {
if (!atomic_read(&(mhi_info->read_ch.opened)) ||
!atomic_read(&(mhi_info->write_ch.opened))) {
@@ -438,7 +459,7 @@ static void mhi_read_done_work_fn(struct work_struct *work)
spin_unlock_irqrestore(&mhi_info->read_ch.lock, flags);
if (!buf)
break;
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"read from mhi port %d buf %pK len:%d\n",
mhi_info->id, buf, len);
/*
@@ -504,7 +525,7 @@ static void mhi_read_work_fn(struct work_struct *work)
goto fail;
}
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"queueing a read buf %pK, ch: %s\n",
buf, mhi_info->name);
@@ -553,54 +574,65 @@ static int mhi_write(int token, int ch, unsigned char *buf, int len, int ctxt)
unsigned long flags;
struct diag_mhi_ch_t *ch_info = NULL;
int dev_idx = get_id_from_token(token);
+ struct diag_mhi_info *mhi_info = NULL;
if (dev_idx < 0 || dev_idx >= NUM_MHI_DEV) {
- pr_err_ratelimited("diag: In %s, invalid index %d\n", __func__,
+ pr_err_ratelimited("diag: %s: invalid index %d\n", __func__,
dev_idx);
return -EINVAL;
}
if (ch < 0 || ch >= NUM_MHI_CHAN) {
- pr_err_ratelimited("diag: In %s, invalid chan %d\n", __func__,
+ pr_err_ratelimited("diag: %s: invalid chan %d\n", __func__,
ch);
return -EINVAL;
}
if (!buf || len <= 0) {
- pr_err("diag: In %s, ch %d, invalid buf %pK len %d\n",
+ pr_err("diag: %s: ch: %d, invalid buf %pK len %d\n",
__func__, dev_idx, buf, len);
return -EINVAL;
}
- if (!diag_mhi[dev_idx][ch].enabled) {
- pr_err_ratelimited("diag: In %s, MHI channel %s is not enabled\n",
- __func__, diag_mhi[dev_idx][ch].name);
+ mhi_info = &diag_mhi[dev_idx][ch];
+
+ if (!mhi_info) {
+ pr_err_ratelimited("diag: %s, Invalid MHI info\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!mhi_info->enabled) {
+ pr_err_ratelimited("diag: %s: MHI channel %s is not enabled\n",
+ __func__, mhi_info->name);
return -EIO;
}
- ch_info = &diag_mhi[dev_idx][ch].write_ch;
+ ch_info = &mhi_info->write_ch;
if (!(atomic_read(&(ch_info->opened)))) {
- pr_err_ratelimited("diag: In %s, MHI write channel %s is not open\n",
- __func__, diag_mhi[dev_idx][ch].name);
+ pr_err_ratelimited("diag: %s: MHI write channel %s is not open\n",
+ __func__, mhi_info->name);
return -EIO;
}
spin_lock_irqsave(&ch_info->lock, flags);
- err = mhi_buf_tbl_add(&diag_mhi[dev_idx][ch], TYPE_MHI_WRITE_CH, buf,
+ err = mhi_buf_tbl_add(mhi_info, TYPE_MHI_WRITE_CH, buf,
len);
if (err) {
spin_unlock_irqrestore(&ch_info->lock, flags);
goto fail;
}
+ DIAG_LOG(DIAG_DEBUG_MHI, "diag: queueing a write buf %pK, ch: %s\n",
+ buf, mhi_info->name);
- err = mhi_queue_transfer(diag_mhi[dev_idx][ch].mhi_dev, DMA_TO_DEVICE,
+ err = mhi_queue_transfer(mhi_info->mhi_dev, DMA_TO_DEVICE,
buf, len, mhi_flags);
spin_unlock_irqrestore(&ch_info->lock, flags);
if (err) {
DIAG_LOG(DIAG_DEBUG_MHI,
- "diag: Cannot write to MHI channel %s, len %d, err: %d\n",
- __func__, diag_mhi[dev_idx][ch].name, len, err);
- mhi_buf_tbl_remove(&diag_mhi[dev_idx][ch], TYPE_MHI_WRITE_CH,
+ "diag: Cannot write to MHI channel: %s, len %d, err: %d\n",
+ mhi_info->name, len, err);
+ mhi_buf_tbl_remove(mhi_info, TYPE_MHI_WRITE_CH,
buf, len);
goto fail;
}
@@ -688,7 +720,7 @@ static void diag_mhi_read_cb(struct mhi_device *mhi_dev,
spin_lock_irqsave(&mhi_info->read_ch.lock, flags);
tp = kmalloc(sizeof(*tp), GFP_ATOMIC);
if (!tp) {
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"no mem for list\n");
spin_unlock_irqrestore(&mhi_info->read_ch.lock, flags);
return;
@@ -697,7 +729,7 @@ static void diag_mhi_read_cb(struct mhi_device *mhi_dev,
&mhi_info->read_ch.buf_tbl, link) {
if (item->buf == buf) {
DIAG_LOG(DIAG_DEBUG_MHI,
- "Callback received on buffer:%pK from mhi\n",
+ "Read callback received on buffer:%pK from mhi\n",
buf);
tp->buf = buf;
tp->len = result->bytes_xferd;
@@ -737,6 +769,9 @@ static void diag_mhi_write_cb(struct mhi_device *mhi_dev,
__func__);
return;
}
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Write callback received on buffer:%pK from mhi\n",
+ buf);
mhi_buf_tbl_remove(mhi_info, TYPE_MHI_WRITE_CH, buf,
result->bytes_xferd);
diag_remote_dev_write_done(mhi_info->dev_id, buf,
@@ -756,6 +791,11 @@ static void diag_mhi_remove(struct mhi_device *mhi_dev)
return;
if (!mhi_info->enabled)
return;
+
+ DIAG_LOG(DIAG_DEBUG_MHI,
+ "Remove called on mhi channel: %s\n",
+ mhi_info->name);
+
__mhi_close(mhi_info, CHANNELS_CLOSED);
spin_lock_irqsave(&mhi_info->lock, flags);
mhi_info->enabled = 0;
@@ -796,11 +836,11 @@ static int diag_mhi_probe(struct mhi_device *mhi_dev,
}
mhi_info = &diag_mhi[dev_idx][ch];
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"received probe for dev:%d ch:%d\n",
dev_idx, ch);
mhi_info->mhi_dev = mhi_dev;
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"diag: mhi device is ready to open\n");
spin_lock_irqsave(&mhi_info->lock, flags);
mhi_info->enabled = 1;
@@ -884,7 +924,7 @@ int diag_mhi_init(void)
ch, dev_idx, err);
goto fail;
}
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ DIAG_LOG(DIAG_DEBUG_MHI,
"mhi dev %d port %d initialized\n",
dev_idx, ch);
}
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index e9b6ac6..4988252 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -66,6 +66,13 @@
#define OMAP4_RNG_OUTPUT_SIZE 0x8
#define EIP76_RNG_OUTPUT_SIZE 0x10
+/*
+ * EIP76 RNG takes approx. 700us to produce 16 bytes of output data
+ * as per testing results. And to account for the lack of udelay()'s
+ * reliability, we keep the timeout as 1000us.
+ */
+#define RNG_DATA_FILL_TIMEOUT 100
+
enum {
RNG_OUTPUT_0_REG = 0,
RNG_OUTPUT_1_REG,
@@ -176,7 +183,7 @@ static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max,
if (max < priv->pdata->data_size)
return 0;
- for (i = 0; i < 20; i++) {
+ for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) {
present = priv->pdata->data_present(priv);
if (present || !wait)
break;
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
index 38b7190..648e39c 100644
--- a/drivers/char/hw_random/omap3-rom-rng.c
+++ b/drivers/char/hw_random/omap3-rom-rng.c
@@ -121,7 +121,8 @@ static int omap3_rom_rng_remove(struct platform_device *pdev)
{
cancel_delayed_work_sync(&idle_work);
hwrng_unregister(&omap3_rom_rng_ops);
- clk_disable_unprepare(rng_clk);
+ if (!rng_idle)
+ clk_disable_unprepare(rng_clk);
return 0;
}
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index 042860d..37b338a 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -169,6 +169,13 @@ static int stm32_rng_probe(struct platform_device *ofdev)
return devm_hwrng_register(dev, &priv->rng);
}
+static int stm32_rng_remove(struct platform_device *ofdev)
+{
+ pm_runtime_disable(&ofdev->dev);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int stm32_rng_runtime_suspend(struct device *dev)
{
@@ -210,6 +217,7 @@ static struct platform_driver stm32_rng_driver = {
.of_match_table = stm32_rng_match,
},
.probe = stm32_rng_probe,
+ .remove = stm32_rng_remove,
};
module_platform_driver(stm32_rng_driver);
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index e2c1438..28dbd55 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -217,6 +217,10 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
slave_addr = data[DMI_IPMI_SLAVEADDR];
memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long));
+ if (!base_addr) {
+ pr_err("Base address is zero, assuming no IPMI interface\n");
+ return;
+ }
if (len >= DMI_IPMI_VER2_LENGTH) {
if (type == IPMI_DMI_TYPE_SSIF) {
offset = 0;
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 3fb297b5..91f2d92 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -447,6 +447,8 @@ enum ipmi_stat_indexes {
#define IPMI_IPMB_NUM_SEQ 64
struct ipmi_smi {
+ struct module *owner;
+
/* What interface number are we? */
int intf_num;
@@ -1139,6 +1141,11 @@ int ipmi_create_user(unsigned int if_num,
if (rv)
goto out_kfree;
+ if (!try_module_get(intf->owner)) {
+ rv = -ENODEV;
+ goto out_kfree;
+ }
+
/* Note that each existing user holds a refcount to the interface. */
kref_get(&intf->refcount);
@@ -1269,6 +1276,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
}
kref_put(&intf->refcount, intf_free);
+ module_put(intf->owner);
}
int ipmi_destroy_user(struct ipmi_user *user)
@@ -1365,7 +1373,7 @@ int ipmi_set_my_LUN(struct ipmi_user *user,
}
release_ipmi_user(user, index);
- return 0;
+ return rv;
}
EXPORT_SYMBOL(ipmi_set_my_LUN);
@@ -2384,7 +2392,7 @@ static int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc)
* been recently fetched, this will just use the cached data. Otherwise
* it will run a new fetch.
*
- * Except for the first time this is called (in ipmi_register_smi()),
+ * Except for the first time this is called (in ipmi_add_smi()),
* this will always return good data;
*/
static int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
@@ -3304,10 +3312,11 @@ static void redo_bmc_reg(struct work_struct *work)
kref_put(&intf->refcount, intf_free);
}
-int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
- void *send_info,
- struct device *si_dev,
- unsigned char slave_addr)
+int ipmi_add_smi(struct module *owner,
+ const struct ipmi_smi_handlers *handlers,
+ void *send_info,
+ struct device *si_dev,
+ unsigned char slave_addr)
{
int i, j;
int rv;
@@ -3333,7 +3342,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
return rv;
}
-
+ intf->owner = owner;
intf->bmc = &intf->tmp_bmc;
INIT_LIST_HEAD(&intf->bmc->intfs);
mutex_init(&intf->bmc->dyn_mutex);
@@ -3440,7 +3449,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
return rv;
}
-EXPORT_SYMBOL(ipmi_register_smi);
+EXPORT_SYMBOL(ipmi_add_smi);
static void deliver_smi_err_response(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg,
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c
index 638f4ab..7558361 100644
--- a/drivers/char/ipmi/ipmi_si_mem_io.c
+++ b/drivers/char/ipmi/ipmi_si_mem_io.c
@@ -51,7 +51,7 @@ static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
static void mem_outq(const struct si_sm_io *io, unsigned int offset,
unsigned char b)
{
- writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));
+ writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
}
#endif
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
index f54ca68..022e0363 100644
--- a/drivers/char/ipmi/ipmi_si_pci.c
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -120,6 +120,8 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
}
io.addr_data = pci_resource_start(pdev, 0);
+ io.dev = &pdev->dev;
+
io.regspacing = ipmi_pci_probe_regspacing(&io);
io.regsize = DEFAULT_REGSIZE;
io.regshift = 0;
@@ -128,8 +130,6 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
if (io.irq)
io.irq_setup = ipmi_std_irq_setup;
- io.dev = &pdev->dev;
-
dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
&pdev->resource[0], io.regsize, io.regspacing, io.irq);
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 8c4dd1a..767740d 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -708,6 +708,10 @@ static int lp_set_timeout64(unsigned int minor, void __user *arg)
if (copy_from_user(karg, arg, sizeof(karg)))
return -EFAULT;
+ /* sparc64 suseconds_t is 32-bit only */
+ if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
+ karg[1] >>= 32;
+
return lp_set_timeout(minor, karg[0], karg[1]);
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 1ae77b4..51faafd 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -623,20 +623,27 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_from_user(time32, argp, sizeof(time32)))
return -EFAULT;
+ if ((time32[0] < 0) || (time32[1] < 0))
+ return -EINVAL;
+
return pp_set_timeout(pp->pdev, time32[0], time32[1]);
case PPSETTIME64:
if (copy_from_user(time64, argp, sizeof(time64)))
return -EFAULT;
+ if ((time64[0] < 0) || (time64[1] < 0))
+ return -EINVAL;
+
+ if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
+ time64[1] >>= 32;
+
return pp_set_timeout(pp->pdev, time64[0], time64[1]);
case PPGETTIME32:
jiffies_to_timespec64(pp->pdev->timeout, &ts);
time32[0] = ts.tv_sec;
time32[1] = ts.tv_nsec / NSEC_PER_USEC;
- if ((time32[0] < 0) || (time32[1] < 0))
- return -EINVAL;
if (copy_to_user(argp, time32, sizeof(time32)))
return -EFAULT;
@@ -647,8 +654,9 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
jiffies_to_timespec64(pp->pdev->timeout, &ts);
time64[0] = ts.tv_sec;
time64[1] = ts.tv_nsec / NSEC_PER_USEC;
- if ((time64[0] < 0) || (time64[1] < 0))
- return -EINVAL;
+
+ if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
+ time64[1] <<= 32;
if (copy_to_user(argp, time64, sizeof(time64)))
return -EFAULT;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3acf4fd..ef381ca 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -938,6 +938,10 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands,
GFP_KERNEL);
+ if (!chip->cc_attrs_tbl) {
+ rc = -ENOMEM;
+ goto out;
+ }
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
if (rc)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index c55f6ae..b353a5e 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1349,24 +1349,24 @@ static void set_console_size(struct port *port, u16 rows, u16 cols)
port->cons.ws.ws_col = cols;
}
-static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
+static int fill_queue(struct virtqueue *vq, spinlock_t *lock)
{
struct port_buffer *buf;
- unsigned int nr_added_bufs;
+ int nr_added_bufs;
int ret;
nr_added_bufs = 0;
do {
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
if (!buf)
- break;
+ return -ENOMEM;
spin_lock_irq(lock);
ret = add_inbuf(vq, buf);
if (ret < 0) {
spin_unlock_irq(lock);
free_buf(buf, true);
- break;
+ return ret;
}
nr_added_bufs++;
spin_unlock_irq(lock);
@@ -1386,7 +1386,6 @@ static int add_port(struct ports_device *portdev, u32 id)
char debugfs_name[16];
struct port *port;
dev_t devt;
- unsigned int nr_added_bufs;
int err;
port = kmalloc(sizeof(*port), GFP_KERNEL);
@@ -1445,11 +1444,13 @@ static int add_port(struct ports_device *portdev, u32 id)
spin_lock_init(&port->outvq_lock);
init_waitqueue_head(&port->waitqueue);
- /* Fill the in_vq with buffers so the host can send us data. */
- nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
- if (!nr_added_bufs) {
+ /* We can safely ignore ENOSPC because it means
+ * the queue already has buffers. Buffers are removed
+ * only by virtcons_remove(), not by unplug_port()
+ */
+ err = fill_queue(port->in_vq, &port->inbuf_lock);
+ if (err < 0 && err != -ENOSPC) {
dev_err(port->dev, "Error allocating inbufs\n");
- err = -ENOMEM;
goto free_device;
}
@@ -2083,14 +2084,11 @@ static int virtcons_probe(struct virtio_device *vdev)
INIT_WORK(&portdev->control_work, &control_work_handler);
if (multiport) {
- unsigned int nr_added_bufs;
-
spin_lock_init(&portdev->c_ivq_lock);
spin_lock_init(&portdev->c_ovq_lock);
- nr_added_bufs = fill_queue(portdev->c_ivq,
- &portdev->c_ivq_lock);
- if (!nr_added_bufs) {
+ err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
+ if (err < 0) {
dev_err(&vdev->dev,
"Error allocating buffers for control queue\n");
/*
@@ -2101,7 +2099,7 @@ static int virtcons_probe(struct virtio_device *vdev)
VIRTIO_CONSOLE_DEVICE_READY, 0);
/* Device was functional: we need full cleanup. */
virtcons_remove(vdev);
- return -ENOMEM;
+ return err;
}
} else {
/*
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a84c557..ed344eb 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -73,6 +73,7 @@
obj-y += imgtec/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_MACH_INGENIC) += ingenic/
+obj-$(CONFIG_ARCH_K3) += keystone/
obj-$(CONFIG_ARCH_KEYSTONE) += keystone/
obj-$(CONFIG_MACH_LOONGSON32) += loongson1/
obj-y += mediatek/
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
index da7bafc..b3eaf65 100644
--- a/drivers/clk/at91/clk-audio-pll.c
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -509,7 +509,7 @@ static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
{
- struct clk_audio_pad *apmc_ck;
+ struct clk_audio_pmc *apmc_ck;
struct clk_init_data init = {};
apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 1131524..ea23002 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -284,7 +284,7 @@ static void clk_generated_startup(struct clk_generated *gck)
static struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const char *name, const char **parent_names,
- u8 num_parents, u8 id,
+ u8 num_parents, u8 id, bool pll_audio,
const struct clk_range *range)
{
struct clk_generated *gck;
@@ -308,6 +308,7 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
gck->regmap = regmap;
gck->lock = lock;
gck->range = *range;
+ gck->audio_pll_allowed = pll_audio;
clk_generated_startup(gck);
hw = &gck->hw;
@@ -333,7 +334,6 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
struct device_node *gcknp;
struct clk_range range = CLK_RANGE(0, 0);
struct regmap *regmap;
- struct clk_generated *gck;
num_parents = of_clk_get_parent_count(np);
if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
@@ -350,6 +350,8 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
return;
for_each_child_of_node(np, gcknp) {
+ bool pll_audio = false;
+
if (of_property_read_u32(gcknp, "reg", &id))
continue;
@@ -362,24 +364,14 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
&range);
+ if (of_device_is_compatible(np, "atmel,sama5d2-clk-generated") &&
+ (id == GCK_ID_I2S0 || id == GCK_ID_I2S1 ||
+ id == GCK_ID_CLASSD))
+ pll_audio = true;
+
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
parent_names, num_parents,
- id, &range);
-
- gck = to_clk_generated(hw);
-
- if (of_device_is_compatible(np,
- "atmel,sama5d2-clk-generated")) {
- if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
- gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
- gck->id == GCK_ID_CLASSD)
- gck->audio_pll_allowed = true;
- else
- gck->audio_pll_allowed = false;
- } else {
- gck->audio_pll_allowed = false;
- }
-
+ id, pll_audio, &range);
if (IS_ERR(hw))
continue;
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 2f97a84..90988e7 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -162,7 +162,7 @@ at91_clk_register_main_osc(struct regmap *regmap,
if (bypass)
regmap_update_bits(regmap,
AT91_CKGR_MOR, MOR_KEY_MASK |
- AT91_PMC_MOSCEN,
+ AT91_PMC_OSCBYPASS,
AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
hw = &osc->hw;
@@ -354,7 +354,10 @@ static int clk_main_probe_frequency(struct regmap *regmap)
regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
if (mcfr & AT91_PMC_MAINRDY)
return 0;
- usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
+ if (system_state < SYSTEM_RUNNING)
+ udelay(MAINF_LOOP_MIN_WAIT);
+ else
+ usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
} while (time_before(prep_time, timeout));
return -ETIMEDOUT;
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index ab6ecef..43ba2a8 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -74,7 +74,10 @@ static int clk_slow_osc_prepare(struct clk_hw *hw)
writel(tmp | AT91_SCKC_OSC32EN, sckcr);
- usleep_range(osc->startup_usec, osc->startup_usec + 1);
+ if (system_state < SYSTEM_RUNNING)
+ udelay(osc->startup_usec);
+ else
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
return 0;
}
@@ -197,7 +200,10 @@ static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
- usleep_range(osc->startup_usec, osc->startup_usec + 1);
+ if (system_state < SYSTEM_RUNNING)
+ udelay(osc->startup_usec);
+ else
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
return 0;
}
@@ -310,7 +316,10 @@ static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
writel(tmp, sckcr);
- usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
+ if (system_state < SYSTEM_RUNNING)
+ udelay(SLOWCK_SW_TIME_USEC);
+ else
+ usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
return 0;
}
@@ -443,7 +452,10 @@ static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
return 0;
}
- usleep_range(osc->startup_usec, osc->startup_usec + 1);
+ if (system_state < SYSTEM_RUNNING)
+ udelay(osc->startup_usec);
+ else
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
osc->prepared = true;
return 0;
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 2eb88b0..673c6de 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -248,7 +248,7 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
else
clk = clk_register_gpio_gate(&pdev->dev, node->name,
parent_names ? parent_names[0] : NULL, gpiod,
- 0);
+ CLK_SET_RATE_PARENT);
if (IS_ERR(clk))
return PTR_ERR(clk);
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
index a907555..bf3b6a4 100644
--- a/drivers/clk/clk-stm32mp1.c
+++ b/drivers/clk/clk-stm32mp1.c
@@ -121,7 +121,7 @@ static const char * const cpu_src[] = {
};
static const char * const axi_src[] = {
- "ck_hsi", "ck_hse", "pll2_p", "pll3_p"
+ "ck_hsi", "ck_hse", "pll2_p"
};
static const char * const per_src[] = {
@@ -225,19 +225,19 @@ static const char * const usart6_src[] = {
};
static const char * const fdcan_src[] = {
- "ck_hse", "pll3_q", "pll4_q"
+ "ck_hse", "pll3_q", "pll4_q", "pll4_r"
};
static const char * const sai_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
};
static const char * const sai2_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r"
};
static const char * const adc12_src[] = {
- "pll4_q", "ck_per"
+ "pll4_r", "ck_per", "pll3_q"
};
static const char * const dsi_src[] = {
@@ -269,7 +269,7 @@ static const struct clk_div_table axi_div_table[] = {
static const struct clk_div_table mcu_div_table[] = {
{ 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
{ 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 },
- { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 },
+ { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 },
{ 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 },
{ 0 },
};
@@ -1286,10 +1286,11 @@ _clk_stm32_register_composite(struct device *dev,
MGATE_MP1(_id, _name, _parent, _flags, _mgate)
#define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\
- COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\
- _MGATE_MP1(_mgate),\
- _MMUX(_mmux),\
- _NO_DIV)
+ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE |\
+ CLK_SET_RATE_NO_REPARENT | _flags,\
+ _MGATE_MP1(_mgate),\
+ _MMUX(_mmux),\
+ _NO_DIV)
enum {
G_SAI1,
@@ -1655,8 +1656,8 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
static const struct clock_config stm32mp1_clock_cfg[] = {
/* Oscillator divider */
- DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2,
- CLK_DIVIDER_READ_ONLY),
+ DIV(NO_ID, "clk-hsi-div", "clk-hsi", CLK_DIVIDER_POWER_OF_TWO,
+ RCC_HSICFGR, 0, 2, CLK_DIVIDER_READ_ONLY),
/* External / Internal Oscillators */
GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0),
@@ -1952,7 +1953,8 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU),
MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12),
- COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE,
+ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
_NO_GATE,
_MMUX(M_ETHCK),
_DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)),
diff --git a/drivers/clk/imgtec/clk-boston.c b/drivers/clk/imgtec/clk-boston.c
index f5d54a6..dddda45 100644
--- a/drivers/clk/imgtec/clk-boston.c
+++ b/drivers/clk/imgtec/clk-boston.c
@@ -73,31 +73,39 @@ static void __init clk_boston_setup(struct device_node *np)
hw = clk_hw_register_fixed_rate(NULL, "input", NULL, 0, in_freq);
if (IS_ERR(hw)) {
pr_err("failed to register input clock: %ld\n", PTR_ERR(hw));
- goto error;
+ goto fail_input;
}
onecell->hws[BOSTON_CLK_INPUT] = hw;
hw = clk_hw_register_fixed_rate(NULL, "sys", "input", 0, sys_freq);
if (IS_ERR(hw)) {
pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw));
- goto error;
+ goto fail_sys;
}
onecell->hws[BOSTON_CLK_SYS] = hw;
hw = clk_hw_register_fixed_rate(NULL, "cpu", "input", 0, cpu_freq);
if (IS_ERR(hw)) {
pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw));
- goto error;
+ goto fail_cpu;
}
onecell->hws[BOSTON_CLK_CPU] = hw;
err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, onecell);
- if (err)
+ if (err) {
pr_err("failed to add DT provider: %d\n", err);
+ goto fail_clk_add;
+ }
return;
-error:
+fail_clk_add:
+ clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_CPU]);
+fail_cpu:
+ clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_SYS]);
+fail_sys:
+ clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_INPUT]);
+fail_input:
kfree(onecell);
}
diff --git a/drivers/clk/keystone/Kconfig b/drivers/clk/keystone/Kconfig
index 7e9f017..b04927d 100644
--- a/drivers/clk/keystone/Kconfig
+++ b/drivers/clk/keystone/Kconfig
@@ -7,7 +7,7 @@
config TI_SCI_CLK
tristate "TI System Control Interface clock drivers"
- depends on (ARCH_KEYSTONE || COMPILE_TEST) && OF
+ depends on (ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST) && OF
depends on TI_SCI_PROTOCOL
default ARCH_KEYSTONE
---help---
diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c
index 16e5677..6c7eaa2 100644
--- a/drivers/clk/mediatek/clk-cpumux.c
+++ b/drivers/clk/mediatek/clk-cpumux.c
@@ -53,7 +53,7 @@ static const struct clk_ops clk_cpumux_ops = {
.set_parent = clk_cpumux_set_parent,
};
-static struct clk __init *
+static struct clk *
mtk_clk_register_cpumux(const struct mtk_composite *mux,
struct regmap *regmap)
{
@@ -84,9 +84,9 @@ mtk_clk_register_cpumux(const struct mtk_composite *mux,
return clk;
}
-int __init mtk_clk_register_cpumuxes(struct device_node *node,
- const struct mtk_composite *clks, int num,
- struct clk_onecell_data *clk_data)
+int mtk_clk_register_cpumuxes(struct device_node *node,
+ const struct mtk_composite *clks, int num,
+ struct clk_onecell_data *clk_data)
{
int i;
struct clk *clk;
diff --git a/drivers/clk/mediatek/clk-mt7622.c b/drivers/clk/mediatek/clk-mt7622.c
index 92f7e32..a8aecef 100644
--- a/drivers/clk/mediatek/clk-mt7622.c
+++ b/drivers/clk/mediatek/clk-mt7622.c
@@ -513,7 +513,7 @@ static const struct mtk_gate peri_clks[] = {
GATE_PERI1(CLK_PERI_IRTX_PD, "peri_irtx_pd", "irtx_sel", 2),
};
-static struct mtk_composite infra_muxes[] __initdata = {
+static struct mtk_composite infra_muxes[] = {
MUX(CLK_INFRA_MUX1_SEL, "infra_mux1_sel", infra_mux1_parents,
0x000, 2, 2),
};
@@ -652,7 +652,7 @@ static int mtk_topckgen_init(struct platform_device *pdev)
return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
}
-static int __init mtk_infrasys_init(struct platform_device *pdev)
+static int mtk_infrasys_init(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct clk_onecell_data *clk_data;
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 4d4f6d8..b039909e 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -295,6 +295,12 @@ static struct clk_regmap gxl_hdmi_pll = {
.shift = 9,
.width = 5,
},
+ /*
+ * On gxl, there is a register shift due to
+ * HHI_HDMI_PLL_CNTL1 which does not exist on gxbb,
+ * so we use the HHI_HDMI_PLL_CNTL2 define from GXBB
+ * instead which is defined at the same offset.
+ */
.frac = {
/*
* On gxl, there is a register shift due to
@@ -304,7 +310,7 @@ static struct clk_regmap gxl_hdmi_pll = {
*/
.reg_off = HHI_HDMI_PLL_CNTL + 4,
.shift = 0,
- .width = 12,
+ .width = 10,
},
.od = {
.reg_off = HHI_HDMI_PLL_CNTL + 8,
@@ -836,6 +842,7 @@ static struct clk_regmap gxbb_sar_adc_clk_div = {
.ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sar_adc_clk_sel" },
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 9d79ff8..e90af55 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -144,7 +144,7 @@ static struct clk_regmap meson8b_vid_pll = {
},
.n = {
.reg_off = HHI_VID_PLL_CNTL,
- .shift = 9,
+ .shift = 10,
.width = 5,
},
.od = {
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c
index 0fc75c3..d083b86 100644
--- a/drivers/clk/mmp/clk-of-mmp2.c
+++ b/drivers/clk/mmp/clk-of-mmp2.c
@@ -227,8 +227,8 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
/* The gate clocks has mux parent. */
{MMP2_CLK_SDH0, "sdh0_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH0, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
{MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
- {MMP2_CLK_SDH1, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
- {MMP2_CLK_SDH1, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
+ {MMP2_CLK_SDH2, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
+ {MMP2_CLK_SDH3, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
{MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock},
{MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock},
{MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x1b, 0x1b, 0x0, 0, &disp1_lock},
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
index d40b63e7..b44c4cf 100644
--- a/drivers/clk/pxa/clk-pxa27x.c
+++ b/drivers/clk/pxa/clk-pxa27x.c
@@ -463,6 +463,7 @@ struct dummy_clk {
};
static struct dummy_clk dummy_clks[] __initdata = {
DUMMY_CLK(NULL, "pxa27x-gpio", "osc_32_768khz"),
+ DUMMY_CLK(NULL, "pxa-rtc", "osc_32_768khz"),
DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"),
DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"),
};
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index fa638d5..0d5ed47 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -451,6 +451,24 @@
Say Y if you want to support camera devices and functionality such as
capturing pictures.
+config SDM_DEBUGCC_LAGOON
+ tristate "LAGOON Debug Clock Controller"
+ select SDM_GCC_LAGOON
+ help
+ Support for the debug clock controller on Qualcomm Technologies, Inc
+ LAGOON devices.
+ Say Y if you want to support the clock measurement functionality
+ of the clocks.
+
+config SDM_DISPCC_LAGOON
+ tristate "LAGOON Display Clock Controller"
+ select SDM_GCC_LAGOON
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc.
+ LAGOON devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
config SDM_GCC_LAGOON
tristate "LAGOON Global Clock Controller"
depends on COMMON_CLK_QCOM
@@ -470,6 +488,15 @@
Say Y if you want to support graphics controller devices
and it's functionalities.
+config SDM_NPUCC_LAGOON
+ tristate "LAGOON NPU Clock Controller"
+ select SDM_GCC_LAGOON
+ help
+ Support for the NPU clock controller on Qualcomm Technologies, Inc.
+ LAGOON devices.
+ Say Y if you want to enable use of the Network Processing Unit in
+ order to speed up certain types of calculations.
+
config SDM_VIDEOCC_LAGOON
tristate "LAGOON Video Clock Controller"
select SDM_GCC_LAGOON
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 7dd0dc7..0168829 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -50,10 +50,13 @@
obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_SDM_CAMCC_LAGOON) += camcc-lagoon.o
+obj-$(CONFIG_SDM_DEBUGCC_LAGOON) += debugcc-lagoon.o
obj-$(CONFIG_SDM_DISPCC_845) += dispcc-sdm845.o
+obj-$(CONFIG_SDM_DISPCC_LAGOON) += dispcc-lagoon.o
obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o
obj-$(CONFIG_SDM_GCC_LAGOON) += gcc-lagoon.o
obj-$(CONFIG_SDM_GPUCC_LAGOON) += gpucc-lagoon.o
+obj-$(CONFIG_SDM_NPUCC_LAGOON) += npucc-lagoon.o
obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
obj-$(CONFIG_SDM_VIDEOCC_LAGOON) += videocc-lagoon.o
obj-$(CONFIG_SM_CAMCC_LITO) += camcc-lito.o
diff --git a/drivers/clk/qcom/camcc-kona.c b/drivers/clk/qcom/camcc-kona.c
index c661977..4f14288 100644
--- a/drivers/clk/qcom/camcc-kona.c
+++ b/drivers/clk/qcom/camcc-kona.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -181,7 +181,7 @@ static const struct alpha_pll_config cam_cc_pll0_config = {
.alpha = 0x8000,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00003100,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -257,7 +257,7 @@ static const struct alpha_pll_config cam_cc_pll1_config = {
.alpha = 0x4000,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000100,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -326,7 +326,7 @@ static const struct alpha_pll_config cam_cc_pll2_config_sm8250_v2 = {
.cal_l = 0x4B,
.alpha = 0x0,
.config_ctl_val = 0x08200920,
- .config_ctl_hi_val = 0x05008011,
+ .config_ctl_hi_val = 0x05002015,
.config_ctl_hi1_val = 0x00000000,
.user_ctl_val = 0x00000100,
.user_ctl_hi_val = 0x00000000,
@@ -347,9 +347,9 @@ static struct clk_alpha_pll cam_cc_pll2 = {
.vdd_class = &vdd_mx,
.num_rate_max = VDD_NUM,
.rate_max = (unsigned long[VDD_NUM]) {
- [VDD_LOWER] = 1600000000,
- [VDD_LOW] = 2000000000,
- [VDD_NOMINAL] = 2900000000,
+ [VDD_LOWER] = 1800000000,
+ [VDD_LOW] = 2400000000,
+ [VDD_NOMINAL] = 3000000000,
[VDD_HIGH] = 3600000000},
},
},
@@ -382,7 +382,7 @@ static const struct alpha_pll_config cam_cc_pll3_config = {
.alpha = 0x7555,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000100,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -437,7 +437,7 @@ static const struct alpha_pll_config cam_cc_pll4_config = {
.alpha = 0x7555,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000100,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -2803,6 +2803,7 @@ static int cam_cc_kona_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "Registered CAM CC clocks\n");
+
return ret;
}
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 201685c..b3ef447 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2015, 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -1328,6 +1328,16 @@ const struct clk_ops clk_alpha_pll_postdiv_ro_ops = {
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops);
+static void clk_alpha_pll_custom_configure(struct clk_alpha_pll *pll,
+ struct regmap *regmap, const struct alpha_pll_config *config)
+{
+ int i;
+
+ for (i = 0; i < config->num_custom_reg; i++)
+ regmap_write(regmap, pll->offset + config->custom_reg_offset[i],
+ config->custom_reg_val[i]);
+}
+
int clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
@@ -1379,6 +1389,8 @@ int clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val);
}
+ clk_alpha_pll_custom_configure(pll, regmap, config);
+
regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS,
PLL_UPDATE_BYPASS);
@@ -1786,16 +1798,6 @@ static int lucid_pll_is_enabled(struct clk_alpha_pll *pll,
(mode_regval & PLL_OUTCTRL));
}
-static void clk_alpha_pll_custom_configure(struct clk_alpha_pll *pll,
- struct regmap *regmap, const struct alpha_pll_config *config)
-{
- int i;
-
- for (i = 0; i < config->num_custom_reg; i++)
- regmap_write(regmap, pll->offset + config->custom_reg_offset[i],
- config->custom_reg_val[i]);
-}
-
void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index dcdba93..e104484 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -345,6 +345,8 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
if (clk_flags & CLK_SET_RATE_PARENT) {
rate = f->freq;
if (f->pre_div) {
+ if (!rate)
+ rate = req->rate;
rate /= 2;
rate *= f->pre_div + 1;
}
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index d1bdfe3..9e72243 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, Linaro Limited
- * Copyright (c) 2014, 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016-2020, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -857,11 +857,109 @@ static const struct rpm_smd_clk_desc rpm_clk_bengal = {
.num_clks = ARRAY_SIZE(bengal_clks),
};
+DEFINE_CLK_SMD_RPM_XO_BUFFER(scuba, ln_bb_clk2, ln_bb_clk2_a, 0x2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(scuba, rf_clk3, rf_clk3_a, 6);
+
+DEFINE_CLK_SMD_RPM(scuba, qpic_clk, qpic_a_clk, RPM_SMD_QPIC_CLK, 0);
+
+/* Scuba */
+static struct clk_hw *scuba_clks[] = {
+ [RPM_SMD_XO_CLK_SRC] = &bengal_bi_tcxo.hw,
+ [RPM_SMD_XO_A_CLK_SRC] = &bengal_bi_tcxo_ao.hw,
+ [RPM_SMD_SNOC_CLK] = &bengal_snoc_clk.hw,
+ [RPM_SMD_SNOC_A_CLK] = &bengal_snoc_a_clk.hw,
+ [RPM_SMD_BIMC_CLK] = &bengal_bimc_clk.hw,
+ [RPM_SMD_BIMC_A_CLK] = &bengal_bimc_a_clk.hw,
+ [RPM_SMD_QDSS_CLK] = &bengal_qdss_clk.hw,
+ [RPM_SMD_QDSS_A_CLK] = &bengal_qdss_a_clk.hw,
+ [RPM_SMD_LN_BB_CLK2] = &scuba_ln_bb_clk2.hw,
+ [RPM_SMD_LN_BB_CLK2_A] = &scuba_ln_bb_clk2_a.hw,
+ [RPM_SMD_RF_CLK3] = &scuba_rf_clk3.hw,
+ [RPM_SMD_RF_CLK3_A] = &scuba_rf_clk3_a.hw,
+ [RPM_SMD_CNOC_CLK] = &bengal_cnoc_clk.hw,
+ [RPM_SMD_CNOC_A_CLK] = &bengal_cnoc_a_clk.hw,
+ [RPM_SMD_IPA_CLK] = &bengal_ipa_clk.hw,
+ [RPM_SMD_IPA_A_CLK] = &bengal_ipa_a_clk.hw,
+ [RPM_SMD_QUP_CLK] = &bengal_qup_clk.hw,
+ [RPM_SMD_QUP_A_CLK] = &bengal_qup_a_clk.hw,
+ [RPM_SMD_MMRT_CLK] = &bengal_mmrt_clk.hw,
+ [RPM_SMD_MMRT_A_CLK] = &bengal_mmrt_a_clk.hw,
+ [RPM_SMD_MMNRT_CLK] = &bengal_mmnrt_clk.hw,
+ [RPM_SMD_MMNRT_A_CLK] = &bengal_mmnrt_a_clk.hw,
+ [RPM_SMD_SNOC_PERIPH_CLK] = &bengal_snoc_periph_clk.hw,
+ [RPM_SMD_SNOC_PERIPH_A_CLK] = &bengal_snoc_periph_a_clk.hw,
+ [RPM_SMD_SNOC_LPASS_CLK] = &bengal_snoc_lpass_clk.hw,
+ [RPM_SMD_SNOC_LPASS_A_CLK] = &bengal_snoc_lpass_a_clk.hw,
+ [RPM_SMD_CE1_CLK] = &bengal_ce1_clk.hw,
+ [RPM_SMD_CE1_A_CLK] = &bengal_ce1_a_clk.hw,
+ [RPM_SMD_QPIC_CLK] = &scuba_qpic_clk.hw,
+ [RPM_SMD_QPIC_A_CLK] = &scuba_qpic_a_clk.hw,
+ [CNOC_MSMBUS_CLK] = &cnoc_msmbus_clk.hw,
+ [CNOC_MSMBUS_A_CLK] = &cnoc_msmbus_a_clk.hw,
+ [SNOC_KEEPALIVE_A_CLK] = &snoc_keepalive_a_clk.hw,
+ [CNOC_KEEPALIVE_A_CLK] = &cnoc_keepalive_a_clk.hw,
+ [SNOC_MSMBUS_CLK] = &snoc_msmbus_clk.hw,
+ [SNOC_MSMBUS_A_CLK] = &snoc_msmbus_a_clk.hw,
+ [BIMC_MSMBUS_CLK] = &bimc_msmbus_clk.hw,
+ [BIMC_MSMBUS_A_CLK] = &bimc_msmbus_a_clk.hw,
+ [CPP_MMNRT_MSMBUS_CLK] = &cpp_mmnrt_msmbus_clk.hw,
+ [CPP_MMNRT_MSMBUS_A_CLK] = &cpp_mmnrt_msmbus_a_clk.hw,
+ [JPEG_MMNRT_MSMBUS_CLK] = &jpeg_mmnrt_msmbus_clk.hw,
+ [JPEG_MMNRT_MSMBUS_A_CLK] = &jpeg_mmnrt_msmbus_a_clk.hw,
+ [VENUS_MMNRT_MSMBUS_CLK] = &venus_mmnrt_msmbus_clk.hw,
+ [VENUS_MMNRT_MSMBUS_A_CLK] = &venus_mmnrt_msmbus_a_clk.hw,
+ [ARM9_MMNRT_MSMBUS_CLK] = &arm9_mmnrt_msmbus_clk.hw,
+ [ARM9_MMNRT_MSMBUS_A_CLK] = &arm9_mmnrt_msmbus_a_clk.hw,
+ [VFE_MMRT_MSMBUS_CLK] = &vfe_mmrt_msmbus_clk.hw,
+ [VFE_MMRT_MSMBUS_A_CLK] = &vfe_mmrt_msmbus_a_clk.hw,
+ [MDP_MMRT_MSMBUS_CLK] = &mdp_mmrt_msmbus_clk.hw,
+ [MDP_MMRT_MSMBUS_A_CLK] = &mdp_mmrt_msmbus_a_clk.hw,
+ [QUP0_MSMBUS_SNOC_PERIPH_CLK] = &qup0_msmbus_snoc_periph_clk.hw,
+ [QUP0_MSMBUS_SNOC_PERIPH_A_CLK] = &qup0_msmbus_snoc_periph_a_clk.hw,
+ [QUP1_MSMBUS_SNOC_PERIPH_CLK] = &qup1_msmbus_snoc_periph_clk.hw,
+ [QUP1_MSMBUS_SNOC_PERIPH_A_CLK] = &qup1_msmbus_snoc_periph_a_clk.hw,
+ [DAP_MSMBUS_SNOC_PERIPH_CLK] = &dap_msmbus_snoc_periph_clk.hw,
+ [DAP_MSMBUS_SNOC_PERIPH_A_CLK] = &dap_msmbus_snoc_periph_a_clk.hw,
+ [SDC1_MSMBUS_SNOC_PERIPH_CLK] = &sdc1_msmbus_snoc_periph_clk.hw,
+ [SDC1_MSMBUS_SNOC_PERIPH_A_CLK] = &sdc1_msmbus_snoc_periph_a_clk.hw,
+ [SDC2_MSMBUS_SNOC_PERIPH_CLK] = &sdc2_msmbus_snoc_periph_clk.hw,
+ [SDC2_MSMBUS_SNOC_PERIPH_A_CLK] = &sdc2_msmbus_snoc_periph_a_clk.hw,
+ [CRYPTO_MSMBUS_SNOC_PERIPH_CLK] = &crypto_msmbus_snoc_periph_clk.hw,
+ [CRYPTO_MSMBUS_SNOC_PERIPH_A_CLK] =
+ &crypto_msmbus_snoc_periph_a_clk.hw,
+ [SDC1_SLV_MSMBUS_SNOC_PERIPH_CLK] =
+ &sdc1_slv_msmbus_snoc_periph_clk.hw,
+ [SDC1_SLV_MSMBUS_SNOC_PERIPH_A_CLK] =
+ &sdc1_slv_msmbus_snoc_periph_a_clk.hw,
+ [SDC2_SLV_MSMBUS_SNOC_PERIPH_CLK] =
+ &sdc2_slv_msmbus_snoc_periph_clk.hw,
+ [SDC2_SLV_MSMBUS_SNOC_PERIPH_A_CLK] =
+ &sdc2_slv_msmbus_snoc_periph_a_clk.hw,
+ [MCD_CE1_CLK] = &mcd_ce1_clk.hw,
+ [QCEDEV_CE1_CLK] = &qcedev_ce1_clk.hw,
+ [QCRYPTO_CE1_CLK] = &qcrypto_ce1_clk.hw,
+ [QSEECOM_CE1_CLK] = &qseecom_ce1_clk.hw,
+ [SCM_CE1_CLK] = &scm_ce1_clk.hw,
+ [CXO_SMD_OTG_CLK] = &bi_tcxo_otg_clk.hw,
+ [CXO_SMD_PIL_PRONTO_CLK] = &bi_tcxo_pil_pronto_clk.hw,
+ [CXO_SMD_PIL_MSS_CLK] = &bi_tcxo_pil_mss_clk.hw,
+ [CXO_SMD_WLAN_CLK] = &bi_tcxo_wlan_clk.hw,
+ [CXO_SMD_PIL_LPASS_CLK] = &bi_tcxo_pil_lpass_clk.hw,
+ [CXO_SMD_PIL_CDSP_CLK] = &bi_tcxo_pil_cdsp_clk.hw,
+};
+
+static const struct rpm_smd_clk_desc rpm_clk_scuba = {
+ .clks = scuba_clks,
+ .num_rpm_clks = RPM_SMD_QPIC_A_CLK,
+ .num_clks = ARRAY_SIZE(scuba_clks),
+};
+
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
{ .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 },
{ .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 },
{ .compatible = "qcom,rpmcc-bengal", .data = &rpm_clk_bengal},
+ { .compatible = "qcom,rpmcc-scuba", .data = &rpm_clk_scuba},
{ }
};
MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
@@ -872,15 +970,17 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
struct clk *clk;
struct rpm_cc *rcc;
struct clk_onecell_data *data;
- int ret, is_bengal;
+ int ret, is_bengal, is_scuba;
size_t num_clks, i;
struct clk_hw **hw_clks;
const struct rpm_smd_clk_desc *desc;
-
is_bengal = of_device_is_compatible(pdev->dev.of_node,
"qcom,rpmcc-bengal");
- if (is_bengal) {
+
+ is_scuba = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,rpmcc-scuba");
+ if (is_bengal || is_scuba) {
ret = clk_vote_bimc(&bengal_bimc_clk.hw, INT_MAX);
if (ret < 0)
return ret;
@@ -949,7 +1049,7 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
if (ret)
goto err;
- if (is_bengal) {
+ if (is_bengal || is_scuba) {
/*
* Keep an active vote on CXO in case no other driver
* votes for it.
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 1a87ad0..da45867 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -32,6 +32,9 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
if (!f)
return NULL;
+ if (!f->freq)
+ return f;
+
for (; f->freq; f++)
if (rate <= f->freq)
return f;
diff --git a/drivers/clk/qcom/debugcc-lagoon.c b/drivers/clk/qcom/debugcc-lagoon.c
new file mode 100644
index 0000000..2d2138b
--- /dev/null
+++ b/drivers/clk/qcom/debugcc-lagoon.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "clk: %s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "clk-debug.h"
+#include "common.h"
+
+static struct measure_clk_data debug_mux_priv = {
+ .ctl_reg = 0x35F24,
+ .status_reg = 0x35F28,
+ .xo_div4_cbcr = 0x2C008,
+};
+
+static const char *const cpu_cc_debug_mux_parent_names[] = {
+ "l3_clk",
+ "perfcl_clk",
+ "pwrcl_clk",
+};
+
+static int cpu_cc_debug_mux_sels[] = {
+ 0x41, /* l3_clk */
+ 0x21, /* perf_clk */
+ 0x25, /* pwrcl_clk */
+};
+
+static int apss_cc_debug_mux_pre_divs[] = {
+ 0x4, /* l3_clk */
+ 0x4, /* perfcl_clk */
+ 0x8, /* pwrcl_clk */
+};
+
+static struct clk_debug_mux cpu_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x18,
+ .post_div_offset = 0x18,
+ .cbcr_offset = 0x0,
+ .src_sel_mask = 0x7F0,
+ .src_sel_shift = 4,
+ .post_div_mask = 0x7800,
+ .post_div_shift = 11,
+ .post_div_val = 1,
+ .mux_sels = cpu_cc_debug_mux_sels,
+ .pre_div_vals = apss_cc_debug_mux_pre_divs,
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = cpu_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(cpu_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const cam_cc_debug_mux_parent_names[] = {
+ "cam_cc_bps_ahb_clk",
+ "cam_cc_bps_areg_clk",
+ "cam_cc_bps_axi_clk",
+ "cam_cc_bps_clk",
+ "cam_cc_camnoc_axi_clk",
+ "cam_cc_cci_0_clk",
+ "cam_cc_cci_1_clk",
+ "cam_cc_core_ahb_clk",
+ "cam_cc_cpas_ahb_clk",
+ "cam_cc_csi0phytimer_clk",
+ "cam_cc_csi1phytimer_clk",
+ "cam_cc_csi2phytimer_clk",
+ "cam_cc_csi3phytimer_clk",
+ "cam_cc_csiphy0_clk",
+ "cam_cc_csiphy1_clk",
+ "cam_cc_csiphy2_clk",
+ "cam_cc_csiphy3_clk",
+ "cam_cc_icp_clk",
+ "cam_cc_icp_ts_clk",
+ "cam_cc_ife_0_axi_clk",
+ "cam_cc_ife_0_clk",
+ "cam_cc_ife_0_cphy_rx_clk",
+ "cam_cc_ife_0_csid_clk",
+ "cam_cc_ife_0_dsp_clk",
+ "cam_cc_ife_1_axi_clk",
+ "cam_cc_ife_1_clk",
+ "cam_cc_ife_1_cphy_rx_clk",
+ "cam_cc_ife_1_csid_clk",
+ "cam_cc_ife_1_dsp_clk",
+ "cam_cc_ife_2_axi_clk",
+ "cam_cc_ife_2_clk",
+ "cam_cc_ife_2_cphy_rx_clk",
+ "cam_cc_ife_2_csid_clk",
+ "cam_cc_ife_2_dsp_clk",
+ "cam_cc_ife_lite_clk",
+ "cam_cc_ife_lite_cphy_rx_clk",
+ "cam_cc_ife_lite_csid_clk",
+ "cam_cc_ipe_0_ahb_clk",
+ "cam_cc_ipe_0_areg_clk",
+ "cam_cc_ipe_0_axi_clk",
+ "cam_cc_ipe_0_clk",
+ "cam_cc_jpeg_clk",
+ "cam_cc_lrme_clk",
+ "cam_cc_mclk0_clk",
+ "cam_cc_mclk1_clk",
+ "cam_cc_mclk2_clk",
+ "cam_cc_mclk3_clk",
+ "cam_cc_mclk4_clk",
+ "cam_cc_soc_ahb_clk",
+ "cam_cc_sys_tmr_clk",
+};
+
+static int cam_cc_debug_mux_sels[] = {
+ 0x12, /* cam_cc_bps_ahb_clk */
+ 0x11, /* cam_cc_bps_areg_clk */
+ 0x10, /* cam_cc_bps_axi_clk */
+ 0xE, /* cam_cc_bps_clk */
+ 0x38, /* cam_cc_camnoc_axi_clk */
+ 0x34, /* cam_cc_cci_0_clk */
+ 0x35, /* cam_cc_cci_1_clk */
+ 0x3B, /* cam_cc_core_ahb_clk */
+ 0x37, /* cam_cc_cpas_ahb_clk */
+ 0x6, /* cam_cc_csi0phytimer_clk */
+ 0x8, /* cam_cc_csi1phytimer_clk */
+ 0xA, /* cam_cc_csi2phytimer_clk */
+ 0xC, /* cam_cc_csi3phytimer_clk */
+ 0x7, /* cam_cc_csiphy0_clk */
+ 0x9, /* cam_cc_csiphy1_clk */
+ 0xB, /* cam_cc_csiphy2_clk */
+ 0xD, /* cam_cc_csiphy3_clk */
+ 0x32, /* cam_cc_icp_clk */
+ 0x30, /* cam_cc_icp_ts_clk */
+ 0x1E, /* cam_cc_ife_0_axi_clk */
+ 0x18, /* cam_cc_ife_0_clk */
+ 0x1D, /* cam_cc_ife_0_cphy_rx_clk */
+ 0x1B, /* cam_cc_ife_0_csid_clk */
+ 0x1A, /* cam_cc_ife_0_dsp_clk */
+ 0x23, /* cam_cc_ife_1_axi_clk */
+ 0x1F, /* cam_cc_ife_1_clk */
+ 0x22, /* cam_cc_ife_1_cphy_rx_clk */
+ 0x21, /* cam_cc_ife_1_csid_clk */
+ 0x20, /* cam_cc_ife_1_dsp_clk */
+ 0x28, /* cam_cc_ife_2_axi_clk */
+ 0x24, /* cam_cc_ife_2_clk */
+ 0x27, /* cam_cc_ife_2_cphy_rx_clk */
+ 0x26, /* cam_cc_ife_2_csid_clk */
+ 0x25, /* cam_cc_ife_2_dsp_clk */
+ 0x29, /* cam_cc_ife_lite_clk */
+ 0x2B, /* cam_cc_ife_lite_cphy_rx_clk */
+ 0x2A, /* cam_cc_ife_lite_csid_clk */
+ 0x17, /* cam_cc_ipe_0_ahb_clk */
+ 0x16, /* cam_cc_ipe_0_areg_clk */
+ 0x15, /* cam_cc_ipe_0_axi_clk */
+ 0x13, /* cam_cc_ipe_0_clk */
+ 0x2C, /* cam_cc_jpeg_clk */
+ 0x36, /* cam_cc_lrme_clk */
+ 0x1, /* cam_cc_mclk0_clk */
+ 0x2, /* cam_cc_mclk1_clk */
+ 0x3, /* cam_cc_mclk2_clk */
+ 0x4, /* cam_cc_mclk3_clk */
+ 0x5, /* cam_cc_mclk4_clk */
+ 0x3A, /* cam_cc_soc_ahb_clk */
+ 0x33, /* cam_cc_sys_tmr_clk */
+};
+
+static struct clk_debug_mux cam_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x15100,
+ .post_div_offset = 0x15004,
+ .cbcr_offset = 0x15008,
+ .src_sel_mask = 0xFF,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x3,
+ .post_div_shift = 0,
+ .post_div_val = 2,
+ .mux_sels = cam_cc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "cam_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = cam_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(cam_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const disp_cc_debug_mux_parent_names[] = {
+ "disp_cc_mdss_ahb_clk",
+ "disp_cc_mdss_byte0_clk",
+ "disp_cc_mdss_byte0_intf_clk",
+ "disp_cc_mdss_dp_aux_clk",
+ "disp_cc_mdss_dp_crypto_clk",
+ "disp_cc_mdss_dp_link_clk",
+ "disp_cc_mdss_dp_link_intf_clk",
+ "disp_cc_mdss_dp_pixel_clk",
+ "disp_cc_mdss_esc0_clk",
+ "disp_cc_mdss_mdp_clk",
+ "disp_cc_mdss_mdp_lut_clk",
+ "disp_cc_mdss_non_gdsc_ahb_clk",
+ "disp_cc_mdss_pclk0_clk",
+ "disp_cc_mdss_rot_clk",
+ "disp_cc_mdss_rscc_ahb_clk",
+ "disp_cc_mdss_rscc_vsync_clk",
+ "disp_cc_mdss_vsync_clk",
+ "disp_cc_sleep_clk",
+ "disp_cc_xo_clk",
+};
+
+static int disp_cc_debug_mux_sels[] = {
+ 0x14, /* disp_cc_mdss_ahb_clk */
+ 0xC, /* disp_cc_mdss_byte0_clk */
+ 0xD, /* disp_cc_mdss_byte0_intf_clk */
+ 0x13, /* disp_cc_mdss_dp_aux_clk */
+ 0x11, /* disp_cc_mdss_dp_crypto_clk */
+ 0xF, /* disp_cc_mdss_dp_link_clk */
+ 0x10, /* disp_cc_mdss_dp_link_intf_clk */
+ 0x12, /* disp_cc_mdss_dp_pixel_clk */
+ 0xE, /* disp_cc_mdss_esc0_clk */
+ 0x8, /* disp_cc_mdss_mdp_clk */
+ 0xA, /* disp_cc_mdss_mdp_lut_clk */
+ 0x15, /* disp_cc_mdss_non_gdsc_ahb_clk */
+ 0x7, /* disp_cc_mdss_pclk0_clk */
+ 0x9, /* disp_cc_mdss_rot_clk */
+ 0x17, /* disp_cc_mdss_rscc_ahb_clk */
+ 0x16, /* disp_cc_mdss_rscc_vsync_clk */
+ 0xB, /* disp_cc_mdss_vsync_clk */
+ 0x1D, /* disp_cc_sleep_clk */
+ 0x1E, /* disp_cc_xo_clk */
+};
+
+static struct clk_debug_mux disp_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x7000,
+ .post_div_offset = 0x3000,
+ .cbcr_offset = 0x3004,
+ .src_sel_mask = 0xFF,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x3,
+ .post_div_shift = 0,
+ .post_div_val = 4,
+ .mux_sels = disp_cc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = disp_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(disp_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const gcc_debug_mux_parent_names[] = {
+ "cam_cc_debug_mux",
+ "cpu_cc_debug_mux",
+ "disp_cc_debug_mux",
+ "gcc_aggre_ufs_phy_axi_clk",
+ "gcc_aggre_usb3_prim_axi_clk",
+ "gcc_boot_rom_ahb_clk",
+ "gcc_camera_ahb_clk",
+ "gcc_camera_axi_clk",
+ "gcc_camera_throttle_nrt_axi_clk",
+ "gcc_camera_throttle_rt_axi_clk",
+ "gcc_camera_xo_clk",
+ "gcc_ce1_ahb_clk",
+ "gcc_ce1_axi_clk",
+ "gcc_ce1_clk",
+ "gcc_cfg_noc_usb3_prim_axi_clk",
+ "gcc_cpuss_ahb_clk",
+ "gcc_cpuss_gnoc_clk",
+ "gcc_cpuss_rbcpr_clk",
+ "gcc_ddrss_gpu_axi_clk",
+ "gcc_disp_ahb_clk",
+ "gcc_disp_axi_clk",
+ "gcc_disp_cc_sleep_clk",
+ "gcc_disp_cc_xo_clk",
+ "gcc_disp_gpll0_clk",
+ "gcc_disp_throttle_axi_clk",
+ "gcc_disp_xo_clk",
+ "gcc_gp1_clk",
+ "gcc_gp2_clk",
+ "gcc_gp3_clk",
+ "gcc_gpu_cfg_ahb_clk",
+ "gcc_gpu_gpll0_clk",
+ "gcc_gpu_gpll0_div_clk",
+ "gcc_gpu_memnoc_gfx_clk",
+ "gcc_gpu_snoc_dvm_gfx_clk",
+ "gcc_npu_axi_clk",
+ "gcc_npu_bwmon_axi_clk",
+ "gcc_npu_bwmon_dma_cfg_ahb_clk",
+ "gcc_npu_bwmon_dsp_cfg_ahb_clk",
+ "gcc_npu_cfg_ahb_clk",
+ "gcc_npu_dma_clk",
+ "gcc_npu_gpll0_clk",
+ "gcc_npu_gpll0_div_clk",
+ "gcc_pdm2_clk",
+ "gcc_pdm_ahb_clk",
+ "gcc_pdm_xo4_clk",
+ "gcc_prng_ahb_clk",
+ "gcc_qupv3_wrap0_core_2x_clk",
+ "gcc_qupv3_wrap0_core_clk",
+ "gcc_qupv3_wrap0_s0_clk",
+ "gcc_qupv3_wrap0_s1_clk",
+ "gcc_qupv3_wrap0_s2_clk",
+ "gcc_qupv3_wrap0_s3_clk",
+ "gcc_qupv3_wrap0_s4_clk",
+ "gcc_qupv3_wrap0_s5_clk",
+ "gcc_qupv3_wrap1_core_2x_clk",
+ "gcc_qupv3_wrap1_core_clk",
+ "gcc_qupv3_wrap1_s0_clk",
+ "gcc_qupv3_wrap1_s1_clk",
+ "gcc_qupv3_wrap1_s2_clk",
+ "gcc_qupv3_wrap1_s3_clk",
+ "gcc_qupv3_wrap1_s4_clk",
+ "gcc_qupv3_wrap1_s5_clk",
+ "gcc_qupv3_wrap_0_m_ahb_clk",
+ "gcc_qupv3_wrap_0_s_ahb_clk",
+ "gcc_qupv3_wrap_1_m_ahb_clk",
+ "gcc_qupv3_wrap_1_s_ahb_clk",
+ "gcc_sdcc1_ahb_clk",
+ "gcc_sdcc1_apps_clk",
+ "gcc_sdcc1_ice_core_clk",
+ "gcc_sdcc2_ahb_clk",
+ "gcc_sdcc2_apps_clk",
+ "gcc_sys_noc_cpuss_ahb_clk",
+ "gcc_ufs_phy_ahb_clk",
+ "gcc_ufs_phy_axi_clk",
+ "gcc_ufs_phy_ice_core_clk",
+ "gcc_ufs_phy_phy_aux_clk",
+ "gcc_ufs_phy_rx_symbol_0_clk",
+ "gcc_ufs_phy_rx_symbol_1_clk",
+ "gcc_ufs_phy_tx_symbol_0_clk",
+ "gcc_ufs_phy_unipro_core_clk",
+ "gcc_usb30_prim_master_clk",
+ "gcc_usb30_prim_mock_utmi_clk",
+ "gcc_usb30_prim_sleep_clk",
+ "gcc_usb3_prim_phy_aux_clk",
+ "gcc_usb3_prim_phy_com_aux_clk",
+ "gcc_usb3_prim_phy_pipe_clk",
+ "gcc_video_ahb_clk",
+ "gcc_video_axi_clk",
+ "gcc_video_throttle_axi_clk",
+ "gcc_video_xo_clk",
+ "gpu_cc_debug_mux",
+ "mc_cc_debug_mux",
+ "measure_only_cnoc_clk",
+ "measure_only_ipa_2x_clk",
+ "measure_only_snoc_clk",
+ "npu_cc_debug_mux",
+ "video_cc_debug_mux",
+};
+
+static int gcc_debug_mux_sels[] = {
+ 0x3F, /* cam_cc_debug_mux */
+ 0xBE, /* cpu_cc_debug_mux */
+ 0x40, /* disp_cc_debug_mux */
+ 0xE2, /* gcc_aggre_ufs_phy_axi_clk */
+ 0xE1, /* gcc_aggre_usb3_prim_axi_clk */
+ 0x80, /* gcc_boot_rom_ahb_clk */
+ 0x32, /* gcc_camera_ahb_clk */
+ 0x36, /* gcc_camera_axi_clk */
+ 0x4A, /* gcc_camera_throttle_nrt_axi_clk */
+ 0x39, /* gcc_camera_throttle_rt_axi_clk */
+ 0x3C, /* gcc_camera_xo_clk */
+ 0x92, /* gcc_ce1_ahb_clk */
+ 0x91, /* gcc_ce1_axi_clk */
+ 0x90, /* gcc_ce1_clk */
+ 0x18, /* gcc_cfg_noc_usb3_prim_axi_clk */
+ 0xB7, /* gcc_cpuss_ahb_clk */
+ 0xB8, /* gcc_cpuss_gnoc_clk */
+ 0xB9, /* gcc_cpuss_rbcpr_clk */
+ 0xA5, /* gcc_ddrss_gpu_axi_clk */
+ 0x33, /* gcc_disp_ahb_clk */
+ 0x37, /* gcc_disp_axi_clk */
+ 0x49, /* gcc_disp_cc_sleep_clk */
+ 0x48, /* gcc_disp_cc_xo_clk */
+ 0x44, /* gcc_disp_gpll0_clk */
+ 0x3A, /* gcc_disp_throttle_axi_clk */
+ 0x3D, /* gcc_disp_xo_clk */
+ 0xC5, /* gcc_gp1_clk */
+ 0xC6, /* gcc_gp2_clk */
+ 0xC7, /* gcc_gp3_clk */
+ 0x105, /* gcc_gpu_cfg_ahb_clk */
+ 0x10B, /* gcc_gpu_gpll0_clk */
+ 0x10C, /* gcc_gpu_gpll0_div_clk */
+ 0x108, /* gcc_gpu_memnoc_gfx_clk */
+ 0x109, /* gcc_gpu_snoc_dvm_gfx_clk */
+ 0x116, /* gcc_npu_axi_clk */
+ 0x11C, /* gcc_npu_bwmon_axi_clk */
+ 0x11D, /* gcc_npu_bwmon_dma_cfg_ahb_clk */
+ 0x11E, /* gcc_npu_bwmon_dsp_cfg_ahb_clk */
+ 0x115, /* gcc_npu_cfg_ahb_clk */
+ 0x11B, /* gcc_npu_dma_clk */
+ 0x118, /* gcc_npu_gpll0_clk */
+ 0x119, /* gcc_npu_gpll0_div_clk */
+ 0x7D, /* gcc_pdm2_clk */
+ 0x7B, /* gcc_pdm_ahb_clk */
+ 0x7C, /* gcc_pdm_xo4_clk */
+ 0x7E, /* gcc_prng_ahb_clk */
+ 0x6A, /* gcc_qupv3_wrap0_core_2x_clk */
+ 0x69, /* gcc_qupv3_wrap0_core_clk */
+ 0x6B, /* gcc_qupv3_wrap0_s0_clk */
+ 0x6C, /* gcc_qupv3_wrap0_s1_clk */
+ 0x6D, /* gcc_qupv3_wrap0_s2_clk */
+ 0x6E, /* gcc_qupv3_wrap0_s3_clk */
+ 0x6F, /* gcc_qupv3_wrap0_s4_clk */
+ 0x70, /* gcc_qupv3_wrap0_s5_clk */
+ 0x71, /* gcc_qupv3_wrap1_core_2x_clk */
+ 0x72, /* gcc_qupv3_wrap1_core_clk */
+ 0x75, /* gcc_qupv3_wrap1_s0_clk */
+ 0x76, /* gcc_qupv3_wrap1_s1_clk */
+ 0x77, /* gcc_qupv3_wrap1_s2_clk */
+ 0x78, /* gcc_qupv3_wrap1_s3_clk */
+ 0x79, /* gcc_qupv3_wrap1_s4_clk */
+ 0x7A, /* gcc_qupv3_wrap1_s5_clk */
+ 0x67, /* gcc_qupv3_wrap_0_m_ahb_clk */
+ 0x68, /* gcc_qupv3_wrap_0_s_ahb_clk */
+ 0x73, /* gcc_qupv3_wrap_1_m_ahb_clk */
+ 0x74, /* gcc_qupv3_wrap_1_s_ahb_clk */
+ 0x112, /* gcc_sdcc1_ahb_clk */
+ 0x113, /* gcc_sdcc1_apps_clk */
+ 0x114, /* gcc_sdcc1_ice_core_clk */
+ 0x66, /* gcc_sdcc2_ahb_clk */
+ 0x65, /* gcc_sdcc2_apps_clk */
+ 0x9, /* gcc_sys_noc_cpuss_ahb_clk */
+ 0xC8, /* gcc_ufs_phy_ahb_clk */
+ 0xCC, /* gcc_ufs_phy_axi_clk */
+ 0xD1, /* gcc_ufs_phy_ice_core_clk */
+ 0xD2, /* gcc_ufs_phy_phy_aux_clk */
+ 0xCA, /* gcc_ufs_phy_rx_symbol_0_clk */
+ 0xCB, /* gcc_ufs_phy_rx_symbol_1_clk */
+ 0xC9, /* gcc_ufs_phy_tx_symbol_0_clk */
+ 0xD0, /* gcc_ufs_phy_unipro_core_clk */
+ 0x5B, /* gcc_usb30_prim_master_clk */
+ 0x5D, /* gcc_usb30_prim_mock_utmi_clk */
+ 0x5C, /* gcc_usb30_prim_sleep_clk */
+ 0x5E, /* gcc_usb3_prim_phy_aux_clk */
+ 0x5F, /* gcc_usb3_prim_phy_com_aux_clk */
+ 0x60, /* gcc_usb3_prim_phy_pipe_clk */
+ 0x31, /* gcc_video_ahb_clk */
+ 0x35, /* gcc_video_axi_clk */
+ 0x38, /* gcc_video_throttle_axi_clk */
+ 0x3B, /* gcc_video_xo_clk */
+ 0x107, /* gpu_cc_debug_mux */
+ 0xAB, /* mc_cc_debug_mux */
+ 0x14, /* measure_only_cnoc_clk */
+ 0xEC, /* measure_only_ipa_2x_clk */
+ 0x07, /* measure_only_snoc_clk */
+ 0x11A, /* npu_cc_debug_mux */
+ 0x41, /* video_cc_debug_mux */
+};
+
+static struct clk_debug_mux gcc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x35F08,
+ .post_div_offset = 0x35008,
+ .cbcr_offset = 0x3500C,
+ .src_sel_mask = 0x3FF,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x7,
+ .post_div_shift = 0,
+ .post_div_val = 4,
+ .mux_sels = gcc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = gcc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(gcc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const gpu_cc_debug_mux_parent_names[] = {
+ "gpu_cc_acd_ahb_clk",
+ "gpu_cc_acd_cxo_clk",
+ "gpu_cc_ahb_clk",
+ "gpu_cc_crc_ahb_clk",
+ "gpu_cc_cx_gfx3d_clk",
+ "gpu_cc_cx_gfx3d_slv_clk",
+ "gpu_cc_cx_gmu_clk",
+ "gpu_cc_cx_snoc_dvm_clk",
+ "gpu_cc_cxo_aon_clk",
+ "gpu_cc_cxo_clk",
+ "gpu_cc_gx_cxo_clk",
+ "gpu_cc_gx_gfx3d_clk",
+ "gpu_cc_gx_gmu_clk",
+ "gpu_cc_gx_vsense_clk",
+};
+
+static int gpu_cc_debug_mux_sels[] = {
+ 0x20, /* gpu_cc_acd_ahb_clk */
+ 0x1F, /* gpu_cc_acd_cxo_clk */
+ 0x11, /* gpu_cc_ahb_clk */
+ 0x12, /* gpu_cc_crc_ahb_clk */
+ 0x1A, /* gpu_cc_cx_gfx3d_clk */
+ 0x1B, /* gpu_cc_cx_gfx3d_slv_clk */
+ 0x19, /* gpu_cc_cx_gmu_clk */
+ 0x16, /* gpu_cc_cx_snoc_dvm_clk */
+ 0xB, /* gpu_cc_cxo_aon_clk */
+ 0xA, /* gpu_cc_cxo_clk */
+ 0xF, /* gpu_cc_gx_cxo_clk */
+ 0xC, /* gpu_cc_gx_gfx3d_clk */
+ 0x10, /* gpu_cc_gx_gmu_clk */
+ 0xD, /* gpu_cc_gx_vsense_clk */
+};
+
+static struct clk_debug_mux gpu_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x1568,
+ .post_div_offset = 0x10FC,
+ .cbcr_offset = 0x1100,
+ .src_sel_mask = 0xFF,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x3,
+ .post_div_shift = 0,
+ .post_div_val = 2,
+ .mux_sels = gpu_cc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "gpu_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = gpu_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(gpu_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const npu_cc_debug_mux_parent_names[] = {
+ "npu_cc_bto_core_clk",
+ "npu_cc_bwmon_clk",
+ "npu_cc_cal_hm0_cdc_clk",
+ "npu_cc_cal_hm0_clk",
+ "npu_cc_cal_hm0_perf_cnt_clk",
+ "npu_cc_core_clk",
+ "npu_cc_dsp_ahbm_clk",
+ "npu_cc_dsp_ahbs_clk",
+ "npu_cc_dsp_axi_clk",
+ "npu_cc_noc_ahb_clk",
+ "npu_cc_noc_axi_clk",
+ "npu_cc_noc_dma_clk",
+ "npu_cc_rsc_xo_clk",
+ "npu_cc_s2p_clk",
+ "npu_cc_xo_clk",
+};
+
+static int npu_cc_debug_mux_sels[] = {
+ 0x19, /* npu_cc_bto_core_clk */
+ 0x18, /* npu_cc_bwmon_clk */
+ 0xB, /* npu_cc_cal_hm0_cdc_clk */
+ 0x2, /* npu_cc_cal_hm0_clk */
+ 0xD, /* npu_cc_cal_hm0_perf_cnt_clk */
+ 0x4, /* npu_cc_core_clk */
+ 0x1C, /* npu_cc_dsp_ahbm_clk */
+ 0x1B, /* npu_cc_dsp_ahbs_clk */
+ 0x1E, /* npu_cc_dsp_axi_clk */
+ 0x13, /* npu_cc_noc_ahb_clk */
+ 0x12, /* npu_cc_noc_axi_clk */
+ 0x11, /* npu_cc_noc_dma_clk */
+ 0x1A, /* npu_cc_rsc_xo_clk */
+ 0x16, /* npu_cc_s2p_clk */
+ 0x1, /* npu_cc_xo_clk */
+};
+
+static struct clk_debug_mux npu_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x3000,
+ .post_div_offset = 0x3004,
+ .cbcr_offset = 0x3008,
+ .src_sel_mask = 0xFF,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x3,
+ .post_div_shift = 0,
+ .post_div_val = 2,
+ .mux_sels = npu_cc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = npu_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(npu_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const video_cc_debug_mux_parent_names[] = {
+ "video_cc_iris_ahb_clk",
+ "video_cc_mvs0_axi_clk",
+ "video_cc_mvs0_core_clk",
+ "video_cc_mvsc_core_clk",
+ "video_cc_mvsc_ctl_axi_clk",
+ "video_cc_sleep_clk",
+ "video_cc_venus_ahb_clk",
+ "video_cc_xo_clk",
+};
+
+static int video_cc_debug_mux_sels[] = {
+ 0x5, /* video_cc_iris_ahb_clk */
+ 0x9, /* video_cc_mvs0_axi_clk */
+ 0x3, /* video_cc_mvs0_core_clk */
+ 0x1, /* video_cc_mvsc_core_clk */
+ 0x8, /* video_cc_mvsc_ctl_axi_clk */
+ 0x7, /* video_cc_sleep_clk */
+ 0xC, /* video_cc_venus_ahb_clk */
+ 0x6, /* video_cc_xo_clk */
+};
+
+static struct clk_debug_mux video_cc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .debug_offset = 0x9000,
+ .post_div_offset = 0x6000,
+ .cbcr_offset = 0x6004,
+ .src_sel_mask = 0x3F,
+ .src_sel_shift = 0,
+ .post_div_mask = 0x7,
+ .post_div_shift = 0,
+ .post_div_val = 2,
+ .mux_sels = video_cc_debug_mux_sels,
+ .hw.init = &(struct clk_init_data){
+ .name = "video_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = video_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(video_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static const char *const mc_cc_debug_mux_parent_names[] = {
+ "measure_only_mccc_clk",
+};
+
+static struct clk_debug_mux mc_cc_debug_mux = {
+ .period_offset = 0x50,
+ .hw.init = &(struct clk_init_data){
+ .name = "mc_cc_debug_mux",
+ .ops = &clk_debug_mux_ops,
+ .parent_names = mc_cc_debug_mux_parent_names,
+ .num_parents = ARRAY_SIZE(mc_cc_debug_mux_parent_names),
+ .flags = CLK_IS_MEASURE,
+ },
+};
+
+static struct mux_regmap_names mux_list[] = {
+ { .mux = &cpu_cc_debug_mux, .regmap_name = "qcom,cpucc" },
+ { .mux = &cam_cc_debug_mux, .regmap_name = "qcom,camcc" },
+ { .mux = &disp_cc_debug_mux, .regmap_name = "qcom,dispcc" },
+ { .mux = &gcc_debug_mux, .regmap_name = "qcom,gcc" },
+ { .mux = &gpu_cc_debug_mux, .regmap_name = "qcom,gpucc" },
+ { .mux = &mc_cc_debug_mux, .regmap_name = "qcom,mccc" },
+ { .mux = &npu_cc_debug_mux, .regmap_name = "qcom,npucc" },
+ { .mux = &video_cc_debug_mux, .regmap_name = "qcom,videocc" },
+};
+
+static struct clk_dummy l3_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "l3_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_mccc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_mccc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_cnoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_cnoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_ipa_2x_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_ipa_2x_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_snoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_snoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy perfcl_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "perfcl_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy pwrcl_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "pwrcl_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+struct clk_hw *debugcc_lagoon_hws[] = {
+ &l3_clk.hw,
+ &measure_only_cnoc_clk.hw,
+ &measure_only_ipa_2x_clk.hw,
+ &measure_only_mccc_clk.hw,
+ &measure_only_snoc_clk.hw,
+ &perfcl_clk.hw,
+ &pwrcl_clk.hw,
+};
+
+static const struct of_device_id clk_debug_match_table[] = {
+ { .compatible = "qcom,lagoon-debugcc" },
+ { }
+};
+
+static int clk_debug_lagoon_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ int ret, i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cpu_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(cpu_cc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(cam_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(cam_cc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(disp_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(disp_cc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(gcc_debug_mux_parent_names) !=
+ ARRAY_SIZE(gcc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(gpu_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(gpu_cc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(npu_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(npu_cc_debug_mux_sels));
+ BUILD_BUG_ON(ARRAY_SIZE(video_cc_debug_mux_parent_names) !=
+ ARRAY_SIZE(video_cc_debug_mux_sels));
+
+ clk = devm_clk_get(&pdev->dev, "xo_clk_src");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get xo clock\n");
+ return PTR_ERR(clk);
+ }
+
+ debug_mux_priv.cxo = clk;
+
+ for (i = 0; i < ARRAY_SIZE(mux_list); i++) {
+ ret = map_debug_bases(pdev, mux_list[i].regmap_name,
+ mux_list[i].mux);
+ if (ret == -EBADR)
+ continue;
+ else if (ret)
+ return ret;
+
+ clk = devm_clk_register(&pdev->dev, &mux_list[i].mux->hw);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Unable to register %s, err:(%d)\n",
+ mux_list[i].mux->hw.init->name, PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(debugcc_lagoon_hws); i++) {
+ clk = devm_clk_register(&pdev->dev, debugcc_lagoon_hws[i]);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Unable to register %s, err:(%d)\n",
+ debugcc_lagoon_hws[i]->init->name,
+ PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ }
+
+ ret = clk_debug_measure_register(&gcc_debug_mux.hw);
+ if (ret)
+ dev_err(&pdev->dev, "Could not register Measure clock\n");
+
+ return ret;
+}
+
+static struct platform_driver clk_debug_driver = {
+ .probe = clk_debug_lagoon_probe,
+ .driver = {
+ .name = "debugcc-lagoon",
+ .of_match_table = clk_debug_match_table,
+ },
+};
+
+int __init clk_debug_lagoon_init(void)
+{
+ return platform_driver_register(&clk_debug_driver);
+}
+fs_initcall(clk_debug_lagoon_init);
+
+MODULE_DESCRIPTION("QTI DEBUG CC LAGOON Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:debugcc-lagoon");
diff --git a/drivers/clk/qcom/dispcc-kona.c b/drivers/clk/qcom/dispcc-kona.c
index 5eb750c..7fe1886 100644
--- a/drivers/clk/qcom/dispcc-kona.c
+++ b/drivers/clk/qcom/dispcc-kona.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -208,10 +208,11 @@ static struct pll_vco lucid_vco[] = {
static const struct alpha_pll_config disp_cc_pll0_config = {
.l = 0x47,
+ .cal_l = 0x44,
.alpha = 0xE000,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000000,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -241,10 +242,11 @@ static struct clk_alpha_pll disp_cc_pll0 = {
static const struct alpha_pll_config disp_cc_pll1_config = {
.l = 0x1F,
+ .cal_l = 0x44,
.alpha = 0x4000,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000000,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -481,8 +483,7 @@ static struct clk_rcg2 disp_cc_mdss_dp_link1_clk_src = {
.num_rate_max = VDD_NUM,
.rate_max = (unsigned long[VDD_NUM]) {
[VDD_MIN] = 19200,
- [VDD_LOWER] = 162000,
- [VDD_LOW] = 270000,
+ [VDD_LOWER] = 270000,
[VDD_LOW_L1] = 540000,
[VDD_NOMINAL] = 810000},
},
@@ -504,8 +505,7 @@ static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = {
.num_rate_max = VDD_NUM,
.rate_max = (unsigned long[VDD_NUM]) {
[VDD_MIN] = 19200,
- [VDD_LOWER] = 162000,
- [VDD_LOW] = 270000,
+ [VDD_LOWER] = 270000,
[VDD_LOW_L1] = 540000,
[VDD_NOMINAL] = 810000},
},
@@ -527,7 +527,7 @@ static struct clk_rcg2 disp_cc_mdss_dp_pixel1_clk_src = {
.rate_max = (unsigned long[VDD_NUM]) {
[VDD_MIN] = 19200,
[VDD_LOWER] = 337500,
- [VDD_LOW_L1] = 675000},
+ [VDD_NOMINAL] = 675000},
},
};
@@ -547,7 +547,7 @@ static struct clk_rcg2 disp_cc_mdss_dp_pixel2_clk_src = {
.rate_max = (unsigned long[VDD_NUM]) {
[VDD_MIN] = 19200,
[VDD_LOWER] = 337500,
- [VDD_LOW_L1] = 675000},
+ [VDD_NOMINAL] = 675000},
},
};
diff --git a/drivers/clk/qcom/dispcc-lagoon.c b/drivers/clk/qcom/dispcc-lagoon.c
new file mode 100644
index 0000000..593a8df
--- /dev/null
+++ b/drivers/clk/qcom/dispcc-lagoon.c
@@ -0,0 +1,894 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "clk: %s: " fmt, __func__
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,dispcc-lagoon.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "common.h"
+#include "reset.h"
+#include "vdd-level-lito.h"
+
+static DEFINE_VDD_REGULATORS(vdd_cx, VDD_NUM, 1, vdd_corner);
+
+enum {
+ P_BI_TCXO,
+ P_CORE_BI_PLL_TEST_SE,
+ P_DISP_CC_PLL0_OUT_EVEN,
+ P_DISP_CC_PLL0_OUT_MAIN,
+ P_DP_PHY_PLL_LINK_CLK,
+ P_DP_PHY_PLL_VCO_DIV_CLK,
+ P_DSI0_PHY_PLL_OUT_BYTECLK,
+ P_DSI0_PHY_PLL_OUT_DSICLK,
+ P_GCC_DISP_GPLL0_CLK,
+};
+
+static const struct parent_map disp_cc_parent_map_0[] = {
+ { P_BI_TCXO, 0 },
+ { P_DP_PHY_PLL_LINK_CLK, 1 },
+ { P_DP_PHY_PLL_VCO_DIV_CLK, 2 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_0[] = {
+ "bi_tcxo",
+ "dp_phy_pll_link_clk",
+ "dp_phy_pll_vco_div_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map disp_cc_parent_map_1[] = {
+ { P_BI_TCXO, 0 },
+ { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_1[] = {
+ "bi_tcxo",
+ "dsi0_phy_pll_out_byteclk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map disp_cc_parent_map_2[] = {
+ { P_BI_TCXO, 0 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_2[] = {
+ "bi_tcxo",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map disp_cc_parent_map_3[] = {
+ { P_BI_TCXO, 0 },
+ { P_DISP_CC_PLL0_OUT_MAIN, 1 },
+ { P_GCC_DISP_GPLL0_CLK, 4 },
+ { P_DISP_CC_PLL0_OUT_EVEN, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_3[] = {
+ "bi_tcxo",
+ "disp_cc_pll0",
+ "gcc_disp_gpll0_clk",
+ "disp_cc_pll0",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map disp_cc_parent_map_4[] = {
+ { P_BI_TCXO, 0 },
+ { P_GCC_DISP_GPLL0_CLK, 4 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_4[] = {
+ "bi_tcxo",
+ "gcc_disp_gpll0_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map disp_cc_parent_map_5[] = {
+ { P_BI_TCXO, 0 },
+ { P_DSI0_PHY_PLL_OUT_DSICLK, 1 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const disp_cc_parent_names_5[] = {
+ "bi_tcxo",
+ "dsi0_phy_pll_out_dsiclk",
+ "core_bi_pll_test_se",
+};
+
+static struct pll_vco fabia_vco[] = {
+ { 249600000, 2000000000, 0 },
+};
+
+static const struct alpha_pll_config disp_cc_pll0_config = {
+ .l = 0x3A,
+ .cal_l = 0x3F,
+ .alpha = 0x5555,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
+ .user_ctl_val = 0x00000000,
+ .user_ctl_hi_val = 0x00004805,
+};
+
+static struct clk_alpha_pll disp_cc_pll0 = {
+ .offset = 0x0,
+ .vco_table = fabia_vco,
+ .num_vco = ARRAY_SIZE(fabia_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_pll0",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_fabia_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 615000000,
+ [VDD_LOW] = 1066000000,
+ [VDD_LOW_L1] = 1600000000,
+ [VDD_NOMINAL] = 2000000000},
+ },
+ },
+};
+
+static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
+ .reg = 0x10dc,
+ .shift = 0,
+ .width = 2,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "disp_cc_mdss_byte0_div_clk_src",
+ .parent_names =
+ (const char *[]){ "disp_cc_mdss_byte0_clk_src" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ .ops = &clk_regmap_div_ro_ops,
+ },
+};
+
+static struct clk_regmap_div disp_cc_mdss_dp_link_div_clk_src = {
+ .reg = 0x1110,
+ .shift = 0,
+ .width = 2,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "disp_cc_mdss_dp_link_div_clk_src",
+ .parent_names =
+ (const char *[]){ "disp_cc_mdss_dp_link_clk_src" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ .ops = &clk_regmap_div_ro_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(37500000, P_GCC_DISP_GPLL0_CLK, 16, 0, 0),
+ F(75000000, P_GCC_DISP_GPLL0_CLK, 8, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = {
+ .cmd_rcgr = 0x115c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_4,
+ .freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_ahb_clk_src",
+ .parent_names = disp_cc_parent_names_4,
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 19200000,
+ [VDD_LOW] = 37500000,
+ [VDD_NOMINAL] = 75000000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = {
+ .cmd_rcgr = 0x10c4,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_1,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_byte0_clk_src",
+ .parent_names = disp_cc_parent_names_1,
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_byte2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 187500000,
+ [VDD_LOW] = 300000000,
+ [VDD_LOW_L1] = 358000000},
+ },
+};
+
+static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 disp_cc_mdss_dp_aux_clk_src = {
+ .cmd_rcgr = 0x1144,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_2,
+ .freq_tbl = ftbl_disp_cc_mdss_dp_aux_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_aux_clk_src",
+ .parent_names = disp_cc_parent_names_2,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 19200000},
+ },
+};
+
+static const struct freq_tbl ftbl_disp_cc_mdss_dp_crypto_clk_src[] = {
+ F(108000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0),
+ F(180000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0),
+ F(360000, P_DP_PHY_PLL_LINK_CLK, 1.5, 0, 0),
+ F(540000, P_DP_PHY_PLL_LINK_CLK, 1.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 disp_cc_mdss_dp_crypto_clk_src = {
+ .cmd_rcgr = 0x1114,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_0,
+ .freq_tbl = ftbl_disp_cc_mdss_dp_crypto_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_crypto_clk_src",
+ .parent_names = disp_cc_parent_names_0,
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_byte2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 180000,
+ [VDD_LOW_L1] = 360000,
+ [VDD_NOMINAL] = 540000},
+ },
+};
+
+static const struct freq_tbl ftbl_disp_cc_mdss_dp_link_clk_src[] = {
+ F(162000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0),
+ F(270000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0),
+ F(540000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0),
+ F(810000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = {
+ .cmd_rcgr = 0x10f8,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_0,
+ .freq_tbl = ftbl_disp_cc_mdss_dp_link_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_link_clk_src",
+ .parent_names = disp_cc_parent_names_0,
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_byte2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 270000,
+ [VDD_LOW_L1] = 540000,
+ [VDD_NOMINAL] = 810000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_dp_pixel_clk_src = {
+ .cmd_rcgr = 0x112c,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_0,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_pixel_clk_src",
+ .parent_names = disp_cc_parent_names_0,
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_dp_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 337500000,
+ [VDD_NOMINAL] = 675000000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = {
+ .cmd_rcgr = 0x10e0,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_1,
+ .freq_tbl = ftbl_disp_cc_mdss_dp_aux_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_esc0_clk_src",
+ .parent_names = disp_cc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 19200000},
+ },
+};
+
+static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(200000000, P_GCC_DISP_GPLL0_CLK, 3, 0, 0),
+ F(300000000, P_GCC_DISP_GPLL0_CLK, 2, 0, 0),
+ F(373333333, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
+ F(448000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0),
+ F(560000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = {
+ .cmd_rcgr = 0x107c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_3,
+ .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_mdp_clk_src",
+ .parent_names = disp_cc_parent_names_3,
+ .num_parents = 5,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 200000000,
+ [VDD_LOW] = 300000000,
+ [VDD_LOW_L1] = 373333333,
+ [VDD_NOMINAL] = 448000000,
+ [VDD_HIGH] = 560000000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = {
+ .cmd_rcgr = 0x1064,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_5,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_pclk0_clk_src",
+ .parent_names = disp_cc_parent_names_5,
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_pixel_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 300000000,
+ [VDD_LOW] = 525000000,
+ [VDD_LOW_L1] = 625000000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_rot_clk_src = {
+ .cmd_rcgr = 0x1094,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_3,
+ .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_rot_clk_src",
+ .parent_names = disp_cc_parent_names_3,
+ .num_parents = 5,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 200000000,
+ [VDD_LOW] = 300000000,
+ [VDD_LOW_L1] = 373333333,
+ [VDD_NOMINAL] = 448000000,
+ [VDD_HIGH] = 560000000},
+ },
+};
+
+static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = {
+ .cmd_rcgr = 0x10ac,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = disp_cc_parent_map_2,
+ .freq_tbl = ftbl_disp_cc_mdss_dp_aux_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_vsync_clk_src",
+ .parent_names = disp_cc_parent_names_2,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_LOWER] = 19200000},
+ },
+};
+
+static struct clk_branch disp_cc_mdss_ahb_clk = {
+ .halt_reg = 0x104c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x104c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_ahb_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_byte0_clk = {
+ .halt_reg = 0x102c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x102c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_byte0_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_byte0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_byte0_intf_clk = {
+ .halt_reg = 0x1030,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1030,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_byte0_intf_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_byte0_div_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_dp_aux_clk = {
+ .halt_reg = 0x1048,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_aux_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_dp_aux_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_dp_crypto_clk = {
+ .halt_reg = 0x1040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_crypto_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_dp_crypto_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_dp_link_clk = {
+ .halt_reg = 0x1038,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1038,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_link_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_dp_link_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_dp_link_intf_clk = {
+ .halt_reg = 0x103c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x103c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_link_intf_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_dp_link_div_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_dp_pixel_clk = {
+ .halt_reg = 0x1044,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1044,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_dp_pixel_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_dp_pixel_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_esc0_clk = {
+ .halt_reg = 0x1034,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1034,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_esc0_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_esc0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_mdp_clk = {
+ .halt_reg = 0x1010,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_mdp_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_mdp_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_mdp_lut_clk = {
+ .halt_reg = 0x1020,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_mdp_lut_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_mdp_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = {
+ .halt_reg = 0x2004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x2004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_non_gdsc_ahb_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_pclk0_clk = {
+ .halt_reg = 0x100c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x100c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_pclk0_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_pclk0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_rot_clk = {
+ .halt_reg = 0x1018,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_rot_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_rot_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_rscc_ahb_clk = {
+ .halt_reg = 0x200c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x200c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_rscc_ahb_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_rscc_vsync_clk = {
+ .halt_reg = 0x2008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_rscc_vsync_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_vsync_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_mdss_vsync_clk = {
+ .halt_reg = 0x1028,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1028,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_mdss_vsync_clk",
+ .parent_names = (const char *[]){
+ "disp_cc_mdss_vsync_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_sleep_clk = {
+ .halt_reg = 0x5004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_sleep_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch disp_cc_xo_clk = {
+ .halt_reg = 0x5008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "disp_cc_xo_clk",
+ .flags = CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_regmap *disp_cc_lagoon_clocks[] = {
+ [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr,
+ [DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr,
+ [DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr,
+ [DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr,
+ [DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr,
+ [DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr,
+ [DISP_CC_MDSS_DP_AUX_CLK] = &disp_cc_mdss_dp_aux_clk.clkr,
+ [DISP_CC_MDSS_DP_AUX_CLK_SRC] = &disp_cc_mdss_dp_aux_clk_src.clkr,
+ [DISP_CC_MDSS_DP_CRYPTO_CLK] = &disp_cc_mdss_dp_crypto_clk.clkr,
+ [DISP_CC_MDSS_DP_CRYPTO_CLK_SRC] = &disp_cc_mdss_dp_crypto_clk_src.clkr,
+ [DISP_CC_MDSS_DP_LINK_CLK] = &disp_cc_mdss_dp_link_clk.clkr,
+ [DISP_CC_MDSS_DP_LINK_CLK_SRC] = &disp_cc_mdss_dp_link_clk_src.clkr,
+ [DISP_CC_MDSS_DP_LINK_DIV_CLK_SRC] =
+ &disp_cc_mdss_dp_link_div_clk_src.clkr,
+ [DISP_CC_MDSS_DP_LINK_INTF_CLK] = &disp_cc_mdss_dp_link_intf_clk.clkr,
+ [DISP_CC_MDSS_DP_PIXEL_CLK] = &disp_cc_mdss_dp_pixel_clk.clkr,
+ [DISP_CC_MDSS_DP_PIXEL_CLK_SRC] = &disp_cc_mdss_dp_pixel_clk_src.clkr,
+ [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr,
+ [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr,
+ [DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr,
+ [DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr,
+ [DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr,
+ [DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr,
+ [DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr,
+ [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr,
+ [DISP_CC_MDSS_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr,
+ [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr,
+ [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr,
+ [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_clk.clkr,
+ [DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr,
+ [DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr,
+ [DISP_CC_PLL0] = &disp_cc_pll0.clkr,
+ [DISP_CC_SLEEP_CLK] = &disp_cc_sleep_clk.clkr,
+ [DISP_CC_XO_CLK] = &disp_cc_xo_clk.clkr,
+};
+
+static const struct regmap_config disp_cc_lagoon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x10000,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc disp_cc_lagoon_desc = {
+ .config = &disp_cc_lagoon_regmap_config,
+ .clks = disp_cc_lagoon_clocks,
+ .num_clks = ARRAY_SIZE(disp_cc_lagoon_clocks),
+};
+
+static const struct of_device_id disp_cc_lagoon_match_table[] = {
+ { .compatible = "qcom,lagoon-dispcc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, disp_cc_lagoon_match_table);
+
+static int disp_cc_lagoon_probe(struct platform_device *pdev)
+{
+ struct regmap *regmap;
+ int ret;
+
+ vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx");
+ if (IS_ERR(vdd_cx.regulator[0])) {
+ if (PTR_ERR(vdd_cx.regulator[0]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get vdd_cx regulator\n");
+ return PTR_ERR(vdd_cx.regulator[0]);
+ }
+
+ regmap = qcom_cc_map(pdev, &disp_cc_lagoon_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk_fabia_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config);
+
+ ret = qcom_cc_really_probe(pdev, &disp_cc_lagoon_desc, regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register DISP CC clocks\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "Registered DISP CC clocks\n");
+
+ return ret;
+}
+
+static struct platform_driver disp_cc_lagoon_driver = {
+ .probe = disp_cc_lagoon_probe,
+ .driver = {
+ .name = "disp_cc-lagoon",
+ .of_match_table = disp_cc_lagoon_match_table,
+ },
+};
+
+static int __init disp_cc_lagoon_init(void)
+{
+ return platform_driver_register(&disp_cc_lagoon_driver);
+}
+subsys_initcall(disp_cc_lagoon_init);
+
+static void __exit disp_cc_lagoon_exit(void)
+{
+ platform_driver_unregister(&disp_cc_lagoon_driver);
+}
+module_exit(disp_cc_lagoon_exit);
+
+MODULE_DESCRIPTION("QTI DISP_CC LAGOON Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/dispcc-lito.c b/drivers/clk/qcom/dispcc-lito.c
index 0d4f4b7..95c9264 100644
--- a/drivers/clk/qcom/dispcc-lito.c
+++ b/drivers/clk/qcom/dispcc-lito.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -144,9 +144,9 @@ static struct pll_vco lucid_vco[] = {
};
static const struct alpha_pll_config disp_cc_pll0_config = {
- .l = 0x47,
+ .l = 0x35,
.cal_l = 0x44,
- .alpha = 0xE000,
+ .alpha = 0xE800,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
.config_ctl_hi1_val = 0x329A699C,
@@ -456,7 +456,7 @@ static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = {
static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = {
F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
- F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0),
+ F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
F(460000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
{ }
};
@@ -526,12 +526,18 @@ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = {
},
};
+static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src[] = {
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
static struct clk_rcg2 disp_cc_mdss_rot_clk_src = {
.cmd_rcgr = 0x20e0,
.mnd_width = 0,
.hid_width = 5,
.parent_map = disp_cc_parent_map_3,
- .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
+ .freq_tbl = ftbl_disp_cc_mdss_rot_clk_src,
.enable_safe_config = true,
.clkr.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_rot_clk_src",
@@ -543,9 +549,7 @@ static struct clk_rcg2 disp_cc_mdss_rot_clk_src = {
.num_rate_max = VDD_NUM,
.rate_max = (unsigned long[VDD_NUM]) {
[VDD_LOWER] = 200000000,
- [VDD_LOW] = 300000000,
- [VDD_LOW_L1] = 345000000,
- [VDD_NOMINAL] = 460000000},
+ [VDD_LOW] = 300000000},
},
};
diff --git a/drivers/clk/qcom/gcc-bengal.c b/drivers/clk/qcom/gcc-bengal.c
index 304b146..b8c5ea4 100644
--- a/drivers/clk/qcom/gcc-bengal.c
+++ b/drivers/clk/qcom/gcc-bengal.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -347,10 +347,11 @@ static struct pll_vco brammo_vco[] = {
};
static struct pll_vco default_vco[] = {
- { 1000000000, 2000000000, 0 },
- { 750000000, 1500000000, 1 },
{ 500000000, 1000000000, 2 },
- { 250000000, 500000000, 3 },
+};
+
+static struct pll_vco alpha_vco[] = {
+ { 750000000, 1500000000, 1 },
};
static const u8 clk_alpha_pll_regs_offset[][PLL_OFF_MAX_REGS] = {
@@ -452,8 +453,8 @@ static const struct alpha_pll_config gpll10_config = {
static struct clk_alpha_pll gpll10 = {
.offset = 0xa000,
- .vco_table = default_vco,
- .num_vco = ARRAY_SIZE(default_vco),
+ .vco_table = alpha_vco,
+ .num_vco = ARRAY_SIZE(alpha_vco),
.regs = clk_alpha_pll_regs_offset[CLK_ALPHA_PLL_TYPE_DEFAULT],
.clkr = {
.enable_reg = 0x79000,
@@ -754,6 +755,7 @@ static struct clk_alpha_pll_postdiv gpll8_out_main = {
.name = "gpll8_out_main",
.parent_names = (const char *[]){ "gpll8" },
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_alpha_pll_postdiv_ro_ops,
},
};
@@ -3766,6 +3768,7 @@ static struct clk_regmap *gcc_bengal_clocks[] = {
static const struct qcom_reset_map gcc_bengal_resets[] = {
[GCC_QUSB2PHY_PRIM_BCR] = { 0x1c000 },
[GCC_QUSB2PHY_SEC_BCR] = { 0x1c004 },
+ [GCC_SDCC1_BCR] = { 0x38000 },
[GCC_UFS_PHY_BCR] = { 0x45000 },
[GCC_USB30_PRIM_BCR] = { 0x1a000 },
[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x1d000 },
diff --git a/drivers/clk/qcom/gcc-lagoon.c b/drivers/clk/qcom/gcc-lagoon.c
index f11cdf2..f0ae8b6 100644
--- a/drivers/clk/qcom/gcc-lagoon.c
+++ b/drivers/clk/qcom/gcc-lagoon.c
@@ -307,6 +307,19 @@ static struct clk_regmap_div gcc_gpu_gpll0_main_div_clk_src = {
},
};
+static struct clk_regmap_div gcc_npu_pll0_main_div_clk_src = {
+ .reg = 0x4ce00,
+ .shift = 0,
+ .width = 2,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "gcc_npu_pll0_main_div_clk_src",
+ .parent_names =
+ (const char *[]){ "gpll0" },
+ .num_parents = 1,
+ .ops = &clk_regmap_div_ro_ops,
+ },
+};
+
static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
F(19200000, P_BI_TCXO, 1, 0, 0),
{ }
@@ -1629,7 +1642,7 @@ static struct clk_branch gcc_npu_gpll0_div_clk = {
.hw.init = &(struct clk_init_data){
.name = "gcc_npu_gpll0_div_clk",
.parent_names = (const char *[]){
- "gpll0",
+ "gcc_npu_pll0_main_div_clk_src",
},
.num_parents = 1,
.ops = &clk_branch2_ops,
@@ -2643,6 +2656,7 @@ static struct clk_regmap *gcc_lagoon_clocks[] = {
[GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK] =
&gcc_ufs_phy_ice_core_hw_ctl_clk.clkr,
[GCC_GPU_GPLL0_MAIN_DIV_CLK_SRC] = &gcc_gpu_gpll0_main_div_clk_src.clkr,
+ [GCC_NPU_PLL0_MAIN_DIV_CLK_SRC] = &gcc_npu_pll0_main_div_clk_src.clkr,
};
static const struct qcom_reset_map gcc_lagoon_resets[] = {
diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c
index cd937ce..4e23973 100644
--- a/drivers/clk/qcom/gcc-msm8998.c
+++ b/drivers/clk/qcom/gcc-msm8998.c
@@ -2402,7 +2402,7 @@ static struct clk_branch gcc_ufs_phy_aux_clk = {
static struct clk_branch gcc_ufs_rx_symbol_0_clk = {
.halt_reg = 0x75014,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_HALT_SKIP,
.clkr = {
.enable_reg = 0x75014,
.enable_mask = BIT(0),
@@ -2415,7 +2415,7 @@ static struct clk_branch gcc_ufs_rx_symbol_0_clk = {
static struct clk_branch gcc_ufs_rx_symbol_1_clk = {
.halt_reg = 0x7605c,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_HALT_SKIP,
.clkr = {
.enable_reg = 0x7605c,
.enable_mask = BIT(0),
@@ -2428,7 +2428,7 @@ static struct clk_branch gcc_ufs_rx_symbol_1_clk = {
static struct clk_branch gcc_ufs_tx_symbol_0_clk = {
.halt_reg = 0x75010,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_HALT_SKIP,
.clkr = {
.enable_reg = 0x75010,
.enable_mask = BIT(0),
@@ -2743,25 +2743,25 @@ static struct gdsc *gcc_msm8998_gdscs[] = {
};
static const struct qcom_reset_map gcc_msm8998_resets[] = {
- [GCC_BLSP1_QUP1_BCR] = { 0x102400 },
- [GCC_BLSP1_QUP2_BCR] = { 0x110592 },
- [GCC_BLSP1_QUP3_BCR] = { 0x118784 },
- [GCC_BLSP1_QUP4_BCR] = { 0x126976 },
- [GCC_BLSP1_QUP5_BCR] = { 0x135168 },
- [GCC_BLSP1_QUP6_BCR] = { 0x143360 },
- [GCC_BLSP2_QUP1_BCR] = { 0x155648 },
- [GCC_BLSP2_QUP2_BCR] = { 0x163840 },
- [GCC_BLSP2_QUP3_BCR] = { 0x172032 },
- [GCC_BLSP2_QUP4_BCR] = { 0x180224 },
- [GCC_BLSP2_QUP5_BCR] = { 0x188416 },
- [GCC_BLSP2_QUP6_BCR] = { 0x196608 },
- [GCC_PCIE_0_BCR] = { 0x438272 },
- [GCC_PDM_BCR] = { 0x208896 },
- [GCC_SDCC2_BCR] = { 0x81920 },
- [GCC_SDCC4_BCR] = { 0x90112 },
- [GCC_TSIF_BCR] = { 0x221184 },
- [GCC_UFS_BCR] = { 0x479232 },
- [GCC_USB_30_BCR] = { 0x61440 },
+ [GCC_BLSP1_QUP1_BCR] = { 0x19000 },
+ [GCC_BLSP1_QUP2_BCR] = { 0x1b000 },
+ [GCC_BLSP1_QUP3_BCR] = { 0x1d000 },
+ [GCC_BLSP1_QUP4_BCR] = { 0x1f000 },
+ [GCC_BLSP1_QUP5_BCR] = { 0x21000 },
+ [GCC_BLSP1_QUP6_BCR] = { 0x23000 },
+ [GCC_BLSP2_QUP1_BCR] = { 0x26000 },
+ [GCC_BLSP2_QUP2_BCR] = { 0x28000 },
+ [GCC_BLSP2_QUP3_BCR] = { 0x2a000 },
+ [GCC_BLSP2_QUP4_BCR] = { 0x2c000 },
+ [GCC_BLSP2_QUP5_BCR] = { 0x2e000 },
+ [GCC_BLSP2_QUP6_BCR] = { 0x30000 },
+ [GCC_PCIE_0_BCR] = { 0x6b000 },
+ [GCC_PDM_BCR] = { 0x33000 },
+ [GCC_SDCC2_BCR] = { 0x14000 },
+ [GCC_SDCC4_BCR] = { 0x16000 },
+ [GCC_TSIF_BCR] = { 0x36000 },
+ [GCC_UFS_BCR] = { 0x75000 },
+ [GCC_USB_30_BCR] = { 0xf000 },
};
static const struct regmap_config gcc_msm8998_regmap_config = {
diff --git a/drivers/clk/qcom/gpucc-kona.c b/drivers/clk/qcom/gpucc-kona.c
index d40b2f0..4e9c6a0 100644
--- a/drivers/clk/qcom/gpucc-kona.c
+++ b/drivers/clk/qcom/gpucc-kona.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -66,13 +66,59 @@ static struct pll_vco lucid_vco[] = {
{ 249600000, 2000000000, 0 },
};
+static const struct alpha_pll_config gpu_cc_pll0_config = {
+ .l = 0x13,
+ .cal_l = 0x44,
+ .alpha = 0xCAAA,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002261,
+ .config_ctl_hi1_val = 0x329A699C,
+ .user_ctl_val = 0x00000000,
+ .user_ctl_hi_val = 0x00000805,
+ .user_ctl_hi1_val = 0x00000000,
+};
+
+static const struct alpha_pll_config gpu_cc_pll0_config_sm8250_v2 = {
+ .l = 0x14,
+ .cal_l = 0x44,
+ .alpha = 0x5000,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002261,
+ .config_ctl_hi1_val = 0x329A699C,
+ .user_ctl_val = 0x00000000,
+ .user_ctl_hi_val = 0x00000805,
+ .user_ctl_hi1_val = 0x00000000,
+};
+
+static struct clk_alpha_pll gpu_cc_pll0 = {
+ .offset = 0x0,
+ .vco_table = lucid_vco,
+ .num_vco = ARRAY_SIZE(lucid_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID],
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gpu_cc_pll0",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_ops,
+ .vdd_class = &vdd_mx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 615000000,
+ [VDD_LOW] = 1066000000,
+ [VDD_LOW_L1] = 1600000000,
+ [VDD_NOMINAL] = 2000000000},
+ },
+ },
+};
+
static const struct alpha_pll_config gpu_cc_pll1_config = {
.l = 0x1A,
.cal_l = 0x44,
.alpha = 0xAAA,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000000,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -398,10 +444,33 @@ static const struct qcom_cc_desc gpu_cc_kona_desc = {
static const struct of_device_id gpu_cc_kona_match_table[] = {
{ .compatible = "qcom,gpucc-kona" },
+ { .compatible = "qcom,gpucc-kona-v2" },
{ }
};
MODULE_DEVICE_TABLE(of, gpu_cc_kona_match_table);
+static void gpu_cc_kona_fixup_konav2(struct regmap *regmap)
+{
+ clk_lucid_pll_configure(&gpu_cc_pll0, regmap,
+ &gpu_cc_pll0_config_sm8250_v2);
+}
+
+static int gpu_cc_kona_fixup(struct platform_device *pdev,
+ struct regmap *regmap)
+{
+ const char *compat = NULL;
+ int compatlen = 0;
+
+ compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen);
+ if (!compat || (compatlen <= 0))
+ return -EINVAL;
+
+ if (!strcmp(compat, "qcom,gpucc-kona-v2"))
+ gpu_cc_kona_fixup_konav2(regmap);
+
+ return 0;
+}
+
static int gpu_cc_kona_probe(struct platform_device *pdev)
{
struct regmap *regmap;
@@ -435,6 +504,9 @@ static int gpu_cc_kona_probe(struct platform_device *pdev)
value = 0xf << CX_GMU_CBCR_WAKE_SHIFT | 0xf << CX_GMU_CBCR_SLEEP_SHIFT;
regmap_update_bits(regmap, gpu_cc_cx_gmu_clk.clkr.enable_reg,
mask, value);
+ ret = gpu_cc_kona_fixup(pdev, regmap);
+ if (ret)
+ return ret;
ret = qcom_cc_really_probe(pdev, &gpu_cc_kona_desc, regmap);
if (ret) {
diff --git a/drivers/clk/qcom/gpucc-lagoon.c b/drivers/clk/qcom/gpucc-lagoon.c
index 6a0a98e0..8c7e09b 100644
--- a/drivers/clk/qcom/gpucc-lagoon.c
+++ b/drivers/clk/qcom/gpucc-lagoon.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -92,6 +92,8 @@ static const struct alpha_pll_config gpu_cc_pll0_config = {
.alpha = 0x5AAA,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
.user_ctl_val = 0x00000001,
.user_ctl_hi_val = 0x00004805,
};
@@ -137,6 +139,8 @@ static const struct alpha_pll_config gpu_cc_pll1_config = {
.alpha = 0xC555,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
.user_ctl_val = 0x00000001,
.user_ctl_hi_val = 0x00004805,
};
@@ -194,6 +198,7 @@ static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src[] = {
F(565000000, P_CRC_DIV, 1, 0, 0),
F(650000000, P_CRC_DIV, 1, 0, 0),
F(800000000, P_CRC_DIV, 1, 0, 0),
+ F(825000000, P_CRC_DIV, 1, 0, 0),
F(850000000, P_CRC_DIV, 1, 0, 0),
{ }
};
diff --git a/drivers/clk/qcom/npucc-lagoon.c b/drivers/clk/qcom/npucc-lagoon.c
new file mode 100644
index 0000000..3c35830
--- /dev/null
+++ b/drivers/clk/qcom/npucc-lagoon.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,npucc-lagoon.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "common.h"
+#include "reset.h"
+#include "vdd-level-lito.h"
+
+static DEFINE_VDD_REGULATORS(vdd_cx, VDD_NUM, 1, vdd_corner);
+
+#define HM0_CRC_SID_FSM_CTRL 0x11A0
+#define CRC_SID_FSM_CTRL_SETTING 0x800000
+#define HM0_CRC_MND_CFG 0x11A4
+#define CRC_MND_CFG_SETTING 0x15011
+
+enum {
+ P_BI_TCXO,
+ P_CORE_BI_PLL_TEST_SE,
+ P_GCC_NPU_GPLL0_CLK,
+ P_GCC_NPU_GPLL0_DIV_CLK,
+ P_NPU_CC_PLL0_OUT_EVEN,
+ P_NPU_CC_PLL1_OUT_EVEN,
+ P_NPU_Q6SS_PLL_OUT_MAIN,
+ P_NPU_CC_CRC_DIV,
+};
+
+static const struct parent_map npu_cc_parent_map_0[] = {
+ { P_BI_TCXO, 0 },
+ { P_NPU_CC_PLL1_OUT_EVEN, 1 },
+ { P_NPU_CC_PLL0_OUT_EVEN, 2 },
+ { P_GCC_NPU_GPLL0_CLK, 4 },
+ { P_GCC_NPU_GPLL0_DIV_CLK, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const npu_cc_parent_names_0[] = {
+ "bi_tcxo",
+ "npu_cc_pll1",
+ "npu_cc_pll0",
+ "gcc_npu_gpll0_clk",
+ "gcc_npu_gpll0_div_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map npu_cc_parent_map_0_crc[] = {
+ { P_BI_TCXO, 0 },
+ { P_NPU_CC_PLL1_OUT_EVEN, 1 },
+ { P_NPU_CC_CRC_DIV, 2 },
+ { P_GCC_NPU_GPLL0_CLK, 4 },
+ { P_GCC_NPU_GPLL0_DIV_CLK, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const npu_cc_parent_names_0_crc[] = {
+ "bi_tcxo",
+ "npu_cc_pll1",
+ "npu_cc_crc_div",
+ "gcc_npu_gpll0_clk",
+ "gcc_npu_gpll0_div_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map npu_cc_parent_map_1[] = {
+ { P_BI_TCXO, 0 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const npu_cc_parent_names_1[] = {
+ "bi_tcxo",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map npu_cc_parent_map_2[] = {
+ { P_BI_TCXO, 0 },
+ { P_NPU_Q6SS_PLL_OUT_MAIN, 2 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const npu_cc_parent_names_2[] = {
+ "bi_tcxo",
+ "npu_q6ss_pll",
+ "core_bi_pll_test_se",
+};
+
+static const u32 crc_reg_offset[] = {
+ HM0_CRC_MND_CFG, HM0_CRC_SID_FSM_CTRL,
+};
+
+static const u32 crc_reg_val[] = {
+ CRC_MND_CFG_SETTING, CRC_SID_FSM_CTRL_SETTING,
+};
+
+static struct pll_vco fabia_vco[] = {
+ { 249600000, 2000000000, 0 },
+};
+
+/* 537.60MHz Configuration */
+static struct alpha_pll_config npu_cc_pll0_config = {
+ .l = 0x1C,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
+ .user_ctl_val = 0x00000001,
+ .user_ctl_hi_val = 0x00004805,
+ .custom_reg_offset = crc_reg_offset,
+ .custom_reg_val = crc_reg_val,
+ .num_custom_reg = ARRAY_SIZE(crc_reg_offset),
+};
+
+static struct clk_alpha_pll npu_cc_pll0 = {
+ .offset = 0x0,
+ .vco_table = fabia_vco,
+ .num_vco = ARRAY_SIZE(fabia_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
+ .config = &npu_cc_pll0_config,
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_pll0",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_fabia_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 615000000,
+ [VDD_LOW] = 1066000000,
+ [VDD_LOW_L1] = 1600000000,
+ [VDD_NOMINAL] = 2000000000},
+ },
+ },
+};
+
+/* 300MHz Configuration */
+static struct alpha_pll_config npu_cc_pll1_config = {
+ .l = 0xF,
+ .alpha = 0xA000,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
+ .user_ctl_val = 0x00000001,
+ .user_ctl_hi_val = 0x00004805,
+};
+
+static struct clk_alpha_pll npu_cc_pll1 = {
+ .offset = 0x400,
+ .vco_table = fabia_vco,
+ .num_vco = ARRAY_SIZE(fabia_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
+ .config = &npu_cc_pll1_config,
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_pll1",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_fabia_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 615000000,
+ [VDD_LOW] = 1066000000,
+ [VDD_LOW_L1] = 1600000000,
+ [VDD_NOMINAL] = 2000000000},
+ },
+ },
+};
+
+/* 250MHz Configuration */
+static struct alpha_pll_config npu_q6ss_pll_config = {
+ .l = 0xD,
+ .alpha = 0x555,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00002067,
+ .test_ctl_val = 0x40000000,
+ .test_ctl_hi_val = 0x00000002,
+ .user_ctl_val = 0x00000001,
+ .user_ctl_hi_val = 0x00004805,
+};
+
+static struct clk_alpha_pll npu_q6ss_pll = {
+ .offset = 0x0,
+ .vco_table = fabia_vco,
+ .num_vco = ARRAY_SIZE(fabia_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
+ .config = &npu_q6ss_pll_config,
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_q6ss_pll",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_fabia_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 615000000,
+ [VDD_LOW] = 1066000000,
+ [VDD_LOW_L1] = 1600000000,
+ [VDD_NOMINAL] = 2000000000},
+ },
+ },
+};
+
+static struct clk_fixed_factor npu_cc_crc_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_crc_div",
+ .parent_names = (const char *[]){ "npu_cc_pll0" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_npu_cc_cal_hm0_clk_src[] = {
+ F(100000000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ F(268800000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ F(403200000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ F(515000000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ F(650000000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ F(850000000, P_NPU_CC_CRC_DIV, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 npu_cc_cal_hm0_clk_src = {
+ .cmd_rcgr = 0x1100,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = npu_cc_parent_map_0_crc,
+ .freq_tbl = ftbl_npu_cc_cal_hm0_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "npu_cc_cal_hm0_clk_src",
+ .parent_names = npu_cc_parent_names_0_crc,
+ .num_parents = 6,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 100000000,
+ [VDD_LOWER] = 268800000,
+ [VDD_LOW] = 403200000,
+ [VDD_LOW_L1] = 515000000,
+ [VDD_NOMINAL] = 650000000,
+ [VDD_HIGH] = 850000000},
+ },
+};
+
+static const struct freq_tbl ftbl_npu_cc_core_clk_src[] = {
+ F(60000000, P_GCC_NPU_GPLL0_DIV_CLK, 5, 0, 0),
+ F(100000000, P_GCC_NPU_GPLL0_DIV_CLK, 3, 0, 0),
+ F(200000000, P_GCC_NPU_GPLL0_CLK, 3, 0, 0),
+ F(333333333, P_NPU_CC_PLL1_OUT_EVEN, 4.5, 0, 0),
+ F(428571429, P_NPU_CC_PLL1_OUT_EVEN, 3.5, 0, 0),
+ F(500000000, P_NPU_CC_PLL1_OUT_EVEN, 3, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 npu_cc_core_clk_src = {
+ .cmd_rcgr = 0x1010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = npu_cc_parent_map_0,
+ .freq_tbl = ftbl_npu_cc_core_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "npu_cc_core_clk_src",
+ .parent_names = npu_cc_parent_names_0,
+ .num_parents = 6,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 60000000,
+ [VDD_LOWER] = 100000000,
+ [VDD_LOW] = 200000000,
+ [VDD_LOW_L1] = 333333333,
+ [VDD_NOMINAL] = 428571429,
+ [VDD_HIGH] = 500000000},
+ },
+};
+
+static const struct freq_tbl ftbl_npu_cc_xo_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 npu_cc_xo_clk_src = {
+ .cmd_rcgr = 0x1400,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = npu_cc_parent_map_1,
+ .freq_tbl = ftbl_npu_cc_xo_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "npu_cc_xo_clk_src",
+ .parent_names = npu_cc_parent_names_1,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_npu_dsp_core_clk_src[] = {
+ F(250000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ F(300000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ F(400000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ F(500000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ F(660000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ F(800000000, P_NPU_Q6SS_PLL_OUT_MAIN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 npu_dsp_core_clk_src = {
+ .cmd_rcgr = 0x28,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = npu_cc_parent_map_2,
+ .freq_tbl = ftbl_npu_dsp_core_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "npu_dsp_core_clk_src",
+ .parent_names = npu_cc_parent_names_2,
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ .vdd_class = &vdd_cx,
+ .num_rate_max = VDD_NUM,
+ .rate_max = (unsigned long[VDD_NUM]) {
+ [VDD_MIN] = 250000000,
+ [VDD_LOWER] = 300000000,
+ [VDD_LOW] = 400000000,
+ [VDD_LOW_L1] = 500000000,
+ [VDD_NOMINAL] = 660000000,
+ [VDD_HIGH] = 800000000},
+ },
+};
+
+static struct clk_branch npu_cc_bto_core_clk = {
+ .halt_reg = 0x10dc,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10dc,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_bto_core_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_xo_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_bwmon_clk = {
+ .halt_reg = 0x10d8,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10d8,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_bwmon_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_xo_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_cal_hm0_cdc_clk = {
+ .halt_reg = 0x1098,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1098,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_cal_hm0_cdc_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_cal_hm0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_cal_hm0_clk = {
+ .halt_reg = 0x1110,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1110,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_cal_hm0_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_cal_hm0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_cal_hm0_perf_cnt_clk = {
+ .halt_reg = 0x10a0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10a0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_cal_hm0_perf_cnt_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_cal_hm0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_core_clk = {
+ .halt_reg = 0x1030,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1030,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_core_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_dsp_ahbm_clk = {
+ .halt_reg = 0x1214,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1214,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_dsp_ahbm_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_dsp_ahbs_clk = {
+ .halt_reg = 0x1210,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x1210,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_dsp_ahbs_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_dsp_axi_clk = {
+ .halt_reg = 0x121c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x121c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_dsp_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_noc_ahb_clk = {
+ .halt_reg = 0x10c0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10c0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_noc_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_noc_axi_clk = {
+ .halt_reg = 0x10b8,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10b8,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_noc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_noc_dma_clk = {
+ .halt_reg = 0x10b0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10b0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_noc_dma_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_rsc_xo_clk = {
+ .halt_reg = 0x10e0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10e0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_rsc_xo_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_xo_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_s2p_clk = {
+ .halt_reg = 0x10cc,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x10cc,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_s2p_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch npu_cc_xo_clk = {
+ .halt_reg = 0x1410,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1410,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "npu_cc_xo_clk",
+ .parent_names = (const char *[]){
+ "npu_cc_xo_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_regmap *npu_cc_lagoon_clocks[] = {
+ [NPU_CC_BTO_CORE_CLK] = &npu_cc_bto_core_clk.clkr,
+ [NPU_CC_BWMON_CLK] = &npu_cc_bwmon_clk.clkr,
+ [NPU_CC_CAL_HM0_CDC_CLK] = &npu_cc_cal_hm0_cdc_clk.clkr,
+ [NPU_CC_CAL_HM0_CLK] = &npu_cc_cal_hm0_clk.clkr,
+ [NPU_CC_CAL_HM0_CLK_SRC] = &npu_cc_cal_hm0_clk_src.clkr,
+ [NPU_CC_CAL_HM0_PERF_CNT_CLK] = &npu_cc_cal_hm0_perf_cnt_clk.clkr,
+ [NPU_CC_CORE_CLK] = &npu_cc_core_clk.clkr,
+ [NPU_CC_CORE_CLK_SRC] = &npu_cc_core_clk_src.clkr,
+ [NPU_CC_DSP_AHBM_CLK] = &npu_cc_dsp_ahbm_clk.clkr,
+ [NPU_CC_DSP_AHBS_CLK] = &npu_cc_dsp_ahbs_clk.clkr,
+ [NPU_CC_DSP_AXI_CLK] = &npu_cc_dsp_axi_clk.clkr,
+ [NPU_CC_NOC_AHB_CLK] = &npu_cc_noc_ahb_clk.clkr,
+ [NPU_CC_NOC_AXI_CLK] = &npu_cc_noc_axi_clk.clkr,
+ [NPU_CC_NOC_DMA_CLK] = &npu_cc_noc_dma_clk.clkr,
+ [NPU_CC_PLL0] = &npu_cc_pll0.clkr,
+ [NPU_CC_PLL1] = &npu_cc_pll1.clkr,
+ [NPU_CC_RSC_XO_CLK] = &npu_cc_rsc_xo_clk.clkr,
+ [NPU_CC_S2P_CLK] = &npu_cc_s2p_clk.clkr,
+ [NPU_CC_XO_CLK] = &npu_cc_xo_clk.clkr,
+ [NPU_CC_XO_CLK_SRC] = &npu_cc_xo_clk_src.clkr,
+};
+
+static struct clk_regmap *npu_qdsp6ss_lagoon_clocks[] = {
+ [NPU_DSP_CORE_CLK_SRC] = &npu_dsp_core_clk_src.clkr,
+};
+
+static struct clk_regmap *npu_qdsp6ss_pll_lagoon_clocks[] = {
+ [NPU_Q6SS_PLL] = &npu_q6ss_pll.clkr,
+};
+
+static const struct qcom_reset_map npu_cc_lagoon_resets[] = {
+ [NPU_CC_CAL_HM0_BCR] = { 0x10f0 },
+ [NPU_CC_CORE_BCR] = { 0x1000 },
+ [NPU_CC_DSP_BCR] = { 0x1200 },
+};
+
+static const struct regmap_config npu_cc_lagoon_regmap_config = {
+ .name = "cc",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xa060,
+ .fast_io = true,
+};
+
+static const struct regmap_config npu_qdsp6ss_lagoon_regmap_config = {
+ .name = "qdsp6ss",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x203c,
+ .fast_io = true,
+};
+
+static const struct regmap_config npu_qdsp6ss_pll_lagoon_regmap_config = {
+ .name = "qdsp6ss_pll",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x50,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc npu_cc_lagoon_desc = {
+ .config = &npu_cc_lagoon_regmap_config,
+ .clks = npu_cc_lagoon_clocks,
+ .num_clks = ARRAY_SIZE(npu_cc_lagoon_clocks),
+ .resets = npu_cc_lagoon_resets,
+ .num_resets = ARRAY_SIZE(npu_cc_lagoon_resets),
+};
+
+static const struct qcom_cc_desc npu_qdsp6ss_lagoon_desc = {
+ .config = &npu_qdsp6ss_lagoon_regmap_config,
+ .clks = npu_qdsp6ss_lagoon_clocks,
+ .num_clks = ARRAY_SIZE(npu_qdsp6ss_lagoon_clocks),
+};
+
+static const struct qcom_cc_desc npu_qdsp6ss_pll_lagoon_desc = {
+ .config = &npu_qdsp6ss_pll_lagoon_regmap_config,
+ .clks = npu_qdsp6ss_pll_lagoon_clocks,
+ .num_clks = ARRAY_SIZE(npu_qdsp6ss_pll_lagoon_clocks),
+};
+
+static const struct of_device_id npu_cc_lagoon_match_table[] = {
+ { .compatible = "qcom,lagoon-npucc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, npu_cc_lagoon_match_table);
+
+static int npu_clocks_lagoon_probe(struct platform_device *pdev,
+ const struct qcom_cc_desc *desc)
+{
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ desc->config->name);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ if (!strcmp("cc", desc->config->name)) {
+ clk_fabia_pll_configure(&npu_cc_pll0, regmap,
+ &npu_cc_pll0_config);
+ clk_fabia_pll_configure(&npu_cc_pll1, regmap,
+ &npu_cc_pll1_config);
+
+ /* Register the fixed factor clock for CRC divider */
+ ret = devm_clk_hw_register(&pdev->dev, &npu_cc_crc_div.hw);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register CRC divider clock, ret=%d\n", ret);
+ return ret;
+ }
+ } else if (!strcmp("qdsp6ss_pll", desc->config->name)) {
+ clk_fabia_pll_configure(&npu_q6ss_pll, regmap,
+ &npu_q6ss_pll_config);
+ }
+
+ ret = qcom_cc_really_probe(pdev, desc, regmap);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register NPU CC clocks\n");
+
+ return ret;
+}
+
+static int npu_cc_lagoon_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx");
+ if (IS_ERR(vdd_cx.regulator[0])) {
+ if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_cx regulator\n");
+ return PTR_ERR(vdd_cx.regulator[0]);
+ }
+
+ ret = npu_clocks_lagoon_probe(pdev, &npu_cc_lagoon_desc);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "npu_cc clock registration failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = npu_clocks_lagoon_probe(pdev, &npu_qdsp6ss_lagoon_desc);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "npu_qdsp6ss clock registration failed, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ ret = npu_clocks_lagoon_probe(pdev, &npu_qdsp6ss_pll_lagoon_desc);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "npu_qdsp6ss_pll clock registration failed, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "Registered NPU CC clocks\n");
+
+ return ret;
+}
+
+static struct platform_driver npu_cc_lagoon_driver = {
+ .probe = npu_cc_lagoon_probe,
+ .driver = {
+ .name = "lagoon-npucc",
+ .of_match_table = npu_cc_lagoon_match_table,
+ },
+};
+
+static int __init npu_cc_lagoon_init(void)
+{
+ return platform_driver_register(&npu_cc_lagoon_driver);
+}
+core_initcall(npu_cc_lagoon_init);
+
+static void __exit npu_cc_lagoon_exit(void)
+{
+ platform_driver_unregister(&npu_cc_lagoon_driver);
+}
+module_exit(npu_cc_lagoon_exit);
+
+MODULE_DESCRIPTION("QTI NPU_CC LAGOON Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:npu_cc-lagoon");
diff --git a/drivers/clk/qcom/videocc-kona.c b/drivers/clk/qcom/videocc-kona.c
index 4616556..c090961 100644
--- a/drivers/clk/qcom/videocc-kona.c
+++ b/drivers/clk/qcom/videocc-kona.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -126,7 +126,7 @@ static const struct alpha_pll_config video_pll0_config = {
.alpha = 0x8000,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000000,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
@@ -160,7 +160,7 @@ static const struct alpha_pll_config video_pll1_config = {
.alpha = 0xFAAA,
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002261,
- .config_ctl_hi1_val = 0x029A699C,
+ .config_ctl_hi1_val = 0x329A699C,
.user_ctl_val = 0x00000000,
.user_ctl_hi_val = 0x00000805,
.user_ctl_hi1_val = 0x00000000,
diff --git a/drivers/clk/qcom/videocc-lagoon.c b/drivers/clk/qcom/videocc-lagoon.c
index 5ddb669..453223b 100644
--- a/drivers/clk/qcom/videocc-lagoon.c
+++ b/drivers/clk/qcom/videocc-lagoon.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "clk: %s: " fmt, __func__
@@ -78,7 +78,7 @@ static const struct alpha_pll_config video_pll0_config = {
.config_ctl_val = 0x20485699,
.config_ctl_hi_val = 0x00002067,
.test_ctl_val = 0x40000000,
- .test_ctl_hi_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000002,
.user_ctl_val = 0x00000101,
.user_ctl_hi_val = 0x00004005,
};
diff --git a/drivers/clk/renesas/r8a77990-cpg-mssr.c b/drivers/clk/renesas/r8a77990-cpg-mssr.c
index 9e14f14..8156976 100644
--- a/drivers/clk/renesas/r8a77990-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a77990-cpg-mssr.c
@@ -175,8 +175,8 @@ static const struct mssr_mod_clk r8a77990_mod_clks[] __initconst = {
DEF_MOD("ehci0", 703, R8A77990_CLK_S3D4),
DEF_MOD("hsusb", 704, R8A77990_CLK_S3D4),
DEF_MOD("csi40", 716, R8A77990_CLK_CSI0),
- DEF_MOD("du1", 723, R8A77990_CLK_S2D1),
- DEF_MOD("du0", 724, R8A77990_CLK_S2D1),
+ DEF_MOD("du1", 723, R8A77990_CLK_S1D1),
+ DEF_MOD("du0", 724, R8A77990_CLK_S1D1),
DEF_MOD("lvds", 727, R8A77990_CLK_S2D1),
DEF_MOD("vin5", 806, R8A77990_CLK_S1D2),
diff --git a/drivers/clk/renesas/r8a77995-cpg-mssr.c b/drivers/clk/renesas/r8a77995-cpg-mssr.c
index ea4cafb..9e16931 100644
--- a/drivers/clk/renesas/r8a77995-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a77995-cpg-mssr.c
@@ -141,8 +141,8 @@ static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = {
DEF_MOD("vspbs", 627, R8A77995_CLK_S0D1),
DEF_MOD("ehci0", 703, R8A77995_CLK_S3D2),
DEF_MOD("hsusb", 704, R8A77995_CLK_S3D2),
- DEF_MOD("du1", 723, R8A77995_CLK_S2D1),
- DEF_MOD("du0", 724, R8A77995_CLK_S2D1),
+ DEF_MOD("du1", 723, R8A77995_CLK_S1D1),
+ DEF_MOD("du0", 724, R8A77995_CLK_S1D1),
DEF_MOD("lvds", 727, R8A77995_CLK_S2D1),
DEF_MOD("vin7", 804, R8A77995_CLK_S1D2),
DEF_MOD("vin6", 805, R8A77995_CLK_S1D2),
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 628b63b..9ace7d3 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -361,7 +361,7 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
struct sd_clock *clock;
struct clk *clk;
unsigned int i;
- u32 sd_fc;
+ u32 val;
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
if (!clock)
@@ -378,17 +378,9 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
clock->div_table = cpg_sd_div_table;
clock->div_num = ARRAY_SIZE(cpg_sd_div_table);
- sd_fc = readl(clock->csn.reg) & CPG_SD_FC_MASK;
- for (i = 0; i < clock->div_num; i++)
- if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK))
- break;
-
- if (WARN_ON(i >= clock->div_num)) {
- kfree(clock);
- return ERR_PTR(-EINVAL);
- }
-
- clock->cur_div_idx = i;
+ val = readl(clock->csn.reg) & ~CPG_SD_FC_MASK;
+ val |= CPG_SD_STP_MASK | (clock->div_table[0].val & CPG_SD_FC_MASK);
+ writel(val, clock->csn.reg);
clock->div_max = clock->div_table[0].div;
clock->div_min = clock->div_max;
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 69fb3af..2ca7e2b 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -362,8 +362,8 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
RK2928_CLKGATE_CON(2), 5, GFLAGS),
MUX(SCLK_MAC, "sclk_macref", mux_sclk_macref_p, CLK_SET_RATE_PARENT,
RK2928_CLKSEL_CON(21), 4, 1, MFLAGS),
- GATE(0, "sclk_mac_lbtest", "sclk_macref",
- RK2928_CLKGATE_CON(2), 12, 0, GFLAGS),
+ GATE(0, "sclk_mac_lbtest", "sclk_macref", 0,
+ RK2928_CLKGATE_CON(2), 12, GFLAGS),
COMPOSITE(0, "hsadc_src", mux_pll_src_gpll_cpll_p, 0,
RK2928_CLKSEL_CON(22), 0, 1, MFLAGS, 8, 8, DFLAGS,
@@ -391,8 +391,8 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
* Clock-Architecture Diagram 4
*/
- GATE(SCLK_SMC, "sclk_smc", "hclk_peri",
- RK2928_CLKGATE_CON(2), 4, 0, GFLAGS),
+ GATE(SCLK_SMC, "sclk_smc", "hclk_peri", 0,
+ RK2928_CLKGATE_CON(2), 4, GFLAGS),
COMPOSITE_NOMUX(SCLK_SPI0, "sclk_spi0", "pclk_peri", 0,
RK2928_CLKSEL_CON(25), 0, 7, DFLAGS,
diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c
index ecbae8a..f2f13b6 100644
--- a/drivers/clk/rockchip/clk-rk3328.c
+++ b/drivers/clk/rockchip/clk-rk3328.c
@@ -392,7 +392,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(1), 5, GFLAGS,
&rk3328_i2s1_fracmux),
GATE(SCLK_I2S1, "clk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT,
- RK3328_CLKGATE_CON(0), 6, GFLAGS),
+ RK3328_CLKGATE_CON(1), 6, GFLAGS),
COMPOSITE_NODIV(SCLK_I2S1_OUT, "i2s1_out", mux_i2s1out_p, 0,
RK3328_CLKSEL_CON(8), 12, 1, MFLAGS,
RK3328_CLKGATE_CON(1), 7, GFLAGS),
diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c
index d2c99d8..a5fddeb 100644
--- a/drivers/clk/samsung/clk-cpu.c
+++ b/drivers/clk/samsung/clk-cpu.c
@@ -152,7 +152,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk, void __iomem *base)
{
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
- unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent);
+ unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
unsigned long div0, div1 = 0, mux_reg;
unsigned long flags;
@@ -280,7 +280,7 @@ static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk, void __iomem *base)
{
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
- unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent);
+ unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
unsigned long div0, div1 = 0, mux_reg;
unsigned long flags;
@@ -432,7 +432,7 @@ int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
else
cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
- cpuclk->alt_parent = __clk_lookup(alt_parent);
+ cpuclk->alt_parent = __clk_get_hw(__clk_lookup(alt_parent));
if (!cpuclk->alt_parent) {
pr_err("%s: could not lookup alternate parent %s\n",
__func__, alt_parent);
diff --git a/drivers/clk/samsung/clk-cpu.h b/drivers/clk/samsung/clk-cpu.h
index d4b6b51..bd38c6a 100644
--- a/drivers/clk/samsung/clk-cpu.h
+++ b/drivers/clk/samsung/clk-cpu.h
@@ -49,7 +49,7 @@ struct exynos_cpuclk_cfg_data {
*/
struct exynos_cpuclk {
struct clk_hw hw;
- struct clk *alt_parent;
+ struct clk_hw *alt_parent;
void __iomem *ctrl_base;
spinlock_t *lock;
const struct exynos_cpuclk_cfg_data *cfg;
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index d4f77c4..d5af937 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -171,12 +171,18 @@ static const unsigned long exynos5x_clk_regs[] __initconst = {
GATE_BUS_CPU,
GATE_SCLK_CPU,
CLKOUT_CMU_CPU,
+ CPLL_CON0,
+ DPLL_CON0,
EPLL_CON0,
EPLL_CON1,
EPLL_CON2,
RPLL_CON0,
RPLL_CON1,
RPLL_CON2,
+ IPLL_CON0,
+ SPLL_CON0,
+ VPLL_CON0,
+ MPLL_CON0,
SRC_TOP0,
SRC_TOP1,
SRC_TOP2,
@@ -634,6 +640,7 @@ static const struct samsung_div_clock exynos5420_div_clks[] __initconst = {
};
static const struct samsung_gate_clock exynos5420_gate_clks[] __initconst = {
+ GATE(CLK_SECKEY, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0),
GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk",
SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0),
};
@@ -1163,8 +1170,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(CLK_TMU, "tmu", "aclk66_psgen", GATE_IP_PERIS, 21, 0, 0),
GATE(CLK_TMU_GPU, "tmu_gpu", "aclk66_psgen", GATE_IP_PERIS, 22, 0, 0),
- GATE(CLK_SECKEY, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0),
-
/* GEN Block */
GATE(CLK_ROTATOR, "rotator", "mout_user_aclk266", GATE_IP_GEN, 1, 0, 0),
GATE(CLK_JPEG, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0),
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 162de44..302596d 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -16,6 +16,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <dt-bindings/clock/exynos5433.h>
@@ -5527,6 +5528,8 @@ static int __init exynos5433_cmu_probe(struct platform_device *pdev)
data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
info->nr_clk_regs);
+ if (!data->clk_save)
+ return -ENOMEM;
data->nr_clk_save = info->nr_clk_regs;
data->clk_suspend = info->suspend_regs;
data->nr_clk_suspend = info->nr_suspend_regs;
@@ -5535,12 +5538,19 @@ static int __init exynos5433_cmu_probe(struct platform_device *pdev)
if (data->nr_pclks > 0) {
data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
data->nr_pclks, GFP_KERNEL);
-
+ if (!data->pclks) {
+ kfree(data->clk_save);
+ return -ENOMEM;
+ }
for (i = 0; i < data->nr_pclks; i++) {
struct clk *clk = of_clk_get(dev->of_node, i);
- if (IS_ERR(clk))
+ if (IS_ERR(clk)) {
+ kfree(data->clk_save);
+ while (--i >= 0)
+ clk_put(data->pclks[i]);
return PTR_ERR(clk);
+ }
data->pclks[i] = clk;
}
}
@@ -5630,7 +5640,7 @@ static const struct of_device_id exynos5433_cmu_of_match[] = {
static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
NULL)
- SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
index ee9c12c..dec4a13 100644
--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
@@ -158,7 +158,12 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
#define SUN50I_A64_PLL_MIPI_REG 0x040
static struct ccu_nkm pll_mipi_clk = {
- .enable = BIT(31),
+ /*
+ * The bit 23 and 22 are called "LDO{1,2}_EN" on the SoC's
+ * user manual, and by experiments the PLL doesn't work without
+ * these bits toggled.
+ */
+ .enable = BIT(31) | BIT(23) | BIT(22),
.lock = BIT(28),
.n = _SUNXI_CCU_MULT(8, 4),
.k = _SUNXI_CCU_MULT_MIN(4, 2, 2),
@@ -577,7 +582,7 @@ static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" };
static const u8 dsi_dphy_table[] = { 0, 2, };
static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
dsi_dphy_parents, dsi_dphy_table,
- 0x168, 0, 4, 8, 2, BIT(31), CLK_SET_RATE_PARENT);
+ 0x168, 0, 4, 8, 2, BIT(15), CLK_SET_RATE_PARENT);
static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT);
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
index 0f7a0ff..d425b47 100644
--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
@@ -352,7 +352,7 @@ static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "psi-ahb1-ahb2",
static SUNXI_CCU_GATE(bus_psi_clk, "bus-psi", "psi-ahb1-ahb2",
0x79c, BIT(0), 0);
-static SUNXI_CCU_GATE(bus_pwm_clk, "bus-pwm", "apb1", 0x79c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_pwm_clk, "bus-pwm", "apb1", 0x7ac, BIT(0), 0);
static SUNXI_CCU_GATE(bus_iommu_clk, "bus-iommu", "apb1", 0x7bc, BIT(0), 0);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 77ed0b0..61e3ba1 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -475,7 +475,7 @@ static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" };
static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents,
0x134, 16, 4, 24, 3, BIT(31), 0);
-static const char * const csi_mclk_parents[] = { "osc24M", "pll-video", "pll-periph0" };
+static const char * const csi_mclk_parents[] = { "osc24M", "pll-video", "pll-periph1" };
static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents,
0x134, 0, 5, 8, 3, BIT(15), 0);
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
index 8936ef8..c14bf78 100644
--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
@@ -1231,7 +1231,7 @@ static int sun9i_a80_ccu_probe(struct platform_device *pdev)
/* Enforce d1 = 0, d2 = 0 for Audio PLL */
val = readl(reg + SUN9I_A80_PLL_AUDIO_REG);
- val &= (BIT(16) & BIT(18));
+ val &= ~(BIT(16) | BIT(18));
writel(val, reg + SUN9I_A80_PLL_AUDIO_REG);
/* Enforce P = 1 for both CPU cluster PLLs */
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index 012714d..004b411 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -1086,8 +1086,8 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node,
rate_hw, rate_ops,
gate_hw, &clk_gate_ops,
clkflags |
- data->div[i].critical ?
- CLK_IS_CRITICAL : 0);
+ (data->div[i].critical ?
+ CLK_IS_CRITICAL : 0));
WARN_ON(IS_ERR(clk_data->clks[i]));
}
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index cc857d4..68551ef 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -578,7 +578,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
[tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true },
[tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true },
[tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },
- [tegra_clk_emc] = { .dt_id = TEGRA20_CLK_EMC, .present = true },
};
static unsigned long tegra20_clk_measure_input_freq(void)
@@ -799,6 +798,31 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2),
};
+static void __init tegra20_emc_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+ ARRAY_SIZE(mux_pllmcp_clkm),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + CLK_SOURCE_EMC,
+ 30, 2, 0, &emc_lock);
+
+ clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+ &emc_lock);
+ clks[TEGRA20_CLK_MC] = clk;
+
+ /*
+ * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
+ * the same time due to a HW bug, this won't happen because we're
+ * defining 'emc_mux' and 'emc' as distinct clocks.
+ */
+ clk = tegra_clk_register_divider("emc", "emc_mux",
+ clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL,
+ TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock);
+ clks[TEGRA20_CLK_EMC] = clk;
+}
+
static void __init tegra20_periph_clk_init(void)
{
struct tegra_periph_init_data *data;
@@ -812,15 +836,7 @@ static void __init tegra20_periph_clk_init(void)
clks[TEGRA20_CLK_AC97] = clk;
/* emc */
- clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
- ARRAY_SIZE(mux_pllmcp_clkm),
- CLK_SET_RATE_NO_REPARENT,
- clk_base + CLK_SOURCE_EMC,
- 30, 2, 0, &emc_lock);
-
- clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
- &emc_lock);
- clks[TEGRA20_CLK_MC] = clk;
+ tegra20_emc_clk_init();
/* dsi */
clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 080bfa2..7264e97 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -2603,7 +2603,7 @@ static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = {
[TEGRA_POWERGATE_MPE] = {
.handle_lvl2_ovr = tegra210_generic_mbist_war,
.lvl2_offset = LVL2_CLK_GATE_OVRE,
- .lvl2_mask = BIT(2),
+ .lvl2_mask = BIT(29),
},
[TEGRA_POWERGATE_SOR] = {
.handle_lvl2_ovr = tegra210_generic_mbist_war,
@@ -2654,14 +2654,14 @@ static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = {
.num_clks = ARRAY_SIZE(nvdec_slcg_clkids),
.clk_init_data = nvdec_slcg_clkids,
.handle_lvl2_ovr = tegra210_generic_mbist_war,
- .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
.lvl2_mask = BIT(9) | BIT(31),
},
[TEGRA_POWERGATE_NVJPG] = {
.num_clks = ARRAY_SIZE(nvjpg_slcg_clkids),
.clk_init_data = nvjpg_slcg_clkids,
.handle_lvl2_ovr = tegra210_generic_mbist_war,
- .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
.lvl2_mask = BIT(9) | BIT(31),
},
[TEGRA_POWERGATE_AUD] = {
diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c
index 1488154..beb672a 100644
--- a/drivers/clk/ti/clk-dra7-atl.c
+++ b/drivers/clk/ti/clk-dra7-atl.c
@@ -174,7 +174,6 @@ static void __init of_dra7_atl_clock_setup(struct device_node *node)
struct clk_init_data init = { NULL };
const char **parent_names = NULL;
struct clk *clk;
- int ret;
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
if (!clk_hw) {
@@ -207,11 +206,6 @@ static void __init of_dra7_atl_clock_setup(struct device_node *node)
clk = ti_clk_register(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
- ret = ti_clk_add_alias(NULL, clk, node->name);
- if (ret) {
- clk_unregister(clk);
- goto cleanup;
- }
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(parent_names);
return;
diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c
index dfaa5aa..2c2564a 100644
--- a/drivers/clk/ti/clkctrl.c
+++ b/drivers/clk/ti/clkctrl.c
@@ -100,11 +100,12 @@ static bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout)
* can be from a timer that requires pm_runtime access, which
* will eventually bring us here with timekeeping_suspended,
* during both suspend entry and resume paths. This happens
- * at least on am43xx platform.
+ * at least on am43xx platform. Account for flakeyness
+ * with udelay() by multiplying the timeout value by 2.
*/
if (unlikely(_early_timeout || timekeeping_suspended)) {
if (time->cycles++ < timeout) {
- udelay(1);
+ udelay(1 * 2);
return false;
}
} else {
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 4d37f01..25e2967 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -376,6 +376,14 @@
the Allwinner A64 SoC. The workaround will only be active if the
allwinner,erratum-unknown1 property is found in the timer node.
+config ARM_ARCH_TIMER_VCT_ACCESS
+ bool "Support for ARM architected timer virtual counter access in userspace"
+ default n
+ depends on ARM_ARCH_TIMER
+ help
+ This option enables support for reading the ARM architected timer's
+ virtual counter in userspace.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select TIMER_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 924a14d..c7f38cf 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -29,6 +29,7 @@
#include <linux/acpi.h>
#include <asm/arch_timer.h>
+#include <asm/traps.h>
#include <asm/virt.h>
#include <clocksource/arm_arch_timer.h>
@@ -871,7 +872,8 @@ static void arch_counter_set_user_access(void)
* need to be workaround. The vdso may have been already
* disabled though.
*/
- if (arch_timer_this_cpu_has_cntvct_wa())
+ if (arch_timer_this_cpu_has_cntvct_wa() ||
+ !IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS))
pr_info("CPU%d: Trapping CNTVCT access\n", smp_processor_id());
else
cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
@@ -1519,6 +1521,8 @@ static int __init arch_timer_mem_of_init(struct device_node *np)
ret = arch_timer_mem_frame_register(frame);
if (!ret && !arch_timer_needs_of_probing())
ret = arch_timer_common_init();
+ get_timer_count_hook_init();
+ get_timer_freq_hook_init();
out:
kfree(timer_mem);
return ret;
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index 38cd2fe..0ce7607 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -198,6 +198,10 @@ static int __init asm9260_timer_init(struct device_node *np)
}
clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ pr_err("Failed to get clk!\n");
+ return PTR_ERR(clk);
+ }
ret = clk_prepare_enable(clk);
if (ret) {
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index bbbf37c..cec90a4 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -78,18 +78,17 @@ struct sh_cmt_info {
unsigned int channels_mask;
unsigned long width; /* 16 or 32 bit version of hardware block */
- unsigned long overflow_bit;
- unsigned long clear_bits;
+ u32 overflow_bit;
+ u32 clear_bits;
/* callbacks for CMSTR and CMCSR access */
- unsigned long (*read_control)(void __iomem *base, unsigned long offs);
+ u32 (*read_control)(void __iomem *base, unsigned long offs);
void (*write_control)(void __iomem *base, unsigned long offs,
- unsigned long value);
+ u32 value);
/* callbacks for CMCNT and CMCOR access */
- unsigned long (*read_count)(void __iomem *base, unsigned long offs);
- void (*write_count)(void __iomem *base, unsigned long offs,
- unsigned long value);
+ u32 (*read_count)(void __iomem *base, unsigned long offs);
+ void (*write_count)(void __iomem *base, unsigned long offs, u32 value);
};
struct sh_cmt_channel {
@@ -103,13 +102,13 @@ struct sh_cmt_channel {
unsigned int timer_bit;
unsigned long flags;
- unsigned long match_value;
- unsigned long next_match_value;
- unsigned long max_match_value;
+ u32 match_value;
+ u32 next_match_value;
+ u32 max_match_value;
raw_spinlock_t lock;
struct clock_event_device ced;
struct clocksource cs;
- unsigned long total_cycles;
+ u64 total_cycles;
bool cs_enabled;
};
@@ -160,24 +159,22 @@ struct sh_cmt_device {
#define SH_CMT32_CMCSR_CKS_RCLK1 (7 << 0)
#define SH_CMT32_CMCSR_CKS_MASK (7 << 0)
-static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs)
+static u32 sh_cmt_read16(void __iomem *base, unsigned long offs)
{
return ioread16(base + (offs << 1));
}
-static unsigned long sh_cmt_read32(void __iomem *base, unsigned long offs)
+static u32 sh_cmt_read32(void __iomem *base, unsigned long offs)
{
return ioread32(base + (offs << 2));
}
-static void sh_cmt_write16(void __iomem *base, unsigned long offs,
- unsigned long value)
+static void sh_cmt_write16(void __iomem *base, unsigned long offs, u32 value)
{
iowrite16(value, base + (offs << 1));
}
-static void sh_cmt_write32(void __iomem *base, unsigned long offs,
- unsigned long value)
+static void sh_cmt_write32(void __iomem *base, unsigned long offs, u32 value)
{
iowrite32(value, base + (offs << 2));
}
@@ -242,7 +239,7 @@ static const struct sh_cmt_info sh_cmt_info[] = {
#define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */
-static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
+static inline u32 sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{
if (ch->iostart)
return ch->cmt->info->read_control(ch->iostart, 0);
@@ -250,8 +247,7 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
return ch->cmt->info->read_control(ch->cmt->mapbase, 0);
}
-static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
- unsigned long value)
+static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, u32 value)
{
if (ch->iostart)
ch->cmt->info->write_control(ch->iostart, 0, value);
@@ -259,39 +255,35 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
}
-static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
+static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
{
return ch->cmt->info->read_control(ch->ioctrl, CMCSR);
}
-static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
- unsigned long value)
+static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, u32 value)
{
ch->cmt->info->write_control(ch->ioctrl, CMCSR, value);
}
-static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
+static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{
return ch->cmt->info->read_count(ch->ioctrl, CMCNT);
}
-static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
- unsigned long value)
+static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value)
{
ch->cmt->info->write_count(ch->ioctrl, CMCNT, value);
}
-static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
- unsigned long value)
+static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, u32 value)
{
ch->cmt->info->write_count(ch->ioctrl, CMCOR, value);
}
-static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
- int *has_wrapped)
+static u32 sh_cmt_get_counter(struct sh_cmt_channel *ch, u32 *has_wrapped)
{
- unsigned long v1, v2, v3;
- int o1, o2;
+ u32 v1, v2, v3;
+ u32 o1, o2;
o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit;
@@ -311,7 +303,8 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
{
- unsigned long flags, value;
+ unsigned long flags;
+ u32 value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&ch->cmt->lock, flags);
@@ -418,11 +411,11 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch,
int absolute)
{
- unsigned long new_match;
- unsigned long value = ch->next_match_value;
- unsigned long delay = 0;
- unsigned long now = 0;
- int has_wrapped;
+ u32 value = ch->next_match_value;
+ u32 new_match;
+ u32 delay = 0;
+ u32 now = 0;
+ u32 has_wrapped;
now = sh_cmt_get_counter(ch, &has_wrapped);
ch->flags |= FLAG_REPROGRAM; /* force reprogram */
@@ -619,9 +612,10 @@ static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs)
static u64 sh_cmt_clocksource_read(struct clocksource *cs)
{
struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
- unsigned long flags, raw;
- unsigned long value;
- int has_wrapped;
+ unsigned long flags;
+ u32 has_wrapped;
+ u64 value;
+ u32 raw;
raw_spin_lock_irqsave(&ch->lock, flags);
value = ch->total_cycles;
@@ -694,7 +688,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch,
cs->disable = sh_cmt_clocksource_disable;
cs->suspend = sh_cmt_clocksource_suspend;
cs->resume = sh_cmt_clocksource_resume;
- cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
+ cs->mask = CLOCKSOURCE_MASK(sizeof(u64) * 8);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&ch->cmt->pdev->dev, "ch%u: used as clock source\n",
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c
index cf93f64..fadff79 100644
--- a/drivers/clocksource/timer-fttmr010.c
+++ b/drivers/clocksource/timer-fttmr010.c
@@ -21,7 +21,7 @@
#include <linux/delay.h>
/*
- * Register definitions for the timers
+ * Register definitions common for all the timer variants.
*/
#define TIMER1_COUNT (0x00)
#define TIMER1_LOAD (0x04)
@@ -36,9 +36,10 @@
#define TIMER3_MATCH1 (0x28)
#define TIMER3_MATCH2 (0x2c)
#define TIMER_CR (0x30)
-#define TIMER_INTR_STATE (0x34)
-#define TIMER_INTR_MASK (0x38)
+/*
+ * Control register (TMC30) bit fields for fttmr010/gemini/moxart timers.
+ */
#define TIMER_1_CR_ENABLE BIT(0)
#define TIMER_1_CR_CLOCK BIT(1)
#define TIMER_1_CR_INT BIT(2)
@@ -53,8 +54,9 @@
#define TIMER_3_CR_UPDOWN BIT(11)
/*
- * The Aspeed AST2400 moves bits around in the control register
- * and lacks bits for setting the timer to count upwards.
+ * Control register (TMC30) bit fields for aspeed ast2400/ast2500 timers.
+ * The aspeed timers move bits around in the control register and lacks
+ * bits for setting the timer to count upwards.
*/
#define TIMER_1_CR_ASPEED_ENABLE BIT(0)
#define TIMER_1_CR_ASPEED_CLOCK BIT(1)
@@ -66,6 +68,18 @@
#define TIMER_3_CR_ASPEED_CLOCK BIT(9)
#define TIMER_3_CR_ASPEED_INT BIT(10)
+/*
+ * Interrupt status/mask register definitions for fttmr010/gemini/moxart
+ * timers.
+ * The registers don't exist and they are not needed on aspeed timers
+ * because:
+ * - aspeed timer overflow interrupt is controlled by bits in Control
+ * Register (TMC30).
+ * - aspeed timers always generate interrupt when either one of the
+ * Match registers equals to Status register.
+ */
+#define TIMER_INTR_STATE (0x34)
+#define TIMER_INTR_MASK (0x38)
#define TIMER_1_INT_MATCH1 BIT(0)
#define TIMER_1_INT_MATCH2 BIT(1)
#define TIMER_1_INT_OVERFLOW BIT(2)
@@ -80,7 +94,7 @@
struct fttmr010 {
void __iomem *base;
unsigned int tick_rate;
- bool count_down;
+ bool is_aspeed;
u32 t1_enable_val;
struct clock_event_device clkevt;
#ifdef CONFIG_ARM
@@ -130,7 +144,7 @@ static int fttmr010_timer_set_next_event(unsigned long cycles,
cr &= ~fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
- if (fttmr010->count_down) {
+ if (fttmr010->is_aspeed) {
/*
* ASPEED Timer Controller will load TIMER1_LOAD register
* into TIMER1_COUNT register when the timer is re-enabled.
@@ -175,16 +189,17 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt)
/* Setup counter start from 0 or ~0 */
writel(0, fttmr010->base + TIMER1_COUNT);
- if (fttmr010->count_down)
+ if (fttmr010->is_aspeed) {
writel(~0, fttmr010->base + TIMER1_LOAD);
- else
+ } else {
writel(0, fttmr010->base + TIMER1_LOAD);
- /* Enable interrupt */
- cr = readl(fttmr010->base + TIMER_INTR_MASK);
- cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
- cr |= TIMER_1_INT_MATCH1;
- writel(cr, fttmr010->base + TIMER_INTR_MASK);
+ /* Enable interrupt */
+ cr = readl(fttmr010->base + TIMER_INTR_MASK);
+ cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
+ cr |= TIMER_1_INT_MATCH1;
+ writel(cr, fttmr010->base + TIMER_INTR_MASK);
+ }
return 0;
}
@@ -201,9 +216,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt)
writel(cr, fttmr010->base + TIMER_CR);
/* Setup timer to fire at 1/HZ intervals. */
- if (fttmr010->count_down) {
+ if (fttmr010->is_aspeed) {
writel(period, fttmr010->base + TIMER1_LOAD);
- writel(0, fttmr010->base + TIMER1_MATCH1);
} else {
cr = 0xffffffff - (period - 1);
writel(cr, fttmr010->base + TIMER1_COUNT);
@@ -281,23 +295,21 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
}
/*
- * The Aspeed AST2400 moves bits around in the control register,
- * otherwise it works the same.
+ * The Aspeed timers move bits around in the control register.
*/
if (is_aspeed) {
fttmr010->t1_enable_val = TIMER_1_CR_ASPEED_ENABLE |
TIMER_1_CR_ASPEED_INT;
- /* Downward not available */
- fttmr010->count_down = true;
+ fttmr010->is_aspeed = true;
} else {
fttmr010->t1_enable_val = TIMER_1_CR_ENABLE | TIMER_1_CR_INT;
- }
- /*
- * Reset the interrupt mask and status
- */
- writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK);
- writel(0, fttmr010->base + TIMER_INTR_STATE);
+ /*
+ * Reset the interrupt mask and status
+ */
+ writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK);
+ writel(0, fttmr010->base + TIMER_INTR_STATE);
+ }
/*
* Enable timer 1 count up, timer 2 count up, except on Aspeed,
@@ -306,9 +318,8 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
if (is_aspeed)
val = TIMER_2_CR_ASPEED_ENABLE;
else {
- val = TIMER_2_CR_ENABLE;
- if (!fttmr010->count_down)
- val |= TIMER_1_CR_UPDOWN | TIMER_2_CR_UPDOWN;
+ val = TIMER_2_CR_ENABLE | TIMER_1_CR_UPDOWN |
+ TIMER_2_CR_UPDOWN;
}
writel(val, fttmr010->base + TIMER_CR);
@@ -321,7 +332,7 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
writel(0, fttmr010->base + TIMER2_MATCH1);
writel(0, fttmr010->base + TIMER2_MATCH2);
- if (fttmr010->count_down) {
+ if (fttmr010->is_aspeed) {
writel(~0, fttmr010->base + TIMER2_LOAD);
clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
"FTTMR010-TIMER2",
@@ -371,7 +382,7 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
#ifdef CONFIG_ARM
/* Also use this timer for delays */
- if (fttmr010->count_down)
+ if (fttmr010->is_aspeed)
fttmr010->delay_timer.read_current_timer =
fttmr010_read_current_timer_down;
else
diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c
index eb10321..8e7894a 100644
--- a/drivers/clocksource/timer-mediatek.c
+++ b/drivers/clocksource/timer-mediatek.c
@@ -277,15 +277,12 @@ static int __init mtk_syst_init(struct device_node *node)
ret = timer_of_init(node, &to);
if (ret)
- goto err;
+ return ret;
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
return 0;
-err:
- timer_of_cleanup(&to);
- return ret;
}
static int __init mtk_gpt_init(struct device_node *node)
@@ -302,7 +299,7 @@ static int __init mtk_gpt_init(struct device_node *node)
ret = timer_of_init(node, &to);
if (ret)
- goto err;
+ return ret;
/* Configure clock source */
mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN);
@@ -320,9 +317,6 @@ static int __init mtk_gpt_init(struct device_node *node)
mtk_gpt_enable_irq(&to, TIMER_CLK_EVT);
return 0;
-err:
- timer_of_cleanup(&to);
- return ret;
}
TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init);
TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init);
diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c
index 06ed88a..6e2cb36 100644
--- a/drivers/clocksource/timer-of.c
+++ b/drivers/clocksource/timer-of.c
@@ -199,7 +199,7 @@ int __init timer_of_init(struct device_node *np, struct timer_of *to)
}
if (!to->clkevt.name)
- to->clkevt.name = np->name;
+ to->clkevt.name = np->full_name;
to->np = np;
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 5864245..2ca0cab 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -310,6 +310,16 @@
The driver implements the cpufreq interface for this HW engine.
Say Y if you want to support CPUFreq HW.
+config ARM_QCOM_CPUFREQ_HW_DEBUG
+ bool "QCOM CPUFreq HW debug"
+ depends on ARM_QCOM_CPUFREQ_HW
+ help
+ Support for the CPUFreq HW debug.
+ CPUFreq HW debug provide the support to capture trace packets for
+ various clock domain and trace type could be set to periodic and
+ xor if applicable.
+ Say Y if you want to support CPUFreq HW Trace.
+
config CPU_FREQ_MSM
bool "MSM CPUFreq support"
depends on CPU_FREQ
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index cc98603..addf17b 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -91,6 +91,7 @@
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW_DEBUG) += qcom-cpufreq-hw-debug.o
##################################################################################
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 136c9c2..b050316 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -944,6 +944,9 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
struct freq_attr *fattr = to_attr(attr);
ssize_t ret;
+ if (!fattr->show)
+ return -EIO;
+
down_read(&policy->rwsem);
ret = fattr->show(policy, buf);
up_read(&policy->rwsem);
@@ -958,6 +961,9 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
struct freq_attr *fattr = to_attr(attr);
ssize_t ret = -EINVAL;
+ if (!fattr->store)
+ return -EIO;
+
/*
* cpus_read_trylock() is used here to work around a circular lock
* dependency problem with respect to the cpufreq_register_driver().
@@ -2516,6 +2522,13 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
if (cpufreq_disabled())
return -ENODEV;
+ /*
+ * The cpufreq core depends heavily on the availability of device
+ * structure, make sure they are available before proceeding further.
+ */
+ if (!get_cpu_device(0))
+ return -EPROBE_DEFER;
+
if (!driver_data || !driver_data->verify || !driver_data->init ||
!(driver_data->setpolicy || driver_data->target_index ||
driver_data->target) ||
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index f4880a4..d8c3595 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -12,6 +12,7 @@
#include <linux/cpu_cooling.h>
#include <linux/err.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_opp.h>
@@ -295,20 +296,32 @@ static void imx6q_opp_check_speed_grading(struct device *dev)
#define OCOTP_CFG3_6ULL_SPEED_792MHZ 0x2
#define OCOTP_CFG3_6ULL_SPEED_900MHZ 0x3
-static void imx6ul_opp_check_speed_grading(struct device *dev)
+static int imx6ul_opp_check_speed_grading(struct device *dev)
{
- struct device_node *np;
- void __iomem *base;
u32 val;
+ int ret = 0;
- np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp");
- if (!np)
- return;
+ if (of_find_property(dev->of_node, "nvmem-cells", NULL)) {
+ ret = nvmem_cell_read_u32(dev, "speed_grade", &val);
+ if (ret)
+ return ret;
+ } else {
+ struct device_node *np;
+ void __iomem *base;
- base = of_iomap(np, 0);
- if (!base) {
- dev_err(dev, "failed to map ocotp\n");
- goto put_node;
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp");
+ if (!np)
+ return -ENOENT;
+
+ base = of_iomap(np, 0);
+ of_node_put(np);
+ if (!base) {
+ dev_err(dev, "failed to map ocotp\n");
+ return -EFAULT;
+ }
+
+ val = readl_relaxed(base + OCOTP_CFG3);
+ iounmap(base);
}
/*
@@ -319,7 +332,6 @@ static void imx6ul_opp_check_speed_grading(struct device *dev)
* 2b'11: 900000000Hz on i.MX6ULL only;
* We need to set the max speed of ARM according to fuse map.
*/
- val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
@@ -339,9 +351,7 @@ static void imx6ul_opp_check_speed_grading(struct device *dev)
dev_warn(dev, "failed to disable 900MHz OPP\n");
}
- iounmap(base);
-put_node:
- of_node_put(np);
+ return ret;
}
static int imx6q_cpufreq_probe(struct platform_device *pdev)
@@ -399,10 +409,18 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
}
if (of_machine_is_compatible("fsl,imx6ul") ||
- of_machine_is_compatible("fsl,imx6ull"))
- imx6ul_opp_check_speed_grading(cpu_dev);
- else
+ of_machine_is_compatible("fsl,imx6ull")) {
+ ret = imx6ul_opp_check_speed_grading(cpu_dev);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ if (ret) {
+ dev_err(cpu_dev, "failed to read ocotp: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
imx6q_opp_check_speed_grading(cpu_dev);
+ }
/* Because we have added the OPPs here, we must free them */
free_opp = true;
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index bf6519c..5fff39d 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -1042,9 +1042,14 @@ static struct cpufreq_driver powernv_cpufreq_driver = {
static int init_chip_info(void)
{
- unsigned int chip[256];
+ unsigned int *chip;
unsigned int cpu, i;
unsigned int prev_chip_id = UINT_MAX;
+ int ret = 0;
+
+ chip = kcalloc(num_possible_cpus(), sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
for_each_possible_cpu(cpu) {
unsigned int id = cpu_to_chip_id(cpu);
@@ -1056,8 +1061,10 @@ static int init_chip_info(void)
}
chips = kcalloc(nr_chips, sizeof(struct chip), GFP_KERNEL);
- if (!chips)
- return -ENOMEM;
+ if (!chips) {
+ ret = -ENOMEM;
+ goto free_and_return;
+ }
for (i = 0; i < nr_chips; i++) {
chips[i].id = chip[i];
@@ -1067,7 +1074,9 @@ static int init_chip_info(void)
per_cpu(chip_info, cpu) = &chips[i];
}
- return 0;
+free_and_return:
+ kfree(chip);
+ return ret;
}
static inline void clean_chip_info(void)
diff --git a/drivers/cpufreq/qcom-cpufreq-hw-debug.c b/drivers/cpufreq/qcom-cpufreq-hw-debug.c
new file mode 100644
index 0000000..d51acca
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq-hw-debug.c
@@ -0,0 +1,624 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "cpufreq_hw_debug: %s: " fmt, __func__
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/uaccess.h>
+#include <soc/qcom/scm.h>
+
+enum trace_type {
+ XOR_PACKET,
+ PERIODIC_PACKET,
+};
+
+enum clock_domain {
+ CLKDOM0,
+ CLKDOM1,
+ CLKDOM2,
+ CLKDOM3,
+
+ CLK_DOMAIN_SIZE,
+};
+
+enum debug_trace_regs_data {
+ GLOBAL_CTRL_OFFSET,
+ CLKDOM_CTRL_OFFSET,
+ PERIODIC_TIMER_CTRL_OFFSET,
+
+ PERIODIC_TRACE_ENABLE_BIT,
+ CPUFREQ_HW_TRACE_ENABLE_BIT,
+
+ REG_PERF_STATE,
+ REG_CYCLE_CNTR,
+ REG_PSTATE_STATUS,
+
+ REG_ARRAY_SIZE,
+};
+
+struct cpufreq_hwregs {
+ void __iomem *base[REG_ARRAY_SIZE];
+ int domain_cnt;
+ struct dentry *debugfs_base;
+};
+
+struct cpufreq_register_data {
+ char *name;
+ u16 offset;
+};
+
+#define CLKDOM0_TRACE_PACKET_SHIFT 0
+#define CLKDOM1_TRACE_PACKET_SHIFT 3
+#define CLKDOM2_TRACE_PACKET_SHIFT 6
+#define CLKDOM3_TRACE_PACKET_SHIFT 9
+#define CLKDOM_TRACE_PACKET_WIDTH 2
+#define MAX_DEBUG_BUF_LEN 50
+#define MAX_PKT_SIZE 5
+#define CLKDOMAIN_SET_VAL(val, packet_sel, shift) \
+ ((val & ~GENMASK(shift + CLKDOM_TRACE_PACKET_WIDTH, shift)) \
+ | (packet_sel << shift))
+#define CLKDOMAIN_CLEAR_VAL(val, packet_sel, shift) \
+ ((val & ~GENMASK(shift + CLKDOM_TRACE_PACKET_WIDTH, shift)))
+
+static struct cpufreq_hwregs *hw_regs;
+static char debug_buf[MAX_DEBUG_BUF_LEN];
+static const u16 *offsets;
+
+static const u16 cpufreq_qcom_std_data[REG_ARRAY_SIZE] = {
+ [GLOBAL_CTRL_OFFSET] = 0x10,
+ [CLKDOM_CTRL_OFFSET] = 0x14,
+ [PERIODIC_TIMER_CTRL_OFFSET] = 0x1C,
+ [REG_PERF_STATE] = 0x920,
+ [REG_CYCLE_CNTR] = 0x9c0,
+ [REG_PSTATE_STATUS] = 0x700,
+ [CPUFREQ_HW_TRACE_ENABLE_BIT] = 16,
+ [PERIODIC_TRACE_ENABLE_BIT] = 17,
+};
+
+static const u16 cpufreq_qcom_std_epss_data[REG_ARRAY_SIZE] = {
+ [REG_PERF_STATE] = 0x320,
+ [REG_CYCLE_CNTR] = 0x3c4,
+ [REG_PSTATE_STATUS] = 0x020,
+};
+
+static int clock_timer_set(void *data, u64 val)
+{
+ u32 base;
+ int ret;
+
+ if (!data)
+ return -EINVAL;
+
+ base = *((u32 *)data);
+
+ ret = scm_io_write(base + offsets[PERIODIC_TIMER_CTRL_OFFSET], val);
+ if (ret)
+ pr_err("failed(0x%x) to set clk timer\n", ret);
+
+ return ret;
+}
+
+static int clock_timer_get(void *data, u64 *val)
+{
+ u32 base;
+
+ if (!data)
+ return -EINVAL;
+
+ base = *((u32 *)data);
+
+ *val = scm_io_read(base + offsets[PERIODIC_TIMER_CTRL_OFFSET]);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(clock_timer_fops, clock_timer_get, clock_timer_set,
+ "%llu\n");
+
+static ssize_t trace_type_set(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 regval, base;
+ int ret;
+
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ if (!file->private_data)
+ return -EINVAL;
+
+ base = *((u32 *)file->private_data);
+
+ if (count < MAX_DEBUG_BUF_LEN) {
+ if (copy_from_user(debug_buf, (void __user *) buf,
+ MAX_DEBUG_BUF_LEN))
+ return -EFAULT;
+ }
+ debug_buf[count] = '\0';
+
+ regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ if (!strcmp(debug_buf, "periodic\n")) {
+ regval |= BIT(offsets[PERIODIC_TRACE_ENABLE_BIT]);
+ } else if (!strcmp(debug_buf, "xor\n")) {
+ regval &= ~BIT(offsets[PERIODIC_TRACE_ENABLE_BIT]);
+ } else {
+ pr_err("error, supported trace mode types: 'periodic' or 'xor'\n");
+ return -EINVAL;
+ }
+
+ ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], regval);
+ if (ret)
+ pr_err("failed(0x%x) to set cpufreq clk timer\n", ret);
+
+ return count;
+}
+
+static ssize_t trace_type_get(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int len;
+ u32 regval, base;
+
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ if (!file->private_data)
+ return -EINVAL;
+
+ base = *((u32 *)file->private_data);
+
+ regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ if (regval && offsets[PERIODIC_TRACE_ENABLE_BIT])
+ len = snprintf(debug_buf, sizeof(debug_buf), "periodic\n");
+ else
+ len = snprintf(debug_buf, sizeof(debug_buf), "xor\n");
+
+ return simple_read_from_buffer((void __user *) buf, count, ppos,
+ (void *) debug_buf, len);
+}
+
+static int trace_type_open(struct inode *inode, struct file *file)
+{
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations clk_trace_type_fops = {
+ .read = trace_type_get,
+ .open = trace_type_open,
+ .write = trace_type_set,
+};
+
+void __domain_packet_set(u32 *clkdom_regval, u32 *pktsel_regval, int clkdomain,
+ int pktsel, int packet_sel_shift, int enable)
+{
+ if (!!enable) {
+ *clkdom_regval |= BIT(clkdomain);
+ *pktsel_regval = CLKDOMAIN_SET_VAL(*pktsel_regval, pktsel,
+ packet_sel_shift);
+ } else {
+ *clkdom_regval &= ~BIT(clkdomain);
+ *pktsel_regval &= CLKDOMAIN_CLEAR_VAL(*pktsel_regval, pktsel,
+ packet_sel_shift);
+ }
+}
+
+static ssize_t domain_packet_set(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ unsigned int filled, clk_domain, packet_sel, enable = 1;
+ u32 clkdom_regval, pktsel_regval, base;
+ char buf[MAX_DEBUG_BUF_LEN];
+ int ret;
+
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ if (!file->private_data)
+ return -EINVAL;
+
+ base = *((u32 *)file->private_data);
+
+ if (count < MAX_DEBUG_BUF_LEN) {
+ if (copy_from_user(buf, ubuf, count))
+ return -EFAULT;
+ }
+
+ buf[count] = '\0';
+ filled = sscanf(buf, "%u %u %u", &clk_domain, &packet_sel, &enable);
+ if (clk_domain > CLK_DOMAIN_SIZE || packet_sel > MAX_PKT_SIZE) {
+ pr_err("Clock domain and source selection not in range\n");
+ return -EINVAL;
+ }
+
+ clkdom_regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ pktsel_regval = scm_io_read(base + offsets[CLKDOM_CTRL_OFFSET]);
+
+
+ switch (clk_domain) {
+ case CLKDOM0:
+ __domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM0,
+ packet_sel, CLKDOM0_TRACE_PACKET_SHIFT, enable);
+ break;
+ case CLKDOM1:
+ __domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM1,
+ packet_sel, CLKDOM1_TRACE_PACKET_SHIFT, enable);
+ break;
+ case CLKDOM2:
+ __domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM2,
+ packet_sel, CLKDOM2_TRACE_PACKET_SHIFT, enable);
+ break;
+ case CLKDOM3:
+ __domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM3,
+ packet_sel, CLKDOM3_TRACE_PACKET_SHIFT, enable);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], clkdom_regval);
+ if (ret)
+ pr_err("failed(0x%x) to set cpufreq domain\n", ret);
+
+ ret = scm_io_write(base + offsets[CLKDOM_CTRL_OFFSET], pktsel_regval);
+ if (ret)
+ pr_err("failed(0x%x) to set cpufreq trace packet\n", ret);
+
+ return count;
+}
+
+static ssize_t domain_packet_get(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 base, clkdom_regval, pktsel_regval;
+ int len;
+
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ if (!file->private_data)
+ return -EINVAL;
+
+ base = *((u32 *)file->private_data);
+
+ clkdom_regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ pktsel_regval = scm_io_read(base + offsets[CLKDOM_CTRL_OFFSET]);
+
+ len = snprintf(debug_buf, sizeof(debug_buf),
+ "GLOBAL_TRACE_CTRL = 0x%x\nCLKDOM_TRACE_CTRL = 0x%x\n",
+ clkdom_regval, pktsel_regval);
+
+ return simple_read_from_buffer((void __user *) buf, count, ppos,
+ (void *) debug_buf, len);
+}
+
+static int domain_packet_open(struct inode *inode, struct file *file)
+{
+ if (IS_ERR(file) || file == NULL) {
+ pr_err("input error %ld\n", PTR_ERR(file));
+ return -EINVAL;
+ }
+
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations clock_domian_packet_set = {
+ .write = domain_packet_set,
+ .open = domain_packet_open,
+ .read = domain_packet_get,
+};
+
+static int cpufreq_hw_trace_enable_set(void *data, u64 val)
+{
+ u32 regval, base;
+ int ret;
+
+ if (!data)
+ return -EINVAL;
+
+ base = *((u32 *)data);
+
+ regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ if (!!val)
+ regval |= BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT]);
+ else
+ regval &= ~BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT]);
+
+ ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], regval);
+ if (ret)
+ pr_err("failed(0x%x) to set cpufreq hw trace\n", ret);
+
+ return ret;
+}
+
+static int cpufreq_hw_trace_enable_get(void *data, u64 *val)
+{
+ u32 regval, base;
+
+ if (!data)
+ return -EINVAL;
+
+ base = *((u32 *)data);
+
+ regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
+ *val = (regval & BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT])) >>
+ offsets[CPUFREQ_HW_TRACE_ENABLE_BIT];
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(clock_trace_enable, cpufreq_hw_trace_enable_get,
+ cpufreq_hw_trace_enable_set, "%llu\n");
+
+static int print_cpufreq_hw_trace_regs(struct seq_file *s, void *unused)
+{
+ u32 base;
+ int i;
+
+ static struct cpufreq_register_data data[] = {
+ {"GLOBAL_TRACE_CTRL", GLOBAL_CTRL_OFFSET},
+ {"CLKDOM_TRACE_CTRL", CLKDOM_CTRL_OFFSET},
+ {"PERIODIC_TIMER_TIMER", PERIODIC_TIMER_CTRL_OFFSET},
+ };
+
+ if (!s->private)
+ return -EINVAL;
+
+ base = *((u32 *)s->private);
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ seq_printf(s, "%25s: 0x%.8x\n", data[i].name,
+ scm_io_read(base + offsets[data[i].offset]));
+ }
+
+ return 0;
+}
+
+static int print_trace_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, print_cpufreq_hw_trace_regs, inode->i_private);
+}
+
+static const struct file_operations cpufreq_trace_register_fops = {
+ .open = print_trace_reg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int print_cpufreq_hw_debug_regs(struct seq_file *s, void *unused)
+{
+ int i, j;
+ u32 regval;
+
+ static struct cpufreq_register_data data[] = {
+ {"PERF_STATE_DESIRED", REG_PERF_STATE},
+ {"CYCLE_CNTR_VAL", REG_CYCLE_CNTR},
+ {"PSTATE_STATUS", REG_PSTATE_STATUS},
+ };
+
+ for (i = 0; i < hw_regs->domain_cnt; i++) {
+ seq_printf(s, "FREQUENCY DOMAIN %d\n", i);
+ for (j = 0; j < ARRAY_SIZE(data); j++) {
+ regval = readl_relaxed(hw_regs->base[i] +
+ offsets[data[j].offset]);
+ seq_printf(s, "%25s: 0x%.8x\n", data[j].name, regval);
+ }
+ }
+
+ return 0;
+}
+
+static int print_cpufreq_hw_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, print_cpufreq_hw_debug_regs, NULL);
+}
+
+static const struct file_operations cpufreq_debug_register_fops = {
+ .open = print_cpufreq_hw_reg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int cpufreq_panic_callback(struct notifier_block *nfb,
+ unsigned long event, void *unused)
+{
+ int i, j;
+ u32 regval;
+
+ static struct cpufreq_register_data data[] = {
+ {"PERF_STATE_DESIRED", REG_PERF_STATE},
+ {"CYCLE_CNTR_VAL", REG_CYCLE_CNTR},
+ {"PSTATE_STATUS", REG_PSTATE_STATUS},
+ };
+
+ for (i = 0; i < hw_regs->domain_cnt; i++) {
+ pr_err("FREQUENCY DOMAIN %d\n", i);
+ for (j = 0; j < ARRAY_SIZE(data); j++) {
+ regval = readl_relaxed(hw_regs->base[i] +
+ offsets[data[j].offset]);
+ pr_err("%25s: 0x%.8x\n", data[j].name, regval);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_panic_notifier = {
+ .notifier_call = cpufreq_panic_callback,
+ .priority = 1,
+};
+
+static int cpufreq_get_hwregs(struct platform_device *pdev)
+{
+ struct of_phandle_args args;
+ struct property *prop;
+ struct resource res;
+ void __iomem *base;
+ int i, ret;
+
+ offsets = of_device_get_match_data(&pdev->dev);
+ if (!offsets)
+ return -EINVAL;
+
+ hw_regs = devm_kzalloc(&pdev->dev, sizeof(*hw_regs), GFP_KERNEL);
+ if (!hw_regs)
+ return -ENOMEM;
+
+ prop = of_find_property(pdev->dev.of_node, "qcom,freq-hw-domain", NULL);
+ hw_regs->domain_cnt = prop->length / (2 * sizeof(prop->length));
+
+ for (i = 0; i < hw_regs->domain_cnt; i++) {
+ ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+ "qcom,freq-hw-domain", 1, i, &args);
+ of_node_put(pdev->dev.of_node);
+ if (ret)
+ return ret;
+
+ ret = of_address_to_resource(args.np, args.args[0], &res);
+ if (ret)
+ return ret;
+
+ base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
+ if (!base)
+ return -ENOMEM;
+
+ hw_regs->base[i] = base;
+ }
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &cpufreq_panic_notifier);
+
+ return 0;
+}
+
+static int enable_cpufreq_hw_trace_debug(struct platform_device *pdev,
+ bool is_secure)
+{
+ struct resource *res;
+ void *base;
+ int ret;
+
+ ret = cpufreq_get_hwregs(pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to map cpufreq hw regs\n");
+ return ret;
+ }
+
+ hw_regs->debugfs_base = debugfs_create_dir("qcom-cpufreq-hw", NULL);
+ if (!hw_regs->debugfs_base) {
+ dev_err(&pdev->dev, "Failed to create debugfs entry\n");
+ return -ENODEV;
+ }
+
+ if (!debugfs_create_file("print_cpufreq_debug_regs", 0444,
+ hw_regs->debugfs_base, NULL, &cpufreq_debug_register_fops))
+ goto debugfs_fail;
+
+ if (!is_secure || of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cpufreq-hw-epss-debug"))
+ return 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "domain-top");
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get domain-top register base\n");
+ return 0;
+ }
+
+ base = &(res->start);
+
+ if (!debugfs_create_file("print_cpufreq_trace_regs", 0444,
+ hw_regs->debugfs_base, base, &cpufreq_trace_register_fops))
+ goto debugfs_fail;
+
+ if (!debugfs_create_file("clock_domain_packet_sel", 0444,
+ hw_regs->debugfs_base, base, &clock_domian_packet_set))
+ goto debugfs_fail;
+
+ if (!debugfs_create_file("trace_enable", 0444, hw_regs->debugfs_base,
+ base, &clock_trace_enable))
+ goto debugfs_fail;
+
+ if (!debugfs_create_file("clock_timer", 0444,
+ hw_regs->debugfs_base, base, &clock_timer_fops))
+ goto debugfs_fail;
+
+ if (!debugfs_create_file("trace_type", 0444,
+ hw_regs->debugfs_base, base, &clk_trace_type_fops))
+ goto debugfs_fail;
+
+ return 0;
+
+debugfs_fail:
+ dev_err(&pdev->dev, "Failed to create debugfs entry so cleaning up\n");
+ debugfs_remove_recursive(hw_regs->debugfs_base);
+ return -ENODEV;
+}
+
+static int qcom_cpufreq_hw_debug_probe(struct platform_device *pdev)
+{
+ bool is_secure = scm_is_secure_device();
+
+ return enable_cpufreq_hw_trace_debug(pdev, is_secure);
+}
+
+static int qcom_cpufreq_hw_debug_remove(struct platform_device *pdev)
+{
+ debugfs_remove_recursive(hw_regs->debugfs_base);
+ return 0;
+}
+
+static const struct of_device_id qcom_cpufreq_hw_debug_trace_match[] = {
+ { .compatible = "qcom,cpufreq-hw-debug-trace",
+ .data = &cpufreq_qcom_std_data },
+ { .compatible = "qcom,cpufreq-hw-epss-debug",
+ .data = &cpufreq_qcom_std_epss_data },
+ {}
+};
+
+static struct platform_driver qcom_cpufreq_hw_debug = {
+ .probe = qcom_cpufreq_hw_debug_probe,
+ .remove = qcom_cpufreq_hw_debug_remove,
+ .driver = {
+ .name = "qcom-cpufreq-hw-debug",
+ .of_match_table = qcom_cpufreq_hw_debug_trace_match,
+ },
+};
+
+static int __init qcom_cpufreq_hw_debug_trace_init(void)
+{
+ return platform_driver_register(&qcom_cpufreq_hw_debug);
+}
+fs_initcall(qcom_cpufreq_hw_debug_trace_init);
+
+static void __exit qcom_cpufreq_hw_debug_trace_exit(void)
+{
+ return platform_driver_unregister(&qcom_cpufreq_hw_debug);
+}
+module_exit(qcom_cpufreq_hw_debug_trace_exit);
+
+MODULE_DESCRIPTION("QTI clock driver for CPUFREQ HW debug");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index dc32f34..01acd88 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -62,25 +62,24 @@ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
* __cpuidle_set_driver - set per CPU driver variables for the given driver.
* @drv: a valid pointer to a struct cpuidle_driver
*
- * For each CPU in the driver's cpumask, unset the registered driver per CPU
- * to @drv.
- *
- * Returns 0 on success, -EBUSY if the CPUs have driver(s) already.
+ * Returns 0 on success, -EBUSY if any CPU in the cpumask have a driver
+ * different from drv already.
*/
static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
{
int cpu;
for_each_cpu(cpu, drv->cpumask) {
+ struct cpuidle_driver *old_drv;
- if (__cpuidle_get_cpu_driver(cpu)) {
- __cpuidle_unset_driver(drv);
+ old_drv = __cpuidle_get_cpu_driver(cpu);
+ if (old_drv && old_drv != drv)
return -EBUSY;
- }
-
- per_cpu(cpuidle_drivers, cpu) = drv;
}
+ for_each_cpu(cpu, drv->cpumask)
+ per_cpu(cpuidle_drivers, cpu) = drv;
+
return 0;
}
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 470ff59..f21fcad 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -513,6 +513,16 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* duration predictor do a better job next time.
*/
measured_us = 9 * MAX_INTERESTING / 10;
+ } else if ((drv->states[last_idx].flags & CPUIDLE_FLAG_POLLING) &&
+ dev->poll_time_limit) {
+ /*
+ * The CPU exited the "polling" state due to a time limit, so
+ * the idle duration prediction leading to the selection of that
+ * state was inaccurate. If a better prediction had been made,
+ * the CPU might have been woken up from idle by the next timer.
+ * Assume that to be the case.
+ */
+ measured_us = data->next_timer_us;
} else {
/* measured value */
measured_us = cpuidle_get_last_residency(dev);
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index ddaa6b72..6f21e13 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1842,6 +1842,7 @@ static int lpm_probe(struct platform_device *pdev)
md_entry.virt_addr = (uintptr_t)lpm_debug;
md_entry.phys_addr = lpm_debug_phys;
md_entry.size = size;
+ md_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&md_entry))
pr_info("Failed to add lpm_debug in Minidump\n");
diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c
index 3f86d23..36ff5a1 100644
--- a/drivers/cpuidle/poll_state.c
+++ b/drivers/cpuidle/poll_state.c
@@ -17,6 +17,8 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev,
{
u64 time_start = local_clock();
+ dev->poll_time_limit = false;
+
local_irq_enable();
if (!current_set_polling_and_test()) {
unsigned int loop_count = 0;
@@ -27,8 +29,10 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev,
continue;
loop_count = 0;
- if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT)
+ if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT) {
+ dev->poll_time_limit = true;
break;
+ }
}
}
current_clr_polling();
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 6386e17..68d5ea8 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -373,12 +373,8 @@ static u32 crypto4xx_build_sdr(struct crypto4xx_device *dev)
dma_alloc_coherent(dev->core_dev->device,
PPC4XX_SD_BUFFER_SIZE * PPC4XX_NUM_SD,
&dev->scatter_buffer_pa, GFP_ATOMIC);
- if (!dev->scatter_buffer_va) {
- dma_free_coherent(dev->core_dev->device,
- sizeof(struct ce_sd) * PPC4XX_NUM_SD,
- dev->sdr, dev->sdr_pa);
+ if (!dev->scatter_buffer_va)
return -ENOMEM;
- }
for (i = 0; i < PPC4XX_NUM_SD; i++) {
dev->sdr[i].ptr = dev->scatter_buffer_pa +
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index 801aeab..53a7803 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -148,7 +148,7 @@ struct atmel_aes_xts_ctx {
u32 key2[AES_KEYSIZE_256 / sizeof(u32)];
};
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
struct atmel_aes_authenc_ctx {
struct atmel_aes_base_ctx base;
struct atmel_sha_authenc_ctx *auth;
@@ -160,7 +160,7 @@ struct atmel_aes_reqctx {
u32 lastc[AES_BLOCK_SIZE / sizeof(u32)];
};
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
struct atmel_aes_authenc_reqctx {
struct atmel_aes_reqctx base;
@@ -489,13 +489,36 @@ static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd)
return (dd->flags & AES_FLAGS_ENCRYPT);
}
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err);
#endif
+static void atmel_aes_set_iv_as_last_ciphertext_block(struct atmel_aes_dev *dd)
+{
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ unsigned int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ if (req->nbytes < ivsize)
+ return;
+
+ if (rctx->mode & AES_FLAGS_ENCRYPT) {
+ scatterwalk_map_and_copy(req->info, req->dst,
+ req->nbytes - ivsize, ivsize, 0);
+ } else {
+ if (req->src == req->dst)
+ memcpy(req->info, rctx->lastc, ivsize);
+ else
+ scatterwalk_map_and_copy(req->info, req->src,
+ req->nbytes - ivsize,
+ ivsize, 0);
+ }
+}
+
static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
{
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
if (dd->ctx->is_aead)
atmel_aes_authenc_complete(dd, err);
#endif
@@ -503,26 +526,8 @@ static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
clk_disable(dd->iclk);
dd->flags &= ~AES_FLAGS_BUSY;
- if (!dd->ctx->is_aead) {
- struct ablkcipher_request *req =
- ablkcipher_request_cast(dd->areq);
- struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
- struct crypto_ablkcipher *ablkcipher =
- crypto_ablkcipher_reqtfm(req);
- int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
-
- if (rctx->mode & AES_FLAGS_ENCRYPT) {
- scatterwalk_map_and_copy(req->info, req->dst,
- req->nbytes - ivsize, ivsize, 0);
- } else {
- if (req->src == req->dst) {
- memcpy(req->info, rctx->lastc, ivsize);
- } else {
- scatterwalk_map_and_copy(req->info, req->src,
- req->nbytes - ivsize, ivsize, 0);
- }
- }
- }
+ if (!dd->ctx->is_aead)
+ atmel_aes_set_iv_as_last_ciphertext_block(dd);
if (dd->is_async)
dd->areq->complete(dd->areq, err);
@@ -1128,10 +1133,12 @@ static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
rctx->mode = mode;
if (!(mode & AES_FLAGS_ENCRYPT) && (req->src == req->dst)) {
- int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ unsigned int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
- scatterwalk_map_and_copy(rctx->lastc, req->src,
- (req->nbytes - ivsize), ivsize, 0);
+ if (req->nbytes >= ivsize)
+ scatterwalk_map_and_copy(rctx->lastc, req->src,
+ req->nbytes - ivsize,
+ ivsize, 0);
}
return atmel_aes_handle_queue(dd, &req->base);
@@ -1976,7 +1983,7 @@ static struct crypto_alg aes_xts_alg = {
}
};
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
/* authenc aead functions */
static int atmel_aes_authenc_start(struct atmel_aes_dev *dd);
@@ -2463,7 +2470,7 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd)
{
int i;
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
if (dd->caps.has_authenc)
for (i = 0; i < ARRAY_SIZE(aes_authenc_algs); i++)
crypto_unregister_aead(&aes_authenc_algs[i]);
@@ -2510,7 +2517,7 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
goto err_aes_xts_alg;
}
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
if (dd->caps.has_authenc) {
for (i = 0; i < ARRAY_SIZE(aes_authenc_algs); i++) {
err = crypto_register_aead(&aes_authenc_algs[i]);
@@ -2522,7 +2529,7 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
return 0;
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
/* i = ARRAY_SIZE(aes_authenc_algs); */
err_aes_authenc_alg:
for (j = 0; j < i; j++)
@@ -2713,7 +2720,7 @@ static int atmel_aes_probe(struct platform_device *pdev)
atmel_aes_get_cap(aes_dd);
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
if (aes_dd->caps.has_authenc && !atmel_sha_authenc_is_ready()) {
err = -EPROBE_DEFER;
goto iclk_unprepare;
diff --git a/drivers/crypto/atmel-authenc.h b/drivers/crypto/atmel-authenc.h
index 2a60d12..7f6742d 100644
--- a/drivers/crypto/atmel-authenc.h
+++ b/drivers/crypto/atmel-authenc.h
@@ -23,7 +23,7 @@
#ifndef __ATMEL_AUTHENC_H__
#define __ATMEL_AUTHENC_H__
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
#include <crypto/authenc.h>
#include <crypto/hash.h>
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 8a19df2..ef125d4 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -2215,7 +2215,7 @@ static struct ahash_alg sha_hmac_algs[] = {
},
};
-#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC)
/* authenc functions */
static int atmel_sha_authenc_init2(struct atmel_sha_dev *dd);
diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c
index cd46463..49c0097 100644
--- a/drivers/crypto/bcm/cipher.c
+++ b/drivers/crypto/bcm/cipher.c
@@ -4634,12 +4634,16 @@ static int spu_register_ahash(struct iproc_alg_s *driver_alg)
hash->halg.statesize = sizeof(struct spu_hash_export_s);
if (driver_alg->auth_info.mode != HASH_MODE_HMAC) {
- hash->setkey = ahash_setkey;
hash->init = ahash_init;
hash->update = ahash_update;
hash->final = ahash_final;
hash->finup = ahash_finup;
hash->digest = ahash_digest;
+ if ((driver_alg->auth_info.alg == HASH_ALG_AES) &&
+ ((driver_alg->auth_info.mode == HASH_MODE_XCBC) ||
+ (driver_alg->auth_info.mode == HASH_MODE_CMAC))) {
+ hash->setkey = ahash_setkey;
+ }
} else {
hash->setkey = ahash_hmac_setkey;
hash->init = ahash_hmac_init;
diff --git a/drivers/crypto/ccp/ccp-dmaengine.c b/drivers/crypto/ccp/ccp-dmaengine.c
index 67155cb..a83588d 100644
--- a/drivers/crypto/ccp/ccp-dmaengine.c
+++ b/drivers/crypto/ccp/ccp-dmaengine.c
@@ -340,6 +340,7 @@ static struct ccp_dma_desc *ccp_alloc_dma_desc(struct ccp_dma_chan *chan,
desc->tx_desc.flags = flags;
desc->tx_desc.tx_submit = ccp_tx_submit;
desc->ccp = chan->ccp;
+ INIT_LIST_HEAD(&desc->entry);
INIT_LIST_HEAD(&desc->pending);
INIT_LIST_HEAD(&desc->active);
desc->status = DMA_IN_PROGRESS;
diff --git a/drivers/crypto/ccree/cc_hw_queue_defs.h b/drivers/crypto/ccree/cc_hw_queue_defs.h
index a091ae5..45985b9 100644
--- a/drivers/crypto/ccree/cc_hw_queue_defs.h
+++ b/drivers/crypto/ccree/cc_hw_queue_defs.h
@@ -449,8 +449,7 @@ static inline void set_flow_mode(struct cc_hw_desc *pdesc,
* @pdesc: pointer HW descriptor struct
* @mode: Any one of the modes defined in [CC7x-DESC]
*/
-static inline void set_cipher_mode(struct cc_hw_desc *pdesc,
- enum drv_cipher_mode mode)
+static inline void set_cipher_mode(struct cc_hw_desc *pdesc, int mode)
{
pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_MODE, mode);
}
@@ -461,8 +460,7 @@ static inline void set_cipher_mode(struct cc_hw_desc *pdesc,
* @pdesc: pointer HW descriptor struct
* @mode: Any one of the modes defined in [CC7x-DESC]
*/
-static inline void set_cipher_config0(struct cc_hw_desc *pdesc,
- enum drv_crypto_direction mode)
+static inline void set_cipher_config0(struct cc_hw_desc *pdesc, int mode)
{
pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_CONF0, mode);
}
diff --git a/drivers/crypto/chelsio/chtls/chtls.h b/drivers/crypto/chelsio/chtls/chtls.h
index 7725b6e..fcb6747 100644
--- a/drivers/crypto/chelsio/chtls/chtls.h
+++ b/drivers/crypto/chelsio/chtls/chtls.h
@@ -153,6 +153,11 @@ struct chtls_dev {
unsigned int cdev_state;
};
+struct chtls_listen {
+ struct chtls_dev *cdev;
+ struct sock *sk;
+};
+
struct chtls_hws {
struct sk_buff_head sk_recv_queue;
u8 txqid;
diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.c b/drivers/crypto/chelsio/chtls/chtls_cm.c
index 0997e16..8b749c7 100644
--- a/drivers/crypto/chelsio/chtls/chtls_cm.c
+++ b/drivers/crypto/chelsio/chtls/chtls_cm.c
@@ -1276,7 +1276,7 @@ static void make_established(struct sock *sk, u32 snd_isn, unsigned int opt)
tp->write_seq = snd_isn;
tp->snd_nxt = snd_isn;
tp->snd_una = snd_isn;
- inet_sk(sk)->inet_id = tp->write_seq ^ jiffies;
+ inet_sk(sk)->inet_id = prandom_u32();
assign_rxopt(sk, opt);
if (tp->rcv_wnd > (RCV_BUFSIZ_M << 10))
diff --git a/drivers/crypto/chelsio/chtls/chtls_io.c b/drivers/crypto/chelsio/chtls/chtls_io.c
index afebbd8..1587f4a 100644
--- a/drivers/crypto/chelsio/chtls/chtls_io.c
+++ b/drivers/crypto/chelsio/chtls/chtls_io.c
@@ -1716,7 +1716,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
return peekmsg(sk, msg, len, nonblock, flags);
if (sk_can_busy_loop(sk) &&
- skb_queue_empty(&sk->sk_receive_queue) &&
+ skb_queue_empty_lockless(&sk->sk_receive_queue) &&
sk->sk_state == TCP_ESTABLISHED)
sk_busy_loop(sk, nonblock);
diff --git a/drivers/crypto/chelsio/chtls/chtls_main.c b/drivers/crypto/chelsio/chtls/chtls_main.c
index f59b044..2bf084a 100644
--- a/drivers/crypto/chelsio/chtls/chtls_main.c
+++ b/drivers/crypto/chelsio/chtls/chtls_main.c
@@ -55,24 +55,19 @@ static void unregister_listen_notifier(struct notifier_block *nb)
static int listen_notify_handler(struct notifier_block *this,
unsigned long event, void *data)
{
- struct chtls_dev *cdev;
- struct sock *sk;
- int ret;
+ struct chtls_listen *clisten;
+ int ret = NOTIFY_DONE;
- sk = data;
- ret = NOTIFY_DONE;
+ clisten = (struct chtls_listen *)data;
switch (event) {
case CHTLS_LISTEN_START:
+ ret = chtls_listen_start(clisten->cdev, clisten->sk);
+ kfree(clisten);
+ break;
case CHTLS_LISTEN_STOP:
- mutex_lock(&cdev_list_lock);
- list_for_each_entry(cdev, &cdev_list, list) {
- if (event == CHTLS_LISTEN_START)
- ret = chtls_listen_start(cdev, sk);
- else
- chtls_listen_stop(cdev, sk);
- }
- mutex_unlock(&cdev_list_lock);
+ chtls_listen_stop(clisten->cdev, clisten->sk);
+ kfree(clisten);
break;
}
return ret;
@@ -90,8 +85,9 @@ static int listen_backlog_rcv(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static int chtls_start_listen(struct sock *sk)
+static int chtls_start_listen(struct chtls_dev *cdev, struct sock *sk)
{
+ struct chtls_listen *clisten;
int err;
if (sk->sk_protocol != IPPROTO_TCP)
@@ -102,21 +98,33 @@ static int chtls_start_listen(struct sock *sk)
return -EADDRNOTAVAIL;
sk->sk_backlog_rcv = listen_backlog_rcv;
+ clisten = kmalloc(sizeof(*clisten), GFP_KERNEL);
+ if (!clisten)
+ return -ENOMEM;
+ clisten->cdev = cdev;
+ clisten->sk = sk;
mutex_lock(¬ify_mutex);
err = raw_notifier_call_chain(&listen_notify_list,
- CHTLS_LISTEN_START, sk);
+ CHTLS_LISTEN_START, clisten);
mutex_unlock(¬ify_mutex);
return err;
}
-static void chtls_stop_listen(struct sock *sk)
+static void chtls_stop_listen(struct chtls_dev *cdev, struct sock *sk)
{
+ struct chtls_listen *clisten;
+
if (sk->sk_protocol != IPPROTO_TCP)
return;
+ clisten = kmalloc(sizeof(*clisten), GFP_KERNEL);
+ if (!clisten)
+ return;
+ clisten->cdev = cdev;
+ clisten->sk = sk;
mutex_lock(¬ify_mutex);
raw_notifier_call_chain(&listen_notify_list,
- CHTLS_LISTEN_STOP, sk);
+ CHTLS_LISTEN_STOP, clisten);
mutex_unlock(¬ify_mutex);
}
@@ -138,15 +146,19 @@ static int chtls_inline_feature(struct tls_device *dev)
static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
{
+ struct chtls_dev *cdev = to_chtls_dev(dev);
+
if (sk->sk_state == TCP_LISTEN)
- return chtls_start_listen(sk);
+ return chtls_start_listen(cdev, sk);
return 0;
}
static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
{
+ struct chtls_dev *cdev = to_chtls_dev(dev);
+
if (sk->sk_state == TCP_LISTEN)
- chtls_stop_listen(sk);
+ chtls_stop_listen(cdev, sk);
}
static void chtls_register_dev(struct chtls_dev *cdev)
diff --git a/drivers/crypto/mxc-scc.c b/drivers/crypto/mxc-scc.c
index e01c463..5190867 100644
--- a/drivers/crypto/mxc-scc.c
+++ b/drivers/crypto/mxc-scc.c
@@ -178,12 +178,12 @@ static int mxc_scc_get_data(struct mxc_scc_ctx *ctx,
else
from = scc->black_memory;
- dev_dbg(scc->dev, "pcopy: from 0x%p %d bytes\n", from,
+ dev_dbg(scc->dev, "pcopy: from 0x%p %zu bytes\n", from,
ctx->dst_nents * 8);
len = sg_pcopy_from_buffer(ablkreq->dst, ctx->dst_nents,
from, ctx->size, ctx->offset);
if (!len) {
- dev_err(scc->dev, "pcopy err from 0x%p (len=%d)\n", from, len);
+ dev_err(scc->dev, "pcopy err from 0x%p (len=%zu)\n", from, len);
return -EINVAL;
}
@@ -274,7 +274,7 @@ static int mxc_scc_put_data(struct mxc_scc_ctx *ctx,
len = sg_pcopy_to_buffer(req->src, ctx->src_nents,
to, len, ctx->offset);
if (!len) {
- dev_err(scc->dev, "pcopy err to 0x%p (len=%d)\n", to, len);
+ dev_err(scc->dev, "pcopy err to 0x%p (len=%zu)\n", to, len);
return -EINVAL;
}
@@ -335,9 +335,9 @@ static void mxc_scc_ablkcipher_next(struct mxc_scc_ctx *ctx,
return;
}
- dev_dbg(scc->dev, "Start encryption (0x%p/0x%p)\n",
- (void *)readl(scc->base + SCC_SCM_RED_START),
- (void *)readl(scc->base + SCC_SCM_BLACK_START));
+ dev_dbg(scc->dev, "Start encryption (0x%x/0x%x)\n",
+ readl(scc->base + SCC_SCM_RED_START),
+ readl(scc->base + SCC_SCM_BLACK_START));
/* clear interrupt control registers */
writel(SCC_SCM_INTR_CTRL_CLR_INTR,
diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index 56bd281..b926098 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -28,9 +28,24 @@
#define DCP_MAX_CHANS 4
#define DCP_BUF_SZ PAGE_SIZE
+#define DCP_SHA_PAY_SZ 64
#define DCP_ALIGNMENT 64
+/*
+ * Null hashes to align with hw behavior on imx6sl and ull
+ * these are flipped for consistency with hw output
+ */
+const uint8_t sha1_null_hash[] =
+ "\x09\x07\xd8\xaf\x90\x18\x60\x95\xef\xbf"
+ "\x55\x32\x0d\x4b\x6b\x5e\xee\xa3\x39\xda";
+
+const uint8_t sha256_null_hash[] =
+ "\x55\xb8\x52\x78\x1b\x99\x95\xa4"
+ "\x4c\x93\x9b\x64\xe4\x41\xae\x27"
+ "\x24\xb9\x6f\x99\xc8\xf4\xfb\x9a"
+ "\x14\x1c\xfc\x98\x42\xc4\xb0\xe3";
+
/* DCP DMA descriptor. */
struct dcp_dma_desc {
uint32_t next_cmd_addr;
@@ -48,6 +63,7 @@ struct dcp_coherent_block {
uint8_t aes_in_buf[DCP_BUF_SZ];
uint8_t aes_out_buf[DCP_BUF_SZ];
uint8_t sha_in_buf[DCP_BUF_SZ];
+ uint8_t sha_out_buf[DCP_SHA_PAY_SZ];
uint8_t aes_key[2 * AES_KEYSIZE_128];
@@ -209,6 +225,12 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
dma_addr_t dst_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_out_buf,
DCP_BUF_SZ, DMA_FROM_DEVICE);
+ if (actx->fill % AES_BLOCK_SIZE) {
+ dev_err(sdcp->dev, "Invalid block size!\n");
+ ret = -EINVAL;
+ goto aes_done_run;
+ }
+
/* Fill in the DMA descriptor. */
desc->control0 = MXS_DCP_CONTROL0_DECR_SEMAPHORE |
MXS_DCP_CONTROL0_INTERRUPT |
@@ -238,6 +260,7 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
ret = mxs_dcp_start_dma(actx);
+aes_done_run:
dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
DMA_TO_DEVICE);
dma_unmap_single(sdcp->dev, src_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
@@ -264,13 +287,15 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
uint8_t *out_tmp, *src_buf, *dst_buf = NULL;
uint32_t dst_off = 0;
+ uint32_t last_out_len = 0;
uint8_t *key = sdcp->coh->aes_key;
int ret = 0;
int split = 0;
- unsigned int i, len, clen, rem = 0;
+ unsigned int i, len, clen, rem = 0, tlen = 0;
int init = 0;
+ bool limit_hit = false;
actx->fill = 0;
@@ -289,6 +314,11 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
for_each_sg(req->src, src, nents, i) {
src_buf = sg_virt(src);
len = sg_dma_len(src);
+ tlen += len;
+ limit_hit = tlen > req->nbytes;
+
+ if (limit_hit)
+ len = req->nbytes - (tlen - len);
do {
if (actx->fill + len > out_off)
@@ -305,13 +335,15 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
* If we filled the buffer or this is the last SG,
* submit the buffer.
*/
- if (actx->fill == out_off || sg_is_last(src)) {
+ if (actx->fill == out_off || sg_is_last(src) ||
+ limit_hit) {
ret = mxs_dcp_run_aes(actx, req, init);
if (ret)
return ret;
init = 0;
out_tmp = out_buf;
+ last_out_len = actx->fill;
while (dst && actx->fill) {
if (!split) {
dst_buf = sg_virt(dst);
@@ -334,6 +366,19 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
}
}
} while (len);
+
+ if (limit_hit)
+ break;
+ }
+
+ /* Copy the IV for CBC for chaining */
+ if (!rctx->ecb) {
+ if (rctx->enc)
+ memcpy(req->info, out_buf+(last_out_len-AES_BLOCK_SIZE),
+ AES_BLOCK_SIZE);
+ else
+ memcpy(req->info, in_buf+(last_out_len-AES_BLOCK_SIZE),
+ AES_BLOCK_SIZE);
}
return ret;
@@ -513,8 +558,6 @@ static int mxs_dcp_run_sha(struct ahash_request *req)
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct dcp_async_ctx *actx = crypto_ahash_ctx(tfm);
struct dcp_sha_req_ctx *rctx = ahash_request_ctx(req);
- struct hash_alg_common *halg = crypto_hash_alg_common(tfm);
-
struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
dma_addr_t digest_phys = 0;
@@ -536,10 +579,23 @@ static int mxs_dcp_run_sha(struct ahash_request *req)
desc->payload = 0;
desc->status = 0;
+ /*
+ * Align driver with hw behavior when generating null hashes
+ */
+ if (rctx->init && rctx->fini && desc->size == 0) {
+ struct hash_alg_common *halg = crypto_hash_alg_common(tfm);
+ const uint8_t *sha_buf =
+ (actx->alg == MXS_DCP_CONTROL1_HASH_SELECT_SHA1) ?
+ sha1_null_hash : sha256_null_hash;
+ memcpy(sdcp->coh->sha_out_buf, sha_buf, halg->digestsize);
+ ret = 0;
+ goto done_run;
+ }
+
/* Set HASH_TERM bit for last transfer block. */
if (rctx->fini) {
- digest_phys = dma_map_single(sdcp->dev, req->result,
- halg->digestsize, DMA_FROM_DEVICE);
+ digest_phys = dma_map_single(sdcp->dev, sdcp->coh->sha_out_buf,
+ DCP_SHA_PAY_SZ, DMA_FROM_DEVICE);
desc->control0 |= MXS_DCP_CONTROL0_HASH_TERM;
desc->payload = digest_phys;
}
@@ -547,9 +603,10 @@ static int mxs_dcp_run_sha(struct ahash_request *req)
ret = mxs_dcp_start_dma(actx);
if (rctx->fini)
- dma_unmap_single(sdcp->dev, digest_phys, halg->digestsize,
+ dma_unmap_single(sdcp->dev, digest_phys, DCP_SHA_PAY_SZ,
DMA_FROM_DEVICE);
+done_run:
dma_unmap_single(sdcp->dev, buf_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
return ret;
@@ -567,6 +624,7 @@ static int dcp_sha_req_to_buf(struct crypto_async_request *arq)
const int nents = sg_nents(req->src);
uint8_t *in_buf = sdcp->coh->sha_in_buf;
+ uint8_t *out_buf = sdcp->coh->sha_out_buf;
uint8_t *src_buf;
@@ -621,11 +679,9 @@ static int dcp_sha_req_to_buf(struct crypto_async_request *arq)
actx->fill = 0;
- /* For some reason, the result is flipped. */
- for (i = 0; i < halg->digestsize / 2; i++) {
- swap(req->result[i],
- req->result[halg->digestsize - i - 1]);
- }
+ /* For some reason the result is flipped */
+ for (i = 0; i < halg->digestsize; i++)
+ req->result[i] = out_buf[halg->digestsize - i - 1];
}
return 0;
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index faa2820..b721693 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -475,9 +475,9 @@ static void s5p_sg_done(struct s5p_aes_dev *dev)
}
/* Calls the completion. Cannot be called with dev->lock hold. */
-static void s5p_aes_complete(struct s5p_aes_dev *dev, int err)
+static void s5p_aes_complete(struct ablkcipher_request *req, int err)
{
- dev->req->base.complete(&dev->req->base, err);
+ req->base.complete(&req->base, err);
}
static void s5p_unset_outdata(struct s5p_aes_dev *dev)
@@ -491,7 +491,7 @@ static void s5p_unset_indata(struct s5p_aes_dev *dev)
}
static int s5p_make_sg_cpy(struct s5p_aes_dev *dev, struct scatterlist *src,
- struct scatterlist **dst)
+ struct scatterlist **dst)
{
void *pages;
int len;
@@ -655,6 +655,7 @@ static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct s5p_aes_dev *dev = platform_get_drvdata(pdev);
+ struct ablkcipher_request *req;
int err_dma_tx = 0;
int err_dma_rx = 0;
int err_dma_hx = 0;
@@ -727,7 +728,7 @@ static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
spin_unlock_irqrestore(&dev->lock, flags);
- s5p_aes_complete(dev, 0);
+ s5p_aes_complete(dev->req, 0);
/* Device is still busy */
tasklet_schedule(&dev->tasklet);
} else {
@@ -752,11 +753,12 @@ static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
error:
s5p_sg_done(dev);
dev->busy = false;
+ req = dev->req;
if (err_dma_hx == 1)
s5p_set_dma_hashdata(dev, dev->hash_sg_iter);
spin_unlock_irqrestore(&dev->lock, flags);
- s5p_aes_complete(dev, err);
+ s5p_aes_complete(req, err);
hash_irq_end:
/*
@@ -1887,7 +1889,7 @@ static int s5p_set_indata_start(struct s5p_aes_dev *dev,
}
static int s5p_set_outdata_start(struct s5p_aes_dev *dev,
- struct ablkcipher_request *req)
+ struct ablkcipher_request *req)
{
struct scatterlist *sg;
int err;
@@ -1983,7 +1985,7 @@ static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode)
s5p_sg_done(dev);
dev->busy = false;
spin_unlock_irqrestore(&dev->lock, flags);
- s5p_aes_complete(dev, err);
+ s5p_aes_complete(req, err);
}
static void s5p_tasklet_cb(unsigned long data)
diff --git a/drivers/crypto/stm32/stm32-hash.c b/drivers/crypto/stm32/stm32-hash.c
index 590d735..641b110 100644
--- a/drivers/crypto/stm32/stm32-hash.c
+++ b/drivers/crypto/stm32/stm32-hash.c
@@ -365,7 +365,7 @@ static int stm32_hash_xmit_cpu(struct stm32_hash_dev *hdev,
return -ETIMEDOUT;
if ((hdev->flags & HASH_FLAGS_HMAC) &&
- (hdev->flags & ~HASH_FLAGS_HMAC_KEY)) {
+ (!(hdev->flags & HASH_FLAGS_HMAC_KEY))) {
hdev->flags |= HASH_FLAGS_HMAC_KEY;
stm32_hash_write_key(hdev);
if (stm32_hash_wait_busy(hdev))
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
index 5cf64746..22e4918 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
@@ -81,7 +81,8 @@ static int sun4i_ss_opti_poll(struct skcipher_request *areq)
oi = 0;
oo = 0;
do {
- todo = min3(rx_cnt, ileft, (mi.length - oi) / 4);
+ todo = min(rx_cnt, ileft);
+ todo = min_t(size_t, todo, (mi.length - oi) / 4);
if (todo) {
ileft -= todo;
writesl(ss->base + SS_RXFIFO, mi.addr + oi, todo);
@@ -96,7 +97,8 @@ static int sun4i_ss_opti_poll(struct skcipher_request *areq)
rx_cnt = SS_RXFIFO_SPACES(spaces);
tx_cnt = SS_TXFIFO_SPACES(spaces);
- todo = min3(tx_cnt, oleft, (mo.length - oo) / 4);
+ todo = min(tx_cnt, oleft);
+ todo = min_t(size_t, todo, (mo.length - oo) / 4);
if (todo) {
oleft -= todo;
readsl(ss->base + SS_TXFIFO, mo.addr + oo, todo);
@@ -220,7 +222,8 @@ static int sun4i_ss_cipher_poll(struct skcipher_request *areq)
* todo is the number of consecutive 4byte word that we
* can read from current SG
*/
- todo = min3(rx_cnt, ileft / 4, (mi.length - oi) / 4);
+ todo = min(rx_cnt, ileft / 4);
+ todo = min_t(size_t, todo, (mi.length - oi) / 4);
if (todo && !ob) {
writesl(ss->base + SS_RXFIFO, mi.addr + oi,
todo);
@@ -234,8 +237,8 @@ static int sun4i_ss_cipher_poll(struct skcipher_request *areq)
* we need to be able to write all buf in one
* pass, so it is why we min() with rx_cnt
*/
- todo = min3(rx_cnt * 4 - ob, ileft,
- mi.length - oi);
+ todo = min(rx_cnt * 4 - ob, ileft);
+ todo = min_t(size_t, todo, mi.length - oi);
memcpy(buf + ob, mi.addr + oi, todo);
ileft -= todo;
oi += todo;
@@ -255,7 +258,8 @@ static int sun4i_ss_cipher_poll(struct skcipher_request *areq)
spaces = readl(ss->base + SS_FCSR);
rx_cnt = SS_RXFIFO_SPACES(spaces);
tx_cnt = SS_TXFIFO_SPACES(spaces);
- dev_dbg(ss->dev, "%x %u/%u %u/%u cnt=%u %u/%u %u/%u cnt=%u %u\n",
+ dev_dbg(ss->dev,
+ "%x %u/%zu %u/%u cnt=%u %u/%zu %u/%u cnt=%u %u\n",
mode,
oi, mi.length, ileft, areq->cryptlen, rx_cnt,
oo, mo.length, oleft, areq->cryptlen, tx_cnt, ob);
@@ -263,7 +267,8 @@ static int sun4i_ss_cipher_poll(struct skcipher_request *areq)
if (!tx_cnt)
continue;
/* todo in 4bytes word */
- todo = min3(tx_cnt, oleft / 4, (mo.length - oo) / 4);
+ todo = min(tx_cnt, oleft / 4);
+ todo = min_t(size_t, todo, (mo.length - oo) / 4);
if (todo) {
readsl(ss->base + SS_TXFIFO, mo.addr + oo, todo);
oleft -= todo * 4;
@@ -287,7 +292,8 @@ static int sun4i_ss_cipher_poll(struct skcipher_request *areq)
* no more than remaining buffer
* no need to test against oleft
*/
- todo = min(mo.length - oo, obl - obo);
+ todo = min_t(size_t,
+ mo.length - oo, obl - obo);
memcpy(mo.addr + oo, bufo + obo, todo);
oleft -= todo;
obo += todo;
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-hash.c b/drivers/crypto/sunxi-ss/sun4i-ss-hash.c
index f6936bb..1a72426 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-hash.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-hash.c
@@ -276,8 +276,8 @@ static int sun4i_hash(struct ahash_request *areq)
*/
while (op->len < 64 && i < end) {
/* how many bytes we can read from current SG */
- in_r = min3(mi.length - in_i, end - i,
- 64 - op->len);
+ in_r = min(end - i, 64 - op->len);
+ in_r = min_t(size_t, mi.length - in_i, in_r);
memcpy(op->buf + op->len, mi.addr + in_i, in_r);
op->len += in_r;
i += in_r;
@@ -297,8 +297,8 @@ static int sun4i_hash(struct ahash_request *areq)
}
if (mi.length - in_i > 3 && i < end) {
/* how many bytes we can read from current SG */
- in_r = min3(mi.length - in_i, areq->nbytes - i,
- ((mi.length - in_i) / 4) * 4);
+ in_r = min_t(size_t, mi.length - in_i, areq->nbytes - i);
+ in_r = min_t(size_t, ((mi.length - in_i) / 4) * 4, in_r);
/* how many bytes we can write in the device*/
todo = min3((u32)(end - i) / 4, rx_cnt, (u32)in_r / 4);
writesl(ss->base + SS_RXFIFO, mi.addr + in_i, todo);
@@ -324,8 +324,8 @@ static int sun4i_hash(struct ahash_request *areq)
if ((areq->nbytes - i) < 64) {
while (i < areq->nbytes && in_i < mi.length && op->len < 64) {
/* how many bytes we can read from current SG */
- in_r = min3(mi.length - in_i, areq->nbytes - i,
- 64 - op->len);
+ in_r = min(areq->nbytes - i, 64 - op->len);
+ in_r = min_t(size_t, mi.length - in_i, in_r);
memcpy(op->buf + op->len, mi.addr + in_i, in_r);
op->len += in_r;
i += in_r;
diff --git a/drivers/crypto/virtio/virtio_crypto_algs.c b/drivers/crypto/virtio/virtio_crypto_algs.c
index 2c573d1..523b712 100644
--- a/drivers/crypto/virtio/virtio_crypto_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_algs.c
@@ -117,8 +117,6 @@ virtio_crypto_alg_validate_key(int key_len, uint32_t *alg)
*alg = VIRTIO_CRYPTO_CIPHER_AES_CBC;
break;
default:
- pr_err("virtio_crypto: Unsupported key length: %d\n",
- key_len);
return -EINVAL;
}
return 0;
@@ -498,6 +496,11 @@ static int virtio_crypto_ablkcipher_encrypt(struct ablkcipher_request *req)
/* Use the first data virtqueue as default */
struct data_queue *data_vq = &vcrypto->data_vq[0];
+ if (!req->nbytes)
+ return 0;
+ if (req->nbytes % AES_BLOCK_SIZE)
+ return -EINVAL;
+
vc_req->dataq = data_vq;
vc_req->alg_cb = virtio_crypto_dataq_sym_callback;
vc_sym_req->ablkcipher_ctx = ctx;
@@ -518,6 +521,11 @@ static int virtio_crypto_ablkcipher_decrypt(struct ablkcipher_request *req)
/* Use the first data virtqueue as default */
struct data_queue *data_vq = &vcrypto->data_vq[0];
+ if (!req->nbytes)
+ return 0;
+ if (req->nbytes % AES_BLOCK_SIZE)
+ return -EINVAL;
+
vc_req->dataq = data_vq;
vc_req->alg_cb = virtio_crypto_dataq_sym_callback;
vc_sym_req->ablkcipher_ctx = ctx;
diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile
index cab32cf..709670d 100644
--- a/drivers/crypto/vmx/Makefile
+++ b/drivers/crypto/vmx/Makefile
@@ -3,13 +3,13 @@
vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o aes_xts.o ghash.o
ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
-TARGET := linux-ppc64le
+override flavour := linux-ppc64le
else
-TARGET := linux-ppc64
+override flavour := linux-ppc64
endif
quiet_cmd_perl = PERL $@
- cmd_perl = $(PERL) $(<) $(TARGET) > $(@)
+ cmd_perl = $(PERL) $(<) $(flavour) > $(@)
targets += aesp8-ppc.S ghashp8-ppc.S
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 97d57c3..25ef772 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -162,6 +163,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
int lev, prev_lev, ret = 0;
unsigned long cur_time;
+ lockdep_assert_held(&devfreq->lock);
cur_time = jiffies;
/* Immediately exit if previous_freq is not initialized yet. */
@@ -221,6 +223,49 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
return ERR_PTR(-ENODEV);
}
+/**
+ * try_then_request_governor() - Try to find the governor and request the
+ * module if is not found.
+ * @name: name of the governor
+ *
+ * Search the list of devfreq governors and request the module and try again
+ * if is not found. This can happen when both drivers (the governor driver
+ * and the driver that call devfreq_add_device) are built as modules.
+ * devfreq_list_lock should be held by the caller. Returns the matched
+ * governor's pointer or an error pointer.
+ */
+static struct devfreq_governor *try_then_request_governor(const char *name)
+{
+ struct devfreq_governor *governor;
+ int err = 0;
+
+ if (IS_ERR_OR_NULL(name)) {
+ pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+ WARN(!mutex_is_locked(&devfreq_list_lock),
+ "devfreq_list_lock must be locked.");
+
+ governor = find_devfreq_governor(name);
+ if (IS_ERR(governor)) {
+ mutex_unlock(&devfreq_list_lock);
+
+ if (!strncmp(name, DEVFREQ_GOV_SIMPLE_ONDEMAND,
+ DEVFREQ_NAME_LEN))
+ err = request_module("governor_%s", "simpleondemand");
+ else
+ err = request_module("governor_%s", name);
+ /* Restore previous state before return */
+ mutex_lock(&devfreq_list_lock);
+ if (err)
+ return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL);
+
+ governor = find_devfreq_governor(name);
+ }
+
+ return governor;
+}
+
static int devfreq_notify_transition(struct devfreq *devfreq,
struct devfreq_freqs *freqs, unsigned int state)
{
@@ -283,11 +328,11 @@ int update_devfreq(struct devfreq *devfreq)
max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
- if (min_freq && freq < min_freq) {
+ if (freq < min_freq) {
freq = min_freq;
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
}
- if (max_freq && freq > max_freq) {
+ if (freq > max_freq) {
freq = max_freq;
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
}
@@ -493,31 +538,30 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
void *devp)
{
struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
- int ret;
- long freq;
+ int err = -EINVAL;
mutex_lock(&devfreq->lock);
- freq = find_available_min_freq(devfreq);
- if (freq < 0) {
- devfreq->scaling_min_freq = 0;
- mutex_unlock(&devfreq->lock);
- return -EINVAL;
- }
- devfreq->scaling_min_freq = freq;
+ devfreq->scaling_min_freq = find_available_min_freq(devfreq);
+ if (!devfreq->scaling_min_freq)
+ goto out;
- freq = find_available_max_freq(devfreq);
- if (freq < 0) {
- devfreq->scaling_max_freq = 0;
- mutex_unlock(&devfreq->lock);
- return -EINVAL;
+ devfreq->scaling_max_freq = find_available_max_freq(devfreq);
+ if (!devfreq->scaling_max_freq) {
+ devfreq->scaling_max_freq = ULONG_MAX;
+ goto out;
}
- devfreq->scaling_max_freq = freq;
- ret = update_devfreq(devfreq);
+ err = update_devfreq(devfreq);
+
+out:
mutex_unlock(&devfreq->lock);
+ if (err)
+ dev_err(devfreq->dev.parent,
+ "failed to update frequency from OPP notifier (%d)\n",
+ err);
- return ret;
+ return NOTIFY_OK;
}
/**
@@ -531,18 +575,9 @@ static void devfreq_dev_release(struct device *dev)
struct devfreq *devfreq = to_devfreq(dev);
mutex_lock(&devfreq_list_lock);
- if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
- mutex_unlock(&devfreq_list_lock);
- dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n");
- return;
- }
list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock);
- if (devfreq->governor)
- devfreq->governor->event_handler(devfreq,
- DEVFREQ_GOV_STOP, NULL);
-
if (devfreq->profile->exit)
devfreq->profile->exit(devfreq->dev.parent);
@@ -596,6 +631,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->dev.parent = dev;
devfreq->dev.class = devfreq_class;
devfreq->dev.release = devfreq_dev_release;
+ INIT_LIST_HEAD(&devfreq->node);
devfreq->profile = profile;
strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq;
@@ -653,9 +689,8 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_unlock(&devfreq->lock);
mutex_lock(&devfreq_list_lock);
- list_add(&devfreq->node, &devfreq_list);
- governor = find_devfreq_governor(devfreq->governor_name);
+ governor = try_then_request_governor(devfreq->governor_name);
if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n",
__func__);
@@ -671,15 +706,17 @@ struct devfreq *devfreq_add_device(struct device *dev,
__func__);
goto err_init;
}
+
+ list_add(&devfreq->node, &devfreq_list);
+
mutex_unlock(&devfreq_list_lock);
return devfreq;
err_init:
- list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock);
- device_unregister(&devfreq->dev);
+ devfreq_remove_device(devfreq);
devfreq = NULL;
err_dev:
if (devfreq)
@@ -700,6 +737,9 @@ int devfreq_remove_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;
+ if (devfreq->governor)
+ devfreq->governor->event_handler(devfreq,
+ DEVFREQ_GOV_STOP, NULL);
device_unregister(&devfreq->dev);
return 0;
@@ -1015,7 +1055,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
mutex_lock(&devfreq_list_lock);
- governor = find_devfreq_governor(str_governor);
+ governor = try_then_request_governor(str_governor);
if (IS_ERR(governor)) {
ret = PTR_ERR(governor);
goto out;
@@ -1030,10 +1070,6 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
}
mutex_lock(&df->event_lock);
- if (df->dev_suspended) {
- ret = -EINVAL;
- goto gov_stop_out;
- }
if (df->governor) {
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) {
@@ -1082,7 +1118,7 @@ static ssize_t available_governors_show(struct device *d,
* The devfreq with immutable governor (e.g., passive) shows
* only own governor.
*/
- if (df->governor->immutable) {
+ if (df->governor && df->governor->immutable) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name);
/*
@@ -1147,16 +1183,14 @@ static ssize_t polling_interval_store(struct device *dev,
unsigned int value;
int ret;
+ if (!df->governor)
+ return -EINVAL;
+
ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->event_lock);
- if (!df->governor || df->dev_suspended) {
- dev_warn(dev, "device suspended, operation not allowed\n");
- mutex_unlock(&df->event_lock);
- return -EINVAL;
- }
df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
ret = count;
mutex_unlock(&df->event_lock);
@@ -1171,23 +1205,27 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
unsigned long value;
int ret;
- unsigned long max;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->event_lock);
- if (df->dev_suspended) {
- dev_warn(dev, "device suspended, min freq not allowed\n");
- mutex_unlock(&df->event_lock);
- return -EINVAL;
- }
mutex_lock(&df->lock);
- max = df->max_freq;
- if (value && max && value > max) {
- ret = -EINVAL;
- goto unlock;
+
+ if (value) {
+ if (value > df->max_freq) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ } else {
+ unsigned long *freq_table = df->profile->freq_table;
+
+ /* Get minimum frequency according to sorting order */
+ if (freq_table[0] < freq_table[df->profile->max_state - 1])
+ value = freq_table[0];
+ else
+ value = freq_table[df->profile->max_state - 1];
}
df->min_freq = value;
@@ -1213,23 +1251,27 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
unsigned long value;
int ret;
- unsigned long min;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->event_lock);
- if (df->dev_suspended) {
- mutex_unlock(&df->event_lock);
- dev_warn(dev, "device suspended, max freq not allowed\n");
- return -EINVAL;
- }
mutex_lock(&df->lock);
- min = df->min_freq;
- if (value && min && value < min) {
- ret = -EINVAL;
- goto unlock;
+
+ if (value) {
+ if (value < df->min_freq) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ } else {
+ unsigned long *freq_table = df->profile->freq_table;
+
+ /* Get maximum frequency according to sorting order */
+ if (freq_table[0] < freq_table[df->profile->max_state - 1])
+ value = freq_table[df->profile->max_state - 1];
+ else
+ value = freq_table[0];
}
df->max_freq = value;
@@ -1284,12 +1326,17 @@ static ssize_t trans_stat_show(struct device *dev,
int i, j;
unsigned int max_state = devfreq->profile->max_state;
- if (!devfreq->stop_polling &&
- devfreq_update_status(devfreq, devfreq->previous_freq))
- return 0;
if (max_state == 0)
return sprintf(buf, "Not Supported.\n");
+ mutex_lock(&devfreq->lock);
+ if (!devfreq->stop_polling &&
+ devfreq_update_status(devfreq, devfreq->previous_freq)) {
+ mutex_unlock(&devfreq->lock);
+ return 0;
+ }
+ mutex_unlock(&devfreq->lock);
+
len = sprintf(buf, " From : To\n");
len += sprintf(buf + len, " :");
for (i = 0; i < max_state; i++)
diff --git a/drivers/devfreq/devfreq_qcom_fw.c b/drivers/devfreq/devfreq_qcom_fw.c
index 87e58971..e2242d8 100644
--- a/drivers/devfreq/devfreq_qcom_fw.c
+++ b/drivers/devfreq/devfreq_qcom_fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/err.h>
@@ -29,6 +29,7 @@
struct devfreq_qcom_fw {
void __iomem *perf_base;
+ void __iomem *pstate_base;
struct devfreq_dev_profile dp;
struct list_head voters;
struct list_head voter;
@@ -37,6 +38,7 @@ struct devfreq_qcom_fw {
static DEFINE_SPINLOCK(voter_lock);
static unsigned int ftbl_row_size = FTBL_ROW_SIZE;
+static struct device *dev_node;
static int devfreq_qcom_fw_target(struct device *dev, unsigned long *freq,
u32 flags)
@@ -80,6 +82,26 @@ static int devfreq_qcom_fw_target(struct device *dev, unsigned long *freq,
return 0;
}
+static int devfreq_panic_callback(struct notifier_block *nfb,
+ unsigned long event, void *unused)
+{
+ struct devfreq_qcom_fw *d = dev_get_drvdata(dev_node);
+ void __iomem *base;
+
+ pr_err("%s: L3-DOMAIN\n", __func__);
+ base = d->perf_base;
+ pr_err("%25s: 0x%.8x\n", "PERF_STATE_DESIRED", readl_relaxed(base));
+ base = d->pstate_base;
+ pr_err("%25s: 0x%.8x\n", "PSTATE_STATUS", readl_relaxed(base));
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block devfreq_panic_notifier = {
+ .notifier_call = devfreq_panic_callback,
+ .priority = 1,
+};
+
static int devfreq_qcom_fw_get_cur_freq(struct device *dev,
unsigned long *freq)
{
@@ -197,6 +219,29 @@ static int devfreq_qcom_init_hw(struct platform_device *pdev)
goto out;
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,support-panic-notifier")) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pstate-base");
+ if (!res) {
+ dev_err(dev, "Unable to find pstate-base!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ d->pstate_base = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (!d->pstate_base) {
+ dev_err(dev, "Unable to map pstate-base\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &devfreq_panic_notifier);
+ dev_node = dev;
+ }
+
INIT_LIST_HEAD(&d->voters);
dev_set_drvdata(dev, d);
diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index c5d21cc..f20b6cf 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2013-2018, 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, 2019-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "bw-hwmon: " fmt
@@ -62,6 +62,7 @@ struct hwmon_node {
ktime_t hist_max_ts;
bool sampled;
bool mon_started;
+ bool init_pending;
struct list_head list;
void *orig_data;
struct bw_hwmon *hw;
@@ -539,8 +540,15 @@ static int start_monitor(struct devfreq *df, bool init)
unsigned long mbps;
int ret;
- node->prev_ts = ktime_get();
+ if (init && df->dev_suspended) {
+ node->init_pending = true;
+ return 0;
+ } else if (!init && node->init_pending) {
+ init = true;
+ node->init_pending = false;
+ }
+ node->prev_ts = ktime_get();
if (init) {
node->prev_ab = 0;
node->resume_freq = 0;
@@ -580,7 +588,8 @@ static void stop_monitor(struct devfreq *df, bool init)
if (init) {
devfreq_monitor_stop(df);
- hw->stop_hwmon(hw);
+ if (!df->dev_suspended)
+ hw->stop_hwmon(hw);
} else {
devfreq_monitor_suspend(df);
hw->suspend_hwmon(hw);
@@ -705,7 +714,7 @@ static int devfreq_bw_hwmon_get_freq(struct devfreq *df,
struct hwmon_node *node = df->data;
/* Suspend/resume sequence */
- if (node && !node->mon_started) {
+ if ((node && !node->mon_started) || df->dev_suspended) {
*freq = node->resume_freq;
*node->dev_ab = node->resume_ab;
return 0;
@@ -877,6 +886,10 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
*/
hw = node->hw;
+ if (!node->mon_started || df->dev_suspended) {
+ devfreq_interval_update(df, &sample_ms);
+ break;
+ }
mutex_lock(&node->mon_lock);
node->mon_started = false;
mutex_unlock(&node->mon_lock);
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index 35dd064..91d6209 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -230,7 +230,7 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
a_fences = get_fences(a, &a_num_fences);
b_fences = get_fences(b, &b_num_fences);
if (a_num_fences > INT_MAX - b_num_fences)
- return NULL;
+ goto err;
num_fences = a_num_fences + b_num_fences;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index dacf3f4..a4f9557 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -143,7 +143,7 @@
config DMA_JZ4780
tristate "JZ4780 DMA support"
- depends on MACH_JZ4780 || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index db5b8fe..7db66f9 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -1608,7 +1608,7 @@ static void at_xdmac_tasklet(unsigned long data)
dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc);
if (!desc->active_xfer) {
dev_err(chan2dev(&atchan->chan), "Xfer not active: exiting");
- spin_unlock_bh(&atchan->lock);
+ spin_unlock(&atchan->lock);
return;
}
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index da74fd7..cee78f9c 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -1797,13 +1797,10 @@ static struct dma_chan *coh901318_xlate(struct of_phandle_args *dma_spec,
static int coh901318_config(struct coh901318_chan *cohc,
struct coh901318_params *param)
{
- unsigned long flags;
const struct coh901318_params *p;
int channel = cohc->id;
void __iomem *virtbase = cohc->base->virtbase;
- spin_lock_irqsave(&cohc->lock, flags);
-
if (param)
p = param;
else
@@ -1823,8 +1820,6 @@ static int coh901318_config(struct coh901318_chan *cohc,
coh901318_set_conf(cohc, p->config);
coh901318_set_ctrl(cohc, p->ctrl_lli_last);
- spin_unlock_irqrestore(&cohc->lock, flags);
-
return 0;
}
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 9878996..edff93a 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -587,7 +587,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
to_jz4780_dma_desc(vdesc), 0);
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
- (jzchan->curr_hwdesc + 1) % jzchan->desc->count);
+ jzchan->curr_hwdesc + 1);
} else
txstate->residue = 0;
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 0f389e0..055d83b 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -160,12 +160,14 @@ static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc)
static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc)
{
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
u32 cfghi = DWC_CFGH_FIFO_MODE;
u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
bool hs_polarity = dwc->dws.hs_polarity;
cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
+ cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl);
/* Set polarity of handshake interface */
cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;
diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c
index f62dd09..c299ff1 100644
--- a/drivers/dma/dw/platform.c
+++ b/drivers/dma/dw/platform.c
@@ -162,6 +162,12 @@ dw_dma_parse_dt(struct platform_device *pdev)
pdata->multi_block[tmp] = 1;
}
+ if (!of_property_read_u32(np, "snps,dma-protection-control", &tmp)) {
+ if (tmp > CHAN_PROTCTL_MASK)
+ return NULL;
+ pdata->protctl = tmp;
+ }
+
return pdata;
}
#else
diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h
index 09e7dfd..646c9c9 100644
--- a/drivers/dma/dw/regs.h
+++ b/drivers/dma/dw/regs.h
@@ -200,6 +200,10 @@ enum dw_dma_msize {
#define DWC_CFGH_FCMODE (1 << 0)
#define DWC_CFGH_FIFO_MODE (1 << 1)
#define DWC_CFGH_PROTCTL(x) ((x) << 2)
+#define DWC_CFGH_PROTCTL_DATA (0 << 2) /* data access - always set */
+#define DWC_CFGH_PROTCTL_PRIV (1 << 2) /* privileged -> AHB HPROT[1] */
+#define DWC_CFGH_PROTCTL_BUFFER (2 << 2) /* bufferable -> AHB HPROT[2] */
+#define DWC_CFGH_PROTCTL_CACHE (4 << 2) /* cacheable -> AHB HPROT[3] */
#define DWC_CFGH_DS_UPD_EN (1 << 5)
#define DWC_CFGH_SS_UPD_EN (1 << 6)
#define DWC_CFGH_SRC_PER(x) ((x) << 7)
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index 21a5708..0fec3c5 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -129,7 +129,7 @@ static void
ioat_init_channel(struct ioatdma_device *ioat_dma,
struct ioatdma_chan *ioat_chan, int idx);
static void ioat_intr_quirk(struct ioatdma_device *ioat_dma);
-static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma);
+static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma);
static int ioat3_dma_self_test(struct ioatdma_device *ioat_dma);
static int ioat_dca_enabled = 1;
@@ -575,7 +575,7 @@ static void ioat_dma_remove(struct ioatdma_device *ioat_dma)
* ioat_enumerate_channels - find and initialize the device's channels
* @ioat_dma: the ioat dma device to be enumerated
*/
-static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma)
+static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma)
{
struct ioatdma_chan *ioat_chan;
struct device *dev = &ioat_dma->pdev->dev;
@@ -594,7 +594,7 @@ static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma)
xfercap_log = readb(ioat_dma->reg_base + IOAT_XFERCAP_OFFSET);
xfercap_log &= 0x1f; /* bits [4:0] valid */
if (xfercap_log == 0)
- return 0;
+ return;
dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log);
for (i = 0; i < dma->chancnt; i++) {
@@ -611,7 +611,6 @@ static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma)
}
}
dma->chancnt = i;
- return i;
}
/**
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index 489c8fa..4451ccf 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -703,6 +703,25 @@ static int bam_dma_terminate_all(struct dma_chan *chan)
/* remove all transactions, including active transaction */
spin_lock_irqsave(&bchan->vc.lock, flag);
+ /*
+ * If we have transactions queued, then some might be committed to the
+ * hardware in the desc fifo. The only way to reset the desc fifo is
+ * to do a hardware reset (either by pipe or the entire block).
+ * bam_chan_init_hw() will trigger a pipe reset, and also reinit the
+ * pipe. If the pipe is left disabled (default state after pipe reset)
+ * and is accessed by a connected hardware engine, a fatal error in
+ * the BAM will occur. There is a small window where this could happen
+ * with bam_chan_init_hw(), but it is assumed that the caller has
+ * stopped activity on any attached hardware engine. Make sure to do
+ * this first so that the BAM hardware doesn't cause memory corruption
+ * by accessing freed resources.
+ */
+ if (!list_empty(&bchan->desc_list)) {
+ async_desc = list_first_entry(&bchan->desc_list,
+ struct bam_async_desc, desc_node);
+ bam_chan_init_hw(bchan, async_desc->dir);
+ }
+
list_for_each_entry_safe(async_desc, tmp,
&bchan->desc_list, desc_node) {
list_add(&async_desc->vd.node, &bchan->vc.desc_issued);
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index 041ce86..80ff95f 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -198,6 +198,7 @@ struct rcar_dmac {
struct dma_device engine;
struct device *dev;
void __iomem *iomem;
+ struct device_dma_parameters parms;
unsigned int n_channels;
struct rcar_dmac_chan *channels;
@@ -1814,6 +1815,8 @@ static int rcar_dmac_probe(struct platform_device *pdev)
dmac->dev = &pdev->dev;
platform_set_drvdata(pdev, dmac);
+ dmac->dev->dma_parms = &dmac->parms;
+ dma_set_max_seg_size(dmac->dev, RCAR_DMATCR_MASK);
dma_set_mask_and_coherent(dmac->dev, DMA_BIT_MASK(40));
ret = rcar_dmac_parse_of(&pdev->dev, dmac);
diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c
index 1ed1c7e..9e8ce56 100644
--- a/drivers/dma/sprd-dma.c
+++ b/drivers/dma/sprd-dma.c
@@ -181,6 +181,7 @@ struct sprd_dma_dev {
struct sprd_dma_chn channels[0];
};
+static void sprd_dma_free_desc(struct virt_dma_desc *vd);
static bool sprd_dma_filter_fn(struct dma_chan *chan, void *param);
static struct of_dma_filter_info sprd_dma_info = {
.filter_fn = sprd_dma_filter_fn,
@@ -493,12 +494,19 @@ static int sprd_dma_alloc_chan_resources(struct dma_chan *chan)
static void sprd_dma_free_chan_resources(struct dma_chan *chan)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
+ struct virt_dma_desc *cur_vd = NULL;
unsigned long flags;
spin_lock_irqsave(&schan->vc.lock, flags);
+ if (schan->cur_desc)
+ cur_vd = &schan->cur_desc->vd;
+
sprd_dma_stop(schan);
spin_unlock_irqrestore(&schan->vc.lock, flags);
+ if (cur_vd)
+ sprd_dma_free_desc(cur_vd);
+
vchan_free_chan_resources(&schan->vc);
pm_runtime_put(chan->device->dev);
}
@@ -814,15 +822,22 @@ static int sprd_dma_resume(struct dma_chan *chan)
static int sprd_dma_terminate_all(struct dma_chan *chan)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
+ struct virt_dma_desc *cur_vd = NULL;
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&schan->vc.lock, flags);
+ if (schan->cur_desc)
+ cur_vd = &schan->cur_desc->vd;
+
sprd_dma_stop(schan);
vchan_get_all_descriptors(&schan->vc, &head);
spin_unlock_irqrestore(&schan->vc.lock, flags);
+ if (cur_vd)
+ sprd_dma_free_desc(cur_vd);
+
vchan_dma_desc_free_list(&schan->vc, &head);
return 0;
}
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 379e8d5..4903a40 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -308,20 +308,12 @@ static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold,
static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold)
{
- switch (threshold) {
- case STM32_DMA_FIFO_THRESHOLD_FULL:
- if (buf_len >= STM32_DMA_MAX_BURST)
- return true;
- else
- return false;
- case STM32_DMA_FIFO_THRESHOLD_HALFFULL:
- if (buf_len >= STM32_DMA_MAX_BURST / 2)
- return true;
- else
- return false;
- default:
- return false;
- }
+ /*
+ * Buffer or period length has to be aligned on FIFO depth.
+ * Otherwise bytes may be stuck within FIFO at buffer or period
+ * length.
+ */
+ return ((buf_len % ((threshold + 1) * 4)) == 0);
}
static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold,
diff --git a/drivers/dma/ti/cppi41.c b/drivers/dma/ti/cppi41.c
index e507ec3..f8fa994 100644
--- a/drivers/dma/ti/cppi41.c
+++ b/drivers/dma/ti/cppi41.c
@@ -585,9 +585,22 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg(
enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
{
struct cppi41_channel *c = to_cpp41_chan(chan);
+ struct dma_async_tx_descriptor *txd = NULL;
+ struct cppi41_dd *cdd = c->cdd;
struct cppi41_desc *d;
struct scatterlist *sg;
unsigned int i;
+ int error;
+
+ error = pm_runtime_get(cdd->ddev.dev);
+ if (error < 0) {
+ pm_runtime_put_noidle(cdd->ddev.dev);
+
+ return NULL;
+ }
+
+ if (cdd->is_suspended)
+ goto err_out_not_ready;
d = c->desc;
for_each_sg(sgl, sg, sg_len, i) {
@@ -610,7 +623,13 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg(
d++;
}
- return &c->txd;
+ txd = &c->txd;
+
+err_out_not_ready:
+ pm_runtime_mark_last_busy(cdd->ddev.dev);
+ pm_runtime_put_autosuspend(cdd->ddev.dev);
+
+ return txd;
}
static void cppi41_compute_td_desc(struct cppi41_desc *d)
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index 395c698..fc0f9c8 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -545,7 +545,7 @@ static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan,
}
dma_sync_single_for_device(chan2dmadev(chan), td_desc->txd.phys,
- td_desc->desc_list_len, DMA_MEM_TO_DEV);
+ td_desc->desc_list_len, DMA_TO_DEVICE);
return &td_desc->txd;
}
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index c124423..d56b6b0 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -72,6 +72,9 @@
#define XILINX_DMA_DMACR_CIRC_EN BIT(1)
#define XILINX_DMA_DMACR_RUNSTOP BIT(0)
#define XILINX_DMA_DMACR_FSYNCSRC_MASK GENMASK(6, 5)
+#define XILINX_DMA_DMACR_DELAY_MASK GENMASK(31, 24)
+#define XILINX_DMA_DMACR_FRAME_COUNT_MASK GENMASK(23, 16)
+#define XILINX_DMA_DMACR_MASTER_MASK GENMASK(11, 8)
#define XILINX_DMA_REG_DMASR 0x0004
#define XILINX_DMA_DMASR_EOL_LATE_ERR BIT(15)
@@ -1424,6 +1427,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
chan->err = false;
chan->idle = true;
+ chan->desc_pendingcount = 0;
chan->desc_submitcount = 0;
return err;
@@ -2112,8 +2116,10 @@ int xilinx_vdma_channel_set_config(struct dma_chan *dchan,
chan->config.gen_lock = cfg->gen_lock;
chan->config.master = cfg->master;
+ dmacr &= ~XILINX_DMA_DMACR_GENLOCK_EN;
if (cfg->gen_lock && chan->genlock) {
dmacr |= XILINX_DMA_DMACR_GENLOCK_EN;
+ dmacr &= ~XILINX_DMA_DMACR_MASTER_MASK;
dmacr |= cfg->master << XILINX_DMA_DMACR_MASTER_SHIFT;
}
@@ -2129,11 +2135,13 @@ int xilinx_vdma_channel_set_config(struct dma_chan *dchan,
chan->config.delay = cfg->delay;
if (cfg->coalesc <= XILINX_DMA_DMACR_FRAME_COUNT_MAX) {
+ dmacr &= ~XILINX_DMA_DMACR_FRAME_COUNT_MASK;
dmacr |= cfg->coalesc << XILINX_DMA_DMACR_FRAME_COUNT_SHIFT;
chan->config.coalesc = cfg->coalesc;
}
if (cfg->delay <= XILINX_DMA_DMACR_DELAY_MAX) {
+ dmacr &= ~XILINX_DMA_DMACR_DELAY_MASK;
dmacr |= cfg->delay << XILINX_DMA_DMACR_DELAY_SHIFT;
chan->config.delay = cfg->delay;
}
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index 574bce6..78c339d 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -210,6 +210,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
/* Cleans the error report buffer */
memset(e, 0, sizeof (*e));
e->error_count = 1;
+ e->grain = 1;
strcpy(e->label, "unknown label");
e->msg = pvt->msg;
e->other_detail = pvt->other_detail;
@@ -305,7 +306,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
/* Error grain */
if (mem_err->validation_bits & CPER_MEM_VALID_PA_MASK)
- e->grain = ~(mem_err->physical_addr_mask & ~PAGE_MASK);
+ e->grain = ~mem_err->physical_addr_mask + 1;
/* Memory error location, mapped on e->location */
p = e->location;
@@ -412,8 +413,13 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
if (p > pvt->other_detail)
*(p - 1) = '\0';
+ /* Sanity-check driver-supplied grain value. */
+ if (WARN_ON_ONCE(!e->grain))
+ e->grain = 1;
+
+ grain_bits = fls_long(e->grain - 1);
+
/* Generate the trace event */
- grain_bits = fls_long(e->grain);
snprintf(pvt->detail_location, sizeof(pvt->detail_location),
"APEI location: %s %s", e->location, e->other_detail);
trace_mc_event(type, e->msg, e->label, e->error_count,
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index d92d56c..299b441 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -399,7 +399,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
if (nr_pages == 0)
continue;
- edac_dbg(0, "csrow %d, channel %d%s, size = %ld Mb\n", i, j,
+ edac_dbg(0, "csrow %d, channel %d%s, size = %ld MiB\n", i, j,
stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages));
dimm->nr_pages = nr_pages;
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index f1d1950..4a3300c 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -597,7 +597,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
/* DDR3 has 8 I/O banks */
size = (rows * cols * banks * ranks) >> (20 - 3);
- edac_dbg(0, "\tdimm %d %d Mb offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "\tdimm %d %d MiB offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
j, size,
RANKOFFSET(dimm_dod[j]),
banks, ranks, rows, cols);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 72cea3c..53074ad 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -1622,7 +1622,7 @@ static int __populate_dimms(struct mem_ctl_info *mci,
size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: ha %d channel %d, dimm %d, %lld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "mc#%d: ha %d channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
pvt->sbridge_dev->mc, pvt->sbridge_dev->dom, i, j,
size, npages,
banks, ranks, rows, cols);
@@ -2912,35 +2912,27 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
* cccc = channel
* If the mask doesn't match, report an error to the parsing logic
*/
- if (! ((errcode & 0xef80) == 0x80)) {
- optype = "Can't parse: it is not a mem";
- } else {
- switch (optypenum) {
- case 0:
- optype = "generic undef request error";
- break;
- case 1:
- optype = "memory read error";
- break;
- case 2:
- optype = "memory write error";
- break;
- case 3:
- optype = "addr/cmd error";
- break;
- case 4:
- optype = "memory scrubbing error";
- break;
- default:
- optype = "reserved";
- break;
- }
+ switch (optypenum) {
+ case 0:
+ optype = "generic undef request error";
+ break;
+ case 1:
+ optype = "memory read error";
+ break;
+ case 2:
+ optype = "memory write error";
+ break;
+ case 3:
+ optype = "addr/cmd error";
+ break;
+ case 4:
+ optype = "memory scrubbing error";
+ break;
+ default:
+ optype = "reserved";
+ break;
}
- /* Only decode errors with an valid address (ADDRV) */
- if (!GET_BITFIELD(m->status, 58, 58))
- return;
-
if (pvt->info.type == KNIGHTS_LANDING) {
if (channel == 14) {
edac_dbg(0, "%s%s err_code:%04x:%04x EDRAM bank %d\n",
@@ -3046,17 +3038,11 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
{
struct mce *mce = (struct mce *)data;
struct mem_ctl_info *mci;
- struct sbridge_pvt *pvt;
char *type;
if (edac_get_report_status() == EDAC_REPORTING_DISABLED)
return NOTIFY_DONE;
- mci = get_mci_for_node_id(mce->socketid, IMC0);
- if (!mci)
- return NOTIFY_DONE;
- pvt = mci->pvt_info;
-
/*
* Just let mcelog handle it if the error is
* outside the memory controller. A memory error
@@ -3066,6 +3052,22 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
if ((mce->status & 0xefff) >> 7 != 1)
return NOTIFY_DONE;
+ /* Check ADDRV bit in STATUS */
+ if (!GET_BITFIELD(mce->status, 58, 58))
+ return NOTIFY_DONE;
+
+ /* Check MISCV bit in STATUS */
+ if (!GET_BITFIELD(mce->status, 59, 59))
+ return NOTIFY_DONE;
+
+ /* Check address type in MISC (physical address only) */
+ if (GET_BITFIELD(mce->misc, 6, 8) != 2)
+ return NOTIFY_DONE;
+
+ mci = get_mci_for_node_id(mce->socketid, IMC0);
+ if (!mci)
+ return NOTIFY_DONE;
+
if (mce->mcgstatus & MCG_STATUS_MCIP)
type = "Exception";
else
diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c
index 4ba92f1..dd209e0 100644
--- a/drivers/edac/skx_edac.c
+++ b/drivers/edac/skx_edac.c
@@ -364,7 +364,7 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
imc->mc, chan, dimmno, size, npages,
banks, 1 << ranks, rows, cols);
@@ -424,7 +424,7 @@ static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
dimm->mtype = MEM_NVDIMM;
dimm->edac_mode = EDAC_SECDED; /* likely better than this */
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu Mb (%u pages)\n",
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n",
imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
diff --git a/drivers/edac/thunderx_edac.c b/drivers/edac/thunderx_edac.c
index c009d94..34be60f 100644
--- a/drivers/edac/thunderx_edac.c
+++ b/drivers/edac/thunderx_edac.c
@@ -1884,7 +1884,7 @@ static irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id)
default:
dev_err(&l2c->pdev->dev, "Unsupported device: %04x\n",
l2c->pdev->device);
- return IRQ_NONE;
+ goto err_free;
}
while (CIRC_CNT(l2c->ring_head, l2c->ring_tail,
@@ -1906,7 +1906,7 @@ static irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id)
l2c->ring_tail++;
}
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
err_free:
kfree(other);
diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c
index 5e1dd27..bdb6787 100644
--- a/drivers/extcon/extcon-intel-cht-wc.c
+++ b/drivers/extcon/extcon-intel-cht-wc.c
@@ -156,7 +156,7 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
dev_warn(ext->dev,
"Unhandled charger type %d, defaulting to SDP\n",
ret);
- /* Fall through, treat as SDP */
+ return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_SDP:
case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN:
case CHT_WC_USBSRC_TYPE_OTHER:
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 9f30f49..7a767b6 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -321,12 +321,10 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
{
int ret = 0;
- if (usb_type == MAX8997_USB_HOST) {
- ret = max8997_muic_set_path(info, info->path_usb, attached);
- if (ret < 0) {
- dev_err(info->dev, "failed to update muic register\n");
- return ret;
- }
+ ret = max8997_muic_set_path(info, info->path_usb, attached);
+ if (ret < 0) {
+ dev_err(info->dev, "failed to update muic register\n");
+ return ret;
}
switch (usb_type) {
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c
index 0cfb5a3..2efcd94 100644
--- a/drivers/extcon/extcon-sm5502.c
+++ b/drivers/extcon/extcon-sm5502.c
@@ -69,6 +69,10 @@ struct sm5502_muic_info {
/* Default value of SM5502 register to bring up MUIC device. */
static struct reg_data sm5502_reg_data[] = {
{
+ .reg = SM5502_REG_RESET,
+ .val = SM5502_REG_RESET_MASK,
+ .invert = true,
+ }, {
.reg = SM5502_REG_CONTROL,
.val = SM5502_REG_CONTROL_MASK_INT_MASK,
.invert = false,
diff --git a/drivers/extcon/extcon-sm5502.h b/drivers/extcon/extcon-sm5502.h
index 974b532..12f8b01 100644
--- a/drivers/extcon/extcon-sm5502.h
+++ b/drivers/extcon/extcon-sm5502.h
@@ -241,6 +241,8 @@ enum sm5502_reg {
#define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \
| (DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DM_SHIFT))
+#define SM5502_REG_RESET_MASK (0x1)
+
/* SM5502 Interrupts */
enum sm5502_irq {
/* INT1 */
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 82ba110..bbabfca 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -249,7 +249,11 @@ static int fwnet_header_cache(const struct neighbour *neigh,
h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
h->h_proto = type;
memcpy(h->h_dest, neigh->ha, net->addr_len);
- hh->hh_len = FWNET_HLEN;
+
+ /* Pairs with the READ_ONCE() in neigh_resolve_output(),
+ * neigh_hh_output() and neigh_update_hhs().
+ */
+ smp_store_release(&hh->hh_len, FWNET_HLEN);
return 0;
}
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 9dff33e..2043902 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -208,7 +208,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t);
if (!ret)
- memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
+ strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
scmi_xfer_put(handle, t);
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 92f843ea..7a30952 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -135,8 +135,10 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
return NULL;
id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
- if (id < 0)
- goto free_mem;
+ if (id < 0) {
+ kfree(scmi_dev);
+ return NULL;
+ }
scmi_dev->id = id;
scmi_dev->protocol_id = protocol;
@@ -154,8 +156,6 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
put_dev:
put_device(&scmi_dev->dev);
ida_simple_remove(&scmi_bus_id, id);
-free_mem:
- kfree(scmi_dev);
return NULL;
}
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index e4119eb..30fc04e 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -111,7 +111,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t);
if (!ret)
- memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
+ strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
else
clk->name[0] = '\0';
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index c8024a3..3c8ae7c 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -174,7 +174,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
dom_info->mult_factor =
(dom_info->sustained_freq_khz * 1000) /
dom_info->sustained_perf_level;
- memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
}
scmi_xfer_put(handle, t);
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index cfa033b..62f3401 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -106,7 +106,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
- memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
}
scmi_xfer_put(handle, t);
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 27f2092..b53d5cc 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -140,7 +140,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
s = &si->sensors[desc_index + cnt];
s->id = le32_to_cpu(buf->desc[cnt].id);
s->type = SENSOR_TYPE(attrh);
- memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
+ strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
}
desc_index += num_returned;
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 1ea7164..c64c7da 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -1009,7 +1009,6 @@ static struct platform_driver sdei_driver = {
static bool __init sdei_present_dt(void)
{
- struct platform_device *pdev;
struct device_node *np, *fw_np;
fw_np = of_find_node_by_name(NULL, "firmware");
@@ -1017,14 +1016,9 @@ static bool __init sdei_present_dt(void)
return false;
np = of_find_matching_node(fw_np, sdei_of_match);
- of_node_put(fw_np);
if (!np)
return false;
-
- pdev = of_platform_device_create(np, sdei_driver.driver.name, NULL);
of_node_put(np);
- if (!pdev)
- return false;
return true;
}
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index fb8af5c..ccefa84 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -45,6 +45,7 @@
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
+#include <asm/set_memory.h>
MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
@@ -181,6 +182,11 @@ static int create_packet(void *data, size_t length)
packet_data_temp_buf = NULL;
}
}
+ /*
+ * set to uncachable or it may never get written back before reboot
+ */
+ set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum);
+
spin_lock(&rbu_data.lock);
newpacket->data = packet_data_temp_buf;
@@ -349,6 +355,8 @@ static void packet_empty_list(void)
* to make sure there are no stale RBU packets left in memory
*/
memset(newpacket->data, 0, rbu_data.packetsize);
+ set_memory_wb((unsigned long)newpacket->data,
+ 1 << newpacket->ordernum);
free_pages((unsigned long) newpacket->data,
newpacket->ordernum);
kfree(newpacket);
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 4045098..116989c 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -393,7 +393,7 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
pcie->device_id.vendor_id, pcie->device_id.device_id);
p = pcie->device_id.class_code;
- printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]);
+ printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
}
if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c
index 24c461d..fd8053f 100644
--- a/drivers/firmware/efi/libstub/gop.c
+++ b/drivers/firmware/efi/libstub/gop.c
@@ -86,30 +86,6 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
}
static efi_status_t
-__gop_query32(efi_system_table_t *sys_table_arg,
- struct efi_graphics_output_protocol_32 *gop32,
- struct efi_graphics_output_mode_info **info,
- unsigned long *size, u64 *fb_base)
-{
- struct efi_graphics_output_protocol_mode_32 *mode;
- efi_graphics_output_protocol_query_mode query_mode;
- efi_status_t status;
- unsigned long m;
-
- m = gop32->mode;
- mode = (struct efi_graphics_output_protocol_mode_32 *)m;
- query_mode = (void *)(unsigned long)gop32->query_mode;
-
- status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
- info);
- if (status != EFI_SUCCESS)
- return status;
-
- *fb_base = mode->frame_buffer_base;
- return status;
-}
-
-static efi_status_t
setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
efi_guid_t *proto, unsigned long size, void **gop_handle)
{
@@ -121,7 +97,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
- efi_status_t status = EFI_NOT_FOUND;
+ efi_status_t status;
u32 *handles = (u32 *)(unsigned long)gop_handle;
int i;
@@ -130,6 +106,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
nr_gops = size / sizeof(u32);
for (i = 0; i < nr_gops; i++) {
+ struct efi_graphics_output_protocol_mode_32 *mode;
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
@@ -147,9 +124,11 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
if (status == EFI_SUCCESS)
conout_found = true;
- status = __gop_query32(sys_table_arg, gop32, &info, &size,
- ¤t_fb_base);
- if (status == EFI_SUCCESS && (!first_gop || conout_found) &&
+ mode = (void *)(unsigned long)gop32->mode;
+ info = (void *)(unsigned long)mode->info;
+ current_fb_base = mode->frame_buffer_base;
+
+ if ((!first_gop || conout_found) &&
info->pixel_format != PIXEL_BLT_ONLY) {
/*
* Systems that use the UEFI Console Splitter may
@@ -177,7 +156,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
/* Did we find any GOPs? */
if (!first_gop)
- goto out;
+ return EFI_NOT_FOUND;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
@@ -199,32 +178,8 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
-out:
- return status;
-}
-static efi_status_t
-__gop_query64(efi_system_table_t *sys_table_arg,
- struct efi_graphics_output_protocol_64 *gop64,
- struct efi_graphics_output_mode_info **info,
- unsigned long *size, u64 *fb_base)
-{
- struct efi_graphics_output_protocol_mode_64 *mode;
- efi_graphics_output_protocol_query_mode query_mode;
- efi_status_t status;
- unsigned long m;
-
- m = gop64->mode;
- mode = (struct efi_graphics_output_protocol_mode_64 *)m;
- query_mode = (void *)(unsigned long)gop64->query_mode;
-
- status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
- info);
- if (status != EFI_SUCCESS)
- return status;
-
- *fb_base = mode->frame_buffer_base;
- return status;
+ return EFI_SUCCESS;
}
static efi_status_t
@@ -239,7 +194,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
- efi_status_t status = EFI_NOT_FOUND;
+ efi_status_t status;
u64 *handles = (u64 *)(unsigned long)gop_handle;
int i;
@@ -248,6 +203,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
nr_gops = size / sizeof(u64);
for (i = 0; i < nr_gops; i++) {
+ struct efi_graphics_output_protocol_mode_64 *mode;
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
@@ -265,9 +221,11 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
if (status == EFI_SUCCESS)
conout_found = true;
- status = __gop_query64(sys_table_arg, gop64, &info, &size,
- ¤t_fb_base);
- if (status == EFI_SUCCESS && (!first_gop || conout_found) &&
+ mode = (void *)(unsigned long)gop64->mode;
+ info = (void *)(unsigned long)mode->info;
+ current_fb_base = mode->frame_buffer_base;
+
+ if ((!first_gop || conout_found) &&
info->pixel_format != PIXEL_BLT_ONLY) {
/*
* Systems that use the UEFI Console Splitter may
@@ -295,7 +253,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
/* Did we find any GOPs? */
if (!first_gop)
- goto out;
+ return EFI_NOT_FOUND;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
@@ -317,8 +275,8 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
-out:
- return status;
+
+ return EFI_SUCCESS;
}
/*
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
index c8f169b..62337be 100644
--- a/drivers/firmware/google/gsmi.c
+++ b/drivers/firmware/google/gsmi.c
@@ -480,11 +480,10 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
if (count < sizeof(u32))
return -EINVAL;
param.type = *(u32 *)buf;
- count -= sizeof(u32);
buf += sizeof(u32);
/* The remaining buffer is the data payload */
- if (count > gsmi_dev.data_buf->length)
+ if ((count - sizeof(u32)) > gsmi_dev.data_buf->length)
return -EINVAL;
param.data_len = count - sizeof(u32);
@@ -504,7 +503,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
spin_unlock_irqrestore(&gsmi_dev.lock, flags);
- return rc;
+ return (rc == 0) ? count : rc;
}
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 77d2390..d90718a 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -279,8 +279,9 @@ static int __init psci_features(u32 psci_func_id)
}
#ifdef CONFIG_CPU_IDLE
-static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+static __maybe_unused DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+#ifdef CONFIG_DT_IDLE_STATES
static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
{
int i, ret, count = 0;
@@ -333,6 +334,10 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
kfree(psci_states);
return ret;
}
+#else
+static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
+{ return 0; }
+#endif
#ifdef CONFIG_ACPI
#include <acpi/processor.h>
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index 688525d..367e727 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -158,7 +158,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
kfree(args_virt);
}
- if (res->a0 < 0)
+ if ((long)res->a0 < 0)
return qcom_scm_remap_error(res->a0);
return 0;
diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index a200a21..44eb998 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
@@ -21,8 +22,6 @@
#define MBOX_DATA28(msg) ((msg) & ~0xf)
#define MBOX_CHAN_PROPERTY 8
-#define MAX_RPI_FW_PROP_BUF_SIZE 32
-
static struct platform_device *rpi_hwmon;
struct rpi_firmware {
@@ -144,28 +143,30 @@ EXPORT_SYMBOL_GPL(rpi_firmware_property_list);
int rpi_firmware_property(struct rpi_firmware *fw,
u32 tag, void *tag_data, size_t buf_size)
{
- /* Single tags are very small (generally 8 bytes), so the
- * stack should be safe.
- */
- u8 data[sizeof(struct rpi_firmware_property_tag_header) +
- MAX_RPI_FW_PROP_BUF_SIZE];
- struct rpi_firmware_property_tag_header *header =
- (struct rpi_firmware_property_tag_header *)data;
+ struct rpi_firmware_property_tag_header *header;
int ret;
- if (WARN_ON(buf_size > sizeof(data) - sizeof(*header)))
- return -EINVAL;
+ /* Some mailboxes can use over 1k bytes. Rather than checking
+ * size and using stack or kmalloc depending on requirements,
+ * just use kmalloc. Mailboxes don't get called enough to worry
+ * too much about the time taken in the allocation.
+ */
+ void *data = kmalloc(sizeof(*header) + buf_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ header = data;
header->tag = tag;
header->buf_size = buf_size;
header->req_resp_size = 0;
- memcpy(data + sizeof(struct rpi_firmware_property_tag_header),
- tag_data, buf_size);
+ memcpy(data + sizeof(*header), tag_data, buf_size);
- ret = rpi_firmware_property_list(fw, &data, buf_size + sizeof(*header));
- memcpy(tag_data,
- data + sizeof(struct rpi_firmware_property_tag_header),
- buf_size);
+ ret = rpi_firmware_property_list(fw, data, buf_size + sizeof(*header));
+
+ memcpy(tag_data, data + sizeof(*header), buf_size);
+
+ kfree(data);
return ret;
}
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 2c31563..c6fa9b3 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -552,6 +552,31 @@ static int fsi_slave_scan(struct fsi_slave *slave)
return 0;
}
+static unsigned long aligned_access_size(size_t offset, size_t count)
+{
+ unsigned long offset_unit, count_unit;
+
+ /* Criteria:
+ *
+ * 1. Access size must be less than or equal to the maximum access
+ * width or the highest power-of-two factor of offset
+ * 2. Access size must be less than or equal to the amount specified by
+ * count
+ *
+ * The access width is optimal if we can calculate 1 to be strictly
+ * equal while still satisfying 2.
+ */
+
+ /* Find 1 by the bottom bit of offset (with a 4 byte access cap) */
+ offset_unit = BIT(__builtin_ctzl(offset | 4));
+
+ /* Find 2 by the top bit of count */
+ count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count));
+
+ /* Constrain the maximum access width to the minimum of both criteria */
+ return BIT(__builtin_ctzl(offset_unit | count_unit));
+}
+
static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
@@ -567,8 +592,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
return -EINVAL;
for (total_len = 0; total_len < count; total_len += read_len) {
- read_len = min_t(size_t, count, 4);
- read_len -= off & 0x3;
+ read_len = aligned_access_size(off, count - total_len);
rc = fsi_slave_read(slave, off, buf + total_len, read_len);
if (rc)
@@ -595,8 +619,7 @@ static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
return -EINVAL;
for (total_len = 0; total_len < count; total_len += write_len) {
- write_len = min_t(size_t, count, 4);
- write_len -= off & 0x3;
+ write_len = aligned_access_size(off, count - total_len);
rc = fsi_slave_write(slave, off, buf + total_len, write_len);
if (rc)
diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c
index 538bce4..78254ed 100644
--- a/drivers/gpio/gpio-max77620.c
+++ b/drivers/gpio/gpio-max77620.c
@@ -163,13 +163,13 @@ static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio,
case 0:
val = MAX77620_CNFG_GPIO_DBNC_None;
break;
- case 1 ... 8:
+ case 1 ... 8000:
val = MAX77620_CNFG_GPIO_DBNC_8ms;
break;
- case 9 ... 16:
+ case 8001 ... 16000:
val = MAX77620_CNFG_GPIO_DBNC_16ms;
break;
- case 17 ... 32:
+ case 16001 ... 32000:
val = MAX77620_CNFG_GPIO_DBNC_32ms;
break;
default:
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index c8673a5..3f10f95 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -348,7 +348,8 @@ static int mpc8xxx_probe(struct platform_device *pdev)
* It's assumed that only a single type of gpio controller is available
* on the current machine, so overwriting global data is fine.
*/
- mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type;
+ if (devtype->irq_set_type)
+ mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type;
if (devtype->gpio_dir_out)
gc->direction_output = devtype->gpio_dir_out;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index e0657fc..0232c25a 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -58,7 +58,7 @@
#define PCA_GPIO_MASK 0x00FF
#define PCAL_GPIO_MASK 0x1f
-#define PCAL_PINCTRL_MASK 0xe0
+#define PCAL_PINCTRL_MASK 0x60
#define PCA_INT 0x0100
#define PCA_PCAL 0x0200
diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c
index d6d36d5..b77ea16 100644
--- a/drivers/gpio/gpio-raspberrypi-exp.c
+++ b/drivers/gpio/gpio-raspberrypi-exp.c
@@ -206,6 +206,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
}
fw = rpi_firmware_get(fw_node);
+ of_node_put(fw_node);
if (!fw)
return -EPROBE_DEFER;
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 87c18a5..7f3da34 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -122,7 +122,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
BIT(offs % SYSCON_REG_BITS));
}
- priv->data->set(chip, offset, val);
+ chip->set(chip, offset, val);
return 0;
}
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cf2604e..8edbb3f 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -1265,11 +1265,28 @@ late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
static const struct dmi_system_id run_edge_events_on_boot_blacklist[] = {
{
+ /*
+ * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
+ * a non existing micro-USB-B connector which puts the HDMI
+ * DDC pins in GPIO mode, breaking HDMI support.
+ */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
}
},
+ {
+ /*
+ * The Terra Pad 1061 has a micro-USB-B id-pin handler, which
+ * instead of controlling the actual micro-USB-B turns the 5V
+ * boost for its USB-A connector off. The actual micro-USB-B
+ * connector is wired for charging only.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"),
+ }
+ },
{} /* Terminating entry */
};
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index bbc58021..113852c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -217,6 +217,14 @@ int gpiod_get_direction(struct gpio_desc *desc)
chip = gpiod_to_chip(desc);
offset = gpio_chip_hwgpio(desc);
+ /*
+ * Open drain emulation using input mode may incorrectly report
+ * input here, fix that up.
+ */
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) &&
+ test_bit(FLAG_IS_OUT, &desc->flags))
+ return 0;
+
if (!chip->get_direction)
return status;
@@ -2548,19 +2556,27 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
int gpiod_direction_input(struct gpio_desc *desc)
{
struct gpio_chip *chip;
- int status = -EINVAL;
+ int status = 0;
VALIDATE_DESC(desc);
chip = desc->gdev->chip;
- if (!chip->get || !chip->direction_input) {
+ if (!chip->get && chip->direction_input) {
gpiod_warn(desc,
- "%s: missing get() or direction_input() operations\n",
+ "%s: missing get() and direction_input() operations\n",
__func__);
return -EIO;
}
- status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
+ if (chip->direction_input) {
+ status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
+ } else if (chip->get_direction &&
+ (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) {
+ gpiod_warn(desc,
+ "%s: missing direction_input() operation\n",
+ __func__);
+ return -EIO;
+ }
if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags);
@@ -2582,16 +2598,28 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
struct gpio_chip *gc = desc->gdev->chip;
int val = !!value;
- int ret;
+ int ret = 0;
- if (!gc->set || !gc->direction_output) {
+ if (!gc->set && !gc->direction_output) {
gpiod_warn(desc,
- "%s: missing set() or direction_output() operations\n",
+ "%s: missing set() and direction_output() operations\n",
__func__);
return -EIO;
}
- ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
+ if (gc->direction_output) {
+ ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
+ } else {
+ if (gc->get_direction &&
+ gc->get_direction(gc, gpio_chip_hwgpio(desc))) {
+ gpiod_warn(desc,
+ "%s: missing direction_output() operation\n",
+ __func__);
+ return -EIO;
+ }
+ gc->set(gc, gpio_chip_hwgpio(desc), val);
+ }
+
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(desc_to_gpio(desc), 0, val);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index b80243d..ce7f18c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -264,7 +264,7 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
r = amdgpu_bo_create_list_entry_array(&args->in, &info);
if (r)
- goto error_free;
+ return r;
switch (args->in.operation) {
case AMDGPU_BO_LIST_OP_CREATE:
@@ -277,8 +277,7 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL);
mutex_unlock(&fpriv->bo_list_lock);
if (r < 0) {
- amdgpu_bo_list_put(list);
- return r;
+ goto error_put_list;
}
handle = r;
@@ -300,9 +299,8 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&fpriv->bo_list_lock);
if (IS_ERR(old)) {
- amdgpu_bo_list_put(list);
r = PTR_ERR(old);
- goto error_free;
+ goto error_put_list;
}
amdgpu_bo_list_put(old);
@@ -319,8 +317,10 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
return 0;
+error_put_list:
+ amdgpu_bo_list_put(list);
+
error_free:
- if (info)
- kvfree(info);
+ kvfree(info);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index f823d4b..cf582cc4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -203,7 +203,7 @@ static struct dma_fence *amdgpu_job_run(struct drm_sched_job *sched_job)
struct amdgpu_ring *ring = to_amdgpu_ring(sched_job->sched);
struct dma_fence *fence = NULL, *finished;
struct amdgpu_job *job;
- int r;
+ int r = 0;
job = to_amdgpu_job(sched_job);
finished = &job->base.s_fence->finished;
@@ -228,6 +228,8 @@ static struct dma_fence *amdgpu_job_run(struct drm_sched_job *sched_job)
job->fence = dma_fence_get(fence);
amdgpu_job_free_resources(job);
+
+ fence = r ? ERR_PTR(r) : fence;
return fence;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index b0e14a3..b14ce11 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -428,7 +428,8 @@ static int amdgpu_bo_do_create(struct amdgpu_device *adev,
.interruptible = (bp->type != ttm_bo_type_kernel),
.no_wait_gpu = false,
.resv = bp->resv,
- .flags = TTM_OPT_FLAG_ALLOW_RES_EVICT
+ .flags = bp->type != ttm_bo_type_kernel ?
+ TTM_OPT_FLAG_ALLOW_RES_EVICT : 0
};
struct amdgpu_bo *bo;
unsigned long page_align, size = bp->size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c
index 8904e62..41d3142 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c
@@ -138,6 +138,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
}
dma_fence_put(fence);
+ fence = NULL;
r = amdgpu_bo_kmap(vram_obj, &vram_map);
if (r) {
@@ -183,6 +184,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
}
dma_fence_put(fence);
+ fence = NULL;
r = amdgpu_bo_kmap(gtt_obj[i], >t_map);
if (r) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 49fe508..f67c332 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -700,10 +700,8 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
id->oa_base != job->oa_base ||
id->oa_size != job->oa_size);
bool vm_flush_needed = job->vm_needs_flush;
- bool pasid_mapping_needed = id->pasid != job->pasid ||
- !id->pasid_mapping ||
- !dma_fence_is_signaled(id->pasid_mapping);
struct dma_fence *fence = NULL;
+ bool pasid_mapping_needed = false;
unsigned patch_offset = 0;
int r;
@@ -713,6 +711,12 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
pasid_mapping_needed = true;
}
+ mutex_lock(&id_mgr->lock);
+ if (id->pasid != job->pasid || !id->pasid_mapping ||
+ !dma_fence_is_signaled(id->pasid_mapping))
+ pasid_mapping_needed = true;
+ mutex_unlock(&id_mgr->lock);
+
gds_switch_needed &= !!ring->funcs->emit_gds_switch;
vm_flush_needed &= !!ring->funcs->emit_vm_flush &&
job->vm_pd_addr != AMDGPU_BO_INVALID_OFFSET;
@@ -752,9 +756,11 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
}
if (pasid_mapping_needed) {
+ mutex_lock(&id_mgr->lock);
id->pasid = job->pasid;
dma_fence_put(id->pasid_mapping);
id->pasid_mapping = dma_fence_get(fence);
+ mutex_unlock(&id_mgr->lock);
}
dma_fence_put(fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c
index d5ebe56..a1c9412 100644
--- a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c
+++ b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c
@@ -75,23 +75,29 @@ static void df_v3_6_update_medium_grain_clock_gating(struct amdgpu_device *adev,
{
u32 tmp;
- /* Put DF on broadcast mode */
- adev->df_funcs->enable_broadcast_mode(adev, true);
+ if (adev->cg_flags & AMD_CG_SUPPORT_DF_MGCG) {
+ /* Put DF on broadcast mode */
+ adev->df_funcs->enable_broadcast_mode(adev, true);
- if (enable && (adev->cg_flags & AMD_CG_SUPPORT_DF_MGCG)) {
- tmp = RREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater);
- tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK;
- tmp |= DF_V3_6_MGCG_ENABLE_15_CYCLE_DELAY;
- WREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater, tmp);
- } else {
- tmp = RREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater);
- tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK;
- tmp |= DF_V3_6_MGCG_DISABLE;
- WREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater, tmp);
+ if (enable) {
+ tmp = RREG32_SOC15(DF, 0,
+ mmDF_PIE_AON0_DfGlobalClkGater);
+ tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK;
+ tmp |= DF_V3_6_MGCG_ENABLE_15_CYCLE_DELAY;
+ WREG32_SOC15(DF, 0,
+ mmDF_PIE_AON0_DfGlobalClkGater, tmp);
+ } else {
+ tmp = RREG32_SOC15(DF, 0,
+ mmDF_PIE_AON0_DfGlobalClkGater);
+ tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK;
+ tmp |= DF_V3_6_MGCG_DISABLE;
+ WREG32_SOC15(DF, 0,
+ mmDF_PIE_AON0_DfGlobalClkGater, tmp);
+ }
+
+ /* Exit broadcast mode */
+ adev->df_funcs->enable_broadcast_mode(adev, false);
}
-
- /* Exit broadcast mode */
- adev->df_funcs->enable_broadcast_mode(adev, false);
}
static void df_v3_6_get_clockgating_state(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index 5a9534a8..e1cb7fa 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -6405,7 +6405,23 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr,
bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
bool int_sel = flags & AMDGPU_FENCE_FLAG_INT;
- /* EVENT_WRITE_EOP - flush caches, send int */
+ /* Workaround for cache flush problems. First send a dummy EOP
+ * event down the pipe with seq one below.
+ */
+ amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
+ amdgpu_ring_write(ring, (EOP_TCL1_ACTION_EN |
+ EOP_TC_ACTION_EN |
+ EOP_TC_WB_ACTION_EN |
+ EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) |
+ EVENT_INDEX(5)));
+ amdgpu_ring_write(ring, addr & 0xfffffffc);
+ amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xffff) |
+ DATA_SEL(1) | INT_SEL(0));
+ amdgpu_ring_write(ring, lower_32_bits(seq - 1));
+ amdgpu_ring_write(ring, upper_32_bits(seq - 1));
+
+ /* Then send the real EOP event down the pipe:
+ * EVENT_WRITE_EOP - flush caches, send int */
amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
amdgpu_ring_write(ring, (EOP_TCL1_ACTION_EN |
EOP_TC_ACTION_EN |
@@ -7154,7 +7170,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = {
5 + /* COND_EXEC */
7 + /* PIPELINE_SYNC */
VI_FLUSH_GPU_TLB_NUM_WREG * 5 + 9 + /* VM_FLUSH */
- 8 + /* FENCE for VM_FLUSH */
+ 12 + /* FENCE for VM_FLUSH */
20 + /* GDS switch */
4 + /* double SWITCH_BUFFER,
the first COND_EXEC jump to the place just
@@ -7166,7 +7182,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = {
31 + /* DE_META */
3 + /* CNTX_CTRL */
5 + /* HDP_INVL */
- 8 + 8 + /* FENCE x2 */
+ 12 + 12 + /* FENCE x2 */
2, /* SWITCH_BUFFER */
.emit_ib_size = 4, /* gfx_v8_0_ring_emit_ib_gfx */
.emit_ib = gfx_v8_0_ring_emit_ib_gfx,
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index 7824116..28794b1 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -2187,7 +2187,8 @@ static void gfx_v9_0_init_pg(struct amdgpu_device *adev)
* And it's needed by gfxoff feature.
*/
if (adev->gfx.rlc.is_rlc_v2_1) {
- gfx_v9_1_init_rlc_save_restore_list(adev);
+ if (adev->asic_type == CHIP_VEGA12)
+ gfx_v9_1_init_rlc_save_restore_list(adev);
gfx_v9_0_enable_save_restore_machine(adev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.c b/drivers/gpu/drm/amd/amdgpu/si_ih.c
index 60dad63..e40a3fb 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_ih.c
@@ -62,7 +62,8 @@ static int si_ih_irq_init(struct amdgpu_device *adev)
u64 wptr_off;
si_ih_disable_interrupts(adev);
- WREG32(INTERRUPT_CNTL2, adev->irq.ih.gpu_addr >> 8);
+ /* set dummy read address to dummy page address */
+ WREG32(INTERRUPT_CNTL2, adev->dummy_page_addr >> 8);
interrupt_cntl = RREG32(INTERRUPT_CNTL);
interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
index c56ac47..bc47f6a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
@@ -62,6 +62,11 @@ int kfd_interrupt_init(struct kfd_dev *kfd)
}
kfd->ih_wq = alloc_workqueue("KFD IH", WQ_HIGHPRI, 1);
+ if (unlikely(!kfd->ih_wq)) {
+ kfifo_free(&kfd->ih_fifo);
+ dev_err(kfd_chardev(), "Failed to allocate KFD IH workqueue\n");
+ return -ENOMEM;
+ }
spin_lock_init(&kfd->interrupt_lock);
INIT_WORK(&kfd->interrupt_work, interrupt_wq);
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index 23a7ef9..2f42964 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -348,7 +348,7 @@ bool dc_link_is_dp_sink_present(struct dc_link *link)
if (GPIO_RESULT_OK != dal_ddc_open(
ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) {
- dal_gpio_destroy_ddc(&ddc);
+ dal_ddc_close(ddc);
return present;
}
@@ -1950,7 +1950,7 @@ static bool dp_active_dongle_validate_timing(
break;
}
- if (dongle_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER ||
+ if (dpcd_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER ||
dongle_caps->extendedCapValid == false)
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
index 8def0d9..46c9cb4 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
@@ -433,6 +433,7 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
enum display_dongle_type *dongle = &sink_cap->dongle_type;
uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE];
bool is_type2_dongle = false;
+ int retry_count = 2;
struct dp_hdmi_dongle_signature_data *dongle_signature;
/* Assume we have no valid DP passive dongle connected */
@@ -445,13 +446,24 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
DP_HDMI_DONGLE_ADDRESS,
type2_dongle_buf,
sizeof(type2_dongle_buf))) {
- *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE;
- sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK;
+ /* Passive HDMI dongles can sometimes fail here without retrying*/
+ while (retry_count > 0) {
+ if (i2c_read(ddc,
+ DP_HDMI_DONGLE_ADDRESS,
+ type2_dongle_buf,
+ sizeof(type2_dongle_buf)))
+ break;
+ retry_count--;
+ }
+ if (retry_count == 0) {
+ *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE;
+ sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK;
- CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf),
- "DP-DVI passive dongle %dMhz: ",
- DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000);
- return;
+ CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf),
+ "DP-DVI passive dongle %dMhz: ",
+ DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000);
+ return;
+ }
}
/* Check if Type 2 dongle.*/
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
index 05840f5..122249d 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
@@ -2172,6 +2172,7 @@ static void get_active_converter_info(
uint8_t data, struct dc_link *link)
{
union dp_downstream_port_present ds_port = { .byte = data };
+ memset(&link->dpcd_caps.dongle_caps, 0, sizeof(link->dpcd_caps.dongle_caps));
/* decode converter info*/
if (!ds_port.fields.PORT_PRESENT) {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index d440b28..6896d69 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -1399,9 +1399,9 @@ bool dc_remove_plane_from_context(
* For head pipe detach surfaces from pipe for tail
* pipe just zero it out
*/
- if (!pipe_ctx->top_pipe ||
- (!pipe_ctx->top_pipe->top_pipe &&
+ if (!pipe_ctx->top_pipe || (!pipe_ctx->top_pipe->top_pipe &&
pipe_ctx->top_pipe->stream_res.opp != pipe_ctx->stream_res.opp)) {
+ pipe_ctx->top_pipe = NULL;
pipe_ctx->plane_state = NULL;
pipe_ctx->bottom_pipe = NULL;
} else {
@@ -1803,8 +1803,6 @@ enum dc_status dc_remove_stream_from_ctx(
dc->res_pool->funcs->remove_stream_from_ctx(dc, new_ctx, stream);
memset(del_pipe, 0, sizeof(*del_pipe));
-
- break;
}
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index c7c5050..6bf032e 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -3472,18 +3472,31 @@ static int smu7_get_pp_table_entry(struct pp_hwmgr *hwmgr,
static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
{
+ struct amdgpu_device *adev = hwmgr->adev;
int i;
u32 tmp = 0;
if (!query)
return -EINVAL;
- smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0);
- tmp = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
- *query = tmp;
+ /*
+ * PPSMC_MSG_GetCurrPkgPwr is not supported on:
+ * - Hawaii
+ * - Bonaire
+ * - Fiji
+ * - Tonga
+ */
+ if ((adev->asic_type != CHIP_HAWAII) &&
+ (adev->asic_type != CHIP_BONAIRE) &&
+ (adev->asic_type != CHIP_FIJI) &&
+ (adev->asic_type != CHIP_TONGA)) {
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0);
+ tmp = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+ *query = tmp;
- if (tmp != 0)
- return 0;
+ if (tmp != 0)
+ return 0;
+ }
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogStart);
cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
index fb86c24..ce459ea 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
@@ -4751,9 +4751,7 @@ static void vega10_odn_update_soc_table(struct pp_hwmgr *hwmgr,
if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_sclk;
- for (i = 0; i < podn_vdd_dep->count - 1; i++)
- od_vddc_lookup_table->entries[i].us_vdd = podn_vdd_dep->entries[i].vddc;
- if (od_vddc_lookup_table->entries[i].us_vdd < podn_vdd_dep->entries[i].vddc)
+ for (i = 0; i < podn_vdd_dep->count; i++)
od_vddc_lookup_table->entries[i].us_vdd = podn_vdd_dep->entries[i].vddc;
} else if (type == PP_OD_EDIT_MCLK_VDDC_TABLE) {
podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_mclk;
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index 0444006..e5b3ba73 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -382,7 +382,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
cfg |= ATMEL_HLCDC_LAYER_LAEN;
else
cfg |= ATMEL_HLCDC_LAYER_GAEN |
- ATMEL_HLCDC_LAYER_GA(state->base.alpha >> 8);
+ ATMEL_HLCDC_LAYER_GA(state->base.alpha);
}
if (state->disc_h && state->disc_w)
diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
index f8433c9..cc820e9 100644
--- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
@@ -725,7 +725,9 @@ static int anx78xx_init_pdata(struct anx78xx *anx78xx)
/* 1.0V digital core power regulator */
pdata->dvdd10 = devm_regulator_get(dev, "dvdd10");
if (IS_ERR(pdata->dvdd10)) {
- DRM_ERROR("DVDD10 regulator not found\n");
+ if (PTR_ERR(pdata->dvdd10) != -EPROBE_DEFER)
+ DRM_ERROR("DVDD10 regulator not found\n");
+
return PTR_ERR(pdata->dvdd10);
}
@@ -1341,7 +1343,9 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
err = anx78xx_init_pdata(anx78xx);
if (err) {
- DRM_ERROR("Failed to initialize pdata: %d\n", err);
+ if (err != -EPROBE_DEFER)
+ DRM_ERROR("Failed to initialize pdata: %d\n", err);
+
return err;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 5971976..2a0a165 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -39,6 +39,7 @@
#include <media/cec-notifier.h>
+#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30
#define HDMI_EDID_LEN 512
@@ -320,6 +321,15 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
u8 addr = msgs[0].addr;
int i, ret = 0;
+ if (addr == DDC_CI_ADDR)
+ /*
+ * The internal I2C controller does not support the multi-byte
+ * read and write operations needed for DDC/CI.
+ * TOFIX: Blacklist the DDC/CI address until we filter out
+ * unsupported I2C operations.
+ */
+ return -EOPNOTSUPP;
+
dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
for (i = 0; i < num; i++) {
@@ -1747,7 +1757,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
/* HDMI Initialization Step E - Configure audio */
hdmi_clk_regenerator_update_pixel_clock(hdmi);
- hdmi_enable_audio_clk(hdmi, true);
+ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable);
}
/* not for DVI mode */
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 7794ad2..7632933 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1639,8 +1639,11 @@ static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
if (ret != 1)
DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
- if (txmsg->seqno != -1)
+ if (txmsg->seqno != -1) {
+ WARN_ON((unsigned int)txmsg->seqno >
+ ARRAY_SIZE(txmsg->dst->tx_slots));
txmsg->dst->tx_slots[txmsg->seqno] = NULL;
+ }
}
static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 1c1fee0..5f601e8 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3976,6 +3976,62 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
connector->audio_latency[1]);
}
+static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
+ const u8 *db)
+{
+ u8 dc_mask;
+ struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
+
+ dc_mask = db[7] & DRM_EDID_YCBCR420_DC_MASK;
+ hdmi->y420_dc_modes = dc_mask;
+}
+
+static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
+ const u8 *hf_vsdb)
+{
+ struct drm_display_info *display = &connector->display_info;
+ struct drm_hdmi_info *hdmi = &display->hdmi;
+
+ display->has_hdmi_infoframe = true;
+
+ if (hf_vsdb[6] & 0x80) {
+ hdmi->scdc.supported = true;
+ if (hf_vsdb[6] & 0x40)
+ hdmi->scdc.read_request = true;
+ }
+
+ /*
+ * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
+ * And as per the spec, three factors confirm this:
+ * * Availability of a HF-VSDB block in EDID (check)
+ * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
+ * * SCDC support available (let's check)
+ * Lets check it out.
+ */
+
+ if (hf_vsdb[5]) {
+ /* max clock is 5000 KHz times block value */
+ u32 max_tmds_clock = hf_vsdb[5] * 5000;
+ struct drm_scdc *scdc = &hdmi->scdc;
+
+ if (max_tmds_clock > 340000) {
+ display->max_tmds_clock = max_tmds_clock;
+ DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n",
+ display->max_tmds_clock);
+ }
+
+ if (scdc->supported) {
+ scdc->scrambling.supported = true;
+
+ /* Few sinks support scrambling for cloks < 340M */
+ if ((hf_vsdb[6] & 0x8))
+ scdc->scrambling.low_rates = true;
+ }
+ }
+
+ drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
+}
+
/*
* drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block
* @connector: connector corresponding to the HDMI sink
@@ -4175,33 +4231,6 @@ drm_hdmi_extract_extended_blk_info(struct drm_connector *connector,
}
static void
-parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
-{
- u8 len = cea_db_payload_len(db);
-
- if (len < 7)
- return;
-
- if (db[4] != 1)
- return; /* invalid version */
-
- connector->max_tmds_char = db[5] * 5;
- connector->scdc_present = db[6] & (1 << 7);
- connector->rr_capable = db[6] & (1 << 6);
- connector->flags_3d = db[6] & 0x7;
- connector->supports_scramble = connector->scdc_present &&
- (db[6] & (1 << 3));
-
- DRM_DEBUG_KMS(
- "HDMI v2: max TMDS char %d, scdc %s, rr %s,3D flags 0x%x, scramble %s\n",
- connector->max_tmds_char,
- connector->scdc_present ? "available" : "not available",
- connector->rr_capable ? "capable" : "not capable",
- connector->flags_3d,
- connector->supports_scramble ? "supported" : "not supported");
-}
-
-static void
monitor_name(struct detailed_timing *t, void *data)
{
if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
@@ -4335,7 +4364,8 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
drm_parse_hdmi_vsdb_audio(connector, db);
/* HDMI Forum Vendor-Specific Data Block */
else if (cea_db_is_hdmi_forum_vsdb(db))
- parse_hdmi_hf_vsdb(connector, db);
+ drm_parse_hdmi_forum_vsdb(connector,
+ db);
break;
default:
break;
@@ -4649,62 +4679,6 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode)
}
EXPORT_SYMBOL(drm_default_rgb_quant_range);
-static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
- const u8 *db)
-{
- u8 dc_mask;
- struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
-
- dc_mask = db[7] & DRM_EDID_YCBCR420_DC_MASK;
- hdmi->y420_dc_modes = dc_mask;
-}
-
-static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
- const u8 *hf_vsdb)
-{
- struct drm_display_info *display = &connector->display_info;
- struct drm_hdmi_info *hdmi = &display->hdmi;
-
- display->has_hdmi_infoframe = true;
-
- if (hf_vsdb[6] & 0x80) {
- hdmi->scdc.supported = true;
- if (hf_vsdb[6] & 0x40)
- hdmi->scdc.read_request = true;
- }
-
- /*
- * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
- * And as per the spec, three factors confirm this:
- * * Availability of a HF-VSDB block in EDID (check)
- * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
- * * SCDC support available (let's check)
- * Lets check it out.
- */
-
- if (hf_vsdb[5]) {
- /* max clock is 5000 KHz times block value */
- u32 max_tmds_clock = hf_vsdb[5] * 5000;
- struct drm_scdc *scdc = &hdmi->scdc;
-
- if (max_tmds_clock > 340000) {
- display->max_tmds_clock = max_tmds_clock;
- DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n",
- display->max_tmds_clock);
- }
-
- if (scdc->supported) {
- scdc->scrambling.supported = true;
-
- /* Few sinks support scrambling for cloks < 340M */
- if ((hf_vsdb[6] & 0x8))
- scdc->scrambling.low_rates = true;
- }
- }
-
- drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
-}
-
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
const u8 *hdmi)
{
@@ -4874,7 +4848,8 @@ drm_hdmi_extract_vsdbs_info(struct drm_connector *connector,
}
/* HDMI Forum Vendor-Specific Data Block */
else if (cea_db_is_hdmi_forum_vsdb(db))
- parse_hdmi_hf_vsdb(connector, db);
+ drm_parse_hdmi_forum_vsdb(connector,
+ db);
}
}
}
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index d1859bc..33a72a8 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -1572,7 +1572,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
unsigned int flags, pipe, high_pipe;
if (!dev->irq_enabled)
- return -EINVAL;
+ return -EOPNOTSUPP;
if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
return -EINVAL;
@@ -1813,7 +1813,7 @@ int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
if (!dev->irq_enabled)
- return -EINVAL;
+ return -EOPNOTSUPP;
crtc = drm_crtc_find(dev, file_priv, get_seq->crtc_id);
if (!crtc)
@@ -1871,7 +1871,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
if (!dev->irq_enabled)
- return -EINVAL;
+ return -EOPNOTSUPP;
crtc = drm_crtc_find(dev, file_priv, queue_seq->crtc_id);
if (!crtc)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index 7ba414b5..d71188b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -1292,6 +1292,7 @@ static int gsc_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ component_del(dev, &gsc_component_ops);
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index 1b7fd6a..f73a02a 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -139,6 +139,7 @@ static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit,
s32 freq_error, min_error = 100000;
memset(best_clock, 0, sizeof(*best_clock));
+ memset(&clock, 0, sizeof(clock));
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
for (clock.n = limit->n.min; clock.n <= limit->n.max;
@@ -195,6 +196,7 @@ static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit,
int err = target;
memset(best_clock, 0, sizeof(*best_clock));
+ memset(&clock, 0, sizeof(clock));
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index 3b37893..a9b1500 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -721,7 +721,7 @@ static void i810_dma_dispatch_vertex(struct drm_device *dev,
if (nbox > I810_NR_SAREA_CLIPRECTS)
nbox = I810_NR_SAREA_CLIPRECTS;
- if (used > 4 * 1024)
+ if (used < 0 || used > 4 * 1024)
used = 0;
if (sarea_priv->dirty)
@@ -1041,7 +1041,7 @@ static void i810_dma_dispatch_mc(struct drm_device *dev, struct drm_buf *buf, in
if (u != I810_BUF_CLIENT)
DRM_DEBUG("MC found buffer that isn't mine!\n");
- if (used > 4 * 1024)
+ if (used < 0 || used > 4 * 1024)
used = 0;
sarea_priv->dirty = 0x7f;
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
index 95478db..e4b9eb1f 100644
--- a/drivers/gpu/drm/i915/i915_cmd_parser.c
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -51,13 +51,11 @@
* granting userspace undue privileges. There are three categories of privilege.
*
* First, commands which are explicitly defined as privileged or which should
- * only be used by the kernel driver. The parser generally rejects such
- * commands, though it may allow some from the drm master process.
+ * only be used by the kernel driver. The parser rejects such commands
*
* Second, commands which access registers. To support correct/enhanced
* userspace functionality, particularly certain OpenGL extensions, the parser
- * provides a whitelist of registers which userspace may safely access (for both
- * normal and drm master processes).
+ * provides a whitelist of registers which userspace may safely access
*
* Third, commands which access privileged memory (i.e. GGTT, HWS page, etc).
* The parser always rejects such commands.
@@ -82,9 +80,9 @@
* in the per-engine command tables.
*
* Other command table entries map fairly directly to high level categories
- * mentioned above: rejected, master-only, register whitelist. The parser
- * implements a number of checks, including the privileged memory checks, via a
- * general bitmasking mechanism.
+ * mentioned above: rejected, register whitelist. The parser implements a number
+ * of checks, including the privileged memory checks, via a general bitmasking
+ * mechanism.
*/
/*
@@ -102,8 +100,6 @@ struct drm_i915_cmd_descriptor {
* CMD_DESC_REJECT: The command is never allowed
* CMD_DESC_REGISTER: The command should be checked against the
* register whitelist for the appropriate ring
- * CMD_DESC_MASTER: The command is allowed if the submitting process
- * is the DRM master
*/
u32 flags;
#define CMD_DESC_FIXED (1<<0)
@@ -111,7 +107,6 @@ struct drm_i915_cmd_descriptor {
#define CMD_DESC_REJECT (1<<2)
#define CMD_DESC_REGISTER (1<<3)
#define CMD_DESC_BITMASK (1<<4)
-#define CMD_DESC_MASTER (1<<5)
/*
* The command's unique identification bits and the bitmask to get them.
@@ -192,7 +187,7 @@ struct drm_i915_cmd_table {
#define CMD(op, opm, f, lm, fl, ...) \
{ \
.flags = (fl) | ((f) ? CMD_DESC_FIXED : 0), \
- .cmd = { (op), ~0u << (opm) }, \
+ .cmd = { (op & ~0u << (opm)), ~0u << (opm) }, \
.length = { (lm) }, \
__VA_ARGS__ \
}
@@ -207,14 +202,13 @@ struct drm_i915_cmd_table {
#define R CMD_DESC_REJECT
#define W CMD_DESC_REGISTER
#define B CMD_DESC_BITMASK
-#define M CMD_DESC_MASTER
/* Command Mask Fixed Len Action
---------------------------------------------------------- */
-static const struct drm_i915_cmd_descriptor common_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_common_cmds[] = {
CMD( MI_NOOP, SMI, F, 1, S ),
CMD( MI_USER_INTERRUPT, SMI, F, 1, R ),
- CMD( MI_WAIT_FOR_EVENT, SMI, F, 1, M ),
+ CMD( MI_WAIT_FOR_EVENT, SMI, F, 1, R ),
CMD( MI_ARB_CHECK, SMI, F, 1, S ),
CMD( MI_REPORT_HEAD, SMI, F, 1, S ),
CMD( MI_SUSPEND_FLUSH, SMI, F, 1, S ),
@@ -244,7 +238,7 @@ static const struct drm_i915_cmd_descriptor common_cmds[] = {
CMD( MI_BATCH_BUFFER_START, SMI, !F, 0xFF, S ),
};
-static const struct drm_i915_cmd_descriptor render_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_render_cmds[] = {
CMD( MI_FLUSH, SMI, F, 1, S ),
CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
CMD( MI_PREDICATE, SMI, F, 1, S ),
@@ -311,7 +305,7 @@ static const struct drm_i915_cmd_descriptor hsw_render_cmds[] = {
CMD( MI_URB_ATOMIC_ALLOC, SMI, F, 1, S ),
CMD( MI_SET_APPID, SMI, F, 1, S ),
CMD( MI_RS_CONTEXT, SMI, F, 1, S ),
- CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, M ),
+ CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, R ),
CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, R ),
CMD( MI_LOAD_REGISTER_REG, SMI, !F, 0xFF, W,
.reg = { .offset = 1, .mask = 0x007FFFFC, .step = 1 } ),
@@ -328,7 +322,7 @@ static const struct drm_i915_cmd_descriptor hsw_render_cmds[] = {
CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS, S3D, !F, 0x1FF, S ),
};
-static const struct drm_i915_cmd_descriptor video_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_video_cmds[] = {
CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
CMD( MI_SET_APPID, SMI, F, 1, S ),
CMD( MI_STORE_DWORD_IMM, SMI, !F, 0xFF, B,
@@ -372,7 +366,7 @@ static const struct drm_i915_cmd_descriptor video_cmds[] = {
CMD( MFX_WAIT, SMFX, F, 1, S ),
};
-static const struct drm_i915_cmd_descriptor vecs_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_vecs_cmds[] = {
CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
CMD( MI_SET_APPID, SMI, F, 1, S ),
CMD( MI_STORE_DWORD_IMM, SMI, !F, 0xFF, B,
@@ -410,7 +404,7 @@ static const struct drm_i915_cmd_descriptor vecs_cmds[] = {
}}, ),
};
-static const struct drm_i915_cmd_descriptor blt_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_blt_cmds[] = {
CMD( MI_DISPLAY_FLIP, SMI, !F, 0xFF, R ),
CMD( MI_STORE_DWORD_IMM, SMI, !F, 0x3FF, B,
.bits = {{
@@ -444,10 +438,64 @@ static const struct drm_i915_cmd_descriptor blt_cmds[] = {
};
static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
- CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, M ),
+ CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, R ),
CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, R ),
};
+/*
+ * For Gen9 we can still rely on the h/w to enforce cmd security, and only
+ * need to re-enforce the register access checks. We therefore only need to
+ * teach the cmdparser how to find the end of each command, and identify
+ * register accesses. The table doesn't need to reject any commands, and so
+ * the only commands listed here are:
+ * 1) Those that touch registers
+ * 2) Those that do not have the default 8-bit length
+ *
+ * Note that the default MI length mask chosen for this table is 0xFF, not
+ * the 0x3F used on older devices. This is because the vast majority of MI
+ * cmds on Gen9 use a standard 8-bit Length field.
+ * All the Gen9 blitter instructions are standard 0xFF length mask, and
+ * none allow access to non-general registers, so in fact no BLT cmds are
+ * included in the table at all.
+ *
+ */
+static const struct drm_i915_cmd_descriptor gen9_blt_cmds[] = {
+ CMD( MI_NOOP, SMI, F, 1, S ),
+ CMD( MI_USER_INTERRUPT, SMI, F, 1, S ),
+ CMD( MI_WAIT_FOR_EVENT, SMI, F, 1, S ),
+ CMD( MI_FLUSH, SMI, F, 1, S ),
+ CMD( MI_ARB_CHECK, SMI, F, 1, S ),
+ CMD( MI_REPORT_HEAD, SMI, F, 1, S ),
+ CMD( MI_ARB_ON_OFF, SMI, F, 1, S ),
+ CMD( MI_SUSPEND_FLUSH, SMI, F, 1, S ),
+ CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, S ),
+ CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, S ),
+ CMD( MI_STORE_DWORD_IMM, SMI, !F, 0x3FF, S ),
+ CMD( MI_LOAD_REGISTER_IMM(1), SMI, !F, 0xFF, W,
+ .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 2 } ),
+ CMD( MI_UPDATE_GTT, SMI, !F, 0x3FF, S ),
+ CMD( MI_STORE_REGISTER_MEM_GEN8, SMI, F, 4, W,
+ .reg = { .offset = 1, .mask = 0x007FFFFC } ),
+ CMD( MI_FLUSH_DW, SMI, !F, 0x3F, S ),
+ CMD( MI_LOAD_REGISTER_MEM_GEN8, SMI, F, 4, W,
+ .reg = { .offset = 1, .mask = 0x007FFFFC } ),
+ CMD( MI_LOAD_REGISTER_REG, SMI, !F, 0xFF, W,
+ .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 1 } ),
+
+ /*
+ * We allow BB_START but apply further checks. We just sanitize the
+ * basic fields here.
+ */
+#define MI_BB_START_OPERAND_MASK GENMASK(SMI-1, 0)
+#define MI_BB_START_OPERAND_EXPECT (MI_BATCH_PPGTT_HSW | 1)
+ CMD( MI_BATCH_BUFFER_START_GEN8, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_BB_START_OPERAND_MASK,
+ .expected = MI_BB_START_OPERAND_EXPECT,
+ }}, ),
+};
+
static const struct drm_i915_cmd_descriptor noop_desc =
CMD(MI_NOOP, SMI, F, 1, S);
@@ -461,40 +509,44 @@ static const struct drm_i915_cmd_descriptor noop_desc =
#undef R
#undef W
#undef B
-#undef M
-static const struct drm_i915_cmd_table gen7_render_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { render_cmds, ARRAY_SIZE(render_cmds) },
+static const struct drm_i915_cmd_table gen7_render_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_render_cmds, ARRAY_SIZE(gen7_render_cmds) },
};
-static const struct drm_i915_cmd_table hsw_render_ring_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { render_cmds, ARRAY_SIZE(render_cmds) },
+static const struct drm_i915_cmd_table hsw_render_ring_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_render_cmds, ARRAY_SIZE(gen7_render_cmds) },
{ hsw_render_cmds, ARRAY_SIZE(hsw_render_cmds) },
};
-static const struct drm_i915_cmd_table gen7_video_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { video_cmds, ARRAY_SIZE(video_cmds) },
+static const struct drm_i915_cmd_table gen7_video_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_video_cmds, ARRAY_SIZE(gen7_video_cmds) },
};
-static const struct drm_i915_cmd_table hsw_vebox_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { vecs_cmds, ARRAY_SIZE(vecs_cmds) },
+static const struct drm_i915_cmd_table hsw_vebox_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_vecs_cmds, ARRAY_SIZE(gen7_vecs_cmds) },
};
-static const struct drm_i915_cmd_table gen7_blt_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { blt_cmds, ARRAY_SIZE(blt_cmds) },
+static const struct drm_i915_cmd_table gen7_blt_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_blt_cmds, ARRAY_SIZE(gen7_blt_cmds) },
};
-static const struct drm_i915_cmd_table hsw_blt_ring_cmds[] = {
- { common_cmds, ARRAY_SIZE(common_cmds) },
- { blt_cmds, ARRAY_SIZE(blt_cmds) },
+static const struct drm_i915_cmd_table hsw_blt_ring_cmd_table[] = {
+ { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+ { gen7_blt_cmds, ARRAY_SIZE(gen7_blt_cmds) },
{ hsw_blt_cmds, ARRAY_SIZE(hsw_blt_cmds) },
};
+static const struct drm_i915_cmd_table gen9_blt_cmd_table[] = {
+ { gen9_blt_cmds, ARRAY_SIZE(gen9_blt_cmds) },
+};
+
+
/*
* Register whitelists, sorted by increasing register offset.
*/
@@ -610,17 +662,27 @@ static const struct drm_i915_reg_descriptor gen7_blt_regs[] = {
REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
};
-static const struct drm_i915_reg_descriptor ivb_master_regs[] = {
- REG32(FORCEWAKE_MT),
- REG32(DERRMR),
- REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_A)),
- REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_B)),
- REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_C)),
-};
-
-static const struct drm_i915_reg_descriptor hsw_master_regs[] = {
- REG32(FORCEWAKE_MT),
- REG32(DERRMR),
+static const struct drm_i915_reg_descriptor gen9_blt_regs[] = {
+ REG64_IDX(RING_TIMESTAMP, RENDER_RING_BASE),
+ REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE),
+ REG32(BCS_SWCTRL),
+ REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
+ REG64_IDX(BCS_GPR, 0),
+ REG64_IDX(BCS_GPR, 1),
+ REG64_IDX(BCS_GPR, 2),
+ REG64_IDX(BCS_GPR, 3),
+ REG64_IDX(BCS_GPR, 4),
+ REG64_IDX(BCS_GPR, 5),
+ REG64_IDX(BCS_GPR, 6),
+ REG64_IDX(BCS_GPR, 7),
+ REG64_IDX(BCS_GPR, 8),
+ REG64_IDX(BCS_GPR, 9),
+ REG64_IDX(BCS_GPR, 10),
+ REG64_IDX(BCS_GPR, 11),
+ REG64_IDX(BCS_GPR, 12),
+ REG64_IDX(BCS_GPR, 13),
+ REG64_IDX(BCS_GPR, 14),
+ REG64_IDX(BCS_GPR, 15),
};
#undef REG64
@@ -629,28 +691,27 @@ static const struct drm_i915_reg_descriptor hsw_master_regs[] = {
struct drm_i915_reg_table {
const struct drm_i915_reg_descriptor *regs;
int num_regs;
- bool master;
};
static const struct drm_i915_reg_table ivb_render_reg_tables[] = {
- { gen7_render_regs, ARRAY_SIZE(gen7_render_regs), false },
- { ivb_master_regs, ARRAY_SIZE(ivb_master_regs), true },
+ { gen7_render_regs, ARRAY_SIZE(gen7_render_regs) },
};
static const struct drm_i915_reg_table ivb_blt_reg_tables[] = {
- { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs), false },
- { ivb_master_regs, ARRAY_SIZE(ivb_master_regs), true },
+ { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs) },
};
static const struct drm_i915_reg_table hsw_render_reg_tables[] = {
- { gen7_render_regs, ARRAY_SIZE(gen7_render_regs), false },
- { hsw_render_regs, ARRAY_SIZE(hsw_render_regs), false },
- { hsw_master_regs, ARRAY_SIZE(hsw_master_regs), true },
+ { gen7_render_regs, ARRAY_SIZE(gen7_render_regs) },
+ { hsw_render_regs, ARRAY_SIZE(hsw_render_regs) },
};
static const struct drm_i915_reg_table hsw_blt_reg_tables[] = {
- { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs), false },
- { hsw_master_regs, ARRAY_SIZE(hsw_master_regs), true },
+ { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs) },
+};
+
+static const struct drm_i915_reg_table gen9_blt_reg_tables[] = {
+ { gen9_blt_regs, ARRAY_SIZE(gen9_blt_regs) },
};
static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
@@ -708,6 +769,17 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
return 0;
}
+static u32 gen9_blt_get_cmd_length_mask(u32 cmd_header)
+{
+ u32 client = cmd_header >> INSTR_CLIENT_SHIFT;
+
+ if (client == INSTR_MI_CLIENT || client == INSTR_BC_CLIENT)
+ return 0xFF;
+
+ DRM_DEBUG_DRIVER("CMD: Abnormal blt cmd length! 0x%08X\n", cmd_header);
+ return 0;
+}
+
static bool validate_cmds_sorted(const struct intel_engine_cs *engine,
const struct drm_i915_cmd_table *cmd_tables,
int cmd_table_count)
@@ -865,18 +937,19 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine)
int cmd_table_count;
int ret;
- if (!IS_GEN7(engine->i915))
+ if (!IS_GEN7(engine->i915) && !(IS_GEN9(engine->i915) &&
+ engine->id == BCS))
return;
switch (engine->id) {
case RCS:
if (IS_HASWELL(engine->i915)) {
- cmd_tables = hsw_render_ring_cmds;
+ cmd_tables = hsw_render_ring_cmd_table;
cmd_table_count =
- ARRAY_SIZE(hsw_render_ring_cmds);
+ ARRAY_SIZE(hsw_render_ring_cmd_table);
} else {
- cmd_tables = gen7_render_cmds;
- cmd_table_count = ARRAY_SIZE(gen7_render_cmds);
+ cmd_tables = gen7_render_cmd_table;
+ cmd_table_count = ARRAY_SIZE(gen7_render_cmd_table);
}
if (IS_HASWELL(engine->i915)) {
@@ -886,36 +959,46 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine)
engine->reg_tables = ivb_render_reg_tables;
engine->reg_table_count = ARRAY_SIZE(ivb_render_reg_tables);
}
-
engine->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
break;
case VCS:
- cmd_tables = gen7_video_cmds;
- cmd_table_count = ARRAY_SIZE(gen7_video_cmds);
+ cmd_tables = gen7_video_cmd_table;
+ cmd_table_count = ARRAY_SIZE(gen7_video_cmd_table);
engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
break;
case BCS:
- if (IS_HASWELL(engine->i915)) {
- cmd_tables = hsw_blt_ring_cmds;
- cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmds);
+ engine->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
+ if (IS_GEN9(engine->i915)) {
+ cmd_tables = gen9_blt_cmd_table;
+ cmd_table_count = ARRAY_SIZE(gen9_blt_cmd_table);
+ engine->get_cmd_length_mask =
+ gen9_blt_get_cmd_length_mask;
+
+ /* BCS Engine unsafe without parser */
+ engine->flags |= I915_ENGINE_REQUIRES_CMD_PARSER;
+ } else if (IS_HASWELL(engine->i915)) {
+ cmd_tables = hsw_blt_ring_cmd_table;
+ cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmd_table);
} else {
- cmd_tables = gen7_blt_cmds;
- cmd_table_count = ARRAY_SIZE(gen7_blt_cmds);
+ cmd_tables = gen7_blt_cmd_table;
+ cmd_table_count = ARRAY_SIZE(gen7_blt_cmd_table);
}
- if (IS_HASWELL(engine->i915)) {
+ if (IS_GEN9(engine->i915)) {
+ engine->reg_tables = gen9_blt_reg_tables;
+ engine->reg_table_count =
+ ARRAY_SIZE(gen9_blt_reg_tables);
+ } else if (IS_HASWELL(engine->i915)) {
engine->reg_tables = hsw_blt_reg_tables;
engine->reg_table_count = ARRAY_SIZE(hsw_blt_reg_tables);
} else {
engine->reg_tables = ivb_blt_reg_tables;
engine->reg_table_count = ARRAY_SIZE(ivb_blt_reg_tables);
}
-
- engine->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
break;
case VECS:
- cmd_tables = hsw_vebox_cmds;
- cmd_table_count = ARRAY_SIZE(hsw_vebox_cmds);
+ cmd_tables = hsw_vebox_cmd_table;
+ cmd_table_count = ARRAY_SIZE(hsw_vebox_cmd_table);
/* VECS can use the same length_mask function as VCS */
engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
break;
@@ -941,7 +1024,7 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine)
return;
}
- engine->flags |= I915_ENGINE_NEEDS_CMD_PARSER;
+ engine->flags |= I915_ENGINE_USING_CMD_PARSER;
}
/**
@@ -953,7 +1036,7 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine)
*/
void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine)
{
- if (!intel_engine_needs_cmd_parser(engine))
+ if (!intel_engine_using_cmd_parser(engine))
return;
fini_hash_table(engine);
@@ -1027,22 +1110,16 @@ __find_reg(const struct drm_i915_reg_descriptor *table, int count, u32 addr)
}
static const struct drm_i915_reg_descriptor *
-find_reg(const struct intel_engine_cs *engine, bool is_master, u32 addr)
+find_reg(const struct intel_engine_cs *engine, u32 addr)
{
const struct drm_i915_reg_table *table = engine->reg_tables;
+ const struct drm_i915_reg_descriptor *reg = NULL;
int count = engine->reg_table_count;
- for (; count > 0; ++table, --count) {
- if (!table->master || is_master) {
- const struct drm_i915_reg_descriptor *reg;
+ for (; !reg && (count > 0); ++table, --count)
+ reg = __find_reg(table->regs, table->num_regs, addr);
- reg = __find_reg(table->regs, table->num_regs, addr);
- if (reg != NULL)
- return reg;
- }
- }
-
- return NULL;
+ return reg;
}
/* Returns a vmap'd pointer to dst_obj, which the caller must unmap */
@@ -1127,8 +1204,7 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
static bool check_cmd(const struct intel_engine_cs *engine,
const struct drm_i915_cmd_descriptor *desc,
- const u32 *cmd, u32 length,
- const bool is_master)
+ const u32 *cmd, u32 length)
{
if (desc->flags & CMD_DESC_SKIP)
return true;
@@ -1138,12 +1214,6 @@ static bool check_cmd(const struct intel_engine_cs *engine,
return false;
}
- if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
- DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
- *cmd);
- return false;
- }
-
if (desc->flags & CMD_DESC_REGISTER) {
/*
* Get the distance between individual register offset
@@ -1157,7 +1227,7 @@ static bool check_cmd(const struct intel_engine_cs *engine,
offset += step) {
const u32 reg_addr = cmd[offset] & desc->reg.mask;
const struct drm_i915_reg_descriptor *reg =
- find_reg(engine, is_master, reg_addr);
+ find_reg(engine, reg_addr);
if (!reg) {
DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (%s)\n",
@@ -1235,16 +1305,112 @@ static bool check_cmd(const struct intel_engine_cs *engine,
return true;
}
+static int check_bbstart(const struct i915_gem_context *ctx,
+ u32 *cmd, u32 offset, u32 length,
+ u32 batch_len,
+ u64 batch_start,
+ u64 shadow_batch_start)
+{
+ u64 jump_offset, jump_target;
+ u32 target_cmd_offset, target_cmd_index;
+
+ /* For igt compatibility on older platforms */
+ if (CMDPARSER_USES_GGTT(ctx->i915)) {
+ DRM_DEBUG("CMD: Rejecting BB_START for ggtt based submission\n");
+ return -EACCES;
+ }
+
+ if (length != 3) {
+ DRM_DEBUG("CMD: Recursive BB_START with bad length(%u)\n",
+ length);
+ return -EINVAL;
+ }
+
+ jump_target = *(u64*)(cmd+1);
+ jump_offset = jump_target - batch_start;
+
+ /*
+ * Any underflow of jump_target is guaranteed to be outside the range
+ * of a u32, so >= test catches both too large and too small
+ */
+ if (jump_offset >= batch_len) {
+ DRM_DEBUG("CMD: BB_START to 0x%llx jumps out of BB\n",
+ jump_target);
+ return -EINVAL;
+ }
+
+ /*
+ * This cannot overflow a u32 because we already checked jump_offset
+ * is within the BB, and the batch_len is a u32
+ */
+ target_cmd_offset = lower_32_bits(jump_offset);
+ target_cmd_index = target_cmd_offset / sizeof(u32);
+
+ *(u64*)(cmd + 1) = shadow_batch_start + target_cmd_offset;
+
+ if (target_cmd_index == offset)
+ return 0;
+
+ if (ctx->jump_whitelist_cmds <= target_cmd_index) {
+ DRM_DEBUG("CMD: Rejecting BB_START - truncated whitelist array\n");
+ return -EINVAL;
+ } else if (!test_bit(target_cmd_index, ctx->jump_whitelist)) {
+ DRM_DEBUG("CMD: BB_START to 0x%llx not a previously executed cmd\n",
+ jump_target);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void init_whitelist(struct i915_gem_context *ctx, u32 batch_len)
+{
+ const u32 batch_cmds = DIV_ROUND_UP(batch_len, sizeof(u32));
+ const u32 exact_size = BITS_TO_LONGS(batch_cmds);
+ u32 next_size = BITS_TO_LONGS(roundup_pow_of_two(batch_cmds));
+ unsigned long *next_whitelist;
+
+ if (CMDPARSER_USES_GGTT(ctx->i915))
+ return;
+
+ if (batch_cmds <= ctx->jump_whitelist_cmds) {
+ bitmap_zero(ctx->jump_whitelist, batch_cmds);
+ return;
+ }
+
+again:
+ next_whitelist = kcalloc(next_size, sizeof(long), GFP_KERNEL);
+ if (next_whitelist) {
+ kfree(ctx->jump_whitelist);
+ ctx->jump_whitelist = next_whitelist;
+ ctx->jump_whitelist_cmds =
+ next_size * BITS_PER_BYTE * sizeof(long);
+ return;
+ }
+
+ if (next_size > exact_size) {
+ next_size = exact_size;
+ goto again;
+ }
+
+ DRM_DEBUG("CMD: Failed to extend whitelist. BB_START may be disallowed\n");
+ bitmap_zero(ctx->jump_whitelist, ctx->jump_whitelist_cmds);
+
+ return;
+}
+
#define LENGTH_BIAS 2
/**
* i915_parse_cmds() - parse a submitted batch buffer for privilege violations
+ * @ctx: the context in which the batch is to execute
* @engine: the engine on which the batch is to execute
* @batch_obj: the batch buffer in question
- * @shadow_batch_obj: copy of the batch buffer in question
+ * @batch_start: Canonical base address of batch
* @batch_start_offset: byte offset in the batch at which execution starts
* @batch_len: length of the commands in batch_obj
- * @is_master: is the submitting process the drm master?
+ * @shadow_batch_obj: copy of the batch buffer in question
+ * @shadow_batch_start: Canonical base address of shadow_batch_obj
*
* Parses the specified batch buffer looking for privilege violations as
* described in the overview.
@@ -1252,14 +1418,17 @@ static bool check_cmd(const struct intel_engine_cs *engine,
* Return: non-zero if the parser finds violations or otherwise fails; -EACCES
* if the batch appears legal but should use hardware parsing
*/
-int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+
+int intel_engine_cmd_parser(struct i915_gem_context *ctx,
+ struct intel_engine_cs *engine,
struct drm_i915_gem_object *batch_obj,
- struct drm_i915_gem_object *shadow_batch_obj,
+ u64 batch_start,
u32 batch_start_offset,
u32 batch_len,
- bool is_master)
+ struct drm_i915_gem_object *shadow_batch_obj,
+ u64 shadow_batch_start)
{
- u32 *cmd, *batch_end;
+ u32 *cmd, *batch_end, offset = 0;
struct drm_i915_cmd_descriptor default_desc = noop_desc;
const struct drm_i915_cmd_descriptor *desc = &default_desc;
bool needs_clflush_after = false;
@@ -1273,6 +1442,8 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
return PTR_ERR(cmd);
}
+ init_whitelist(ctx, batch_len);
+
/*
* We use the batch length as size because the shadow object is as
* large or larger and copy_batch() will write MI_NOPs to the extra
@@ -1282,31 +1453,15 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
do {
u32 length;
- if (*cmd == MI_BATCH_BUFFER_END) {
- if (needs_clflush_after) {
- void *ptr = page_mask_bits(shadow_batch_obj->mm.mapping);
- drm_clflush_virt_range(ptr,
- (void *)(cmd + 1) - ptr);
- }
+ if (*cmd == MI_BATCH_BUFFER_END)
break;
- }
desc = find_cmd(engine, *cmd, desc, &default_desc);
if (!desc) {
DRM_DEBUG_DRIVER("CMD: Unrecognized command: 0x%08X\n",
*cmd);
ret = -EINVAL;
- break;
- }
-
- /*
- * If the batch buffer contains a chained batch, return an
- * error that tells the caller to abort and dispatch the
- * workload as a non-secure batch.
- */
- if (desc->cmd.value == MI_BATCH_BUFFER_START) {
- ret = -EACCES;
- break;
+ goto err;
}
if (desc->flags & CMD_DESC_FIXED)
@@ -1320,22 +1475,43 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
length,
batch_end - cmd);
ret = -EINVAL;
+ goto err;
+ }
+
+ if (!check_cmd(engine, desc, cmd, length)) {
+ ret = -EACCES;
+ goto err;
+ }
+
+ if (desc->cmd.value == MI_BATCH_BUFFER_START) {
+ ret = check_bbstart(ctx, cmd, offset, length,
+ batch_len, batch_start,
+ shadow_batch_start);
+
+ if (ret)
+ goto err;
break;
}
- if (!check_cmd(engine, desc, cmd, length, is_master)) {
- ret = -EACCES;
- break;
- }
+ if (ctx->jump_whitelist_cmds > offset)
+ set_bit(offset, ctx->jump_whitelist);
cmd += length;
+ offset += length;
if (cmd >= batch_end) {
DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n");
ret = -EINVAL;
- break;
+ goto err;
}
} while (1);
+ if (needs_clflush_after) {
+ void *ptr = page_mask_bits(shadow_batch_obj->mm.mapping);
+
+ drm_clflush_virt_range(ptr, (void *)(cmd + 1) - ptr);
+ }
+
+err:
i915_gem_object_unpin_map(shadow_batch_obj);
return ret;
}
@@ -1357,7 +1533,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv)
/* If the command parser is not enabled, report 0 - unsupported */
for_each_engine(engine, dev_priv, id) {
- if (intel_engine_needs_cmd_parser(engine)) {
+ if (intel_engine_using_cmd_parser(engine)) {
active = true;
break;
}
@@ -1382,6 +1558,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv)
* the parser enabled.
* 9. Don't whitelist or handle oacontrol specially, as ownership
* for oacontrol state is moving to i915-perf.
+ * 10. Support for Gen9 BCS Parsing
*/
- return 9;
+ return 10;
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index a4b4ab7..b0d76a7 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -351,7 +351,7 @@ static int i915_getparam_ioctl(struct drm_device *dev, void *data,
value = HAS_LEGACY_SEMAPHORES(dev_priv);
break;
case I915_PARAM_HAS_SECURE_BATCHES:
- value = capable(CAP_SYS_ADMIN);
+ value = HAS_SECURE_BATCHES(dev_priv) && capable(CAP_SYS_ADMIN);
break;
case I915_PARAM_CMD_PARSER_VERSION:
value = i915_cmd_parser_get_version(dev_priv);
@@ -1627,6 +1627,7 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
i915_gem_suspend_late(dev_priv);
intel_display_set_init_power(dev_priv, false);
+ i915_rc6_ctx_wa_suspend(dev_priv);
intel_uncore_suspend(dev_priv);
/*
@@ -1853,6 +1854,8 @@ static int i915_drm_resume_early(struct drm_device *dev)
else
intel_display_set_init_power(dev_priv, true);
+ i915_rc6_ctx_wa_resume(dev_priv);
+
intel_engines_sanitize(dev_priv);
enable_rpm_wakeref_asserts(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d6c25bea..db2e9af 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -801,6 +801,7 @@ struct intel_rps {
struct intel_rc6 {
bool enabled;
+ bool ctx_corrupted;
u64 prev_hw_residency[4];
u64 cur_residency[4];
};
@@ -2496,6 +2497,12 @@ intel_info(const struct drm_i915_private *dev_priv)
#define IS_GEN9_LP(dev_priv) (IS_GEN9(dev_priv) && IS_LP(dev_priv))
#define IS_GEN9_BC(dev_priv) (IS_GEN9(dev_priv) && !IS_LP(dev_priv))
+/*
+ * The Gen7 cmdparser copies the scanned buffer to the ggtt for execution
+ * All later gens can run the final buffer from the ppgtt
+ */
+#define CMDPARSER_USES_GGTT(dev_priv) IS_GEN7(dev_priv)
+
#define ENGINE_MASK(id) BIT(id)
#define RENDER_RING ENGINE_MASK(RCS)
#define BSD_RING ENGINE_MASK(VCS)
@@ -2517,6 +2524,8 @@ intel_info(const struct drm_i915_private *dev_priv)
#define HAS_LEGACY_SEMAPHORES(dev_priv) IS_GEN7(dev_priv)
+#define HAS_SECURE_BATCHES(dev_priv) (INTEL_GEN(dev_priv) < 6)
+
#define HAS_LLC(dev_priv) ((dev_priv)->info.has_llc)
#define HAS_SNOOP(dev_priv) ((dev_priv)->info.has_snoop)
#define HAS_EDRAM(dev_priv) (!!((dev_priv)->edram_cap & EDRAM_ENABLED))
@@ -2549,10 +2558,12 @@ intel_info(const struct drm_i915_private *dev_priv)
/* Early gen2 have a totally busted CS tlb and require pinned batches. */
#define HAS_BROKEN_CS_TLB(dev_priv) (IS_I830(dev_priv) || IS_I845G(dev_priv))
+#define NEEDS_RC6_CTX_CORRUPTION_WA(dev_priv) \
+ (IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) == 9)
+
/* WaRsDisableCoarsePowerGating:skl,cnl */
#define NEEDS_WaRsDisableCoarsePowerGating(dev_priv) \
- (IS_CANNONLAKE(dev_priv) || \
- IS_SKL_GT3(dev_priv) || IS_SKL_GT4(dev_priv))
+ (IS_CANNONLAKE(dev_priv) || INTEL_GEN(dev_priv) == 9)
#define HAS_GMBUS_IRQ(dev_priv) (INTEL_GEN(dev_priv) >= 4)
#define HAS_GMBUS_BURST_READ(dev_priv) (INTEL_GEN(dev_priv) >= 10 || \
@@ -2944,6 +2955,14 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
u64 alignment,
u64 flags);
+struct i915_vma * __must_check
+i915_gem_object_pin(struct drm_i915_gem_object *obj,
+ struct i915_address_space *vm,
+ const struct i915_ggtt_view *view,
+ u64 size,
+ u64 alignment,
+ u64 flags);
+
int i915_gem_object_unbind(struct drm_i915_gem_object *obj);
void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
@@ -3337,12 +3356,14 @@ const char *i915_cache_level_str(struct drm_i915_private *i915, int type);
int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv);
void intel_engine_init_cmd_parser(struct intel_engine_cs *engine);
void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine);
-int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+int intel_engine_cmd_parser(struct i915_gem_context *cxt,
+ struct intel_engine_cs *engine,
struct drm_i915_gem_object *batch_obj,
- struct drm_i915_gem_object *shadow_batch_obj,
+ u64 user_batch_start,
u32 batch_start_offset,
u32 batch_len,
- bool is_master);
+ struct drm_i915_gem_object *shadow_batch_obj,
+ u64 shadow_batch_start);
/* i915_perf.c */
extern void i915_perf_init(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 9372877..c7d05ac 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -174,6 +174,11 @@ static u32 __i915_gem_park(struct drm_i915_private *i915)
if (INTEL_GEN(i915) >= 6)
gen6_rps_idle(i915);
+ if (NEEDS_RC6_CTX_CORRUPTION_WA(i915)) {
+ i915_rc6_ctx_wa_check(i915);
+ intel_uncore_forcewake_put(i915, FORCEWAKE_ALL);
+ }
+
intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ);
intel_runtime_pm_put(i915);
@@ -220,6 +225,9 @@ void i915_gem_unpark(struct drm_i915_private *i915)
*/
intel_display_power_get(i915, POWER_DOMAIN_GT_IRQ);
+ if (NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+ intel_uncore_forcewake_get(i915, FORCEWAKE_ALL);
+
i915->gt.awake = true;
if (unlikely(++i915->gt.epoch == 0)) /* keep 0 as invalid */
i915->gt.epoch = 1;
@@ -4425,6 +4433,20 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
struct i915_address_space *vm = &dev_priv->ggtt.vm;
+
+ return i915_gem_object_pin(obj, vm, view, size, alignment,
+ flags | PIN_GLOBAL);
+}
+
+struct i915_vma *
+i915_gem_object_pin(struct drm_i915_gem_object *obj,
+ struct i915_address_space *vm,
+ const struct i915_ggtt_view *view,
+ u64 size,
+ u64 alignment,
+ u64 flags)
+{
+ struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
struct i915_vma *vma;
int ret;
@@ -4488,7 +4510,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
return ERR_PTR(ret);
}
- ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
+ ret = i915_vma_pin(vma, size, alignment, flags);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index b10770c..7a0e6db 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -124,6 +124,8 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
i915_ppgtt_put(ctx->ppgtt);
+ kfree(ctx->jump_whitelist);
+
for (n = 0; n < ARRAY_SIZE(ctx->__engine); n++) {
struct intel_context *ce = &ctx->__engine[n];
@@ -339,6 +341,9 @@ __create_hw_context(struct drm_i915_private *dev_priv,
else
ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
+ ctx->jump_whitelist = NULL;
+ ctx->jump_whitelist_cmds = 0;
+
return ctx;
err_pid:
diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h
index b116e49..834d395 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.h
+++ b/drivers/gpu/drm/i915/i915_gem_context.h
@@ -183,6 +183,12 @@ struct i915_gem_context {
/** remap_slice: Bitmask of cache lines that need remapping */
u8 remap_slice;
+ /** jump_whitelist: Bit array for tracking cmds during cmdparsing */
+ unsigned long *jump_whitelist;
+
+ /** jump_whitelist_cmds: No of cmd slots available */
+ u32 jump_whitelist_cmds;
+
/** handles_vma: rbtree to look up our context specific obj/vma for
* the user handle. (user handles are per fd, but the binding is
* per vm, which may be one per context or shared with the global GTT)
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 679bbae..f08c547 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -309,7 +309,9 @@ static inline u64 gen8_noncanonical_addr(u64 address)
static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
{
- return intel_engine_needs_cmd_parser(eb->engine) && eb->batch_len;
+ return intel_engine_requires_cmd_parser(eb->engine) ||
+ (intel_engine_using_cmd_parser(eb->engine) &&
+ eb->args->batch_len);
}
static int eb_create(struct i915_execbuffer *eb)
@@ -1893,10 +1895,38 @@ static int i915_reset_gen7_sol_offsets(struct i915_request *rq)
return 0;
}
-static struct i915_vma *eb_parse(struct i915_execbuffer *eb, bool is_master)
+static struct i915_vma *
+shadow_batch_pin(struct i915_execbuffer *eb, struct drm_i915_gem_object *obj)
+{
+ struct drm_i915_private *dev_priv = eb->i915;
+ struct i915_address_space *vm;
+ u64 flags;
+
+ /*
+ * PPGTT backed shadow buffers must be mapped RO, to prevent
+ * post-scan tampering
+ */
+ if (CMDPARSER_USES_GGTT(dev_priv)) {
+ flags = PIN_GLOBAL;
+ vm = &dev_priv->ggtt.vm;
+ } else if (eb->vm->has_read_only) {
+ flags = PIN_USER;
+ vm = eb->vm;
+ i915_gem_object_set_readonly(obj);
+ } else {
+ DRM_DEBUG("Cannot prevent post-scan tampering without RO capable vm\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return i915_gem_object_pin(obj, vm, NULL, 0, 0, flags);
+}
+
+static struct i915_vma *eb_parse(struct i915_execbuffer *eb)
{
struct drm_i915_gem_object *shadow_batch_obj;
struct i915_vma *vma;
+ u64 batch_start;
+ u64 shadow_batch_start;
int err;
shadow_batch_obj = i915_gem_batch_pool_get(&eb->engine->batch_pool,
@@ -1904,29 +1934,54 @@ static struct i915_vma *eb_parse(struct i915_execbuffer *eb, bool is_master)
if (IS_ERR(shadow_batch_obj))
return ERR_CAST(shadow_batch_obj);
- err = intel_engine_cmd_parser(eb->engine,
+ vma = shadow_batch_pin(eb, shadow_batch_obj);
+ if (IS_ERR(vma))
+ goto out;
+
+ batch_start = gen8_canonical_addr(eb->batch->node.start) +
+ eb->batch_start_offset;
+
+ shadow_batch_start = gen8_canonical_addr(vma->node.start);
+
+ err = intel_engine_cmd_parser(eb->ctx,
+ eb->engine,
eb->batch->obj,
- shadow_batch_obj,
+ batch_start,
eb->batch_start_offset,
eb->batch_len,
- is_master);
+ shadow_batch_obj,
+ shadow_batch_start);
+
if (err) {
- if (err == -EACCES) /* unhandled chained batch */
+ i915_vma_unpin(vma);
+
+ /*
+ * Unsafe GGTT-backed buffers can still be submitted safely
+ * as non-secure.
+ * For PPGTT backing however, we have no choice but to forcibly
+ * reject unsafe buffers
+ */
+ if (CMDPARSER_USES_GGTT(eb->i915) && (err == -EACCES))
+ /* Execute original buffer non-secure */
vma = NULL;
else
vma = ERR_PTR(err);
+
goto out;
}
- vma = i915_gem_object_ggtt_pin(shadow_batch_obj, NULL, 0, 0, 0);
- if (IS_ERR(vma))
- goto out;
-
eb->vma[eb->buffer_count] = i915_vma_get(vma);
eb->flags[eb->buffer_count] =
__EXEC_OBJECT_HAS_PIN | __EXEC_OBJECT_HAS_REF;
vma->exec_flags = &eb->flags[eb->buffer_count];
eb->buffer_count++;
+ eb->batch_start_offset = 0;
+ eb->batch = vma;
+
+ /* eb->batch_len unchanged */
+
+ if (CMDPARSER_USES_GGTT(eb->i915))
+ eb->batch_flags |= I915_DISPATCH_SECURE;
out:
i915_gem_object_unpin_pages(shadow_batch_obj);
@@ -2177,6 +2232,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
struct drm_i915_gem_exec_object2 *exec,
struct drm_syncobj **fences)
{
+ struct drm_i915_private *i915 = to_i915(dev);
struct i915_execbuffer eb;
struct dma_fence *in_fence = NULL;
struct sync_file *out_fence = NULL;
@@ -2187,7 +2243,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
~__EXEC_OBJECT_UNKNOWN_FLAGS);
- eb.i915 = to_i915(dev);
+ eb.i915 = i915;
eb.file = file;
eb.args = args;
if (DBG_FORCE_RELOC || !(args->flags & I915_EXEC_NO_RELOC))
@@ -2209,8 +2265,15 @@ i915_gem_do_execbuffer(struct drm_device *dev,
eb.batch_flags = 0;
if (args->flags & I915_EXEC_SECURE) {
+ if (INTEL_GEN(i915) >= 11)
+ return -ENODEV;
+
+ /* Return -EPERM to trigger fallback code on old binaries. */
+ if (!HAS_SECURE_BATCHES(i915))
+ return -EPERM;
+
if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN))
- return -EPERM;
+ return -EPERM;
eb.batch_flags |= I915_DISPATCH_SECURE;
}
@@ -2297,34 +2360,19 @@ i915_gem_do_execbuffer(struct drm_device *dev,
goto err_vma;
}
+ if (eb.batch_len == 0)
+ eb.batch_len = eb.batch->size - eb.batch_start_offset;
+
if (eb_use_cmdparser(&eb)) {
struct i915_vma *vma;
- vma = eb_parse(&eb, drm_is_current_master(file));
+ vma = eb_parse(&eb);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
goto err_vma;
}
-
- if (vma) {
- /*
- * Batch parsed and accepted:
- *
- * Set the DISPATCH_SECURE bit to remove the NON_SECURE
- * bit from MI_BATCH_BUFFER_START commands issued in
- * the dispatch_execbuffer implementations. We
- * specifically don't want that set on batches the
- * command parser has accepted.
- */
- eb.batch_flags |= I915_DISPATCH_SECURE;
- eb.batch_start_offset = 0;
- eb.batch = vma;
- }
}
- if (eb.batch_len == 0)
- eb.batch_len = eb.batch->size - eb.batch_start_offset;
-
/*
* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
* batch" bit. Hence we need to pin secure batches into the global gtt.
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 87411a5..d4c6aa7 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -158,7 +158,8 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
if (enable_ppgtt == 0 && INTEL_GEN(dev_priv) < 9)
return 0;
- if (enable_ppgtt == 1)
+ /* Full PPGTT is required by the Gen9 cmdparser */
+ if (enable_ppgtt == 1 && INTEL_GEN(dev_priv) != 9)
return 1;
if (enable_ppgtt == 2 && has_full_ppgtt)
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index 2c9b284..961abb6 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -691,8 +691,28 @@ i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj,
i915_gem_gtt_finish_pages(obj, pages);
for_each_sgt_page(page, sgt_iter, pages) {
- if (obj->mm.dirty)
+ if (obj->mm.dirty && trylock_page(page)) {
+ /*
+ * As this may not be anonymous memory (e.g. shmem)
+ * but exist on a real mapping, we have to lock
+ * the page in order to dirty it -- holding
+ * the page reference is not sufficient to
+ * prevent the inode from being truncated.
+ * Play safe and take the lock.
+ *
+ * However...!
+ *
+ * The mmu-notifier can be invalidated for a
+ * migrate_page, that is alreadying holding the lock
+ * on the page. Such a try_to_unmap() will result
+ * in us calling put_pages() and so recursively try
+ * to lock the page. We avoid that deadlock with
+ * a trylock_page() and in exchange we risk missing
+ * some page dirtying.
+ */
set_page_dirty(page);
+ unlock_page(page);
+ }
mark_page_accessed(page);
put_page(page);
diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c
index d6c8f8f..b7fda69 100644
--- a/drivers/gpu/drm/i915/i915_pmu.c
+++ b/drivers/gpu/drm/i915/i915_pmu.c
@@ -827,8 +827,8 @@ create_event_attributes(struct drm_i915_private *i915)
const char *name;
const char *unit;
} events[] = {
- __event(I915_PMU_ACTUAL_FREQUENCY, "actual-frequency", "MHz"),
- __event(I915_PMU_REQUESTED_FREQUENCY, "requested-frequency", "MHz"),
+ __event(I915_PMU_ACTUAL_FREQUENCY, "actual-frequency", "M"),
+ __event(I915_PMU_REQUESTED_FREQUENCY, "requested-frequency", "M"),
__event(I915_PMU_INTERRUPTS, "interrupts", NULL),
__event(I915_PMU_RC6_RESIDENCY, "rc6-residency", "ns"),
};
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 4e070af..a6f4f32 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -387,6 +387,8 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define ECOCHK_PPGTT_WT_HSW (0x2 << 3)
#define ECOCHK_PPGTT_WB_HSW (0x3 << 3)
+#define GEN8_RC6_CTX_INFO _MMIO(0x8504)
+
#define GAC_ECO_BITS _MMIO(0x14090)
#define ECOBITS_SNB_BIT (1 << 13)
#define ECOBITS_PPGTT_CACHE64B (3 << 8)
@@ -471,6 +473,10 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
*/
#define BCS_SWCTRL _MMIO(0x22200)
+/* There are 16 GPR registers */
+#define BCS_GPR(n) _MMIO(0x22600 + (n) * 8)
+#define BCS_GPR_UDW(n) _MMIO(0x22600 + (n) * 8 + 4)
+
#define GPGPU_THREADS_DISPATCHED _MMIO(0x2290)
#define GPGPU_THREADS_DISPATCHED_UDW _MMIO(0x2290 + 4)
#define HS_INVOCATION_COUNT _MMIO(0x2300)
@@ -7005,6 +7011,10 @@ enum {
#define SKL_CSR_DC5_DC6_COUNT _MMIO(0x8002C)
#define BXT_CSR_DC3_DC5_COUNT _MMIO(0x80038)
+/* Display Internal Timeout Register */
+#define RM_TIMEOUT _MMIO(0x42060)
+#define MMIO_TIMEOUT_US(us) ((us) << 0)
+
/* interrupts */
#define DE_MASTER_IRQ_CONTROL (1 << 31)
#define DE_SPRITEB_FLIP_DONE (1 << 29)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 50d5649..b1154d8 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2064,6 +2064,9 @@ void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv);
void intel_enable_gt_powersave(struct drm_i915_private *dev_priv);
void intel_disable_gt_powersave(struct drm_i915_private *dev_priv);
void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv);
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915);
void gen6_rps_busy(struct drm_i915_private *dev_priv);
void gen6_rps_reset_ei(struct drm_i915_private *dev_priv);
void gen6_rps_idle(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 425df81..8d731eb 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -114,6 +114,14 @@ static void bxt_init_clock_gating(struct drm_i915_private *dev_priv)
*/
I915_WRITE(GEN9_CLKGATE_DIS_0, I915_READ(GEN9_CLKGATE_DIS_0) |
PWM1_GATING_DIS | PWM2_GATING_DIS);
+
+ /*
+ * Lower the display internal timeout.
+ * This is needed to avoid any hard hangs when DSI port PLL
+ * is off and a MMIO access is attempted by any privilege
+ * application, using batch buffers or any other means.
+ */
+ I915_WRITE(RM_TIMEOUT, MMIO_TIMEOUT_US(950));
}
static void glk_init_clock_gating(struct drm_i915_private *dev_priv)
@@ -8188,6 +8196,95 @@ static void intel_init_emon(struct drm_i915_private *dev_priv)
dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
}
+static bool i915_rc6_ctx_corrupted(struct drm_i915_private *dev_priv)
+{
+ return !I915_READ(GEN8_RC6_CTX_INFO);
+}
+
+static void i915_rc6_ctx_wa_init(struct drm_i915_private *i915)
+{
+ if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+ return;
+
+ if (i915_rc6_ctx_corrupted(i915)) {
+ DRM_INFO("RC6 context corrupted, disabling runtime power management\n");
+ i915->gt_pm.rc6.ctx_corrupted = true;
+ intel_runtime_pm_get(i915);
+ }
+}
+
+static void i915_rc6_ctx_wa_cleanup(struct drm_i915_private *i915)
+{
+ if (i915->gt_pm.rc6.ctx_corrupted) {
+ intel_runtime_pm_put(i915);
+ i915->gt_pm.rc6.ctx_corrupted = false;
+ }
+}
+
+/**
+ * i915_rc6_ctx_wa_suspend - system suspend sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to clean up the RC6 CTX WA before system suspend.
+ */
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915)
+{
+ if (i915->gt_pm.rc6.ctx_corrupted)
+ intel_runtime_pm_put(i915);
+}
+
+/**
+ * i915_rc6_ctx_wa_resume - system resume sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to re-init the RC6 CTX WA after system resume.
+ */
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915)
+{
+ if (!i915->gt_pm.rc6.ctx_corrupted)
+ return;
+
+ if (i915_rc6_ctx_corrupted(i915)) {
+ intel_runtime_pm_get(i915);
+ return;
+ }
+
+ DRM_INFO("RC6 context restored, re-enabling runtime power management\n");
+ i915->gt_pm.rc6.ctx_corrupted = false;
+}
+
+static void intel_disable_rc6(struct drm_i915_private *dev_priv);
+
+/**
+ * i915_rc6_ctx_wa_check - check for a new RC6 CTX corruption
+ * @i915: i915 device
+ *
+ * Check if an RC6 CTX corruption has happened since the last check and if so
+ * disable RC6 and runtime power management.
+ *
+ * Return false if no context corruption has happened since the last call of
+ * this function, true otherwise.
+*/
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915)
+{
+ if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+ return false;
+
+ if (i915->gt_pm.rc6.ctx_corrupted)
+ return false;
+
+ if (!i915_rc6_ctx_corrupted(i915))
+ return false;
+
+ DRM_NOTE("RC6 context corruption, disabling runtime power management\n");
+
+ intel_disable_rc6(i915);
+ i915->gt_pm.rc6.ctx_corrupted = true;
+ intel_runtime_pm_get_noresume(i915);
+
+ return true;
+}
+
void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
{
struct intel_rps *rps = &dev_priv->gt_pm.rps;
@@ -8203,6 +8300,8 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->pcu_lock);
+ i915_rc6_ctx_wa_init(dev_priv);
+
/* Initialize RPS limits (for userspace) */
if (IS_CHERRYVIEW(dev_priv))
cherryview_init_gt_powersave(dev_priv);
@@ -8249,6 +8348,8 @@ void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv)
if (IS_VALLEYVIEW(dev_priv))
valleyview_cleanup_gt_powersave(dev_priv);
+ i915_rc6_ctx_wa_cleanup(dev_priv);
+
if (!HAS_RC6(dev_priv))
intel_runtime_pm_put(dev_priv);
}
@@ -8293,7 +8394,7 @@ static inline void intel_disable_llc_pstate(struct drm_i915_private *i915)
i915->gt_pm.llc_pstate.enabled = false;
}
-static void intel_disable_rc6(struct drm_i915_private *dev_priv)
+static void __intel_disable_rc6(struct drm_i915_private *dev_priv)
{
lockdep_assert_held(&dev_priv->pcu_lock);
@@ -8312,6 +8413,13 @@ static void intel_disable_rc6(struct drm_i915_private *dev_priv)
dev_priv->gt_pm.rc6.enabled = false;
}
+static void intel_disable_rc6(struct drm_i915_private *dev_priv)
+{
+ mutex_lock(&dev_priv->pcu_lock);
+ __intel_disable_rc6(dev_priv);
+ mutex_unlock(&dev_priv->pcu_lock);
+}
+
static void intel_disable_rps(struct drm_i915_private *dev_priv)
{
lockdep_assert_held(&dev_priv->pcu_lock);
@@ -8337,7 +8445,7 @@ void intel_disable_gt_powersave(struct drm_i915_private *dev_priv)
{
mutex_lock(&dev_priv->pcu_lock);
- intel_disable_rc6(dev_priv);
+ __intel_disable_rc6(dev_priv);
intel_disable_rps(dev_priv);
if (HAS_LLC(dev_priv))
intel_disable_llc_pstate(dev_priv);
@@ -8364,6 +8472,9 @@ static void intel_enable_rc6(struct drm_i915_private *dev_priv)
if (dev_priv->gt_pm.rc6.enabled)
return;
+ if (dev_priv->gt_pm.rc6.ctx_corrupted)
+ return;
+
if (IS_CHERRYVIEW(dev_priv))
cherryview_enable_rc6(dev_priv);
else if (IS_VALLEYVIEW(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index f5ffa6d..eaf1a16 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -584,9 +584,10 @@ struct intel_engine_cs {
struct intel_engine_hangcheck hangcheck;
-#define I915_ENGINE_NEEDS_CMD_PARSER BIT(0)
-#define I915_ENGINE_SUPPORTS_STATS BIT(1)
-#define I915_ENGINE_HAS_PREEMPTION BIT(2)
+#define I915_ENGINE_USING_CMD_PARSER BIT(0)
+#define I915_ENGINE_SUPPORTS_STATS BIT(1)
+#define I915_ENGINE_HAS_PREEMPTION BIT(2)
+#define I915_ENGINE_REQUIRES_CMD_PARSER BIT(3)
unsigned int flags;
/*
@@ -647,9 +648,15 @@ struct intel_engine_cs {
};
static inline bool
-intel_engine_needs_cmd_parser(const struct intel_engine_cs *engine)
+intel_engine_using_cmd_parser(const struct intel_engine_cs *engine)
{
- return engine->flags & I915_ENGINE_NEEDS_CMD_PARSER;
+ return engine->flags & I915_ENGINE_USING_CMD_PARSER;
+}
+
+static inline bool
+intel_engine_requires_cmd_parser(const struct intel_engine_cs *engine)
+{
+ return engine->flags & I915_ENGINE_REQUIRES_CMD_PARSER;
}
static inline bool
diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c
index f7945ba..e176014 100644
--- a/drivers/gpu/drm/meson/meson_venc_cvbs.c
+++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c
@@ -75,6 +75,25 @@ struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
},
};
+static const struct meson_cvbs_mode *
+meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
+{
+ int i;
+
+ for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
+ struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
+
+ if (drm_mode_match(req_mode, &meson_mode->mode,
+ DRM_MODE_MATCH_TIMINGS |
+ DRM_MODE_MATCH_CLOCK |
+ DRM_MODE_MATCH_FLAGS |
+ DRM_MODE_MATCH_3D_FLAGS))
+ return meson_mode;
+ }
+
+ return NULL;
+}
+
/* Connector */
static void meson_cvbs_connector_destroy(struct drm_connector *connector)
@@ -147,14 +166,8 @@ static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
- int i;
-
- for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
- struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
-
- if (drm_mode_equal(&crtc_state->mode, &meson_mode->mode))
- return 0;
- }
+ if (meson_cvbs_get_mode(&crtc_state->mode))
+ return 0;
return -EINVAL;
}
@@ -192,24 +205,17 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode);
struct meson_venc_cvbs *meson_venc_cvbs =
encoder_to_meson_venc_cvbs(encoder);
struct meson_drm *priv = meson_venc_cvbs->priv;
- int i;
- for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
- struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
+ if (meson_mode) {
+ meson_venci_cvbs_mode_set(priv, meson_mode->enci);
- if (drm_mode_equal(mode, &meson_mode->mode)) {
- meson_venci_cvbs_mode_set(priv,
- meson_mode->enci);
-
- /* Setup 27MHz vclk2 for ENCI and VDAC */
- meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
- MESON_VCLK_CVBS, MESON_VCLK_CVBS,
- MESON_VCLK_CVBS, true);
- break;
- }
+ /* Setup 27MHz vclk2 for ENCI and VDAC */
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS,
+ MESON_VCLK_CVBS, MESON_VCLK_CVBS, true);
}
}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 9acb9df..9cde79a 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -1140,7 +1140,7 @@ int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
gmu->dev = &pdev->dev;
- of_dma_configure(gmu->dev, node, false);
+ of_dma_configure(gmu->dev, node, true);
/* Fow now, don't do anything fancy until we get our feet under us */
gmu->idle_level = GMU_IDLE_STATE_ACTIVE;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 74cc204..2d9b7b5 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -442,35 +442,38 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
}
}
-static void _dpu_kms_initialize_dsi(struct drm_device *dev,
+static int _dpu_kms_initialize_dsi(struct drm_device *dev,
struct msm_drm_private *priv,
struct dpu_kms *dpu_kms)
{
struct drm_encoder *encoder = NULL;
- int i, rc;
+ int i, rc = 0;
+
+ if (!(priv->dsi[0] || priv->dsi[1]))
+ return rc;
/*TODO: Support two independent DSI connectors */
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DSI);
- if (IS_ERR_OR_NULL(encoder)) {
+ if (IS_ERR(encoder)) {
DPU_ERROR("encoder init failed for dsi display\n");
- return;
+ return PTR_ERR(encoder);
}
priv->encoders[priv->num_encoders++] = encoder;
for (i = 0; i < ARRAY_SIZE(priv->dsi); i++) {
- if (!priv->dsi[i]) {
- DPU_DEBUG("invalid msm_dsi for ctrl %d\n", i);
- return;
- }
+ if (!priv->dsi[i])
+ continue;
rc = msm_dsi_modeset_init(priv->dsi[i], dev, encoder);
if (rc) {
DPU_ERROR("modeset_init failed for dsi[%d], rc = %d\n",
i, rc);
- continue;
+ break;
}
}
+
+ return rc;
}
/**
@@ -481,16 +484,16 @@ static void _dpu_kms_initialize_dsi(struct drm_device *dev,
* @dpu_kms: Pointer to dpu kms structure
* Returns: Zero on success
*/
-static void _dpu_kms_setup_displays(struct drm_device *dev,
+static int _dpu_kms_setup_displays(struct drm_device *dev,
struct msm_drm_private *priv,
struct dpu_kms *dpu_kms)
{
- _dpu_kms_initialize_dsi(dev, priv, dpu_kms);
-
/**
* Extend this function to initialize other
* types of displays
*/
+
+ return _dpu_kms_initialize_dsi(dev, priv, dpu_kms);
}
static void _dpu_kms_drm_obj_destroy(struct dpu_kms *dpu_kms)
@@ -552,7 +555,9 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
* Create encoder and query display drivers to create
* bridges and connectors
*/
- _dpu_kms_setup_displays(dev, priv, dpu_kms);
+ ret = _dpu_kms_setup_displays(dev, priv, dpu_kms);
+ if (ret)
+ goto fail;
max_crtc_count = min(catalog->mixer_count, priv->num_encoders);
diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c
index d756436..989465e 100644
--- a/drivers/gpu/drm/msm/msm_debugfs.c
+++ b/drivers/gpu/drm/msm/msm_debugfs.c
@@ -53,12 +53,8 @@ static int msm_gpu_release(struct inode *inode, struct file *file)
struct msm_gpu_show_priv *show_priv = m->private;
struct msm_drm_private *priv = show_priv->dev->dev_private;
struct msm_gpu *gpu = priv->gpu;
- int ret;
- ret = mutex_lock_interruptible(&show_priv->dev->struct_mutex);
- if (ret)
- return ret;
-
+ mutex_lock(&show_priv->dev->struct_mutex);
gpu->funcs->gpu_state_put(show_priv->state);
mutex_unlock(&show_priv->dev->struct_mutex);
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 52a2146..5e6c78e 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -25,6 +25,7 @@
#include <linux/pm_opp.h>
#include <linux/devfreq.h>
#include <linux/devcoredump.h>
+#include <linux/sched/task.h>
/*
* Power Management:
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index dc7454e..b46e99f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -29,6 +29,7 @@
#include <nvif/notify.h>
+#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_dp_helper.h>
@@ -37,6 +38,60 @@
struct nvkm_i2c_port;
+#define nouveau_conn_atom(p) \
+ container_of((p), struct nouveau_conn_atom, state)
+
+struct nouveau_conn_atom {
+ struct drm_connector_state state;
+
+ struct {
+ /* The enum values specifically defined here match nv50/gf119
+ * hw values, and the code relies on this.
+ */
+ enum {
+ DITHERING_MODE_OFF = 0x00,
+ DITHERING_MODE_ON = 0x01,
+ DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON,
+ DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON,
+ DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON,
+ DITHERING_MODE_AUTO
+ } mode;
+ enum {
+ DITHERING_DEPTH_6BPC = 0x00,
+ DITHERING_DEPTH_8BPC = 0x02,
+ DITHERING_DEPTH_AUTO
+ } depth;
+ } dither;
+
+ struct {
+ int mode; /* DRM_MODE_SCALE_* */
+ struct {
+ enum {
+ UNDERSCAN_OFF,
+ UNDERSCAN_ON,
+ UNDERSCAN_AUTO,
+ } mode;
+ u32 hborder;
+ u32 vborder;
+ } underscan;
+ bool full;
+ } scaler;
+
+ struct {
+ int color_vibrance;
+ int vibrant_hue;
+ } procamp;
+
+ union {
+ struct {
+ bool dither:1;
+ bool scaler:1;
+ bool procamp:1;
+ };
+ u8 mask;
+ } set;
+};
+
struct nouveau_connector {
struct drm_connector base;
enum dcb_connector_type type;
@@ -111,61 +166,6 @@ extern int nouveau_ignorelid;
extern int nouveau_duallink;
extern int nouveau_hdmimhz;
-#include <drm/drm_crtc.h>
-#define nouveau_conn_atom(p) \
- container_of((p), struct nouveau_conn_atom, state)
-
-struct nouveau_conn_atom {
- struct drm_connector_state state;
-
- struct {
- /* The enum values specifically defined here match nv50/gf119
- * hw values, and the code relies on this.
- */
- enum {
- DITHERING_MODE_OFF = 0x00,
- DITHERING_MODE_ON = 0x01,
- DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON,
- DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON,
- DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON,
- DITHERING_MODE_AUTO
- } mode;
- enum {
- DITHERING_DEPTH_6BPC = 0x00,
- DITHERING_DEPTH_8BPC = 0x02,
- DITHERING_DEPTH_AUTO
- } depth;
- } dither;
-
- struct {
- int mode; /* DRM_MODE_SCALE_* */
- struct {
- enum {
- UNDERSCAN_OFF,
- UNDERSCAN_ON,
- UNDERSCAN_AUTO,
- } mode;
- u32 hborder;
- u32 vborder;
- } underscan;
- bool full;
- } scaler;
-
- struct {
- int color_vibrance;
- int vibrant_hue;
- } procamp;
-
- union {
- struct {
- bool dither:1;
- bool scaler:1;
- bool procamp:1;
- };
- u8 mask;
- } set;
-};
-
void nouveau_conn_attach_properties(struct drm_connector *);
void nouveau_conn_reset(struct drm_connector *);
struct drm_connector_state *
diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 9a2cb8a..aab6a70 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -427,6 +427,7 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
return PTR_ERR(ts->dsi);
}
+ drm_panel_init(&ts->base);
ts->base.dev = dev;
ts->base.funcs = &rpi_touchscreen_funcs;
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index 74284e5..89fa178 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -380,6 +380,7 @@ static int st7789v_probe(struct spi_device *spi)
spi_set_drvdata(spi, ctx);
ctx->spi = spi;
+ drm_panel_init(&ctx->panel);
ctx->panel.dev = &spi->dev;
ctx->panel.funcs = &st7789v_drm_funcs;
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 2445e75..d00f45e 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -136,20 +136,11 @@ static int qxl_drm_freeze(struct drm_device *dev)
{
struct pci_dev *pdev = dev->pdev;
struct qxl_device *qdev = dev->dev_private;
- struct drm_crtc *crtc;
+ int ret;
- drm_kms_helper_poll_disable(dev);
-
- console_lock();
- qxl_fbdev_set_suspend(qdev, 1);
- console_unlock();
-
- /* unpin the front buffers */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- if (crtc->enabled)
- (*crtc_funcs->disable)(crtc);
- }
+ ret = drm_mode_config_helper_suspend(dev);
+ if (ret)
+ return ret;
qxl_destroy_monitors_object(qdev);
qxl_surf_evict(qdev);
@@ -175,14 +166,7 @@ static int qxl_drm_resume(struct drm_device *dev, bool thaw)
}
qxl_create_monitors_object(qdev);
- drm_helper_resume_force_mode(dev);
-
- console_lock();
- qxl_fbdev_set_suspend(qdev, 0);
- console_unlock();
-
- drm_kms_helper_poll_enable(dev);
- return 0;
+ return drm_mode_config_helper_resume(dev);
}
static int qxl_pm_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 7d39ed6..b24401f 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -1820,8 +1820,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
track->textures[i].use_pitch = 1;
} else {
track->textures[i].use_pitch = 0;
- track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK);
- track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK);
+ track->textures[i].width = 1 << ((idx_value & RADEON_TXFORMAT_WIDTH_MASK) >> RADEON_TXFORMAT_WIDTH_SHIFT);
+ track->textures[i].height = 1 << ((idx_value & RADEON_TXFORMAT_HEIGHT_MASK) >> RADEON_TXFORMAT_HEIGHT_SHIFT);
}
if (idx_value & RADEON_TXFORMAT_CUBIC_MAP_ENABLE)
track->textures[i].tex_coord_type = 2;
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
index c22321c..c2b506c7 100644
--- a/drivers/gpu/drm/radeon/r200.c
+++ b/drivers/gpu/drm/radeon/r200.c
@@ -476,8 +476,8 @@ int r200_packet0_check(struct radeon_cs_parser *p,
track->textures[i].use_pitch = 1;
} else {
track->textures[i].use_pitch = 0;
- track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK);
- track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK);
+ track->textures[i].width = 1 << ((idx_value & RADEON_TXFORMAT_WIDTH_MASK) >> RADEON_TXFORMAT_WIDTH_SHIFT);
+ track->textures[i].height = 1 << ((idx_value & RADEON_TXFORMAT_HEIGHT_MASK) >> RADEON_TXFORMAT_HEIGHT_SHIFT);
}
if (idx_value & R200_TXFORMAT_LOOKUP_DISABLE)
track->textures[i].lookup_disable = true;
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 0a785ef..db2d8b8 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -1956,6 +1956,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
case 0x682C:
si_pi->cac_weights = cac_weights_cape_verde_pro;
si_pi->dte_data = dte_data_sun_xt;
+ update_dte_from_pl2 = true;
break;
case 0x6825:
case 0x6827:
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 416da53..8ad36f5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -651,8 +651,6 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
cec_unregister_adapter(hdmi->cec_adap);
- drm_connector_cleanup(&hdmi->connector);
- drm_encoder_cleanup(&hdmi->encoder);
i2c_del_adapter(hdmi->i2c);
clk_disable_unprepare(hdmi->mod_clk);
clk_disable_unprepare(hdmi->bus_clk);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 8c31c9a..fda1ae1 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -423,7 +423,7 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
WARN_ON(!tcon->quirks->has_channel_0);
- tcon->dclk_min_div = 6;
+ tcon->dclk_min_div = 1;
tcon->dclk_max_div = 127;
sun4i_tcon0_mode_set_common(tcon, mode);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index d7fe9f1..89cb70d 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -2922,6 +2922,11 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)
* earlier
*/
sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index;
+ } else {
+ if (sor->soc->supports_edp)
+ sor->index = 0;
+ else
+ sor->index = 1;
}
return 0;
diff --git a/drivers/gpu/host1x/hw/hw_host1x06_uclass.h b/drivers/gpu/host1x/hw/hw_host1x06_uclass.h
index 4457486..e599e15 100644
--- a/drivers/gpu/host1x/hw/hw_host1x06_uclass.h
+++ b/drivers/gpu/host1x/hw/hw_host1x06_uclass.h
@@ -59,7 +59,7 @@ static inline u32 host1x_uclass_incr_syncpt_r(void)
host1x_uclass_incr_syncpt_r()
static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
{
- return (v & 0xff) << 8;
+ return (v & 0xff) << 10;
}
#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
host1x_uclass_incr_syncpt_cond_f(v)
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 527a1cd..916b2355 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -447,7 +447,8 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
return err;
}
-static inline int copy_gathers(struct host1x_job *job, struct device *dev)
+static inline int copy_gathers(struct device *host, struct host1x_job *job,
+ struct device *dev)
{
struct host1x_firewall fw;
size_t size = 0;
@@ -470,12 +471,12 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
* Try a non-blocking allocation from a higher priority pools first,
* as awaiting for the allocation here is a major performance hit.
*/
- job->gather_copy_mapped = dma_alloc_wc(dev, size, &job->gather_copy,
+ job->gather_copy_mapped = dma_alloc_wc(host, size, &job->gather_copy,
GFP_NOWAIT);
/* the higher priority allocation failed, try the generic-blocking */
if (!job->gather_copy_mapped)
- job->gather_copy_mapped = dma_alloc_wc(dev, size,
+ job->gather_copy_mapped = dma_alloc_wc(host, size,
&job->gather_copy,
GFP_KERNEL);
if (!job->gather_copy_mapped)
@@ -523,7 +524,7 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)
goto out;
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) {
- err = copy_gathers(job, dev);
+ err = copy_gathers(host->dev, job, dev);
if (err)
goto out;
}
@@ -584,7 +585,7 @@ void host1x_job_unpin(struct host1x_job *job)
job->num_unpins = 0;
if (job->gather_copy_size)
- dma_free_wc(job->channel->dev, job->gather_copy_size,
+ dma_free_wc(host->dev, job->gather_copy_size,
job->gather_copy_mapped, job->gather_copy);
}
EXPORT_SYMBOL(host1x_job_unpin);
diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c
index 2f8db9d..4a28f3f 100644
--- a/drivers/gpu/ipu-v3/ipu-pre.c
+++ b/drivers/gpu/ipu-v3/ipu-pre.c
@@ -106,6 +106,7 @@ struct ipu_pre {
void *buffer_virt;
bool in_use;
unsigned int safe_window_end;
+ unsigned int last_bufaddr;
};
static DEFINE_MUTEX(ipu_pre_list_mutex);
@@ -185,6 +186,7 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
+ pre->last_bufaddr = bufaddr;
val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
@@ -242,7 +244,11 @@ void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
unsigned short current_yblock;
u32 val;
+ if (bufaddr == pre->last_bufaddr)
+ return;
+
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
+ pre->last_bufaddr = bufaddr;
do {
if (time_after(jiffies, timeout)) {
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 5654308..30a730c 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -722,7 +722,7 @@ static const struct adreno_reglist a630_vbif_regs[] = {
};
-/* For a615, a616, a618, a630, a640 and a680 */
+/* For a615, a616, a618, A619, a630, a640 and a680 */
static const struct a6xx_protected_regs a630_protected_regs[] = {
{ A6XX_CP_PROTECT_REG + 0, 0x00000, 0x004ff, 0 },
{ A6XX_CP_PROTECT_REG + 1, 0x00501, 0x00506, 0 },
@@ -785,7 +785,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a630v2 = {
.protected_regs = a630_protected_regs,
};
-/* For a615, a616 and a618 */
+/* For a615, a616, a618 and a619 */
static const struct adreno_reglist a615_hwcg_regs[] = {
{A6XX_RBBM_CLOCK_CNTL_SP0, 0x02222222},
{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
@@ -852,7 +852,7 @@ static const struct adreno_reglist a615_hwcg_regs[] = {
{A6XX_RBBM_CLOCK_HYST_GMU_GX, 0x00000555}
};
-/* For a615, a616 and a618 */
+/* For a615, a616, a618 and a619 */
static const struct adreno_reglist a615_gbif_regs[] = {
{A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x3},
};
@@ -909,6 +909,33 @@ static const struct adreno_a6xx_core adreno_gpu_core_a618 = {
.protected_regs = a630_protected_regs,
};
+static const struct adreno_a6xx_core adreno_gpu_core_a619 = {
+ .base = {
+ DEFINE_ADRENO_REV(ADRENO_REV_A619, 6, 1, 9, ANY_ID),
+ .features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_PREEMPTION |
+ ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
+ ADRENO_IOCOHERENT,
+ .gpudev = &adreno_a6xx_gpudev,
+ .gmem_size = SZ_512K,
+ .busy_mask = 0xfffffffe,
+ .bus_width = 32,
+ },
+ .prim_fifo_threshold = 0x0018000,
+ .pdc_address_offset = 0x00030090,
+ .gmu_major = 1,
+ .gmu_minor = 9,
+ .sqefw_name = "a630_sqe.fw",
+ .gmufw_name = "a619_gmu.bin",
+ .zap_name = "a615_zap",
+ .hwcg = a615_hwcg_regs,
+ .hwcg_count = ARRAY_SIZE(a615_hwcg_regs),
+ .vbif = a615_gbif_regs,
+ .vbif_count = ARRAY_SIZE(a615_gbif_regs),
+ .hang_detect_cycles = 0x3fffff,
+ .protected_regs = a630_protected_regs,
+};
+
+
static const struct adreno_reglist a620_hwcg_regs[] = {
{A6XX_RBBM_CLOCK_CNTL_SP0, 0x02222222},
{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
@@ -1015,7 +1042,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a620 = {
.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_GPMU |
ADRENO_CONTENT_PROTECTION | ADRENO_IOCOHERENT |
ADRENO_IFPC | ADRENO_PREEMPTION | ADRENO_ACD |
- ADRENO_APRIV,
+ ADRENO_APRIV | ADRENO_LM,
.gpudev = &adreno_a6xx_gpudev,
.gmem_size = SZ_512K,
.busy_mask = 0xfffffffe,
@@ -1404,6 +1431,7 @@ static const struct adreno_gpu_core *adreno_gpulist[] = {
&adreno_gpu_core_a630v2.base,
&adreno_gpu_core_a615.base,
&adreno_gpu_core_a618.base,
+ &adreno_gpu_core_a619.base,
&adreno_gpu_core_a620.base,
&adreno_gpu_core_a640.base,
&adreno_gpu_core_a650.base,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index c399e47..b545b4d 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2002,2007-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002,2007-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/input.h>
@@ -339,6 +339,7 @@ void adreno_fault_detect_stop(struct adreno_device *adreno_dev)
/* Send an NMI to the GMU */
void adreno_gmu_send_nmi(struct adreno_device *adreno_dev)
{
+ u32 val;
/* Mask so there's no interrupt caused by NMI */
adreno_write_gmureg(adreno_dev,
ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF);
@@ -348,9 +349,10 @@ void adreno_gmu_send_nmi(struct adreno_device *adreno_dev)
if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
adreno_write_gmureg(adreno_dev,
ADRENO_REG_GMU_NMI_CONTROL_STATUS, 0);
- adreno_write_gmureg(adreno_dev,
- ADRENO_REG_GMU_CM3_CFG,
- (1 << GMU_CM3_CFG_NONMASKINTR_SHIFT));
+
+ adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_CM3_CFG, &val);
+ val |= 1 << GMU_CM3_CFG_NONMASKINTR_SHIFT;
+ adreno_write_gmureg(adreno_dev, ADRENO_REG_GMU_CM3_CFG, val);
/* Make sure the NMI is invoked before we proceed*/
wmb();
@@ -1350,15 +1352,15 @@ static int adreno_read_speed_bin(struct platform_device *pdev,
}
buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
if (!IS_ERR(buf)) {
memcpy(&adreno_dev->speed_bin, buf,
min(len, sizeof(adreno_dev->speed_bin)));
kfree(buf);
}
- nvmem_cell_put(cell);
-
- return 0;
+ return PTR_ERR_OR_ZERO(buf);
}
static int adreno_probe_efuse(struct platform_device *pdev,
@@ -3315,12 +3317,15 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev,
unsigned int status, i;
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
unsigned int reg_offset = gpudev->reg_offsets->offsets[offset];
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ u64 ts1, ts2;
adreno_writereg(adreno_dev, offset, val);
if (!gmu_core_isenabled(KGSL_DEVICE(adreno_dev)))
return 0;
+ ts1 = gmu_core_dev_read_ao_counter(device);
for (i = 0; i < GMU_CORE_LONG_WAKEUP_RETRY_LIMIT; i++) {
/*
* Make sure the previous register write is posted before
@@ -3344,17 +3349,20 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev,
adreno_writereg(adreno_dev, offset, val);
if (i == GMU_CORE_SHORT_WAKEUP_RETRY_LIMIT)
- dev_err(adreno_dev->dev.dev,
- "Waited %d usecs to write fenced register 0x%x. Continuing to wait...\n",
+ dev_err(device->dev,
+ "Waited %d usecs to write fenced register 0x%x, status 0x%x. Continuing to wait...\n",
(GMU_CORE_SHORT_WAKEUP_RETRY_LIMIT *
GMU_CORE_WAKEUP_DELAY_US),
- reg_offset);
+ reg_offset, status);
}
- dev_err(adreno_dev->dev.dev,
- "Timed out waiting %d usecs to write fenced register 0x%x\n",
+ ts2 = gmu_core_dev_read_ao_counter(device);
+ dev_err(device->dev,
+ "fenced write for 0x%x timed out in %dus. timestamps %llu %llu, status 0x%x\n",
+ reg_offset,
GMU_CORE_LONG_WAKEUP_RETRY_LIMIT * GMU_CORE_WAKEUP_DELAY_US,
- reg_offset);
+ ts1, ts2, status);
+
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 267332d..3edab23 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2008-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __ADRENO_H
#define __ADRENO_H
@@ -200,6 +200,7 @@ enum adreno_gpurev {
ADRENO_REV_A615 = 615,
ADRENO_REV_A616 = 616,
ADRENO_REV_A618 = 618,
+ ADRENO_REV_A619 = 619,
ADRENO_REV_A620 = 620,
ADRENO_REV_A630 = 630,
ADRENO_REV_A640 = 640,
@@ -1167,6 +1168,7 @@ static inline int adreno_is_a6xx(struct adreno_device *adreno_dev)
ADRENO_TARGET(a610, ADRENO_REV_A610)
ADRENO_TARGET(a612, ADRENO_REV_A612)
ADRENO_TARGET(a618, ADRENO_REV_A618)
+ADRENO_TARGET(a619, ADRENO_REV_A619)
ADRENO_TARGET(a620, ADRENO_REV_A620)
ADRENO_TARGET(a630, ADRENO_REV_A630)
ADRENO_TARGET(a640, ADRENO_REV_A640)
@@ -1175,14 +1177,14 @@ ADRENO_TARGET(a680, ADRENO_REV_A680)
/*
* All the derived chipsets from A615 needs to be added to this
- * list such as A616, A618 etc.
+ * list such as A616, A618, A619 etc.
*/
static inline int adreno_is_a615_family(struct adreno_device *adreno_dev)
{
unsigned int rev = ADRENO_GPUREV(adreno_dev);
return (rev == ADRENO_REV_A615 || rev == ADRENO_REV_A616 ||
- rev == ADRENO_REV_A618);
+ rev == ADRENO_REV_A618 || rev == ADRENO_REV_A619);
}
/*
@@ -1379,7 +1381,7 @@ static inline void adreno_set_gpu_fault(struct adreno_device *adreno_dev,
int state)
{
/* only set the fault bit w/o overwriting other bits */
- atomic_add(state, &adreno_dev->dispatcher.fault);
+ atomic_or(state, &adreno_dev->dispatcher.fault);
/* make sure other CPUs see the update */
smp_wmb();
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 63e48e6..d97a03f 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/firmware.h>
@@ -145,6 +145,18 @@ static void a6xx_init(struct adreno_device *adreno_dev)
/* LP DDR4 highest bank bit is different and needs to be overridden */
if (adreno_is_a650(adreno_dev) && of_fdt_get_ddrtype() == 0x7)
adreno_dev->highest_bank_bit = 15;
+ else if (adreno_is_a610(adreno_dev) && of_fdt_get_ddrtype() == 0x5) {
+ /*
+ * LPDDR3 has multiple different highest bank bit value
+ * based on different DDR density. Query this value from
+ * FDT, in case FDT returns error fallback to value in GPU
+ * DT node.
+ */
+ int hbb = of_fdt_get_ddrhbb(0, 0);
+
+ if (hbb > 0)
+ adreno_dev->highest_bank_bit = hbb;
+ }
a6xx_crashdump_init(adreno_dev);
@@ -1313,11 +1325,19 @@ static const char *a6xx_fault_block_uche(struct kgsl_device *device,
unsigned int uche_client_id = 0;
static char str[40];
- mutex_lock(&device->mutex);
+ /*
+ * Smmu driver takes a vote on CX gdsc before calling the kgsl pagefault
+ * handler. If there is contention for device mutex in this path and the
+ * dispatcher fault handler is holding this lock, trying to turn off CX
+ * gdsc will fail during the reset. So to avoid blocking here, try to
+ * lock device mutex and return if it fails.
+ */
+ if (!mutex_trylock(&device->mutex))
+ return "UCHE";
if (!kgsl_state_is_awake(device)) {
mutex_unlock(&device->mutex);
- return "UCHE: unknown";
+ return "UCHE";
}
kgsl_regread(device, A6XX_UCHE_CLIENT_PF, &uche_client_id);
@@ -1325,7 +1345,7 @@ static const char *a6xx_fault_block_uche(struct kgsl_device *device,
/* Ignore the value if the gpu is in IFPC */
if (uche_client_id == SCOOBYDOO)
- return "UCHE: unknown";
+ return "UCHE";
uche_client_id &= A6XX_UCHE_CLIENT_PF_CLIENT_ID_MASK;
snprintf(str, sizeof(str), "UCHE: %s",
diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c
index af13818..b6a1b13 100644
--- a/drivers/gpu/msm/adreno_a6xx_gmu.c
+++ b/drivers/gpu/msm/adreno_a6xx_gmu.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
/* soc/qcom/cmd-db.h needs types.h */
@@ -34,7 +34,7 @@ static const unsigned int a6xx_gmu_tcm_registers[] = {
static const unsigned int a6xx_gmu_registers[] = {
/* GMU CX */
- 0x1F400, 0x1F407, 0x1F410, 0x1F412, 0x1F500, 0x1F500, 0x1F507, 0x1F50A,
+ 0x1F400, 0x1F40B, 0x1F410, 0x1F412, 0x1F500, 0x1F500, 0x1F507, 0x1F50A,
0x1F800, 0x1F804, 0x1F807, 0x1F808, 0x1F80B, 0x1F80C, 0x1F80F, 0x1F81C,
0x1F824, 0x1F82A, 0x1F82D, 0x1F830, 0x1F840, 0x1F853, 0x1F887, 0x1F889,
0x1F8A0, 0x1F8A2, 0x1F8A4, 0x1F8AF, 0x1F8C0, 0x1F8C3, 0x1F8D0, 0x1F8D0,
@@ -228,7 +228,8 @@ static int a6xx_load_pdc_ucode(struct kgsl_device *device)
_regwrite(cfg, PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
_regwrite(cfg, PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
- if (adreno_is_a618(adreno_dev) || adreno_is_a650_family(adreno_dev))
+ if (adreno_is_a618(adreno_dev) || adreno_is_a619(adreno_dev) ||
+ adreno_is_a650_family(adreno_dev))
_regwrite(cfg, PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x2);
else
_regwrite(cfg, PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x3);
@@ -351,12 +352,22 @@ static int a6xx_gmu_start(struct kgsl_device *device)
mask = 0xFFFFFFFF;
}
+ /**
+ * We may have asserted gbif halt as part of reset sequence which may
+ * not get cleared if the gdsc was not reset. So clear it before
+ * attempting GMU boot.
+ */
+ if (adreno_has_gbif(ADRENO_DEVICE(device)))
+ kgsl_regwrite(device, A6XX_GBIF_HALT, 0x0);
+
/* Set the log wptr index */
gmu_core_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP,
gmu->log_wptr_retention);
/* Bring GMU out of reset */
gmu_core_regwrite(device, A6XX_GMU_CM3_SYSRESET, 0);
+ /* Make sure the request completes before continuing */
+ wmb();
if (timed_poll_check(device,
A6XX_GMU_CM3_FW_INIT_RESULT,
val, GMU_START_TIMEOUT, mask)) {
@@ -1057,6 +1068,13 @@ static int a6xx_gmu_fw_start(struct kgsl_device *device,
gmu_core_regwrite(device, A6XX_GMU_AHB_FENCE_RANGE_0,
GMU_FENCE_RANGE_MASK);
+ /*
+ * Make sure that CM3 state is at reset value. Snapshot is changing
+ * NMI bit and if we boot up GMU with NMI bit set.GMU will boot straight
+ * in to NMI handler without executing __main code
+ */
+ gmu_core_regwrite(device, A6XX_GMU_CM3_CFG, 0x4052);
+
/* Pass chipid to GMU FW, must happen before starting GMU */
/* Keep Core and Major bitfields unchanged */
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index 8c44c4a..db850c2 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -760,7 +760,7 @@ void a6xx_preemption_context_destroy(struct kgsl_context *context)
struct kgsl_device *device = context->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION))
return;
gpumem_free_entry(context->user_ctxt_record);
@@ -775,7 +775,7 @@ int a6xx_preemption_context_init(struct kgsl_context *context)
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
uint64_t flags = 0;
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION))
return 0;
if (context->flags & KGSL_CONTEXT_SECURE)
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 2fe694f..b0342c7 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/msm-bus.h>
@@ -908,17 +908,34 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
* The problem is that IB size from the register is the unprocessed size
* of the buffer not the original size, so if we didn't catch this
* buffer being directly used in the RB, then we might not be able to
- * dump the whole thing. Print a warning message so we can try to
+ * dump the whole thing. Try to dump the maximum possible size from the
+ * IB1 base address till the end of memdesc size so that we dont miss
+ * what we are interested in. Print a warning message so we can try to
* figure how often this really happens.
*/
if (-ENOENT == find_object(snapshot->ib1base, snapshot->process) &&
snapshot->ib1size) {
- kgsl_snapshot_push_object(device, snapshot->process,
- snapshot->ib1base, snapshot->ib1size);
- dev_err(device->dev,
- "CP_IB1_BASE not found in the ringbuffer.Dumping %x dwords of the buffer\n",
- snapshot->ib1size);
+ struct kgsl_mem_entry *entry;
+ u64 ibsize;
+
+ entry = kgsl_sharedmem_find(snapshot->process,
+ snapshot->ib1base);
+ if (entry == NULL) {
+ dev_err(device->dev,
+ "Can't find a memory entry containing IB1BASE %16llx\n",
+ snapshot->ib1base);
+ } else {
+ ibsize = entry->memdesc.size -
+ (snapshot->ib1base - entry->memdesc.gpuaddr);
+ kgsl_mem_entry_put(entry);
+
+ kgsl_snapshot_push_object(device, snapshot->process,
+ snapshot->ib1base, ibsize >> 2);
+ dev_err(device->dev,
+ "CP_IB1_BASE is not found in the ringbuffer. Dumping %llx dwords of the buffer\n",
+ ibsize >> 2);
+ }
}
/*
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 8fe0ccb..4125053 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2008-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2020, The Linux Foundation. All rights reserved.
*/
#include <uapi/linux/sched/types.h>
@@ -3438,7 +3438,7 @@ struct kgsl_mem_entry *gpumem_alloc_entry(
/* Cap the alignment bits to the highest number we can handle */
align = MEMFLAGS(flags, KGSL_MEMALIGN_MASK, KGSL_MEMALIGN_SHIFT);
if (align >= ilog2(KGSL_MAX_ALIGN)) {
- dev_err(dev_priv->device->dev,
+ dev_info(dev_priv->device->dev,
"Alignment too large; restricting to %dK\n",
KGSL_MAX_ALIGN >> 10);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 72dd75d..ea0cce6 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -549,7 +549,8 @@ static int gmu_dcvs_set(struct kgsl_device *device,
*/
if (test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) {
gmu_core_snapshot(device);
- adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT |
+ adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
+ adreno_set_gpu_fault(adreno_dev,
ADRENO_GMU_FAULT_SKIP_SNAPSHOT);
adreno_dispatcher_schedule(device);
}
diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c
index cf82890..754662f 100644
--- a/drivers/gpu/msm/kgsl_hfi.c
+++ b/drivers/gpu/msm/kgsl_hfi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
@@ -268,9 +268,9 @@ static int poll_adreno_gmu_reg(struct adreno_device *adreno_dev,
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
- u64 ao_pre_poll, ao_post_poll;
+ u64 ts1, ts2;
- ao_pre_poll = gmu_core_dev_read_ao_counter(device);
+ ts1 = gmu_core_dev_read_ao_counter(device);
while (time_is_after_jiffies(timeout)) {
adreno_read_gmureg(adreno_dev, offset_name, &val);
@@ -279,15 +279,16 @@ static int poll_adreno_gmu_reg(struct adreno_device *adreno_dev,
usleep_range(10, 100);
}
- ao_post_poll = gmu_core_dev_read_ao_counter(device);
+ ts2 = gmu_core_dev_read_ao_counter(device);
/* Check one last time */
adreno_read_gmureg(adreno_dev, offset_name, &val);
if ((val & mask) == expected_val)
return 0;
- dev_err(&gmu->pdev->dev, "kgsl hfi poll timeout: always on: %lld ms\n",
- div_u64((ao_post_poll - ao_pre_poll) * 52, USEC_PER_SEC));
+ dev_err(&gmu->pdev->dev,
+ "Timed out waiting for HFI response. Wait start=%llx end=%llx\n",
+ ts1, ts2);
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index d5a7f5e..5970692 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <asm/cacheflush.h>
@@ -100,7 +100,10 @@ _kgsl_pool_get_page(struct kgsl_page_pool *pool)
list_del(&p->lru);
}
spin_unlock(&pool->list_lock);
- mod_node_page_state(page_pgdat(p), NR_INDIRECTLY_RECLAIMABLE_BYTES,
+
+ if (p != NULL)
+ mod_node_page_state(page_pgdat(p),
+ NR_INDIRECTLY_RECLAIMABLE_BYTES,
-(PAGE_SIZE << pool->pool_order));
return p;
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index e60d1a5..fda9c03 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/msm-bus.h>
@@ -15,6 +15,7 @@
#include "kgsl_device.h"
#include "kgsl_pwrscale.h"
#include "kgsl_trace.h"
+#include "kgsl_trace_power.h"
#define KGSL_PWRFLAGS_POWER_ON 0
#define KGSL_PWRFLAGS_CLK_ON 1
@@ -616,6 +617,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
pwr->previous_pwrlevel,
pwr->pwrlevels[old_level].gpu_freq);
+ trace_gpu_frequency(pwrlevel->gpu_freq/1000, 0);
+
/*
* Some targets do not support the bandwidth requirement of
* GPU at TURBO, for such targets we need to set GPU-BIMC
@@ -2664,6 +2667,7 @@ static int _wake(struct kgsl_device *device)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int status = 0;
+ unsigned int state = device->state;
switch (device->state) {
case KGSL_STATE_SUSPEND:
@@ -2690,6 +2694,9 @@ static int _wake(struct kgsl_device *device)
/* Turn on the core clocks */
kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE);
+ if (state == KGSL_STATE_SLUMBER || state == KGSL_STATE_SUSPEND)
+ trace_gpu_frequency(
+ pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq/1000, 0);
/*
* No need to turn on/off irq here as it no longer affects
* power collapse
@@ -2895,6 +2902,7 @@ _slumber(struct kgsl_device *device)
kgsl_pwrctrl_clk_set_options(device, false);
kgsl_pwrctrl_disable(device);
kgsl_pwrscale_sleep(device);
+ trace_gpu_frequency(0, 0);
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
@@ -2910,6 +2918,7 @@ _slumber(struct kgsl_device *device)
break;
case KGSL_STATE_AWARE:
kgsl_pwrctrl_disable(device);
+ trace_gpu_frequency(0, 0);
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
break;
default:
diff --git a/drivers/gpu/msm/kgsl_rgmu.c b/drivers/gpu/msm/kgsl_rgmu.c
index e3426e1e..ff89170 100644
--- a/drivers/gpu/msm/kgsl_rgmu.c
+++ b/drivers/gpu/msm/kgsl_rgmu.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/clk-provider.h>
@@ -404,6 +404,18 @@ static int rgmu_suspend(struct kgsl_device *device)
return 0;
}
+static int rgmu_init(struct kgsl_device *device)
+{
+ struct gmu_dev_ops *ops = GMU_DEVICE_OPS(device);
+ int ret;
+
+ ret = ops->load_firmware(device);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
/* To be called to power on both GPU and RGMU */
static int rgmu_start(struct kgsl_device *device)
{
@@ -470,6 +482,7 @@ static bool rgmu_regulator_isenabled(struct kgsl_device *device)
struct gmu_core_ops rgmu_ops = {
.probe = rgmu_probe,
.remove = rgmu_stop,
+ .init = rgmu_init,
.start = rgmu_start,
.stop = rgmu_stop,
.dcvs_set = rgmu_dcvs_set,
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 99bb67b..efc7879 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
@@ -1329,7 +1329,7 @@ static void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
snapshot->ib2base);
gmu_only:
- complete_all(&snapshot->dump_gate);
BUG_ON(!snapshot->device->skip_ib_capture &
snapshot->device->force_panic);
+ complete_all(&snapshot->dump_gate);
}
diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c
index 30a7bcb..2ba50fe1 100644
--- a/drivers/gpu/msm/kgsl_trace.c
+++ b/drivers/gpu/msm/kgsl_trace.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2011,2013,2015,2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011,2013,2015,2019-2020 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -10,8 +10,11 @@
/* Instantiate tracepoints */
#define CREATE_TRACE_POINTS
#include "kgsl_trace.h"
+#include "kgsl_trace_power.h"
EXPORT_TRACEPOINT_SYMBOL(kgsl_regwrite);
EXPORT_TRACEPOINT_SYMBOL(kgsl_issueibcmds);
EXPORT_TRACEPOINT_SYMBOL(kgsl_user_pwrlevel_constraint);
EXPORT_TRACEPOINT_SYMBOL(kgsl_constraint);
+
+EXPORT_TRACEPOINT_SYMBOL(gpu_frequency);
diff --git a/drivers/gpu/msm/kgsl_trace_power.h b/drivers/gpu/msm/kgsl_trace_power.h
new file mode 100644
index 0000000..971beb2
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_trace_power.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#if !defined(_KGSL_TRACE_POWER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _KGSL_TRACE_POWER_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM power
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE kgsl_trace_power
+
+#include <linux/tracepoint.h>
+
+/**
+ * gpu_frequency - Reports frequency changes in GPU clock domains
+ * @state: New frequency (in KHz)
+ * @gpu_id: GPU clock domain
+ */
+TRACE_EVENT(gpu_frequency,
+ TP_PROTO(unsigned int state, unsigned int gpu_id),
+ TP_ARGS(state, gpu_id),
+ TP_STRUCT__entry(
+ __field(unsigned int, state)
+ __field(unsigned int, gpu_id)
+ ),
+ TP_fast_assign(
+ __entry->state = state;
+ __entry->gpu_id = gpu_id;
+ ),
+
+ TP_printk("state=%lu gpu_id=%lu",
+ (unsigned long)__entry->state,
+ (unsigned long)__entry->gpu_id)
+);
+
+#endif /* _KGSL_TRACE_POWER_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c
index a594e47..843aed4 100644
--- a/drivers/hid/hid-axff.c
+++ b/drivers/hid/hid-axff.c
@@ -75,13 +75,20 @@ static int axff_init(struct hid_device *hid)
{
struct axff_device *axff;
struct hid_report *report;
- struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
+ struct input_dev *dev;
int field_count = 0;
int i, j;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+ dev = hidinput->input;
+
if (list_empty(report_list)) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 4545cd1..3a35971 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -212,16 +212,37 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
}
/*
+ * Concatenate usage which defines 16 bits or less with the
+ * currently defined usage page to form a 32 bit usage
+ */
+
+static void complete_usage(struct hid_parser *parser, unsigned int index)
+{
+ parser->local.usage[index] &= 0xFFFF;
+ parser->local.usage[index] |=
+ (parser->global.usage_page & 0xFFFF) << 16;
+}
+
+/*
* Add a usage to the temporary parser table.
*/
-static int hid_add_usage(struct hid_parser *parser, unsigned int usage)
+static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
{
if (parser->local.usage_index >= HID_MAX_USAGES) {
hid_err(parser->device, "usage index exceeded\n");
return -1;
}
parser->local.usage[parser->local.usage_index] = usage;
+
+ /*
+ * If Usage item only includes usage id, concatenate it with
+ * currently defined usage page
+ */
+ if (size <= 2)
+ complete_usage(parser, parser->local.usage_index);
+
+ parser->local.usage_size[parser->local.usage_index] = size;
parser->local.collection_index[parser->local.usage_index] =
parser->collection_stack_ptr ?
parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
@@ -482,10 +503,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
- return hid_add_usage(parser, data);
+ return hid_add_usage(parser, data, item->size);
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
@@ -494,9 +512,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
parser->local.usage_minimum = data;
return 0;
@@ -507,9 +522,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
count = data - parser->local.usage_minimum;
if (count + parser->local.usage_index >= HID_MAX_USAGES) {
/*
@@ -529,7 +541,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
}
for (n = parser->local.usage_minimum; n <= data; n++)
- if (hid_add_usage(parser, n)) {
+ if (hid_add_usage(parser, n, item->size)) {
dbg_hid("hid_add_usage failed\n");
return -1;
}
@@ -544,6 +556,41 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
}
/*
+ * Concatenate Usage Pages into Usages where relevant:
+ * As per specification, 6.2.2.8: "When the parser encounters a main item it
+ * concatenates the last declared Usage Page with a Usage to form a complete
+ * usage value."
+ */
+
+static void hid_concatenate_last_usage_page(struct hid_parser *parser)
+{
+ int i;
+ unsigned int usage_page;
+ unsigned int current_page;
+
+ if (!parser->local.usage_index)
+ return;
+
+ usage_page = parser->global.usage_page;
+
+ /*
+ * Concatenate usage page again only if last declared Usage Page
+ * has not been already used in previous usages concatenation
+ */
+ for (i = parser->local.usage_index - 1; i >= 0; i--) {
+ if (parser->local.usage_size[i] > 2)
+ /* Ignore extended usages */
+ continue;
+
+ current_page = parser->local.usage[i] >> 16;
+ if (current_page == usage_page)
+ break;
+
+ complete_usage(parser, i);
+ }
+}
+
+/*
* Process a main item.
*/
@@ -552,6 +599,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
__u32 data;
int ret;
+ hid_concatenate_last_usage_page(parser);
+
data = item_udata(item);
switch (item->tag) {
@@ -731,6 +780,10 @@ static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage)
if (usage == 0xff0000c5 && parser->global.report_count == 256 &&
parser->global.report_size == 8)
parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8;
+
+ if (usage == 0xff0000c6 && parser->global.report_count == 1 &&
+ parser->global.report_size == 8)
+ parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8;
}
static void hid_scan_collection(struct hid_parser *parser, unsigned type)
@@ -761,6 +814,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
__u32 data;
int i;
+ hid_concatenate_last_usage_page(parser);
+
data = item_udata(item);
switch (item->tag) {
@@ -963,6 +1018,7 @@ int hid_open_report(struct hid_device *device)
__u8 *start;
__u8 *buf;
__u8 *end;
+ __u8 *next;
int ret;
static int (*dispatch_type[])(struct hid_parser *parser,
struct hid_item *item) = {
@@ -1016,7 +1072,8 @@ int hid_open_report(struct hid_device *device)
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
ret = -EINVAL;
- while ((start = fetch_item(start, end, &item)) != NULL) {
+ while ((next = fetch_item(start, end, &item)) != NULL) {
+ start = next;
if (item.format != HID_ITEM_FORMAT_SHORT) {
hid_err(device, "unexpected long global item\n");
@@ -1046,7 +1103,8 @@ int hid_open_report(struct hid_device *device)
}
}
- hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
+ hid_err(device, "item fetching failed at offset %u/%u\n",
+ size - (unsigned int)(end - start), size);
err:
kfree(parser->collection_stack);
alloc_err:
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index 818ea7d..309969b 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -87,13 +87,19 @@ static int drff_init(struct hid_device *hid)
{
struct drff_device *drff;
struct hid_report *report;
- struct hid_input *hidinput = list_first_entry(&hid->inputs,
- struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
+ struct input_dev *dev;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+ dev = hidinput->input;
+
if (list_empty(report_list)) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c
index d82d75b..80f9a02 100644
--- a/drivers/hid/hid-emsff.c
+++ b/drivers/hid/hid-emsff.c
@@ -59,13 +59,19 @@ static int emsff_init(struct hid_device *hid)
{
struct emsff_device *emsff;
struct hid_report *report;
- struct hid_input *hidinput = list_first_entry(&hid->inputs,
- struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
+ struct input_dev *dev;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+ dev = hidinput->input;
+
if (list_empty(report_list)) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c
index 2d8cead..5a02c50 100644
--- a/drivers/hid/hid-gaff.c
+++ b/drivers/hid/hid-gaff.c
@@ -77,14 +77,20 @@ static int gaff_init(struct hid_device *hid)
{
struct gaff_device *gaff;
struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct list_head *report_ptr = report_list;
- struct input_dev *dev = hidinput->input;
+ struct input_dev *dev;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
if (list_empty(report_list)) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c
index 6bf4da7..8cb63ea 100644
--- a/drivers/hid/hid-google-hammer.c
+++ b/drivers/hid/hid-google-hammer.c
@@ -121,6 +121,10 @@ static const struct hid_device_id hammer_devices[] = {
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) },
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MASTERBALL) },
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) },
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c
index edc0f64..c68486ee 100644
--- a/drivers/hid/hid-holtekff.c
+++ b/drivers/hid/hid-holtekff.c
@@ -136,13 +136,19 @@ static int holtekff_init(struct hid_device *hid)
{
struct holtekff_device *holtekff;
struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
+ struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
+ struct input_dev *dev;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
if (list_empty(report_list)) {
hid_err(hid, "no output report found\n");
return -ENODEV;
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index 704049e..4d1496f 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -322,60 +322,24 @@ static void mousevsc_on_receive(struct hv_device *device,
static void mousevsc_on_channel_callback(void *context)
{
- const int packet_size = 0x100;
- int ret;
struct hv_device *device = context;
- u32 bytes_recvd;
- u64 req_id;
struct vmpacket_descriptor *desc;
- unsigned char *buffer;
- int bufferlen = packet_size;
- buffer = kmalloc(bufferlen, GFP_ATOMIC);
- if (!buffer)
- return;
-
- do {
- ret = vmbus_recvpacket_raw(device->channel, buffer,
- bufferlen, &bytes_recvd, &req_id);
-
- switch (ret) {
- case 0:
- if (bytes_recvd <= 0) {
- kfree(buffer);
- return;
- }
- desc = (struct vmpacket_descriptor *)buffer;
-
- switch (desc->type) {
- case VM_PKT_COMP:
- break;
-
- case VM_PKT_DATA_INBAND:
- mousevsc_on_receive(device, desc);
- break;
-
- default:
- pr_err("unhandled packet type %d, tid %llx len %d\n",
- desc->type, req_id, bytes_recvd);
- break;
- }
-
+ foreach_vmbus_pkt(desc, device->channel) {
+ switch (desc->type) {
+ case VM_PKT_COMP:
break;
- case -ENOBUFS:
- kfree(buffer);
- /* Handle large packet */
- bufferlen = bytes_recvd;
- buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
+ case VM_PKT_DATA_INBAND:
+ mousevsc_on_receive(device, desc);
+ break;
- if (!buffer)
- return;
-
+ default:
+ pr_err("Unhandled packet type %d, tid %llx len %d\n",
+ desc->type, desc->trans_id, desc->len8 * 8);
break;
}
- } while (1);
-
+ }
}
static int mousevsc_connect_to_vsp(struct hv_device *device)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f5da7f3..4f41adc 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -466,6 +466,8 @@
#define USB_DEVICE_ID_GOOGLE_STAFF 0x502b
#define USB_DEVICE_ID_GOOGLE_WAND 0x502d
#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030
+#define USB_DEVICE_ID_GOOGLE_MASTERBALL 0x503c
+#define USB_DEVICE_ID_GOOGLE_MAGNEMITE 0x503d
#define USB_VENDOR_ID_GOTOP 0x08f2
#define USB_DEVICE_ID_SUPER_Q2 0x007f
@@ -561,6 +563,7 @@
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A 0x094a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941 0x0941
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641 0x0641
+#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a 0x1f4a
#define USB_VENDOR_ID_HUION 0x256c
#define USB_DEVICE_ID_HUION_TABLET 0x006e
@@ -713,6 +716,7 @@
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_DEVICE_ID_LG_MELFAS_MT 0x6007
+#define I2C_DEVICE_ID_LG_8001 0x8001
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index d988b92..01bed2f 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -328,6 +328,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL,
USB_DEVICE_ID_SYMBOL_SCANNER_3),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD),
+ HID_BATTERY_QUIRK_IGNORE },
{}
};
diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
index 0e3fb1a..6909d9c 100644
--- a/drivers/hid/hid-lg2ff.c
+++ b/drivers/hid/hid-lg2ff.c
@@ -62,11 +62,17 @@ int lg2ff_init(struct hid_device *hid)
{
struct lg2ff_device *lg2ff;
struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
int error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
/* Check that the report looks ok */
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
if (!report)
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c
index 8c2da18..acf739f 100644
--- a/drivers/hid/hid-lg3ff.c
+++ b/drivers/hid/hid-lg3ff.c
@@ -129,12 +129,19 @@ static const signed short ff3_joystick_ac[] = {
int lg3ff_init(struct hid_device *hid)
{
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
const signed short *ff_bits = ff3_joystick_ac;
int error;
int i;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
/* Check that the report looks ok */
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
return -ENODEV;
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 4b26928..ef80c59 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -1259,8 +1259,8 @@ static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_produc
int lg4ff_init(struct hid_device *hid)
{
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
@@ -1272,6 +1272,13 @@ int lg4ff_init(struct hid_device *hid)
int mmode_ret, mmode_idx = -1;
u16 real_product_id;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
/* Check that the report looks ok */
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -1;
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index e1394af..1871cdc 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -127,12 +127,19 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
int lgff_init(struct hid_device* hid)
{
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
const signed short *ff_bits = ff_joystick;
int error;
int i;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
/* Check that the report looks ok */
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index e642cfa..504e891 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -978,6 +978,9 @@ static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp,
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
NULL, 0, &response);
+ /* Ignore these intermittent errors */
+ if (ret == HIDPP_ERROR_RESOURCE_ERROR)
+ return -EIO;
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
@@ -1867,8 +1870,8 @@ static void hidpp_ff_destroy(struct ff_device *ff)
static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
{
struct hid_device *hid = hidpp->hid_dev;
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
struct ff_device *ff;
@@ -1877,6 +1880,13 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
int error, j, num_slots;
u8 version;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
if (!dev) {
hid_err(hid, "Struct input_dev not set!\n");
return -EINVAL;
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 4865e67..e5aafda 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -96,6 +96,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641), HID_QUIRK_ALWAYS_POLL },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X), HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/hid/hid-qvr.c b/drivers/hid/hid-qvr.c
index 6df6010..8ece1d7 100644
--- a/drivers/hid/hid-qvr.c
+++ b/drivers/hid/hid-qvr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,7 +46,8 @@
#include "hid-qvr.h"
#include "hid-trace.h"
-#define WAIT_EVENT_INT_TOUT 20
+#define TIME_OUT_START_STOP_MS 500
+#define TIME_OUT_READ_WRITE_MS 20
#define QVR_START_IMU _IO('q', 1)
#define QVR_STOP_IMU _IO('q', 2)
@@ -63,6 +64,7 @@ struct qvr_buf_index {
uint8_t padding[60];
};
+// struct must be 64 bit aligned
struct qvr_sensor_t {
uint64_t gts;
uint64_t ats;
@@ -70,13 +72,19 @@ struct qvr_sensor_t {
s32 gx;
s32 gy;
s32 gz;
+ u32 gNumerator;
+ u32 gDenominator;
s32 ax;
s32 ay;
s32 az;
+ u32 aNumerator;
+ u32 aDenominator;
s32 mx;
s32 my;
s32 mz;
- uint8_t padding[4];
+ u32 mNumerator;
+ u32 mDenominator;
+ uint8_t padding[44];
};
struct qvr_calib_data {
@@ -105,7 +113,6 @@ struct qvr_external_sensor {
static DECLARE_WAIT_QUEUE_HEAD(wq);
static struct qvr_external_sensor qvr_external_sensor;
-static uint8_t DEBUG_ORIENTATION;
static int read_calibration_len(void)
{
@@ -128,7 +135,7 @@ static int read_calibration_len(void)
ret = wait_event_interruptible_timeout(wq,
sensor->calib_data_len != -1,
- msecs_to_jiffies(WAIT_EVENT_INT_TOUT));
+ msecs_to_jiffies(TIME_OUT_READ_WRITE_MS));
if (ret == 0) {
kfree(hid_buf);
return -ETIME;
@@ -173,7 +180,7 @@ static uint8_t *read_calibration_data(void)
HID_REQ_SET_REPORT);
ret = wait_event_interruptible_timeout(wq,
sensor->calib_data_recv == 1,
- msecs_to_jiffies(WAIT_EVENT_INT_TOUT));
+ msecs_to_jiffies(TIME_OUT_READ_WRITE_MS));
if (ret == 0) {
pr_err("%s:get calibration data timeout\n", __func__);
kfree(hid_buf);
@@ -221,7 +228,7 @@ static int control_imu_stream(bool status)
HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
ret = wait_event_interruptible_timeout(wq, sensor->ext_ack == 1,
- msecs_to_jiffies(WAIT_EVENT_INT_TOUT));
+ msecs_to_jiffies(TIME_OUT_START_STOP_MS));
if (!ret && status) {
pr_debug("qvr: falling back - start IMU stream failed\n");
hid_buf[0] = QVR_HID_REPORT_ID_CAL;
@@ -255,10 +262,6 @@ static int qvr_send_package_wrap(u8 *message, int msize, struct hid_device *hid)
memcpy((void *)&imuData, (void *)message,
sizeof(struct external_imu_format));
if (!sensor->ts_base) {
- if (imuData.gNumerator == 1 && imuData.aNumerator == 1)
- DEBUG_ORIENTATION = 1;
- else
- DEBUG_ORIENTATION = 0;
pr_debug("qvr msize = %d reportID=%d padding=%d\n"
"qvr version=%d numImu=%d nspip=%d pSize=%d\n"
"qvr imuID=%d sampleID=%d temp=%d\n",
@@ -285,9 +288,9 @@ static int qvr_send_package_wrap(u8 *message, int msize, struct hid_device *hid)
if (!sensor->ts_offset)
sensor->ts_offset = imuData.gts0;
index_buf = (struct qvr_buf_index *)((uintptr_t)sensor->vaddr +
- (sensor->vsize / 2) + (8 * sizeof(*sensor_buf)));
- sensor_buf = (struct qvr_sensor_t *)((uintptr_t)sensor->vaddr +
(sensor->vsize / 2));
+ sensor_buf = (struct qvr_sensor_t *)((uintptr_t)sensor->vaddr +
+ (sensor->vsize / 2) + sizeof(struct qvr_buf_index));
data = (struct qvr_sensor_t *)&(sensor_buf[buf_index]);
if (sensor->ts_offset > imuData.gts0)
@@ -302,27 +305,30 @@ static int qvr_send_package_wrap(u8 *message, int msize, struct hid_device *hid)
data->mts = data->ats;
data->gts = data->ats;
- if (DEBUG_ORIENTATION == 1) {
- data->ax = -imuData.ax0;
- data->ay = imuData.ay0;
- data->az = -imuData.az0;
- data->gx = -imuData.gx0;
- data->gy = imuData.gy0;
- data->gz = -imuData.gz0;
- data->mx = -imuData.my0;
- data->my = -imuData.mx0;
- data->mz = -imuData.mz0;
- } else {
- data->ax = -imuData.ay0;
- data->ay = -imuData.ax0;
- data->az = -imuData.az0;
- data->gx = -imuData.gy0;
- data->gy = -imuData.gx0;
- data->gz = -imuData.gz0;
- data->mx = -imuData.my0;
- data->my = -imuData.mx0;
- data->mz = -imuData.mz0;
- }
+ data->ax = imuData.ax0;
+ data->ay = imuData.ay0;
+ data->az = imuData.az0;
+ data->gx = imuData.gx0;
+ data->gy = imuData.gy0;
+ data->gz = imuData.gz0;
+ data->mx = imuData.my0;
+ data->my = imuData.mx0;
+ data->mz = imuData.mz0;
+ data->ax = imuData.ax0;
+ data->ay = imuData.ay0;
+ data->az = imuData.az0;
+ data->gx = imuData.gx0;
+ data->gy = imuData.gy0;
+ data->gz = imuData.gz0;
+ data->mx = imuData.my0;
+ data->my = imuData.mx0;
+ data->mz = imuData.mz0;
+ data->aNumerator = imuData.aNumerator;
+ data->aDenominator = imuData.aDenominator;
+ data->gNumerator = imuData.gNumerator;
+ data->gDenominator = imuData.gDenominator;
+ data->mNumerator = imuData.mNumerator;
+ data->mDenominator = imuData.mDenominator;
trace_qvr_recv_sensor("gyro", data->gts, data->gx, data->gy, data->gz);
trace_qvr_recv_sensor("accel", data->ats, data->ax, data->ay, data->az);
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 9e33165..a5b6b2b 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -737,7 +737,8 @@ static void rmi_remove(struct hid_device *hdev)
{
struct rmi_data *hdata = hid_get_drvdata(hdev);
- if (hdata->device_flags & RMI_DEVICE) {
+ if ((hdata->device_flags & RMI_DEVICE)
+ && test_bit(RMI_STARTED, &hdata->flags)) {
clear_bit(RMI_STARTED, &hdata->flags);
cancel_work_sync(&hdata->reset_work);
rmi_unregister_transport_device(&hdata->xport);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 09f2c61..d05c387 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -2249,9 +2249,15 @@ static int sony_play_effect(struct input_dev *dev, void *data,
static int sony_init_ff(struct sony_sc *sc)
{
- struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
- struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *input_dev;
+
+ if (list_empty(&sc->hdev->inputs)) {
+ hid_err(sc->hdev, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(sc->hdev->inputs.next, struct hid_input, list);
+ input_dev = hidinput->input;
input_set_capability(input_dev, EV_FF, FF_RUMBLE);
return input_ff_create_memless(input_dev, NULL, sony_play_effect);
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index dc4128b..8dae0f9 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -283,11 +283,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
static int steam_input_open(struct input_dev *dev)
{
struct steam_device *steam = input_get_drvdata(dev);
- int ret;
-
- ret = hid_hw_open(steam->hdev);
- if (ret)
- return ret;
mutex_lock(&steam->mutex);
if (!steam->client_opened && lizard_mode)
@@ -304,8 +299,6 @@ static void steam_input_close(struct input_dev *dev)
if (!steam->client_opened && lizard_mode)
steam_set_lizard_mode(steam, true);
mutex_unlock(&steam->mutex);
-
- hid_hw_close(steam->hdev);
}
static enum power_supply_property steam_battery_props[] = {
@@ -506,6 +499,7 @@ static void steam_battery_unregister(struct steam_device *steam)
static int steam_register(struct steam_device *steam)
{
int ret;
+ bool client_opened;
/*
* This function can be called several times in a row with the
@@ -518,9 +512,11 @@ static int steam_register(struct steam_device *steam)
* Unlikely, but getting the serial could fail, and it is not so
* important, so make up a serial number and go on.
*/
+ mutex_lock(&steam->mutex);
if (steam_get_serial(steam) < 0)
strlcpy(steam->serial_no, "XXXXXXXXXX",
sizeof(steam->serial_no));
+ mutex_unlock(&steam->mutex);
hid_info(steam->hdev, "Steam Controller '%s' connected",
steam->serial_no);
@@ -535,14 +531,16 @@ static int steam_register(struct steam_device *steam)
}
mutex_lock(&steam->mutex);
- if (!steam->client_opened) {
+ client_opened = steam->client_opened;
+ if (!client_opened)
steam_set_lizard_mode(steam, lizard_mode);
- ret = steam_input_register(steam);
- } else {
- ret = 0;
- }
mutex_unlock(&steam->mutex);
+ if (!client_opened)
+ ret = steam_input_register(steam);
+ else
+ ret = 0;
+
return ret;
}
@@ -623,11 +621,6 @@ static void steam_client_ll_stop(struct hid_device *hdev)
static int steam_client_ll_open(struct hid_device *hdev)
{
struct steam_device *steam = hdev->driver_data;
- int ret;
-
- ret = hid_hw_open(steam->hdev);
- if (ret)
- return ret;
mutex_lock(&steam->mutex);
steam->client_opened = true;
@@ -635,22 +628,28 @@ static int steam_client_ll_open(struct hid_device *hdev)
steam_input_unregister(steam);
- return ret;
+ return 0;
}
static void steam_client_ll_close(struct hid_device *hdev)
{
struct steam_device *steam = hdev->driver_data;
+ unsigned long flags;
+ bool connected;
+
+ spin_lock_irqsave(&steam->lock, flags);
+ connected = steam->connected;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
mutex_lock(&steam->mutex);
steam->client_opened = false;
+ if (connected)
+ steam_set_lizard_mode(steam, lizard_mode);
mutex_unlock(&steam->mutex);
- hid_hw_close(steam->hdev);
- if (steam->connected) {
- steam_set_lizard_mode(steam, lizard_mode);
+ if (connected)
steam_input_register(steam);
- }
}
static int steam_client_ll_raw_request(struct hid_device *hdev,
@@ -759,14 +758,15 @@ static int steam_probe(struct hid_device *hdev,
if (ret)
goto client_hdev_add_fail;
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev,
+ "%s:hid_hw_open\n",
+ __func__);
+ goto hid_hw_open_fail;
+ }
+
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
- ret = hid_hw_open(hdev);
- if (ret) {
- hid_err(hdev,
- "%s:hid_hw_open for wireless\n",
- __func__);
- goto hid_hw_open_fail;
- }
hid_info(hdev, "Steam wireless receiver connected");
steam_request_conn_status(steam);
} else {
@@ -781,8 +781,8 @@ static int steam_probe(struct hid_device *hdev,
return 0;
-hid_hw_open_fail:
input_register_fail:
+hid_hw_open_fail:
client_hdev_add_fail:
hid_hw_stop(hdev);
hid_hw_start_fail:
@@ -809,8 +809,8 @@ static void steam_remove(struct hid_device *hdev)
cancel_work_sync(&steam->work_connect);
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver disconnected");
- hid_hw_close(hdev);
}
+ hid_hw_close(hdev);
hid_hw_stop(hdev);
steam_unregister(steam);
}
diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c
index 30b8c32..efe8c2a 100644
--- a/drivers/hid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -136,12 +136,18 @@ static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
struct tmff_device *tmff;
struct hid_report *report;
struct list_head *report_list;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *input_dev;
int error;
int i;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ input_dev = hidinput->input;
+
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
if (!tmff)
return -ENOMEM;
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index a29756c..4e7e01b 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -66,11 +66,17 @@ static int zpff_init(struct hid_device *hid)
{
struct zpff_device *zpff;
struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct input_dev *dev = hidinput->input;
+ struct hid_input *hidinput;
+ struct input_dev *dev;
int i, error;
+ if (list_empty(&hid->inputs)) {
+ hid_err(hid, "no inputs found\n");
+ return -ENODEV;
+ }
+ hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ dev = hidinput->input;
+
for (i = 0; i < 4; i++) {
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
if (!report)
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index 3cde7c1..0a39e44 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -50,6 +50,8 @@
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
#define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2)
#define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3)
+#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
+#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5)
/* flags */
#define I2C_HID_STARTED 0
@@ -177,6 +179,12 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_4B33,
I2C_HID_QUIRK_DELAY_AFTER_SLEEP },
+ { USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001,
+ I2C_HID_QUIRK_NO_RUNTIME_PM },
+ { USB_VENDOR_ID_ELAN, HID_ANY_ID,
+ I2C_HID_QUIRK_BOGUS_IRQ },
+ { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,
+ I2C_HID_QUIRK_RESET_ON_RESUME },
{ 0, 0 }
};
@@ -501,6 +509,12 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return;
}
+ if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) {
+ dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but "
+ "there's no data\n", __func__);
+ return;
+ }
+
if ((ret_size > size) || (ret_size < 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
@@ -1279,8 +1293,15 @@ static int i2c_hid_resume(struct device *dev)
* solves "incomplete reports" on Raydium devices 2386:3118 and
* 2386:4B33 and fixes various SIS touchscreens no longer sending
* data after a suspend/resume.
+ *
+ * However some ALPS touchpads generate IRQ storm without reset, so
+ * let's still reset them here.
*/
- ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
+ ret = i2c_hid_hwreset(client);
+ else
+ ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+
if (ret)
return ret;
diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
index cac262a..10af858 100644
--- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
@@ -323,6 +323,25 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
.driver_data = (void *)&sipodev_desc
},
{
+ /*
+ * There are at least 2 Primebook C11B versions, the older
+ * version has a product-name of "Primebook C11B", and a
+ * bios version / release / firmware revision of:
+ * V2.1.2 / 05/03/2018 / 18.2
+ * The new version has "PRIMEBOOK C11B" as product-name and a
+ * bios version / release / firmware revision of:
+ * CFALKSW05_BIOS_V1.1.2 / 11/19/2018 / 19.2
+ * Only the older version needs this quirk, note the newer
+ * version will not match as it has a different product-name.
+ */
+ .ident = "Trekstor Primebook C11B",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11B"),
+ },
+ .driver_data = (void *)&sipodev_desc
+ },
+ {
.ident = "Direkt-Tek DTLAPY116-2",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
@@ -331,6 +350,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
.driver_data = (void *)&sipodev_desc
},
{
+ .ident = "Direkt-Tek DTLAPY133-1",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY133-1"),
+ },
+ .driver_data = (void *)&sipodev_desc
+ },
+ {
.ident = "Mediacom Flexbook Edge 11",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
@@ -338,6 +365,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
},
.driver_data = (void *)&sipodev_desc
},
+ {
+ .ident = "Odys Winbook 13",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AXDIA International GmbH"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WINBOOK 13"),
+ },
+ .driver_data = (void *)&sipodev_desc
+ },
{ } /* Terminate list */
};
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
index cd23903..e918d78 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
@@ -222,7 +222,7 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
err_hid_device:
kfree(hid_data);
err_hid_data:
- kfree(hid);
+ hid_destroy_device(hid);
return rv;
}
diff --git a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c
index b9b917d..c41dbb1 100644
--- a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c
+++ b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c
@@ -90,7 +90,7 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
return 0;
out:
dev_err(&cl->device->dev, "error in allocating Tx pool\n");
- ishtp_cl_free_rx_ring(cl);
+ ishtp_cl_free_tx_ring(cl);
return -ENOMEM;
}
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 3c37c3c..9c0900c 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -205,6 +205,21 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac,
}
}
+/*
+ * Convert a signed 32-bit integer to an unsigned n-bit integer. Undoes
+ * the normally-helpful work of 'hid_snto32' for fields that use signed
+ * ranges for questionable reasons.
+ */
+static inline __u32 wacom_s32tou(s32 value, __u8 n)
+{
+ switch (n) {
+ case 8: return ((__u8)value);
+ case 16: return ((__u16)value);
+ case 32: return ((__u32)value);
+ }
+ return value & (1 << (n - 1)) ? value & (~(~0U << n)) : value;
+}
+
extern const struct hid_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 1df037e..77bb469 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -2271,7 +2271,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
case HID_DG_TOOLSERIALNUMBER:
if (value) {
wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL);
- wacom_wac->serial[0] |= (__u32)value;
+ wacom_wac->serial[0] |= wacom_s32tou(value, field->report_size);
}
return;
case HID_DG_TWIST:
@@ -2287,15 +2287,17 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
return;
case WACOM_HID_WD_SERIALHI:
if (value) {
+ __u32 raw_value = wacom_s32tou(value, field->report_size);
+
wacom_wac->serial[0] = (wacom_wac->serial[0] & 0xFFFFFFFF);
- wacom_wac->serial[0] |= ((__u64)value) << 32;
+ wacom_wac->serial[0] |= ((__u64)raw_value) << 32;
/*
* Non-USI EMR devices may contain additional tool type
* information here. See WACOM_HID_WD_TOOLTYPE case for
* more details.
*/
if (value >> 20 == 1) {
- wacom_wac->id[0] |= value & 0xFFFFF;
+ wacom_wac->id[0] |= raw_value & 0xFFFFF;
}
}
return;
@@ -2307,7 +2309,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
* bitwise OR so the complete value can be built
* up over time :(
*/
- wacom_wac->id[0] |= value;
+ wacom_wac->id[0] |= wacom_s32tou(value, field->report_size);
return;
case WACOM_HID_WD_OFFSETLEFT:
if (features->offset_left && value != features->offset_left)
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index fdb0f83..5e51553 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -91,11 +91,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
unsigned long flags;
int ret, err = 0;
struct page *page;
+ unsigned int order;
if (send_ringbuffer_size % PAGE_SIZE ||
recv_ringbuffer_size % PAGE_SIZE)
return -EINVAL;
+ order = get_order(send_ringbuffer_size + recv_ringbuffer_size);
+
spin_lock_irqsave(&newchannel->lock, flags);
if (newchannel->state == CHANNEL_OPEN_STATE) {
newchannel->state = CHANNEL_OPENING_STATE;
@@ -110,21 +113,17 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
/* Allocate the ring buffer */
page = alloc_pages_node(cpu_to_node(newchannel->target_cpu),
- GFP_KERNEL|__GFP_ZERO,
- get_order(send_ringbuffer_size +
- recv_ringbuffer_size));
+ GFP_KERNEL|__GFP_ZERO, order);
if (!page)
- page = alloc_pages(GFP_KERNEL|__GFP_ZERO,
- get_order(send_ringbuffer_size +
- recv_ringbuffer_size));
+ page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order);
if (!page) {
err = -ENOMEM;
goto error_set_chnstate;
}
- newchannel->ringbuffer_pages = page_address(page);
+ newchannel->ringbuffer_page = page;
newchannel->ringbuffer_pagecount = (send_ringbuffer_size +
recv_ringbuffer_size) >> PAGE_SHIFT;
@@ -239,8 +238,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
error_free_pages:
hv_ringbuffer_cleanup(&newchannel->outbound);
hv_ringbuffer_cleanup(&newchannel->inbound);
- __free_pages(page,
- get_order(send_ringbuffer_size + recv_ringbuffer_size));
+ __free_pages(page, order);
error_set_chnstate:
newchannel->state = CHANNEL_OPEN_STATE;
return err;
@@ -666,8 +664,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
hv_ringbuffer_cleanup(&channel->outbound);
hv_ringbuffer_cleanup(&channel->inbound);
- free_pages((unsigned long)channel->ringbuffer_pages,
- get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
+ __free_pages(channel->ringbuffer_page,
+ get_order(channel->ringbuffer_pagecount << PAGE_SHIFT));
out:
return ret;
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 8e923e7..12bc9fa 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -189,6 +189,17 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
int hv_synic_alloc(void)
{
int cpu;
+ struct hv_per_cpu_context *hv_cpu;
+
+ /*
+ * First, zero all per-cpu memory areas so hv_synic_free() can
+ * detect what memory has been allocated and cleanup properly
+ * after any failures.
+ */
+ for_each_present_cpu(cpu) {
+ hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
+ memset(hv_cpu, 0, sizeof(*hv_cpu));
+ }
hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask),
GFP_KERNEL);
@@ -198,10 +209,8 @@ int hv_synic_alloc(void)
}
for_each_present_cpu(cpu) {
- struct hv_per_cpu_context *hv_cpu
- = per_cpu_ptr(hv_context.cpu_context, cpu);
+ hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
- memset(hv_cpu, 0, sizeof(*hv_cpu));
tasklet_init(&hv_cpu->msg_dpc,
vmbus_on_msg_dpc, (unsigned long) hv_cpu);
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index e6b4950..8c95553 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -38,9 +38,9 @@
#define INA3221_WARN3 0x0c
#define INA3221_MASK_ENABLE 0x0f
-#define INA3221_CONFIG_MODE_SHUNT BIT(1)
-#define INA3221_CONFIG_MODE_BUS BIT(2)
-#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3)
+#define INA3221_CONFIG_MODE_SHUNT BIT(0)
+#define INA3221_CONFIG_MODE_BUS BIT(1)
+#define INA3221_CONFIG_MODE_CONTINUOUS BIT(2)
#define INA3221_RSHUNT_DEFAULT 10000
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index bb15d78..2cef0c3 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -325,8 +325,9 @@ static int k10temp_probe(struct pci_dev *pdev,
data->pdev = pdev;
- if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 ||
- boot_cpu_data.x86_model == 0x70)) {
+ if (boot_cpu_data.x86 == 0x15 &&
+ ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
+ (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
data->read_htcreg = read_htcreg_nb_f15;
data->read_tempreg = read_tempreg_nb_f15;
} else if (boot_cpu_data.x86 == 0x17) {
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index eba692c..559101a 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -704,10 +704,10 @@ static const char *const nct6795_temp_label[] = {
"PCH_CHIP_TEMP",
"PCH_CPU_TEMP",
"PCH_MCH_TEMP",
- "PCH_DIM0_TEMP",
- "PCH_DIM1_TEMP",
- "PCH_DIM2_TEMP",
- "PCH_DIM3_TEMP",
+ "Agent0 Dimm0",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
"BYTE_TEMP0",
"BYTE_TEMP1",
"PECI Agent 0 Calibration",
@@ -742,10 +742,10 @@ static const char *const nct6796_temp_label[] = {
"PCH_CHIP_TEMP",
"PCH_CPU_TEMP",
"PCH_MCH_TEMP",
- "PCH_DIM0_TEMP",
- "PCH_DIM1_TEMP",
- "PCH_DIM2_TEMP",
- "PCH_DIM3_TEMP",
+ "Agent0 Dimm0",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
"BYTE_TEMP0",
"BYTE_TEMP1",
"PECI Agent 0 Calibration",
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
index b998f9f..979b579 100644
--- a/drivers/hwmon/npcm750-pwm-fan.c
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -52,7 +52,7 @@
/* Define the Counter Register, value = 100 for match 100% */
#define NPCM7XX_PWM_COUNTER_DEFAULT_NUM 255
-#define NPCM7XX_PWM_CMR_DEFAULT_NUM 127
+#define NPCM7XX_PWM_CMR_DEFAULT_NUM 255
#define NPCM7XX_PWM_CMR_MAX 255
/* default all PWM channels PRESCALE2 = 1 */
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 7f01fad..65de80b 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -221,8 +221,12 @@ static int pwm_fan_probe(struct platform_device *pdev)
ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
if (IS_ERR(ctx->pwm)) {
- dev_err(&pdev->dev, "Could not get PWM\n");
- return PTR_ERR(ctx->pwm);
+ ret = PTR_ERR(ctx->pwm);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Could not get PWM: %d\n", ret);
+
+ return ret;
}
platform_set_drvdata(pdev, ctx);
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ed3f2c0..50e7363 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -29,4 +29,4 @@
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
-obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o
+obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o apss_tgu.o
diff --git a/drivers/hwtracing/coresight/apss_tgu.c b/drivers/hwtracing/coresight/apss_tgu.c
new file mode 100644
index 0000000..5cbd10f
--- /dev/null
+++ b/drivers/hwtracing/coresight/apss_tgu.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <soc/qcom/tgu.h>
+#define CREATE_TRACE_POINTS
+#include "trace/events/tgu.h"
+#include "apss_tgu.h"
+
+struct tgu_test_notifier tgu_notify;
+int register_tgu_notifier(struct tgu_test_notifier *tgu_test)
+{
+ tgu_notify.cb = tgu_test->cb;
+ return 0;
+}
+EXPORT_SYMBOL(register_tgu_notifier);
+
+int unregister_tgu_notifier(struct tgu_test_notifier *tgu_test)
+{
+ if (tgu_test->cb == tgu_notify.cb)
+ tgu_notify.cb = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(unregister_tgu_notifier);
+
+irqreturn_t tgu_irq_thread_handler(int irq, void *dev_id)
+{
+ if (tgu_notify.cb)
+ tgu_notify.cb();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tgu_irq_handler(int irq, void *data)
+{
+ trace_tgu_interrupt(irq);
+ return IRQ_WAKE_THREAD;
+}
+
+int register_interrupt_handler(struct device_node *node)
+{
+ int irq, ret, i, n;
+
+ n = of_irq_count(node);
+ pr_debug("number of irqs == %d\n", n);
+
+ for (i = 0; i < n; i++) {
+ irq = of_irq_get(node, i);
+ if (irq < 0) {
+ pr_err("Invalid IRQ for error fatal %u\n", irq);
+ return irq;
+ }
+
+ ret = request_threaded_irq(irq, tgu_irq_handler,
+ tgu_irq_thread_handler,
+ IRQF_TRIGGER_RISING, "apps-tgu", NULL);
+ if (ret < 0) {
+ pr_err("Unable to register IRQ handler %d\n", irq);
+ return ret;
+ }
+
+ ret = irq_set_irq_wake(irq, true);
+ if (ret < 0) {
+ pr_err("Unable to set as wakeup irq %d\n", irq);
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/hwtracing/coresight/apss_tgu.h b/drivers/hwtracing/coresight/apss_tgu.h
new file mode 100644
index 0000000..793840e
--- /dev/null
+++ b/drivers/hwtracing/coresight/apss_tgu.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __QCOM_APSS_TGU_H__
+#define __QCOM_APSS_TGU_H__
+
+int register_interrupt_handler(struct device_node *node);
+#endif /* __QCOM_APSS_TGU_H__ */
diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c
index 775f363..6a19c6b 100644
--- a/drivers/hwtracing/coresight/coresight-csr.c
+++ b/drivers/hwtracing/coresight/coresight-csr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2013, 2015-2017, 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 2015-2017, 2019-2020 The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -66,8 +66,11 @@ do { \
#define BLKSIZE_1024 2
#define BLKSIZE_2048 3
+#define FLUSHPERIOD_1 0x1
#define FLUSHPERIOD_2048 0x800
+#define PERFLSHEOT_BIT BIT(18)
+
struct csr_drvdata {
void __iomem *base;
phys_addr_t pbase;
@@ -79,6 +82,7 @@ struct csr_drvdata {
struct clk *clk;
spinlock_t spin_lock;
bool usb_bam_support;
+ bool perflsheot_set_support;
bool hwctrl_set_support;
bool set_byte_cntr_support;
bool timestamp_support;
@@ -153,6 +157,8 @@ void msm_qdss_csr_enable_flush(struct coresight_csr *csr)
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl |= 0x2;
+ if (drvdata->perflsheot_set_support)
+ usbflshctrl |= PERFLSHEOT_BIT;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
@@ -205,6 +211,8 @@ void msm_qdss_csr_disable_flush(struct coresight_csr *csr)
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl &= ~0x2;
+ if (drvdata->perflsheot_set_support)
+ usbflshctrl &= ~PERFLSHEOT_BIT;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
@@ -479,8 +487,16 @@ static int csr_probe(struct platform_device *pdev)
dev_dbg(dev, "aodbg_csr_support operation not supported\n");
else
dev_dbg(dev, "aodbg_csr_support operation supported\n");
+ drvdata->perflsheot_set_support = of_property_read_bool(
+ pdev->dev.of_node, "qcom,perflsheot-set-support");
+ if (!drvdata->perflsheot_set_support)
+ dev_dbg(dev, "perflsheot_set_support handled by other subsystem\n");
+ else
+ dev_dbg(dev, "perflsheot_set_support operation supported\n");
- if (drvdata->usb_bam_support)
+ if (drvdata->perflsheot_set_support)
+ drvdata->flushperiod = FLUSHPERIOD_1;
+ else if (drvdata->usb_bam_support)
drvdata->flushperiod = FLUSHPERIOD_2048;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c
index f6d0571..d31f1d8 100644
--- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c
@@ -34,26 +34,42 @@ struct replicator_state {
struct coresight_device *csdev;
};
+/*
+ * replicator_reset : Reset the replicator configuration to sane values.
+ */
+static void replicator_reset(struct replicator_state *drvdata)
+{
+ CS_UNLOCK(drvdata->base);
+
+ writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
+ writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
+
+ CS_LOCK(drvdata->base);
+}
+
static int replicator_enable(struct coresight_device *csdev, int inport,
int outport)
{
+ u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
+ switch (outport) {
+ case 0:
+ reg = REPLICATOR_IDFILTER0;
+ break;
+ case 1:
+ reg = REPLICATOR_IDFILTER1;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
CS_UNLOCK(drvdata->base);
- /*
- * Ensure that the other port is disabled
- * 0x00 - passing through the replicator unimpeded
- * 0xff - disable (or impede) the flow of ATB data
- */
- if (outport == 0) {
- writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER0);
- writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
- } else {
- writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER1);
- writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
- }
+ /* Ensure that the outport is enabled. */
+ writel_relaxed(0x00, drvdata->base + reg);
CS_LOCK(drvdata->base);
dev_info(drvdata->dev, "REPLICATOR enabled\n");
@@ -63,15 +79,25 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
static void replicator_disable(struct coresight_device *csdev, int inport,
int outport)
{
+ u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
+ switch (outport) {
+ case 0:
+ reg = REPLICATOR_IDFILTER0;
+ break;
+ case 1:
+ reg = REPLICATOR_IDFILTER1;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
CS_UNLOCK(drvdata->base);
/* disable the flow of ATB data through port */
- if (outport == 0)
- writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
- else
- writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
+ writel_relaxed(0xff, drvdata->base + reg);
CS_LOCK(drvdata->base);
@@ -156,7 +182,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
desc.groups = replicator_groups;
drvdata->csdev = coresight_register(&desc);
- return PTR_ERR_OR_ZERO(drvdata->csdev);
+ if (!IS_ERR(drvdata->csdev)) {
+ replicator_reset(drvdata);
+ return 0;
+ }
+ return PTR_ERR(drvdata->csdev);
}
#ifdef CONFIG_PM
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 44451fa..284fbb3 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -12,6 +12,7 @@
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/perf_event.h>
+#include <linux/percpu-defs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/workqueue.h>
@@ -33,7 +34,7 @@ struct etm_event_data {
struct work_struct work;
cpumask_t mask;
void *snk_config;
- struct list_head **path;
+ struct list_head * __percpu *path;
};
static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle);
@@ -61,6 +62,18 @@ static const struct attribute_group *etm_pmu_attr_groups[] = {
NULL,
};
+static inline struct list_head **
+etm_event_cpu_path_ptr(struct etm_event_data *data, int cpu)
+{
+ return per_cpu_ptr(data->path, cpu);
+}
+
+static inline struct list_head *
+etm_event_cpu_path(struct etm_event_data *data, int cpu)
+{
+ return *etm_event_cpu_path_ptr(data, cpu);
+}
+
static void etm_event_read(struct perf_event *event) {}
static int etm_addr_filters_alloc(struct perf_event *event)
@@ -121,7 +134,7 @@ static void free_event_data(struct work_struct *work)
*/
if (event_data->snk_config) {
cpu = cpumask_first(mask);
- sink = coresight_get_sink(event_data->path[cpu]);
+ sink = coresight_get_sink(etm_event_cpu_path(event_data, cpu));
if (sink_ops(sink)->free_buffer)
sink_ops(sink)->free_buffer(event_data->snk_config);
}
@@ -132,13 +145,12 @@ static void free_event_data(struct work_struct *work)
coresight_release_path(source, event_data->path[cpu]);
}
- kfree(event_data->path);
+ free_percpu(event_data->path);
kfree(event_data);
}
static void *alloc_event_data(int cpu)
{
- int size;
cpumask_t *mask;
struct etm_event_data *event_data;
@@ -149,7 +161,6 @@ static void *alloc_event_data(int cpu)
/* Make sure nothing disappears under us */
get_online_cpus();
- size = num_online_cpus();
mask = &event_data->mask;
if (cpu != -1)
@@ -166,8 +177,8 @@ static void *alloc_event_data(int cpu)
* unused memory when dealing with single CPU trace scenarios is small
* compared to the cost of searching through an optimized array.
*/
- event_data->path = kcalloc(size,
- sizeof(struct list_head *), GFP_KERNEL);
+ event_data->path = alloc_percpu(struct list_head *);
+
if (!event_data->path) {
kfree(event_data);
return NULL;
@@ -215,6 +226,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
/* Setup the path for each CPU in a trace session */
for_each_cpu(cpu, mask) {
+ struct list_head *path;
struct coresight_device *csdev;
csdev = per_cpu(csdev_src, cpu);
@@ -226,9 +238,11 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
* list of devices from source to sink that can be
* referenced later when the path is actually needed.
*/
- event_data->path[cpu] = coresight_build_path(csdev, sink);
- if (IS_ERR(event_data->path[cpu]))
+ path = coresight_build_path(csdev, sink);
+ if (IS_ERR(path))
goto err;
+
+ *etm_event_cpu_path_ptr(event_data, cpu) = path;
}
if (!sink_ops(sink)->alloc_buffer)
@@ -257,6 +271,7 @@ static void etm_event_start(struct perf_event *event, int flags)
struct etm_event_data *event_data;
struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+ struct list_head *path;
if (!csdev)
goto fail;
@@ -269,8 +284,9 @@ static void etm_event_start(struct perf_event *event, int flags)
if (!event_data)
goto fail;
+ path = etm_event_cpu_path(event_data, cpu);
/* We need a sink, no need to continue without one */
- sink = coresight_get_sink(event_data->path[cpu]);
+ sink = coresight_get_sink(path);
if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer))
goto fail_end_stop;
@@ -280,7 +296,7 @@ static void etm_event_start(struct perf_event *event, int flags)
goto fail_end_stop;
/* Nothing will happen without a path */
- if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
+ if (coresight_enable_path(path, CS_MODE_PERF))
goto fail_end_stop;
/* Tell the perf core the event is alive */
@@ -288,11 +304,13 @@ static void etm_event_start(struct perf_event *event, int flags)
/* Finally enable the tracer */
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
- goto fail_end_stop;
+ goto fail_disable_path;
out:
return;
+fail_disable_path:
+ coresight_disable_path(path);
fail_end_stop:
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
perf_aux_output_end(handle, 0);
@@ -308,6 +326,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
struct etm_event_data *event_data = perf_get_aux(handle);
+ struct list_head *path;
if (event->hw.state == PERF_HES_STOPPED)
return;
@@ -315,7 +334,11 @@ static void etm_event_stop(struct perf_event *event, int mode)
if (!csdev)
return;
- sink = coresight_get_sink(event_data->path[cpu]);
+ path = etm_event_cpu_path(event_data, cpu);
+ if (!path)
+ return;
+
+ sink = coresight_get_sink(path);
if (!sink)
return;
@@ -346,7 +369,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
}
/* Disabling the path make its elements available to other sessions */
- coresight_disable_path(event_data->path[cpu]);
+ coresight_disable_path(path);
}
static int etm_event_add(struct perf_event *event, int mode)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index a1213c0..4dd59ed 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -386,7 +386,7 @@ static ssize_t mode_store(struct device *dev,
/* bit[12], Low-power state behavior override bit */
if ((config->mode & ETM_MODE_LPOVERRIDE) &&
- (drvdata->lpoverride == true))
+ (drvdata->lpoverride == true) && !drvdata->tupwr_disable)
config->eventctrl1 |= BIT(12);
else
config->eventctrl1 &= ~BIT(12);
@@ -655,10 +655,13 @@ static ssize_t cyc_threshold_store(struct device *dev,
if (kstrtoul(buf, 16, &val))
return -EINVAL;
+
+ /* mask off max threshold before checking min value */
+ val &= ETM_CYC_THRESHOLD_MASK;
if (val < drvdata->ccitmin)
return -EINVAL;
- config->ccctlr = val & ETM_CYC_THRESHOLD_MASK;
+ config->ccctlr = val;
return size;
}
static DEVICE_ATTR_RW(cyc_threshold);
@@ -689,14 +692,16 @@ static ssize_t bb_ctrl_store(struct device *dev,
return -EINVAL;
if (!drvdata->nr_addr_cmp)
return -EINVAL;
+
/*
- * Bit[7:0] selects which address range comparator is used for
- * branch broadcast control.
+ * Bit[8] controls include(1) / exclude(0), bits[0-7] select
+ * individual range comparators. If include then at least 1
+ * range must be selected.
*/
- if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp)
+ if ((val & BIT(8)) && (BMVAL(val, 0, 7) == 0))
return -EINVAL;
- config->bb_ctrl = val;
+ config->bb_ctrl = val & GENMASK(8, 0);
return size;
}
static DEVICE_ATTR_RW(bb_ctrl);
@@ -1329,8 +1334,8 @@ static ssize_t seq_event_store(struct device *dev,
spin_lock(&drvdata->spinlock);
idx = config->seq_idx;
- /* RST, bits[7:0] */
- config->seq_ctrl[idx] = val & 0xFF;
+ /* Seq control has two masks B[15:8] F[7:0] */
+ config->seq_ctrl[idx] = val & 0xFFFF;
spin_unlock(&drvdata->spinlock);
return size;
}
@@ -1585,7 +1590,7 @@ static ssize_t res_ctrl_store(struct device *dev,
if (idx % 2 != 0)
/* PAIRINV, bit[21] */
val &= ~BIT(21);
- config->res_ctrl[idx] = val;
+ config->res_ctrl[idx] = val & GENMASK(21, 0);
spin_unlock(&drvdata->spinlock);
return size;
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 9e894c9..3f4b0b4 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#include <asm/sections.h>
#include <asm/local.h>
+#include <asm/virt.h>
#include "coresight-etm4x.h"
#include "coresight-etm-perf.h"
@@ -620,7 +621,7 @@ static void etm4_set_default_config(struct etmv4_config *config)
config->vinst_ctrl |= BIT(0);
}
-static u64 etm4_get_access_type(struct etmv4_config *config)
+static u64 etm4_get_ns_access_type(struct etmv4_config *config)
{
u64 access_type = 0;
@@ -631,17 +632,26 @@ static u64 etm4_get_access_type(struct etmv4_config *config)
* Bit[13] Exception level 1 - OS
* Bit[14] Exception level 2 - Hypervisor
* Bit[15] Never implemented
- *
- * Always stay away from hypervisor mode.
*/
- access_type = ETM_EXLEVEL_NS_HYP;
-
- if (config->mode & ETM_MODE_EXCL_KERN)
- access_type |= ETM_EXLEVEL_NS_OS;
+ if (!is_kernel_in_hyp_mode()) {
+ /* Stay away from hypervisor mode for non-VHE */
+ access_type = ETM_EXLEVEL_NS_HYP;
+ if (config->mode & ETM_MODE_EXCL_KERN)
+ access_type |= ETM_EXLEVEL_NS_OS;
+ } else if (config->mode & ETM_MODE_EXCL_KERN) {
+ access_type = ETM_EXLEVEL_NS_HYP;
+ }
if (config->mode & ETM_MODE_EXCL_USER)
access_type |= ETM_EXLEVEL_NS_APP;
+ return access_type;
+}
+
+static u64 etm4_get_access_type(struct etmv4_config *config)
+{
+ u64 access_type = etm4_get_ns_access_type(config);
+
/*
* EXLEVEL_S, bits[11:8], don't trace anything happening
* in secure state.
@@ -895,20 +905,10 @@ void etm4_config_trace_mode(struct etmv4_config *config)
addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP];
/* clear default config */
- addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS);
+ addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS |
+ ETM_EXLEVEL_NS_HYP);
- /*
- * EXLEVEL_NS, bits[15:12]
- * The Exception levels are:
- * Bit[12] Exception level 0 - Application
- * Bit[13] Exception level 1 - OS
- * Bit[14] Exception level 2 - Hypervisor
- * Bit[15] Never implemented
- */
- if (mode & ETM_MODE_EXCL_KERN)
- addr_acc |= ETM_EXLEVEL_NS_OS;
- else
- addr_acc |= ETM_EXLEVEL_NS_APP;
+ addr_acc |= etm4_get_ns_access_type(config);
config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc;
config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc;
diff --git a/drivers/hwtracing/coresight/coresight-tgu.c b/drivers/hwtracing/coresight/coresight-tgu.c
index 9d24a9a..a0891f5 100644
--- a/drivers/hwtracing/coresight/coresight-tgu.c
+++ b/drivers/hwtracing/coresight/coresight-tgu.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019-2020 The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -15,9 +15,11 @@
#include <linux/amba/bus.h>
#include <linux/topology.h>
#include <linux/of.h>
+#include <linux/string.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
+#include "apss_tgu.h"
#define tgu_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define tgu_readl(drvdata, off) __raw_readl(drvdata->base + off)
@@ -407,6 +409,7 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata;
struct tgu_drvdata *drvdata;
struct coresight_desc *desc;
+ const char *name;
pdata = of_get_coresight_platform_data(dev, adev->dev.of_node);
if (IS_ERR(pdata))
@@ -496,6 +499,13 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
goto err;
}
+ of_property_read_string(adev->dev.of_node, "coresight-name", &name);
+ if (!strcmp(name, "coresight-tgu-apss")) {
+ ret = register_interrupt_handler(adev->dev.of_node);
+ if (ret)
+ return ret;
+ }
+
pm_runtime_put(&adev->dev);
dev_dbg(dev, "TGU initialized\n");
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index a20de48..594d987 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -447,10 +447,10 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev,
case TMC_MEM_INTF_WIDTH_32BITS:
case TMC_MEM_INTF_WIDTH_64BITS:
case TMC_MEM_INTF_WIDTH_128BITS:
- mask = GENMASK(31, 5);
+ mask = GENMASK(31, 4);
break;
case TMC_MEM_INTF_WIDTH_256BITS:
- mask = GENMASK(31, 6);
+ mask = GENMASK(31, 5);
break;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index a7adfd7..f312436 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -544,7 +544,7 @@ tmc_init_etr_sg_table(struct device *dev, int node,
sg_table = tmc_alloc_sg_table(dev, node, nr_tpages, nr_dpages, pages);
if (IS_ERR(sg_table)) {
kfree(etr_table);
- return ERR_PTR(PTR_ERR(sg_table));
+ return ERR_CAST(sg_table);
}
etr_table->sg_table = sg_table;
@@ -917,10 +917,15 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata)
tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset);
}
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
+ struct etr_buf *etr_buf)
{
u32 axictl, sts;
- struct etr_buf *etr_buf = drvdata->etr_buf;
+
+ /* Callers should provide an appropriate buffer for use */
+ if (WARN_ON(!etr_buf || drvdata->etr_buf))
+ return;
+ drvdata->etr_buf = etr_buf;
/*
* If this ETR is connected to a CATU, enable it before we turn
@@ -990,13 +995,16 @@ void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
* also updating the @bufpp on where to find it. Since the trace data
* starts at anywhere in the buffer, depending on the RRP, we adjust the
* @len returned to handle buffer wrapping around.
+ *
+ * We are protected here by drvdata->reading != 0, which ensures the
+ * sysfs_buf stays alive.
*/
ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
{
s64 offset;
ssize_t actual = len;
- struct etr_buf *etr_buf = drvdata->etr_buf;
+ struct etr_buf *etr_buf = drvdata->sysfs_buf;
if (pos + actual > etr_buf->len)
actual = etr_buf->len - pos;
@@ -1026,7 +1034,14 @@ tmc_etr_free_sysfs_buf(struct etr_buf *buf)
static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata)
{
- tmc_sync_etr_buf(drvdata);
+ struct etr_buf *etr_buf = drvdata->etr_buf;
+
+ if (WARN_ON(drvdata->sysfs_buf != etr_buf)) {
+ tmc_etr_free_sysfs_buf(drvdata->sysfs_buf);
+ drvdata->sysfs_buf = NULL;
+ } else {
+ tmc_sync_etr_buf(drvdata);
+ }
}
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
@@ -1047,6 +1062,8 @@ void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
/* Disable CATU device if this ETR is connected to one */
tmc_etr_disable_catu(drvdata);
+ /* Reset the ETR buf used by hardware */
+ drvdata->etr_buf = NULL;
}
static int tmc_etr_fill_usb_bam_data(struct tmc_drvdata *drvdata)
@@ -1326,7 +1343,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
int ret = 0;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etr_buf *new_buf = NULL, *free_buf = NULL;
+ struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL;
/*
* If we are enabling the ETR from disabled state, we need to make
@@ -1337,7 +1354,9 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* with the lock released.
*/
spin_lock_irqsave(&drvdata->spinlock, flags);
- if (!drvdata->etr_buf || (drvdata->etr_buf->size != drvdata->size)) {
+ sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
+ if (!sysfs_buf || (sysfs_buf->size != drvdata->size)
+ || !drvdata->usbch) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
@@ -1402,10 +1421,10 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* If we don't have a buffer or it doesn't match the requested size,
* use the buffer allocated above. Otherwise reuse the existing buffer.
*/
- if (!drvdata->etr_buf ||
- (new_buf && drvdata->etr_buf->size != new_buf->size)) {
- free_buf = drvdata->etr_buf;
- drvdata->etr_buf = new_buf;
+ sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
+ if (!sysfs_buf || (new_buf && sysfs_buf->size != new_buf->size)) {
+ free_buf = sysfs_buf;
+ drvdata->sysfs_buf = new_buf;
}
drvdata->mode = CS_MODE_SYSFS;
@@ -1413,7 +1432,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM ||
(drvdata->out_mode == TMC_ETR_OUT_MODE_USB
&& drvdata->byte_cntr->sw_usb))
- tmc_etr_enable_hw(drvdata);
+ tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
drvdata->enable = true;
out:
@@ -1477,10 +1496,12 @@ static void _tmc_disable_etr_sink(struct coresight_device *csdev)
flags);
tmc_etr_bam_disable(drvdata);
usb_qdss_close(drvdata->usbch);
+ drvdata->usbch = NULL;
drvdata->mode = CS_MODE_DISABLED;
goto out;
} else {
usb_qdss_close(drvdata->usbch);
+ drvdata->usbch = NULL;
tmc_etr_disable_hw(drvdata);
}
} else {
@@ -1607,8 +1628,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
goto out;
}
- /* If drvdata::etr_buf is NULL the trace data has been read already */
- if (drvdata->etr_buf == NULL) {
+ /* If sysfs_buf is NULL the trace data has been read already */
+ if (!drvdata->sysfs_buf) {
ret = -EINVAL;
goto out;
}
@@ -1618,7 +1639,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
goto out;
}
- /* Disable the TMC if need be */
+ /* Disable the TMC if we are trying to read from a running session */
if (drvdata->mode == CS_MODE_SYSFS) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
coresight_disable_all_source_link();
@@ -1637,7 +1658,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
unsigned long flags;
- struct etr_buf *etr_buf = NULL;
+ struct etr_buf *sysfs_buf = NULL;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
@@ -1653,7 +1674,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
* buffer. Since the tracer is still enabled drvdata::buf can't
* be NULL.
*/
- tmc_etr_enable_hw(drvdata);
+ tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
coresight_enable_all_source_link();
@@ -1663,15 +1684,15 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
* The ETR is not tracing and the buffer was just read.
* As such prepare to free the trace buffer.
*/
- etr_buf = drvdata->etr_buf;
- drvdata->etr_buf = NULL;
+ sysfs_buf = drvdata->sysfs_buf;
+ drvdata->sysfs_buf = NULL;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free allocated memory out side of the spinlock */
- if (etr_buf)
- tmc_free_etr_buf(etr_buf);
+ if (sysfs_buf)
+ tmc_etr_free_sysfs_buf(sysfs_buf);
mutex_unlock(&drvdata->mem_lock);
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 0f93cfe..b3cdae3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -217,6 +217,7 @@ struct etr_buf {
* @trigger_cntr: amount of words to store after a trigger.
* @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the
* device configuration register (DEVID)
+ * @sysfs_data: SYSFS buffer for ETR.
*/
struct tmc_drvdata {
void __iomem *base;
@@ -237,6 +238,7 @@ struct tmc_drvdata {
struct mutex mem_lock;
u32 trigger_cntr;
u32 etr_caps;
+ struct etr_buf *sysfs_buf;
struct coresight_csr *csr;
const char *csr_name;
bool enable;
@@ -311,7 +313,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
void tmc_free_etr_buf(struct etr_buf *etr_buf);
void __tmc_etr_disable_to_bam(struct tmc_drvdata *drvdata);
void tmc_etr_bam_disable(struct tmc_drvdata *drvdata);
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, struct etr_buf *etr_buf);
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
void usb_notifier(void *priv, unsigned int event, struct qdss_request *d_req,
struct usb_qdss_ch *ch);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index f99efc1..fdbd088 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -268,17 +268,19 @@ static int coresight_enable_sink(struct coresight_device *csdev, u32 mode)
{
int ret;
- if (!csdev->enable) {
- if (sink_ops(csdev)->enable) {
- ret = coresight_enable_reg_clk(csdev);
- if (ret)
- return ret;
+ /*
+ * We need to make sure the "new" session is compatible with the
+ * existing "mode" of operation.
+ */
+ if (sink_ops(csdev)->enable) {
+ ret = coresight_enable_reg_clk(csdev);
+ if (ret)
+ return ret;
- ret = sink_ops(csdev)->enable(csdev, mode);
- if (ret) {
- coresight_disable_reg_clk(csdev);
- return ret;
- }
+ ret = sink_ops(csdev)->enable(csdev, mode);
+ if (ret) {
+ coresight_disable_reg_clk(csdev);
+ return ret;
}
csdev->enable = true;
}
@@ -530,8 +532,14 @@ int coresight_enable_path(struct list_head *path, u32 mode)
switch (type) {
case CORESIGHT_DEV_TYPE_SINK:
ret = coresight_enable_sink(csdev, mode);
+ /*
+ * Sink is the first component turned on. If we
+ * failed to enable the sink, there are no components
+ * that need disabling. Disabling the path here
+ * would mean we could disrupt an existing session.
+ */
if (ret)
- goto err;
+ goto out;
break;
case CORESIGHT_DEV_TYPE_SOURCE:
/* sources are enabled from either sysFS or Perf */
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index fc6b7f8..a8e7502 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -629,10 +629,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
}
err = intel_th_device_add_resources(thdev, res, subdev->nres);
- if (err) {
- put_device(&thdev->dev);
+ if (err)
goto fail_put_device;
- }
if (subdev->type == INTEL_TH_OUTPUT) {
thdev->dev.devt = MKDEV(th->major, th->num_thdevs);
@@ -646,10 +644,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
}
err = device_add(&thdev->dev);
- if (err) {
- put_device(&thdev->dev);
+ if (err)
goto fail_free_res;
- }
/* need switch driver to be loaded to enumerate the rest */
if (subdev->type == INTEL_TH_SWITCH && !req) {
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 968319f..e63a0c2 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -176,15 +176,45 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
+ /* Comet Lake PCH */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x06a6),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Comet Lake PCH-V */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa3a6),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
/* Ice Lake NNPI */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5),
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
+ /* Ice Lake CPU */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8a29),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Tiger Lake CPU */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9a33),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
/* Tiger Lake PCH */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6),
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
+ {
+ /* Jasper Lake PCH */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4da6),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Elkhart Lake */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4b26),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
{ 0 },
};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 8f80381..ee6dd1b 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -433,12 +433,13 @@
If you do not need KONA I2C interface, say N.
config I2C_BRCMSTB
- tristate "BRCM Settop I2C controller"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+ tristate "BRCM Settop/DSL I2C controller"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_63XX || \
+ COMPILE_TEST
default y
help
If you say yes to this option, support will be included for the
- I2C interface on the Broadcom Settop SoCs.
+ I2C interface on the Broadcom Settop/DSL SoCs.
If you do not need I2C interface, say N.
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index a19fbff..d9401b5 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -137,7 +137,8 @@ struct aspeed_i2c_bus {
/* Synchronizes I/O mem access to base. */
spinlock_t lock;
struct completion cmd_complete;
- u32 (*get_clk_reg_val)(u32 divisor);
+ u32 (*get_clk_reg_val)(struct device *dev,
+ u32 divisor);
unsigned long parent_clk_frequency;
u32 bus_frequency;
/* Transaction state. */
@@ -686,16 +687,27 @@ static const struct i2c_algorithm aspeed_i2c_algo = {
#endif /* CONFIG_I2C_SLAVE */
};
-static u32 aspeed_i2c_get_clk_reg_val(u32 clk_high_low_max, u32 divisor)
+static u32 aspeed_i2c_get_clk_reg_val(struct device *dev,
+ u32 clk_high_low_mask,
+ u32 divisor)
{
- u32 base_clk, clk_high, clk_low, tmp;
+ u32 base_clk_divisor, clk_high_low_max, clk_high, clk_low, tmp;
+
+ /*
+ * SCL_high and SCL_low represent a value 1 greater than what is stored
+ * since a zero divider is meaningless. Thus, the max value each can
+ * store is every bit set + 1. Since SCL_high and SCL_low are added
+ * together (see below), the max value of both is the max value of one
+ * them times two.
+ */
+ clk_high_low_max = (clk_high_low_mask + 1) * 2;
/*
* The actual clock frequency of SCL is:
* SCL_freq = APB_freq / (base_freq * (SCL_high + SCL_low))
* = APB_freq / divisor
* where base_freq is a programmable clock divider; its value is
- * base_freq = 1 << base_clk
+ * base_freq = 1 << base_clk_divisor
* SCL_high is the number of base_freq clock cycles that SCL stays high
* and SCL_low is the number of base_freq clock cycles that SCL stays
* low for a period of SCL.
@@ -705,47 +717,59 @@ static u32 aspeed_i2c_get_clk_reg_val(u32 clk_high_low_max, u32 divisor)
* SCL_low = clk_low + 1
* Thus,
* SCL_freq = APB_freq /
- * ((1 << base_clk) * (clk_high + 1 + clk_low + 1))
+ * ((1 << base_clk_divisor) * (clk_high + 1 + clk_low + 1))
* The documentation recommends clk_high >= clk_high_max / 2 and
* clk_low >= clk_low_max / 2 - 1 when possible; this last constraint
* gives us the following solution:
*/
- base_clk = divisor > clk_high_low_max ?
+ base_clk_divisor = divisor > clk_high_low_max ?
ilog2((divisor - 1) / clk_high_low_max) + 1 : 0;
- tmp = (divisor + (1 << base_clk) - 1) >> base_clk;
- clk_low = tmp / 2;
- clk_high = tmp - clk_low;
- if (clk_high)
- clk_high--;
+ if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) {
+ base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK;
+ clk_low = clk_high_low_mask;
+ clk_high = clk_high_low_mask;
+ dev_err(dev,
+ "clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.\n",
+ divisor, (1 << base_clk_divisor) * clk_high_low_max);
+ } else {
+ tmp = (divisor + (1 << base_clk_divisor) - 1)
+ >> base_clk_divisor;
+ clk_low = tmp / 2;
+ clk_high = tmp - clk_low;
- if (clk_low)
- clk_low--;
+ if (clk_high)
+ clk_high--;
+
+ if (clk_low)
+ clk_low--;
+ }
return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT)
& ASPEED_I2CD_TIME_SCL_HIGH_MASK)
| ((clk_low << ASPEED_I2CD_TIME_SCL_LOW_SHIFT)
& ASPEED_I2CD_TIME_SCL_LOW_MASK)
- | (base_clk & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK);
+ | (base_clk_divisor
+ & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK);
}
-static u32 aspeed_i2c_24xx_get_clk_reg_val(u32 divisor)
+static u32 aspeed_i2c_24xx_get_clk_reg_val(struct device *dev, u32 divisor)
{
/*
* clk_high and clk_low are each 3 bits wide, so each can hold a max
* value of 8 giving a clk_high_low_max of 16.
*/
- return aspeed_i2c_get_clk_reg_val(16, divisor);
+ return aspeed_i2c_get_clk_reg_val(dev, GENMASK(2, 0), divisor);
}
-static u32 aspeed_i2c_25xx_get_clk_reg_val(u32 divisor)
+static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor)
{
/*
* clk_high and clk_low are each 4 bits wide, so each can hold a max
* value of 16 giving a clk_high_low_max of 32.
*/
- return aspeed_i2c_get_clk_reg_val(32, divisor);
+ return aspeed_i2c_get_clk_reg_val(dev, GENMASK(3, 0), divisor);
}
/* precondition: bus.lock has been acquired. */
@@ -758,7 +782,7 @@ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK |
ASPEED_I2CD_TIME_THDSTA_MASK |
ASPEED_I2CD_TIME_TACST_MASK);
- clk_reg_val |= bus->get_clk_reg_val(divisor);
+ clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor);
writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1);
writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2);
@@ -874,7 +898,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
if (!match)
bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val;
else
- bus->get_clk_reg_val = (u32 (*)(u32))match->data;
+ bus->get_clk_reg_val = (u32 (*)(struct device *, u32))
+ match->data;
/* Initialize the I2C adapter */
spin_lock_init(&bus->lock);
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 7bd409e..d4b72e4 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1090,7 +1090,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
/* Get I2C clock */
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
- dev_err(&pdev->dev, "can't get I2C clock\n");
+ if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "can't get I2C clock\n");
return PTR_ERR(i2c_imx->clk);
}
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 1e57f58..2bb4d20 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -441,6 +441,8 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
u16 control_reg;
u16 restart_flag = 0;
u32 reg_4g_mode;
+ u8 *dma_rd_buf = NULL;
+ u8 *dma_wr_buf = NULL;
dma_addr_t rpaddr = 0;
dma_addr_t wpaddr = 0;
int ret;
@@ -500,11 +502,19 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (i2c->op == I2C_MASTER_RD) {
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON);
- rpaddr = dma_map_single(i2c->dev, msgs->buf,
- msgs->len, DMA_FROM_DEVICE);
- if (dma_mapping_error(i2c->dev, rpaddr))
+
+ dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
+ if (!dma_rd_buf)
return -ENOMEM;
+ rpaddr = dma_map_single(i2c->dev, dma_rd_buf,
+ msgs->len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(i2c->dev, rpaddr)) {
+ i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, false);
+
+ return -ENOMEM;
+ }
+
if (i2c->dev_comp->support_33bits) {
reg_4g_mode = mtk_i2c_set_4g_mode(rpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE);
@@ -515,11 +525,19 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
} else if (i2c->op == I2C_MASTER_WR) {
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
- wpaddr = dma_map_single(i2c->dev, msgs->buf,
- msgs->len, DMA_TO_DEVICE);
- if (dma_mapping_error(i2c->dev, wpaddr))
+
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
+ if (!dma_wr_buf)
return -ENOMEM;
+ wpaddr = dma_map_single(i2c->dev, dma_wr_buf,
+ msgs->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(i2c->dev, wpaddr)) {
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false);
+
+ return -ENOMEM;
+ }
+
if (i2c->dev_comp->support_33bits) {
reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
@@ -530,16 +548,39 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
} else {
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
- wpaddr = dma_map_single(i2c->dev, msgs->buf,
- msgs->len, DMA_TO_DEVICE);
- if (dma_mapping_error(i2c->dev, wpaddr))
+
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
+ if (!dma_wr_buf)
return -ENOMEM;
- rpaddr = dma_map_single(i2c->dev, (msgs + 1)->buf,
+
+ wpaddr = dma_map_single(i2c->dev, dma_wr_buf,
+ msgs->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(i2c->dev, wpaddr)) {
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false);
+
+ return -ENOMEM;
+ }
+
+ dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 1);
+ if (!dma_rd_buf) {
+ dma_unmap_single(i2c->dev, wpaddr,
+ msgs->len, DMA_TO_DEVICE);
+
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false);
+
+ return -ENOMEM;
+ }
+
+ rpaddr = dma_map_single(i2c->dev, dma_rd_buf,
(msgs + 1)->len,
DMA_FROM_DEVICE);
if (dma_mapping_error(i2c->dev, rpaddr)) {
dma_unmap_single(i2c->dev, wpaddr,
msgs->len, DMA_TO_DEVICE);
+
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false);
+ i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), false);
+
return -ENOMEM;
}
@@ -578,14 +619,21 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (i2c->op == I2C_MASTER_WR) {
dma_unmap_single(i2c->dev, wpaddr,
msgs->len, DMA_TO_DEVICE);
+
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true);
} else if (i2c->op == I2C_MASTER_RD) {
dma_unmap_single(i2c->dev, rpaddr,
msgs->len, DMA_FROM_DEVICE);
+
+ i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, true);
} else {
dma_unmap_single(i2c->dev, wpaddr, msgs->len,
DMA_TO_DEVICE);
dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len,
DMA_FROM_DEVICE);
+
+ i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true);
+ i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), true);
}
if (ret == 0) {
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 2ac8609..cd9c65f 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -661,9 +661,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop);
- if (msg->len == 0)
- return -EINVAL;
-
omap->receiver = !!(msg->flags & I2C_M_RD);
omap_i2c_resize_fifo(omap, msg->len, omap->receiver);
@@ -1179,6 +1176,10 @@ static const struct i2c_algorithm omap_i2c_algo = {
.functionality = omap_i2c_func,
};
+static const struct i2c_adapter_quirks omap_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
#ifdef CONFIG_OF
static struct omap_i2c_bus_platform_data omap2420_pdata = {
.rev = OMAP_I2C_IP_VERSION_1,
@@ -1453,6 +1454,7 @@ omap_i2c_probe(struct platform_device *pdev)
adap->class = I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
adap->algo = &omap_i2c_algo;
+ adap->quirks = &omap_i2c_quirks;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
adap->bus_recovery_info = &omap_i2c_bus_recovery_info;
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index c86c3ae..e09cd07 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1088,11 +1088,6 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
for (idx = 0; idx < num; idx++) {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
- }
-
if (qup_i2c_poll_state_i2c_master(qup)) {
ret = -EIO;
goto out;
@@ -1520,9 +1515,6 @@ qup_i2c_determine_mode_v2(struct qup_i2c_dev *qup,
/* All i2c_msgs should be transferred using either dma or cpu */
for (idx = 0; idx < num; idx++) {
- if (msgs[idx].len == 0)
- return -EINVAL;
-
if (msgs[idx].flags & I2C_M_RD)
max_rx_len = max_t(unsigned int, max_rx_len,
msgs[idx].len);
@@ -1636,9 +1628,14 @@ static const struct i2c_algorithm qup_i2c_algo_v2 = {
* which limits the possible read to 256 (QUP_READ_LIMIT) bytes.
*/
static const struct i2c_adapter_quirks qup_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
.max_read_len = QUP_READ_LIMIT,
};
+static const struct i2c_adapter_quirks qup_i2c_quirks_v2 = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
{
clk_prepare_enable(qup->clk);
@@ -1701,6 +1698,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
is_qup_v1 = true;
} else {
qup->adap.algo = &qup_i2c_algo_v2;
+ qup->adap.quirks = &qup_i2c_quirks_v2;
is_qup_v1 = false;
if (acpi_match_device(qup_i2c_acpi_match, qup->dev))
goto nodma;
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index ac9c948..f4e3613 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -297,7 +297,7 @@ struct stm32f7_i2c_dev {
bool use_dma;
};
-/**
+/*
* All these values are coming from I2C Specification, Version 6.0, 4th of
* April 2014.
*
@@ -1177,6 +1177,8 @@ static void stm32f7_i2c_slave_start(struct stm32f7_i2c_dev *i2c_dev)
STM32F7_I2C_CR1_TXIE;
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+ /* Write 1st data byte */
+ writel_relaxed(value, base + STM32F7_I2C_TXDR);
} else {
/* Notify i2c slave that new write transfer is starting */
i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
@@ -1486,7 +1488,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
void __iomem *base = i2c_dev->base;
struct device *dev = i2c_dev->dev;
struct stm32_i2c_dma *dma = i2c_dev->dma;
- u32 mask, status;
+ u32 status;
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
@@ -1511,12 +1513,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
f7_msg->result = -EINVAL;
}
- /* Disable interrupts */
- if (stm32f7_i2c_is_slave_registered(i2c_dev))
- mask = STM32F7_I2C_XFER_IRQ_MASK;
- else
- mask = STM32F7_I2C_ALL_IRQ_MASK;
- stm32f7_i2c_disable_irq(i2c_dev, mask);
+ if (!i2c_dev->slave_running) {
+ u32 mask;
+ /* Disable interrupts */
+ if (stm32f7_i2c_is_slave_registered(i2c_dev))
+ mask = STM32F7_I2C_XFER_IRQ_MASK;
+ else
+ mask = STM32F7_I2C_ALL_IRQ_MASK;
+ stm32f7_i2c_disable_irq(i2c_dev, mask);
+ }
/* Disable dma */
if (i2c_dev->use_dma) {
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index ef13b6c..47d196c 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -684,9 +684,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
tegra_i2c_flush_fifos(i2c_dev);
- if (msg->len == 0)
- return -EINVAL;
-
i2c_dev->msg_buf = msg->buf;
i2c_dev->msg_buf_remaining = msg->len;
i2c_dev->msg_err = I2C_ERR_NONE;
@@ -831,6 +828,7 @@ static const struct i2c_algorithm tegra_i2c_algo = {
/* payload size is only 12 bit */
static const struct i2c_adapter_quirks tegra_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
.max_read_len = 4096,
.max_write_len = 4096 - 12,
};
diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index bc26ec8..dd0687e 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -98,6 +98,7 @@ struct uniphier_fi2c_priv {
unsigned int flags;
unsigned int busy_cnt;
unsigned int clk_cycle;
+ spinlock_t lock; /* IRQ synchronization */
};
static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv,
@@ -142,9 +143,10 @@ static void uniphier_fi2c_set_irqs(struct uniphier_fi2c_priv *priv)
writel(priv->enabled_irqs, priv->membase + UNIPHIER_FI2C_IE);
}
-static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv)
+static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv,
+ u32 mask)
{
- writel(-1, priv->membase + UNIPHIER_FI2C_IC);
+ writel(mask, priv->membase + UNIPHIER_FI2C_IC);
}
static void uniphier_fi2c_stop(struct uniphier_fi2c_priv *priv)
@@ -162,7 +164,10 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
struct uniphier_fi2c_priv *priv = dev_id;
u32 irq_status;
+ spin_lock(&priv->lock);
+
irq_status = readl(priv->membase + UNIPHIER_FI2C_INT);
+ irq_status &= priv->enabled_irqs;
dev_dbg(&priv->adap.dev,
"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
@@ -207,7 +212,13 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) {
uniphier_fi2c_drain_rxfifo(priv);
- if (!priv->len)
+ /*
+ * If the number of bytes to read is multiple of the FIFO size
+ * (msg->len == 8, 16, 24, ...), the INT_RF bit is set a little
+ * earlier than INT_RB. We wait for INT_RB to confirm the
+ * completion of the current message.
+ */
+ if (!priv->len && (irq_status & UNIPHIER_FI2C_INT_RB))
goto data_done;
if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
@@ -230,6 +241,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
goto handled;
}
+ spin_unlock(&priv->lock);
+
return IRQ_NONE;
data_done:
@@ -244,7 +257,14 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
}
handled:
- uniphier_fi2c_clear_irqs(priv);
+ /*
+ * This controller makes a pause while any bit of the IRQ status is
+ * asserted. Clear the asserted bit to kick the controller just before
+ * exiting the handler.
+ */
+ uniphier_fi2c_clear_irqs(priv, irq_status);
+
+ spin_unlock(&priv->lock);
return IRQ_HANDLED;
}
@@ -252,6 +272,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
{
priv->enabled_irqs |= UNIPHIER_FI2C_INT_TE;
+ uniphier_fi2c_set_irqs(priv);
+
/* do not use TX byte counter */
writel(0, priv->membase + UNIPHIER_FI2C_TBC);
/* set slave address */
@@ -284,6 +306,8 @@ static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr)
priv->enabled_irqs |= UNIPHIER_FI2C_INT_RF;
}
+ uniphier_fi2c_set_irqs(priv);
+
/* set slave address with RD bit */
writel(UNIPHIER_FI2C_DTTX_CMD | UNIPHIER_FI2C_DTTX_RD | addr << 1,
priv->membase + UNIPHIER_FI2C_DTTX);
@@ -307,14 +331,16 @@ static void uniphier_fi2c_recover(struct uniphier_fi2c_priv *priv)
}
static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
- struct i2c_msg *msg, bool stop)
+ struct i2c_msg *msg, bool repeat,
+ bool stop)
{
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
bool is_read = msg->flags & I2C_M_RD;
- unsigned long time_left;
+ unsigned long time_left, flags;
- dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
- is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
+ dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, repeat=%d, stop=%d\n",
+ is_read ? "receive" : "transmit", msg->addr, msg->len,
+ repeat, stop);
priv->len = msg->len;
priv->buf = msg->buf;
@@ -326,22 +352,36 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
priv->flags |= UNIPHIER_FI2C_STOP;
reinit_completion(&priv->comp);
- uniphier_fi2c_clear_irqs(priv);
+ uniphier_fi2c_clear_irqs(priv, U32_MAX);
writel(UNIPHIER_FI2C_RST_TBRST | UNIPHIER_FI2C_RST_RBRST,
priv->membase + UNIPHIER_FI2C_RST); /* reset TX/RX FIFO */
+ spin_lock_irqsave(&priv->lock, flags);
+
if (is_read)
uniphier_fi2c_rx_init(priv, msg->addr);
else
uniphier_fi2c_tx_init(priv, msg->addr);
- uniphier_fi2c_set_irqs(priv);
-
dev_dbg(&adap->dev, "start condition\n");
- writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STA,
- priv->membase + UNIPHIER_FI2C_CR);
+ /*
+ * For a repeated START condition, writing a slave address to the FIFO
+ * kicks the controller. So, the UNIPHIER_FI2C_CR register should be
+ * written only for a non-repeated START condition.
+ */
+ if (!repeat)
+ writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STA,
+ priv->membase + UNIPHIER_FI2C_CR);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->enabled_irqs = 0;
+ uniphier_fi2c_set_irqs(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
if (!time_left) {
dev_err(&adap->dev, "transaction timeout.\n");
uniphier_fi2c_recover(priv);
@@ -394,6 +434,7 @@ static int uniphier_fi2c_master_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct i2c_msg *msg, *emsg = msgs + num;
+ bool repeat = false;
int ret;
ret = uniphier_fi2c_check_bus_busy(adap);
@@ -404,9 +445,11 @@ static int uniphier_fi2c_master_xfer(struct i2c_adapter *adap,
/* Emit STOP if it is the last message or I2C_M_STOP is set. */
bool stop = (msg + 1 == emsg) || (msg->flags & I2C_M_STOP);
- ret = uniphier_fi2c_master_xfer_one(adap, msg, stop);
+ ret = uniphier_fi2c_master_xfer_one(adap, msg, repeat, stop);
if (ret)
return ret;
+
+ repeat = !stop;
}
return num;
@@ -546,6 +589,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
priv->clk_cycle = clk_rate / bus_speed;
init_completion(&priv->comp);
+ spin_lock_init(&priv->lock);
priv->adap.owner = THIS_MODULE;
priv->adap.algo = &uniphier_fi2c_algo;
priv->adap.dev.parent = dev;
diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c
index 48281c1..b8f9e02 100644
--- a/drivers/i2c/busses/i2c-zx2967.c
+++ b/drivers/i2c/busses/i2c-zx2967.c
@@ -281,9 +281,6 @@ static int zx2967_i2c_xfer_msg(struct zx2967_i2c *i2c,
int ret;
int i;
- if (msg->len == 0)
- return -EINVAL;
-
zx2967_i2c_flush_fifos(i2c);
i2c->cur_trans = msg->buf;
@@ -498,6 +495,10 @@ static const struct i2c_algorithm zx2967_i2c_algo = {
.functionality = zx2967_i2c_func,
};
+static const struct i2c_adapter_quirks zx2967_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static const struct of_device_id zx2967_i2c_of_match[] = {
{ .compatible = "zte,zx296718-i2c", },
{ },
@@ -568,6 +569,7 @@ static int zx2967_i2c_probe(struct platform_device *pdev)
strlcpy(i2c->adap.name, "zx2967 i2c adapter",
sizeof(i2c->adap.name));
i2c->adap.algo = &zx2967_i2c_algo;
+ i2c->adap.quirks = &zx2967_i2c_quirks;
i2c->adap.nr = pdev->id;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 32affd3..559c3b1 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -43,6 +43,7 @@ struct i2c_acpi_lookup {
int index;
u32 speed;
u32 min_speed;
+ u32 force_speed;
};
static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
@@ -240,6 +241,19 @@ i2c_acpi_match_device(const struct acpi_device_id *matches,
return acpi_match_device(matches, &client->dev);
}
+static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
+ /*
+ * These Silead touchscreen controllers only work at 400KHz, for
+ * some reason they do not work at 100KHz. On some devices the ACPI
+ * tables list another device at their bus as only being capable
+ * of 100KHz, testing has shown that these other devices work fine
+ * at 400KHz (as can be expected of any recent i2c hw) so we force
+ * the speed of the bus to 400 KHz if a Silead device is present.
+ */
+ { "MSSL1680", 0 },
+ {}
+};
+
static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
void *data, void **return_value)
{
@@ -258,6 +272,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
if (lookup->speed <= lookup->min_speed)
lookup->min_speed = lookup->speed;
+ if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0)
+ lookup->force_speed = 400000;
+
return AE_OK;
}
@@ -295,7 +312,16 @@ u32 i2c_acpi_find_bus_speed(struct device *dev)
return 0;
}
- return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
+ if (lookup.force_speed) {
+ if (lookup.force_speed != lookup.min_speed)
+ dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n",
+ lookup.min_speed, lookup.force_speed);
+ return lookup.force_speed;
+ } else if (lookup.min_speed != UINT_MAX) {
+ return lookup.min_speed;
+ } else {
+ return 0;
+ }
}
EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);
diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 0f01cdb..14d4884 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -253,14 +253,14 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
}
client = of_i2c_register_device(adap, rd->dn);
- put_device(&adap->dev);
-
if (IS_ERR(client)) {
dev_err(&adap->dev, "failed to create client for '%pOF'\n",
rd->dn);
+ put_device(&adap->dev);
of_node_clear_flag(rd->dn, OF_POPULATED);
return notifier_from_errno(PTR_ERR(client));
}
+ put_device(&adap->dev);
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* already depopulated? */
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 383c802..cb8c98a 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -125,7 +125,7 @@
#define BMC150_ACCEL_SLEEP_1_SEC 0x0F
#define BMC150_ACCEL_REG_TEMP 0x08
-#define BMC150_ACCEL_TEMP_CENTER_VAL 24
+#define BMC150_ACCEL_TEMP_CENTER_VAL 23
#define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2))
#define BMC150_AUTO_SUSPEND_DELAY_MS 2000
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
index c64c667..4ab052d 100644
--- a/drivers/iio/adc/dln2-adc.c
+++ b/drivers/iio/adc/dln2-adc.c
@@ -527,6 +527,10 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
u16 conflict;
unsigned int trigger_chan;
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret)
+ return ret;
+
mutex_lock(&dln2->mutex);
/* Enable ADC */
@@ -540,6 +544,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
(int)conflict);
ret = -EBUSY;
}
+ iio_triggered_buffer_predisable(indio_dev);
return ret;
}
@@ -553,6 +558,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
mutex_unlock(&dln2->mutex);
if (ret < 0) {
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ iio_triggered_buffer_predisable(indio_dev);
return ret;
}
} else {
@@ -560,12 +566,12 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
mutex_unlock(&dln2->mutex);
}
- return iio_triggered_buffer_postenable(indio_dev);
+ return 0;
}
static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
{
- int ret;
+ int ret, ret2;
struct dln2_adc *dln2 = iio_priv(indio_dev);
mutex_lock(&dln2->mutex);
@@ -580,12 +586,14 @@ static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
ret = dln2_adc_set_port_enabled(dln2, false, NULL);
mutex_unlock(&dln2->mutex);
- if (ret < 0) {
+ if (ret < 0)
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
- return ret;
- }
- return iio_triggered_buffer_predisable(indio_dev);
+ ret2 = iio_triggered_buffer_predisable(indio_dev);
+ if (ret == 0)
+ ret = ret2;
+
+ return ret;
}
static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 311c1a8..0939eb0 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -460,6 +460,14 @@ static int max1027_probe(struct spi_device *spi)
goto fail_dev_register;
}
+ /* Internal reset */
+ st->reg = MAX1027_RST_REG;
+ ret = spi_write(st->spi, &st->reg, 1);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to reset the ADC\n");
+ return ret;
+ }
+
/* Disable averaging */
st->reg = MAX1027_AVG_REG;
ret = spi_write(st->spi, &st->reg, 1);
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 49c1956..9f1a5ef 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -92,6 +92,12 @@
#define MAX9611_TEMP_SCALE_NUM 1000000
#define MAX9611_TEMP_SCALE_DIV 2083
+/*
+ * Conversion time is 2 ms (typically) at Ta=25 degreeC
+ * No maximum value is known, so play it safe.
+ */
+#define MAX9611_CONV_TIME_US_RANGE 3000, 3300
+
struct max9611_dev {
struct device *dev;
struct i2c_client *i2c_client;
@@ -239,11 +245,9 @@ static int max9611_read_single(struct max9611_dev *max9611,
return ret;
}
- /*
- * need a delay here to make register configuration
- * stabilize. 1 msec at least, from empirical testing.
- */
- usleep_range(1000, 2000);
+ /* need a delay here to make register configuration stabilize. */
+
+ usleep_range(MAX9611_CONV_TIME_US_RANGE);
ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
if (ret < 0) {
@@ -289,7 +293,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611,
return ret;
if (*adc_raw > 0) {
- *csa_gain = gain_selectors[i];
+ *csa_gain = (enum max9611_csa_gain)gain_selectors[i];
return 0;
}
}
@@ -510,7 +514,7 @@ static int max9611_init(struct max9611_dev *max9611)
MAX9611_REG_CTRL2, 0);
return ret;
}
- usleep_range(1000, 2000);
+ usleep_range(MAX9611_CONV_TIME_US_RANGE);
return 0;
}
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index 5dd104c..6e0ef9b 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -1023,6 +1023,11 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ priv->data->param->regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irq)
return -EINVAL;
@@ -1032,11 +1037,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- priv->data->param->regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
priv->clkin = devm_clk_get(&pdev->dev, "clkin");
if (IS_ERR(priv->clkin)) {
dev_err(&pdev->dev, "failed to get clkin\n");
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index 35ef3c5..a71075f 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/bitops.h>
@@ -26,6 +26,7 @@
#include "qcom-vadc-common.h"
#define ADC_USR_STATUS1 0x8
+#define ADC7_USR_STATUS1_CONV_FAULT BIT(7)
#define ADC_USR_STATUS1_REQ_STS BIT(1)
#define ADC_USR_STATUS1_EOC BIT(0)
#define ADC_USR_STATUS1_REQ_STS_EOC_MASK 0x3
@@ -73,6 +74,9 @@
#define ADC_CHAN_MIN ADC_USBIN
#define ADC_CHAN_MAX ADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
+#define ADC_CHANNEL_OFFSET 0x8
+#define ADC_CHANNEL_MASK 0xff
+
/*
* Conversion time varies between 139uS to 6827uS based on the decimation,
* clock rate, fast average samples with no measurement in queue.
@@ -89,6 +93,11 @@
#define ADC_CAL_DELAY_CTL_VAL_256S 0x73
#define ADC_CAL_DELAY_CTL_VAL_125MS 0x3
+/* For PMIC7 */
+#define ADC_APP_SID 0x40
+#define ADC_APP_SID_MASK 0xf
+#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10)
+
enum adc_cal_method {
ADC_NO_CAL = 0,
ADC_RATIOMETRIC_CAL,
@@ -111,6 +120,7 @@ struct pmic_rev_data {
* @cal_method: calibration method.
* @cal_val: calibration value
* @decimation: sampling rate supported for the channel.
+ * @sid: slave id of PMIC owning the channel, for PMIC7.
* @prescale: channel scaling performed on the input signal.
* @hw_settle_time: the time between AMUX being configured and the
* start of conversion.
@@ -124,6 +134,7 @@ struct adc_channel_prop {
enum adc_cal_method cal_method;
enum adc_cal_val cal_val;
unsigned int decimation;
+ unsigned int sid;
unsigned int prescale;
unsigned int hw_settle_time;
unsigned int avg_samples;
@@ -161,6 +172,7 @@ struct adc_chip {
bool skip_usb_wa;
void *ipc_log0;
void *ipc_log1;
+ bool is_pmic7;
struct pmic_revid_data *pmic_rev_id;
const struct adc_data *data;
};
@@ -187,6 +199,11 @@ static int adc_write(struct adc_chip *adc, u16 offset, u8 *data, int len)
return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
}
+static int adc_masked_write(struct adc_chip *adc, u16 offset, u8 mask, u8 val)
+{
+ return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
+}
+
static int adc_prescaling_from_dt(u32 num, u32 den)
{
unsigned int pre;
@@ -529,6 +546,49 @@ static int adc_configure(struct adc_chip *adc,
return ret;
}
+static int adc7_configure(struct adc_chip *adc,
+ struct adc_channel_prop *prop)
+{
+ int ret;
+ u8 conv_req = 0, buf[4];
+
+ ret = adc_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
+ if (ret)
+ return ret;
+
+ ret = adc_read(adc, ADC_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ /* Digital param selection */
+ adc_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select CONV request */
+ conv_req = ADC_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ ret = adc_write(adc, ADC_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ ret = adc_write(adc, ADC_USR_CONV_REQ, &conv_req, 1);
+
+ return ret;
+}
+
static int adc_do_conversion(struct adc_chip *adc,
struct adc_channel_prop *prop,
struct iio_chan_spec const *chan,
@@ -579,6 +639,44 @@ static int adc_do_conversion(struct adc_chip *adc,
return ret;
}
+static int adc7_do_conversion(struct adc_chip *adc,
+ struct adc_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+ u8 status = 0;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc7_configure(adc, prop);
+ if (ret) {
+ pr_err("ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ /* No support for polling mode at present*/
+ wait_for_completion_timeout(&adc->complete,
+ ADC7_CONV_TIMEOUT);
+
+ ret = adc_read(adc, ADC_USR_STATUS1, &status, 1);
+ if (ret < 0)
+ goto unlock;
+
+ if (status & ADC7_USR_STATUS1_CONV_FAULT) {
+ pr_err("Unexpected conversion fault\n");
+ ret = -EIO;
+ goto unlock;
+ }
+
+ ret = adc_read_voltage_data(adc, data_volt);
+
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
static irqreturn_t adc_isr(int irq, void *dev_id)
{
struct adc_chip *adc = dev_id;
@@ -601,6 +699,66 @@ static int adc_of_xlate(struct iio_dev *indio_dev,
return -EINVAL;
}
+static int adc7_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct adc_chip *adc = iio_priv(indio_dev);
+ int i, v_channel;
+
+ for (i = 0; i < adc->nchannels; i++) {
+ v_channel = ((adc->chan_props[i].sid << ADC_CHANNEL_OFFSET) |
+ adc->chan_props[i].channel);
+ if (v_channel == iiospec->args[0])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc7_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct adc_chip *adc = iio_priv(indio_dev);
+ struct adc_channel_prop *prop;
+ u16 adc_code_volt, adc_code_cur;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc7_do_conversion(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ ret = qcom_vadc_hw_scale(prop->scale_fn_type,
+ &adc_prescale_ratios[prop->prescale],
+ adc->data, prop->lut_index,
+ adc_code_volt, val);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_RAW:
+ ret = adc7_do_conversion(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ *val = (int)adc_code_volt;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
@@ -672,6 +830,11 @@ static const struct iio_info adc_info = {
.of_xlate = adc_of_xlate,
};
+static const struct iio_info adc7_info = {
+ .read_raw = adc7_read_raw,
+ .of_xlate = adc7_of_xlate,
+};
+
struct adc_channels {
const char *datasheet_name;
unsigned int prescale_index;
@@ -746,12 +909,16 @@ static const struct adc_channels adc_chans_pmic5[ADC_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC_AMUX_THM4_PU2] = ADC_CHAN_TEMP("amux_thm4_pu2", 1,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC_INT_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("int_ext_isense", 1,
+ [ADC_PARALLEL_ISENSE] = ADC_CHAN_VOLT("parallel_isense", 1,
SCALE_HW_CALIB_CUR)
- [ADC_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("ext_isense", 1,
+ [ADC_INT_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER(
+ "int_ext_vbat_isense", 1,
+ SCALE_HW_CALIB_CUR)
+ [ADC_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("ext_vbat_isense", 1,
SCALE_HW_CALIB_CUR)
- [ADC_PARALLEL_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("parallel_isense", 1,
- SCALE_HW_CALIB_CUR)
+ [ADC_PARALLEL_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER(
+ "parallel_vbat_isense", 1,
+ SCALE_HW_CALIB_CUR)
[ADC_AMUX_THM2] = ADC_CHAN_TEMP("amux_thm2", 1,
SCALE_HW_CALIB_PM5_SMB_TEMP)
[ADC_AMUX_THM3] = ADC_CHAN_TEMP("amux_thm3", 1,
@@ -766,6 +933,39 @@ static const struct adc_channels adc_chans_pmic5[ADC_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
};
+static const struct adc_channels adc7_chans_pmic[ADC_MAX_CHANNEL] = {
+ [ADC7_REF_GND] = ADC_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_1P25VREF] = ADC_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_VPH_PWR] = ADC_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_VBAT_SNS] = ADC_CHAN_VOLT("vbat_sns", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_DIE_TEMP] = ADC_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM_PM7)
+ [ADC7_AMUX_THM1_100K_PU] = ADC_CHAN_TEMP("amux_thm1_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM2_100K_PU] = ADC_CHAN_TEMP("amux_thm2_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM3_100K_PU] = ADC_CHAN_TEMP("amux_thm3_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM4_100K_PU] = ADC_CHAN_TEMP("amux_thm4_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM5_100K_PU] = ADC_CHAN_TEMP("amux_thm5_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM6_100K_PU] = ADC_CHAN_TEMP("amux_thm6_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO1_100K_PU] = ADC_CHAN_TEMP("gpio1_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO2_100K_PU] = ADC_CHAN_TEMP("gpio2_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO3_100K_PU] = ADC_CHAN_TEMP("gpio3_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO4_100K_PU] = ADC_CHAN_TEMP("gpio4_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+};
+
static const struct adc_channels adc_chans_rev2[ADC_MAX_CHANNEL] = {
[ADC_REF_GND] = ADC_CHAN_VOLT("ref_gnd", 1,
SCALE_HW_CALIB_DEFAULT)
@@ -789,14 +989,16 @@ static const struct adc_channels adc_chans_rev2[ADC_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
};
-static int adc_get_dt_channel_data(struct device *dev,
+static int adc_get_dt_channel_data(struct adc_chip *adc,
struct adc_channel_prop *prop,
struct device_node *node,
const struct adc_data *data)
{
const char *name = node->name, *channel_name;
u32 chan, value, varr[2];
+ u32 sid = 0;
int ret;
+ struct device *dev = adc->dev;
ret = of_property_read_u32(node, "reg", &chan);
if (ret) {
@@ -804,6 +1006,16 @@ static int adc_get_dt_channel_data(struct device *dev,
return ret;
}
+ /*
+ * Value read from "reg" is virtual channel number
+ * virtual channel number = (sid << 8 | channel number).
+ */
+
+ if (adc->is_pmic7) {
+ sid = (chan >> ADC_CHANNEL_OFFSET);
+ chan = (chan & ADC_CHANNEL_MASK);
+ }
+
if (chan > ADC_PARALLEL_ISENSE_VBAT_IDATA) {
dev_err(dev, "%s invalid channel number %d\n", name, chan);
return -EINVAL;
@@ -811,6 +1023,7 @@ static int adc_get_dt_channel_data(struct device *dev,
/* the channel has DT description */
prop->channel = chan;
+ prop->sid = sid;
channel_name = of_get_property(node,
"label", NULL) ? : node->name;
@@ -872,6 +1085,7 @@ static int adc_get_dt_channel_data(struct device *dev,
prop->scale_fn_type = -EINVAL;
ret = of_property_read_u32(node, "qcom,scale-fn-type", &value);
+
if (!ret && value < SCALE_HW_CALIB_MAX)
prop->scale_fn_type = value;
@@ -907,6 +1121,24 @@ const struct adc_data data_pmic5 = {
800, 900, 1, 2, 4, 6, 8, 10},
};
+const struct adc_data data_pmic5_lite = {
+ .full_scale_code_volt = 0x70e4,
+ /* On PMI632, IBAT LSB = 5A/32767 */
+ .full_scale_code_cur = 5000,
+ .adc_chans = adc_chans_pmic5,
+ .decimation = (unsigned int []) {250, 420, 840},
+ .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+};
+
+const struct adc_data adc7_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .adc_chans = adc7_chans_pmic,
+ .decimation = (unsigned int []) {85, 340, 1360},
+ .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
+ 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000},
+};
+
const struct adc_data data_pmic_rev2 = {
.full_scale_code_volt = 0x4000,
.full_scale_code_cur = 0x1800,
@@ -922,9 +1154,17 @@ static const struct of_device_id adc_match_table[] = {
.data = &data_pmic5,
},
{
+ .compatible = "qcom,spmi-adc7",
+ .data = &adc7_data_pmic,
+ },
+ {
.compatible = "qcom,spmi-adc-rev2",
.data = &data_pmic_rev2,
},
+ {
+ .compatible = "qcom,spmi-adc5-lite",
+ .data = &data_pmic5_lite,
+ },
{ }
};
@@ -962,7 +1202,7 @@ static int adc_get_dt_data(struct adc_chip *adc, struct device_node *node)
adc->data = data;
for_each_available_child_of_node(node, child) {
- ret = adc_get_dt_channel_data(adc->dev, &prop, child, data);
+ ret = adc_get_dt_channel_data(adc, &prop, child, data);
if (ret) {
of_node_put(child);
return ret;
@@ -1051,6 +1291,7 @@ static int adc_probe(struct platform_device *pdev)
adc = iio_priv(indio_dev);
adc->regmap = regmap;
adc->dev = dev;
+
adc->pmic_rev_id = pmic_rev_id;
prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
@@ -1068,6 +1309,14 @@ static int adc_probe(struct platform_device *pdev)
adc->skip_usb_wa = skip_usb_wa;
+ if (of_device_is_compatible(node, "qcom,spmi-adc7")) {
+ indio_dev->info = &adc7_info;
+ adc->is_pmic7 = true;
+ } else
+ indio_dev->info = &adc_info;
+
+ platform_set_drvdata(pdev, adc);
+
init_completion(&adc->complete);
mutex_init(&adc->lock);
@@ -1093,7 +1342,6 @@ static int adc_probe(struct platform_device *pdev)
indio_dev->dev.of_node = node;
indio_dev->name = pdev->name;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->info = &adc_info;
indio_dev->channels = adc->iio_chans;
indio_dev->num_channels = adc->nchannels;
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index 114dab7..9be81ab 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -570,6 +570,195 @@ static const struct lut_table lut_table_400[] = {
{adcmap_batt_therm_400k_6125, ARRAY_SIZE(adcmap_batt_therm_400k_6125)},
};
+static const struct vadc_map_pt adcmap7_die_temp[] = {
+ { 433700, 1967},
+ { 473100, 1964},
+ { 512400, 1957},
+ { 551500, 1949},
+ { 590500, 1940},
+ { 629300, 1930},
+ { 667900, 1921},
+ { 706400, 1910},
+ { 744600, 1896},
+ { 782500, 1878},
+ { 820100, 1859},
+ { 857300, 0},
+};
+
+/*
+ * Resistance to temperature table for 100k pull up for NTCG104EF104.
+ */
+static const struct vadc_map_pt adcmap7_100k[] = {
+ { 4250657, -40960 },
+ { 3962085, -39936 },
+ { 3694875, -38912 },
+ { 3447322, -37888 },
+ { 3217867, -36864 },
+ { 3005082, -35840 },
+ { 2807660, -34816 },
+ { 2624405, -33792 },
+ { 2454218, -32768 },
+ { 2296094, -31744 },
+ { 2149108, -30720 },
+ { 2012414, -29696 },
+ { 1885232, -28672 },
+ { 1766846, -27648 },
+ { 1656598, -26624 },
+ { 1553884, -25600 },
+ { 1458147, -24576 },
+ { 1368873, -23552 },
+ { 1285590, -22528 },
+ { 1207863, -21504 },
+ { 1135290, -20480 },
+ { 1067501, -19456 },
+ { 1004155, -18432 },
+ { 944935, -17408 },
+ { 889550, -16384 },
+ { 837731, -15360 },
+ { 789229, -14336 },
+ { 743813, -13312 },
+ { 701271, -12288 },
+ { 661405, -11264 },
+ { 624032, -10240 },
+ { 588982, -9216 },
+ { 556100, -8192 },
+ { 525239, -7168 },
+ { 496264, -6144 },
+ { 469050, -5120 },
+ { 443480, -4096 },
+ { 419448, -3072 },
+ { 396851, -2048 },
+ { 375597, -1024 },
+ { 355598, 0 },
+ { 336775, 1024 },
+ { 319052, 2048 },
+ { 302359, 3072 },
+ { 286630, 4096 },
+ { 271806, 5120 },
+ { 257829, 6144 },
+ { 244646, 7168 },
+ { 232209, 8192 },
+ { 220471, 9216 },
+ { 209390, 10240 },
+ { 198926, 11264 },
+ { 189040, 12288 },
+ { 179698, 13312 },
+ { 170868, 14336 },
+ { 162519, 15360 },
+ { 154622, 16384 },
+ { 147150, 17408 },
+ { 140079, 18432 },
+ { 133385, 19456 },
+ { 127046, 20480 },
+ { 121042, 21504 },
+ { 115352, 22528 },
+ { 109960, 23552 },
+ { 104848, 24576 },
+ { 100000, 25600 },
+ { 95402, 26624 },
+ { 91038, 27648 },
+ { 86897, 28672 },
+ { 82965, 29696 },
+ { 79232, 30720 },
+ { 75686, 31744 },
+ { 72316, 32768 },
+ { 69114, 33792 },
+ { 66070, 34816 },
+ { 63176, 35840 },
+ { 60423, 36864 },
+ { 57804, 37888 },
+ { 55312, 38912 },
+ { 52940, 39936 },
+ { 50681, 40960 },
+ { 48531, 41984 },
+ { 46482, 43008 },
+ { 44530, 44032 },
+ { 42670, 45056 },
+ { 40897, 46080 },
+ { 39207, 47104 },
+ { 37595, 48128 },
+ { 36057, 49152 },
+ { 34590, 50176 },
+ { 33190, 51200 },
+ { 31853, 52224 },
+ { 30577, 53248 },
+ { 29358, 54272 },
+ { 28194, 55296 },
+ { 27082, 56320 },
+ { 26020, 57344 },
+ { 25004, 58368 },
+ { 24033, 59392 },
+ { 23104, 60416 },
+ { 22216, 61440 },
+ { 21367, 62464 },
+ { 20554, 63488 },
+ { 19776, 64512 },
+ { 19031, 65536 },
+ { 18318, 66560 },
+ { 17636, 67584 },
+ { 16982, 68608 },
+ { 16355, 69632 },
+ { 15755, 70656 },
+ { 15180, 71680 },
+ { 14628, 72704 },
+ { 14099, 73728 },
+ { 13592, 74752 },
+ { 13106, 75776 },
+ { 12640, 76800 },
+ { 12192, 77824 },
+ { 11762, 78848 },
+ { 11350, 79872 },
+ { 10954, 80896 },
+ { 10574, 81920 },
+ { 10209, 82944 },
+ { 9858, 83968 },
+ { 9521, 84992 },
+ { 9197, 86016 },
+ { 8886, 87040 },
+ { 8587, 88064 },
+ { 8299, 89088 },
+ { 8023, 90112 },
+ { 7757, 91136 },
+ { 7501, 92160 },
+ { 7254, 93184 },
+ { 7017, 94208 },
+ { 6789, 95232 },
+ { 6570, 96256 },
+ { 6358, 97280 },
+ { 6155, 98304 },
+ { 5959, 99328 },
+ { 5770, 100352 },
+ { 5588, 101376 },
+ { 5412, 102400 },
+ { 5243, 103424 },
+ { 5080, 104448 },
+ { 4923, 105472 },
+ { 4771, 106496 },
+ { 4625, 107520 },
+ { 4484, 108544 },
+ { 4348, 109568 },
+ { 4217, 110592 },
+ { 4090, 111616 },
+ { 3968, 112640 },
+ { 3850, 113664 },
+ { 3736, 114688 },
+ { 3626, 115712 },
+ { 3519, 116736 },
+ { 3417, 117760 },
+ { 3317, 118784 },
+ { 3221, 119808 },
+ { 3129, 120832 },
+ { 3039, 121856 },
+ { 2952, 122880 },
+ { 2868, 123904 },
+ { 2787, 124928 },
+ { 2709, 125952 },
+ { 2633, 126976 },
+ { 2560, 128000 },
+ { 2489, 129024 },
+ { 2420, 130048 }
+};
+
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, s64 *output)
{
@@ -796,6 +985,32 @@ static int qcom_vadc_scale_hw_calib_batt_therm_100(
return 0;
}
+static int qcom_vadc7_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ s64 resistance = 0, result = 0;
+ int ret;
+
+ if (adc_code >= RATIO_MAX_ADC7)
+ return -EINVAL;
+
+ /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
+ resistance = (s64) adc_code * R_PU_100K;
+ resistance = div64_s64(resistance, (RATIO_MAX_ADC7 - adc_code));
+
+ ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
+ ARRAY_SIZE(adcmap7_100k),
+ resistance, &result);
+ if (ret)
+ return ret;
+
+ *result_mdec = result;
+
+ return 0;
+}
+
static int qcom_vadc_scale_hw_calib_batt_therm_30(
const struct vadc_prescale_ratio *prescale,
const struct adc_data *data,
@@ -896,6 +1111,45 @@ static int qcom_vadc_scale_hw_calib_die_temp(
return 0;
}
+static int qcom_vadc7_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc_data *data,
+ u16 adc_code, int *result_mdec)
+{
+
+ s64 voltage, vtemp0, temp;
+ int adc_vdd_ref_mv = 1875, i = 0;
+
+ if (adc_code > VADC5_MAX_CODE)
+ adc_code = 0;
+
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
+ voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
+ voltage = div64_s64(voltage, data->full_scale_code_volt);
+ voltage = voltage * prescale->den;
+ voltage = div64_s64(voltage, prescale->num);
+
+ while (i < ARRAY_SIZE(adcmap7_die_temp)) {
+ if (adcmap7_die_temp[i].x > voltage)
+ break;
+ i++;
+ }
+
+ if (i == 0) {
+ *result_mdec = DIE_TEMP_ADC7_SCALE_1;
+ } else if (i == ARRAY_SIZE(adcmap7_die_temp)) {
+ *result_mdec = DIE_TEMP_ADC7_MAX;
+ } else {
+ vtemp0 = adcmap7_die_temp[i-1].x;
+ voltage = voltage - vtemp0;
+ temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
+ adcmap7_die_temp[i-1].y);
+ temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i-1));
+ *result_mdec = temp;
+ }
+ return 0;
+}
+
static int qcom_vadc_scale_hw_smb_temp(
const struct vadc_prescale_ratio *prescale,
const struct adc_data *data,
@@ -1075,6 +1329,12 @@ int qcom_vadc_hw_scale(enum vadc_scale_fn_type scaletype,
case SCALE_HW_CALIB_PM5_SMB1398_TEMP:
return qcom_vadc_scale_hw_smb1398_temp(prescale, data,
adc_code, result);
+ case SCALE_HW_CALIB_THERM_100K_PU_PM7:
+ return qcom_vadc7_scale_hw_calib_therm(prescale, data,
+ adc_code, result);
+ case SCALE_HW_CALIB_PMIC_THERM_PM7:
+ return qcom_vadc7_scale_hw_calib_die_temp(prescale, data,
+ adc_code, result);
default:
return -EINVAL;
}
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
index e77c92f..e29ea9e 100644
--- a/drivers/iio/adc/qcom-vadc-common.h
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -83,6 +83,14 @@
#define ADC_DBG1(dev, msg, args...) pr_debug(msg, ##args)
#endif
+#define R_PU_100K 100000
+#define RATIO_MAX_ADC7 0x4000
+
+#define DIE_TEMP_ADC7_SCALE_1 -60000
+#define DIE_TEMP_ADC7_SCALE_2 20000
+#define DIE_TEMP_ADC7_SCALE_FACTOR 1000
+#define DIE_TEMP_ADC7_MAX 160000
+
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
@@ -144,9 +152,13 @@ struct vadc_prescale_ratio {
* lookup table. The hardware applies offset/slope to adc code.
* SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
* 100k pullup. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_THERM_100K_PU_PM7: Returns temperature in millidegC using
+ * lookup table for PMIC7. The hardware applies offset/slope to adc code.
* SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
* The hardware applies offset/slope to adc code.
* SCALE_HW_CALIB_CUR: Returns result in uA for PMIC5.
+ * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * The hardware applies offset/slope to adc code. This is for PMIC7.
* SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
* charger temperature.
* SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
@@ -170,8 +182,10 @@ enum vadc_scale_fn_type {
SCALE_HW_CALIB_DEFAULT,
SCALE_HW_CALIB_THERM_100K_PULLUP,
SCALE_HW_CALIB_XOTHERM,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7,
SCALE_HW_CALIB_PMIC_THERM,
SCALE_HW_CALIB_CUR,
+ SCALE_HW_CALIB_PMIC_THERM_PM7,
SCALE_HW_CALIB_PM5_CHG_TEMP,
SCALE_HW_CALIB_PM5_SMB_TEMP,
SCALE_HW_CALIB_BATT_THERM_100K,
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index c52d20f..0409dcf 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1340,7 +1340,7 @@ static int stm32_adc_dma_start(struct iio_dev *indio_dev)
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
return ret;
}
@@ -1413,7 +1413,7 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
dev_err(&indio_dev->dev, "predisable failed\n");
if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
if (stm32_adc_set_trig(indio_dev, NULL))
dev_err(&indio_dev->dev, "Can't clear trigger\n");
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 80beb64..69f4cfa 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -59,8 +59,8 @@
help
Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
- AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
- AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs
+ AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5600, AD5601, AD5602, AD5611,
+ AD5612, AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs
as well as Texas Instruments DAC081S101, DAC101S101, DAC121S101.
To compile this driver as a module, choose M here: the
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
index fd26a42..d3ce5de 100644
--- a/drivers/iio/dac/ad5446.c
+++ b/drivers/iio/dac/ad5446.c
@@ -328,6 +328,7 @@ enum ad5446_supported_spi_device_ids {
ID_AD5541A,
ID_AD5512A,
ID_AD5553,
+ ID_AD5600,
ID_AD5601,
ID_AD5611,
ID_AD5621,
@@ -382,6 +383,10 @@ static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
.channel = AD5446_CHANNEL(14, 16, 0),
.write = ad5446_write,
},
+ [ID_AD5600] = {
+ .channel = AD5446_CHANNEL(16, 16, 0),
+ .write = ad5446_write,
+ },
[ID_AD5601] = {
.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
.write = ad5446_write,
@@ -449,6 +454,7 @@ static const struct spi_device_id ad5446_spi_ids[] = {
{"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
{"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
{"ad5553", ID_AD5553},
+ {"ad5600", ID_AD5600},
{"ad5601", ID_AD5601},
{"ad5611", ID_AD5611},
{"ad5621", ID_AD5621},
diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c
index bf9aa3f..b5190d1 100644
--- a/drivers/iio/dac/mcp4922.c
+++ b/drivers/iio/dac/mcp4922.c
@@ -94,17 +94,22 @@ static int mcp4922_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct mcp4922_state *state = iio_priv(indio_dev);
+ int ret;
if (val2 != 0)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (val > GENMASK(chan->scan_type.realbits-1, 0))
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
return -EINVAL;
val <<= chan->scan_type.shift;
- state->value[chan->channel] = val;
- return mcp4922_spi_write(state, chan->channel, val);
+
+ ret = mcp4922_spi_write(state, chan->channel, val);
+ if (!ret)
+ state->value[chan->channel] = val;
+ return ret;
+
default:
return -EINVAL;
}
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index 066e05f..ff6666a 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -229,7 +229,7 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
*val2 = 65536;
return IIO_VAL_FRACTIONAL;
} else {
- *val = 100;
+ *val = 100000;
*val2 = 65536;
return IIO_VAL_FRACTIONAL;
}
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index a27fe20..ea24701 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -270,8 +270,11 @@ static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2)
struct adis16480 *st = iio_priv(indio_dev);
unsigned int t;
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
t = val * 1000 + val2 / 1000;
- if (t <= 0)
+ if (t == 0)
return -EINVAL;
t = 2460000 / t;
@@ -724,6 +727,7 @@ static const struct iio_info adis16480_info = {
.read_raw = &adis16480_read_raw,
.write_raw = &adis16480_write_raw,
.update_scan_mode = adis_update_scan_mode,
+ .debugfs_reg_access = adis_debugfs_reg_access,
};
static int adis16480_stop_device(struct iio_dev *indio_dev)
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
index 76643c5..e59d043 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu/adis_buffer.c
@@ -39,8 +39,11 @@ int adis_update_scan_mode(struct iio_dev *indio_dev,
return -ENOMEM;
adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL);
- if (!adis->buffer)
+ if (!adis->buffer) {
+ kfree(adis->xfer);
+ adis->xfer = NULL;
return -ENOMEM;
+ }
rx = adis->buffer;
tx = rx + scan_count;
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig
index 5483b2e..d2fe9db 100644
--- a/drivers/iio/imu/inv_mpu6050/Kconfig
+++ b/drivers/iio/imu/inv_mpu6050/Kconfig
@@ -13,8 +13,8 @@
select INV_MPU6050_IIO
select REGMAP_I2C
help
- This driver supports the Invensense MPU6050/6500/9150 and ICM20608
- motion tracking devices over I2C.
+ This driver supports the Invensense MPU6050/6500/9150 and
+ ICM20608/20602 motion tracking devices over I2C.
This driver can be built as a module. The module will be called
inv-mpu6050-i2c.
@@ -24,7 +24,7 @@
select INV_MPU6050_IIO
select REGMAP_SPI
help
- This driver supports the Invensense MPU6050/6500/9150 and ICM20608
- motion tracking devices over SPI.
+ This driver supports the Invensense MPU6050/6500/9150 and
+ ICM20608/20602 motion tracking devices over SPI.
This driver can be built as a module. The module will be called
inv-mpu6050-spi.
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index d80ef468..6b560d9 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -37,6 +37,29 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
*/
static const int accel_scale[] = {598, 1196, 2392, 4785};
+static const struct inv_mpu6050_reg_map reg_set_icm20602 = {
+ .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
+ .lpf = INV_MPU6050_REG_CONFIG,
+ .accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2,
+ .user_ctrl = INV_MPU6050_REG_USER_CTRL,
+ .fifo_en = INV_MPU6050_REG_FIFO_EN,
+ .gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
+ .accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
+ .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
+ .fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
+ .raw_gyro = INV_MPU6050_REG_RAW_GYRO,
+ .raw_accl = INV_MPU6050_REG_RAW_ACCEL,
+ .temperature = INV_MPU6050_REG_TEMPERATURE,
+ .int_enable = INV_MPU6050_REG_INT_ENABLE,
+ .int_status = INV_MPU6050_REG_INT_STATUS,
+ .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
+ .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
+ .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
+ .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
+ .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = INV_ICM20602_REG_I2C_IF,
+};
+
static const struct inv_mpu6050_reg_map reg_set_6500 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
@@ -57,6 +80,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = 0,
};
static const struct inv_mpu6050_reg_map reg_set_6050 = {
@@ -77,6 +101,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6050_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = 0,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@@ -96,48 +121,72 @@ static const struct inv_mpu6050_hw hw_info[] = {
.name = "MPU6050",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
+ .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
},
{
.whoami = INV_MPU6500_WHOAMI_VALUE,
.name = "MPU6500",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_MPU6515_WHOAMI_VALUE,
.name = "MPU6515",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_MPU6000_WHOAMI_VALUE,
.name = "MPU6000",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
+ .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
},
{
.whoami = INV_MPU9150_WHOAMI_VALUE,
.name = "MPU9150",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
+ .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
},
{
.whoami = INV_MPU9250_WHOAMI_VALUE,
.name = "MPU9250",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_MPU9255_WHOAMI_VALUE,
.name = "MPU9255",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_ICM20608_WHOAMI_VALUE,
.name = "ICM20608",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
+ },
+ {
+ .whoami = INV_ICM20602_WHOAMI_VALUE,
+ .name = "ICM20602",
+ .reg = ®_set_icm20602,
+ .config = &chip_config_6050,
+ .fifo_size = 1008,
+ .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
},
};
@@ -438,9 +487,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
- *val = 0;
- *val2 = INV_MPU6050_TEMP_SCALE;
-
+ *val = st->hw->temp.scale / 1000000;
+ *val2 = st->hw->temp.scale % 1000000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
@@ -448,8 +496,7 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
- *val = INV_MPU6050_TEMP_OFFSET;
-
+ *val = st->hw->temp.offset;
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -813,6 +860,73 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
};
+static const unsigned long inv_mpu_scan_masks[] = {
+ /* 3-axis accel */
+ BIT(INV_MPU6050_SCAN_ACCL_X)
+ | BIT(INV_MPU6050_SCAN_ACCL_Y)
+ | BIT(INV_MPU6050_SCAN_ACCL_Z),
+ /* 3-axis gyro */
+ BIT(INV_MPU6050_SCAN_GYRO_X)
+ | BIT(INV_MPU6050_SCAN_GYRO_Y)
+ | BIT(INV_MPU6050_SCAN_GYRO_Z),
+ /* 6-axis accel + gyro */
+ BIT(INV_MPU6050_SCAN_ACCL_X)
+ | BIT(INV_MPU6050_SCAN_ACCL_Y)
+ | BIT(INV_MPU6050_SCAN_ACCL_Z)
+ | BIT(INV_MPU6050_SCAN_GYRO_X)
+ | BIT(INV_MPU6050_SCAN_GYRO_Y)
+ | BIT(INV_MPU6050_SCAN_GYRO_Z),
+ 0,
+};
+
+static const struct iio_chan_spec inv_icm20602_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = INV_ICM20602_SCAN_TEMP,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
+
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
+};
+
+static const unsigned long inv_icm20602_scan_masks[] = {
+ /* 3-axis accel + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_ACCL_X)
+ | BIT(INV_ICM20602_SCAN_ACCL_Y)
+ | BIT(INV_ICM20602_SCAN_ACCL_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ /* 3-axis gyro + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_GYRO_X)
+ | BIT(INV_ICM20602_SCAN_GYRO_Y)
+ | BIT(INV_ICM20602_SCAN_GYRO_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ /* 6-axis accel + gyro + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_ACCL_X)
+ | BIT(INV_ICM20602_SCAN_ACCL_Y)
+ | BIT(INV_ICM20602_SCAN_ACCL_Z)
+ | BIT(INV_ICM20602_SCAN_GYRO_X)
+ | BIT(INV_ICM20602_SCAN_GYRO_Y)
+ | BIT(INV_ICM20602_SCAN_GYRO_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ 0,
+};
+
/*
* The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and
* INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the
@@ -1013,8 +1127,16 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
indio_dev->name = name;
else
indio_dev->name = dev_name(dev);
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ if (chip_type == INV_ICM20602) {
+ indio_dev->channels = inv_icm20602_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
+ indio_dev->available_scan_masks = inv_icm20602_scan_masks;
+ } else {
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ }
indio_dev->info = &mpu_info;
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
index dd758e3..e46eb4d 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
@@ -127,6 +127,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(dev_get_drvdata(&client->dev));
switch (st->chip_type) {
case INV_ICM20608:
+ case INV_ICM20602:
/* no i2c auxiliary bus on the chip */
break;
default:
@@ -179,6 +180,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
+ {"icm20602", INV_ICM20602},
{}
};
@@ -213,6 +215,10 @@ static const struct of_device_id inv_of_match[] = {
.compatible = "invensense,icm20608",
.data = (void *)INV_ICM20608
},
+ {
+ .compatible = "invensense,icm20602",
+ .data = (void *)INV_ICM20602
+ },
{ }
};
MODULE_DEVICE_TABLE(of, inv_of_match);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index e69a596..220eba5 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -44,6 +44,7 @@
* @int_pin_cfg; Controls interrupt pin configuration.
* @accl_offset: Controls the accelerometer calibration offset.
* @gyro_offset: Controls the gyroscope calibration offset.
+ * @i2c_if: Controls the i2c interface
*/
struct inv_mpu6050_reg_map {
u8 sample_rate_div;
@@ -65,6 +66,7 @@ struct inv_mpu6050_reg_map {
u8 int_pin_cfg;
u8 accl_offset;
u8 gyro_offset;
+ u8 i2c_if;
};
/*device enum */
@@ -77,6 +79,7 @@ enum inv_devices {
INV_MPU9250,
INV_MPU9255,
INV_ICM20608,
+ INV_ICM20602,
INV_NUM_PARTS
};
@@ -105,12 +108,19 @@ struct inv_mpu6050_chip_config {
* @name: name of the chip.
* @reg: register map of the chip.
* @config: configuration of the chip.
+ * @fifo_size: size of the FIFO in bytes.
+ * @temp: offset and scale to apply to raw temperature.
*/
struct inv_mpu6050_hw {
u8 whoami;
u8 *name;
const struct inv_mpu6050_reg_map *reg;
const struct inv_mpu6050_chip_config *config;
+ size_t fifo_size;
+ struct {
+ int offset;
+ int scale;
+ } temp;
};
/*
@@ -193,12 +203,19 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
+/* ICM20602 register */
+#define INV_ICM20602_REG_I2C_IF 0x70
+#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
+
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
#define INV_MPU6050_REG_FIFO_R_W 0x74
#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6
#define INV_MPU6050_FIFO_COUNT_BYTE 2
+/* ICM20602 FIFO samples include temperature readings */
+#define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2
+
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
@@ -212,14 +229,20 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_UP_TIME_MIN 5000
#define INV_MPU6050_REG_UP_TIME_MAX 10000
-#define INV_MPU6050_TEMP_OFFSET 12421
-#define INV_MPU6050_TEMP_SCALE 2941
+#define INV_MPU6050_TEMP_OFFSET 12420
+#define INV_MPU6050_TEMP_SCALE 2941176
#define INV_MPU6050_MAX_GYRO_FS_PARAM 3
#define INV_MPU6050_MAX_ACCL_FS_PARAM 3
#define INV_MPU6050_THREE_AXIS 3
#define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3
#define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3
+#define INV_MPU6500_TEMP_OFFSET 7011
+#define INV_MPU6500_TEMP_SCALE 2995178
+
+#define INV_ICM20608_TEMP_OFFSET 8170
+#define INV_ICM20608_TEMP_SCALE 3059976
+
/* 6 + 6 round up and plus 8 */
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
@@ -259,8 +282,9 @@ struct inv_mpu6050_state {
#define INV_MPU9255_WHOAMI_VALUE 0x73
#define INV_MPU6515_WHOAMI_VALUE 0x74
#define INV_ICM20608_WHOAMI_VALUE 0xAF
+#define INV_ICM20602_WHOAMI_VALUE 0x12
-/* scan element definition */
+/* scan element definition for generic MPU6xxx devices */
enum inv_mpu6050_scan {
INV_MPU6050_SCAN_ACCL_X,
INV_MPU6050_SCAN_ACCL_Y,
@@ -271,6 +295,18 @@ enum inv_mpu6050_scan {
INV_MPU6050_SCAN_TIMESTAMP,
};
+/* scan element definition for ICM20602, which includes temperature */
+enum inv_icm20602_scan {
+ INV_ICM20602_SCAN_ACCL_X,
+ INV_ICM20602_SCAN_ACCL_Y,
+ INV_ICM20602_SCAN_ACCL_Z,
+ INV_ICM20602_SCAN_TEMP,
+ INV_ICM20602_SCAN_GYRO_X,
+ INV_ICM20602_SCAN_GYRO_Y,
+ INV_ICM20602_SCAN_GYRO_Z,
+ INV_ICM20602_SCAN_TIMESTAMP,
+};
+
enum inv_mpu6050_filter_e {
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
INV_MPU6050_FILTER_188HZ,
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index 548e042..0e54f2d 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -188,9 +188,6 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
"failed to ack interrupt\n");
goto flush_fifo;
}
- /* handle fifo overflow by reseting fifo */
- if (int_status & INV_MPU6050_BIT_FIFO_OVERFLOW_INT)
- goto flush_fifo;
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
dev_warn(regmap_get_device(st->map),
"spurious interrupt with status 0x%x\n", int_status);
@@ -207,6 +204,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
if (st->chip_config.gyro_fifo_enable)
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
+ if (st->chip_type == INV_ICM20602)
+ bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR;
+
/*
* read fifo_count register to know how many bytes are inside the FIFO
* right now
@@ -216,6 +216,18 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
if (result)
goto end_session;
fifo_count = get_unaligned_be16(&data[0]);
+
+ /*
+ * Handle fifo overflow by resetting fifo.
+ * Reset if there is only 3 data set free remaining to mitigate
+ * possible delay between reading fifo count and fifo data.
+ */
+ nb = 3 * bytes_per_datum;
+ if (fifo_count >= st->hw->fifo_size - nb) {
+ dev_warn(regmap_get_device(st->map), "fifo overflow reset\n");
+ goto flush_fifo;
+ }
+
/* compute and process all complete datum */
nb = fifo_count / bytes_per_datum;
inv_mpu6050_update_period(st, pf->timestamp, nb);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
index 227f50a..a112c3f 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
@@ -31,9 +31,14 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
if (ret)
return ret;
- st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
- ret = regmap_write(st->map, st->reg->user_ctrl,
- st->chip_config.user_ctrl);
+ if (st->reg->i2c_if) {
+ ret = regmap_write(st->map, st->reg->i2c_if,
+ INV_ICM20602_BIT_I2C_IF_DIS);
+ } else {
+ st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
+ ret = regmap_write(st->map, st->reg->user_ctrl,
+ st->chip_config.user_ctrl);
+ }
if (ret) {
inv_mpu6050_set_power_itg(st, false);
return ret;
@@ -81,6 +86,7 @@ static const struct spi_device_id inv_mpu_id[] = {
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
+ {"icm20602", INV_ICM20602},
{}
};
diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c
index a814828..5f5d54c 100644
--- a/drivers/iio/light/bh1750.c
+++ b/drivers/iio/light/bh1750.c
@@ -62,9 +62,9 @@ struct bh1750_chip_info {
u16 int_time_low_mask;
u16 int_time_high_mask;
-}
+};
-static const bh1750_chip_info_tbl[] = {
+static const struct bh1750_chip_info bh1750_chip_info_tbl[] = {
[BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 },
[BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 },
[BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 },
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index 09c7b9c..0428a0d 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -105,7 +105,7 @@ static int srf04_read(struct srf04_data *data)
udelay(10);
gpiod_set_value(data->gpiod_trig, 0);
- /* it cannot take more than 20 ms */
+ /* it should not take more than 20 ms until echo is rising */
ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
if (ret < 0) {
mutex_unlock(&data->lock);
@@ -115,7 +115,8 @@ static int srf04_read(struct srf04_data *data)
return -ETIMEDOUT;
}
- ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
+ /* it cannot take more than 50 ms until echo is falling */
+ ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
@@ -130,19 +131,19 @@ static int srf04_read(struct srf04_data *data)
dt_ns = ktime_to_ns(ktime_dt);
/*
- * measuring more than 3 meters is beyond the capabilities of
- * the sensor
+ * measuring more than 6,45 meters is beyond the capabilities of
+ * the supported sensors
* ==> filter out invalid results for not measuring echos of
* another us sensor
*
* formula:
- * distance 3 m
- * time = ---------- = --------- = 9404389 ns
- * speed 319 m/s
+ * distance 6,45 * 2 m
+ * time = ---------- = ------------ = 40438871 ns
+ * speed 319 m/s
*
* using a minimum speed at -20 °C of 319 m/s
*/
- if (dt_ns > 9404389)
+ if (dt_ns > 40438871)
return -EIO;
time_ns = dt_ns;
@@ -154,20 +155,20 @@ static int srf04_read(struct srf04_data *data)
* with Temp in °C
* and speed in m/s
*
- * use 343 m/s as ultrasonic speed at 20 °C here in absence of the
+ * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
* temperature
*
* therefore:
- * time 343
- * distance = ------ * -----
- * 10^6 2
+ * time 343,5 time * 106
+ * distance = ------ * ------- = ------------
+ * 10^6 2 617176
* with time in ns
* and distance in mm (one way)
*
- * because we limit to 3 meters the multiplication with 343 just
+ * because we limit to 6,45 meters the multiplication with 106 just
* fits into 32 bit
*/
- distance_mm = time_ns * 343 / 2000000;
+ distance_mm = time_ns * 106 / 617176;
return distance_mm;
}
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 6257be2..319bfef 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -2270,9 +2270,10 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
conn_id->cm_id.iw = NULL;
cma_exch(conn_id, RDMA_CM_DESTROYING);
mutex_unlock(&conn_id->handler_mutex);
+ mutex_unlock(&listen_id->handler_mutex);
cma_deref_id(conn_id);
rdma_destroy_id(&conn_id->id);
- goto out;
+ return ret;
}
mutex_unlock(&conn_id->handler_mutex);
@@ -4657,6 +4658,7 @@ static int __init cma_init(void)
err:
unregister_netdevice_notifier(&cma_nb);
ib_sa_unregister_client(&sa_client);
+ unregister_pernet_subsys(&cma_pernet_operations);
err_wq:
destroy_workqueue(cma_wq);
return ret;
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 6d8ac51..6a585c3 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -599,8 +599,8 @@ void ib_unregister_device(struct ib_device *device)
}
up_read(&lists_rwsem);
- ib_device_unregister_rdmacg(device);
ib_device_unregister_sysfs(device);
+ ib_device_unregister_rdmacg(device);
mutex_unlock(&device_mutex);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 74aa3e6..2184112 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -223,30 +223,30 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
/* Validate parameters */
qpn = get_spl_qp_index(qp_type);
if (qpn == -1) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: invalid QP Type %d\n",
- qp_type);
+ dev_dbg_ratelimited(&device->dev, "%s: invalid QP Type %d\n",
+ __func__, qp_type);
goto error1;
}
if (rmpp_version && rmpp_version != IB_MGMT_RMPP_VERSION) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: invalid RMPP Version %u\n",
- rmpp_version);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: invalid RMPP Version %u\n",
+ __func__, rmpp_version);
goto error1;
}
/* Validate MAD registration request if supplied */
if (mad_reg_req) {
if (mad_reg_req->mgmt_class_version >= MAX_MGMT_VERSION) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: invalid Class Version %u\n",
- mad_reg_req->mgmt_class_version);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: invalid Class Version %u\n",
+ __func__,
+ mad_reg_req->mgmt_class_version);
goto error1;
}
if (!recv_handler) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: no recv_handler\n");
+ dev_dbg_ratelimited(&device->dev,
+ "%s: no recv_handler\n", __func__);
goto error1;
}
if (mad_reg_req->mgmt_class >= MAX_MGMT_CLASS) {
@@ -256,9 +256,9 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
*/
if (mad_reg_req->mgmt_class !=
IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: Invalid Mgmt Class 0x%x\n",
- mad_reg_req->mgmt_class);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: Invalid Mgmt Class 0x%x\n",
+ __func__, mad_reg_req->mgmt_class);
goto error1;
}
} else if (mad_reg_req->mgmt_class == 0) {
@@ -266,8 +266,9 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
* Class 0 is reserved in IBA and is used for
* aliasing of IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE
*/
- dev_notice(&device->dev,
- "ib_register_mad_agent: Invalid Mgmt Class 0\n");
+ dev_dbg_ratelimited(&device->dev,
+ "%s: Invalid Mgmt Class 0\n",
+ __func__);
goto error1;
} else if (is_vendor_class(mad_reg_req->mgmt_class)) {
/*
@@ -275,18 +276,19 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
* ensure supplied OUI is not zero
*/
if (!is_vendor_oui(mad_reg_req->oui)) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: No OUI specified for class 0x%x\n",
- mad_reg_req->mgmt_class);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: No OUI specified for class 0x%x\n",
+ __func__,
+ mad_reg_req->mgmt_class);
goto error1;
}
}
/* Make sure class supplied is consistent with RMPP */
if (!ib_is_mad_class_rmpp(mad_reg_req->mgmt_class)) {
if (rmpp_version) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: RMPP version for non-RMPP class 0x%x\n",
- mad_reg_req->mgmt_class);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: RMPP version for non-RMPP class 0x%x\n",
+ __func__, mad_reg_req->mgmt_class);
goto error1;
}
}
@@ -297,9 +299,9 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
IB_MGMT_CLASS_SUBN_LID_ROUTED) &&
(mad_reg_req->mgmt_class !=
IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: Invalid SM QP type: class 0x%x\n",
- mad_reg_req->mgmt_class);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: Invalid SM QP type: class 0x%x\n",
+ __func__, mad_reg_req->mgmt_class);
goto error1;
}
} else {
@@ -307,9 +309,9 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
(mad_reg_req->mgmt_class ==
IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: Invalid GS QP type: class 0x%x\n",
- mad_reg_req->mgmt_class);
+ dev_dbg_ratelimited(&device->dev,
+ "%s: Invalid GS QP type: class 0x%x\n",
+ __func__, mad_reg_req->mgmt_class);
goto error1;
}
}
@@ -324,18 +326,18 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
/* Validate device and port */
port_priv = ib_get_mad_port(device, port_num);
if (!port_priv) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: Invalid port %d\n",
- port_num);
+ dev_dbg_ratelimited(&device->dev, "%s: Invalid port %d\n",
+ __func__, port_num);
ret = ERR_PTR(-ENODEV);
goto error1;
}
- /* Verify the QP requested is supported. For example, Ethernet devices
- * will not have QP0 */
+ /* Verify the QP requested is supported. For example, Ethernet devices
+ * will not have QP0.
+ */
if (!port_priv->qp_info[qpn].qp) {
- dev_notice(&device->dev,
- "ib_register_mad_agent: QP %d not supported\n", qpn);
+ dev_dbg_ratelimited(&device->dev, "%s: QP %d not supported\n",
+ __func__, qpn);
ret = ERR_PTR(-EPROTONOSUPPORT);
goto error1;
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 5df8e54..4a14de2 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -98,7 +98,7 @@ ib_uverbs_init_udata_buf_or_null(struct ib_udata *udata,
struct ib_uverbs_device {
atomic_t refcount;
- int num_comp_vectors;
+ u32 num_comp_vectors;
struct completion comp;
struct device *dev;
struct ib_device __rcu *ib_dev;
diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
index 96f7689..802942a 100644
--- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
@@ -120,6 +120,8 @@ struct bnxt_re_dev {
#define BNXT_RE_FLAG_HAVE_L2_REF 3
#define BNXT_RE_FLAG_RCFW_CHANNEL_EN 4
#define BNXT_RE_FLAG_QOS_WORK_REG 5
+#define BNXT_RE_FLAG_RESOURCES_ALLOCATED 7
+#define BNXT_RE_FLAG_RESOURCES_INITIALIZED 8
#define BNXT_RE_FLAG_ISSUE_ROCE_STATS 29
struct net_device *netdev;
unsigned int version, major, minor;
diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
index 22bd978..589b0d4 100644
--- a/drivers/infiniband/hw/bnxt_re/main.c
+++ b/drivers/infiniband/hw/bnxt_re/main.c
@@ -864,10 +864,8 @@ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
{
int i;
- if (rdev->nq[0].hwq.max_elements) {
- for (i = 1; i < rdev->num_msix; i++)
- bnxt_qplib_disable_nq(&rdev->nq[i - 1]);
- }
+ for (i = 1; i < rdev->num_msix; i++)
+ bnxt_qplib_disable_nq(&rdev->nq[i - 1]);
if (rdev->qplib_res.rcfw)
bnxt_qplib_cleanup_res(&rdev->qplib_res);
@@ -876,6 +874,7 @@ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
static int bnxt_re_init_res(struct bnxt_re_dev *rdev)
{
int rc = 0, i;
+ int num_vec_enabled = 0;
bnxt_qplib_init_res(&rdev->qplib_res);
@@ -891,9 +890,13 @@ static int bnxt_re_init_res(struct bnxt_re_dev *rdev)
"Failed to enable NQ with rc = 0x%x", rc);
goto fail;
}
+ num_vec_enabled++;
}
return 0;
fail:
+ for (i = num_vec_enabled; i >= 0; i--)
+ bnxt_qplib_disable_nq(&rdev->nq[i]);
+
return rc;
}
@@ -925,6 +928,7 @@ static void bnxt_re_free_res(struct bnxt_re_dev *rdev)
static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
{
int rc = 0, i;
+ int num_vec_created = 0;
/* Configure and allocate resources for qplib */
rdev->qplib_res.rcfw = &rdev->rcfw;
@@ -951,7 +955,7 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
if (rc) {
dev_err(rdev_to_dev(rdev), "Alloc Failed NQ%d rc:%#x",
i, rc);
- goto dealloc_dpi;
+ goto free_nq;
}
rc = bnxt_re_net_ring_alloc
(rdev, rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr,
@@ -964,14 +968,17 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
dev_err(rdev_to_dev(rdev),
"Failed to allocate NQ fw id with rc = 0x%x",
rc);
+ bnxt_qplib_free_nq(&rdev->nq[i]);
goto free_nq;
}
+ num_vec_created++;
}
return 0;
free_nq:
- for (i = 0; i < rdev->num_msix - 1; i++)
+ for (i = num_vec_created; i >= 0; i--) {
+ bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id);
bnxt_qplib_free_nq(&rdev->nq[i]);
-dealloc_dpi:
+ }
bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
&rdev->qplib_res.dpi_tbl,
&rdev->dpi_privileged);
@@ -989,12 +996,17 @@ static void bnxt_re_dispatch_event(struct ib_device *ibdev, struct ib_qp *qp,
struct ib_event ib_event;
ib_event.device = ibdev;
- if (qp)
+ if (qp) {
ib_event.element.qp = qp;
- else
+ ib_event.event = event;
+ if (qp->event_handler)
+ qp->event_handler(&ib_event, qp->qp_context);
+
+ } else {
ib_event.element.port_num = port_num;
- ib_event.event = event;
- ib_dispatch_event(&ib_event);
+ ib_event.event = event;
+ ib_dispatch_event(&ib_event);
+ }
}
#define HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_IVLAN 0x02
@@ -1201,8 +1213,11 @@ static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev)
if (test_and_clear_bit(BNXT_RE_FLAG_QOS_WORK_REG, &rdev->flags))
cancel_delayed_work(&rdev->worker);
- bnxt_re_cleanup_res(rdev);
- bnxt_re_free_res(rdev);
+ if (test_and_clear_bit(BNXT_RE_FLAG_RESOURCES_INITIALIZED,
+ &rdev->flags))
+ bnxt_re_cleanup_res(rdev);
+ if (test_and_clear_bit(BNXT_RE_FLAG_RESOURCES_ALLOCATED, &rdev->flags))
+ bnxt_re_free_res(rdev);
if (test_and_clear_bit(BNXT_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags)) {
rc = bnxt_qplib_deinit_rcfw(&rdev->rcfw);
@@ -1332,12 +1347,15 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
pr_err("Failed to allocate resources: %#x\n", rc);
goto fail;
}
+ set_bit(BNXT_RE_FLAG_RESOURCES_ALLOCATED, &rdev->flags);
rc = bnxt_re_init_res(rdev);
if (rc) {
pr_err("Failed to initialize resources: %#x\n", rc);
goto fail;
}
+ set_bit(BNXT_RE_FLAG_RESOURCES_INITIALIZED, &rdev->flags);
+
if (!rdev->is_virtfn) {
rc = bnxt_re_setup_qos(rdev);
if (rc)
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
index 6637df7..8b3b5fd 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
@@ -614,13 +614,8 @@ void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw)
bnxt_qplib_rcfw_stop_irq(rcfw, true);
- if (rcfw->cmdq_bar_reg_iomem)
- iounmap(rcfw->cmdq_bar_reg_iomem);
- rcfw->cmdq_bar_reg_iomem = NULL;
-
- if (rcfw->creq_bar_reg_iomem)
- iounmap(rcfw->creq_bar_reg_iomem);
- rcfw->creq_bar_reg_iomem = NULL;
+ iounmap(rcfw->cmdq_bar_reg_iomem);
+ iounmap(rcfw->creq_bar_reg_iomem);
indx = find_first_bit(rcfw->cmdq_bitmap, rcfw->bmap_size);
if (indx != rcfw->bmap_size)
@@ -629,6 +624,8 @@ void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw)
kfree(rcfw->cmdq_bitmap);
rcfw->bmap_size = 0;
+ rcfw->cmdq_bar_reg_iomem = NULL;
+ rcfw->creq_bar_reg_iomem = NULL;
rcfw->aeq_handler = NULL;
rcfw->vector = 0;
}
@@ -714,6 +711,8 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev,
dev_err(&rcfw->pdev->dev,
"QPLIB: CREQ BAR region %d mapping failed",
rcfw->creq_bar_reg);
+ iounmap(rcfw->cmdq_bar_reg_iomem);
+ rcfw->cmdq_bar_reg_iomem = NULL;
return -ENOMEM;
}
rcfw->creq_qp_event_processed = 0;
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
index 4097f3fa..09e7d3d 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
@@ -775,9 +775,8 @@ int bnxt_qplib_map_tc2cos(struct bnxt_qplib_res *res, u16 *cids)
req.cos0 = cpu_to_le16(cids[0]);
req.cos1 = cpu_to_le16(cids[1]);
- bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void *)&resp, NULL,
- 0);
- return 0;
+ return bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void *)&resp,
+ NULL, 0);
}
int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw,
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 3be6405..4dcc92d 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -493,7 +493,6 @@ static int _put_ep_safe(struct c4iw_dev *dev, struct sk_buff *skb)
ep = *((struct c4iw_ep **)(skb->cb + 2 * sizeof(void *)));
release_ep_resources(ep);
- kfree_skb(skb);
return 0;
}
@@ -504,7 +503,6 @@ static int _put_pass_ep_safe(struct c4iw_dev *dev, struct sk_buff *skb)
ep = *((struct c4iw_ep **)(skb->cb + 2 * sizeof(void *)));
c4iw_put_ep(&ep->parent_ep->com);
release_ep_resources(ep);
- kfree_skb(skb);
return 0;
}
@@ -2380,20 +2378,6 @@ static int accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
enum chip_type adapter_type = ep->com.dev->rdev.lldi.adapter_type;
pr_debug("ep %p tid %u\n", ep, ep->hwtid);
-
- skb_get(skb);
- rpl = cplhdr(skb);
- if (!is_t4(adapter_type)) {
- skb_trim(skb, roundup(sizeof(*rpl5), 16));
- rpl5 = (void *)rpl;
- INIT_TP_WR(rpl5, ep->hwtid);
- } else {
- skb_trim(skb, sizeof(*rpl));
- INIT_TP_WR(rpl, ep->hwtid);
- }
- OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
- ep->hwtid));
-
cxgb_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
enable_tcp_timestamps && req->tcpopt.tstamp,
(ep->com.remote_addr.ss_family == AF_INET) ? 0 : 1);
@@ -2439,6 +2423,20 @@ static int accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
if (tcph->ece && tcph->cwr)
opt2 |= CCTRL_ECN_V(1);
}
+
+ skb_get(skb);
+ rpl = cplhdr(skb);
+ if (!is_t4(adapter_type)) {
+ skb_trim(skb, roundup(sizeof(*rpl5), 16));
+ rpl5 = (void *)rpl;
+ INIT_TP_WR(rpl5, ep->hwtid);
+ } else {
+ skb_trim(skb, sizeof(*rpl));
+ INIT_TP_WR(rpl, ep->hwtid);
+ }
+ OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
+ ep->hwtid));
+
if (CHELSIO_CHIP_VERSION(adapter_type) > CHELSIO_T4) {
u32 isn = (prandom_u32() & ~7UL) - 1;
opt2 |= T5_OPT_2_VALID_F;
@@ -2800,7 +2798,8 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
break;
case MPA_REQ_SENT:
(void)stop_ep_timer(ep);
- if (mpa_rev == 1 || (mpa_rev == 2 && ep->tried_with_mpa_v1))
+ if (status != CPL_ERR_CONN_RESET || mpa_rev == 1 ||
+ (mpa_rev == 2 && ep->tried_with_mpa_v1))
connect_reply_upcall(ep, -ECONNRESET);
else {
/*
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 6d30427..1fd8798 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -161,7 +161,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
cq->gts = rdev->lldi.gts_reg;
cq->rdev = rdev;
- cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, T4_BAR2_QTYPE_INGRESS,
+ cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, CXGB4_BAR2_QTYPE_INGRESS,
&cq->bar2_qid,
user ? &cq->bar2_pa : NULL);
if (user && !cq->bar2_pa) {
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 347fe18..a9e3a11 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -279,12 +279,13 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
wq->db = rdev->lldi.db_reg;
- wq->sq.bar2_va = c4iw_bar2_addrs(rdev, wq->sq.qid, T4_BAR2_QTYPE_EGRESS,
+ wq->sq.bar2_va = c4iw_bar2_addrs(rdev, wq->sq.qid,
+ CXGB4_BAR2_QTYPE_EGRESS,
&wq->sq.bar2_qid,
user ? &wq->sq.bar2_pa : NULL);
if (need_rq)
wq->rq.bar2_va = c4iw_bar2_addrs(rdev, wq->rq.qid,
- T4_BAR2_QTYPE_EGRESS,
+ CXGB4_BAR2_QTYPE_EGRESS,
&wq->rq.bar2_qid,
user ? &wq->rq.bar2_pa : NULL);
@@ -2572,7 +2573,7 @@ static int alloc_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx,
memset(wq->queue, 0, wq->memsize);
pci_unmap_addr_set(wq, mapping, wq->dma_addr);
- wq->bar2_va = c4iw_bar2_addrs(rdev, wq->qid, T4_BAR2_QTYPE_EGRESS,
+ wq->bar2_va = c4iw_bar2_addrs(rdev, wq->qid, CXGB4_BAR2_QTYPE_EGRESS,
&wq->bar2_qid,
user ? &wq->bar2_pa : NULL);
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index 6aa5a8a..6b89353 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -1074,6 +1074,8 @@ static void log_state_transition(struct hfi1_pportdata *ppd, u32 state);
static void log_physical_state(struct hfi1_pportdata *ppd, u32 state);
static int wait_physical_linkstate(struct hfi1_pportdata *ppd, u32 state,
int msecs);
+static int wait_phys_link_out_of_offline(struct hfi1_pportdata *ppd,
+ int msecs);
static void read_planned_down_reason_code(struct hfi1_devdata *dd, u8 *pdrrc);
static void read_link_down_reason(struct hfi1_devdata *dd, u8 *ldr);
static void handle_temp_err(struct hfi1_devdata *dd);
@@ -10769,13 +10771,15 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
break;
ppd->port_error_action = 0;
- ppd->host_link_state = HLS_DN_POLL;
if (quick_linkup) {
/* quick linkup does not go into polling */
ret = do_quick_linkup(dd);
} else {
ret1 = set_physical_link_state(dd, PLS_POLLING);
+ if (!ret1)
+ ret1 = wait_phys_link_out_of_offline(ppd,
+ 3000);
if (ret1 != HCMD_SUCCESS) {
dd_dev_err(dd,
"Failed to transition to Polling link state, return 0x%x\n",
@@ -10783,6 +10787,14 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
ret = -EINVAL;
}
}
+
+ /*
+ * Change the host link state after requesting DC8051 to
+ * change its physical state so that we can ignore any
+ * interrupt with stale LNI(XX) error, which will not be
+ * cleared until DC8051 transitions to Polling state.
+ */
+ ppd->host_link_state = HLS_DN_POLL;
ppd->offline_disabled_reason =
HFI1_ODR_MASK(OPA_LINKDOWN_REASON_NONE);
/*
@@ -12914,6 +12926,39 @@ static int wait_phys_link_offline_substates(struct hfi1_pportdata *ppd,
return read_state;
}
+/*
+ * wait_phys_link_out_of_offline - wait for any out of offline state
+ * @ppd: port device
+ * @msecs: the number of milliseconds to wait
+ *
+ * Wait up to msecs milliseconds for any out of offline physical link
+ * state change to occur.
+ * Returns 0 if at least one state is reached, otherwise -ETIMEDOUT.
+ */
+static int wait_phys_link_out_of_offline(struct hfi1_pportdata *ppd,
+ int msecs)
+{
+ u32 read_state;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(msecs);
+ while (1) {
+ read_state = read_physical_state(ppd->dd);
+ if ((read_state & 0xF0) != PLS_OFFLINE)
+ break;
+ if (time_after(jiffies, timeout)) {
+ dd_dev_err(ppd->dd,
+ "timeout waiting for phy link out of offline. Read state 0x%x, %dms\n",
+ read_state, msecs);
+ return -ETIMEDOUT;
+ }
+ usleep_range(1950, 2050); /* sleep 2ms-ish */
+ }
+
+ log_state_transition(ppd, read_state);
+ return read_state;
+}
+
#define CLEAR_STATIC_RATE_CONTROL_SMASK(r) \
(r &= ~SEND_CTXT_CHECK_ENABLE_DISALLOW_PBC_STATIC_RATE_CONTROL_SMASK)
diff --git a/drivers/infiniband/hw/hfi1/mad.c b/drivers/infiniband/hw/hfi1/mad.c
index f208a25..1669548 100644
--- a/drivers/infiniband/hw/hfi1/mad.c
+++ b/drivers/infiniband/hw/hfi1/mad.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2015-2017 Intel Corporation.
+ * Copyright(c) 2015-2018 Intel Corporation.
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
@@ -4829,7 +4829,7 @@ static int hfi1_process_opa_mad(struct ib_device *ibdev, int mad_flags,
int ret;
int pkey_idx;
int local_mad = 0;
- u32 resp_len = 0;
+ u32 resp_len = in_wc->byte_len - sizeof(*in_grh);
struct hfi1_ibport *ibp = to_iport(ibdev, port);
pkey_idx = hfi1_lookup_pkey_idx(ibp, LIM_MGMT_P_KEY);
diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c
index 6c967dd..a8dd12e 100644
--- a/drivers/infiniband/hw/hfi1/pcie.c
+++ b/drivers/infiniband/hw/hfi1/pcie.c
@@ -331,7 +331,9 @@ int pcie_speeds(struct hfi1_devdata *dd)
/*
* bus->max_bus_speed is set from the bridge's linkcap Max Link Speed
*/
- if (parent && dd->pcidev->bus->max_bus_speed != PCIE_SPEED_8_0GT) {
+ if (parent &&
+ (dd->pcidev->bus->max_bus_speed == PCIE_SPEED_2_5GT ||
+ dd->pcidev->bus->max_bus_speed == PCIE_SPEED_5_0GT)) {
dd_dev_info(dd, "Parent PCIe bridge does not support Gen3\n");
dd->link_gen3_capable = 0;
}
diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c
index d648a41..291c12f 100644
--- a/drivers/infiniband/hw/hfi1/sdma.c
+++ b/drivers/infiniband/hw/hfi1/sdma.c
@@ -65,6 +65,7 @@
#define SDMA_DESCQ_CNT 2048
#define SDMA_DESC_INTR 64
#define INVALID_TAIL 0xffff
+#define SDMA_PAD max_t(size_t, MAX_16B_PADDING, sizeof(u32))
static uint sdma_descq_cnt = SDMA_DESCQ_CNT;
module_param(sdma_descq_cnt, uint, S_IRUGO);
@@ -1280,7 +1281,7 @@ void sdma_clean(struct hfi1_devdata *dd, size_t num_engines)
struct sdma_engine *sde;
if (dd->sdma_pad_dma) {
- dma_free_coherent(&dd->pcidev->dev, 4,
+ dma_free_coherent(&dd->pcidev->dev, SDMA_PAD,
(void *)dd->sdma_pad_dma,
dd->sdma_pad_phys);
dd->sdma_pad_dma = NULL;
@@ -1481,7 +1482,7 @@ int sdma_init(struct hfi1_devdata *dd, u8 port)
/* Allocate memory for pad */
dd->sdma_pad_dma = dma_zalloc_coherent(
&dd->pcidev->dev,
- sizeof(u32),
+ SDMA_PAD,
&dd->sdma_pad_phys,
GFP_KERNEL
);
@@ -1518,8 +1519,11 @@ int sdma_init(struct hfi1_devdata *dd, u8 port)
}
ret = rhashtable_init(tmp_sdma_rht, &sdma_rht_params);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(tmp_sdma_rht);
goto bail;
+ }
+
dd->sdma_rht = tmp_sdma_rht;
dd_dev_info(dd, "SDMA num_sdma: %u\n", dd->num_sdma);
diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c
index cbff746..684a298e 100644
--- a/drivers/infiniband/hw/hfi1/user_sdma.c
+++ b/drivers/infiniband/hw/hfi1/user_sdma.c
@@ -856,8 +856,10 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
changes = set_txreq_header_ahg(req, tx,
datalen);
- if (changes < 0)
+ if (changes < 0) {
+ ret = changes;
goto free_tx;
+ }
}
} else {
ret = sdma_txinit(&tx->txreq, 0, sizeof(req->hdr) +
diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c
index 1ad38c8..4e7b3c0 100644
--- a/drivers/infiniband/hw/hfi1/verbs.c
+++ b/drivers/infiniband/hw/hfi1/verbs.c
@@ -149,9 +149,6 @@ static int pio_wait(struct rvt_qp *qp,
/* Length of buffer to create verbs txreq cache name */
#define TXREQ_NAME_LEN 24
-/* 16B trailing buffer */
-static const u8 trail_buf[MAX_16B_PADDING];
-
static uint wss_threshold;
module_param(wss_threshold, uint, S_IRUGO);
MODULE_PARM_DESC(wss_threshold, "Percentage (1-100) of LLC to use as a threshold for a cacheless copy");
@@ -893,8 +890,8 @@ static int build_verbs_tx_desc(
/* add icrc, lt byte, and padding to flit */
if (extra_bytes)
- ret = sdma_txadd_kvaddr(sde->dd, &tx->txreq,
- (void *)trail_buf, extra_bytes);
+ ret = sdma_txadd_daddr(sde->dd, &tx->txreq,
+ sde->dd->sdma_pad_phys, extra_bytes);
bail_txadd:
return ret;
@@ -1151,7 +1148,8 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
}
/* add icrc, lt byte, and padding to flit */
if (extra_bytes)
- seg_pio_copy_mid(pbuf, trail_buf, extra_bytes);
+ seg_pio_copy_mid(pbuf, ppd->dd->sdma_pad_dma,
+ extra_bytes);
seg_pio_copy_end(pbuf);
}
diff --git a/drivers/infiniband/hw/hfi1/vnic_sdma.c b/drivers/infiniband/hw/hfi1/vnic_sdma.c
index c3c96c5..718dcde 100644
--- a/drivers/infiniband/hw/hfi1/vnic_sdma.c
+++ b/drivers/infiniband/hw/hfi1/vnic_sdma.c
@@ -57,7 +57,6 @@
#define HFI1_VNIC_TXREQ_NAME_LEN 32
#define HFI1_VNIC_SDMA_DESC_WTRMRK 64
-#define HFI1_VNIC_SDMA_RETRY_COUNT 1
/*
* struct vnic_txreq - VNIC transmit descriptor
@@ -67,7 +66,6 @@
* @pad: pad buffer
* @plen: pad length
* @pbc_val: pbc value
- * @retry_count: tx retry count
*/
struct vnic_txreq {
struct sdma_txreq txreq;
@@ -77,8 +75,6 @@ struct vnic_txreq {
unsigned char pad[HFI1_VNIC_MAX_PAD];
u16 plen;
__le64 pbc_val;
-
- u32 retry_count;
};
static void vnic_sdma_complete(struct sdma_txreq *txreq,
@@ -196,7 +192,6 @@ int hfi1_vnic_send_dma(struct hfi1_devdata *dd, u8 q_idx,
ret = build_vnic_tx_desc(sde, tx, pbc);
if (unlikely(ret))
goto free_desc;
- tx->retry_count = 0;
ret = sdma_send_txreq(sde, &vnic_sdma->wait, &tx->txreq,
vnic_sdma->pkts_sent);
@@ -238,14 +233,14 @@ static int hfi1_vnic_sdma_sleep(struct sdma_engine *sde,
struct hfi1_vnic_sdma *vnic_sdma =
container_of(wait, struct hfi1_vnic_sdma, wait);
struct hfi1_ibdev *dev = &vnic_sdma->dd->verbs_dev;
- struct vnic_txreq *tx = container_of(txreq, struct vnic_txreq, txreq);
- if (sdma_progress(sde, seq, txreq))
- if (tx->retry_count++ < HFI1_VNIC_SDMA_RETRY_COUNT)
- return -EAGAIN;
+ write_seqlock(&dev->iowait_lock);
+ if (sdma_progress(sde, seq, txreq)) {
+ write_sequnlock(&dev->iowait_lock);
+ return -EAGAIN;
+ }
vnic_sdma->state = HFI1_VNIC_SDMA_Q_DEFERRED;
- write_seqlock(&dev->iowait_lock);
if (list_empty(&vnic_sdma->wait.list))
iowait_queue(pkts_sent, wait, &sde->dmawait);
write_sequnlock(&dev->iowait_lock);
diff --git a/drivers/infiniband/hw/hns/Kconfig b/drivers/infiniband/hw/hns/Kconfig
index fddb5fd..21c2100 100644
--- a/drivers/infiniband/hw/hns/Kconfig
+++ b/drivers/infiniband/hw/hns/Kconfig
@@ -1,6 +1,7 @@
config INFINIBAND_HNS
tristate "HNS RoCE Driver"
depends on NET_VENDOR_HISILICON
+ depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
depends on ARM64 || (COMPILE_TEST && 64BIT)
---help---
This is a RoCE/RDMA driver for the Hisilicon RoCE engine. The engine
diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h
index 9a24fd0..ebfb099 100644
--- a/drivers/infiniband/hw/hns/hns_roce_device.h
+++ b/drivers/infiniband/hw/hns/hns_roce_device.h
@@ -665,7 +665,9 @@ struct hns_roce_caps {
u32 max_sq_sg; /* 2 */
u32 max_sq_inline; /* 32 */
u32 max_rq_sg; /* 2 */
+ u32 max_extend_sg;
int num_qps; /* 256k */
+ int reserved_qps;
u32 max_wqes; /* 16k */
u32 max_sq_desc_sz; /* 64 */
u32 max_rq_desc_sz; /* 64 */
diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.h b/drivers/infiniband/hw/hns/hns_roce_hem.h
index e8850d5..a94444d 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.h
@@ -54,7 +54,7 @@ enum {
#define HNS_ROCE_HEM_CHUNK_LEN \
((256 - sizeof(struct list_head) - 2 * sizeof(int)) / \
- (sizeof(struct scatterlist)))
+ (sizeof(struct scatterlist) + sizeof(void *)))
#define check_whether_bt_num_3(type, hop_num) \
(type < HEM_TYPE_MTT && hop_num == 2)
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
index a442b29..7021444 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
@@ -121,6 +121,7 @@ static int set_rwqe_data_seg(struct ib_qp *ibqp, const struct ib_send_wr *wr,
}
if (wr->opcode == IB_WR_RDMA_READ) {
+ *bad_wr = wr;
dev_err(hr_dev->dev, "Not support inline data!\n");
return -EINVAL;
}
@@ -1193,6 +1194,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->num_cqs = HNS_ROCE_V2_MAX_CQ_NUM;
caps->max_cqes = HNS_ROCE_V2_MAX_CQE_NUM;
caps->max_sq_sg = HNS_ROCE_V2_MAX_SQ_SGE_NUM;
+ caps->max_extend_sg = HNS_ROCE_V2_MAX_EXTEND_SGE_NUM;
caps->max_rq_sg = HNS_ROCE_V2_MAX_RQ_SGE_NUM;
caps->max_sq_inline = HNS_ROCE_V2_MAX_SQ_INLINE;
caps->num_uars = HNS_ROCE_V2_UAR_NUM;
@@ -1222,6 +1224,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->reserved_mrws = 1;
caps->reserved_uars = 0;
caps->reserved_cqs = 0;
+ caps->reserved_qps = HNS_ROCE_V2_RSV_QPS;
caps->qpc_ba_pg_sz = 0;
caps->qpc_buf_pg_sz = 0;
@@ -1773,6 +1776,9 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
struct hns_roce_v2_mpt_entry *mpt_entry = mb_buf;
int ret = 0;
+ roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_MPT_ST_M,
+ V2_MPT_BYTE_4_MPT_ST_S, V2_MPT_ST_VALID);
+
if (flags & IB_MR_REREG_PD) {
roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_PD_M,
V2_MPT_BYTE_4_PD_S, pdn);
@@ -2266,6 +2272,7 @@ static int hns_roce_v2_poll_one(struct hns_roce_cq *hr_cq,
wc->src_qp = (u8)roce_get_field(cqe->byte_32,
V2_CQE_BYTE_32_RMT_QPN_M,
V2_CQE_BYTE_32_RMT_QPN_S);
+ wc->slid = 0;
wc->wc_flags |= (roce_get_bit(cqe->byte_32,
V2_CQE_BYTE_32_GRH_S) ?
IB_WC_GRH : 0);
@@ -3439,7 +3446,7 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp,
struct device *dev = hr_dev->dev;
int ret = -EINVAL;
- context = kcalloc(2, sizeof(*context), GFP_KERNEL);
+ context = kcalloc(2, sizeof(*context), GFP_ATOMIC);
if (!context)
return -ENOMEM;
@@ -3499,13 +3506,16 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp,
roce_set_field(qpc_mask->byte_160_sq_ci_pi,
V2_QPC_BYTE_160_SQ_PRODUCER_IDX_M,
V2_QPC_BYTE_160_SQ_PRODUCER_IDX_S, 0);
- roce_set_field(context->byte_84_rq_ci_pi,
+
+ if (!ibqp->srq) {
+ roce_set_field(context->byte_84_rq_ci_pi,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S,
hr_qp->rq.head);
- roce_set_field(qpc_mask->byte_84_rq_ci_pi,
+ roce_set_field(qpc_mask->byte_84_rq_ci_pi,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S, 0);
+ }
}
if (attr_mask & IB_QP_AV) {
@@ -3967,7 +3977,8 @@ static void hns_roce_set_qps_to_err(struct hns_roce_dev *hr_dev, u32 qpn)
if (hr_qp->ibqp.uobject) {
if (hr_qp->sdb_en == 1) {
hr_qp->sq.head = *(int *)(hr_qp->sdb.virt_addr);
- hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
+ if (hr_qp->rdb_en == 1)
+ hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
} else {
dev_warn(hr_dev->dev, "flush cqe is unsupported in userspace!\n");
return;
@@ -4572,9 +4583,9 @@ static void hns_roce_v2_free_eq(struct hns_roce_dev *hr_dev,
return;
}
- if (eq->buf_list)
- dma_free_coherent(hr_dev->dev, buf_chk_sz,
- eq->buf_list->buf, eq->buf_list->map);
+ dma_free_coherent(hr_dev->dev, buf_chk_sz, eq->buf_list->buf,
+ eq->buf_list->map);
+ kfree(eq->buf_list);
}
static void hns_roce_config_eqc(struct hns_roce_dev *hr_dev,
@@ -5117,6 +5128,7 @@ static int hns_roce_v2_init_eq_table(struct hns_roce_dev *hr_dev)
create_singlethread_workqueue("hns_roce_irq_workqueue");
if (!hr_dev->irq_workq) {
dev_err(dev, "Create irq workqueue failed!\n");
+ ret = -ENOMEM;
goto err_request_irq_fail;
}
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
index 14aa308..2c3e600d 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
@@ -50,6 +50,7 @@
#define HNS_ROCE_V2_MAX_CQE_NUM 0x10000
#define HNS_ROCE_V2_MAX_RQ_SGE_NUM 0x100
#define HNS_ROCE_V2_MAX_SQ_SGE_NUM 0xff
+#define HNS_ROCE_V2_MAX_EXTEND_SGE_NUM 0x200000
#define HNS_ROCE_V2_MAX_SQ_INLINE 0x20
#define HNS_ROCE_V2_UAR_NUM 256
#define HNS_ROCE_V2_PHY_UAR_NUM 1
@@ -78,6 +79,7 @@
#define HNS_ROCE_INVALID_LKEY 0x100
#define HNS_ROCE_CMQ_TX_TIMEOUT 30000
#define HNS_ROCE_V2_UC_RC_SGE_NUM_IN_WQE 2
+#define HNS_ROCE_V2_RSV_QPS 8
#define HNS_ROCE_CONTEXT_HOP_NUM 1
#define HNS_ROCE_MTT_HOP_NUM 1
diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c
index 41a538d..c68596d 100644
--- a/drivers/infiniband/hw/hns/hns_roce_mr.c
+++ b/drivers/infiniband/hw/hns/hns_roce_mr.c
@@ -1017,14 +1017,14 @@ struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
goto err_umem;
}
} else {
- int pbl_size = 1;
+ u64 pbl_size = 1;
bt_size = (1 << (hr_dev->caps.pbl_ba_pg_sz + PAGE_SHIFT)) / 8;
for (i = 0; i < hr_dev->caps.pbl_hop_num; i++)
pbl_size *= bt_size;
if (n > pbl_size) {
dev_err(dev,
- " MR len %lld err. MR page num is limited to %d!\n",
+ " MR len %lld err. MR page num is limited to %lld!\n",
length, pbl_size);
ret = -EINVAL;
goto err_umem;
diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c
index 2fa4fb1..af24698 100644
--- a/drivers/infiniband/hw/hns/hns_roce_qp.c
+++ b/drivers/infiniband/hw/hns/hns_roce_qp.c
@@ -31,6 +31,7 @@
* SOFTWARE.
*/
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_umem.h>
@@ -372,6 +373,16 @@ static int hns_roce_set_user_sq_size(struct hns_roce_dev *hr_dev,
if (hr_qp->sq.max_gs > 2)
hr_qp->sge.sge_cnt = roundup_pow_of_two(hr_qp->sq.wqe_cnt *
(hr_qp->sq.max_gs - 2));
+
+ if ((hr_qp->sq.max_gs > 2) && (hr_dev->pci_dev->revision == 0x20)) {
+ if (hr_qp->sge.sge_cnt > hr_dev->caps.max_extend_sg) {
+ dev_err(hr_dev->dev,
+ "The extended sge cnt error! sge_cnt=%d\n",
+ hr_qp->sge.sge_cnt);
+ return -EINVAL;
+ }
+ }
+
hr_qp->sge.sge_shift = 4;
/* Get buf size, SQ and RQ are aligned to page_szie */
@@ -465,6 +476,14 @@ static int hns_roce_set_kernel_sq_size(struct hns_roce_dev *hr_dev,
hr_qp->sge.sge_shift = 4;
}
+ if ((hr_qp->sq.max_gs > 2) && hr_dev->pci_dev->revision == 0x20) {
+ if (hr_qp->sge.sge_cnt > hr_dev->caps.max_extend_sg) {
+ dev_err(dev, "The extended sge cnt error! sge_cnt=%d\n",
+ hr_qp->sge.sge_cnt);
+ return -EINVAL;
+ }
+ }
+
/* Get buf size, SQ and RQ are aligned to PAGE_SIZE */
page_size = 1 << (hr_dev->caps.mtt_buf_pg_sz + PAGE_SHIFT);
hr_qp->sq.offset = 0;
@@ -503,7 +522,8 @@ static int hns_roce_qp_has_sq(struct ib_qp_init_attr *attr)
static int hns_roce_qp_has_rq(struct ib_qp_init_attr *attr)
{
if (attr->qp_type == IB_QPT_XRC_INI ||
- attr->qp_type == IB_QPT_XRC_TGT || attr->srq)
+ attr->qp_type == IB_QPT_XRC_TGT || attr->srq ||
+ !attr->cap.max_recv_wr)
return 0;
return 1;
@@ -1106,14 +1126,20 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_qp_table *qp_table = &hr_dev->qp_table;
int reserved_from_top = 0;
+ int reserved_from_bot;
int ret;
spin_lock_init(&qp_table->lock);
INIT_RADIX_TREE(&hr_dev->qp_table_tree, GFP_ATOMIC);
- /* A port include two SQP, six port total 12 */
+ /* In hw v1, a port include two SQP, six ports total 12 */
+ if (hr_dev->caps.max_sq_sg <= 2)
+ reserved_from_bot = SQP_NUM;
+ else
+ reserved_from_bot = hr_dev->caps.reserved_qps;
+
ret = hns_roce_bitmap_init(&qp_table->bitmap, hr_dev->caps.num_qps,
- hr_dev->caps.num_qps - 1, SQP_NUM,
+ hr_dev->caps.num_qps - 1, reserved_from_bot,
reserved_from_top);
if (ret) {
dev_err(hr_dev->dev, "qp bitmap init failed!error=%d\n",
diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c
index 423818a..771eb6b 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_cm.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c
@@ -1689,7 +1689,7 @@ static enum i40iw_status_code i40iw_add_mqh_6(struct i40iw_device *iwdev,
unsigned long flags;
rtnl_lock();
- for_each_netdev_rcu(&init_net, ip_dev) {
+ for_each_netdev(&init_net, ip_dev) {
if ((((rdma_vlan_dev_vlan_id(ip_dev) < I40IW_NO_VLAN) &&
(rdma_vlan_dev_real_dev(ip_dev) == iwdev->netdev)) ||
(ip_dev == iwdev->netdev)) && (ip_dev->flags & IFF_UP)) {
diff --git a/drivers/infiniband/hw/mlx4/Kconfig b/drivers/infiniband/hw/mlx4/Kconfig
index db4aa13..d1de328 100644
--- a/drivers/infiniband/hw/mlx4/Kconfig
+++ b/drivers/infiniband/hw/mlx4/Kconfig
@@ -1,6 +1,7 @@
config MLX4_INFINIBAND
tristate "Mellanox ConnectX HCA support"
depends on NETDEVICES && ETHERNET && PCI && INET
+ depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
depends on MAY_USE_DEVLINK
select NET_VENDOR_MELLANOX
select MLX4_CORE
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 0bbeaaa..9386bb5 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -3069,16 +3069,17 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr)
ibdev->ib_active = false;
flush_workqueue(wq);
- mlx4_ib_close_sriov(ibdev);
- mlx4_ib_mad_cleanup(ibdev);
- ib_unregister_device(&ibdev->ib_dev);
- mlx4_ib_diag_cleanup(ibdev);
if (ibdev->iboe.nb.notifier_call) {
if (unregister_netdevice_notifier(&ibdev->iboe.nb))
pr_warn("failure unregistering notifier\n");
ibdev->iboe.nb.notifier_call = NULL;
}
+ mlx4_ib_close_sriov(ibdev);
+ mlx4_ib_mad_cleanup(ibdev);
+ ib_unregister_device(&ibdev->ib_dev);
+ mlx4_ib_diag_cleanup(ibdev);
+
mlx4_qp_release_range(dev, ibdev->steer_qpn_base,
ibdev->steer_qpn_count);
kfree(ibdev->ib_uc_qpns_bitmap);
diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c
index e219093..d2da28d 100644
--- a/drivers/infiniband/hw/mlx4/sysfs.c
+++ b/drivers/infiniband/hw/mlx4/sysfs.c
@@ -353,16 +353,12 @@ static int add_port_entries(struct mlx4_ib_dev *device, int port_num)
static void get_name(struct mlx4_ib_dev *dev, char *name, int i, int max)
{
- char base_name[9];
-
- /* pci_name format is: bus:dev:func -> xxxx:yy:zz.n */
- strlcpy(name, pci_name(dev->dev->persist->pdev), max);
- strncpy(base_name, name, 8); /*till xxxx:yy:*/
- base_name[8] = '\0';
- /* with no ARI only 3 last bits are used so when the fn is higher than 8
+ /* pci_name format is: bus:dev:func -> xxxx:yy:zz.n
+ * with no ARI only 3 last bits are used so when the fn is higher than 8
* need to add it to the dev num, so count in the last number will be
* modulo 8 */
- sprintf(name, "%s%.2d.%d", base_name, (i/8), (i%8));
+ snprintf(name, max, "%.8s%.2d.%d", pci_name(dev->dev->persist->pdev),
+ i / 8, i % 8);
}
struct mlx4_port {
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index c05eae9..df5be46 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -1823,6 +1823,14 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
context->lib_caps = req.lib_caps;
print_lib_caps(dev, context->lib_caps);
+ if (mlx5_lag_is_active(dev->mdev)) {
+ u8 port = mlx5_core_native_port_num(dev->mdev);
+
+ atomic_set(&context->tx_port_affinity,
+ atomic_add_return(
+ 1, &dev->roce[port].tx_port_affinity));
+ }
+
return &context->ibucontext;
out_mdev:
@@ -3278,10 +3286,6 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
}
INIT_LIST_HEAD(&handler->list);
- if (dst) {
- memcpy(&dest_arr[0], dst, sizeof(*dst));
- dest_num++;
- }
for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
err = parse_flow_attr(dev->mdev, spec->match_criteria,
@@ -3295,6 +3299,11 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
ib_flow += ((union ib_flow_spec *)ib_flow)->size;
}
+ if (dst && !(flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP)) {
+ memcpy(&dest_arr[0], dst, sizeof(*dst));
+ dest_num++;
+ }
+
if (!flow_is_multicast_only(flow_attr))
set_underlay_qp(dev, spec, underlay_qpn);
@@ -3332,10 +3341,8 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
}
if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
- if (!(flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT)) {
+ if (!dest_num)
rule_dst = NULL;
- dest_num = 0;
- }
} else {
if (is_egress)
flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW;
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 941d1df..6a060c8 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -139,6 +139,8 @@ struct mlx5_ib_ucontext {
u64 lib_caps;
DECLARE_BITMAP(dm_pages, MLX5_MAX_MEMIC_PAGES);
u16 devx_uid;
+ /* For RoCE LAG TX affinity */
+ atomic_t tx_port_affinity;
};
static inline struct mlx5_ib_ucontext *to_mucontext(struct ib_ucontext *ibucontext)
@@ -700,7 +702,7 @@ struct mlx5_roce {
rwlock_t netdev_lock;
struct net_device *netdev;
struct notifier_block nb;
- atomic_t next_port;
+ atomic_t tx_port_affinity;
enum ib_port_state last_port_state;
struct mlx5_ib_dev *dev;
u8 native_port_num;
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 77b1f3f..ef0f710 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -2828,10 +2828,12 @@ static int modify_raw_packet_qp_sq(struct mlx5_core_dev *dev,
}
/* Only remove the old rate after new rate was set */
- if ((old_rl.rate &&
- !mlx5_rl_are_equal(&old_rl, &new_rl)) ||
- (new_state != MLX5_SQC_STATE_RDY))
+ if ((old_rl.rate && !mlx5_rl_are_equal(&old_rl, &new_rl)) ||
+ (new_state != MLX5_SQC_STATE_RDY)) {
mlx5_rl_remove_rate(dev, &old_rl);
+ if (new_state != MLX5_SQC_STATE_RDY)
+ memset(&new_rl, 0, sizeof(new_rl));
+ }
ibqp->rl = new_rl;
sq->state = new_state;
@@ -2908,6 +2910,37 @@ static int modify_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
return 0;
}
+static unsigned int get_tx_affinity(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_pd *pd,
+ struct mlx5_ib_qp_base *qp_base,
+ u8 port_num)
+{
+ struct mlx5_ib_ucontext *ucontext = NULL;
+ unsigned int tx_port_affinity;
+
+ if (pd && pd->ibpd.uobject && pd->ibpd.uobject->context)
+ ucontext = to_mucontext(pd->ibpd.uobject->context);
+
+ if (ucontext) {
+ tx_port_affinity = (unsigned int)atomic_add_return(
+ 1, &ucontext->tx_port_affinity) %
+ MLX5_MAX_PORTS +
+ 1;
+ mlx5_ib_dbg(dev, "Set tx affinity 0x%x to qpn 0x%x ucontext %p\n",
+ tx_port_affinity, qp_base->mqp.qpn, ucontext);
+ } else {
+ tx_port_affinity =
+ (unsigned int)atomic_add_return(
+ 1, &dev->roce[port_num].tx_port_affinity) %
+ MLX5_MAX_PORTS +
+ 1;
+ mlx5_ib_dbg(dev, "Set tx affinity 0x%x to qpn 0x%x\n",
+ tx_port_affinity, qp_base->mqp.qpn);
+ }
+
+ return tx_port_affinity;
+}
+
static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
enum ib_qp_state cur_state, enum ib_qp_state new_state,
@@ -2973,6 +3006,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
if (!context)
return -ENOMEM;
+ pd = get_pd(qp);
context->flags = cpu_to_be32(mlx5_st << 16);
if (!(attr_mask & IB_QP_PATH_MIG_STATE)) {
@@ -3001,9 +3035,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
(ibqp->qp_type == IB_QPT_XRC_TGT)) {
if (mlx5_lag_is_active(dev->mdev)) {
u8 p = mlx5_core_native_port_num(dev->mdev);
- tx_affinity = (unsigned int)atomic_add_return(1,
- &dev->roce[p].next_port) %
- MLX5_MAX_PORTS + 1;
+ tx_affinity = get_tx_affinity(dev, pd, base, p);
context->flags |= cpu_to_be32(tx_affinity << 24);
}
}
@@ -3061,7 +3093,6 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
goto out;
}
- pd = get_pd(qp);
get_cqs(qp->ibqp.qp_type, qp->ibqp.send_cq, qp->ibqp.recv_cq,
&send_cq, &recv_cq);
@@ -4376,6 +4407,12 @@ static int _mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
u8 next_fence = 0;
u8 fence;
+ if (unlikely(mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR &&
+ !drain)) {
+ *bad_wr = wr;
+ return -EIO;
+ }
+
if (unlikely(ibqp->qp_type == IB_QPT_GSI))
return mlx5_ib_gsi_post_send(ibqp, wr, bad_wr);
@@ -4385,13 +4422,6 @@ static int _mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
spin_lock_irqsave(&qp->sq.lock, flags);
- if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR && !drain) {
- err = -EIO;
- *bad_wr = wr;
- nreq = 0;
- goto out;
- }
-
for (nreq = 0; wr; nreq++, wr = wr->next) {
if (unlikely(wr->opcode >= ARRAY_SIZE(mlx5_ib_opcode))) {
mlx5_ib_warn(dev, "\n");
@@ -4706,18 +4736,17 @@ static int _mlx5_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
int ind;
int i;
+ if (unlikely(mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR &&
+ !drain)) {
+ *bad_wr = wr;
+ return -EIO;
+ }
+
if (unlikely(ibqp->qp_type == IB_QPT_GSI))
return mlx5_ib_gsi_post_recv(ibqp, wr, bad_wr);
spin_lock_irqsave(&qp->rq.lock, flags);
- if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR && !drain) {
- err = -EIO;
- *bad_wr = wr;
- nreq = 0;
- goto out;
- }
-
ind = qp->rq.head & (qp->rq.wqe_cnt - 1);
for (nreq = 0; wr; nreq++, wr = wr->next) {
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index f3e80de..af7f2083 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -986,7 +986,8 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type)
goto err_free_dev;
}
- if (mthca_cmd_init(mdev)) {
+ err = mthca_cmd_init(mdev);
+ if (err) {
mthca_err(mdev, "Failed to init command interface, aborting.\n");
goto err_free_dev;
}
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c
index a0af6d4..d1680d3 100644
--- a/drivers/infiniband/hw/qedr/main.c
+++ b/drivers/infiniband/hw/qedr/main.c
@@ -77,7 +77,7 @@ static void qedr_get_dev_fw_str(struct ib_device *ibdev, char *str)
struct qedr_dev *qedr = get_qedr_dev(ibdev);
u32 fw_ver = (u32)qedr->attr.fw_ver;
- snprintf(str, IB_FW_VERSION_NAME_MAX, "%d. %d. %d. %d",
+ snprintf(str, IB_FW_VERSION_NAME_MAX, "%d.%d.%d.%d",
(fw_ver >> 24) & 0xFF, (fw_ver >> 16) & 0xFF,
(fw_ver >> 8) & 0xFF, fw_ver & 0xFF);
}
diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c
index 505fa36..93b16237 100644
--- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c
+++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c
@@ -492,6 +492,8 @@ int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
int i;
qp = idr_find(&dev->qpidr.idr, conn_param->qpn);
+ if (unlikely(!qp))
+ return -EINVAL;
laddr = (struct sockaddr_in *)&cm_id->m_local_addr;
raddr = (struct sockaddr_in *)&cm_id->m_remote_addr;
diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c
index 8cc3df2..9167a1c 100644
--- a/drivers/infiniband/hw/qedr/verbs.c
+++ b/drivers/infiniband/hw/qedr/verbs.c
@@ -1701,6 +1701,14 @@ static void qedr_cleanup_user(struct qedr_dev *dev, struct qedr_qp *qp)
if (qp->urq.umem)
ib_umem_release(qp->urq.umem);
qp->urq.umem = NULL;
+
+ if (rdma_protocol_roce(&dev->ibdev, 1)) {
+ qedr_free_pbl(dev, &qp->usq.pbl_info, qp->usq.pbl_tbl);
+ qedr_free_pbl(dev, &qp->urq.pbl_info, qp->urq.pbl_tbl);
+ } else {
+ kfree(qp->usq.pbl_tbl);
+ kfree(qp->urq.pbl_tbl);
+ }
}
static int qedr_create_user_qp(struct qedr_dev *dev,
@@ -2809,8 +2817,8 @@ int qedr_dereg_mr(struct ib_mr *ib_mr)
dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
- if ((mr->type != QEDR_MR_DMA) && (mr->type != QEDR_MR_FRMR))
- qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table);
+ if (mr->type != QEDR_MR_DMA)
+ free_mr_info(dev, &mr->info);
/* it could be user registered memory. */
if (mr->umem)
diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c
index d0723d4..7424e88 100644
--- a/drivers/infiniband/hw/qib/qib_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_sdma.c
@@ -576,8 +576,10 @@ int qib_sdma_verbs_send(struct qib_pportdata *ppd,
dw = (len + 3) >> 2;
addr = dma_map_single(&ppd->dd->pcidev->dev, sge->vaddr,
dw << 2, DMA_TO_DEVICE);
- if (dma_mapping_error(&ppd->dd->pcidev->dev, addr))
+ if (dma_mapping_error(&ppd->dd->pcidev->dev, addr)) {
+ ret = -ENOMEM;
goto unmap;
+ }
sdmadesc[0] = 0;
make_sdma_desc(ppd, sdmadesc, (u64) addr, dw, dwoffset);
/* SDmaUseLargeBuf has to be set in every descriptor */
diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c
index ca2638d..d831f3e 100644
--- a/drivers/infiniband/hw/qib/qib_sysfs.c
+++ b/drivers/infiniband/hw/qib/qib_sysfs.c
@@ -301,6 +301,9 @@ static ssize_t qib_portattr_show(struct kobject *kobj,
struct qib_pportdata *ppd =
container_of(kobj, struct qib_pportdata, pport_kobj);
+ if (!pattr->show)
+ return -EIO;
+
return pattr->show(ppd, buf);
}
@@ -312,6 +315,9 @@ static ssize_t qib_portattr_store(struct kobject *kobj,
struct qib_pportdata *ppd =
container_of(kobj, struct qib_pportdata, pport_kobj);
+ if (!pattr->store)
+ return -EIO;
+
return pattr->store(ppd, buf, len);
}
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
index b65d10b..f4cb5cf 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
@@ -555,7 +555,7 @@ struct ib_ah *pvrdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr,
if (!atomic_add_unless(&dev->num_ahs, 1, dev->dsr->caps.max_ah))
return ERR_PTR(-ENOMEM);
- ah = kzalloc(sizeof(*ah), GFP_KERNEL);
+ ah = kzalloc(sizeof(*ah), GFP_ATOMIC);
if (!ah) {
atomic_dec(&dev->num_ahs);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c
index 83311dd..ea089cb 100644
--- a/drivers/infiniband/sw/rxe/rxe_comp.c
+++ b/drivers/infiniband/sw/rxe/rxe_comp.c
@@ -191,6 +191,7 @@ static inline void reset_retry_counters(struct rxe_qp *qp)
{
qp->comp.retry_cnt = qp->attr.retry_cnt;
qp->comp.rnr_retry = qp->attr.rnr_retry;
+ qp->comp.started_retry = 0;
}
static inline enum comp_state check_psn(struct rxe_qp *qp,
@@ -253,6 +254,17 @@ static inline enum comp_state check_ack(struct rxe_qp *qp,
case IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE:
if (pkt->opcode != IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE &&
pkt->opcode != IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST) {
+ /* read retries of partial data may restart from
+ * read response first or response only.
+ */
+ if ((pkt->psn == wqe->first_psn &&
+ pkt->opcode ==
+ IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST) ||
+ (wqe->first_psn == wqe->last_psn &&
+ pkt->opcode ==
+ IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY))
+ break;
+
return COMPST_ERROR;
}
break;
@@ -499,11 +511,11 @@ static inline enum comp_state complete_wqe(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
- qp->comp.opcode = -1;
-
- if (pkt) {
- if (psn_compare(pkt->psn, qp->comp.psn) >= 0)
- qp->comp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
+ if (pkt && wqe->state == wqe_state_pending) {
+ if (psn_compare(wqe->last_psn, qp->comp.psn) >= 0) {
+ qp->comp.psn = (wqe->last_psn + 1) & BTH_PSN_MASK;
+ qp->comp.opcode = -1;
+ }
if (qp->req.wait_psn) {
qp->req.wait_psn = 0;
@@ -676,6 +688,20 @@ int rxe_completer(void *arg)
goto exit;
}
+ /* if we've started a retry, don't start another
+ * retry sequence, unless this is a timeout.
+ */
+ if (qp->comp.started_retry &&
+ !qp->comp.timeout_retry) {
+ if (pkt) {
+ rxe_drop_ref(pkt->qp);
+ kfree_skb(skb);
+ skb = NULL;
+ }
+
+ goto done;
+ }
+
if (qp->comp.retry_cnt > 0) {
if (qp->comp.retry_cnt != 7)
qp->comp.retry_cnt--;
@@ -692,6 +718,7 @@ int rxe_completer(void *arg)
rxe_counter_inc(rxe,
RXE_CNT_COMP_RETRY);
qp->req.need_retry = 1;
+ qp->comp.started_retry = 1;
rxe_run_task(&qp->req.task, 1);
}
@@ -701,7 +728,7 @@ int rxe_completer(void *arg)
skb = NULL;
}
- goto exit;
+ goto done;
} else {
rxe_counter_inc(rxe, RXE_CNT_RETRY_EXCEEDED);
diff --git a/drivers/infiniband/sw/rxe/rxe_hw_counters.c b/drivers/infiniband/sw/rxe/rxe_hw_counters.c
index 6aeb7a1..ea4542a 100644
--- a/drivers/infiniband/sw/rxe/rxe_hw_counters.c
+++ b/drivers/infiniband/sw/rxe/rxe_hw_counters.c
@@ -59,7 +59,7 @@ int rxe_ib_get_hw_stats(struct ib_device *ibdev,
return -EINVAL;
for (cnt = 0; cnt < ARRAY_SIZE(rxe_counter_name); cnt++)
- stats->value[cnt] = dev->stats_counters[cnt];
+ stats->value[cnt] = atomic64_read(&dev->stats_counters[cnt]);
return ARRAY_SIZE(rxe_counter_name);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c
index d30dbac..695a607 100644
--- a/drivers/infiniband/sw/rxe/rxe_recv.c
+++ b/drivers/infiniband/sw/rxe/rxe_recv.c
@@ -391,7 +391,7 @@ void rxe_rcv(struct sk_buff *skb)
calc_icrc = rxe_icrc_hdr(pkt, skb);
calc_icrc = rxe_crc32(rxe, calc_icrc, (u8 *)payload_addr(pkt),
- payload_size(pkt));
+ payload_size(pkt) + bth_pad(pkt));
calc_icrc = (__force u32)cpu_to_be32(~calc_icrc);
if (unlikely(calc_icrc != pack_icrc)) {
if (skb->protocol == htons(ETH_P_IPV6))
diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c
index fa98a52..1c1eae0 100644
--- a/drivers/infiniband/sw/rxe/rxe_req.c
+++ b/drivers/infiniband/sw/rxe/rxe_req.c
@@ -73,9 +73,6 @@ static void req_retry(struct rxe_qp *qp)
int npsn;
int first = 1;
- wqe = queue_head(qp->sq.queue);
- npsn = (qp->comp.psn - wqe->first_psn) & BTH_PSN_MASK;
-
qp->req.wqe_index = consumer_index(qp->sq.queue);
qp->req.psn = qp->comp.psn;
qp->req.opcode = -1;
@@ -107,11 +104,17 @@ static void req_retry(struct rxe_qp *qp)
if (first) {
first = 0;
- if (mask & WR_WRITE_OR_SEND_MASK)
+ if (mask & WR_WRITE_OR_SEND_MASK) {
+ npsn = (qp->comp.psn - wqe->first_psn) &
+ BTH_PSN_MASK;
retry_first_write_send(qp, wqe, mask, npsn);
+ }
- if (mask & WR_READ_MASK)
+ if (mask & WR_READ_MASK) {
+ npsn = (wqe->dma.length - wqe->dma.resid) /
+ qp->mtu;
wqe->iova += npsn * qp->mtu;
+ }
}
wqe->state = wqe_state_posted;
@@ -435,7 +438,7 @@ static struct sk_buff *init_req_packet(struct rxe_qp *qp,
if (pkt->mask & RXE_RETH_MASK) {
reth_set_rkey(pkt, ibwr->wr.rdma.rkey);
reth_set_va(pkt, wqe->iova);
- reth_set_len(pkt, wqe->dma.length);
+ reth_set_len(pkt, wqe->dma.resid);
}
if (pkt->mask & RXE_IMMDT_MASK)
@@ -497,6 +500,12 @@ static int fill_packet(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
if (err)
return err;
}
+ if (bth_pad(pkt)) {
+ u8 *pad = payload_addr(pkt) + paylen;
+
+ memset(pad, 0, bth_pad(pkt));
+ crc = rxe_crc32(rxe, crc, pad, bth_pad(pkt));
+ }
}
p = payload_addr(pkt) + paylen + bth_pad(pkt);
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 681d8e0..9078cfd 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -737,6 +737,13 @@ static enum resp_states read_reply(struct rxe_qp *qp,
if (err)
pr_err("Failed copying memory\n");
+ if (bth_pad(&ack_pkt)) {
+ struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
+ u8 *pad = payload_addr(&ack_pkt) + payload;
+
+ memset(pad, 0, bth_pad(&ack_pkt));
+ icrc = rxe_crc32(rxe, icrc, pad, bth_pad(&ack_pkt));
+ }
p = payload_addr(&ack_pkt) + payload + bth_pad(&ack_pkt);
*p = ~icrc;
diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
index 0d6c04b..c41a5fe 100644
--- a/drivers/infiniband/sw/rxe/rxe_srq.c
+++ b/drivers/infiniband/sw/rxe/rxe_srq.c
@@ -31,6 +31,7 @@
* SOFTWARE.
*/
+#include <linux/vmalloc.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
@@ -129,13 +130,18 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, context, q->buf,
q->buf_size, &q->ip);
- if (err)
+ if (err) {
+ vfree(q->buf);
+ kfree(q);
return err;
+ }
if (uresp) {
if (copy_to_user(&uresp->srq_num, &srq->srq_num,
- sizeof(uresp->srq_num)))
+ sizeof(uresp->srq_num))) {
+ rxe_queue_cleanup(q);
return -EFAULT;
+ }
}
return 0;
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h
index 3b731c7..6a75f96 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.h
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.h
@@ -158,6 +158,7 @@ struct rxe_comp_info {
int opcode;
int timeout;
int timeout_retry;
+ int started_retry;
u32 retry_cnt;
u32 rnr_retry;
struct rxe_task task;
@@ -408,16 +409,16 @@ struct rxe_dev {
spinlock_t mmap_offset_lock; /* guard mmap_offset */
int mmap_offset;
- u64 stats_counters[RXE_NUM_OF_COUNTERS];
+ atomic64_t stats_counters[RXE_NUM_OF_COUNTERS];
struct rxe_port port;
struct list_head list;
struct crypto_shash *tfm;
};
-static inline void rxe_counter_inc(struct rxe_dev *rxe, enum rxe_counters cnt)
+static inline void rxe_counter_inc(struct rxe_dev *rxe, enum rxe_counters index)
{
- rxe->stats_counters[cnt]++;
+ atomic64_inc(&rxe->stats_counters[index]);
}
static inline struct rxe_dev *to_rdev(struct ib_device *dev)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 78dd36d..d8cb5bb 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -243,7 +243,8 @@ static int ipoib_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
- if (new_mtu > IPOIB_UD_MTU(priv->max_ib_mtu))
+ if (new_mtu < (ETH_MIN_MTU + IPOIB_ENCAP_LEN) ||
+ new_mtu > IPOIB_UD_MTU(priv->max_ib_mtu))
return -EINVAL;
priv->admin_mtu = new_mtu;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 3fecd87..b4e0ae0 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -646,6 +646,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
if (ib_conn->pi_support) {
u32 sig_caps = ib_conn->device->ib_device->attrs.sig_prot_cap;
+ shost->sg_prot_tablesize = shost->sg_tablesize;
scsi_host_set_prot(shost, iser_dif_prot_caps(sig_caps));
scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP |
SHOST_DIX_GUARD_CRC);
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 2f63885..96af06c 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -589,13 +589,19 @@ void iser_login_rsp(struct ib_cq *cq, struct ib_wc *wc)
ib_conn->post_recv_buf_count--;
}
-static inline void
+static inline int
iser_inv_desc(struct iser_fr_desc *desc, u32 rkey)
{
- if (likely(rkey == desc->rsc.mr->rkey))
+ if (likely(rkey == desc->rsc.mr->rkey)) {
desc->rsc.mr_valid = 0;
- else if (likely(rkey == desc->pi_ctx->sig_mr->rkey))
+ } else if (likely(desc->pi_ctx && rkey == desc->pi_ctx->sig_mr->rkey)) {
desc->pi_ctx->sig_mr_valid = 0;
+ } else {
+ iser_err("Bogus remote invalidation for rkey %#x\n", rkey);
+ return -EINVAL;
+ }
+
+ return 0;
}
static int
@@ -623,12 +629,14 @@ iser_check_remote_inv(struct iser_conn *iser_conn,
if (iser_task->dir[ISER_DIR_IN]) {
desc = iser_task->rdma_reg[ISER_DIR_IN].mem_h;
- iser_inv_desc(desc, rkey);
+ if (unlikely(iser_inv_desc(desc, rkey)))
+ return -EINVAL;
}
if (iser_task->dir[ISER_DIR_OUT]) {
desc = iser_task->rdma_reg[ISER_DIR_OUT].mem_h;
- iser_inv_desc(desc, rkey);
+ if (unlikely(iser_inv_desc(desc, rkey)))
+ return -EINVAL;
}
} else {
iser_err("failed to get task for itt=%d\n", hdr->itt);
diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c
index 267da82..31cd361 100644
--- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c
+++ b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c
@@ -351,7 +351,8 @@ static uint32_t opa_vnic_get_dlid(struct opa_vnic_adapter *adapter,
if (unlikely(!dlid))
v_warn("Null dlid in MAC address\n");
} else if (def_port != OPA_VNIC_INVALID_PORT) {
- dlid = info->vesw.u_ucast_dlid[def_port];
+ if (def_port < OPA_VESW_MAX_NUM_DEF_PORT)
+ dlid = info->vesw.u_ucast_dlid[def_port];
}
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index bc6a44a..03ee53a 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -2357,6 +2357,7 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
if (srp_post_send(ch, iu, len)) {
shost_printk(KERN_ERR, target->scsi_host, PFX "Send failed\n");
+ scmnd->result = DID_ERROR << 16;
goto err_unmap;
}
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index 2743ed46..1cd23bf 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -501,6 +501,15 @@ static void ml_ff_destroy(struct ff_device *ff)
{
struct ml_device *ml = ff->private;
+ /*
+ * Even though we stop all playing effects when tearing down
+ * an input device (via input_device_flush() that calls into
+ * input_ff_flush() that stops and erases all effects), we
+ * do not actually stop the timer, and therefore we should
+ * do it here.
+ */
+ del_timer_sync(&ml->timer);
+
kfree(ml->private);
}
diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c
index 28b473f..092096e 100644
--- a/drivers/input/joystick/psxpad-spi.c
+++ b/drivers/input/joystick/psxpad-spi.c
@@ -292,7 +292,7 @@ static int psxpad_spi_probe(struct spi_device *spi)
if (!pad)
return -ENOMEM;
- pdev = input_allocate_polled_device();
+ pdev = devm_input_allocate_polled_device(&spi->dev);
if (!pdev) {
dev_err(&spi->dev, "failed to allocate input device\n");
return -ENOMEM;
diff --git a/drivers/input/misc/qti-haptics.c b/drivers/input/misc/qti-haptics.c
index 81606e1..6b89a10 100644
--- a/drivers/input/misc/qti-haptics.c
+++ b/drivers/input/misc/qti-haptics.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
@@ -956,6 +956,8 @@ static int qti_haptics_playback(struct input_dev *dev, int effect_id, int val)
dev_dbg(chip->dev, "playback, val = %d\n", val);
if (!!val) {
+ pr_debug("Vibration - on at %lu us\n",
+ (unsigned long)ktime_to_us(ktime_get()));
rc = qti_haptics_module_en(chip, true);
if (rc < 0)
return rc;
@@ -985,6 +987,8 @@ static int qti_haptics_playback(struct input_dev *dev, int effect_id, int val)
}
}
} else {
+ pr_debug("Vibration - off at %lu us\n",
+ (unsigned long)ktime_to_us(ktime_get()));
play->length_us = 0;
rc = qti_haptics_play(chip, false);
if (rc < 0)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 06cebde..e8d1134 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -175,6 +175,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN0071", /* T480 */
"LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
"LEN0073", /* X1 Carbon G5 (Elantech) */
+ "LEN0091", /* X1 Carbon 6 */
"LEN0092", /* X1 Carbon 6 */
"LEN0093", /* T480 */
"LEN0096", /* X280 */
diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
index 93901eb..c8e07ea 100644
--- a/drivers/input/rmi4/rmi_f11.c
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -1287,8 +1287,8 @@ static irqreturn_t rmi_f11_attention(int irq, void *ctx)
valid_bytes = f11->sensor.attn_size;
memcpy(f11->sensor.data_pkt, drvdata->attn_data.data,
valid_bytes);
- drvdata->attn_data.data += f11->sensor.attn_size;
- drvdata->attn_data.size -= f11->sensor.attn_size;
+ drvdata->attn_data.data += valid_bytes;
+ drvdata->attn_data.size -= valid_bytes;
} else {
error = rmi_read_block(rmi_dev,
data_base_addr, f11->sensor.data_pkt,
diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c
index 5c7f489..9066f2b 100644
--- a/drivers/input/rmi4/rmi_f12.c
+++ b/drivers/input/rmi4/rmi_f12.c
@@ -58,6 +58,9 @@ struct f12_data {
const struct rmi_register_desc_item *data15;
u16 data15_offset;
+
+ unsigned long *abs_mask;
+ unsigned long *rel_mask;
};
static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
@@ -214,8 +217,8 @@ static irqreturn_t rmi_f12_attention(int irq, void *ctx)
valid_bytes = sensor->attn_size;
memcpy(sensor->data_pkt, drvdata->attn_data.data,
valid_bytes);
- drvdata->attn_data.data += sensor->attn_size;
- drvdata->attn_data.size -= sensor->attn_size;
+ drvdata->attn_data.data += valid_bytes;
+ drvdata->attn_data.size -= valid_bytes;
} else {
retval = rmi_read_block(rmi_dev, f12->data_addr,
sensor->data_pkt, sensor->pkt_size);
@@ -296,9 +299,18 @@ static int rmi_f12_write_control_regs(struct rmi_function *fn)
static int rmi_f12_config(struct rmi_function *fn)
{
struct rmi_driver *drv = fn->rmi_dev->driver;
+ struct f12_data *f12 = dev_get_drvdata(&fn->dev);
+ struct rmi_2d_sensor *sensor;
int ret;
- drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
+ sensor = &f12->sensor;
+
+ if (!sensor->report_abs)
+ drv->clear_irq_bits(fn->rmi_dev, f12->abs_mask);
+ else
+ drv->set_irq_bits(fn->rmi_dev, f12->abs_mask);
+
+ drv->clear_irq_bits(fn->rmi_dev, f12->rel_mask);
ret = rmi_f12_write_control_regs(fn);
if (ret)
@@ -320,9 +332,12 @@ static int rmi_f12_probe(struct rmi_function *fn)
struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
u16 data_offset = 0;
+ int mask_size;
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__);
+ mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long);
+
ret = rmi_read(fn->rmi_dev, query_addr, &buf);
if (ret < 0) {
dev_err(&fn->dev, "Failed to read general info register: %d\n",
@@ -337,10 +352,19 @@ static int rmi_f12_probe(struct rmi_function *fn)
return -ENODEV;
}
- f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data), GFP_KERNEL);
+ f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data) + mask_size * 2,
+ GFP_KERNEL);
if (!f12)
return -ENOMEM;
+ f12->abs_mask = (unsigned long *)((char *)f12
+ + sizeof(struct f12_data));
+ f12->rel_mask = (unsigned long *)((char *)f12
+ + sizeof(struct f12_data) + mask_size);
+
+ set_bit(fn->irq_pos, f12->abs_mask);
+ set_bit(fn->irq_pos + 1, f12->rel_mask);
+
f12->has_dribble = !!(buf & BIT(3));
if (fn->dev.of_node) {
diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c
index 3991d29..099dde6 100644
--- a/drivers/input/rmi4/rmi_f34v7.c
+++ b/drivers/input/rmi4/rmi_f34v7.c
@@ -1192,6 +1192,9 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
{
int ret;
+ f34->fn->rmi_dev->driver->set_irq_bits(f34->fn->rmi_dev,
+ f34->fn->irq_mask);
+
rmi_f34v7_read_queries_bl_version(f34);
f34->v7.image = fw->data;
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
index a6f515b..539a474 100644
--- a/drivers/input/rmi4/rmi_f54.c
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -362,7 +362,7 @@ static const struct vb2_ops rmi_f54_queue_ops = {
static const struct vb2_queue rmi_f54_queue = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
- .buf_struct_size = sizeof(struct vb2_buffer),
+ .buf_struct_size = sizeof(struct vb2_v4l2_buffer),
.ops = &rmi_f54_queue_ops,
.mem_ops = &vb2_vmalloc_memops,
.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
@@ -614,7 +614,7 @@ static int rmi_f54_config(struct rmi_function *fn)
{
struct rmi_driver *drv = fn->rmi_dev->driver;
- drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
+ drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask);
return 0;
}
@@ -742,6 +742,7 @@ static void rmi_f54_remove(struct rmi_function *fn)
video_unregister_device(&f54->vdev);
v4l2_device_unregister(&f54->v4l2);
+ destroy_workqueue(f54->workqueue);
}
struct rmi_function_handler rmi_f54_handler = {
diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c
index b6ccf39c..4b2466c 100644
--- a/drivers/input/rmi4/rmi_smbus.c
+++ b/drivers/input/rmi4/rmi_smbus.c
@@ -166,7 +166,6 @@ static int rmi_smb_write_block(struct rmi_transport_dev *xport, u16 rmiaddr,
/* prepare to write next block of bytes */
cur_len -= SMB_MAX_COUNT;
databuff += SMB_MAX_COUNT;
- rmiaddr += SMB_MAX_COUNT;
}
exit:
mutex_unlock(&rmi_smb->page_mutex);
@@ -218,7 +217,6 @@ static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr,
/* prepare to read next block of bytes */
cur_len -= SMB_MAX_COUNT;
databuff += SMB_MAX_COUNT;
- rmiaddr += SMB_MAX_COUNT;
}
retval = 0;
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 49d8d53..96f9b53 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -381,9 +381,9 @@ static int __init gscps2_probe(struct parisc_device *dev)
goto fail;
#endif
- printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n",
+ pr_info("serio: %s port at 0x%08lx irq %d @ %s\n",
ps2port->port->name,
- ps2port->addr,
+ hpa,
ps2port->padev->irq,
ps2port->port->phys);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 0b8a25c..6542523 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -884,8 +884,8 @@ static int __init hp_sdc_init(void)
"HP SDC NMI", &hp_sdc))
goto err2;
- printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n",
- (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+ pr_info(PREFIX "HP SDC at 0x%08lx, IRQ %d (NMI IRQ %d)\n",
+ hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
hp_sdc_status_in8();
hp_sdc_data_in8();
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 85bb21b..d8fd886 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1351,4 +1351,6 @@
source "drivers/input/touchscreen/synaptics_tcm/Kconfig"
+source "drivers/input/touchscreen/focaltech_touch/Kconfig"
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 8a2d92a..d4757cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -112,3 +112,4 @@
obj-$(CONFIG_TOUCHSCREEN_ST) += st/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM) += synaptics_tcm/
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index a7ace07..e8f98de 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -3162,6 +3162,8 @@ static int __maybe_unused mxt_suspend(struct device *dev)
mutex_unlock(&input_dev->mutex);
+ disable_irq(data->irq);
+
return 0;
}
@@ -3174,6 +3176,8 @@ static int __maybe_unused mxt_resume(struct device *dev)
if (!input_dev)
return 0;
+ enable_irq(data->irq);
+
mutex_lock(&input_dev->mutex);
if (input_dev->users)
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
index 727c323..c84ee73 100644
--- a/drivers/input/touchscreen/cyttsp4_core.c
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -2000,11 +2000,6 @@ static int cyttsp4_mt_probe(struct cyttsp4 *cd)
/* get sysinfo */
md->si = &cd->sysinfo;
- if (!md->si) {
- dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
- __func__, md->si);
- goto error_get_sysinfo;
- }
rc = cyttsp4_setup_input_device(cd);
if (rc)
@@ -2014,8 +2009,6 @@ static int cyttsp4_mt_probe(struct cyttsp4 *cd)
error_init_input:
input_free_device(md->input);
-error_get_sysinfo:
- input_set_drvdata(md->input, NULL);
error_alloc_failed:
dev_err(dev, "%s failed.\n", __func__);
return rc;
diff --git a/drivers/input/touchscreen/focaltech_touch/Kconfig b/drivers/input/touchscreen/focaltech_touch/Kconfig
new file mode 100644
index 0000000..aeb312b
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Kconfig
@@ -0,0 +1,14 @@
+#
+# Focaltech Touchscreen driver configuration
+#
+
+config TOUCHSCREEN_FTS
+ bool "Focaltech Touchscreen"
+ help
+ Say Y here if you have Focaltech touch panel.
+ If unsure, say N.
+
+config TOUCHSCREEN_FTS_DIRECTORY
+ string "Focaltech ts directory name"
+ default "focaltech_touch"
+ depends on TOUCHSCREEN_FTS
diff --git a/drivers/input/touchscreen/focaltech_touch/Makefile b/drivers/input/touchscreen/focaltech_touch/Makefile
new file mode 100644
index 0000000..d77fef8
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Makefile
@@ -0,0 +1,13 @@
+# Makefile for the focaltech touchscreen drivers.
+
+
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o
+
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash/
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
new file mode 100644
index 0000000..67415c4
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_common.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-16
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_COMMON_H__
+#define __LINUX_FOCALTECH_COMMON_H__
+
+#include "focaltech_config.h"
+
+/*****************************************************************************
+* Macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_VERSION "Focaltech V3.1 20190807"
+
+#define BYTE_OFF_0(x) (u8)((x) & 0xFF)
+#define BYTE_OFF_8(x) (u8)(((x) >> 8) & 0xFF)
+#define BYTE_OFF_16(x) (u8)(((x) >> 16) & 0xFF)
+#define BYTE_OFF_24(x) (u8)(((x) >> 24) & 0xFF)
+#define FLAGBIT(x) (0x00000001 << (x))
+#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x)))
+
+#define FLAG_ICSERIALS_LEN 8
+#define FLAG_HID_BIT 10
+#define FLAG_IDC_BIT 11
+
+#define IC_SERIALS (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT))
+#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT))
+
+#define FTS_CHIP_TYPE_MAPPING {{0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}}
+
+#define FILE_NAME_LENGTH 128
+#define ENABLE 1
+#define DISABLE 0
+#define VALID 1
+#define INVALID 0
+#define FTS_CMD_START1 0x55
+#define FTS_CMD_START2 0xAA
+#define FTS_CMD_START_DELAY 12
+#define FTS_CMD_READ_ID 0x90
+#define FTS_CMD_READ_ID_LEN 4
+#define FTS_CMD_READ_ID_LEN_INCELL 1
+/*register address*/
+#define FTS_REG_INT_CNT 0x8F
+#define FTS_REG_FLOW_WORK_CNT 0x91
+#define FTS_REG_WORKMODE 0x00
+#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40
+#define FTS_REG_WORKMODE_WORK_VALUE 0x00
+#define FTS_REG_ESDCHECK_DISABLE 0x8D
+#define FTS_REG_CHIP_ID 0xA3
+#define FTS_REG_CHIP_ID2 0x9F
+#define FTS_REG_POWER_MODE 0xA5
+#define FTS_REG_POWER_MODE_SLEEP 0x03
+#define FTS_REG_FW_VER 0xA6
+#define FTS_REG_VENDOR_ID 0xA8
+#define FTS_REG_LCD_BUSY_NUM 0xAB
+#define FTS_REG_FACE_DEC_MODE_EN 0xB0
+#define FTS_REG_FACTORY_MODE_DETACH_FLAG 0xB4
+#define FTS_REG_FACE_DEC_MODE_STATUS 0x01
+#define FTS_REG_IDE_PARA_VER_ID 0xB5
+#define FTS_REG_IDE_PARA_STATUS 0xB6
+#define FTS_REG_GLOVE_MODE_EN 0xC0
+#define FTS_REG_COVER_MODE_EN 0xC1
+#define FTS_REG_CHARGER_MODE_EN 0x8B
+#define FTS_REG_GESTURE_EN 0xD0
+#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3
+#define FTS_REG_MODULE_ID 0xE3
+#define FTS_REG_LIC_VER 0xE4
+#define FTS_REG_ESD_SATURATE 0xED
+
+#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1')
+#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0')
+
+#define kfree_safe(pbuf) do {\
+ if (pbuf) {\
+ kfree(pbuf);\
+ pbuf = NULL;\
+ }\
+} while(0)
+
+/*****************************************************************************
+* Alternative mode (When something goes wrong,
+* the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * point report check
+ * default: disable
+ */
+#define FTS_POINT_REPORT_CHECK_EN 0
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct ft_chip_t {
+ u64 type;
+ u8 chip_idh;
+ u8 chip_idl;
+ u8 rom_idh;
+ u8 rom_idl;
+ u8 pb_idh;
+ u8 pb_idl;
+ u8 bl_idh;
+ u8 bl_idl;
+};
+
+struct ts_ic_info {
+ bool is_incell;
+ bool hid_supported;
+ struct ft_chip_t ids;
+};
+
+/*****************************************************************************
+* DEBUG function define here
+*****************************************************************************/
+#if FTS_DEBUG_EN
+#define FTS_DEBUG(fmt, args...) do { \
+ printk("[FTS_TS]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_FUNC_ENTER() do { \
+ printk("[FTS_TS]%s: Enter\n", __func__); \
+} while (0)
+
+#define FTS_FUNC_EXIT() do { \
+ printk("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \
+} while (0)
+#else /* #if FTS_DEBUG_EN*/
+#define FTS_DEBUG(fmt, args...)
+#define FTS_FUNC_ENTER()
+#define FTS_FUNC_EXIT()
+#endif
+
+#define FTS_INFO(fmt, args...) do { \
+ printk(KERN_INFO "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_ERROR(fmt, args...) do { \
+ printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+#endif /* __LINUX_FOCALTECH_COMMON_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
new file mode 100644
index 0000000..d98222c
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
@@ -0,0 +1,242 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_config.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract: global configurations
+*
+* Version: v1.0
+*
+************************************************************************/
+#ifndef _LINUX_FOCLATECH_CONFIG_H_
+#define _LINUX_FOCLATECH_CONFIG_H_
+
+/**************************************************/
+/****** G: A, I: B, S: C, U: D ******************/
+/****** chip type defines, do not modify *********/
+#define _FT8716 0x87160805
+#define _FT8736 0x87360806
+#define _FT8006M 0x80060807
+#define _FT8607 0x86070809
+#define _FT8006U 0x8006D80B
+#define _FT8006S 0x8006A80B
+#define _FT8613 0x8613080C
+#define _FT8719 0x8719080D
+#define _FT8739 0x8739080E
+#define _FT8615 0x8615080F
+#define _FT8201 0x82010810
+#define _FT8006P 0x86220811
+#define _FT7251 0x72510812
+#define _FT7252 0x72520813
+#define _FT8613S 0x8613C814
+#define _FT8756 0x87560815
+#define _FT8302 0x83020816
+#define _FT8009 0x80090817
+#define _FT8656 0x86560818
+#define _FT8006S_AA 0x86320819
+#define _FT7250 0x7250081A
+
+
+#define _FT5416 0x54160402
+#define _FT5426 0x54260402
+#define _FT5435 0x54350402
+#define _FT5436 0x54360402
+#define _FT5526 0x55260402
+#define _FT5526I 0x5526B402
+#define _FT5446 0x54460402
+#define _FT5346 0x53460402
+#define _FT5446I 0x5446B402
+#define _FT5346I 0x5346B402
+#define _FT7661 0x76610402
+#define _FT7511 0x75110402
+#define _FT7421 0x74210402
+#define _FT7681 0x76810402
+#define _FT3C47U 0x3C47D402
+#define _FT3417 0x34170402
+#define _FT3517 0x35170402
+#define _FT3327 0x33270402
+#define _FT3427 0x34270402
+#define _FT7311 0x73110402
+
+#define _FT5626 0x56260401
+#define _FT5726 0x57260401
+#define _FT5826B 0x5826B401
+#define _FT5826S 0x5826C401
+#define _FT7811 0x78110401
+#define _FT3D47 0x3D470401
+#define _FT3617 0x36170401
+#define _FT3717 0x37170401
+#define _FT3817B 0x3817B401
+#define _FT3517U 0x3517D401
+
+#define _FT6236U 0x6236D003
+#define _FT6336G 0x6336A003
+#define _FT6336U 0x6336D003
+#define _FT6436U 0x6436D003
+
+#define _FT3267 0x32670004
+#define _FT3367 0x33670004
+
+#define _FT3327DQQ_XXX 0x3327D482
+#define _FT5446DQS_XXX 0x5446D482
+
+#define _FT3518 0x35180481
+#define _FT3558 0x35580481
+#define _FT3528 0x35280481
+#define _FT5536 0x55360481
+
+#define _FT5446U 0x5446D083
+#define _FT5456U 0x5456D083
+#define _FT3417U 0x3417D083
+#define _FT5426U 0x5426D083
+#define _FT3428 0x34280083
+#define _FT3437U 0x3437D083
+
+#define _FT7302 0x73020084
+#define _FT7202 0x72020084
+#define _FT3308 0x33080084
+
+#define _FT6346U 0x6346D085
+#define _FT6346G 0x6346A085
+#define _FT3067 0x30670085
+#define _FT3068 0x30680085
+#define _FT3168 0x31680085
+#define _FT3268 0x32680085
+
+/*************************************************/
+
+/*
+ * choose your ic chip type of focaltech
+ */
+#define FTS_CHIP_TYPE _FT3518
+
+/******************* Enables *********************/
+/*********** 1 to enable, 0 to disable ***********/
+
+/*
+ * show debug log info
+ * enable it for debug, disable it for release
+ */
+#define FTS_DEBUG_EN 0
+
+/*
+ * Linux MultiTouch Protocol
+ * 1: Protocol B(default), 0: Protocol A
+ */
+#define FTS_MT_PROTOCOL_B_EN 1
+
+/*
+ * Report Pressure in multitouch
+ * 1:enable(default),0:disable
+*/
+#define FTS_REPORT_PRESSURE_EN 1
+
+/*
+ * Gesture function enable
+ * default: disable
+ */
+#define FTS_GESTURE_EN 0
+
+/*
+ * ESD check & protection
+ * default: disable
+ */
+#define FTS_ESDCHECK_EN 0
+
+
+/*
+ * Pinctrl enable
+ * default: disable
+ */
+#define FTS_PINCTRL_EN 1
+
+/*
+ * Customer power enable
+ * enable it when customer need control TP power
+ * default: disable
+ */
+#define FTS_POWER_SOURCE_CUST_EN 1
+
+/****************************************************/
+
+/********************** Upgrade ****************************/
+/*
+ * auto upgrade
+ */
+#define FTS_AUTO_UPGRADE_EN 1
+
+/*
+ * auto upgrade for lcd cfg
+ */
+#define FTS_AUTO_LIC_UPGRADE_EN 0
+
+/*
+ * Numbers of modules support
+ */
+#define FTS_GET_MODULE_NUM 0
+
+/*
+ * module_id: mean vendor_id generally, also maybe gpio or lcm_id...
+ * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID
+ * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them
+ * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID
+ * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID
+ */
+#define FTS_MODULE_ID 0x0000
+#define FTS_MODULE2_ID 0x0000
+#define FTS_MODULE3_ID 0x0000
+
+/*
+ * Need set the following when get firmware via firmware_request()
+ * For example: if module'vendor is tianma,
+ * #define FTS_MODULE_NAME "tianma"
+ * then file_name will be "focaltech_ts_fw_tianma"
+ * You should rename fw to "focaltech_ts_fw_tianma", and push it into
+ * etc/firmware or by customers
+ */
+#define FTS_MODULE_NAME "gvo"
+#define FTS_MODULE2_NAME ""
+#define FTS_MODULE3_NAME ""
+
+/*
+ * FW.i file for auto upgrade, you must replace it with your own
+ * define your own fw_file, the sample one to be replaced is invalid
+ * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID
+ */
+#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW2_FILE "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW3_FILE "include/firmware/fw_sample.i"
+
+/*********************************************************/
+
+#endif /* _LINUX_FOCLATECH_CONFIG_H_ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
new file mode 100644
index 0000000..e7d6a0f
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
@@ -0,0 +1,1849 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract: entrance for focaltech ts driver
+*
+* Version: V1.0
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#if defined(CONFIG_DRM)
+#include <drm/drm_panel.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */
+#endif
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_NAME "fts_ts"
+#define INTERVAL_READ_REG 200 /* unit:ms */
+#define TIMEOUT_READ_REG 1000 /* unit:ms */
+#if FTS_POWER_SOURCE_CUST_EN
+#define FTS_VTG_MIN_UV 2800000
+#define FTS_VTG_MAX_UV 3300000
+#define FTS_I2C_VTG_MIN_UV 1800000
+#define FTS_I2C_VTG_MAX_UV 1800000
+#endif
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct fts_ts_data *fts_data;
+
+#if defined(CONFIG_DRM)
+static struct drm_panel *active_panel;
+#endif
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static int fts_ts_suspend(struct device *dev);
+static int fts_ts_resume(struct device *dev);
+
+/*****************************************************************************
+* Name: fts_wait_tp_to_valid
+* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG),
+* need call when reset/power on/resume...
+* Input:
+* Output:
+* Return: return 0 if tp valid, otherwise return error code
+*****************************************************************************/
+int fts_wait_tp_to_valid(void)
+{
+ int ret = 0;
+ int cnt = 0;
+ u8 idh = 0;
+ u8 idl = 0;
+ u8 chip_idh = fts_data->ic_info.ids.chip_idh;
+ u8 chip_idl = fts_data->ic_info.ids.chip_idl;
+
+ do {
+ ret = fts_read_reg(FTS_REG_CHIP_ID, &idh);
+ ret = fts_read_reg(FTS_REG_CHIP_ID2, &idl);
+ if ((ret < 0) || (idh != chip_idh) || (idl != chip_idl)) {
+ FTS_DEBUG("TP Not Ready,ReadData:0x%02x%02x", idh, idl);
+ } else if ((idh == chip_idh) && (idl == chip_idl)) {
+ FTS_INFO("TP Ready,Device ID:0x%02x%02x", idh, idl);
+ return 0;
+ }
+ cnt++;
+ msleep(INTERVAL_READ_REG);
+ } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+ return -EIO;
+}
+
+/*****************************************************************************
+* Name: fts_tp_state_recovery
+* Brief: Need execute this function when reset
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+void fts_tp_state_recovery(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+ /* wait tp stable */
+ fts_wait_tp_to_valid();
+ /* recover TP charger state 0x8B */
+ /* recover TP glove state 0xC0 */
+ /* recover TP cover state 0xC1 */
+ fts_ex_mode_recovery(ts_data);
+ /* recover TP gesture state 0xD0 */
+ fts_gesture_recovery(ts_data);
+ FTS_FUNC_EXIT();
+}
+
+int fts_reset_proc(int hdelayms)
+{
+ FTS_DEBUG("tp reset");
+ gpio_direction_output(fts_data->pdata->reset_gpio, 0);
+ msleep(1);
+ gpio_direction_output(fts_data->pdata->reset_gpio, 1);
+ if (hdelayms) {
+ msleep(hdelayms);
+ }
+
+ return 0;
+}
+
+void fts_irq_disable(void)
+{
+ unsigned long irqflags;
+
+ FTS_FUNC_ENTER();
+ spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+ if (!fts_data->irq_disabled) {
+ disable_irq_nosync(fts_data->irq);
+ fts_data->irq_disabled = true;
+ }
+
+ spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+ FTS_FUNC_EXIT();
+}
+
+void fts_irq_enable(void)
+{
+ unsigned long irqflags = 0;
+
+ FTS_FUNC_ENTER();
+ spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+ if (fts_data->irq_disabled) {
+ enable_irq(fts_data->irq);
+ fts_data->irq_disabled = false;
+ }
+
+ spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+ FTS_FUNC_EXIT();
+}
+
+void fts_hid2std(void)
+{
+ int ret = 0;
+ u8 buf[3] = {0xEB, 0xAA, 0x09};
+
+ ret = fts_write(buf, 3);
+ if (ret < 0) {
+ FTS_ERROR("hid2std cmd write fail");
+ return;
+ }
+
+ msleep(10);
+ buf[0] = buf[1] = buf[2] = 0;
+ ret = fts_read(NULL, 0, buf, 3);
+ if (ret < 0) {
+ FTS_ERROR("hid2std cmd read fail");
+ } else if ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) {
+ FTS_DEBUG("hidi2c change to stdi2c successful");
+ } else {
+ FTS_DEBUG("hidi2c change to stdi2c not support or fail");
+ }
+
+}
+
+static int fts_get_chip_types(
+ struct fts_ts_data *ts_data,
+ u8 id_h, u8 id_l, bool fw_valid)
+{
+ int i = 0;
+ struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING;
+ u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t);
+
+ if ((0x0 == id_h) || (0x0 == id_l)) {
+ FTS_ERROR("id_h/id_l is 0");
+ return -EINVAL;
+ }
+
+ FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l);
+ for (i = 0; i < ctype_entries; i++) {
+ if (VALID == fw_valid) {
+ if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl))
+ break;
+ } else {
+ if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl))
+ || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl))
+ || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl)))
+ break;
+ }
+ }
+
+ if (i >= ctype_entries)
+ return -ENODATA;
+
+ ts_data->ic_info.ids = ctype[i];
+ return 0;
+}
+
+static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id)
+{
+ int ret = 0;
+ u8 chip_id[2] = { 0 };
+ u8 id_cmd[4] = { 0 };
+ u32 id_cmd_len = 0;
+
+ id_cmd[0] = FTS_CMD_START1;
+ id_cmd[1] = FTS_CMD_START2;
+ ret = fts_write(id_cmd, 2);
+ if (ret < 0) {
+ FTS_ERROR("start cmd write fail");
+ return ret;
+ }
+
+ msleep(FTS_CMD_START_DELAY);
+ id_cmd[0] = FTS_CMD_READ_ID;
+ id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00;
+ if (ts_data->ic_info.is_incell)
+ id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+ else
+ id_cmd_len = FTS_CMD_READ_ID_LEN;
+ ret = fts_read(id_cmd, id_cmd_len, chip_id, 2);
+ if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+ FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]);
+ return -EIO;
+ }
+
+ id[0] = chip_id[0];
+ id[1] = chip_id[1];
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_get_ic_information
+* Brief: read chip id to get ic information, after run the function, driver w-
+* ill know which IC is it.
+* If cant get the ic information, maybe not focaltech's touch IC, need
+* unregister the driver
+* Input:
+* Output:
+* Return: return 0 if get correct ic information, otherwise return error code
+*****************************************************************************/
+static int fts_get_ic_information(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ int cnt = 0;
+ u8 chip_id[2] = { 0 };
+
+ ts_data->ic_info.is_incell = FTS_CHIP_IDC;
+ ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED;
+
+ do {
+ ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]);
+ ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]);
+ if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+ FTS_DEBUG("i2c read invalid, read:0x%02x%02x",
+ chip_id[0], chip_id[1]);
+ } else {
+ ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID);
+ if (!ret)
+ break;
+ else
+ FTS_DEBUG("TP not ready, read:0x%02x%02x",
+ chip_id[0], chip_id[1]);
+ }
+
+ cnt++;
+ msleep(INTERVAL_READ_REG);
+ } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+ if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) {
+ FTS_INFO("fw is invalid, need read boot id");
+ if (ts_data->ic_info.hid_supported) {
+ fts_hid2std();
+ }
+
+ ret = fts_read_bootid(ts_data, &chip_id[0]);
+ if (ret < 0) {
+ FTS_ERROR("read boot id fail");
+ return ret;
+ }
+
+ ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID);
+ if (ret < 0) {
+ FTS_ERROR("can't get ic informaton");
+ return ret;
+ }
+ }
+
+ FTS_INFO("get ic information, chip id = 0x%02x%02x",
+ ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
+
+ return 0;
+}
+
+/*****************************************************************************
+* Reprot related
+*****************************************************************************/
+static void fts_show_touch_buffer(u8 *data, int datalen)
+{
+ int i = 0;
+ int count = 0;
+ char *tmpbuf = NULL;
+
+ tmpbuf = kzalloc(1024, GFP_KERNEL);
+ if (!tmpbuf) {
+ FTS_ERROR("tmpbuf zalloc fail");
+ return;
+ }
+
+ for (i = 0; i < datalen; i++) {
+ count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]);
+ if (count >= 1024)
+ break;
+ }
+ FTS_DEBUG("point buffer:%s", tmpbuf);
+
+ if (tmpbuf) {
+ kfree(tmpbuf);
+ tmpbuf = NULL;
+ }
+}
+
+void fts_release_all_finger(void)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+#if FTS_MT_PROTOCOL_B_EN
+ u32 finger_count = 0;
+ u32 max_touches = fts_data->pdata->max_touch_number;
+#endif
+
+ FTS_FUNC_ENTER();
+ mutex_lock(&fts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+ for (finger_count = 0; finger_count < max_touches; finger_count++) {
+ input_mt_slot(input_dev, finger_count);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+ }
+#else
+ input_mt_sync(input_dev);
+#endif
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+
+ fts_data->touchs = 0;
+ fts_data->key_state = 0;
+ mutex_unlock(&fts_data->report_mutex);
+ FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+* Name: fts_input_report_key
+* Brief: process key events,need report key-event if key enable.
+* if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50),
+* need report it to key event.
+* x_dim: parse from dts, means key x_coordinate, dimension:+-50
+* y_dim: parse from dts, means key y_coordinate, dimension:+-50
+* Input:
+* Output:
+* Return: return 0 if it's key event, otherwise return error code
+*****************************************************************************/
+static int fts_input_report_key(struct fts_ts_data *data, int index)
+{
+ int i = 0;
+ int x = data->events[index].x;
+ int y = data->events[index].y;
+ int *x_dim = &data->pdata->key_x_coords[0];
+ int *y_dim = &data->pdata->key_y_coords[0];
+
+ if (!data->pdata->have_key) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data->pdata->key_number; i++) {
+ if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) &&
+ (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) {
+ if (EVENT_DOWN(data->events[index].flag)
+ && !(data->key_state & (1 << i))) {
+ input_report_key(data->input_dev, data->pdata->keys[i], 1);
+ data->key_state |= (1 << i);
+ FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y);
+ } else if (EVENT_UP(data->events[index].flag)
+ && (data->key_state & (1 << i))) {
+ input_report_key(data->input_dev, data->pdata->keys[i], 0);
+ data->key_state &= ~(1 << i);
+ FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y);
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+#if FTS_MT_PROTOCOL_B_EN
+static int fts_input_report_b(struct fts_ts_data *data)
+{
+ int i = 0;
+ int uppoint = 0;
+ int touchs = 0;
+ bool va_reported = false;
+ u32 max_touch_num = data->pdata->max_touch_number;
+ struct ts_event *events = data->events;
+
+ for (i = 0; i < data->touch_point; i++) {
+ if (fts_input_report_key(data, i) == 0)
+ continue;
+
+ va_reported = true;
+ input_mt_slot(data->input_dev, events[i].id);
+
+ if (EVENT_DOWN(events[i].flag)) {
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
+
+#if FTS_REPORT_PRESSURE_EN
+ if (events[i].p <= 0) {
+ events[i].p = 0x3f;
+ }
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+ if (events[i].area <= 0) {
+ events[i].area = 0x09;
+ }
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
+
+ touchs |= BIT(events[i].id);
+ data->touchs |= BIT(events[i].id);
+
+ if ((data->log_level >= 2) ||
+ ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+ FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+ events[i].id,
+ events[i].x, events[i].y,
+ events[i].p, events[i].area);
+ }
+ } else {
+ uppoint++;
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+ data->touchs &= ~BIT(events[i].id);
+ if (data->log_level >= 1) {
+ FTS_DEBUG("[B]P%d UP!", events[i].id);
+ }
+ }
+ }
+
+ if (unlikely(data->touchs ^ touchs)) {
+ for (i = 0; i < max_touch_num; i++) {
+ if (BIT(i) & (data->touchs ^ touchs)) {
+ if (data->log_level >= 1) {
+ FTS_DEBUG("[B]P%d UP!", i);
+ }
+ va_reported = true;
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+ }
+ }
+ }
+ data->touchs = touchs;
+
+ if (va_reported) {
+ /* touchs==0, there's no point but key */
+ if (EVENT_NO_DOWN(data) || (!touchs)) {
+ if (data->log_level >= 1) {
+ FTS_DEBUG("[B]Points All Up!");
+ }
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ } else {
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ }
+ }
+
+ input_sync(data->input_dev);
+ return 0;
+}
+
+#else
+static int fts_input_report_a(struct fts_ts_data *data)
+{
+ int i = 0;
+ int touchs = 0;
+ bool va_reported = false;
+ struct ts_event *events = data->events;
+
+ for (i = 0; i < data->touch_point; i++) {
+ if (fts_input_report_key(data, i) == 0) {
+ continue;
+ }
+
+ va_reported = true;
+ if (EVENT_DOWN(events[i].flag)) {
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id);
+#if FTS_REPORT_PRESSURE_EN
+ if (events[i].p <= 0) {
+ events[i].p = 0x3f;
+ }
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+ if (events[i].area <= 0) {
+ events[i].area = 0x09;
+ }
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
+
+ input_mt_sync(data->input_dev);
+
+ if ((data->log_level >= 2) ||
+ ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+ FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+ events[i].id,
+ events[i].x, events[i].y,
+ events[i].p, events[i].area);
+ }
+ touchs++;
+ }
+ }
+
+ /* last point down, current no point but key */
+ if (data->touchs && !touchs) {
+ va_reported = true;
+ }
+ data->touchs = touchs;
+
+ if (va_reported) {
+ if (EVENT_NO_DOWN(data)) {
+ if (data->log_level >= 1) {
+ FTS_DEBUG("[A]Points All Up!");
+ }
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_mt_sync(data->input_dev);
+ } else {
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ }
+ }
+
+ input_sync(data->input_dev);
+ return 0;
+}
+#endif
+
+static int fts_read_touchdata(struct fts_ts_data *data)
+{
+ int ret = 0;
+ u8 *buf = data->point_buf;
+
+ memset(buf, 0xFF, data->pnt_buf_size);
+ buf[0] = 0x01;
+
+ if (data->gesture_mode) {
+ if (0 == fts_gesture_readdata(data, NULL)) {
+ FTS_INFO("succuss to get gesture data in irq handler");
+ return 1;
+ }
+ }
+
+ ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1);
+ if (ret < 0) {
+ FTS_ERROR("read touchdata failed, ret:%d", ret);
+ return ret;
+ }
+
+ if (data->log_level >= 3) {
+ fts_show_touch_buffer(buf, data->pnt_buf_size);
+ }
+
+ return 0;
+}
+
+static int fts_read_parse_touchdata(struct fts_ts_data *data)
+{
+ int ret = 0;
+ int i = 0;
+ u8 pointid = 0;
+ int base = 0;
+ struct ts_event *events = data->events;
+ int max_touch_num = data->pdata->max_touch_number;
+ u8 *buf = data->point_buf;
+
+ ret = fts_read_touchdata(data);
+ if (ret) {
+ return ret;
+ }
+
+ data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+ data->touch_point = 0;
+
+ if (data->ic_info.is_incell) {
+ if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF)
+ && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) {
+ FTS_DEBUG("touch buff is 0xff, need recovery state");
+ fts_release_all_finger();
+ fts_tp_state_recovery(data);
+ return -EIO;
+ }
+ }
+
+ if (data->point_num > max_touch_num) {
+ FTS_INFO("invalid point_num(%d)", data->point_num);
+ return -EIO;
+ }
+
+ for (i = 0; i < max_touch_num; i++) {
+ base = FTS_ONE_TCH_LEN * i;
+ pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4;
+ if (pointid >= FTS_MAX_ID)
+ break;
+ else if (pointid >= max_touch_num) {
+ FTS_ERROR("ID(%d) beyond max_touch_number", pointid);
+ return -EINVAL;
+ }
+
+ data->touch_point++;
+ events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) +
+ (buf[FTS_TOUCH_X_L_POS + base] & 0xFF);
+ events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) +
+ (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF);
+ events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6;
+ events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4;
+ events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4;
+ events[i].p = buf[FTS_TOUCH_PRE_POS + base];
+
+ if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) {
+ FTS_INFO("abnormal touch data from fw");
+ return -EIO;
+ }
+ }
+
+ if (data->touch_point == 0) {
+ FTS_INFO("no touch point information");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void fts_irq_read_report(void)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_set_intr(1);
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+ fts_prc_queue_work(ts_data);
+#endif
+
+ ret = fts_read_parse_touchdata(ts_data);
+ if (ret == 0) {
+ mutex_lock(&ts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+ fts_input_report_b(ts_data);
+#else
+ fts_input_report_a(ts_data);
+#endif
+ mutex_unlock(&ts_data->report_mutex);
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_set_intr(0);
+#endif
+}
+
+static irqreturn_t fts_irq_handler(int irq, void *data)
+{
+ fts_irq_read_report();
+ return IRQ_HANDLED;
+}
+
+static int fts_irq_registration(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ struct fts_ts_platform_data *pdata = ts_data->pdata;
+
+ ts_data->irq = gpio_to_irq(pdata->irq_gpio);
+ pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags);
+ ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler,
+ pdata->irq_gpio_flags,
+ FTS_DRIVER_NAME, ts_data);
+
+ return ret;
+}
+
+static int fts_input_init(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ int key_num = 0;
+ struct fts_ts_platform_data *pdata = ts_data->pdata;
+ struct input_dev *input_dev;
+
+ FTS_FUNC_ENTER();
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ FTS_ERROR("Failed to allocate memory for input device");
+ return -ENOMEM;
+ }
+
+ /* Init and register Input device */
+ input_dev->name = FTS_DRIVER_NAME;
+
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = ts_data->dev;
+
+ input_set_drvdata(input_dev, ts_data);
+
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ if (pdata->have_key) {
+ FTS_INFO("set key capabilities");
+ for (key_num = 0; key_num < pdata->key_number; key_num++)
+ input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]);
+ }
+
+#if FTS_MT_PROTOCOL_B_EN
+ input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT);
+#else
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0);
+#endif
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
+#if FTS_REPORT_PRESSURE_EN
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
+#endif
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ FTS_ERROR("Input device registration failed");
+ input_set_drvdata(input_dev, NULL);
+ input_free_device(input_dev);
+ input_dev = NULL;
+ return ret;
+ }
+
+ ts_data->input_dev = input_dev;
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+static int fts_report_buffer_init(struct fts_ts_data *ts_data)
+{
+ int point_num = 0;
+ int events_num = 0;
+
+ point_num = ts_data->pdata->max_touch_number;
+ ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3;
+ ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size + 1, GFP_KERNEL);
+ if (!ts_data->point_buf) {
+ FTS_ERROR("failed to alloc memory for point buf");
+ return -ENOMEM;
+ }
+
+ events_num = point_num * sizeof(struct ts_event);
+ ts_data->events = (struct ts_event *)kzalloc(events_num, GFP_KERNEL);
+ if (!ts_data->events) {
+ FTS_ERROR("failed to alloc memory for point events");
+ kfree_safe(ts_data->point_buf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+#if FTS_POWER_SOURCE_CUST_EN
+/*****************************************************************************
+* Power Control
+*****************************************************************************/
+#if FTS_PINCTRL_EN
+static int fts_pinctrl_init(struct fts_ts_data *ts)
+{
+ int ret = 0;
+
+ ts->pinctrl = devm_pinctrl_get(ts->dev);
+ if (IS_ERR_OR_NULL(ts->pinctrl)) {
+ FTS_ERROR("Failed to get pinctrl, please check dts");
+ ret = PTR_ERR(ts->pinctrl);
+ goto err_pinctrl_get;
+ }
+
+ ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active");
+ if (IS_ERR_OR_NULL(ts->pins_active)) {
+ FTS_ERROR("Pin state[active] not found");
+ ret = PTR_ERR(ts->pins_active);
+ goto err_pinctrl_lookup;
+ }
+
+ ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend");
+ if (IS_ERR_OR_NULL(ts->pins_suspend)) {
+ FTS_ERROR("Pin state[suspend] not found");
+ ret = PTR_ERR(ts->pins_suspend);
+ goto err_pinctrl_lookup;
+ }
+
+ ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release");
+ if (IS_ERR_OR_NULL(ts->pins_release)) {
+ FTS_ERROR("Pin state[release] not found");
+ ret = PTR_ERR(ts->pins_release);
+ }
+
+ return 0;
+err_pinctrl_lookup:
+ if (ts->pinctrl) {
+ devm_pinctrl_put(ts->pinctrl);
+ }
+err_pinctrl_get:
+ ts->pinctrl = NULL;
+ ts->pins_release = NULL;
+ ts->pins_suspend = NULL;
+ ts->pins_active = NULL;
+ return ret;
+}
+
+static int fts_pinctrl_select_normal(struct fts_ts_data *ts)
+{
+ int ret = 0;
+
+ if (ts->pinctrl && ts->pins_active) {
+ ret = pinctrl_select_state(ts->pinctrl, ts->pins_active);
+ if (ret < 0) {
+ FTS_ERROR("Set normal pin state error:%d", ret);
+ }
+ }
+
+ return ret;
+}
+
+static int fts_pinctrl_select_suspend(struct fts_ts_data *ts)
+{
+ int ret = 0;
+
+ if (ts->pinctrl && ts->pins_suspend) {
+ ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend);
+ if (ret < 0) {
+ FTS_ERROR("Set suspend pin state error:%d", ret);
+ }
+ }
+
+ return ret;
+}
+
+static int fts_pinctrl_select_release(struct fts_ts_data *ts)
+{
+ int ret = 0;
+
+ if (ts->pinctrl) {
+ if (IS_ERR_OR_NULL(ts->pins_release)) {
+ devm_pinctrl_put(ts->pinctrl);
+ ts->pinctrl = NULL;
+ } else {
+ ret = pinctrl_select_state(ts->pinctrl, ts->pins_release);
+ if (ret < 0)
+ FTS_ERROR("Set gesture pin state error:%d", ret);
+ }
+ }
+
+ return ret;
+}
+#endif /* FTS_PINCTRL_EN */
+
+static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable)
+{
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(ts_data->vdd)) {
+ FTS_ERROR("vdd is invalid");
+ return -EINVAL;
+ }
+
+ FTS_FUNC_ENTER();
+ if (enable) {
+ if (ts_data->power_disabled) {
+ FTS_DEBUG("regulator enable !");
+ gpio_direction_output(ts_data->pdata->reset_gpio, 0);
+ msleep(1);
+ ret = regulator_enable(ts_data->vdd);
+ if (ret) {
+ FTS_ERROR("enable vdd regulator failed,ret=%d", ret);
+ }
+
+ if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+ ret = regulator_enable(ts_data->vcc_i2c);
+ if (ret) {
+ FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret);
+ }
+ }
+ ts_data->power_disabled = false;
+ }
+ } else {
+ if (!ts_data->power_disabled) {
+ FTS_DEBUG("regulator disable !");
+ gpio_direction_output(ts_data->pdata->reset_gpio, 0);
+ msleep(1);
+ ret = regulator_disable(ts_data->vdd);
+ if (ret) {
+ FTS_ERROR("disable vdd regulator failed,ret=%d", ret);
+ }
+ if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+ ret = regulator_disable(ts_data->vcc_i2c);
+ if (ret) {
+ FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret);
+ }
+ }
+ ts_data->power_disabled = true;
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+/*****************************************************************************
+* Name: fts_power_source_init
+* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io
+* vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse
+* Must be call after fts_gpio_configure() execute,because this function
+* will operate reset-gpio which request gpio in fts_gpio_configure()
+* Input:
+* Output:
+* Return: return 0 if init power successfully, otherwise return error code
+*****************************************************************************/
+static int fts_power_source_init(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+
+ FTS_FUNC_ENTER();
+ ts_data->vdd = regulator_get(ts_data->dev, "vdd");
+ if (IS_ERR_OR_NULL(ts_data->vdd)) {
+ ret = PTR_ERR(ts_data->vdd);
+ FTS_ERROR("get vdd regulator failed,ret=%d", ret);
+ return ret;
+ }
+
+ if (regulator_count_voltages(ts_data->vdd) > 0) {
+ ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV,
+ FTS_VTG_MAX_UV);
+ if (ret) {
+ FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret);
+ regulator_put(ts_data->vdd);
+ return ret;
+ }
+ }
+
+ ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c");
+ if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+ if (regulator_count_voltages(ts_data->vcc_i2c) > 0) {
+ ret = regulator_set_voltage(ts_data->vcc_i2c,
+ FTS_I2C_VTG_MIN_UV,
+ FTS_I2C_VTG_MAX_UV);
+ if (ret) {
+ FTS_ERROR("vcc_i2c regulator set_vtg failed,ret=%d", ret);
+ regulator_put(ts_data->vcc_i2c);
+ }
+ }
+ }
+
+#if FTS_PINCTRL_EN
+ fts_pinctrl_init(ts_data);
+ fts_pinctrl_select_normal(ts_data);
+#endif
+
+ ts_data->power_disabled = true;
+ ret = fts_power_source_ctrl(ts_data, ENABLE);
+ if (ret) {
+ FTS_ERROR("fail to enable power(regulator)");
+ }
+
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+static int fts_power_source_exit(struct fts_ts_data *ts_data)
+{
+#if FTS_PINCTRL_EN
+ fts_pinctrl_select_release(ts_data);
+#endif
+
+ fts_power_source_ctrl(ts_data, DISABLE);
+
+ if (!IS_ERR_OR_NULL(ts_data->vdd)) {
+ if (regulator_count_voltages(ts_data->vdd) > 0)
+ regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV);
+ regulator_put(ts_data->vdd);
+ }
+
+ if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+ if (regulator_count_voltages(ts_data->vcc_i2c) > 0)
+ regulator_set_voltage(ts_data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV);
+ regulator_put(ts_data->vcc_i2c);
+ }
+
+ return 0;
+}
+
+static int fts_power_source_suspend(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+
+#if FTS_PINCTRL_EN
+ fts_pinctrl_select_suspend(ts_data);
+#endif
+
+ ret = fts_power_source_ctrl(ts_data, DISABLE);
+ if (ret < 0) {
+ FTS_ERROR("power off fail, ret=%d", ret);
+ }
+
+ return ret;
+}
+
+static int fts_power_source_resume(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+
+#if FTS_PINCTRL_EN
+ fts_pinctrl_select_normal(ts_data);
+#endif
+
+ ret = fts_power_source_ctrl(ts_data, ENABLE);
+ if (ret < 0) {
+ FTS_ERROR("power on fail, ret=%d", ret);
+ }
+
+ return ret;
+}
+#endif /* FTS_POWER_SOURCE_CUST_EN */
+
+static int fts_gpio_configure(struct fts_ts_data *data)
+{
+ int ret = 0;
+
+ FTS_FUNC_ENTER();
+ /* request irq gpio */
+ if (gpio_is_valid(data->pdata->irq_gpio)) {
+ ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio");
+ if (ret) {
+ FTS_ERROR("[GPIO]irq gpio request failed");
+ goto err_irq_gpio_req;
+ }
+
+ ret = gpio_direction_input(data->pdata->irq_gpio);
+ if (ret) {
+ FTS_ERROR("[GPIO]set_direction for irq gpio failed");
+ goto err_irq_gpio_dir;
+ }
+ }
+
+ /* request reset gpio */
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio");
+ if (ret) {
+ FTS_ERROR("[GPIO]reset gpio request failed");
+ goto err_irq_gpio_dir;
+ }
+
+ ret = gpio_direction_output(data->pdata->reset_gpio, 1);
+ if (ret) {
+ FTS_ERROR("[GPIO]set_direction for reset gpio failed");
+ goto err_reset_gpio_dir;
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+
+err_reset_gpio_dir:
+ if (gpio_is_valid(data->pdata->reset_gpio))
+ gpio_free(data->pdata->reset_gpio);
+err_irq_gpio_dir:
+ if (gpio_is_valid(data->pdata->irq_gpio))
+ gpio_free(data->pdata->irq_gpio);
+err_irq_gpio_req:
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+static int fts_get_dt_coords(struct device *dev, char *name,
+ struct fts_ts_platform_data *pdata)
+{
+ int ret = 0;
+ u32 coords[FTS_COORDS_ARR_SIZE] = { 0 };
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int coords_size;
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != FTS_COORDS_ARR_SIZE) {
+ FTS_ERROR("invalid:%s, size:%d", name, coords_size);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(np, name, coords, coords_size);
+ if (ret < 0) {
+ FTS_ERROR("Unable to read %s, please check dts", name);
+ pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT;
+ pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT;
+ pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT;
+ pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT;
+ return -ENODATA;
+ } else {
+ pdata->x_min = coords[0];
+ pdata->y_min = coords[1];
+ pdata->x_max = coords[2];
+ pdata->y_max = coords[3];
+ }
+
+ FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max,
+ pdata->y_min, pdata->y_max);
+ return 0;
+}
+
+static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
+{
+ int ret = 0;
+ struct device_node *np = dev->of_node;
+ u32 temp_val = 0;
+
+ FTS_FUNC_ENTER();
+
+ ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
+ if (ret < 0)
+ FTS_ERROR("Unable to get display-coords");
+
+ /* key */
+ pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
+ if (pdata->have_key) {
+ ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number);
+ if (ret < 0)
+ FTS_ERROR("Key number undefined!");
+
+ ret = of_property_read_u32_array(np, "focaltech,keys",
+ pdata->keys, pdata->key_number);
+ if (ret < 0)
+ FTS_ERROR("Keys undefined!");
+ else if (pdata->key_number > FTS_MAX_KEYS)
+ pdata->key_number = FTS_MAX_KEYS;
+
+ ret = of_property_read_u32_array(np, "focaltech,key-x-coords",
+ pdata->key_x_coords,
+ pdata->key_number);
+ if (ret < 0)
+ FTS_ERROR("Key Y Coords undefined!");
+
+ ret = of_property_read_u32_array(np, "focaltech,key-y-coords",
+ pdata->key_y_coords,
+ pdata->key_number);
+ if (ret < 0)
+ FTS_ERROR("Key X Coords undefined!");
+
+ FTS_INFO("VK Number:%d, key:(%d,%d,%d), "
+ "coords:(%d,%d),(%d,%d),(%d,%d)",
+ pdata->key_number,
+ pdata->keys[0], pdata->keys[1], pdata->keys[2],
+ pdata->key_x_coords[0], pdata->key_y_coords[0],
+ pdata->key_x_coords[1], pdata->key_y_coords[1],
+ pdata->key_x_coords[2], pdata->key_y_coords[2]);
+ }
+
+ /* reset, irq gpio info */
+ pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio",
+ 0, &pdata->reset_gpio_flags);
+ if (pdata->reset_gpio < 0)
+ FTS_ERROR("Unable to get reset_gpio");
+
+ pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio",
+ 0, &pdata->irq_gpio_flags);
+ if (pdata->irq_gpio < 0)
+ FTS_ERROR("Unable to get irq_gpio");
+
+ ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
+ if (ret < 0) {
+ FTS_ERROR("Unable to get max-touch-number, please check dts");
+ pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+ } else {
+ if (temp_val < 2)
+ pdata->max_touch_number = 2; /* max_touch_number must >= 2 */
+ else if (temp_val > FTS_MAX_POINTS_SUPPORT)
+ pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+ else
+ pdata->max_touch_number = temp_val;
+ }
+
+ FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d",
+ pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio);
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+#if defined(CONFIG_DRM)
+static void fts_resume_work(struct work_struct *work)
+{
+ struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data,
+ resume_work);
+
+ fts_ts_resume(ts_data->dev);
+}
+
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct drm_panel_notifier *evdata = data;
+ int *blank = NULL;
+ struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data,
+ fb_notif);
+
+ if (!evdata)
+ return 0;
+
+ if (!(event == DRM_PANEL_EARLY_EVENT_BLANK ||
+ event == DRM_PANEL_EVENT_BLANK)) {
+ FTS_INFO("event(%lu) do not need process\n", event);
+ return 0;
+ }
+
+ blank = evdata->data;
+ FTS_INFO("FB event:%lu,blank:%d", event, *blank);
+ switch (*blank) {
+ case DRM_PANEL_BLANK_UNBLANK:
+ if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
+ FTS_INFO("resume: event = %lu, not care\n", event);
+ } else if (event == DRM_PANEL_EVENT_BLANK) {
+ queue_work(fts_data->ts_workqueue, &fts_data->resume_work);
+ }
+ break;
+
+ case DRM_PANEL_BLANK_POWERDOWN:
+ if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
+ cancel_work_sync(&fts_data->resume_work);
+ fts_ts_suspend(ts_data->dev);
+ } else if (event == DRM_PANEL_EVENT_BLANK) {
+ FTS_INFO("suspend: event = %lu, not care\n", event);
+ }
+ break;
+
+ default:
+ FTS_INFO("FB BLANK(%d) do not need process\n", *blank);
+ break;
+ }
+
+ return 0;
+}
+
+#elif defined(CONFIG_FB)
+static void fts_resume_work(struct work_struct *work)
+{
+ struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data,
+ resume_work);
+
+ fts_ts_resume(ts_data->dev);
+}
+
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank = NULL;
+ struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data,
+ fb_notif);
+
+ if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) {
+ FTS_INFO("event(%lu) do not need process\n", event);
+ return 0;
+ }
+
+ blank = evdata->data;
+ FTS_INFO("FB event:%lu,blank:%d", event, *blank);
+ switch (*blank) {
+ case FB_BLANK_UNBLANK:
+ if (FB_EARLY_EVENT_BLANK == event) {
+ FTS_INFO("resume: event = %lu, not care\n", event);
+ } else if (FB_EVENT_BLANK == event) {
+ queue_work(fts_data->ts_workqueue, &fts_data->resume_work);
+ }
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ if (FB_EARLY_EVENT_BLANK == event) {
+ cancel_work_sync(&fts_data->resume_work);
+ fts_ts_suspend(ts_data->dev);
+ } else if (FB_EVENT_BLANK == event) {
+ FTS_INFO("suspend: event = %lu, not care\n", event);
+ }
+ break;
+
+ default:
+ FTS_INFO("FB BLANK(%d) do not need process\n", *blank);
+ break;
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void fts_ts_early_suspend(struct early_suspend *handler)
+{
+ struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data,
+ early_suspend);
+
+ fts_ts_suspend(ts_data->dev);
+}
+
+static void fts_ts_late_resume(struct early_suspend *handler)
+{
+ struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data,
+ early_suspend);
+
+ fts_ts_resume(ts_data->dev);
+}
+#endif
+
+static int fts_ts_probe_entry(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ int pdata_size = sizeof(struct fts_ts_platform_data);
+
+ FTS_FUNC_ENTER();
+ FTS_INFO("%s", FTS_DRIVER_VERSION);
+ ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL);
+ if (!ts_data->pdata) {
+ FTS_ERROR("allocate memory for platform_data fail");
+ return -ENOMEM;
+ }
+
+ if (ts_data->dev->of_node) {
+ ret = fts_parse_dt(ts_data->dev, ts_data->pdata);
+ if (ret)
+ FTS_ERROR("device-tree parse fail");
+ } else {
+ if (ts_data->dev->platform_data) {
+ memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size);
+ } else {
+ FTS_ERROR("platform_data is null");
+ return -ENODEV;
+ }
+ }
+
+ ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq");
+ if (!ts_data->ts_workqueue) {
+ FTS_ERROR("create fts workqueue fail");
+ }
+
+ spin_lock_init(&ts_data->irq_lock);
+ mutex_init(&ts_data->report_mutex);
+ mutex_init(&ts_data->bus_lock);
+
+ /* Init communication interface */
+ ret = fts_bus_init(ts_data);
+ if (ret) {
+ FTS_ERROR("bus initialize fail");
+ goto err_bus_init;
+ }
+
+ ret = fts_input_init(ts_data);
+ if (ret) {
+ FTS_ERROR("input initialize fail");
+ goto err_input_init;
+ }
+
+ ret = fts_report_buffer_init(ts_data);
+ if (ret) {
+ FTS_ERROR("report buffer init fail");
+ goto err_report_buffer;
+ }
+
+ ret = fts_gpio_configure(ts_data);
+ if (ret) {
+ FTS_ERROR("configure the gpios fail");
+ goto err_gpio_config;
+ }
+
+#if FTS_POWER_SOURCE_CUST_EN
+ ret = fts_power_source_init(ts_data);
+ if (ret) {
+ FTS_ERROR("fail to get power(regulator)");
+ goto err_power_init;
+ }
+#endif
+
+#if (!FTS_CHIP_IDC)
+ fts_reset_proc(200);
+#endif
+
+ ret = fts_get_ic_information(ts_data);
+ if (ret) {
+ FTS_ERROR("not focal IC, unregister driver");
+ goto err_irq_req;
+ }
+
+ ret = fts_create_apk_debug_channel(ts_data);
+ if (ret) {
+ FTS_ERROR("create apk debug node fail");
+ }
+
+ ret = fts_create_sysfs(ts_data);
+ if (ret) {
+ FTS_ERROR("create sysfs node fail");
+ }
+
+#if FTS_POINT_REPORT_CHECK_EN
+ ret = fts_point_report_check_init(ts_data);
+ if (ret) {
+ FTS_ERROR("init point report check fail");
+ }
+#endif
+
+ ret = fts_ex_mode_init(ts_data);
+ if (ret) {
+ FTS_ERROR("init glove/cover/charger fail");
+ }
+
+ ret = fts_gesture_init(ts_data);
+ if (ret) {
+ FTS_ERROR("init gesture fail");
+ }
+
+
+#if FTS_ESDCHECK_EN
+ ret = fts_esdcheck_init(ts_data);
+ if (ret) {
+ FTS_ERROR("init esd check fail");
+ }
+#endif
+
+ ret = fts_irq_registration(ts_data);
+ if (ret) {
+ FTS_ERROR("request irq failed");
+ goto err_irq_req;
+ }
+
+ ret = fts_fwupg_init(ts_data);
+ if (ret) {
+ FTS_ERROR("init fw upgrade fail");
+ }
+
+#if defined(CONFIG_DRM)
+ if (ts_data->ts_workqueue) {
+ INIT_WORK(&ts_data->resume_work, fts_resume_work);
+ }
+ ts_data->fb_notif.notifier_call = fb_notifier_callback;
+
+ if (active_panel &&
+ drm_panel_notifier_register(active_panel,
+ &ts_data->fb_notif) < 0)
+ FTS_ERROR("register notifier failed!\n");
+
+#elif defined(CONFIG_FB)
+ if (ts_data->ts_workqueue) {
+ INIT_WORK(&ts_data->resume_work, fts_resume_work);
+ }
+ ts_data->fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&ts_data->fb_notif);
+ if (ret) {
+ FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret);
+ }
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
+ ts_data->early_suspend.suspend = fts_ts_early_suspend;
+ ts_data->early_suspend.resume = fts_ts_late_resume;
+ register_early_suspend(&ts_data->early_suspend);
+#endif
+
+ FTS_FUNC_EXIT();
+ return 0;
+
+err_irq_req:
+#if FTS_POWER_SOURCE_CUST_EN
+err_power_init:
+ fts_power_source_exit(ts_data);
+#endif
+ if (gpio_is_valid(ts_data->pdata->reset_gpio))
+ gpio_free(ts_data->pdata->reset_gpio);
+ if (gpio_is_valid(ts_data->pdata->irq_gpio))
+ gpio_free(ts_data->pdata->irq_gpio);
+err_gpio_config:
+ kfree_safe(ts_data->point_buf);
+ kfree_safe(ts_data->events);
+err_report_buffer:
+ input_unregister_device(ts_data->input_dev);
+err_input_init:
+ if (ts_data->ts_workqueue)
+ destroy_workqueue(ts_data->ts_workqueue);
+err_bus_init:
+ kfree_safe(ts_data->bus_tx_buf);
+ kfree_safe(ts_data->bus_rx_buf);
+ kfree_safe(ts_data->pdata);
+
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+static int fts_ts_remove_entry(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+
+#if FTS_POINT_REPORT_CHECK_EN
+ fts_point_report_check_exit(ts_data);
+#endif
+
+ fts_release_apk_debug_channel(ts_data);
+ fts_remove_sysfs(ts_data);
+ fts_ex_mode_exit(ts_data);
+
+ fts_fwupg_exit(ts_data);
+
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_exit(ts_data);
+#endif
+
+ fts_gesture_exit(ts_data);
+ fts_bus_exit(ts_data);
+
+ free_irq(ts_data->irq, ts_data);
+ input_unregister_device(ts_data->input_dev);
+
+ if (ts_data->ts_workqueue)
+ destroy_workqueue(ts_data->ts_workqueue);
+
+#if defined(CONFIG_DRM)
+ if (active_panel)
+ drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif);
+
+#elif defined(CONFIG_FB)
+ if (fb_unregister_client(&ts_data->fb_notif))
+ FTS_ERROR("Error occurred while unregistering fb_notifier.");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts_data->early_suspend);
+#endif
+
+ if (gpio_is_valid(ts_data->pdata->reset_gpio))
+ gpio_free(ts_data->pdata->reset_gpio);
+
+ if (gpio_is_valid(ts_data->pdata->irq_gpio))
+ gpio_free(ts_data->pdata->irq_gpio);
+
+#if FTS_POWER_SOURCE_CUST_EN
+ fts_power_source_exit(ts_data);
+#endif
+
+ kfree_safe(ts_data->point_buf);
+ kfree_safe(ts_data->events);
+
+ kfree_safe(ts_data->pdata);
+ kfree_safe(ts_data);
+
+ FTS_FUNC_EXIT();
+
+ return 0;
+}
+
+static int fts_ts_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+ FTS_FUNC_ENTER();
+ if (ts_data->suspended) {
+ FTS_INFO("Already in suspend state");
+ return 0;
+ }
+
+ if (ts_data->fw_loading) {
+ FTS_INFO("fw upgrade in process, can't suspend");
+ return 0;
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_suspend();
+#endif
+
+ if (ts_data->gesture_mode) {
+ fts_gesture_suspend(ts_data);
+ } else {
+ fts_irq_disable();
+
+ FTS_INFO("make TP enter into sleep mode");
+ ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP);
+ if (ret < 0)
+ FTS_ERROR("set TP to sleep mode fail, ret=%d", ret);
+
+ if (!ts_data->ic_info.is_incell) {
+#if FTS_POWER_SOURCE_CUST_EN
+ ret = fts_power_source_suspend(ts_data);
+ if (ret < 0) {
+ FTS_ERROR("power enter suspend fail");
+ }
+#endif
+ }
+ }
+
+ fts_release_all_finger();
+ ts_data->suspended = true;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+static int fts_ts_resume(struct device *dev)
+{
+ struct fts_ts_data *ts_data = fts_data;
+
+ FTS_FUNC_ENTER();
+ if (!ts_data->suspended) {
+ FTS_DEBUG("Already in awake state");
+ return 0;
+ }
+
+ fts_release_all_finger();
+
+ if (!ts_data->ic_info.is_incell) {
+#if FTS_POWER_SOURCE_CUST_EN
+ fts_power_source_resume(ts_data);
+#endif
+ fts_reset_proc(200);
+ }
+
+ fts_wait_tp_to_valid();
+ fts_ex_mode_recovery(ts_data);
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_resume();
+#endif
+
+ if (ts_data->gesture_mode) {
+ fts_gesture_resume(ts_data);
+ } else {
+ fts_irq_enable();
+ }
+
+ ts_data->suspended = false;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* TP Driver
+*****************************************************************************/
+static int fts_ts_check_dt(struct device_node *np)
+{
+ int i;
+ int count;
+ struct device_node *node;
+ struct drm_panel *panel;
+
+ count = of_count_phandle_with_args(np, "panel", NULL);
+ if (count <= 0)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ node = of_parse_phandle(np, "panel", i);
+ panel = of_drm_find_panel(node);
+ of_node_put(node);
+ if (!IS_ERR(panel)) {
+ active_panel = panel;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int fts_ts_check_default_tp(struct device_node *dt, const char *prop)
+{
+ const char **active_tp = NULL;
+ int count, tmp, score = 0;
+ const char *active;
+ int ret, i;
+
+ count = of_property_count_strings(dt->parent, prop);
+ if (count <= 0 || count > 3)
+ return -ENODEV;
+
+ active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL);
+ if (!active_tp) {
+ FTS_ERROR("FTS alloc failed\n");
+ return -ENOMEM;
+ }
+
+ ret = of_property_read_string_array(dt->parent, prop,
+ active_tp, count);
+ if (ret < 0) {
+ FTS_ERROR("fail to read %s %d\n", prop, ret);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ active = active_tp[i];
+ if (active != NULL) {
+ tmp = of_device_is_compatible(dt, active);
+ if (tmp > 0)
+ score++;
+ }
+ }
+
+ if (score <= 0) {
+ FTS_INFO("not match this driver\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(active_tp);
+ return ret;
+}
+
+static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = NULL;
+ struct device_node *dp = client->dev.of_node;
+
+ FTS_INFO("Touch Screen(I2C BUS) driver prboe...");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ FTS_ERROR("I2C not supported");
+ return -ENODEV;
+ }
+
+ if (fts_ts_check_dt(dp)) {
+ if (!fts_ts_check_default_tp(dp, "qcom,i2c-touch-active"))
+ ret = -EPROBE_DEFER;
+ else
+ ret = -ENODEV;
+
+ return ret;
+ }
+
+ /* malloc memory for global struct variable */
+ ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL);
+ if (!ts_data) {
+ FTS_ERROR("allocate memory for fts_data fail");
+ return -ENOMEM;
+ }
+
+ fts_data = ts_data;
+ ts_data->client = client;
+ ts_data->dev = &client->dev;
+ ts_data->log_level = 1;
+ ts_data->fw_is_running = 0;
+ i2c_set_clientdata(client, ts_data);
+
+ ret = fts_ts_probe_entry(ts_data);
+ if (ret) {
+ FTS_ERROR("Touch Screen(I2C BUS) driver probe fail");
+ kfree_safe(ts_data);
+ return ret;
+ }
+
+ FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully");
+ return 0;
+}
+
+static int fts_ts_remove(struct i2c_client *client)
+{
+ return fts_ts_remove_entry(i2c_get_clientdata(client));
+}
+
+static const struct i2c_device_id fts_ts_id[] = {
+ {FTS_DRIVER_NAME, 0},
+ {},
+};
+static const struct of_device_id fts_dt_match[] = {
+ {.compatible = "focaltech,fts_ts", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fts_dt_match);
+
+static struct i2c_driver fts_ts_driver = {
+ .probe = fts_ts_probe,
+ .remove = fts_ts_remove,
+ .driver = {
+ .name = FTS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(fts_dt_match),
+ },
+ .id_table = fts_ts_id,
+};
+
+static int __init fts_ts_init(void)
+{
+ int ret = 0;
+
+ FTS_FUNC_ENTER();
+ ret = i2c_add_driver(&fts_ts_driver);
+ if ( ret != 0 ) {
+ FTS_ERROR("Focaltech touch screen driver init failed!");
+ }
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+static void __exit fts_ts_exit(void)
+{
+ i2c_del_driver(&fts_ts_driver);
+}
+late_initcall(fts_ts_init);
+module_exit(fts_ts_exit);
+
+MODULE_AUTHOR("FocalTech Driver Team");
+MODULE_DESCRIPTION("FocalTech Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
new file mode 100644
index 0000000..7603a64
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
@@ -0,0 +1,260 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.h
+
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_CORE_H__
+#define __LINUX_FOCALTECH_CORE_H__
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#include "focaltech_common.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */
+#define FTS_MAX_KEYS 4
+#define FTS_KEY_DIM 10
+#define FTS_ONE_TCH_LEN 6
+#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 3)
+
+#define FTS_GESTURE_POINTS_MAX 6
+#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4)
+
+#define FTS_MAX_ID 0x0A
+#define FTS_TOUCH_X_H_POS 3
+#define FTS_TOUCH_X_L_POS 4
+#define FTS_TOUCH_Y_H_POS 5
+#define FTS_TOUCH_Y_L_POS 6
+#define FTS_TOUCH_PRE_POS 7
+#define FTS_TOUCH_AREA_POS 8
+#define FTS_TOUCH_POINT_NUM 2
+#define FTS_TOUCH_EVENT_POS 3
+#define FTS_TOUCH_ID_POS 5
+#define FTS_COORDS_ARR_SIZE 4
+#define FTS_X_MIN_DISPLAY_DEFAULT 0
+#define FTS_Y_MIN_DISPLAY_DEFAULT 0
+#define FTS_X_MAX_DISPLAY_DEFAULT 720
+#define FTS_Y_MAX_DISPLAY_DEFAULT 1280
+
+#define FTS_TOUCH_DOWN 0
+#define FTS_TOUCH_UP 1
+#define FTS_TOUCH_CONTACT 2
+#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag))
+#define EVENT_UP(flag) (FTS_TOUCH_UP == flag)
+#define EVENT_NO_DOWN(data) (!data->point_num)
+
+#define FTX_MAX_COMPATIBLE_TYPE 4
+#define FTX_MAX_COMMMAND_LENGTH 16
+
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct ftxxxx_proc {
+ struct proc_dir_entry *proc_entry;
+ u8 opmode;
+ u8 cmd_len;
+ u8 cmd[FTX_MAX_COMMMAND_LENGTH];
+};
+
+struct fts_ts_platform_data {
+ u32 irq_gpio;
+ u32 irq_gpio_flags;
+ u32 reset_gpio;
+ u32 reset_gpio_flags;
+ bool have_key;
+ u32 key_number;
+ u32 keys[FTS_MAX_KEYS];
+ u32 key_y_coords[FTS_MAX_KEYS];
+ u32 key_x_coords[FTS_MAX_KEYS];
+ u32 x_max;
+ u32 y_max;
+ u32 x_min;
+ u32 y_min;
+ u32 max_touch_number;
+};
+
+struct ts_event {
+ int x; /*x coordinate */
+ int y; /*y coordinate */
+ int p; /* pressure */
+ int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */
+ int id; /*touch ID */
+ int area;
+};
+
+struct fts_ts_data {
+ struct i2c_client *client;
+ struct spi_device *spi;
+ struct device *dev;
+ struct input_dev *input_dev;
+ struct fts_ts_platform_data *pdata;
+ struct ts_ic_info ic_info;
+ struct workqueue_struct *ts_workqueue;
+ struct work_struct fwupg_work;
+ struct delayed_work esdcheck_work;
+ struct delayed_work prc_work;
+ struct work_struct resume_work;
+ struct ftxxxx_proc proc;
+ spinlock_t irq_lock;
+ struct mutex report_mutex;
+ struct mutex bus_lock;
+ int irq;
+ int log_level;
+ int fw_is_running; /* confirm fw is running when using spi:default 0 */
+ int dummy_byte;
+ bool suspended;
+ bool fw_loading;
+ bool irq_disabled;
+ bool power_disabled;
+ bool glove_mode;
+ bool cover_mode;
+ bool charger_mode;
+ bool gesture_mode; /* gesture enable or disable, default: disable */
+ /* multi-touch */
+ struct ts_event *events;
+ u8 *bus_tx_buf;
+ u8 *bus_rx_buf;
+ u8 *point_buf;
+ int pnt_buf_size;
+ int touchs;
+ int key_state;
+ int touch_point;
+ int point_num;
+ struct regulator *vdd;
+ struct regulator *vcc_i2c;
+#if FTS_PINCTRL_EN
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_active;
+ struct pinctrl_state *pins_suspend;
+ struct pinctrl_state *pins_release;
+#endif
+#if defined(CONFIG_FB) || defined(CONFIG_DRM)
+ struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+};
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct fts_ts_data *fts_data;
+
+/* communication interface */
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen);
+int fts_read_reg(u8 addr, u8 *value);
+int fts_write(u8 *writebuf, u32 writelen);
+int fts_write_reg(u8 addr, u8 value);
+void fts_hid2std(void);
+int fts_bus_init(struct fts_ts_data *ts_data);
+int fts_bus_exit(struct fts_ts_data *ts_data);
+
+/* Gesture functions */
+int fts_gesture_init(struct fts_ts_data *ts_data);
+int fts_gesture_exit(struct fts_ts_data *ts_data);
+void fts_gesture_recovery(struct fts_ts_data *ts_data);
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data);
+int fts_gesture_suspend(struct fts_ts_data *ts_data);
+int fts_gesture_resume(struct fts_ts_data *ts_data);
+
+/* Apk and functions */
+int fts_create_apk_debug_channel(struct fts_ts_data *);
+void fts_release_apk_debug_channel(struct fts_ts_data *);
+
+/* ADB functions */
+int fts_create_sysfs(struct fts_ts_data *ts_data);
+int fts_remove_sysfs(struct fts_ts_data *ts_data);
+
+/* ESD */
+#if FTS_ESDCHECK_EN
+int fts_esdcheck_init(struct fts_ts_data *ts_data);
+int fts_esdcheck_exit(struct fts_ts_data *ts_data);
+int fts_esdcheck_switch(bool enable);
+int fts_esdcheck_proc_busy(bool proc_debug);
+int fts_esdcheck_set_intr(bool intr);
+int fts_esdcheck_suspend(void);
+int fts_esdcheck_resume(void);
+#endif
+
+
+/* Point Report Check*/
+#if FTS_POINT_REPORT_CHECK_EN
+int fts_point_report_check_init(struct fts_ts_data *ts_data);
+int fts_point_report_check_exit(struct fts_ts_data *ts_data);
+void fts_prc_queue_work(struct fts_ts_data *ts_data);
+#endif
+
+/* FW upgrade */
+int fts_fwupg_init(struct fts_ts_data *ts_data);
+int fts_fwupg_exit(struct fts_ts_data *ts_data);
+int fts_upgrade_bin(char *fw_name, bool force);
+int fts_enter_test_environment(bool test_state);
+
+/* Other */
+int fts_reset_proc(int hdelayms);
+int fts_wait_tp_to_valid(void);
+void fts_release_all_finger(void);
+void fts_tp_state_recovery(struct fts_ts_data *ts_data);
+int fts_ex_mode_init(struct fts_ts_data *ts_data);
+int fts_ex_mode_exit(struct fts_ts_data *ts_data);
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data);
+
+void fts_irq_disable(void);
+void fts_irq_enable(void);
+#endif /* __LINUX_FOCALTECH_CORE_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
new file mode 100644
index 0000000..295f91d
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
@@ -0,0 +1,465 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-03
+*
+* Abstract: ESD check function
+*
+* Version: v1.0
+*
+* Revision History:
+* v1.0:
+* First release. By luougojin 2016-08-03
+* v1.1: By luougojin 2017-02-15
+* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_ESDCHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define ESDCHECK_WAIT_TIME 1000 /* ms */
+#define LCD_ESD_PATCH 0
+#define ESDCHECK_INTRCNT_MAX 2
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_esdcheck_st {
+ u8 mode : 1; /* 1- need check esd 0- no esd check */
+ u8 suspend : 1;
+ u8 proc_debug : 1; /* apk or adb use */
+ u8 intr : 1; /* 1- Interrupt trigger */
+ u8 unused : 4;
+ u8 intr_cnt;
+ u8 flow_work_hold_cnt; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */
+ u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */
+ u32 hardware_reset_cnt;
+ u32 nack_cnt;
+ u32 dataerror_cnt;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_esdcheck_st fts_esdcheck_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+#if LCD_ESD_PATCH
+int lcd_need_reset;
+static int tp_need_recovery; /* LCD reset cause Tp reset */
+int idc_esdcheck_lcderror(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ u8 val = 0;
+
+ FTS_DEBUG("check LCD ESD");
+ if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) {
+ tp_need_recovery = 0;
+ /* LCD reset, need recover TP state */
+ fts_release_all_finger();
+ fts_tp_state_recovery(ts_data);
+ }
+
+ ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val);
+ if ( ret < 0) {
+ FTS_ERROR("read reg0xED fail,ret:%d", ret);
+ return -EIO;
+ }
+
+ if (val == 0xAA) {
+ /*
+ * 1. Set flag lcd_need_reset = 1;
+ * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0
+ * 3. recover TP state
+ */
+ FTS_INFO("LCD ESD, need execute LCD reset");
+ lcd_need_reset = 1;
+ tp_need_recovery = 1;
+ }
+
+ return 0;
+}
+#endif
+
+static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+
+ fts_esdcheck_data.flow_work_hold_cnt = 0;
+ fts_esdcheck_data.hardware_reset_cnt++;
+
+ fts_reset_proc(200);
+ fts_release_all_finger();
+ fts_tp_state_recovery(ts_data);
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+static bool get_chip_id(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ int i = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+ u8 chip_id = ts_data->ic_info.ids.chip_idh;
+
+ for (i = 0; i < 3; i++) {
+ reg_addr = FTS_REG_CHIP_ID;
+ ret = fts_read(®_addr, 1, ®_value, 1);
+ if (ret < 0) {
+ FTS_ERROR("read chip id fail,ret:%d", ret);
+ fts_esdcheck_data.nack_cnt++;
+ } else {
+ if (reg_value == chip_id) {
+ break;
+ } else {
+ FTS_DEBUG("read chip_id:%x,retry:%d", reg_value, i);
+ fts_esdcheck_data.dataerror_cnt++;
+ }
+ }
+ msleep(10);
+ }
+
+ /* if can't get correct data in 3 times, then need hardware reset */
+ if (i >= 3) {
+ FTS_ERROR("read chip id 3 times fail, need execute TP reset");
+ return true;
+ }
+
+ return false;
+}
+
+/*****************************************************************************
+* Name: get_flow_cnt
+* Brief: Read flow cnt(0x91)
+* Input:
+* Output:
+* Return: 1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
+* 0(false) - Reg 0x91(flow cnt) normal
+*****************************************************************************/
+static bool get_flow_cnt(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+
+ reg_addr = FTS_REG_FLOW_WORK_CNT;
+ ret = fts_read(®_addr, 1, ®_value, 1);
+ if (ret < 0) {
+ FTS_ERROR("read reg0x91 fail,ret:%d", ret);
+ fts_esdcheck_data.nack_cnt++;
+ } else {
+ if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) {
+ FTS_DEBUG("reg0x91,val:%x,last:%x", reg_value,
+ fts_esdcheck_data.flow_work_cnt_last);
+ fts_esdcheck_data.flow_work_hold_cnt++;
+ } else {
+ fts_esdcheck_data.flow_work_hold_cnt = 0;
+ }
+
+ fts_esdcheck_data.flow_work_cnt_last = reg_value;
+ }
+
+ /* Flow Work Cnt keep a value for 5 times, need execute TP reset */
+ if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
+ FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset");
+ return true;
+ }
+
+ return false;
+}
+
+static int esdcheck_algorithm(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+ bool hardware_reset = 0;
+
+ /* 1. esdcheck is interrupt, then return */
+ if (fts_esdcheck_data.intr == 1) {
+ fts_esdcheck_data.intr_cnt++;
+ if (fts_esdcheck_data.intr_cnt > ESDCHECK_INTRCNT_MAX)
+ fts_esdcheck_data.intr = 0;
+ else
+ return 0;
+ }
+
+ /* 2. check power state, if suspend, no need check esd */
+ if (fts_esdcheck_data.suspend == 1) {
+ FTS_DEBUG("In suspend, not check esd");
+ /* because in suspend state, adb can be used, when upgrade FW, will
+ * active ESD check(active = 1); But in suspend, then will don't
+ * queue_delayed_work, when resume, don't check ESD again
+ */
+ return 0;
+ }
+
+ /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/
+ if (fts_esdcheck_data.proc_debug == 1) {
+ FTS_INFO("In apk/adb command mode, not check esd");
+ return 0;
+ }
+
+ /* 4. In factory mode, can't check esd */
+ reg_addr = FTS_REG_WORKMODE;
+ ret = fts_read_reg(reg_addr, ®_value);
+ if ( ret < 0 ) {
+ fts_esdcheck_data.nack_cnt++;
+ } else if ( (reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) {
+ FTS_DEBUG("not in work mode(%x), no check esd", reg_value);
+ return 0;
+ }
+
+ /* 5. IDC esd check lcd default:close */
+#if LCD_ESD_PATCH
+ idc_esdcheck_lcderror(ts_data);
+#endif
+
+ /* 6. Get Chip ID */
+ hardware_reset = get_chip_id(ts_data);
+
+ /* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */
+ if (!hardware_reset) {
+ hardware_reset = get_flow_cnt(ts_data);
+ }
+
+ /* 8. If need hardware reset, then handle it here */
+ if (hardware_reset == 1) {
+ FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d",
+ fts_esdcheck_data.nack_cnt,
+ fts_esdcheck_data.dataerror_cnt,
+ fts_esdcheck_data.hardware_reset_cnt);
+ fts_esdcheck_tp_reset(ts_data);
+ }
+
+ return 0;
+}
+
+static void esdcheck_func(struct work_struct *work)
+{
+ struct fts_ts_data *ts_data = container_of(work,
+ struct fts_ts_data, esdcheck_work.work);
+
+ if (ENABLE == fts_esdcheck_data.mode) {
+ esdcheck_algorithm(ts_data);
+ queue_delayed_work(ts_data->ts_workqueue,
+ &ts_data->esdcheck_work,
+ msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+ }
+}
+
+int fts_esdcheck_set_intr(bool intr)
+{
+ /* interrupt don't add debug message */
+ fts_esdcheck_data.intr = intr;
+ fts_esdcheck_data.intr_cnt = (u8)intr;
+ return 0;
+}
+
+static int fts_esdcheck_get_status(void)
+{
+ /* interrupt don't add debug message */
+ return fts_esdcheck_data.mode;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_proc_busy
+* Brief: When APK or ADB command access TP via driver, then need set proc_debug,
+* then will not check ESD.
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_proc_busy(bool proc_debug)
+{
+ fts_esdcheck_data.proc_debug = proc_debug;
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_switch
+* Brief: FTS esd check function switch.
+* Input: enable: 1 - Enable esd check
+* 0 - Disable esd check
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_switch(bool enable)
+{
+ struct fts_ts_data *ts_data = fts_data;
+ FTS_FUNC_ENTER();
+ if (fts_esdcheck_data.mode == ENABLE) {
+ if (enable) {
+ FTS_DEBUG("ESD check start");
+ fts_esdcheck_data.flow_work_hold_cnt = 0;
+ fts_esdcheck_data.flow_work_cnt_last = 0;
+ fts_esdcheck_data.intr = 0;
+ fts_esdcheck_data.intr_cnt = 0;
+ queue_delayed_work(ts_data->ts_workqueue,
+ &ts_data->esdcheck_work,
+ msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+ } else {
+ FTS_DEBUG("ESD check stop");
+ cancel_delayed_work_sync(&ts_data->esdcheck_work);
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_esdcheck_suspend(void)
+{
+ FTS_FUNC_ENTER();
+ fts_esdcheck_switch(DISABLE);
+ fts_esdcheck_data.suspend = 1;
+ fts_esdcheck_data.intr = 0;
+ fts_esdcheck_data.intr_cnt = 0;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_esdcheck_resume( void )
+{
+ FTS_FUNC_ENTER();
+ fts_esdcheck_switch(ENABLE);
+ fts_esdcheck_data.suspend = 0;
+ fts_esdcheck_data.intr = 0;
+ fts_esdcheck_data.intr_cnt = 0;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+static ssize_t fts_esdcheck_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ FTS_DEBUG("enable esdcheck");
+ fts_esdcheck_data.mode = ENABLE;
+ fts_esdcheck_switch(ENABLE);
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ FTS_DEBUG("disable esdcheck");
+ fts_esdcheck_switch(DISABLE);
+ fts_esdcheck_data.mode = DISABLE;
+ }
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_esdcheck_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \
+ fts_esdcheck_get_status() ? "On" : "Off");
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+/* sysfs esd node
+ * read example: cat fts_esd_mode ---read esd mode
+ * write example:echo 01 > fts_esd_mode ---make esdcheck enable
+ *
+ */
+static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store);
+
+static struct attribute *fts_esd_mode_attrs[] = {
+
+ &dev_attr_fts_esd_mode.attr,
+ NULL,
+};
+
+static struct attribute_group fts_esd_group = {
+ .attrs = fts_esd_mode_attrs,
+};
+
+int fts_create_esd_sysfs(struct device *dev)
+{
+ int ret = 0;
+
+ ret = sysfs_create_group(&dev->kobj, &fts_esd_group);
+ if ( ret != 0) {
+ FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail");
+ sysfs_remove_group(&dev->kobj, &fts_esd_group);
+ return ret;
+ }
+ return 0;
+}
+
+int fts_esdcheck_init(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+
+ if (ts_data->ts_workqueue) {
+ INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func);
+ } else {
+ FTS_ERROR("fts workqueue is NULL, can't run esd check func!");
+ return -EINVAL;
+ }
+
+ memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
+
+ fts_esdcheck_data.mode = ENABLE;
+ fts_esdcheck_data.intr = 0;
+ fts_esdcheck_data.intr_cnt = 0;
+ fts_esdcheck_switch(ENABLE);
+ fts_create_esd_sysfs(ts_data->dev);
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_esdcheck_exit(struct fts_ts_data *ts_data)
+{
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group);
+ return 0;
+}
+
+#endif /* FTS_ESDCHECK_EN */
+
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c
new file mode 100644
index 0000000..b9ae3f1
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c
@@ -0,0 +1,1191 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: Focaltech_ex_fun.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include <linux/uaccess.h>
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define PROC_UPGRADE 0
+#define PROC_READ_REGISTER 1
+#define PROC_WRITE_REGISTER 2
+#define PROC_AUTOCLB 4
+#define PROC_UPGRADE_INFO 5
+#define PROC_WRITE_DATA 6
+#define PROC_READ_DATA 7
+#define PROC_SET_TEST_FLAG 8
+#define PROC_SET_SLAVE_ADDR 10
+#define PROC_HW_RESET 11
+#define PROC_READ_STATUS 12
+#define PROC_SET_BOOT_MODE 13
+#define PROC_ENTER_TEST_ENVIRONMENT 14
+#define PROC_NAME "ftxxxx-debug"
+#define PROC_BUF_SIZE 256
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+enum {
+ RWREG_OP_READ = 0,
+ RWREG_OP_WRITE = 1,
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct rwreg_operation_t {
+ int type; /* 0: read, 1: write */
+ int reg; /* register */
+ int len; /* read/write length */
+ int val; /* length = 1; read: return value, write: op return */
+ int res; /* 0: success, otherwise: fail */
+ char *opbuf; /* length >= 1, read return value, write: op return */
+} rw_op;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+static ssize_t fts_debug_write(
+ struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
+{
+ u8 *writebuf = NULL;
+ u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+ int buflen = count;
+ int writelen = 0;
+ int ret = 0;
+ char tmp[PROC_BUF_SIZE];
+ struct fts_ts_data *ts_data = fts_data;
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+ if ((buflen <= 1) || (buflen > PAGE_SIZE)) {
+ FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (buflen > PROC_BUF_SIZE) {
+ writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+ if (NULL == writebuf) {
+ FTS_ERROR("apk proc wirte buf zalloc fail");
+ return -ENOMEM;
+ }
+ } else {
+ writebuf = tmpbuf;
+ }
+
+ if (copy_from_user(writebuf, buff, buflen)) {
+ FTS_ERROR("[APK]: copy from user error!!");
+ ret = -EFAULT;
+ goto proc_write_err;
+ }
+
+ proc->opmode = writebuf[0];
+ switch (proc->opmode) {
+ case PROC_SET_TEST_FLAG:
+ FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]);
+ if (writebuf[1] == 0) {
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(ENABLE);
+#endif
+ } else {
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(DISABLE);
+#endif
+ }
+ break;
+
+ case PROC_READ_REGISTER:
+ proc->cmd[0] = writebuf[1];
+ break;
+
+ case PROC_WRITE_REGISTER:
+ ret = fts_write_reg(writebuf[1], writebuf[2]);
+ if (ret < 0) {
+ FTS_ERROR("PROC_WRITE_REGISTER write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_READ_DATA:
+ writelen = buflen - 1;
+ if (writelen >= FTX_MAX_COMMMAND_LENGTH) {
+ FTS_ERROR("cmd(PROC_READ_DATA) len(%d) fail", writelen);
+ goto proc_write_err;
+ }
+ memcpy(proc->cmd, writebuf + 1, writelen);
+ proc->cmd_len = writelen;
+ ret = fts_write(writebuf + 1, writelen);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_DATA write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_WRITE_DATA:
+ writelen = buflen - 1;
+ ret = fts_write(writebuf + 1, writelen);
+ if (ret < 0) {
+ FTS_ERROR("PROC_WRITE_DATA write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_SET_SLAVE_ADDR:
+ break;
+
+ case PROC_HW_RESET:
+ snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1);
+ tmp[buflen - 1] = '\0';
+ if (strncmp(tmp, "focal_driver", 12) == 0) {
+ FTS_INFO("APK execute HW Reset");
+ fts_reset_proc(0);
+ }
+ break;
+
+ case PROC_SET_BOOT_MODE:
+ FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]);
+ if (0 == writebuf[1]) {
+ ts_data->fw_is_running = true;
+ } else {
+ ts_data->fw_is_running = false;
+ }
+ break;
+
+ case PROC_ENTER_TEST_ENVIRONMENT:
+ FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]);
+ if (0 == writebuf[1]) {
+ fts_enter_test_environment(0);
+ } else {
+ fts_enter_test_environment(1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ret = buflen;
+proc_write_err:
+ if ((buflen > PROC_BUF_SIZE) && writebuf) {
+ kfree(writebuf);
+ writebuf = NULL;
+ }
+ return ret;
+}
+
+static ssize_t fts_debug_read(
+ struct file *filp, char __user *buff, size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int num_read_chars = 0;
+ int buflen = count;
+ u8 *readbuf = NULL;
+ u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+ struct fts_ts_data *ts_data = fts_data;
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+ if ((buflen <= 0) || (buflen > PAGE_SIZE)) {
+ FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (buflen > PROC_BUF_SIZE) {
+ readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+ if (NULL == readbuf) {
+ FTS_ERROR("apk proc wirte buf zalloc fail");
+ return -ENOMEM;
+ }
+ } else {
+ readbuf = tmpbuf;
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(1);
+#endif
+
+ switch (proc->opmode) {
+ case PROC_READ_REGISTER:
+ num_read_chars = 1;
+ ret = fts_read_reg(proc->cmd[0], &readbuf[0]);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_REGISTER read error");
+ goto proc_read_err;
+ }
+ break;
+
+ case PROC_WRITE_REGISTER:
+ break;
+
+ case PROC_READ_DATA:
+ num_read_chars = buflen;
+ ret = fts_read(NULL, 0, readbuf, num_read_chars);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_DATA read error");
+ goto proc_read_err;
+ }
+ break;
+
+ case PROC_WRITE_DATA:
+ break;
+
+ default:
+ break;
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(0);
+#endif
+
+ if (copy_to_user(buff, readbuf, num_read_chars)) {
+ FTS_ERROR("copy to user error");
+ ret = -EFAULT;
+ goto proc_read_err;
+ }
+
+ ret = num_read_chars;
+proc_read_err:
+ if ((buflen > PROC_BUF_SIZE) && readbuf) {
+ kfree(readbuf);
+ readbuf = NULL;
+ }
+ return ret;
+}
+
+static const struct file_operations fts_proc_fops = {
+ .owner = THIS_MODULE,
+ .read = fts_debug_read,
+ .write = fts_debug_write,
+};
+#else
+static int fts_debug_write(struct file *filp,
+ const char __user *buff, unsigned long len, void *data)
+{
+ u8 *writebuf = NULL;
+ u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+ int buflen = count;
+ int writelen = 0;
+ int ret = 0;
+ char tmp[PROC_BUF_SIZE];
+ struct fts_ts_data *ts_data = fts_data;
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+ if ((buflen <= 1) || (buflen > PAGE_SIZE)) {
+ FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (buflen > PROC_BUF_SIZE) {
+ writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+ if (NULL == writebuf) {
+ FTS_ERROR("apk proc wirte buf zalloc fail");
+ return -ENOMEM;
+ }
+ } else {
+ writebuf = tmpbuf;
+ }
+
+ if (copy_from_user(writebuf, buff, buflen)) {
+ FTS_ERROR("[APK]: copy from user error!!");
+ ret = -EFAULT;
+ goto proc_write_err;
+ }
+
+ proc->opmode = writebuf[0];
+ switch (proc->opmode) {
+ case PROC_SET_TEST_FLAG:
+ FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]);
+ if (writebuf[1] == 0) {
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(ENABLE);
+#endif
+ } else {
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(DISABLE);
+#endif
+ }
+ break;
+
+ case PROC_READ_REGISTER:
+ proc->cmd[0] = writebuf[1];
+ break;
+
+ case PROC_WRITE_REGISTER:
+ ret = fts_write_reg(writebuf[1], writebuf[2]);
+ if (ret < 0) {
+ FTS_ERROR("PROC_WRITE_REGISTER write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_READ_DATA:
+ writelen = buflen - 1;
+ if (writelen >= FTX_MAX_COMMMAND_LENGTH) {
+ FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen);
+ goto proc_write_err;
+ }
+ memcpy(proc->cmd, writebuf + 1, writelen);
+ proc->cmd_len = writelen;
+ ret = fts_write(writebuf + 1, writelen);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_DATA write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_WRITE_DATA:
+ writelen = buflen - 1;
+ ret = fts_write(writebuf + 1, writelen);
+ if (ret < 0) {
+ FTS_ERROR("PROC_WRITE_DATA write error");
+ goto proc_write_err;
+ }
+ break;
+
+ case PROC_SET_SLAVE_ADDR:
+ break;
+
+ case PROC_HW_RESET:
+ snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1);
+ tmp[buflen - 1] = '\0';
+ if (strncmp(tmp, "focal_driver", 12) == 0) {
+ FTS_INFO("APK execute HW Reset");
+ fts_reset_proc(0);
+ }
+ break;
+
+ case PROC_SET_BOOT_MODE:
+ FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]);
+ if (0 == writebuf[1]) {
+ ts_data->fw_is_running = true;
+ } else {
+ ts_data->fw_is_running = false;
+ }
+ break;
+
+ case PROC_ENTER_TEST_ENVIRONMENT:
+ FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]);
+ if (0 == writebuf[1]) {
+ fts_enter_test_environment(0);
+ } else {
+ fts_enter_test_environment(1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ret = buflen;
+proc_write_err:
+ if ((buflen > PROC_BUF_SIZE) && writebuf) {
+ kfree(writebuf);
+ writebuf = NULL;
+ }
+ return ret;
+}
+
+static int fts_debug_read(
+ char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ int ret = 0;
+ int num_read_chars = 0;
+ int buflen = count;
+ u8 *readbuf = NULL;
+ u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+ struct fts_ts_data *ts_data = fts_data;
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+ if ((buflen <= 0) || (buflen > PAGE_SIZE)) {
+ FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (buflen > PROC_BUF_SIZE) {
+ readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+ if (NULL == readbuf) {
+ FTS_ERROR("apk proc wirte buf zalloc fail");
+ return -ENOMEM;
+ }
+ } else {
+ readbuf = tmpbuf;
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(1);
+#endif
+
+ switch (proc->opmode) {
+ case PROC_READ_REGISTER:
+ num_read_chars = 1;
+ ret = fts_read_reg(proc->cmd[0], &readbuf[0]);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_REGISTER read error");
+ goto proc_read_err;
+ }
+ break;
+
+ case PROC_WRITE_REGISTER:
+ break;
+
+ case PROC_READ_DATA:
+ num_read_chars = buflen;
+ ret = fts_read(NULL, 0, readbuf, num_read_chars);
+ if (ret < 0) {
+ FTS_ERROR("PROC_READ_DATA read error");
+ goto proc_read_err;
+ }
+ break;
+
+ case PROC_WRITE_DATA:
+ break;
+
+ default:
+ break;
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(0);
+#endif
+
+ if (copy_to_user(buff, readbuf, num_read_chars)) {
+ FTS_ERROR("copy to user error");
+ ret = -EFAULT;
+ goto proc_read_err;
+ }
+
+ ret = num_read_chars;
+proc_read_err:
+ if ((buflen > PROC_BUF_SIZE) && readbuf) {
+ kfree(readbuf);
+ readbuf = NULL;
+ }
+ return ret;
+}
+#endif
+
+int fts_create_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+ proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops);
+ if (NULL == proc->proc_entry) {
+ FTS_ERROR("create proc entry fail");
+ return -ENOMEM;
+ }
+#else
+ proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL);
+ if (NULL == proc->proc_entry) {
+ FTS_ERROR("create proc entry fail");
+ return -ENOMEM;
+ }
+ proc->proc_entry->write_proc = fts_debug_write;
+ proc->proc_entry->read_proc = fts_debug_read;
+#endif
+
+ FTS_INFO("Create proc entry success!");
+ return 0;
+}
+
+void fts_release_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+ struct ftxxxx_proc *proc = &ts_data->proc;
+
+ if (proc->proc_entry) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+ proc_remove(proc->proc_entry);
+#else
+ remove_proc_entry(PROC_NAME, NULL);
+#endif
+ }
+}
+
+/************************************************************************
+ * sysfs interface
+ ***********************************************************************/
+/* fts_hw_reset interface */
+static ssize_t fts_hw_reset_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+ ssize_t count = 0;
+
+ mutex_lock(&input_dev->mutex);
+ fts_reset_proc(0);
+ count = snprintf(buf, PAGE_SIZE, "hw reset executed\n");
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_hw_reset_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+/* fts_irq interface */
+static ssize_t fts_irq_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t count = 0;
+ struct irq_desc *desc = irq_to_desc(fts_data->irq);
+
+ count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth);
+
+ return count;
+}
+
+static ssize_t fts_irq_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ FTS_INFO("enable irq");
+ fts_irq_enable();
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ FTS_INFO("disable irq");
+ fts_irq_disable();
+ }
+ mutex_unlock(&input_dev->mutex);
+ return count;
+}
+
+/* fts_boot_mode interface */
+static ssize_t fts_bootmode_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ FTS_FUNC_ENTER();
+ mutex_lock(&input_dev->mutex);
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ FTS_INFO("[EX-FUN]set to boot mode");
+ fts_data->fw_is_running = false;
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ FTS_INFO("[EX-FUN]set to fw mode");
+ fts_data->fw_is_running = true;
+ }
+ mutex_unlock(&input_dev->mutex);
+ FTS_FUNC_EXIT();
+
+ return count;
+}
+
+static ssize_t fts_bootmode_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t count = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ FTS_FUNC_ENTER();
+ mutex_lock(&input_dev->mutex);
+ if (true == fts_data->fw_is_running) {
+ count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n");
+ } else {
+ count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n");
+ }
+ mutex_unlock(&input_dev->mutex);
+ FTS_FUNC_EXIT();
+
+ return count;
+}
+
+/* fts_tpfwver interface */
+static ssize_t fts_tpfwver_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fts_ts_data *ts_data = fts_data;
+ struct input_dev *input_dev = ts_data->input_dev;
+ ssize_t num_read_chars = 0;
+ u8 fwver = 0;
+
+ mutex_lock(&input_dev->mutex);
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(1);
+#endif
+ fts_read_reg(FTS_REG_FW_VER, &fwver);
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(0);
+#endif
+ if ((fwver == 0xFF) || (fwver == 0x00))
+ num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n");
+ else
+ num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver);
+
+ mutex_unlock(&input_dev->mutex);
+ return num_read_chars;
+}
+
+static ssize_t fts_tpfwver_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+/* fts_rw_reg */
+static ssize_t fts_tprwreg_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count;
+ int i;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (rw_op.len < 0) {
+ count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
+ } else if (rw_op.len == 1) {
+ if (RWREG_OP_READ == rw_op.type) {
+ if (rw_op.res == 0) {
+ count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
+ } else {
+ count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
+ }
+ } else {
+ if (rw_op.res == 0) {
+ count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val);
+ } else {
+ count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
+ }
+ }
+ } else {
+ if (RWREG_OP_READ == rw_op.type) {
+ count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
+ count += snprintf(buf + count, PAGE_SIZE, "Result: ");
+ if (rw_op.res) {
+ count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
+ } else {
+ if (rw_op.opbuf) {
+ for (i = 0; i < rw_op.len; i++) {
+ count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+ }
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ }
+ }
+ } else {
+ count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
+ count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
+ if (rw_op.opbuf) {
+ for (i = 1; i < rw_op.len; i++) {
+ count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+ }
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ }
+ if (rw_op.res) {
+ count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
+ } else {
+ count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
+ }
+ }
+ /*if (rw_op.opbuf) {
+ * kfree(rw_op.opbuf);
+ * rw_op.opbuf = NULL;
+ *}
+ */
+ }
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static int shex_to_int(const char *hex_buf, int size)
+{
+ int i;
+ int base = 1;
+ int value = 0;
+ char single;
+
+ for (i = size - 1; i >= 0; i--) {
+ single = hex_buf[i];
+
+ if ((single >= '0') && (single <= '9')) {
+ value += (single - '0') * base;
+ } else if ((single >= 'a') && (single <= 'z')) {
+ value += (single - 'a' + 10) * base;
+ } else if ((single >= 'A') && (single <= 'Z')) {
+ value += (single - 'A' + 10) * base;
+ } else {
+ return -EINVAL;
+ }
+
+ base *= 16;
+ }
+
+ return value;
+}
+
+
+static u8 shex_to_u8(const char *hex_buf, int size)
+{
+ return (u8)shex_to_int(hex_buf, size);
+}
+/*
+ * Format buf:
+ * [0]: '0' write, '1' read(reserved)
+ * [1-2]: addr, hex
+ * [3-4]: length, hex
+ * [5-6]...[n-(n+1)]: data, hex
+ */
+static int fts_parse_buf(const char *buf, size_t cmd_len)
+{
+ int length;
+ int i;
+ char *tmpbuf;
+
+ rw_op.reg = shex_to_u8(buf + 1, 2);
+ length = shex_to_int(buf + 3, 2);
+
+ if (buf[0] == '1') {
+ rw_op.len = length;
+ rw_op.type = RWREG_OP_READ;
+ FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
+ } else {
+ if (cmd_len < (length * 2 + 5)) {
+ pr_err("data invalided!\n");
+ return -EINVAL;
+ }
+ FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length);
+
+ /* first byte is the register addr */
+ rw_op.type = RWREG_OP_WRITE;
+ rw_op.len = length + 1;
+ }
+
+ if (rw_op.len > 0) {
+ tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
+ if (!tmpbuf) {
+ FTS_ERROR("allocate memory failed!\n");
+ return -ENOMEM;
+ }
+
+ if (RWREG_OP_WRITE == rw_op.type) {
+ tmpbuf[0] = rw_op.reg & 0xFF;
+ FTS_DEBUG("write buffer: ");
+ for (i = 1; i < rw_op.len; i++) {
+ tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
+ FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
+ }
+ }
+ rw_op.opbuf = tmpbuf;
+ }
+
+ return rw_op.len;
+}
+
+static ssize_t fts_tprwreg_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct input_dev *input_dev = fts_data->input_dev;
+ ssize_t cmd_length = 0;
+
+ mutex_lock(&input_dev->mutex);
+ cmd_length = count - 1;
+
+ if (rw_op.opbuf) {
+ kfree(rw_op.opbuf);
+ rw_op.opbuf = NULL;
+ }
+
+ FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
+ /* compatible old ops */
+ if (2 == cmd_length) {
+ rw_op.type = RWREG_OP_READ;
+ rw_op.len = 1;
+ rw_op.reg = shex_to_int(buf, 2);
+ } else if (4 == cmd_length) {
+ rw_op.type = RWREG_OP_WRITE;
+ rw_op.len = 1;
+ rw_op.reg = shex_to_int(buf, 2);
+ rw_op.val = shex_to_int(buf + 2, 2);
+ } else if (cmd_length < 5) {
+ FTS_ERROR("Invalid cmd buffer");
+ mutex_unlock(&input_dev->mutex);
+ return -EINVAL;
+ } else {
+ rw_op.len = fts_parse_buf(buf, cmd_length);
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(1);
+#endif
+ if (rw_op.len < 0) {
+ FTS_ERROR("cmd buffer error!");
+ goto exit;
+ }
+
+ if (RWREG_OP_READ == rw_op.type) {
+ if (rw_op.len == 1) {
+ u8 reg, val;
+ reg = rw_op.reg & 0xFF;
+ rw_op.res = fts_read_reg(reg, &val);
+ rw_op.val = val;
+ } else {
+ char reg;
+ reg = rw_op.reg & 0xFF;
+
+ rw_op.res = fts_read(®, 1, rw_op.opbuf, rw_op.len);
+ }
+
+ if (rw_op.res < 0) {
+ FTS_ERROR("Could not read 0x%02x", rw_op.reg);
+ } else {
+ FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
+ rw_op.res = 0;
+ }
+
+ } else {
+ if (rw_op.len == 1) {
+ u8 reg, val;
+ reg = rw_op.reg & 0xFF;
+ val = rw_op.val & 0xFF;
+ rw_op.res = fts_write_reg(reg, val);
+ } else {
+ rw_op.res = fts_write(rw_op.opbuf, rw_op.len);
+ }
+ if (rw_op.res < 0) {
+ FTS_ERROR("Could not write 0x%02x", rw_op.reg);
+
+ } else {
+ FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
+ rw_op.res = 0;
+ }
+ }
+
+exit:
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(0);
+#endif
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+/* fts_upgrade_bin interface */
+static ssize_t fts_fwupgradebin_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return -EPERM;
+}
+
+static ssize_t fts_fwupgradebin_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char fwname[FILE_NAME_LENGTH] = { 0 };
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
+ FTS_ERROR("fw bin name's length(%d) fail", (int)count);
+ return -EINVAL;
+ }
+
+ memset(fwname, 0, sizeof(fwname));
+ snprintf(fwname, FILE_NAME_LENGTH, "%s", buf);
+ fwname[count - 1] = '\0';
+
+ FTS_INFO("upgrade with bin file through sysfs node");
+ mutex_lock(&input_dev->mutex);
+ fts_upgrade_bin(fwname, 0);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+/* fts_force_upgrade interface */
+static ssize_t fts_fwforceupg_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return -EPERM;
+}
+
+static ssize_t fts_fwforceupg_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char fwname[FILE_NAME_LENGTH];
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
+ FTS_ERROR("fw bin name's length(%d) fail", (int)count);
+ return -EINVAL;
+ }
+
+ memset(fwname, 0, sizeof(fwname));
+ snprintf(fwname, FILE_NAME_LENGTH, "%s", buf);
+ fwname[count - 1] = '\0';
+
+ FTS_INFO("force upgrade through sysfs node");
+ mutex_lock(&input_dev->mutex);
+ fts_upgrade_bin(fwname, 1);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+/* fts_driver_info interface */
+static ssize_t fts_driverinfo_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct fts_ts_platform_data *pdata = ts_data->pdata;
+ struct input_dev *input_dev = ts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ count += snprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION);
+
+ count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n",
+ pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max);
+
+ count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number);
+
+ count += snprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n",
+ pdata->reset_gpio, pdata->irq_gpio, ts_data->irq);
+
+ count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n",
+ ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_driverinfo_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_dumpreg_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ u8 val = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(1);
+#endif
+ fts_read_reg(FTS_REG_POWER_MODE, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_FW_VER, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_LIC_VER, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_VENDOR_ID, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_LCD_BUSY_NUM, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_GESTURE_EN, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_INT_CNT, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val);
+
+ fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val);
+ count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val);
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_proc_busy(0);
+#endif
+
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_dumpreg_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_tpbuf_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ int i = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n");
+ for (i = 0; i < fts_data->pnt_buf_size; i++) {
+ count += snprintf(buf + count, PAGE_SIZE, "%02x ",
+ fts_data->point_buf[i]);
+ }
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_tpbuf_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+/* fts_log_level interface */
+static ssize_t fts_log_level_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n",
+ fts_data->log_level);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_log_level_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+
+ FTS_FUNC_ENTER();
+ mutex_lock(&input_dev->mutex);
+ sscanf(buf, "%d", &value);
+ FTS_DEBUG("log level:%d->%d", fts_data->log_level, value);
+ fts_data->log_level = value;
+ mutex_unlock(&input_dev->mutex);
+ FTS_FUNC_EXIT();
+
+ return count;
+}
+
+/* get the fw version example:cat fw_version */
+static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store);
+
+/* read and write register(s)
+* All data type is **HEX**
+* Single Byte:
+* read: echo 88 > rw_reg ---read register 0x88
+* write: echo 8807 > rw_reg ---write 0x07 into register 0x88
+* Multi-bytes:
+* [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex]
+* rw-flag: 0, write; 1, read
+* read: echo 10005 > rw_reg ---read reg 0x00-0x05
+* write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05
+* Get result:
+* cat rw_reg
+*/
+static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store);
+/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */
+static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store);
+static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_store);
+static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store);
+static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store);
+static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store);
+static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store);
+static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store);
+static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store);
+static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store);
+
+/* add your attr in here*/
+static struct attribute *fts_attributes[] = {
+ &dev_attr_fts_fw_version.attr,
+ &dev_attr_fts_rw_reg.attr,
+ &dev_attr_fts_dump_reg.attr,
+ &dev_attr_fts_upgrade_bin.attr,
+ &dev_attr_fts_force_upgrade.attr,
+ &dev_attr_fts_driver_info.attr,
+ &dev_attr_fts_hw_reset.attr,
+ &dev_attr_fts_irq.attr,
+ &dev_attr_fts_boot_mode.attr,
+ &dev_attr_fts_touch_point.attr,
+ &dev_attr_fts_log_level.attr,
+ NULL
+};
+
+static struct attribute_group fts_attribute_group = {
+ .attrs = fts_attributes
+};
+
+int fts_create_sysfs(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+
+ ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group);
+ if (ret) {
+ FTS_ERROR("[EX]: sysfs_create_group() failed!!");
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+ return -ENOMEM;
+ } else {
+ FTS_INFO("[EX]: sysfs_create_group() succeeded!!");
+ }
+
+ return ret;
+}
+
+int fts_remove_sysfs(struct fts_ts_data *ts_data)
+{
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+ return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
new file mode 100644
index 0000000..4727744
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
@@ -0,0 +1,310 @@
+/*
+ *
+ * FocalTech ftxxxx TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_ex_mode.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-31
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* 2.Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* 3.Private enumerations, structures and unions using typedef
+*****************************************************************************/
+enum _ex_mode {
+ MODE_GLOVE = 0,
+ MODE_COVER,
+ MODE_CHARGER,
+};
+
+/*****************************************************************************
+* 4.Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* 5.Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* 6.Static function prototypes
+*******************************************************************************/
+static int fts_ex_mode_switch(enum _ex_mode mode, u8 value)
+{
+ int ret = 0;
+ u8 m_val = 0;
+
+ if (value)
+ m_val = 0x01;
+ else
+ m_val = 0x00;
+
+ switch (mode) {
+ case MODE_GLOVE:
+ ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val);
+ if (ret < 0) {
+ FTS_ERROR("MODE_GLOVE switch to %d fail", m_val);
+ }
+ break;
+
+ case MODE_COVER:
+ ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val);
+ if (ret < 0) {
+ FTS_ERROR("MODE_COVER switch to %d fail", m_val);
+ }
+ break;
+
+ case MODE_CHARGER:
+ ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val);
+ if (ret < 0) {
+ FTS_ERROR("MODE_CHARGER switch to %d fail", m_val);
+ }
+ break;
+
+ default:
+ FTS_ERROR("mode(%d) unsupport", mode);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t fts_glove_mode_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ u8 val = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct input_dev *input_dev = ts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val);
+ count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n",
+ ts_data->glove_mode ? "On" : "Off");
+ count += snprintf(buf + count, PAGE_SIZE, "Glove Reg(0xC0):%d\n", val);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_glove_mode_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!ts_data->glove_mode) {
+ FTS_DEBUG("enter glove mode");
+ ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+ if (ret >= 0) {
+ ts_data->glove_mode = ENABLE;
+ }
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (ts_data->glove_mode) {
+ FTS_DEBUG("exit glove mode");
+ ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE);
+ if (ret >= 0) {
+ ts_data->glove_mode = DISABLE;
+ }
+ }
+ }
+
+ FTS_DEBUG("glove mode:%d", ts_data->glove_mode);
+ return count;
+}
+
+
+static ssize_t fts_cover_mode_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ u8 val = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct input_dev *input_dev = ts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ fts_read_reg(FTS_REG_COVER_MODE_EN, &val);
+ count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n",
+ ts_data->cover_mode ? "On" : "Off");
+ count += snprintf(buf + count, PAGE_SIZE, "Cover Reg(0xC1):%d\n", val);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_cover_mode_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!ts_data->cover_mode) {
+ FTS_DEBUG("enter cover mode");
+ ret = fts_ex_mode_switch(MODE_COVER, ENABLE);
+ if (ret >= 0) {
+ ts_data->cover_mode = ENABLE;
+ }
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (ts_data->cover_mode) {
+ FTS_DEBUG("exit cover mode");
+ ret = fts_ex_mode_switch(MODE_COVER, DISABLE);
+ if (ret >= 0) {
+ ts_data->cover_mode = DISABLE;
+ }
+ }
+ }
+
+ FTS_DEBUG("cover mode:%d", ts_data->cover_mode);
+ return count;
+}
+
+static ssize_t fts_charger_mode_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ u8 val = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct input_dev *input_dev = ts_data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+ fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
+ count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n",
+ ts_data->charger_mode ? "On" : "Off");
+ count += snprintf(buf + count, PAGE_SIZE, "Charger Reg(0x8B):%d\n", val);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_charger_mode_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!ts_data->charger_mode) {
+ FTS_DEBUG("enter charger mode");
+ ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+ if (ret >= 0) {
+ ts_data->charger_mode = ENABLE;
+ }
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (ts_data->charger_mode) {
+ FTS_DEBUG("exit charger mode");
+ ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE);
+ if (ret >= 0) {
+ ts_data->charger_mode = DISABLE;
+ }
+ }
+ }
+
+ FTS_DEBUG("charger mode:%d", ts_data->glove_mode);
+ return count;
+}
+
+
+/* read and write charger mode
+ * read example: cat fts_glove_mode ---read glove mode
+ * write example:echo 1 > fts_glove_mode ---write glove mode to 01
+ */
+static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR,
+ fts_glove_mode_show, fts_glove_mode_store);
+
+static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR,
+ fts_cover_mode_show, fts_cover_mode_store);
+
+static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR,
+ fts_charger_mode_show, fts_charger_mode_store);
+
+static struct attribute *fts_touch_mode_attrs[] = {
+ &dev_attr_fts_glove_mode.attr,
+ &dev_attr_fts_cover_mode.attr,
+ &dev_attr_fts_charger_mode.attr,
+ NULL,
+};
+
+static struct attribute_group fts_touch_mode_group = {
+ .attrs = fts_touch_mode_attrs,
+};
+
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data)
+{
+ if (ts_data->glove_mode) {
+ fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+ }
+
+ if (ts_data->cover_mode) {
+ fts_ex_mode_switch(MODE_COVER, ENABLE);
+ }
+
+ if (ts_data->charger_mode) {
+ fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+ }
+
+ return 0;
+}
+
+int fts_ex_mode_init(struct fts_ts_data *ts_data)
+{
+ int ret = 0;
+
+ ts_data->glove_mode = DISABLE;
+ ts_data->cover_mode = DISABLE;
+ ts_data->charger_mode = DISABLE;
+
+ ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+ if (ret < 0) {
+ FTS_ERROR("create sysfs(ex_mode) fail");
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+ return ret;
+ } else {
+ FTS_DEBUG("create sysfs(ex_mode) succeedfully");
+ }
+
+ return 0;
+}
+
+int fts_ex_mode_exit(struct fts_ts_data *ts_data)
+{
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+ return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c
new file mode 100644
index 0000000..51c5622
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c
@@ -0,0 +1,2002 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_flash.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#include "focaltech_flash.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_FW_REQUEST_SUPPORT 1
+/* Example: focaltech_ts_fw_tianma.bin */
+#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_"
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+u8 fw_file[1] = {
+0,
+};
+
+struct upgrade_module module_list[] = {
+ {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)},
+};
+
+struct upgrade_func *upgrade_func_list[] = {
+ &upgrade_func_ft5452,
+};
+
+struct fts_upgrade *fwupgrade;
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static bool fts_fwupg_check_state(
+ struct fts_upgrade *upg, enum FW_STATUS rstate);
+
+/************************************************************************
+* Name: fts_fwupg_get_boot_state
+* Brief: read boot id(rom/pram/bootloader), confirm boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_get_boot_state(
+ struct fts_upgrade *upg,
+ enum FW_STATUS *fw_sts)
+{
+ int ret = 0;
+ u8 cmd[4] = { 0 };
+ u32 cmd_len = 0;
+ u8 val[2] = { 0 };
+ struct ft_chip_t *ids = NULL;
+
+ FTS_INFO("**********read boot id**********");
+ if ((!upg) || (!upg->func) || (!upg->ts_data) || (!fw_sts)) {
+ FTS_ERROR("upg/func/ts_data/fw_sts is null");
+ return -EINVAL;
+ }
+
+ if (upg->func->hid_supported)
+ fts_hid2std();
+
+ cmd[0] = FTS_CMD_START1;
+ cmd[1] = FTS_CMD_START2;
+ ret = fts_write(cmd, 2);
+ if (ret < 0) {
+ FTS_ERROR("write 55 aa cmd fail");
+ return ret;
+ }
+
+ msleep(FTS_CMD_START_DELAY);
+ cmd[0] = FTS_CMD_READ_ID;
+ cmd[1] = cmd[2] = cmd[3] = 0x00;
+ if (fts_data->ic_info.is_incell)
+ cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+ else
+ cmd_len = FTS_CMD_READ_ID_LEN;
+ ret = fts_read(cmd, cmd_len, val, 2);
+ if (ret < 0) {
+ FTS_ERROR("write 90 cmd fail");
+ return ret;
+ }
+ FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]);
+
+ ids = &upg->ts_data->ic_info.ids;
+ if ((val[0] == ids->rom_idh) && (val[1] == ids->rom_idl)) {
+ FTS_INFO("tp run in romboot");
+ *fw_sts = FTS_RUN_IN_ROM;
+ } else if ((val[0] == ids->pb_idh) && (val[1] == ids->pb_idl)) {
+ FTS_INFO("tp run in pramboot");
+ *fw_sts = FTS_RUN_IN_PRAM;
+ } else if ((val[0] == ids->bl_idh) && (val[1] == ids->bl_idl)) {
+ FTS_INFO("tp run in bootloader");
+ *fw_sts = FTS_RUN_IN_BOOTLOADER;
+ }
+
+ return 0;
+}
+
+static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ u8 reg = FTS_REG_UPGRADE;
+
+ FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment");
+ if (upg && upg->func && upg->func->is_reset_register_BC) {
+ reg = FTS_REG_UPGRADE2;
+ }
+
+ ret = fts_write_reg(reg, FTS_UPGRADE_AA);
+ if (ret < 0) {
+ FTS_ERROR("write FC=0xAA fail");
+ return ret;
+ }
+ msleep(FTS_DELAY_UPGRADE_AA);
+
+ ret = fts_write_reg(reg, FTS_UPGRADE_55);
+ if (ret < 0) {
+ FTS_ERROR("write FC=0x55 fail");
+ return ret;
+ }
+
+ msleep(FTS_DELAY_UPGRADE_RESET);
+ return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_to_romboot
+* Brief: reset to romboot, to load pramboot
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_reset_to_romboot(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ int i = 0;
+ u8 cmd = FTS_CMD_RESET;
+ enum FW_STATUS state = FTS_RUN_IN_ERROR;
+
+ ret = fts_write(&cmd, 1);
+ if (ret < 0) {
+ FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+ return ret;
+ }
+ mdelay(10);
+
+ for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+ ret = fts_fwupg_get_boot_state(upg, &state);
+ if (FTS_RUN_IN_ROM == state)
+ break;
+ mdelay(5);
+ }
+ if (i >= FTS_UPGRADE_LOOP) {
+ FTS_ERROR("reset to romboot fail");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static u16 fts_crc16_calc_host(u8 *pbuf, u16 length)
+{
+ u16 ecc = 0;
+ u16 i = 0;
+ u16 j = 0;
+
+ for ( i = 0; i < length; i += 2 ) {
+ ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1]));
+ for (j = 0; j < 16; j ++) {
+ if (ecc & 0x01)
+ ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF);
+ else
+ ecc >>= 1;
+ }
+ }
+
+ return ecc;
+}
+
+static u16 fts_pram_ecc_calc_host(u8 *pbuf, u16 length)
+{
+ return fts_crc16_calc_host(pbuf, length);
+}
+
+static int fts_pram_ecc_cal_algo(
+ struct fts_upgrade *upg,
+ u32 start_addr,
+ u32 ecc_length)
+{
+ int ret = 0;
+ int i = 0;
+ int ecc = 0;
+ u8 val[2] = { 0 };
+ u8 tmp = 0;
+ u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 };
+
+ FTS_INFO("read out pramboot checksum");
+ if ((!upg) || (!upg->func)) {
+ FTS_ERROR("upg/func is null");
+ return -EINVAL;
+ }
+
+ cmd[0] = FTS_ROMBOOT_CMD_ECC;
+ cmd[1] = BYTE_OFF_16(start_addr);
+ cmd[2] = BYTE_OFF_8(start_addr);
+ cmd[3] = BYTE_OFF_0(start_addr);
+ cmd[4] = BYTE_OFF_16(ecc_length);
+ cmd[5] = BYTE_OFF_8(ecc_length);
+ cmd[6] = BYTE_OFF_0(ecc_length);
+ ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN);
+ if (ret < 0) {
+ FTS_ERROR("write pramboot ecc cal cmd fail");
+ return ret;
+ }
+
+ cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH;
+ for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) {
+ msleep(1);
+ ret = fts_read(cmd, 1, val, 1);
+ if (ret < 0) {
+ FTS_ERROR("ecc_finish read cmd fail");
+ return ret;
+ }
+ if (upg->func->new_return_value_from_ic) {
+ tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5;
+ } else {
+ tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_00;
+ }
+ if (tmp == val[0])
+ break;
+ }
+ if (i >= 100) {
+ FTS_ERROR("wait ecc finish fail");
+ return -EIO;
+ }
+
+ cmd[0] = FTS_ROMBOOT_CMD_ECC_READ;
+ ret = fts_read(cmd, 1, val, 2);
+ if (ret < 0) {
+ FTS_ERROR("read pramboot ecc fail");
+ return ret;
+ }
+
+ ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF;
+ return ecc;
+}
+
+static int fts_pram_ecc_cal_xor(void)
+{
+ int ret = 0;
+ u8 reg_val = 0;
+
+ FTS_INFO("read out pramboot checksum");
+
+ ret = fts_read_reg(FTS_ROMBOOT_CMD_ECC, ®_val);
+ if (ret < 0) {
+ FTS_ERROR("read pramboot ecc fail");
+ return ret;
+ }
+
+ return (int)reg_val;
+}
+
+static int fts_pram_ecc_cal(struct fts_upgrade *upg, u32 saddr, u32 len)
+{
+ if ((!upg) || (!upg->func)) {
+ FTS_ERROR("upg/func is null");
+ return -EINVAL;
+ }
+
+ if (ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) {
+ return fts_pram_ecc_cal_algo(upg, saddr, len);
+ } else {
+ return fts_pram_ecc_cal_xor();
+ }
+}
+
+static int fts_pram_write_buf(struct fts_upgrade *upg, u8 *buf, u32 len)
+{
+ int ret = 0;
+ u32 i = 0;
+ u32 j = 0;
+ u32 offset = 0;
+ u32 remainder = 0;
+ u32 packet_number;
+ u32 packet_len = 0;
+ u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+ u8 ecc_tmp = 0;
+ int ecc_in_host = 0;
+
+ FTS_INFO("write pramboot to pram");
+ if ((!upg) || (!upg->func) || !buf) {
+ FTS_ERROR("upg/func/buf is null");
+ return -EINVAL;
+ }
+
+ FTS_INFO("pramboot len=%d", len);
+ if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) {
+ FTS_ERROR("pramboot length(%d) fail", len);
+ return -EINVAL;
+ }
+
+ packet_number = len / FTS_FLASH_PACKET_LENGTH;
+ remainder = len % FTS_FLASH_PACKET_LENGTH;
+ if (remainder > 0)
+ packet_number++;
+ packet_len = FTS_FLASH_PACKET_LENGTH;
+
+ packet_buf[0] = FTS_ROMBOOT_CMD_WRITE;
+ for (i = 0; i < packet_number; i++) {
+ offset = i * FTS_FLASH_PACKET_LENGTH;
+ packet_buf[1] = BYTE_OFF_16(offset);
+ packet_buf[2] = BYTE_OFF_8(offset);
+ packet_buf[3] = BYTE_OFF_0(offset);
+
+ /* last packet */
+ if ((i == (packet_number - 1)) && remainder)
+ packet_len = remainder;
+
+ packet_buf[4] = BYTE_OFF_8(packet_len);
+ packet_buf[5] = BYTE_OFF_0(packet_len);
+
+ for (j = 0; j < packet_len; j++) {
+ packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j];
+ if (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) {
+ ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j];
+ }
+ }
+
+ ret = fts_write(packet_buf, packet_len + FTS_CMD_WRITE_LEN);
+ if (ret < 0) {
+ FTS_ERROR("pramboot write data(%d) fail", i);
+ return ret;
+ }
+ }
+
+ if (ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) {
+ ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len);
+ } else {
+ ecc_in_host = (int)ecc_tmp;
+ }
+
+ return ecc_in_host;
+}
+
+static int fts_pram_start(void)
+{
+ u8 cmd = FTS_ROMBOOT_CMD_START_APP;
+ int ret = 0;
+
+ FTS_INFO("remap to start pramboot");
+
+ ret = fts_write(&cmd, 1);
+ if (ret < 0) {
+ FTS_ERROR("write start pram cmd fail");
+ return ret;
+ }
+ msleep(FTS_DELAY_PRAMBOOT_START);
+
+ return 0;
+}
+
+static int fts_pram_write_remap(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ int ecc_in_host = 0;
+ int ecc_in_tp = 0;
+ u8 *pb_buf = NULL;
+ u32 pb_len = 0;
+
+ FTS_INFO("write pram and remap");
+ if (!upg || !upg->func || !upg->func->pramboot) {
+ FTS_ERROR("upg/func/pramboot is null");
+ return -EINVAL;
+ }
+
+ if (upg->func->pb_length < FTS_MIN_LEN) {
+ FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length);
+ return -EINVAL;
+ }
+
+ pb_buf = upg->func->pramboot;
+ pb_len = upg->func->pb_length;
+
+ /* write pramboot to pram */
+ ecc_in_host = fts_pram_write_buf(upg, pb_buf, pb_len);
+ if (ecc_in_host < 0) {
+ FTS_ERROR( "write pramboot fail");
+ return ecc_in_host;
+ }
+
+ /* read out checksum */
+ ecc_in_tp = fts_pram_ecc_cal(upg, 0, pb_len);
+ if (ecc_in_tp < 0) {
+ FTS_ERROR( "read pramboot ecc fail");
+ return ecc_in_tp;
+ }
+
+ FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+ /* pramboot checksum != fw checksum, upgrade fail */
+ if (ecc_in_host != ecc_in_tp) {
+ FTS_ERROR("pramboot ecc check fail");
+ return -EIO;
+ }
+
+ /*start pram*/
+ ret = fts_pram_start();
+ if (ret < 0) {
+ FTS_ERROR("pram start fail");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fts_pram_init(void)
+{
+ int ret = 0;
+ u8 reg_val = 0;
+ u8 wbuf[3] = { 0 };
+
+ FTS_INFO("pramboot initialization");
+
+ /* read flash ID */
+ wbuf[0] = FTS_CMD_FLASH_TYPE;
+ ret = fts_read(wbuf, 1, ®_val, 1);
+ if (ret < 0) {
+ FTS_ERROR("read flash type fail");
+ return ret;
+ }
+
+ /* set flash clk */
+ wbuf[0] = FTS_CMD_FLASH_TYPE;
+ wbuf[1] = reg_val;
+ wbuf[2] = 0x00;
+ ret = fts_write(wbuf, 3);
+ if (ret < 0) {
+ FTS_ERROR("write flash type fail");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fts_pram_write_init(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ bool state = 0;
+ enum FW_STATUS status = FTS_RUN_IN_ERROR;
+
+ FTS_INFO("**********pram write and init**********");
+ if ((NULL == upg) || (NULL == upg->func)) {
+ FTS_ERROR("upgrade/func is null");
+ return -EINVAL;
+ }
+
+ if (!upg->func->pramboot_supported) {
+ FTS_ERROR("ic not support pram");
+ return -EINVAL;
+ }
+
+ FTS_DEBUG("check whether tp is in romboot or not ");
+ /* need reset to romboot when non-romboot state */
+ ret = fts_fwupg_get_boot_state(upg, &status);
+ if (status != FTS_RUN_IN_ROM) {
+ if (FTS_RUN_IN_PRAM == status) {
+ FTS_INFO("tp is in pramboot, need send reset cmd before upgrade");
+ ret = fts_pram_init();
+ if (ret < 0) {
+ FTS_ERROR("pramboot(before) init fail");
+ return ret;
+ }
+ }
+
+ FTS_INFO("tp isn't in romboot, need send reset to romboot");
+ ret = fts_fwupg_reset_to_romboot(upg);
+ if (ret < 0) {
+ FTS_ERROR("reset to romboot fail");
+ return ret;
+ }
+ }
+
+ /* check the length of the pramboot */
+ ret = fts_pram_write_remap(upg);
+ if (ret < 0) {
+ FTS_ERROR("pram write fail, ret=%d", ret);
+ return ret;
+ }
+
+ FTS_DEBUG("after write pramboot, confirm run in pramboot");
+ state = fts_fwupg_check_state(upg, FTS_RUN_IN_PRAM);
+ if (!state) {
+ FTS_ERROR("not in pramboot");
+ return -EIO;
+ }
+
+ ret = fts_pram_init();
+ if (ret < 0) {
+ FTS_ERROR("pramboot init fail");
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool fts_fwupg_check_fw_valid(void)
+{
+ int ret = 0;
+
+ ret = fts_wait_tp_to_valid();
+ if (ret < 0) {
+ FTS_INFO("tp fw invaild");
+ return false;
+ }
+
+ FTS_INFO("tp fw vaild");
+ return true;
+}
+
+/************************************************************************
+* Name: fts_fwupg_check_state
+* Brief: confirm tp run in which mode: romboot/pramboot/bootloader
+* Input:
+* Output:
+* Return: return true if state is match, otherwise return false
+***********************************************************************/
+static bool fts_fwupg_check_state(
+ struct fts_upgrade *upg, enum FW_STATUS rstate)
+{
+ int ret = 0;
+ int i = 0;
+ enum FW_STATUS cstate = FTS_RUN_IN_ERROR;
+
+ for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+ ret = fts_fwupg_get_boot_state(upg, &cstate);
+ /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */
+ if (cstate == rstate)
+ return true;
+ msleep(FTS_DELAY_READ_ID);
+ }
+
+ return false;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_in_boot
+* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_reset_in_boot(void)
+{
+ int ret = 0;
+ u8 cmd = FTS_CMD_RESET;
+
+ FTS_INFO("reset in boot environment");
+ ret = fts_write(&cmd, 1);
+ if (ret < 0) {
+ FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+ return ret;
+ }
+
+ msleep(FTS_DELAY_UPGRADE_RESET);
+ return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_enter_into_boot
+* Brief: enter into boot environment, ready for upgrade
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_enter_into_boot(void)
+{
+ int ret = 0;
+ bool fwvalid = false;
+ bool state = false;
+ struct fts_upgrade *upg = fwupgrade;
+
+ FTS_INFO("***********enter into pramboot/bootloader***********");
+ if ((!upg) || (NULL == upg->func)) {
+ FTS_ERROR("upgrade/func is null");
+ return -EINVAL;
+ }
+
+ fwvalid = fts_fwupg_check_fw_valid();
+ if (fwvalid) {
+ ret = fts_fwupg_reset_to_boot(upg);
+ if (ret < 0) {
+ FTS_ERROR("enter into romboot/bootloader fail");
+ return ret;
+ }
+ } else if (upg->func->read_boot_id_need_reset) {
+ ret = fts_fwupg_reset_in_boot();
+ if (ret < 0) {
+ FTS_ERROR("reset before read boot id when fw invalid fail");
+ return ret;
+ }
+ }
+
+ if (upg->func->pramboot_supported) {
+ FTS_INFO("pram supported, write pramboot and init");
+ /* pramboot */
+ ret = fts_pram_write_init(upg);
+ if (ret < 0) {
+ FTS_ERROR("pram write_init fail");
+ return ret;
+ }
+ } else {
+ FTS_DEBUG("pram not supported, confirm in bootloader");
+ /* bootloader */
+ state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER);
+ if (!state) {
+ FTS_ERROR("fw not in bootloader, fail");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_check_flash_status
+ * Brief: read status from tp
+ * Input: flash_status: correct value from tp
+ * retries: read retry times
+ * retries_delay: retry delay
+ * Output:
+ * Return: return true if flash status check pass, otherwise return false
+***********************************************************************/
+static bool fts_fwupg_check_flash_status(
+ u16 flash_status,
+ int retries,
+ int retries_delay)
+{
+ int ret = 0;
+ int i = 0;
+ u8 cmd = 0;
+ u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+ u16 read_status = 0;
+
+ for (i = 0; i < retries; i++) {
+ cmd = FTS_CMD_FLASH_STATUS;
+ ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+ read_status = (((u16)val[0]) << 8) + val[1];
+ if (flash_status == read_status) {
+ /* FTS_DEBUG("[UPGRADE]flash status ok"); */
+ return true;
+ }
+ /* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */
+ msleep(retries_delay);
+ }
+
+ return false;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_erase
+ * Brief: erase flash area
+ * Input: delay - delay after erase
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_erase(u32 delay)
+{
+ int ret = 0;
+ u8 cmd = 0;
+ bool flag = false;
+
+ FTS_INFO("**********erase now**********");
+
+ /*send to erase flash*/
+ cmd = FTS_CMD_ERASE_APP;
+ ret = fts_write(&cmd, 1);
+ if (ret < 0) {
+ FTS_ERROR("erase cmd fail");
+ return ret;
+ }
+ msleep(delay);
+
+ /* read status 0xF0AA: success */
+ flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK,
+ FTS_RETRIES_REASE,
+ FTS_RETRIES_DELAY_REASE);
+ if (!flag) {
+ FTS_ERROR("ecc flash status check fail");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_ecc_cal
+ * Brief: calculate and get ecc from tp
+ * Input: saddr - start address need calculate ecc
+ * len - length need calculate ecc
+ * Output:
+ * Return: return data ecc of tp if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_ecc_cal(u32 saddr, u32 len)
+{
+ int ret = 0;
+ u32 i = 0;
+ u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 };
+ u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+ int ecc = 0;
+ int ecc_len = 0;
+ u32 packet_num = 0;
+ u32 packet_len = 0;
+ u32 remainder = 0;
+ u32 addr = 0;
+ u32 offset = 0;
+ struct fts_upgrade *upg = fwupgrade;
+
+ FTS_INFO( "**********read out checksum**********");
+ if ((NULL == upg) || (NULL == upg->func)) {
+ FTS_ERROR("upgrade/func is null");
+ return -EINVAL;
+ }
+
+ /* check sum init */
+ wbuf[0] = FTS_CMD_ECC_INIT;
+ ret = fts_write(wbuf, 1);
+ if (ret < 0) {
+ FTS_ERROR("ecc init cmd write fail");
+ return ret;
+ }
+
+ packet_num = len / FTS_MAX_LEN_ECC_CALC;
+ remainder = len % FTS_MAX_LEN_ECC_CALC;
+ if (remainder)
+ packet_num++;
+
+ packet_len = FTS_MAX_LEN_ECC_CALC;
+ FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder);
+
+ /* send commond to start checksum */
+ wbuf[0] = FTS_CMD_ECC_CAL;
+ for (i = 0; i < packet_num; i++) {
+ offset = FTS_MAX_LEN_ECC_CALC * i;
+ addr = saddr + offset;
+ wbuf[1] = BYTE_OFF_16(addr);
+ wbuf[2] = BYTE_OFF_8(addr);
+ wbuf[3] = BYTE_OFF_0(addr);
+
+ if ((i == (packet_num - 1)) && remainder)
+ packet_len = remainder;
+ wbuf[4] = BYTE_OFF_8(packet_len);
+ wbuf[5] = BYTE_OFF_0(packet_len);
+
+ FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len);
+ ret = fts_write(wbuf, FTS_CMD_ECC_CAL_LEN);
+ if (ret < 0) {
+ FTS_ERROR("ecc calc cmd write fail");
+ return ret;
+ }
+
+ msleep(packet_len / 256);
+
+ /* read status if check sum is finished */
+ ret = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ECC_OK,
+ FTS_RETRIES_ECC_CAL,
+ FTS_RETRIES_DELAY_ECC_CAL);
+ if (ret < 0) {
+ FTS_ERROR("ecc flash status read fail");
+ return ret;
+ }
+ }
+
+ ecc_len = 1;
+ if (ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) {
+ ecc_len = 2;
+ }
+
+ /* read out check sum */
+ wbuf[0] = FTS_CMD_ECC_READ;
+ ret = fts_read(wbuf, 1, val, ecc_len);
+ if (ret < 0) {
+ FTS_ERROR( "ecc read cmd write fail");
+ return ret;
+ }
+
+ if (ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) {
+ ecc = (int)((u16)(val[0] << 8) + val[1]);
+ } else {
+ ecc = (int)val[0];
+ }
+
+ return ecc;
+}
+
+/************************************************************************
+ * Name: fts_flash_write_buf
+ * Brief: write buf data to flash address
+ * Input: saddr - start address data write to flash
+ * buf - data buffer
+ * len - data length
+ * delay - delay after write
+ * Output:
+ * Return: return data ecc of host if success, otherwise return error code
+ ***********************************************************************/
+int fts_flash_write_buf(
+ u32 saddr,
+ u8 *buf,
+ u32 len,
+ u32 delay)
+{
+ int ret = 0;
+ u32 i = 0;
+ u32 j = 0;
+ u32 packet_number = 0;
+ u32 packet_len = 0;
+ u32 addr = 0;
+ u32 offset = 0;
+ u32 remainder = 0;
+ u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+ u8 ecc_tmp = 0;
+ int ecc_in_host = 0;
+ u8 cmd = 0;
+ u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+ u16 read_status = 0;
+ u16 wr_ok = 0;
+ struct fts_upgrade *upg = fwupgrade;
+
+ FTS_INFO( "**********write data to flash**********");
+ if ((!upg) || (!upg->func || !buf || !len)) {
+ FTS_ERROR("upgrade/func/buf/len is invalid");
+ return -EINVAL;
+ }
+
+ FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len);
+ packet_number = len / FTS_FLASH_PACKET_LENGTH;
+ remainder = len % FTS_FLASH_PACKET_LENGTH;
+ if (remainder > 0)
+ packet_number++;
+ packet_len = FTS_FLASH_PACKET_LENGTH;
+ FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder);
+
+ packet_buf[0] = FTS_CMD_WRITE;
+ for (i = 0; i < packet_number; i++) {
+ offset = i * FTS_FLASH_PACKET_LENGTH;
+ addr = saddr + offset;
+ packet_buf[1] = BYTE_OFF_16(addr);
+ packet_buf[2] = BYTE_OFF_8(addr);
+ packet_buf[3] = BYTE_OFF_0(addr);
+
+ /* last packet */
+ if ((i == (packet_number - 1)) && remainder)
+ packet_len = remainder;
+
+ packet_buf[4] = BYTE_OFF_8(packet_len);
+ packet_buf[5] = BYTE_OFF_0(packet_len);
+
+ for (j = 0; j < packet_len; j++) {
+ packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j];
+ ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j];
+ }
+
+ ret = fts_write(packet_buf, packet_len + FTS_CMD_WRITE_LEN);
+ if (ret < 0) {
+ FTS_ERROR("app write fail");
+ return ret;
+ }
+ mdelay(delay);
+
+ /* read status */
+ wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len;
+ for (j = 0; j < FTS_RETRIES_WRITE; j++) {
+ cmd = FTS_CMD_FLASH_STATUS;
+ ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+ read_status = (((u16)val[0]) << 8) + val[1];
+ /* FTS_INFO("%x %x", wr_ok, read_status); */
+ if (wr_ok == read_status) {
+ break;
+ }
+ mdelay(FTS_RETRIES_DELAY_WRITE);
+ }
+ }
+
+ ecc_in_host = (int)ecc_tmp;
+ if (ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) {
+ ecc_in_host = (int)fts_crc16_calc_host(buf, len);
+ }
+
+ return ecc_in_host;
+}
+
+/************************************************************************
+ * Name: fts_flash_read_buf
+ * Brief: read data from flash
+ * Input: saddr - start address data write to flash
+ * buf - buffer to store data read from flash
+ * len - read length
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ *
+ * Warning: can't call this function directly, need call in boot environment
+ ***********************************************************************/
+static int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len)
+{
+ int ret = 0;
+ u32 i = 0;
+ u32 packet_number = 0;
+ u32 packet_len = 0;
+ u32 addr = 0;
+ u32 offset = 0;
+ u32 remainder = 0;
+ u8 wbuf[FTS_CMD_READ_LEN] = { 0 };
+
+ if ((NULL == buf) || (0 == len)) {
+ FTS_ERROR("buf is NULL or len is 0");
+ return -EINVAL;
+ }
+
+ packet_number = len / FTS_FLASH_PACKET_LENGTH;
+ remainder = len % FTS_FLASH_PACKET_LENGTH;
+ if (remainder > 0) {
+ packet_number++;
+ }
+ packet_len = FTS_FLASH_PACKET_LENGTH;
+ FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder);
+
+ wbuf[0] = FTS_CMD_READ;
+ for (i = 0; i < packet_number; i++) {
+ offset = i * FTS_FLASH_PACKET_LENGTH;
+ addr = saddr + offset;
+ wbuf[1] = BYTE_OFF_16(addr);
+ wbuf[2] = BYTE_OFF_8(addr);
+ wbuf[3] = BYTE_OFF_0(addr);
+
+ /* last packet */
+ if ((i == (packet_number - 1)) && remainder)
+ packet_len = remainder;
+
+ ret = fts_write(wbuf, FTS_CMD_READ_LEN);
+ if (ret < 0) {
+ FTS_ERROR("pram/bootloader write 03 command fail");
+ return ret;
+ }
+
+ msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */
+ ret = fts_read(NULL, 0, buf + offset, packet_len);
+ if (ret < 0) {
+ FTS_ERROR("pram/bootloader read 03 command fail");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************
+ * Name: fts_flash_read
+ * Brief:
+ * Input: addr - address of flash
+ * len - length of read
+ * Output: buf - data read from flash
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+static int fts_flash_read(u32 addr, u8 *buf, u32 len)
+{
+ int ret = 0;
+
+ FTS_INFO("***********read flash***********");
+ if ((NULL == buf) || (0 == len)) {
+ FTS_ERROR("buf is NULL or len is 0");
+ return -EINVAL;
+ }
+
+ ret = fts_fwupg_enter_into_boot();
+ if (ret < 0) {
+ FTS_ERROR("enter into pramboot/bootloader fail");
+ goto read_flash_err;
+ }
+
+ ret = fts_flash_read_buf(addr, buf, len);
+ if (ret < 0) {
+ FTS_ERROR("read flash fail");
+ goto read_flash_err;
+ }
+
+read_flash_err:
+ /* reset to normal boot */
+ ret = fts_fwupg_reset_in_boot();
+ if (ret < 0) {
+ FTS_ERROR("reset to normal boot fail");
+ }
+ return ret;
+}
+
+static int fts_read_file(char *file_name, u8 **file_buf)
+{
+ int ret = 0;
+ char file_path[FILE_NAME_LENGTH] = { 0 };
+ struct file *filp = NULL;
+ struct inode *inode;
+ mm_segment_t old_fs;
+ loff_t pos;
+ loff_t file_len = 0;
+
+ if ((NULL == file_name) || (NULL == file_buf)) {
+ FTS_ERROR("filename/filebuf is NULL");
+ return -EINVAL;
+ }
+
+ snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name);
+ filp = filp_open(file_path, O_RDONLY, 0);
+ if (IS_ERR(filp)) {
+ FTS_ERROR("open %s file fail", file_path);
+ return -ENOENT;
+ }
+
+#if 1
+ inode = filp->f_inode;
+#else
+ /* reserved for linux earlier verion */
+ inode = filp->f_dentry->d_inode;
+#endif
+
+ file_len = inode->i_size;
+ *file_buf = (u8 *)vmalloc(file_len);
+ if (NULL == *file_buf) {
+ FTS_ERROR("file buf malloc fail");
+ filp_close(filp, NULL);
+ return -ENOMEM;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ ret = vfs_read(filp, *file_buf, file_len , &pos);
+ if (ret < 0)
+ FTS_ERROR("read file fail");
+ FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos);
+ filp_close(filp, NULL);
+ set_fs(old_fs);
+
+ return ret;
+}
+
+int fts_upgrade_bin(char *fw_name, bool force)
+{
+ int ret = 0;
+ u32 fw_file_len = 0;
+ u8 *fw_file_buf = NULL;
+ struct fts_upgrade *upg = fwupgrade;
+
+ FTS_INFO("start upgrade with fw bin");
+ if ((!upg) || (!upg->func) || !upg->ts_data) {
+ FTS_ERROR("upgrade/func/ts_data is null");
+ return -EINVAL;
+ }
+
+ upg->ts_data->fw_loading = 1;
+ fts_irq_disable();
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(DISABLE);
+#endif
+
+ ret = fts_read_file(fw_name, &fw_file_buf);
+ if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) {
+ FTS_ERROR("read fw bin file(sdcard) fail, len:%d", fw_file_len);
+ goto err_bin;
+ }
+
+ fw_file_len = ret;
+ FTS_INFO("fw bin file len:%d", fw_file_len);
+ if (force) {
+ if (upg->func->force_upgrade) {
+ ret = upg->func->force_upgrade(fw_file_buf, fw_file_len);
+ } else {
+ FTS_INFO("force_upgrade function is null, no upgrade");
+ goto err_bin;
+ }
+ } else {
+#if FTS_AUTO_LIC_UPGRADE_EN
+ if (upg->func->lic_upgrade) {
+ ret = upg->func->lic_upgrade(fw_file_buf, fw_file_len);
+ } else {
+ FTS_INFO("lic_upgrade function is null, no upgrade");
+ }
+#endif
+ if (upg->func->upgrade) {
+ ret = upg->func->upgrade(fw_file_buf, fw_file_len);
+ } else {
+ FTS_INFO("upgrade function is null, no upgrade");
+ }
+ }
+
+ if (ret < 0) {
+ FTS_ERROR("upgrade fw bin failed");
+ fts_fwupg_reset_in_boot();
+ goto err_bin;
+ }
+
+ FTS_INFO("upgrade fw bin success");
+ ret = 0;
+
+err_bin:
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(ENABLE);
+#endif
+ fts_irq_enable();
+ upg->ts_data->fw_loading = 0;
+
+ if (fw_file_buf) {
+ vfree(fw_file_buf);
+ fw_file_buf = NULL;
+ }
+ return ret;
+}
+
+int fts_enter_test_environment(bool test_state)
+{
+ return 0;
+}
+#if FTS_AUTO_LIC_UPGRADE_EN
+static int fts_lic_get_vid_in_tp(u16 *vid)
+{
+ int ret = 0;
+ u8 val[2] = { 0 };
+
+ if (NULL == vid) {
+ FTS_ERROR("vid is NULL");
+ return -EINVAL;
+ }
+
+ ret = fts_read_reg(FTS_REG_VENDOR_ID, &val[0]);
+ if (fts_data->ic_info.is_incell)
+ ret = fts_read_reg(FTS_REG_MODULE_ID, &val[1]);
+ if (ret < 0) {
+ FTS_ERROR("read vid from tp fail");
+ return ret;
+ }
+
+ *vid = *(u16 *)val;
+ return 0;
+}
+
+static int fts_lic_get_vid_in_host(struct fts_upgrade *upg, u16 *vid)
+{
+ u8 val[2] = { 0 };
+ u8 *licbuf = NULL;
+ u32 conf_saddr = 0;
+
+ if (!upg || !upg->func || !upg->lic || !vid) {
+ FTS_ERROR("upgrade/func/get_hlic_ver/lic/vid is null");
+ return -EINVAL;
+ }
+
+ if (upg->lic_length < FTS_MAX_LEN_SECTOR) {
+ FTS_ERROR("lic length(%x) fail", upg->lic_length);
+ return -EINVAL;
+ }
+
+ licbuf = upg->lic;
+ conf_saddr = upg->func->fwcfgoff;
+ val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF];
+ if (fts_data->ic_info.is_incell)
+ val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF];
+
+ *vid = *(u16 *)val;
+ return 0;
+}
+
+static int fts_lic_get_ver_in_tp(u8 *ver)
+{
+ int ret = 0;
+
+ if (NULL == ver) {
+ FTS_ERROR("ver is NULL");
+ return -EINVAL;
+ }
+
+ ret = fts_read_reg(FTS_REG_LIC_VER, ver);
+ if (ret < 0) {
+ FTS_ERROR("read lcd initcode ver from tp fail");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fts_lic_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+ int ret = 0;
+
+ if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) {
+ FTS_ERROR("upgrade/func/get_hlic_ver/lic is null");
+ return -EINVAL;
+ }
+
+ ret = upg->func->get_hlic_ver(upg->lic);
+ if (ret < 0) {
+ FTS_ERROR("get host lcd initial code version fail");
+ return ret;
+ }
+
+ *ver = (u8)ret;
+ return ret;
+}
+
+static bool fts_lic_need_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ u8 initcode_ver_in_tp = 0;
+ u8 initcode_ver_in_host = 0;
+ u16 vid_in_tp = 0;
+ u16 vid_in_host = 0;
+ bool fwvalid = false;
+
+ fwvalid = fts_fwupg_check_fw_valid();
+ if ( !fwvalid) {
+ FTS_INFO("fw is invalid, no upgrade lcd init code");
+ return false;
+ }
+
+ ret = fts_lic_get_vid_in_host(upg, &vid_in_host);
+ if (ret < 0) {
+ FTS_ERROR("vendor id in host invalid");
+ return false;
+ }
+
+ ret = fts_lic_get_vid_in_tp(&vid_in_tp);
+ if (ret < 0) {
+ FTS_ERROR("vendor id in tp invalid");
+ return false;
+ }
+
+ FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host);
+ if (vid_in_tp != vid_in_host) {
+ FTS_INFO("vendor id in tp&host are different, no upgrade lic");
+ return false;
+ }
+
+ ret = fts_lic_get_ver_in_host(upg, &initcode_ver_in_host);
+ if (ret < 0) {
+ FTS_ERROR("init code in host invalid");
+ return false;
+ }
+
+ ret = fts_lic_get_ver_in_tp(&initcode_ver_in_tp);
+ if (ret < 0) {
+ FTS_ERROR("read reg0xE4 fail");
+ return false;
+ }
+
+ FTS_DEBUG("lcd initial code version in tp:%x, host:%x",
+ initcode_ver_in_tp, initcode_ver_in_host);
+ if (0xA5 == initcode_ver_in_tp) {
+ FTS_INFO("lcd init code ver is 0xA5, don't upgade init code");
+ return false;
+ } else if (0xFF == initcode_ver_in_tp) {
+ FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code");
+ return true;
+ } else if (initcode_ver_in_tp < initcode_ver_in_host)
+ return true;
+ else
+ return false;
+}
+
+static int fts_lic_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ bool hlic_upgrade = false;
+ int upgrade_count = 0;
+ u8 ver = 0;
+
+ FTS_INFO("lcd initial code auto upgrade function");
+ if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) {
+ FTS_ERROR("lcd upgrade function is null");
+ return -EINVAL;
+ }
+
+ hlic_upgrade = fts_lic_need_upgrade(upg);
+ FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade);
+ if (hlic_upgrade) {
+ FTS_INFO("lcd initial code need upgrade, upgrade begin...");
+ do {
+ FTS_INFO("lcd initial code upgrade times:%d", upgrade_count);
+ upgrade_count++;
+
+ ret = upg->func->lic_upgrade(upg->lic, upg->lic_length);
+ if (ret < 0) {
+ fts_fwupg_reset_in_boot();
+ } else {
+ fts_lic_get_ver_in_tp(&ver);
+ FTS_INFO("success upgrade to lcd initcode ver:%02x", ver);
+ break;
+ }
+ } while (upgrade_count < 2);
+ } else {
+ FTS_INFO("lcd initial code don't need upgrade");
+ }
+
+ return ret;
+}
+#endif /* FTS_AUTO_LIC_UPGRADE_EN */
+
+
+static int fts_param_get_ver_in_tp(u8 *ver)
+{
+ int ret = 0;
+
+ if (NULL == ver) {
+ FTS_ERROR("ver is NULL");
+ return -EINVAL;
+ }
+
+ ret = fts_read_reg(FTS_REG_IDE_PARA_VER_ID, ver);
+ if (ret < 0) {
+ FTS_ERROR("read fw param ver from tp fail");
+ return ret;
+ }
+
+ if ((0x00 == *ver) || (0xFF == *ver)) {
+ FTS_INFO("param version in tp invalid");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fts_param_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+ if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+ FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL");
+ return -EINVAL;
+ }
+
+ if (upg->fw_length < upg->func->paramcfgveroff) {
+ FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)",
+ upg->fw_length, upg->func->paramcfgveroff);
+ return -EINVAL;
+ }
+
+ FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff);
+ *ver = upg->fw[upg->func->paramcfgveroff];
+
+ if ((0x00 == *ver) || (0xFF == *ver)) {
+ FTS_INFO("param version in host invalid");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * return: < 0 : error
+ * == 0: no ide
+ * == 1: ide
+ */
+static int fts_param_ide_in_host(struct fts_upgrade *upg)
+{
+ u32 off = 0;
+
+ if ((!upg) || (!upg->func) || (!upg->fw)) {
+ FTS_ERROR("fts_data/upgrade/func/fw is NULL");
+ return -EINVAL;
+ }
+
+ if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) {
+ FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE",
+ upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN);
+ return 0;
+ }
+
+ off = upg->func->paramcfgoff;
+ if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) {
+ FTS_INFO("fw in host is IDE version");
+ return 1;
+ }
+
+ FTS_INFO("fw in host isn't IDE version");
+ return 0;
+}
+
+/*
+ * return: < 0 : error
+ * 0 : no ide
+ * 1 : ide
+ */
+static int fts_param_ide_in_tp(u8 *val)
+{
+ int ret = 0;
+
+ ret = fts_read_reg(FTS_REG_IDE_PARA_STATUS, val);
+ if (ret < 0) {
+ FTS_ERROR("read IDE PARAM STATUS in tp fail");
+ return ret;
+ }
+
+ if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) {
+ FTS_INFO("fw in tp is IDE version");
+ return 1;
+ }
+
+ FTS_INFO("fw in tp isn't IDE version");
+ return 0;
+}
+
+/************************************************************************
+ * fts_param_need_upgrade - check fw paramcfg need upgrade or not
+ *
+ * Return: < 0 : error if paramcfg need upgrade
+ * 0 : no need upgrade
+ * 1 : need upgrade app + param
+ * 2 : need upgrade param
+ ***********************************************************************/
+static int fts_param_need_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ u8 val = 0;
+ int ide_in_host = 0;
+ int ide_in_tp = 0;
+ u8 ver_in_host = 0;
+ u8 ver_in_tp = 0;
+ bool fwvalid = false;
+
+ fwvalid = fts_fwupg_check_fw_valid();
+ if ( !fwvalid) {
+ FTS_INFO("fw is invalid, upgrade app+param");
+ return 1;
+ }
+
+ ide_in_host = fts_param_ide_in_host(upg);
+ if (ide_in_host < 0) {
+ FTS_INFO("fts_param_ide_in_host fail");
+ return ide_in_host;
+ }
+
+ ide_in_tp = fts_param_ide_in_tp(&val);
+ if (ide_in_tp < 0) {
+ FTS_INFO("fts_param_ide_in_tp fail");
+ return ide_in_tp;
+ }
+
+ if ((0 == ide_in_host) && (0 == ide_in_tp)) {
+ FTS_INFO("fw in host&tp are both no ide");
+ return 0;
+ } else if (ide_in_host != ide_in_tp) {
+ FTS_INFO("fw in host&tp not equal, need upgrade app+param");
+ return 1;
+ } else if ((1 == ide_in_host) && (1 == ide_in_tp)) {
+ FTS_INFO("fw in host&tp are both ide");
+ if ((val & 0x7F) != 0x00) {
+ FTS_INFO("param invalid, need upgrade param");
+ return 2;
+ }
+
+ ret = fts_param_get_ver_in_host(upg, &ver_in_host);
+ if (ret < 0) {
+ FTS_ERROR("param version in host invalid");
+ return ret;
+ }
+
+ ret = fts_param_get_ver_in_tp(&ver_in_tp);
+ if (ret < 0) {
+ FTS_ERROR("get IDE param ver in tp fail");
+ return ret;
+ }
+
+ FTS_INFO("fw paramcfg version in tp:%x, host:%x",
+ ver_in_tp, ver_in_host);
+ if (ver_in_tp != ver_in_host) {
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int fts_fwupg_get_ver_in_tp(u8 *ver)
+{
+ int ret = 0;
+
+ if (NULL == ver) {
+ FTS_ERROR("ver is NULL");
+ return -EINVAL;
+ }
+
+ ret = fts_read_reg(FTS_REG_FW_VER, ver);
+ if (ret < 0) {
+ FTS_ERROR("read fw ver from tp fail");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fts_fwupg_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+ if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+ FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL");
+ return -EINVAL;
+ }
+
+ if (upg->fw_length < upg->func->fwveroff) {
+ FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)",
+ upg->fw_length, upg->func->fwveroff);
+ return -EINVAL;
+ }
+
+ FTS_INFO("fw version offset:0x%x", upg->func->fwveroff);
+ *ver = upg->fw[upg->func->fwveroff];
+ return 0;
+}
+
+static bool fts_fwupg_need_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ bool fwvalid = false;
+ u8 fw_ver_in_host = 0;
+ u8 fw_ver_in_tp = 0;
+
+ fwvalid = fts_fwupg_check_fw_valid();
+ if (fwvalid) {
+ ret = fts_fwupg_get_ver_in_host(upg, &fw_ver_in_host);
+ if (ret < 0) {
+ FTS_ERROR("get fw ver in host fail");
+ return false;
+ }
+
+ ret = fts_fwupg_get_ver_in_tp(&fw_ver_in_tp);
+ if (ret < 0) {
+ FTS_ERROR("get fw ver in tp fail");
+ return false;
+ }
+
+ FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host);
+ if (fw_ver_in_tp != fw_ver_in_host) {
+ return true;
+ }
+ } else {
+ FTS_INFO("fw invalid, need upgrade fw");
+ return true;
+ }
+
+ return false;
+}
+
+/************************************************************************
+ * Name: fts_fw_upgrade
+ * Brief: fw upgrade main entry, run in following steps
+ * 1. check fw version(A6), not equal, will upgrade app(+param)
+ * 2. if fw version equal, will check ide, will upgrade app(+param)
+ * in the follow situation
+ * a. host&tp IDE's type are not equal, will upgrade app+param
+ * b. host&tp are both IDE's type, and param's version are not
+ * equal, will upgrade param
+ * Input:
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ bool upgrade_flag = false;
+ int upgrade_count = 0;
+ u8 ver = 0;
+
+ FTS_INFO("fw auto upgrade function");
+ if ((NULL == upg) || (NULL == upg->func)) {
+ FTS_ERROR("upg/upg->func is null");
+ return -EINVAL;
+ }
+
+ upgrade_flag = fts_fwupg_need_upgrade(upg);
+ FTS_INFO("fw upgrade flag:%d", upgrade_flag);
+ do {
+ upgrade_count++;
+ if (upgrade_flag) {
+ FTS_INFO("upgrade fw app(times:%d)", upgrade_count);
+ if (upg->func->upgrade) {
+ ret = upg->func->upgrade(upg->fw, upg->fw_length);
+ if (ret < 0) {
+ fts_fwupg_reset_in_boot();
+ } else {
+ fts_fwupg_get_ver_in_tp(&ver);
+ FTS_INFO("success upgrade to fw version %02x", ver);
+ break;
+ }
+ } else {
+ FTS_ERROR("upgrade func/upgrade is null, return immediately");
+ ret = -ENODATA;
+ break;
+ }
+ } else {
+ if (upg->func->param_upgrade) {
+ ret = fts_param_need_upgrade(upg);
+ if (ret <= 0) {
+ FTS_INFO("param don't need upgrade");
+ break;
+ } else if (1 == ret) {
+ FTS_INFO("force upgrade fw app(times:%d)", upgrade_count);
+ if (upg->func->upgrade) {
+ ret = upg->func->upgrade(upg->fw, upg->fw_length);
+ if (ret < 0) {
+ fts_fwupg_reset_in_boot();
+ } else {
+ break;
+ }
+ }
+ } else if (2 == ret) {
+ FTS_INFO("upgrade param area(times:%d)", upgrade_count);
+ ret = upg->func->param_upgrade(upg->fw, upg->fw_length);
+ if (ret < 0) {
+ fts_fwupg_reset_in_boot();
+ } else {
+ fts_param_get_ver_in_tp(&ver);
+ FTS_INFO("success upgrade to fw param version %02x", ver);
+ break;
+ }
+ } else
+ break;
+ } else {
+ break;
+ }
+ }
+ } while (upgrade_count < 2);
+
+ return ret;
+}
+
+/************************************************************************
+ * fts_fwupg_auto_upgrade - upgrade main entry
+ ***********************************************************************/
+static void fts_fwupg_auto_upgrade(struct fts_upgrade *upg)
+{
+ int ret = 0;
+
+ FTS_INFO("********************FTS enter upgrade********************");
+ if (!upg || !upg->ts_data) {
+ FTS_ERROR("upg/ts_data is null");
+ return ;
+ }
+
+ ret = fts_fwupg_upgrade(upg);
+ if (ret < 0)
+ FTS_ERROR("**********tp fw(app/param) upgrade failed**********");
+ else
+ FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********");
+
+#if FTS_AUTO_LIC_UPGRADE_EN
+ ret = fts_lic_upgrade(upg);
+ if (ret < 0)
+ FTS_ERROR("**********lcd init code upgrade failed**********");
+ else
+ FTS_INFO("**********lcd init code no upgrade/upgrade success**********");
+#endif
+
+ FTS_INFO("********************FTS exit upgrade********************");
+}
+
+static int fts_fwupg_get_vendorid(struct fts_upgrade *upg, int *vid)
+{
+ int ret = 0;
+ bool fwvalid = false;
+ u8 vendor_id = 0;
+ u8 module_id = 0;
+ u32 fwcfg_addr = 0;
+ u8 cfgbuf[FTS_HEADER_LEN] = { 0 };
+
+ FTS_INFO("read vendor id from tp");
+ if ((!upg) || (!upg->func) || (!upg->ts_data) || (!vid)) {
+ FTS_ERROR("upgrade/func/ts_data/vid is null");
+ return -EINVAL;
+ }
+
+ fwvalid = fts_fwupg_check_fw_valid();
+ if (fwvalid) {
+ ret = fts_read_reg(FTS_REG_VENDOR_ID, &vendor_id);
+ if (upg->ts_data->ic_info.is_incell)
+ ret = fts_read_reg(FTS_REG_MODULE_ID, &module_id);
+ } else {
+ fwcfg_addr = upg->func->fwcfgoff;
+ ret = fts_flash_read(fwcfg_addr, cfgbuf, FTS_HEADER_LEN);
+ vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF];
+ if (upg->ts_data->ic_info.is_incell) {
+ if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] +
+ cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF)
+ module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF];
+ }
+ }
+
+ if (ret < 0) {
+ FTS_ERROR("fail to get vendor id from tp");
+ return ret;
+ }
+
+ *vid = (int)((module_id << 8) + vendor_id);
+ return 0;
+}
+
+static int fts_fwupg_get_module_info(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ int i = 0;
+ struct upgrade_module *info = &module_list[0];
+
+ if (!upg || !upg->ts_data) {
+ FTS_ERROR("upg/ts_data is null");
+ return -EINVAL;
+ }
+
+ if (FTS_GET_MODULE_NUM > 1) {
+ /* support multi modules, must read correct module id(vendor id) */
+ ret = fts_fwupg_get_vendorid(upg, &upg->module_id);
+ if (ret < 0) {
+ FTS_ERROR("get vendor id failed");
+ return ret;
+ }
+ FTS_INFO("module id:%04x", upg->module_id);
+ for (i = 0; i < FTS_GET_MODULE_NUM; i++) {
+ info = &module_list[i];
+ if (upg->module_id == info->id) {
+ FTS_INFO("module id match, get module info pass");
+ break;
+ }
+ }
+ if (i >= FTS_GET_MODULE_NUM) {
+ FTS_ERROR("no module id match, don't get file");
+ return -ENODATA;
+ }
+ }
+
+ upg->module_info = info;
+ return 0;
+}
+
+static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ const struct firmware *fw = NULL;
+ u8 *tmpbuf = NULL;
+ char fwname[FILE_NAME_LENGTH] = { 0 };
+
+ if (!upg || !upg->ts_data || !upg->ts_data->dev) {
+ FTS_ERROR("upg/ts_data/dev is null");
+ return -EINVAL;
+ }
+
+ snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \
+ FTS_FW_NAME_PREX_WITH_REQUEST, \
+ upg->module_info->vendor_name);
+
+ ret = request_firmware(&fw, fwname, upg->ts_data->dev);
+ if (0 == ret) {
+ FTS_INFO("firmware(%s) request successfully", fwname);
+ tmpbuf = vmalloc(fw->size);
+ if (NULL == tmpbuf) {
+ FTS_ERROR("fw buffer vmalloc fail");
+ ret = -ENOMEM;
+ } else {
+ memcpy(tmpbuf, fw->data, fw->size);
+ upg->fw = tmpbuf;
+ upg->fw_length = fw->size;
+ upg->fw_from_request = 1;
+ }
+ } else {
+ FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
+ }
+
+ if (fw != NULL) {
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ return ret;
+}
+
+static int fts_get_fw_file_via_i(struct fts_upgrade *upg)
+{
+ upg->fw = upg->module_info->fw_file;
+ upg->fw_length = upg->module_info->fw_len;
+ upg->fw_from_request = 0;
+
+ return 0;
+}
+
+/*****************************************************************************
+ * Name: fts_fwupg_get_fw_file
+ * Brief: get fw image/file,
+ * If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_-
+ * MODULE_ID/FTS_MODULE_NAME;
+ * If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F-
+ * TS_MODULE_ID; will use module id to distingwish different modules;
+ * If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT
+ * =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt-
+ * ech_ts_fw_" & FTS_VENDOR_NAME;
+ *
+ * If have flash, module_id=vendor_id, If non-flash,module_id need
+ * transfer from LCD driver(gpio or lcm_id or ...);
+ * Input:
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ *****************************************************************************/
+static int fts_fwupg_get_fw_file(struct fts_upgrade *upg)
+{
+ int ret = 0;
+ bool get_fw_i_flag = false;
+
+ FTS_DEBUG("get upgrade fw file");
+ if (!upg || !upg->ts_data) {
+ FTS_ERROR("upg/ts_data is null");
+ return -EINVAL;
+ }
+
+ ret = fts_fwupg_get_module_info(upg);
+ if ((ret < 0) || (!upg->module_info)) {
+ FTS_ERROR("get module info fail");
+ return ret;
+ }
+
+ if (FTS_FW_REQUEST_SUPPORT) {
+ ret = fts_get_fw_file_via_request_firmware(upg);
+ if (ret != 0) {
+ get_fw_i_flag = true;
+ }
+ } else {
+ get_fw_i_flag = true;
+ }
+
+ if (get_fw_i_flag) {
+ ret = fts_get_fw_file_via_i(upg);
+ }
+
+ upg->lic = upg->fw;
+ upg->lic_length = upg->fw_length;
+
+ FTS_INFO("upgrade fw file len:%d", upg->fw_length);
+ if ((upg->fw_length < FTS_MIN_LEN)
+ || (upg->fw_length > FTS_MAX_LEN_FILE)) {
+ FTS_ERROR("fw file len(%d) fail", upg->fw_length);
+ return -ENODATA;
+ }
+
+ return ret;
+}
+
+static void fts_fwupg_init_ic_detail(struct fts_upgrade *upg)
+{
+ if (upg && upg->func && upg->func->init) {
+ upg->func->init(upg->fw, upg->fw_length);
+ }
+}
+
+/*****************************************************************************
+ * Name: fts_fwupg_work
+ * Brief: 1. get fw image/file
+ * 2. ic init if have
+ * 3. call upgrade main function(fts_fwupg_auto_upgrade)
+ * Input:
+ * Output:
+ * Return:
+ *****************************************************************************/
+static void fts_fwupg_work(struct work_struct *work)
+{
+ int ret = 0;
+ struct fts_upgrade *upg = fwupgrade;
+
+#if !FTS_AUTO_UPGRADE_EN
+ FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on");
+ return ;
+#endif
+
+ FTS_INFO("fw upgrade work function");
+ if (!upg || !upg->ts_data) {
+ FTS_ERROR("upg/ts_data is null");
+ return ;
+ }
+
+ upg->ts_data->fw_loading = 1;
+ fts_irq_disable();
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(DISABLE);
+#endif
+
+ /* get fw */
+ ret = fts_fwupg_get_fw_file(upg);
+ if (ret < 0) {
+ FTS_ERROR("get file fail, can't upgrade");
+ } else {
+ /* ic init if have */
+ fts_fwupg_init_ic_detail(upg);
+ /* run auto upgrade */
+ fts_fwupg_auto_upgrade(upg);
+ }
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_switch(ENABLE);
+#endif
+ fts_irq_enable();
+ upg->ts_data->fw_loading = 0;
+}
+
+int fts_fwupg_init(struct fts_ts_data *ts_data)
+{
+ int i = 0;
+ int j = 0;
+ int ic_stype = 0;
+ struct upgrade_func *func = upgrade_func_list[0];
+ int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]);
+
+ FTS_INFO("fw upgrade init function");
+
+ if (!ts_data || !ts_data->ts_workqueue) {
+ FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function");
+ return -EINVAL;
+ }
+
+ if (0 == func_count) {
+ FTS_ERROR("no upgrade function in tp driver");
+ return -ENODATA;
+ }
+
+ fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL);
+ if (NULL == fwupgrade) {
+ FTS_ERROR("malloc memory for upgrade fail");
+ return -ENOMEM;
+ }
+
+ ic_stype = ts_data->ic_info.ids.type;
+ if (1 == func_count) {
+ fwupgrade->func = func;
+ } else {
+ for (i = 0; i < func_count; i++) {
+ func = upgrade_func_list[i];
+ for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) {
+ if (0 == func->ctype[j])
+ break;
+ else if (func->ctype[j] == ic_stype) {
+ FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]);
+ fwupgrade->func = func;
+ }
+ }
+ }
+ }
+
+ if (NULL == fwupgrade->func) {
+ FTS_ERROR("no upgrade function match, can't upgrade");
+ kfree(fwupgrade);
+ fwupgrade = NULL;
+ return -ENODATA;
+ }
+
+ fwupgrade->ts_data = ts_data;
+ INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work);
+ queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work);
+
+ return 0;
+}
+
+int fts_fwupg_exit(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+ if (fwupgrade) {
+ if (fwupgrade->fw_from_request) {
+ vfree(fwupgrade->fw);
+ fwupgrade->fw = NULL;
+ }
+
+ kfree(fwupgrade);
+ fwupgrade = NULL;
+ }
+ FTS_FUNC_EXIT();
+ return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h
new file mode 100644
index 0000000..398787e
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h
@@ -0,0 +1,205 @@
+/************************************************************************
+* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved.
+*
+* File Name: focaltech_flash.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-07
+*
+* Abstract:
+*
+************************************************************************/
+#ifndef __LINUX_FOCALTECH_FLASH_H__
+#define __LINUX_FOCALTECH_FLASH_H__
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_CMD_RESET 0x07
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR 0xAD
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN 4
+#define FTS_ROMBOOT_CMD_WRITE 0xAE
+#define FTS_ROMBOOT_CMD_START_APP 0x08
+#define FTS_DELAY_PRAMBOOT_START 10
+#define FTS_ROMBOOT_CMD_ECC 0xCC
+#define FTS_PRAM_SADDR 0x000000
+#define FTS_DRAM_SADDR 0xD00000
+
+#define FTS_CMD_READ 0x03
+#define FTS_CMD_READ_DELAY 1
+#define FTS_CMD_READ_LEN 4
+#define FTS_CMD_FLASH_TYPE 0x05
+#define FTS_CMD_FLASH_MODE 0x09
+#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A
+#define FLASH_MODE_UPGRADE_VALUE 0x0B
+#define FLASH_MODE_LIC_VALUE 0x0C
+#define FLASH_MODE_PARAM_VALUE 0x0D
+#define FTS_CMD_ERASE_APP 0x61
+#define FTS_REASE_APP_DELAY 1350
+#define FTS_ERASE_SECTOR_DELAY 60
+#define FTS_RETRIES_REASE 50
+#define FTS_RETRIES_DELAY_REASE 200
+#define FTS_CMD_FLASH_STATUS 0x6A
+#define FTS_CMD_FLASH_STATUS_LEN 2
+#define FTS_CMD_FLASH_STATUS_NOP 0x0000
+#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055
+#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA
+#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000
+#define FTS_CMD_ECC_INIT 0x64
+#define FTS_CMD_ECC_CAL 0x65
+#define FTS_CMD_ECC_CAL_LEN 6
+#define FTS_RETRIES_ECC_CAL 10
+#define FTS_RETRIES_DELAY_ECC_CAL 50
+#define FTS_CMD_ECC_READ 0x66
+#define FTS_CMD_DATA_LEN 0xB0
+#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A
+#define FTS_CMD_DATA_LEN_LEN 4
+#define FTS_CMD_WRITE 0xBF
+#define FTS_RETRIES_WRITE 100
+#define FTS_RETRIES_DELAY_WRITE 1
+#define FTS_CMD_WRITE_LEN 6
+#define FTS_DELAY_READ_ID 20
+#define FTS_DELAY_UPGRADE_RESET 80
+#define PRAMBOOT_MIN_SIZE 0x120
+#define PRAMBOOT_MAX_SIZE (64*1024)
+#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */
+#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */
+#define FTS_MIN_LEN 0x120
+#define FTS_MAX_LEN_FILE (128 * 1024)
+#define FTS_MAX_LEN_APP (64 * 1024)
+#define FTS_MAX_LEN_SECTOR (4 * 1024)
+#define FTS_CONIFG_VENDORID_OFF 0x04
+#define FTS_CONIFG_MODULEID_OFF 0x1E
+#define FTS_CONIFG_PROJECTID_OFF 0x20
+#define FTS_APPINFO_OFF 0x100
+#define FTS_APPINFO_APPLEN_OFF 0x00
+#define FTS_APPINFO_APPLEN2_OFF 0x12
+#define FTS_REG_UPGRADE 0xFC
+#define FTS_REG_UPGRADE2 0xBC
+#define FTS_UPGRADE_AA 0xAA
+#define FTS_UPGRADE_55 0x55
+#define FTS_DELAY_UPGRADE_AA 10
+#define FTS_UPGRADE_LOOP 30
+#define FTS_HEADER_LEN 32
+#define FTS_FW_BIN_FILEPATH "/sdcard/"
+#define FTS_FW_IDE_SIG "IDE_"
+#define FTS_FW_IDE_SIG_LEN 4
+#define MAX_MODULE_VENDOR_NAME_LEN 16
+
+#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7
+#define FTS_ECC_FINISH_TIMEOUT 100
+#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5 0xA5
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00 0x00
+#define FTS_ROMBOOT_CMD_ECC_READ 0xCD
+#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3))
+
+#define FTS_APP_INFO_OFFSET 0x100
+
+enum FW_STATUS {
+ FTS_RUN_IN_ERROR,
+ FTS_RUN_IN_APP,
+ FTS_RUN_IN_ROM,
+ FTS_RUN_IN_PRAM,
+ FTS_RUN_IN_BOOTLOADER,
+};
+
+enum FW_FLASH_MODE {
+ FLASH_MODE_APP,
+ FLASH_MODE_LIC,
+ FLASH_MODE_PARAM,
+ FLASH_MODE_ALL,
+};
+
+enum ECC_CHECK_MODE {
+ ECC_CHECK_MODE_XOR,
+ ECC_CHECK_MODE_CRC16,
+};
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/* IC info */
+struct upgrade_func {
+ u64 ctype[FTX_MAX_COMPATIBLE_TYPE];
+ u32 fwveroff;
+ u32 fwcfgoff;
+ u32 appoff;
+ u32 licoff;
+ u32 paramcfgoff;
+ u32 paramcfgveroff;
+ u32 paramcfg2off;
+ int pram_ecc_check_mode;
+ int fw_ecc_check_mode;
+ bool new_return_value_from_ic;
+ bool appoff_handle_in_ic;
+ bool is_reset_register_BC;
+ bool read_boot_id_need_reset;
+ bool hid_supported;
+ bool pramboot_supported;
+ u8 *pramboot;
+ u32 pb_length;
+ int (*init)(u8 *, u32);
+ int (*upgrade)(u8 *, u32);
+ int (*get_hlic_ver)(u8 *);
+ int (*lic_upgrade)(u8 *, u32);
+ int (*param_upgrade)(u8 *, u32);
+ int (*force_upgrade)(u8 *, u32);
+};
+
+struct upgrade_setting_nf {
+ u8 rom_idh;
+ u8 rom_idl;
+ u16 reserved;
+ u32 app2_offset;
+ u32 ecclen_max;
+ u8 eccok_val;
+ u8 upgsts_boot;
+ u8 delay_init;
+ bool spi_pe;
+ bool half_length;
+ bool fd_check;
+ bool drwr_support;
+};
+
+struct upgrade_module {
+ int id;
+ char vendor_name[MAX_MODULE_VENDOR_NAME_LEN];
+ u8 *fw_file;
+ u32 fw_len;
+};
+
+struct fts_upgrade {
+ struct fts_ts_data *ts_data;
+ struct upgrade_module *module_info;
+ struct upgrade_func *func;
+ struct upgrade_setting_nf *setting_nf;
+ int module_id;
+ bool fw_from_request;
+ u8 *fw;
+ u32 fw_length;
+ u8 *lic;
+ u32 lic_length;
+};
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct upgrade_func upgrade_func_ft5452;
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+int fts_fwupg_reset_in_boot(void);
+int fts_fwupg_enter_into_boot(void);
+int fts_fwupg_erase(u32 delay);
+int fts_fwupg_ecc_cal(u32 saddr, u32 len);
+int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay);
+int fts_fwupg_upgrade(struct fts_upgrade *upg);
+#endif
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile
new file mode 100644
index 0000000..43deb4a
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_upgrade_ft3518.o
\ No newline at end of file
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c
new file mode 100644
index 0000000..a7b0b5b
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c
@@ -0,0 +1,186 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_upgrade_ft5452.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-15
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "../focaltech_flash.h"
+
+/************************************************************************
+* Name: fts_ft5452_upgrade
+* Brief:
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_ft5452_upgrade(u8 *buf, u32 len)
+{
+ int ret = 0;
+ u32 start_addr = 0;
+ u8 cmd[4] = { 0 };
+ int ecc_in_host = 0;
+ int ecc_in_tp = 0;
+
+ int i = 0;
+ u8 wbuf[7] = { 0 };
+ u8 reg_val[4] = {0};
+
+ if (NULL == buf) {
+ FTS_ERROR("fw buf is null");
+ return -EINVAL;
+ }
+
+ if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) {
+ FTS_ERROR("fw buffer len(%x) fail", len);
+ return -EINVAL;
+ }
+
+ /* enter into upgrade environment */
+ ret = fts_fwupg_enter_into_boot();
+ if (ret < 0) {
+ FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret);
+ goto fw_reset;
+ }
+
+ cmd[0] = FTS_CMD_FLASH_MODE;
+ cmd[1] = FLASH_MODE_UPGRADE_VALUE;
+ ret = fts_write(cmd, 2);
+ if (ret < 0) {
+ FTS_ERROR("upgrade mode(09) cmd write fail");
+ goto fw_reset;
+ }
+
+ cmd[0] = FTS_CMD_DATA_LEN;
+ cmd[1] = BYTE_OFF_16(len);
+ cmd[2] = BYTE_OFF_8(len);
+ cmd[3] = BYTE_OFF_0(len);
+ ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN);
+ if (ret < 0) {
+ FTS_ERROR("data len cmd write fail");
+ goto fw_reset;
+ }
+
+ ret = fts_fwupg_erase(FTS_REASE_APP_DELAY);
+ if (ret < 0) {
+ FTS_ERROR("erase cmd write fail");
+ goto fw_reset;
+ }
+
+ /* write app */
+ start_addr = upgrade_func_ft5452.appoff;
+ ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);
+ if (ecc_in_host < 0 ) {
+ FTS_ERROR("lcd initial code write fail");
+ goto fw_reset;
+ }
+
+ FTS_INFO( "**********read out checksum**********");
+
+ /* check sum init */
+ wbuf[0] = FTS_CMD_ECC_INIT;
+ ret = fts_write(wbuf, 1);
+ if (ret < 0) {
+ FTS_ERROR("ecc init cmd write fail");
+ return ret;
+ }
+
+ /* send commond to start checksum */
+ wbuf[0] = FTS_CMD_ECC_CAL;
+ wbuf[1] = BYTE_OFF_16(start_addr);
+ wbuf[2] = BYTE_OFF_8(start_addr);
+ wbuf[3] = BYTE_OFF_0(start_addr);
+
+ wbuf[4] = BYTE_OFF_16(len);
+ wbuf[5] = BYTE_OFF_8(len);
+ wbuf[6] = BYTE_OFF_0(len);
+
+ FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", start_addr, len);
+ ret = fts_write(wbuf, 7);
+ if (ret < 0) {
+ FTS_ERROR("ecc calc cmd write fail");
+ return ret;
+ }
+
+ msleep(len / 256);
+
+ /* read status if check sum is finished */
+ for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) {
+ wbuf[0] = FTS_CMD_FLASH_STATUS;
+ reg_val[0] = reg_val[1] = 0x00;
+ fts_read(wbuf, 1, reg_val, 2);
+ FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]);
+ if ((0xF0 == reg_val[0]) && (0x55 == reg_val[1])) {
+ break;
+ }
+ msleep(FTS_RETRIES_DELAY_ECC_CAL);
+ }
+
+ /* read out check sum */
+ wbuf[0] = FTS_CMD_ECC_READ;
+ ret = fts_read(wbuf, 1, reg_val, 1);
+ if (ret < 0) {
+ FTS_ERROR( "ecc read cmd write fail");
+ return ret;
+ }
+ ecc_in_tp = reg_val[0];
+
+ FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+ if (ecc_in_tp != ecc_in_host) {
+ FTS_ERROR("ecc check fail");
+ goto fw_reset;
+ }
+
+ FTS_INFO("upgrade success, reset to normal boot");
+ ret = fts_fwupg_reset_in_boot();
+ if (ret < 0) {
+ FTS_ERROR("reset to normal boot fail");
+ }
+
+ msleep(200);
+ return 0;
+
+fw_reset:
+ FTS_INFO("upgrade fail, reset to normal boot");
+ ret = fts_fwupg_reset_in_boot();
+ if (ret < 0) {
+ FTS_ERROR("reset to normal boot fail");
+ }
+ return -EIO;
+}
+
+struct upgrade_func upgrade_func_ft5452 = {
+ .ctype = {0x81},
+ .fwveroff = 0x010E,
+ .fwcfgoff = 0x1FFB0,
+ .appoff = 0x0000,
+ .pramboot_supported = false,
+ .hid_supported = true,
+ .upgrade = fts_ft5452_upgrade,
+};
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
new file mode 100644
index 0000000..92fe990
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
@@ -0,0 +1,477 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_gestrue.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/******************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define KEY_GESTURE_U KEY_U
+#define KEY_GESTURE_UP KEY_UP
+#define KEY_GESTURE_DOWN KEY_DOWN
+#define KEY_GESTURE_LEFT KEY_LEFT
+#define KEY_GESTURE_RIGHT KEY_RIGHT
+#define KEY_GESTURE_O KEY_O
+#define KEY_GESTURE_E KEY_E
+#define KEY_GESTURE_M KEY_M
+#define KEY_GESTURE_L KEY_L
+#define KEY_GESTURE_W KEY_W
+#define KEY_GESTURE_S KEY_S
+#define KEY_GESTURE_V KEY_V
+#define KEY_GESTURE_C KEY_C
+#define KEY_GESTURE_Z KEY_Z
+
+#define GESTURE_LEFT 0x20
+#define GESTURE_RIGHT 0x21
+#define GESTURE_UP 0x22
+#define GESTURE_DOWN 0x23
+#define GESTURE_DOUBLECLICK 0x24
+#define GESTURE_O 0x30
+#define GESTURE_W 0x31
+#define GESTURE_M 0x32
+#define GESTURE_E 0x33
+#define GESTURE_L 0x44
+#define GESTURE_S 0x46
+#define GESTURE_V 0x54
+#define GESTURE_Z 0x41
+#define GESTURE_C 0x34
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/*
+* gesture_id - mean which gesture is recognised
+* point_num - points number of this gesture
+* coordinate_x - All gesture point x coordinate
+* coordinate_y - All gesture point y coordinate
+* mode - gesture enable/disable, need enable by host
+* - 1:enable gesture function(default) 0:disable
+* active - gesture work flag,
+* always set 1 when suspend, set 0 when resume
+*/
+struct fts_gesture_st {
+ u8 gesture_id;
+ u8 point_num;
+ u16 coordinate_x[FTS_GESTURE_POINTS_MAX];
+ u16 coordinate_y[FTS_GESTURE_POINTS_MAX];
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_gesture_st fts_gesture_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static ssize_t fts_gesture_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ u8 val = 0;
+ struct fts_ts_data *ts_data = fts_data;
+
+ mutex_lock(&ts_data->input_dev->mutex);
+ fts_read_reg(FTS_REG_GESTURE_EN, &val);
+ count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n",
+ ts_data->gesture_mode ? "On" : "Off");
+ count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val);
+ mutex_unlock(&ts_data->input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_gesture_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fts_ts_data *ts_data = fts_data;
+
+ mutex_lock(&ts_data->input_dev->mutex);
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ FTS_DEBUG("enable gesture");
+ ts_data->gesture_mode = ENABLE;
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ FTS_DEBUG("disable gesture");
+ ts_data->gesture_mode = DISABLE;
+ }
+ mutex_unlock(&ts_data->input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_gesture_buf_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ int i = 0;
+ struct input_dev *input_dev = fts_data->input_dev;
+ struct fts_gesture_st *gesture = &fts_gesture_data;
+
+ mutex_lock(&input_dev->mutex);
+ count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id);
+ count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n",
+ gesture->point_num);
+ count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n");
+
+ /* save point data,max:6 */
+ for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+ count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
+ gesture->coordinate_x[i], gesture->coordinate_y[i]);
+
+ if ((i + 1) % 4 == 0)
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ }
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static ssize_t fts_gesture_buf_store(
+ struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+
+/* sysfs gesture node
+ * read example: cat fts_gesture_mode ---read gesture mode
+ * write example:echo 1 > fts_gesture_mode --- write gesture mode to 1
+ *
+ */
+static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show,
+ fts_gesture_store);
+/*
+ * read example: cat fts_gesture_buf --- read gesture buf
+ */
+static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR,
+ fts_gesture_buf_show, fts_gesture_buf_store);
+
+static struct attribute *fts_gesture_mode_attrs[] = {
+ &dev_attr_fts_gesture_mode.attr,
+ &dev_attr_fts_gesture_buf.attr,
+ NULL,
+};
+
+static struct attribute_group fts_gesture_group = {
+ .attrs = fts_gesture_mode_attrs,
+};
+
+static int fts_create_gesture_sysfs(struct device *dev)
+{
+ int ret = 0;
+
+ ret = sysfs_create_group(&dev->kobj, &fts_gesture_group);
+ if (ret) {
+ FTS_ERROR("gesture sys node create fail");
+ sysfs_remove_group(&dev->kobj, &fts_gesture_group);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fts_gesture_report(struct input_dev *input_dev, int gesture_id)
+{
+ int gesture;
+
+ FTS_DEBUG("gesture_id:0x%x", gesture_id);
+ switch (gesture_id) {
+ case GESTURE_LEFT:
+ gesture = KEY_GESTURE_LEFT;
+ break;
+
+ case GESTURE_RIGHT:
+ gesture = KEY_GESTURE_RIGHT;
+ break;
+
+ case GESTURE_UP:
+ gesture = KEY_GESTURE_UP;
+ break;
+
+ case GESTURE_DOWN:
+ gesture = KEY_GESTURE_DOWN;
+ break;
+
+ case GESTURE_DOUBLECLICK:
+ gesture = KEY_POWER;
+
+ break;
+
+ case GESTURE_O:
+ gesture = KEY_GESTURE_O;
+ break;
+
+ case GESTURE_W:
+ gesture = KEY_GESTURE_W;
+ break;
+
+ case GESTURE_M:
+ gesture = KEY_GESTURE_M;
+ break;
+
+ case GESTURE_E:
+ gesture = KEY_GESTURE_E;
+ break;
+
+ case GESTURE_L:
+ gesture = KEY_GESTURE_L;
+ break;
+
+ case GESTURE_S:
+ gesture = KEY_GESTURE_S;
+ break;
+
+ case GESTURE_V:
+ gesture = KEY_GESTURE_V;
+ break;
+
+ case GESTURE_Z:
+ gesture = KEY_GESTURE_Z;
+ break;
+
+ case GESTURE_C:
+ gesture = KEY_GESTURE_C;
+ break;
+
+ default:
+ gesture = -1;
+ break;
+ }
+
+ /* report event key */
+ if (gesture != -1) {
+ FTS_DEBUG("Gesture Code=%d", gesture);
+ input_report_key(input_dev, gesture, 1);
+ input_sync(input_dev);
+ input_report_key(input_dev, gesture, 0);
+ input_sync(input_dev);
+ }
+}
+
+/*****************************************************************************
+* Name: fts_gesture_readdata
+* Brief: Read information about gesture: enable flag/gesture points..., if ges-
+* ture enable, save gesture points' information, and report to OS.
+* It will be called this function every intrrupt when FTS_GESTURE_EN = 1
+*
+* gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4
+* Input: ts_data - global struct data
+* data - gesture data buffer if non-flash, else NULL
+* Output:
+* Return: 0 - read gesture data successfully, the report data is gesture data
+* 1 - tp not in suspend/gesture not enable in TP FW
+* -Exx - error
+*****************************************************************************/
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data)
+{
+ int ret = 0;
+ int i = 0;
+ int index = 0;
+ u8 buf[FTS_GESTURE_DATA_LEN] = { 0 };
+ struct input_dev *input_dev = ts_data->input_dev;
+ struct fts_gesture_st *gesture = &fts_gesture_data;
+
+ if (!ts_data->suspended || !ts_data->gesture_mode) {
+ return 1;
+ }
+
+
+ ret = fts_read_reg(FTS_REG_GESTURE_EN, &buf[0]);
+ if ((ret < 0) || (buf[0] != ENABLE)) {
+ FTS_DEBUG("gesture not enable in fw, don't process gesture");
+ return 1;
+ }
+
+ buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+ ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2);
+ if (ret < 0) {
+ FTS_ERROR("read gesture header data fail");
+ return ret;
+ }
+
+ /* init variable before read gesture point */
+ memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+ memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+ gesture->gesture_id = buf[2];
+ gesture->point_num = buf[3];
+ FTS_DEBUG("gesture_id=%d, point_num=%d",
+ gesture->gesture_id, gesture->point_num);
+
+ /* save point data,max:6 */
+ for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+ index = 4 * i + 4;
+ gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8)
+ + buf[1 + index]);
+ gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8)
+ + buf[3 + index]);
+ }
+
+ /* report gesture to OS */
+ fts_gesture_report(input_dev, gesture->gesture_id);
+ return 0;
+}
+
+void fts_gesture_recovery(struct fts_ts_data *ts_data)
+{
+ if (ts_data->gesture_mode && ts_data->suspended) {
+ FTS_DEBUG("gesture recovery...");
+ fts_write_reg(0xD1, 0xFF);
+ fts_write_reg(0xD2, 0xFF);
+ fts_write_reg(0xD5, 0xFF);
+ fts_write_reg(0xD6, 0xFF);
+ fts_write_reg(0xD7, 0xFF);
+ fts_write_reg(0xD8, 0xFF);
+ fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+ }
+}
+
+int fts_gesture_suspend(struct fts_ts_data *ts_data)
+{
+ int i = 0;
+ u8 state = 0xFF;
+
+ FTS_FUNC_ENTER();
+ if (enable_irq_wake(ts_data->irq)) {
+ FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq);
+ }
+
+ for (i = 0; i < 5; i++) {
+ fts_write_reg(0xD1, 0xFF);
+ fts_write_reg(0xD2, 0xFF);
+ fts_write_reg(0xD5, 0xFF);
+ fts_write_reg(0xD6, 0xFF);
+ fts_write_reg(0xD7, 0xFF);
+ fts_write_reg(0xD8, 0xFF);
+ fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+ msleep(1);
+ fts_read_reg(FTS_REG_GESTURE_EN, &state);
+ if (state == ENABLE)
+ break;
+ }
+
+ if (i >= 5)
+ FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state);
+ else
+ FTS_INFO("Enter into gesture(suspend) successfully");
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_gesture_resume(struct fts_ts_data *ts_data)
+{
+ int i = 0;
+ u8 state = 0xFF;
+
+ FTS_FUNC_ENTER();
+ if (disable_irq_wake(ts_data->irq)) {
+ FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq);
+ }
+
+ for (i = 0; i < 5; i++) {
+ fts_write_reg(FTS_REG_GESTURE_EN, DISABLE);
+ msleep(1);
+ fts_read_reg(FTS_REG_GESTURE_EN, &state);
+ if (state == DISABLE)
+ break;
+ }
+
+ if (i >= 5)
+ FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state);
+ else
+ FTS_INFO("resume from gesture successfully");
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_gesture_init(struct fts_ts_data *ts_data)
+{
+ struct input_dev *input_dev = ts_data->input_dev;
+
+ FTS_FUNC_ENTER();
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C);
+
+ __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit);
+ __set_bit(KEY_GESTURE_LEFT, input_dev->keybit);
+ __set_bit(KEY_GESTURE_UP, input_dev->keybit);
+ __set_bit(KEY_GESTURE_DOWN, input_dev->keybit);
+ __set_bit(KEY_GESTURE_U, input_dev->keybit);
+ __set_bit(KEY_GESTURE_O, input_dev->keybit);
+ __set_bit(KEY_GESTURE_E, input_dev->keybit);
+ __set_bit(KEY_GESTURE_M, input_dev->keybit);
+ __set_bit(KEY_GESTURE_W, input_dev->keybit);
+ __set_bit(KEY_GESTURE_L, input_dev->keybit);
+ __set_bit(KEY_GESTURE_S, input_dev->keybit);
+ __set_bit(KEY_GESTURE_V, input_dev->keybit);
+ __set_bit(KEY_GESTURE_C, input_dev->keybit);
+ __set_bit(KEY_GESTURE_Z, input_dev->keybit);
+
+ fts_create_gesture_sysfs(ts_data->dev);
+
+ memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st));
+ ts_data->gesture_mode = FTS_GESTURE_EN;
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_gesture_exit(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+ sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group);
+ FTS_FUNC_EXIT();
+ return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
new file mode 100644
index 0000000..f4e74af
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
@@ -0,0 +1,193 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_i2c.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-04
+*
+* Abstract: i2c communication with TP
+*
+* Version: v1.0
+*
+* Revision History:
+*
+************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define I2C_RETRY_NUMBER 3
+#define I2C_BUF_LENGTH 256
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
+{
+ int ret = 0;
+ int i = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct i2c_msg msg_list[2];
+ struct i2c_msg *msg = NULL;
+ int msg_num = 0;
+
+ /* must have data when read */
+ if (!ts_data || !ts_data->client || !data || !datalen
+ || (datalen >= I2C_BUF_LENGTH) || (cmdlen >= I2C_BUF_LENGTH)) {
+ FTS_ERROR("fts_data/client/cmdlen(%d)/data/datalen(%d) is invalid",
+ cmdlen, datalen);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ts_data->bus_lock);
+ memset(&msg_list[0], 0, sizeof(struct i2c_msg));
+ memset(&msg_list[1], 0, sizeof(struct i2c_msg));
+ memcpy(ts_data->bus_tx_buf, cmd, cmdlen);
+ msg_list[0].addr = ts_data->client->addr;
+ msg_list[0].flags = 0;
+ msg_list[0].len = cmdlen;
+ msg_list[0].buf = ts_data->bus_tx_buf;
+ msg_list[1].addr = ts_data->client->addr;
+ msg_list[1].flags = I2C_M_RD;
+ msg_list[1].len = datalen;
+ msg_list[1].buf = ts_data->bus_rx_buf;
+ if (cmd && cmdlen) {
+ msg = &msg_list[0];
+ msg_num = 2;
+ } else {
+ msg = &msg_list[1];
+ msg_num = 1;
+ }
+
+ for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+ ret = i2c_transfer(ts_data->client->adapter, msg, msg_num);
+ if (ret < 0) {
+ FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret);
+ } else {
+ memcpy(data, ts_data->bus_rx_buf, datalen);
+ break;
+ }
+ }
+
+ mutex_unlock(&ts_data->bus_lock);
+ return ret;
+}
+
+int fts_write(u8 *writebuf, u32 writelen)
+{
+ int ret = 0;
+ int i = 0;
+ struct fts_ts_data *ts_data = fts_data;
+ struct i2c_msg msgs;
+
+ if (!ts_data || !ts_data->client || !writebuf || !writelen
+ || (writelen >= I2C_BUF_LENGTH)) {
+ FTS_ERROR("fts_data/client/data/datalen(%d) is invalid", writelen);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ts_data->bus_lock);
+ memset(&msgs, 0, sizeof(struct i2c_msg));
+ memcpy(ts_data->bus_tx_buf, writebuf, writelen);
+ msgs.addr = ts_data->client->addr;
+ msgs.flags = 0;
+ msgs.len = writelen;
+ msgs.buf = ts_data->bus_tx_buf;
+ for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+ ret = i2c_transfer(ts_data->client->adapter, &msgs, 1);
+ if (ret < 0) {
+ FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret);
+ } else {
+ break;
+ }
+ }
+ mutex_unlock(&ts_data->bus_lock);
+ return ret;
+}
+
+int fts_read_reg(u8 addr, u8 *value)
+{
+ return fts_read(&addr, 1, value, 1);
+}
+
+int fts_write_reg(u8 addr, u8 value)
+{
+ u8 buf[2] = { 0 };
+
+ buf[0] = addr;
+ buf[1] = value;
+ return fts_write(buf, sizeof(buf));
+}
+
+int fts_bus_init(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+ ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL);
+ if (NULL == ts_data->bus_tx_buf) {
+ FTS_ERROR("failed to allocate memory for bus_tx_buf");
+ return -ENOMEM;
+ }
+
+ ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL);
+ if (NULL == ts_data->bus_rx_buf) {
+ FTS_ERROR("failed to allocate memory for bus_rx_buf");
+ return -ENOMEM;
+ }
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+int fts_bus_exit(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+ if (ts_data && ts_data->bus_tx_buf) {
+ kfree(ts_data->bus_tx_buf);
+ ts_data->bus_tx_buf = NULL;
+ }
+
+ if (ts_data && ts_data->bus_rx_buf) {
+ kfree(ts_data->bus_rx_buf);
+ ts_data->bus_rx_buf = NULL;
+ }
+ FTS_FUNC_EXIT();
+ return 0;
+}
\ No newline at end of file
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
new file mode 100644
index 0000000..0f62640
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
@@ -0,0 +1,135 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_point_report_check.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-11-16
+*
+* Abstract: point report check function
+*
+* Version: v1.0
+*
+* Revision History:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_POINT_REPORT_CHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+* Name: fts_prc_func
+* Brief: fts point report check work func, report whole up of points
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_prc_func(struct work_struct *work)
+{
+ struct fts_ts_data *ts_data = container_of(work,
+ struct fts_ts_data, prc_work.work);
+ struct input_dev *input_dev = ts_data->input_dev;
+#if FTS_MT_PROTOCOL_B_EN
+ u32 finger_count = 0;
+ u32 max_touches = fts_data->pdata->max_touch_number;
+#endif
+
+ FTS_FUNC_ENTER();
+ mutex_lock(&ts_data->report_mutex);
+
+#if FTS_MT_PROTOCOL_B_EN
+ for (finger_count = 0; finger_count < max_touches; finger_count++) {
+ input_mt_slot(input_dev, finger_count);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+ }
+#else
+ input_mt_sync(input_dev);
+#endif
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+
+ mutex_unlock(&ts_data->report_mutex);
+
+ FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+* Name: fts_prc_queue_work
+* Brief: fts point report check queue work, call it when interrupt comes
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+void fts_prc_queue_work(struct fts_ts_data *ts_data)
+{
+ cancel_delayed_work_sync(&ts_data->prc_work);
+ queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work,
+ msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+}
+
+/*****************************************************************************
+* Name: fts_point_report_check_init
+* Brief:
+* Input:
+* Output:
+* Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_point_report_check_init(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+
+ if (ts_data->ts_workqueue) {
+ INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
+ } else {
+ FTS_ERROR("fts workqueue is NULL, can't run point report check function");
+ return -EINVAL;
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_point_report_check_exit
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_point_report_check_exit(struct fts_ts_data *ts_data)
+{
+ FTS_FUNC_ENTER();
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+#endif /* FTS_POINT_REPORT_CHECK_EN */
+
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index f2d9c2c..b20ba65 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -128,6 +128,15 @@ static const unsigned long goodix_irq_flags[] = {
static const struct dmi_system_id rotated_screen[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
+ .ident = "Teclast X89",
+ .matches = {
+ /* tPAD is too generic, also match on bios date */
+ DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
+ DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
+ DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"),
+ },
+ },
+ {
.ident = "WinBook TW100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
diff --git a/drivers/input/touchscreen/nt36xxx/Kconfig b/drivers/input/touchscreen/nt36xxx/Kconfig
new file mode 100644
index 0000000..c331f91
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Novatek NT36xxx touchscreen driver configuration
+#
+config TOUCHSCREEN_NT36xxx
+ bool "Novatek NT36xxx"
+ default y
+ help
+ Say Y here if you have a Novatek NT36xxx touchscreen connected
+ to your system.
+
+ If unsure, say N.
diff --git a/drivers/input/touchscreen/nt36xxx/Makefile b/drivers/input/touchscreen/nt36xxx/Makefile
new file mode 100644
index 0000000..80954c7
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Novatek NT36xxx touchscreen driver.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_NT36xxx) += nt36xxx.o nt36xxx_fw_update.o nt36xxx_ext_proc.o nt36xxx_mp_ctrlram.o
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx.c b/drivers/input/touchscreen/nt36xxx/nt36xxx.c
new file mode 100644
index 0000000..e4d66bf
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx.c
@@ -0,0 +1,1903 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+
+#if defined(CONFIG_FB)
+#ifdef CONFIG_DRM_MSM
+#include <linux/msm_drm_notify.h>
+#endif
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#include "nt36xxx.h"
+#if NVT_TOUCH_ESD_PROTECT
+#include <linux/jiffies.h>
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if NVT_TOUCH_ESD_PROTECT
+static struct delayed_work nvt_esd_check_work;
+static struct workqueue_struct *nvt_esd_check_wq;
+static unsigned long irq_timer = 0;
+uint8_t esd_check = false;
+uint8_t esd_retry = 0;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if NVT_TOUCH_EXT_PROC
+extern int32_t nvt_extra_proc_init(void);
+extern void nvt_extra_proc_deinit(void);
+#endif
+
+#if NVT_TOUCH_MP
+extern int32_t nvt_mp_proc_init(void);
+extern void nvt_mp_proc_deinit(void);
+#endif
+
+struct nvt_ts_data *ts;
+
+#if BOOT_UPDATE_FIRMWARE
+static struct workqueue_struct *nvt_fwu_wq;
+extern void Boot_Update_Firmware(struct work_struct *work);
+#endif
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+#else
+static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void nvt_ts_early_suspend(struct early_suspend *h);
+static void nvt_ts_late_resume(struct early_suspend *h);
+#endif
+
+#if TOUCH_KEY_NUM > 0
+const uint16_t touch_key_array[TOUCH_KEY_NUM] = {
+ KEY_BACK,
+ KEY_HOME,
+ KEY_MENU
+};
+#endif
+
+#if WAKEUP_GESTURE
+const uint16_t gesture_key_array[] = {
+ KEY_POWER, //GESTURE_WORD_C
+ KEY_POWER, //GESTURE_WORD_W
+ KEY_POWER, //GESTURE_WORD_V
+ KEY_POWER, //GESTURE_DOUBLE_CLICK
+ KEY_POWER, //GESTURE_WORD_Z
+ KEY_POWER, //GESTURE_WORD_M
+ KEY_POWER, //GESTURE_WORD_O
+ KEY_POWER, //GESTURE_WORD_e
+ KEY_POWER, //GESTURE_WORD_S
+ KEY_POWER, //GESTURE_SLIDE_UP
+ KEY_POWER, //GESTURE_SLIDE_DOWN
+ KEY_POWER, //GESTURE_SLIDE_LEFT
+ KEY_POWER, //GESTURE_SLIDE_RIGHT
+};
+#endif
+
+static uint8_t bTouchIsAwake = 0;
+
+/*******************************************************
+Description:
+ Novatek touchscreen irq enable/disable function.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_irq_enable(bool enable)
+{
+ struct irq_desc *desc;
+
+ if (enable) {
+ if (!ts->irq_enabled) {
+ enable_irq(ts->client->irq);
+ ts->irq_enabled = true;
+ }
+ } else {
+ if (ts->irq_enabled) {
+ disable_irq(ts->client->irq);
+ ts->irq_enabled = false;
+ }
+ }
+
+ desc = irq_to_desc(ts->client->irq);
+ NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen i2c read function.
+
+return:
+ Executive outcomes. 2---succeed. -5---I/O error
+*******************************************************/
+int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf,
+ uint16_t len)
+{
+ struct i2c_msg msgs[2];
+ int32_t ret = -1;
+ int32_t retries = 0;
+
+ mutex_lock(&ts->xbuf_lock);
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = address;
+ msgs[0].len = 1;
+ msgs[0].buf = &buf[0];
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = address;
+ msgs[1].len = len - 1;
+ msgs[1].buf = ts->xbuf;
+
+ while (retries < 5) {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret == 2) break;
+ retries++;
+ }
+
+ if (unlikely(retries == 5)) {
+ NVT_ERR("error, ret=%d\n", ret);
+ ret = -EIO;
+ }
+
+ memcpy(buf + 1, ts->xbuf, len - 1);
+
+ mutex_unlock(&ts->xbuf_lock);
+
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen i2c write function.
+
+return:
+ Executive outcomes. 1---succeed. -5---I/O error
+*******************************************************/
+int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf,
+ uint16_t len)
+{
+ struct i2c_msg msg;
+ int32_t ret = -1;
+ int32_t retries = 0;
+
+ mutex_lock(&ts->xbuf_lock);
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = address;
+ msg.len = len;
+ memcpy(ts->xbuf, buf, len);
+ msg.buf = ts->xbuf;
+
+ while (retries < 5) {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1) break;
+ retries++;
+ }
+
+ if (unlikely(retries == 5)) {
+ NVT_ERR("error, ret=%d\n", ret);
+ ret = -EIO;
+ }
+
+ mutex_unlock(&ts->xbuf_lock);
+
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen set index/page/addr address.
+
+return:
+ Executive outcomes. 0---succeed. -5---access fail.
+*******************************************************/
+int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr)
+{
+ uint8_t buf[4] = {0};
+
+ buf[0] = 0xFF; //set index/page/addr command
+ buf[1] = (addr >> 16) & 0xFF;
+ buf[2] = (addr >> 8) & 0xFF;
+
+ return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen reset MCU then into idle mode
+ function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_sw_reset_idle(void)
+{
+ uint8_t buf[4]={0};
+
+ //---write i2c cmds to reset idle---
+ buf[0]=0x00;
+ buf[1]=0xA5;
+ CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+ msleep(15);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen reset MCU (boot) function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_bootloader_reset(void)
+{
+ uint8_t buf[8] = {0};
+
+ NVT_LOG("start\n");
+
+ //---write i2c cmds to reset---
+ buf[0] = 0x00;
+ buf[1] = 0x69;
+ CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+ // need 35ms delay after bootloader reset
+ msleep(35);
+
+ NVT_LOG("end\n");
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen clear FW status function.
+
+return:
+ Executive outcomes. 0---succeed. -1---fail.
+*******************************************************/
+int32_t nvt_clear_fw_status(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t i = 0;
+ const int32_t retry = 20;
+
+ for (i = 0; i < retry; i++) {
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+ //---clear fw status---
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = 0x00;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+ //---read fw status---
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = 0xFF;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+ if (buf[1] == 0x00)
+ break;
+
+ usleep_range(10000, 10000);
+ }
+
+ if (i >= retry) {
+ NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check FW status function.
+
+return:
+ Executive outcomes. 0---succeed. -1---failed.
+*******************************************************/
+int32_t nvt_check_fw_status(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t i = 0;
+ const int32_t retry = 50;
+
+ for (i = 0; i < retry; i++) {
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+ //---read fw status---
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+ if ((buf[1] & 0xF0) == 0xA0)
+ break;
+
+ usleep_range(10000, 10000);
+ }
+
+ if (i >= retry) {
+ NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check FW reset state function.
+
+return:
+ Executive outcomes. 0---succeed. -1---failed.
+*******************************************************/
+int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state)
+{
+ uint8_t buf[8] = {0};
+ int32_t ret = 0;
+ int32_t retry = 0;
+
+ while (1) {
+ usleep_range(10000, 10000);
+
+ //---read reset state---
+ buf[0] = EVENT_MAP_RESET_COMPLETE;
+ buf[1] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6);
+
+ if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) {
+ ret = 0;
+ break;
+ }
+
+ retry++;
+ if(unlikely(retry > 100)) {
+ NVT_ERR("error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ retry, buf[1], buf[2], buf[3], buf[4], buf[5]);
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen get novatek project id information
+ function.
+
+return:
+ Executive outcomes. 0---success. -1---fail.
+*******************************************************/
+int32_t nvt_read_pid(void)
+{
+ uint8_t buf[3] = {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID);
+
+ //---read project id---
+ buf[0] = EVENT_MAP_PROJECTID;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 3);
+
+ ts->nvt_pid = (buf[2] << 8) + buf[1];
+
+ NVT_LOG("PID=%04X\n", ts->nvt_pid);
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen get firmware related information
+ function.
+
+return:
+ Executive outcomes. 0---success. -1---fail.
+*******************************************************/
+int32_t nvt_get_fw_info(void)
+{
+ uint8_t buf[64] = {0};
+ uint32_t retry_count = 0;
+ int32_t ret = 0;
+
+info_retry:
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
+
+ //---read fw info---
+ buf[0] = EVENT_MAP_FWINFO;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 17);
+ ts->fw_ver = buf[1];
+ ts->x_num = buf[3];
+ ts->y_num = buf[4];
+ ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]);
+ ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]);
+ ts->max_button_num = buf[11];
+
+ //---clear x_num, y_num if fw info is broken---
+ if ((buf[1] + buf[2]) != 0xFF) {
+ NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]);
+ ts->fw_ver = 0;
+ ts->x_num = 18;
+ ts->y_num = 32;
+ ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH;
+ ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT;
+ ts->max_button_num = TOUCH_KEY_NUM;
+
+ if(retry_count < 3) {
+ retry_count++;
+ NVT_ERR("retry_count=%d\n", retry_count);
+ goto info_retry;
+ } else {
+ NVT_ERR("Set default fw_ver=%d, x_num=%d, y_num=%d, "
+ "abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n",
+ ts->fw_ver, ts->x_num, ts->y_num,
+ ts->abs_x_max, ts->abs_y_max, ts->max_button_num);
+ ret = -1;
+ }
+ } else {
+ ret = 0;
+ }
+
+ //---Get Novatek PID---
+ nvt_read_pid();
+
+ return ret;
+}
+
+/*******************************************************
+ Create Device Node (Proc Entry)
+*******************************************************/
+#if NVT_TOUCH_PROC
+static struct proc_dir_entry *NVT_proc_entry;
+#define DEVICE_NAME "NVTflash"
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/NVTflash read function.
+
+return:
+ Executive outcomes. 2---succeed. -5,-14---failed.
+*******************************************************/
+static ssize_t nvt_flash_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
+{
+ uint8_t str[68] = {0};
+ int32_t ret = -1;
+ int32_t retries = 0;
+ int8_t i2c_wr = 0;
+
+ if (count > sizeof(str)) {
+ NVT_ERR("error count=%zu\n", count);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(str, buff, count)) {
+ NVT_ERR("copy from user error\n");
+ return -EFAULT;
+ }
+
+#if NVT_TOUCH_ESD_PROTECT
+ /*
+ * stop esd check work to avoid case that 0x77 report righ after here to enable esd check again
+ * finally lead to trigger esd recovery bootloader reset
+ */
+ cancel_delayed_work_sync(&nvt_esd_check_work);
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ i2c_wr = str[0] >> 7;
+
+ if (i2c_wr == 0) { //I2C write
+ while (retries < 20) {
+ ret = CTP_I2C_WRITE(ts->client, (str[0] & 0x7F), &str[2], str[1]);
+ if (ret == 1)
+ break;
+ else
+ NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+ retries++;
+ }
+
+ if (unlikely(retries == 20)) {
+ NVT_ERR("error, ret = %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+ } else if (i2c_wr == 1) { //I2C read
+ while (retries < 20) {
+ ret = CTP_I2C_READ(ts->client, (str[0] & 0x7F), &str[2], str[1]);
+ if (ret == 2)
+ break;
+ else
+ NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+ retries++;
+ }
+
+ // copy buff to user if i2c transfer
+ if (retries < 20) {
+ if (copy_to_user(buff, str, count))
+ return -EFAULT;
+ }
+
+ if (unlikely(retries == 20)) {
+ NVT_ERR("error, ret = %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+ } else {
+ NVT_ERR("Call error, str[0]=%d\n", str[0]);
+ return -EFAULT;
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/NVTflash open function.
+
+return:
+ Executive outcomes. 0---succeed. -12---failed.
+*******************************************************/
+static int32_t nvt_flash_open(struct inode *inode, struct file *file)
+{
+ struct nvt_flash_data *dev;
+
+ dev = kmalloc(sizeof(struct nvt_flash_data), GFP_KERNEL);
+ if (dev == NULL) {
+ NVT_ERR("Failed to allocate memory for nvt flash data\n");
+ return -ENOMEM;
+ }
+
+ rwlock_init(&dev->lock);
+ file->private_data = dev;
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/NVTflash close function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_flash_close(struct inode *inode, struct file *file)
+{
+ struct nvt_flash_data *dev = file->private_data;
+
+ kfree(dev);
+
+ return 0;
+}
+
+static const struct file_operations nvt_flash_fops = {
+ .open = nvt_flash_open,
+ .release = nvt_flash_close,
+ .read = nvt_flash_read,
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/NVTflash initial function.
+
+return:
+ Executive outcomes. 0---succeed. -12---failed.
+*******************************************************/
+static int32_t nvt_flash_proc_init(void)
+{
+ NVT_proc_entry = proc_create(DEVICE_NAME, 0444, NULL,&nvt_flash_fops);
+ if (NVT_proc_entry == NULL) {
+ NVT_ERR("Failed!\n");
+ return -ENOMEM;
+ } else {
+ NVT_LOG("Succeeded!\n");
+ }
+
+ NVT_LOG("==========================================================\n");
+ NVT_LOG("Create /proc/%s\n", DEVICE_NAME);
+ NVT_LOG("==========================================================\n");
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/NVTflash deinitial function.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_flash_proc_deinit(void)
+{
+ if (NVT_proc_entry != NULL) {
+ remove_proc_entry(DEVICE_NAME, NULL);
+ NVT_proc_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", DEVICE_NAME);
+ }
+}
+#endif
+
+#if WAKEUP_GESTURE
+#define GESTURE_WORD_C 12
+#define GESTURE_WORD_W 13
+#define GESTURE_WORD_V 14
+#define GESTURE_DOUBLE_CLICK 15
+#define GESTURE_WORD_Z 16
+#define GESTURE_WORD_M 17
+#define GESTURE_WORD_O 18
+#define GESTURE_WORD_e 19
+#define GESTURE_WORD_S 20
+#define GESTURE_SLIDE_UP 21
+#define GESTURE_SLIDE_DOWN 22
+#define GESTURE_SLIDE_LEFT 23
+#define GESTURE_SLIDE_RIGHT 24
+/* customized gesture id */
+#define DATA_PROTOCOL 30
+
+/* function page definition */
+#define FUNCPAGE_GESTURE 1
+
+/*******************************************************
+Description:
+ Novatek touchscreen wake up gesture key report function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_ts_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data)
+{
+ uint32_t keycode = 0;
+ uint8_t func_type = data[2];
+ uint8_t func_id = data[3];
+
+ /* support fw specifal data protocol */
+ if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) {
+ gesture_id = func_id;
+ } else if (gesture_id > DATA_PROTOCOL) {
+ NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", gesture_id, func_type, func_id);
+ return;
+ }
+
+ NVT_LOG("gesture_id = %d\n", gesture_id);
+
+ switch (gesture_id) {
+ case GESTURE_WORD_C:
+ NVT_LOG("Gesture : Word-C.\n");
+ keycode = gesture_key_array[0];
+ break;
+ case GESTURE_WORD_W:
+ NVT_LOG("Gesture : Word-W.\n");
+ keycode = gesture_key_array[1];
+ break;
+ case GESTURE_WORD_V:
+ NVT_LOG("Gesture : Word-V.\n");
+ keycode = gesture_key_array[2];
+ break;
+ case GESTURE_DOUBLE_CLICK:
+ NVT_LOG("Gesture : Double Click.\n");
+ keycode = gesture_key_array[3];
+ break;
+ case GESTURE_WORD_Z:
+ NVT_LOG("Gesture : Word-Z.\n");
+ keycode = gesture_key_array[4];
+ break;
+ case GESTURE_WORD_M:
+ NVT_LOG("Gesture : Word-M.\n");
+ keycode = gesture_key_array[5];
+ break;
+ case GESTURE_WORD_O:
+ NVT_LOG("Gesture : Word-O.\n");
+ keycode = gesture_key_array[6];
+ break;
+ case GESTURE_WORD_e:
+ NVT_LOG("Gesture : Word-e.\n");
+ keycode = gesture_key_array[7];
+ break;
+ case GESTURE_WORD_S:
+ NVT_LOG("Gesture : Word-S.\n");
+ keycode = gesture_key_array[8];
+ break;
+ case GESTURE_SLIDE_UP:
+ NVT_LOG("Gesture : Slide UP.\n");
+ keycode = gesture_key_array[9];
+ break;
+ case GESTURE_SLIDE_DOWN:
+ NVT_LOG("Gesture : Slide DOWN.\n");
+ keycode = gesture_key_array[10];
+ break;
+ case GESTURE_SLIDE_LEFT:
+ NVT_LOG("Gesture : Slide LEFT.\n");
+ keycode = gesture_key_array[11];
+ break;
+ case GESTURE_SLIDE_RIGHT:
+ NVT_LOG("Gesture : Slide RIGHT.\n");
+ keycode = gesture_key_array[12];
+ break;
+ default:
+ break;
+ }
+
+ if (keycode > 0) {
+ input_report_key(ts->input_dev, keycode, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, keycode, 0);
+ input_sync(ts->input_dev);
+ }
+}
+#endif
+
+/*******************************************************
+Description:
+ Novatek touchscreen parse device tree function.
+
+return:
+ n.a.
+*******************************************************/
+#ifdef CONFIG_OF
+static void nvt_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+
+#if NVT_TOUCH_SUPPORT_HW_RST
+ ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags);
+ NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio);
+#endif
+ ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags);
+ NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio);
+
+}
+#else
+static void nvt_parse_dt(struct device *dev)
+{
+#if NVT_TOUCH_SUPPORT_HW_RST
+ ts->reset_gpio = NVTTOUCH_RST_PIN;
+#endif
+ ts->irq_gpio = NVTTOUCH_INT_PIN;
+}
+#endif
+
+/*******************************************************
+Description:
+ Novatek touchscreen config and request gpio
+
+return:
+ Executive outcomes. 0---succeed. not 0---failed.
+*******************************************************/
+static int nvt_gpio_config(struct nvt_ts_data *ts)
+{
+ int32_t ret = 0;
+
+#if NVT_TOUCH_SUPPORT_HW_RST
+ /* request RST-pin (Output/High) */
+ if (gpio_is_valid(ts->reset_gpio)) {
+ ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_HIGH, "NVT-tp-rst");
+ if (ret) {
+ NVT_ERR("Failed to request NVT-tp-rst GPIO\n");
+ goto err_request_reset_gpio;
+ }
+ }
+#endif
+
+ /* request INT-pin (Input) */
+ if (gpio_is_valid(ts->irq_gpio)) {
+ ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int");
+ if (ret) {
+ NVT_ERR("Failed to request NVT-int GPIO\n");
+ goto err_request_irq_gpio;
+ }
+ }
+
+ return ret;
+
+err_request_irq_gpio:
+#if NVT_TOUCH_SUPPORT_HW_RST
+ gpio_free(ts->reset_gpio);
+err_request_reset_gpio:
+#endif
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen deconfig gpio
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_gpio_deconfig(struct nvt_ts_data *ts)
+{
+ if (gpio_is_valid(ts->irq_gpio))
+ gpio_free(ts->irq_gpio);
+#if NVT_TOUCH_SUPPORT_HW_RST
+ if (gpio_is_valid(ts->reset_gpio))
+ gpio_free(ts->reset_gpio);
+#endif
+}
+
+static uint8_t nvt_fw_recovery(uint8_t *point_data)
+{
+ uint8_t i = 0;
+ uint8_t detected = true;
+
+ /* check pattern */
+ for (i=1 ; i<7 ; i++) {
+ if (point_data[i] != 0x77) {
+ detected = false;
+ break;
+ }
+ }
+
+ return detected;
+}
+
+#if NVT_TOUCH_ESD_PROTECT
+void nvt_esd_check_enable(uint8_t enable)
+{
+ /* update interrupt timer */
+ irq_timer = jiffies;
+ /* clear esd_retry counter, if protect function is enabled */
+ esd_retry = enable ? 0 : esd_retry;
+ /* enable/disable esd check flag */
+ esd_check = enable;
+}
+
+static void nvt_esd_check_func(struct work_struct *work)
+{
+ unsigned int timer = jiffies_to_msecs(jiffies - irq_timer);
+
+ //NVT_ERR("esd_check = %d (retry %d)\n", esd_check, esd_retry); //DEBUG
+
+ if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) {
+ mutex_lock(&ts->lock);
+ NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry);
+ /* do esd recovery, bootloader reset */
+ nvt_bootloader_reset();
+ mutex_unlock(&ts->lock);
+ /* update interrupt timer */
+ irq_timer = jiffies;
+ /* update esd_retry counter */
+ esd_retry++;
+ }
+
+ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+}
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#define POINT_DATA_LEN 65
+/*******************************************************
+Description:
+ Novatek touchscreen work function.
+
+return:
+ n.a.
+*******************************************************/
+static irqreturn_t nvt_ts_work_func(int irq, void *data)
+{
+ int32_t ret = -1;
+ uint8_t point_data[POINT_DATA_LEN + 1] = {0};
+ uint32_t position = 0;
+ uint32_t input_x = 0;
+ uint32_t input_y = 0;
+ uint32_t input_w = 0;
+ uint32_t input_p = 0;
+ uint8_t input_id = 0;
+#if MT_PROTOCOL_B
+ uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0};
+#endif /* MT_PROTOCOL_B */
+ int32_t i = 0;
+ int32_t finger_cnt = 0;
+
+#if WAKEUP_GESTURE
+ if (bTouchIsAwake == 0) {
+ pm_wakeup_event(&ts->input_dev->dev, 5000);
+ }
+#endif
+
+ mutex_lock(&ts->lock);
+
+ ret = CTP_I2C_READ(ts->client, I2C_FW_Address, point_data, POINT_DATA_LEN + 1);
+ if (ret < 0) {
+ NVT_ERR("CTP_I2C_READ failed.(%d)\n", ret);
+ goto XFER_ERROR;
+ }
+/*
+ //--- dump I2C buf ---
+ for (i = 0; i < 10; i++) {
+ printk("%02X %02X %02X %02X %02X %02X ",
+ point_data[1+i*6], point_data[2+i*6], point_data[3+i*6], point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]);
+ }
+ printk("\n");
+*/
+
+ if (nvt_fw_recovery(point_data)) {
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(true);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+ goto XFER_ERROR;
+ }
+
+#if WAKEUP_GESTURE
+ if (bTouchIsAwake == 0) {
+ input_id = (uint8_t)(point_data[1] >> 3);
+ nvt_ts_wakeup_gesture_report(input_id, point_data);
+ mutex_unlock(&ts->lock);
+ return IRQ_HANDLED;
+ }
+#endif
+
+ finger_cnt = 0;
+
+ for (i = 0; i < ts->max_touch_num; i++) {
+ position = 1 + 6 * i;
+ input_id = (uint8_t)(point_data[position + 0] >> 3);
+ if ((input_id == 0) || (input_id > ts->max_touch_num))
+ continue;
+
+ if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) { //finger down (enter & moving)
+#if NVT_TOUCH_ESD_PROTECT
+ /* update interrupt timer */
+ irq_timer = jiffies;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+ input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4);
+ input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F);
+ if ((input_x < 0) || (input_y < 0))
+ continue;
+ if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max))
+ continue;
+ input_w = (uint32_t)(point_data[position + 4]);
+ if (input_w == 0)
+ input_w = 1;
+ if (i < 2) {
+ input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8);
+ if (input_p > TOUCH_FORCE_NUM)
+ input_p = TOUCH_FORCE_NUM;
+ } else {
+ input_p = (uint32_t)(point_data[position + 5]);
+ }
+ if (input_p == 0)
+ input_p = 1;
+
+#if MT_PROTOCOL_B
+ press_id[input_id - 1] = 1;
+ input_mt_slot(ts->input_dev, input_id - 1);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+#else /* MT_PROTOCOL_B */
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1);
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
+#endif /* MT_PROTOCOL_B */
+
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p);
+
+#if MT_PROTOCOL_B
+#else /* MT_PROTOCOL_B */
+ input_mt_sync(ts->input_dev);
+#endif /* MT_PROTOCOL_B */
+
+ finger_cnt++;
+ }
+ }
+
+#if MT_PROTOCOL_B
+ for (i = 0; i < ts->max_touch_num; i++) {
+ if (press_id[i] != 1) {
+ input_mt_slot(ts->input_dev, i);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
+ }
+ }
+
+ input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0));
+#else /* MT_PROTOCOL_B */
+ if (finger_cnt == 0) {
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
+ input_mt_sync(ts->input_dev);
+ }
+#endif /* MT_PROTOCOL_B */
+
+#if TOUCH_KEY_NUM > 0
+ if (point_data[61] == 0xF8) {
+#if NVT_TOUCH_ESD_PROTECT
+ /* update interrupt timer */
+ irq_timer = jiffies;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+ for (i = 0; i < ts->max_button_num; i++) {
+ input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01));
+ }
+ } else {
+ for (i = 0; i < ts->max_button_num; i++) {
+ input_report_key(ts->input_dev, touch_key_array[i], 0);
+ }
+ }
+#endif
+
+ input_sync(ts->input_dev);
+
+XFER_ERROR:
+
+ mutex_unlock(&ts->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check and stop crc reboot loop.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_stop_crc_reboot(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t retry = 0;
+
+ //read dummy buffer to check CRC fail reboot is happening or not
+
+ //---change I2C index to prevent geting 0xFF, but not 0xFC---
+ nvt_set_page(I2C_BLDR_Address, 0x1F64E);
+
+ //---read to check if buf is 0xFC which means IC is in CRC reboot ---
+ buf[0] = 0x4E;
+ CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 4);
+
+ if ((buf[1] == 0xFC) ||
+ ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) {
+
+ //IC is in CRC fail reboot loop, needs to be stopped!
+ for (retry = 5; retry > 0; retry--) {
+
+ //---write i2c cmds to reset idle : 1st---
+ buf[0]=0x00;
+ buf[1]=0xA5;
+ CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+ //---write i2c cmds to reset idle : 2rd---
+ buf[0]=0x00;
+ buf[1]=0xA5;
+ CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ msleep(1);
+
+ //---clear CRC_ERR_FLAG---
+ nvt_set_page(I2C_BLDR_Address, 0x3F135);
+
+ buf[0] = 0x35;
+ buf[1] = 0xA5;
+ CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2);
+
+ //---check CRC_ERR_FLAG---
+ nvt_set_page(I2C_BLDR_Address, 0x3F135);
+
+ buf[0] = 0x35;
+ buf[1] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 2);
+
+ if (buf[1] == 0xA5)
+ break;
+ }
+ if (retry == 0)
+ NVT_ERR("CRC auto reboot is not able to be stopped! buf[1]=0x%02X\n", buf[1]);
+ }
+
+ return;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check chip version trim function.
+
+return:
+ Executive outcomes. 0---NVT IC. -1---not NVT IC.
+*******************************************************/
+static int8_t nvt_ts_check_chip_ver_trim(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t retry = 0;
+ int32_t list = 0;
+ int32_t i = 0;
+ int32_t found_nvt_chip = 0;
+ int32_t ret = -1;
+
+ nvt_bootloader_reset(); // NOT in retry loop
+
+ //---Check for 5 times---
+ for (retry = 5; retry > 0; retry--) {
+ nvt_sw_reset_idle();
+
+ buf[0] = 0x00;
+ buf[1] = 0x35;
+ CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ msleep(10);
+
+ nvt_set_page(I2C_BLDR_Address, 0x1F64E);
+
+ buf[0] = 0x4E;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 7);
+ NVT_LOG("buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X\n",
+ buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+ //---Stop CRC check to prevent IC auto reboot---
+ if ((buf[1] == 0xFC) ||
+ ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) {
+ nvt_stop_crc_reboot();
+ continue;
+ }
+
+ // compare read chip id on supported list
+ for (list = 0; list < (sizeof(trim_id_table) / sizeof(struct nvt_ts_trim_id_table)); list++) {
+ found_nvt_chip = 0;
+
+ // compare each byte
+ for (i = 0; i < NVT_ID_BYTE_MAX; i++) {
+ if (trim_id_table[list].mask[i]) {
+ if (buf[i + 1] != trim_id_table[list].id[i])
+ break;
+ }
+ }
+
+ if (i == NVT_ID_BYTE_MAX) {
+ found_nvt_chip = 1;
+ }
+
+ if (found_nvt_chip) {
+ NVT_LOG("This is NVT touch IC\n");
+ ts->mmap = trim_id_table[list].mmap;
+ ts->carrier_system = trim_id_table[list].hwinfo->carrier_system;
+ ret = 0;
+ goto out;
+ } else {
+ ts->mmap = NULL;
+ ret = -1;
+ }
+ }
+
+ msleep(10);
+ }
+
+out:
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen driver probe function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed
+*******************************************************/
+static int32_t nvt_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int32_t ret = 0;
+#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE)
+ int32_t retry = 0;
+#endif
+
+ NVT_LOG("start\n");
+
+ ts = kmalloc(sizeof(struct nvt_ts_data), GFP_KERNEL);
+ if (ts == NULL) {
+ NVT_ERR("failed to allocated memory for nvt ts data\n");
+ return -ENOMEM;
+ }
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ //---parse dts---
+ nvt_parse_dt(&client->dev);
+
+ //---request and config GPIOs---
+ ret = nvt_gpio_config(ts);
+ if (ret) {
+ NVT_ERR("gpio config error!\n");
+ goto err_gpio_config_failed;
+ }
+
+ //---check i2c func.---
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ mutex_init(&ts->lock);
+ mutex_init(&ts->xbuf_lock);
+
+ // need 10ms delay after POR(power on reset)
+ msleep(10);
+
+ //---check chip version trim---
+ ret = nvt_ts_check_chip_ver_trim();
+ if (ret) {
+ NVT_ERR("chip is not identified\n");
+ ret = -EINVAL;
+ goto err_chipvertrim_failed;
+ }
+
+ nvt_bootloader_reset();
+ nvt_check_fw_reset_state(RESET_STATE_INIT);
+ nvt_get_fw_info();
+
+ //---allocate input device---
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ NVT_ERR("allocate input device failed\n");
+ ret = -ENOMEM;
+ goto err_input_dev_alloc_failed;
+ }
+
+ ts->max_touch_num = TOUCH_MAX_FINGER_NUM;
+
+#if TOUCH_KEY_NUM > 0
+ ts->max_button_num = TOUCH_KEY_NUM;
+#endif
+
+ ts->int_trigger_type = INT_TRIGGER_TYPE;
+
+
+ //---set input device info.---
+ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+ ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
+
+#if MT_PROTOCOL_B
+ input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0);
+#endif
+
+ input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0); //pressure = TOUCH_FORCE_NUM
+
+#if TOUCH_MAX_FINGER_NUM > 1
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //area = 255
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
+#if MT_PROTOCOL_B
+ // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it
+#else
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0);
+#endif //MT_PROTOCOL_B
+#endif //TOUCH_MAX_FINGER_NUM > 1
+
+#if TOUCH_KEY_NUM > 0
+ for (retry = 0; retry < ts->max_button_num; retry++) {
+ input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]);
+ }
+#endif
+
+#if WAKEUP_GESTURE
+ for (retry = 0; retry < ARRAY_SIZE(gesture_key_array); retry++) {
+ input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]);
+ }
+#endif
+
+ snprintf(ts->phys, sizeof(ts->phys), "input/ts");
+ ts->input_dev->name = NVT_TS_NAME;
+ ts->input_dev->phys = ts->phys;
+ ts->input_dev->id.bustype = BUS_I2C;
+
+ //---register input device---
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret);
+ goto err_input_register_device_failed;
+ }
+
+ //---set int-pin & request irq---
+ client->irq = gpio_to_irq(ts->irq_gpio);
+ if (client->irq) {
+ NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type);
+ ts->irq_enabled = true;
+ ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func,
+ ts->int_trigger_type | IRQF_ONESHOT, NVT_I2C_NAME, ts);
+ if (ret != 0) {
+ NVT_ERR("request irq failed. ret=%d\n", ret);
+ goto err_int_request_failed;
+ } else {
+ nvt_irq_enable(false);
+ NVT_LOG("request irq %d succeed\n", client->irq);
+ }
+ }
+
+#if WAKEUP_GESTURE
+ device_init_wakeup(&ts->input_dev->dev, 1);
+#endif
+
+#if BOOT_UPDATE_FIRMWARE
+ nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (!nvt_fwu_wq) {
+ NVT_ERR("nvt_fwu_wq create workqueue failed\n");
+ ret = -ENOMEM;
+ goto err_create_nvt_fwu_wq_failed;
+ }
+ INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware);
+ // please make sure boot update start after display reset(RESX) sequence
+ queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000));
+#endif
+
+ NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT);
+#if NVT_TOUCH_ESD_PROTECT
+ INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func);
+ nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1);
+ if (!nvt_esd_check_wq) {
+ NVT_ERR("nvt_esd_check_wq create workqueue failed\n");
+ ret = -ENOMEM;
+ goto err_create_nvt_esd_check_wq_failed;
+ }
+ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ //---set device node---
+#if NVT_TOUCH_PROC
+ ret = nvt_flash_proc_init();
+ if (ret != 0) {
+ NVT_ERR("nvt flash proc init failed. ret=%d\n", ret);
+ goto err_flash_proc_init_failed;
+ }
+#endif
+
+#if NVT_TOUCH_EXT_PROC
+ ret = nvt_extra_proc_init();
+ if (ret != 0) {
+ NVT_ERR("nvt extra proc init failed. ret=%d\n", ret);
+ goto err_extra_proc_init_failed;
+ }
+#endif
+
+#if NVT_TOUCH_MP
+ ret = nvt_mp_proc_init();
+ if (ret != 0) {
+ NVT_ERR("nvt mp proc init failed. ret=%d\n", ret);
+ goto err_mp_proc_init_failed;
+ }
+#endif
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+ ts->drm_notif.notifier_call = nvt_drm_notifier_callback;
+ ret = msm_drm_register_client(&ts->drm_notif);
+ if(ret) {
+ NVT_ERR("register drm_notifier failed. ret=%d\n", ret);
+ goto err_register_drm_notif_failed;
+ }
+#else
+ ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
+ ret = fb_register_client(&ts->fb_notif);
+ if(ret) {
+ NVT_ERR("register fb_notifier failed. ret=%d\n", ret);
+ goto err_register_fb_notif_failed;
+ }
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = nvt_ts_early_suspend;
+ ts->early_suspend.resume = nvt_ts_late_resume;
+ ret = register_early_suspend(&ts->early_suspend);
+ if(ret) {
+ NVT_ERR("register early suspend failed. ret=%d\n", ret);
+ goto err_register_early_suspend_failed;
+ }
+#endif
+
+ bTouchIsAwake = 1;
+ NVT_LOG("end\n");
+
+ nvt_irq_enable(true);
+
+ return 0;
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+ if (msm_drm_unregister_client(&ts->drm_notif))
+ NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+err_register_drm_notif_failed:
+#else
+ if (fb_unregister_client(&ts->fb_notif))
+ NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+err_register_fb_notif_failed:
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts->early_suspend);
+err_register_early_suspend_failed:
+#endif
+#if NVT_TOUCH_MP
+nvt_mp_proc_deinit();
+err_mp_proc_init_failed:
+#endif
+#if NVT_TOUCH_EXT_PROC
+nvt_extra_proc_deinit();
+err_extra_proc_init_failed:
+#endif
+#if NVT_TOUCH_PROC
+nvt_flash_proc_deinit();
+err_flash_proc_init_failed:
+#endif
+#if NVT_TOUCH_ESD_PROTECT
+ if (nvt_esd_check_wq) {
+ cancel_delayed_work_sync(&nvt_esd_check_work);
+ destroy_workqueue(nvt_esd_check_wq);
+ nvt_esd_check_wq = NULL;
+ }
+err_create_nvt_esd_check_wq_failed:
+#endif
+#if BOOT_UPDATE_FIRMWARE
+ if (nvt_fwu_wq) {
+ cancel_delayed_work_sync(&ts->nvt_fwu_work);
+ destroy_workqueue(nvt_fwu_wq);
+ nvt_fwu_wq = NULL;
+ }
+err_create_nvt_fwu_wq_failed:
+#endif
+#if WAKEUP_GESTURE
+ device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+ free_irq(client->irq, ts);
+err_int_request_failed:
+ input_unregister_device(ts->input_dev);
+ ts->input_dev = NULL;
+err_input_register_device_failed:
+ if (ts->input_dev) {
+ input_free_device(ts->input_dev);
+ ts->input_dev = NULL;
+ }
+err_input_dev_alloc_failed:
+err_chipvertrim_failed:
+ mutex_destroy(&ts->xbuf_lock);
+ mutex_destroy(&ts->lock);
+err_check_functionality_failed:
+ nvt_gpio_deconfig(ts);
+err_gpio_config_failed:
+ i2c_set_clientdata(client, NULL);
+ if (ts) {
+ kfree(ts);
+ ts = NULL;
+ }
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen driver release function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_ts_remove(struct i2c_client *client)
+{
+ NVT_LOG("Removing driver...\n");
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+ if (msm_drm_unregister_client(&ts->drm_notif))
+ NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+ if (fb_unregister_client(&ts->fb_notif))
+ NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#if NVT_TOUCH_MP
+ nvt_mp_proc_deinit();
+#endif
+#if NVT_TOUCH_EXT_PROC
+ nvt_extra_proc_deinit();
+#endif
+#if NVT_TOUCH_PROC
+ nvt_flash_proc_deinit();
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+ if (nvt_esd_check_wq) {
+ cancel_delayed_work_sync(&nvt_esd_check_work);
+ nvt_esd_check_enable(false);
+ destroy_workqueue(nvt_esd_check_wq);
+ nvt_esd_check_wq = NULL;
+ }
+#endif
+
+#if BOOT_UPDATE_FIRMWARE
+ if (nvt_fwu_wq) {
+ cancel_delayed_work_sync(&ts->nvt_fwu_work);
+ destroy_workqueue(nvt_fwu_wq);
+ nvt_fwu_wq = NULL;
+ }
+#endif
+
+#if WAKEUP_GESTURE
+ device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+
+ nvt_irq_enable(false);
+ free_irq(client->irq, ts);
+
+ mutex_destroy(&ts->xbuf_lock);
+ mutex_destroy(&ts->lock);
+
+ nvt_gpio_deconfig(ts);
+
+ if (ts->input_dev) {
+ input_unregister_device(ts->input_dev);
+ ts->input_dev = NULL;
+ }
+
+ i2c_set_clientdata(client, NULL);
+
+ if (ts) {
+ kfree(ts);
+ ts = NULL;
+ }
+
+ return 0;
+}
+
+static void nvt_ts_shutdown(struct i2c_client *client)
+{
+ NVT_LOG("Shutdown driver...\n");
+
+ nvt_irq_enable(false);
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+ if (msm_drm_unregister_client(&ts->drm_notif))
+ NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+ if (fb_unregister_client(&ts->fb_notif))
+ NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#if NVT_TOUCH_MP
+ nvt_mp_proc_deinit();
+#endif
+#if NVT_TOUCH_EXT_PROC
+ nvt_extra_proc_deinit();
+#endif
+#if NVT_TOUCH_PROC
+ nvt_flash_proc_deinit();
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+ if (nvt_esd_check_wq) {
+ cancel_delayed_work_sync(&nvt_esd_check_work);
+ nvt_esd_check_enable(false);
+ destroy_workqueue(nvt_esd_check_wq);
+ nvt_esd_check_wq = NULL;
+ }
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if BOOT_UPDATE_FIRMWARE
+ if (nvt_fwu_wq) {
+ cancel_delayed_work_sync(&ts->nvt_fwu_work);
+ destroy_workqueue(nvt_fwu_wq);
+ nvt_fwu_wq = NULL;
+ }
+#endif
+
+#if WAKEUP_GESTURE
+ device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen driver suspend function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_ts_suspend(struct device *dev)
+{
+ uint8_t buf[4] = {0};
+#if MT_PROTOCOL_B
+ uint32_t i = 0;
+#endif
+
+ if (!bTouchIsAwake) {
+ NVT_LOG("Touch is already suspend\n");
+ return 0;
+ }
+
+#if !WAKEUP_GESTURE
+ nvt_irq_enable(false);
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+ NVT_LOG("cancel delayed work sync\n");
+ cancel_delayed_work_sync(&nvt_esd_check_work);
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ mutex_lock(&ts->lock);
+
+ NVT_LOG("start\n");
+
+ bTouchIsAwake = 0;
+
+#if WAKEUP_GESTURE
+ //---write command to enter "wakeup gesture mode"---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x13;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+ enable_irq_wake(ts->client->irq);
+
+ NVT_LOG("Enabled touch wakeup gesture\n");
+
+#else // WAKEUP_GESTURE
+ //---write command to enter "deep sleep mode"---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x11;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+#endif // WAKEUP_GESTURE
+
+ mutex_unlock(&ts->lock);
+
+ /* release all touches */
+#if MT_PROTOCOL_B
+ for (i = 0; i < ts->max_touch_num; i++) {
+ input_mt_slot(ts->input_dev, i);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+ }
+#endif
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
+#if !MT_PROTOCOL_B
+ input_mt_sync(ts->input_dev);
+#endif
+ input_sync(ts->input_dev);
+
+ msleep(50);
+
+ NVT_LOG("end\n");
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen driver resume function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_ts_resume(struct device *dev)
+{
+ if (bTouchIsAwake) {
+ NVT_LOG("Touch is already resume\n");
+ return 0;
+ }
+
+ mutex_lock(&ts->lock);
+
+ NVT_LOG("start\n");
+
+ // please make sure display reset(RESX) sequence and mipi dsi cmds sent before this
+#if NVT_TOUCH_SUPPORT_HW_RST
+ gpio_set_value(ts->reset_gpio, 1);
+#endif
+
+ // need to uncomment the following code for NT36672, NT36772 IC due to no boot-load when RESX/TP_RESX
+ //nvt_bootloader_reset();
+ if (nvt_check_fw_reset_state(RESET_STATE_REK)) {
+ NVT_ERR("FW is not ready! Try to bootloader reset...\n");
+ nvt_bootloader_reset();
+ nvt_check_fw_reset_state(RESET_STATE_REK);
+ }
+
+#if !WAKEUP_GESTURE
+ nvt_irq_enable(true);
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ bTouchIsAwake = 1;
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("end\n");
+
+ return 0;
+}
+
+
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+ struct msm_drm_notifier *evdata = data;
+ int *blank;
+ struct nvt_ts_data *ts =
+ container_of(self, struct nvt_ts_data, drm_notif);
+
+ if (!evdata || (evdata->id != 0))
+ return 0;
+
+ if (evdata->data && ts) {
+ blank = evdata->data;
+ if (event == MSM_DRM_EARLY_EVENT_BLANK) {
+ if (*blank == MSM_DRM_BLANK_POWERDOWN) {
+ NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+ nvt_ts_suspend(&ts->client->dev);
+ }
+ } else if (event == MSM_DRM_EVENT_BLANK) {
+ if (*blank == MSM_DRM_BLANK_UNBLANK) {
+ NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+ nvt_ts_resume(&ts->client->dev);
+ }
+ }
+ }
+
+ return 0;
+}
+#else
+static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct nvt_ts_data *ts =
+ container_of(self, struct nvt_ts_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_POWERDOWN) {
+ NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+ nvt_ts_suspend(&ts->client->dev);
+ }
+ } else if (evdata && evdata->data && event == FB_EVENT_BLANK) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK) {
+ NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+ nvt_ts_resume(&ts->client->dev);
+ }
+ }
+
+ return 0;
+}
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+/*******************************************************
+Description:
+ Novatek touchscreen driver early suspend function.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_ts_early_suspend(struct early_suspend *h)
+{
+ nvt_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen driver late resume function.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_ts_late_resume(struct early_suspend *h)
+{
+ nvt_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id nvt_ts_id[] = {
+ { NVT_I2C_NAME, 0 },
+ { }
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id nvt_match_table[] = {
+ { .compatible = "novatek,NVT-ts",},
+ { },
+};
+#endif
+
+static struct i2c_driver nvt_i2c_driver = {
+ .probe = nvt_ts_probe,
+ .remove = nvt_ts_remove,
+ .shutdown = nvt_ts_shutdown,
+ .id_table = nvt_ts_id,
+ .driver = {
+ .name = NVT_I2C_NAME,
+#ifdef CONFIG_OF
+ .of_match_table = nvt_match_table,
+#endif
+ },
+};
+
+/*******************************************************
+Description:
+ Driver Install function.
+
+return:
+ Executive Outcomes. 0---succeed. not 0---failed.
+********************************************************/
+static int32_t __init nvt_driver_init(void)
+{
+ int32_t ret = 0;
+
+ NVT_LOG("start\n");
+ //---add i2c driver---
+ ret = i2c_add_driver(&nvt_i2c_driver);
+ if (ret) {
+ NVT_ERR("failed to add i2c driver");
+ goto err_driver;
+ }
+
+ NVT_LOG("finished\n");
+
+err_driver:
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Driver uninstall function.
+
+return:
+ n.a.
+********************************************************/
+static void __exit nvt_driver_exit(void)
+{
+ i2c_del_driver(&nvt_i2c_driver);
+}
+
+//late_initcall(nvt_driver_init);
+module_init(nvt_driver_init);
+module_exit(nvt_driver_exit);
+
+MODULE_DESCRIPTION("Novatek Touchscreen Driver");
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx.h b/drivers/input/touchscreen/nt36xxx/nt36xxx.h
new file mode 100644
index 0000000..0a3daf3
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+#ifndef _LINUX_NVT_TOUCH_H
+#define _LINUX_NVT_TOUCH_H
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "nt36xxx_mem_map.h"
+
+#define NVT_DEBUG 1
+
+//---GPIO number---
+#define NVTTOUCH_RST_PIN 980
+#define NVTTOUCH_INT_PIN 943
+
+
+//---INT trigger mode---
+//#define IRQ_TYPE_EDGE_RISING 1
+//#define IRQ_TYPE_EDGE_FALLING 2
+#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING
+
+
+//---I2C driver info.---
+#define NVT_I2C_NAME "NVT-ts"
+#define I2C_BLDR_Address 0x01
+#define I2C_FW_Address 0x01
+#define I2C_HW_Address 0x62
+
+#if NVT_DEBUG
+#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+#else
+#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+#endif
+#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+
+//---Input device info.---
+#define NVT_TS_NAME "NVTCapacitiveTouchScreen"
+
+
+//---Touch info.---
+#define TOUCH_DEFAULT_MAX_WIDTH 1080
+#define TOUCH_DEFAULT_MAX_HEIGHT 1920
+#define TOUCH_MAX_FINGER_NUM 10
+#define TOUCH_KEY_NUM 0
+#if TOUCH_KEY_NUM > 0
+extern const uint16_t touch_key_array[TOUCH_KEY_NUM];
+#endif
+#define TOUCH_FORCE_NUM 1000
+
+/* Enable only when module have tp reset pin and connected to host */
+#define NVT_TOUCH_SUPPORT_HW_RST 0
+
+//---Customerized func.---
+#define NVT_TOUCH_PROC 1
+#define NVT_TOUCH_EXT_PROC 1
+#define NVT_TOUCH_MP 1
+#define MT_PROTOCOL_B 1
+#define WAKEUP_GESTURE 1
+#if WAKEUP_GESTURE
+extern const uint16_t gesture_key_array[];
+#endif
+#define BOOT_UPDATE_FIRMWARE 0
+#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin"
+
+//---ESD Protect.---
+#define NVT_TOUCH_ESD_PROTECT 0
+#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */
+
+struct nvt_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct delayed_work nvt_fwu_work;
+ uint16_t addr;
+ int8_t phys[32];
+#if defined(CONFIG_FB)
+#ifdef _MSM_DRM_NOTIFY_H_
+ struct notifier_block drm_notif;
+#else
+ struct notifier_block fb_notif;
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+ uint8_t fw_ver;
+ uint8_t x_num;
+ uint8_t y_num;
+ uint16_t abs_x_max;
+ uint16_t abs_y_max;
+ uint8_t max_touch_num;
+ uint8_t max_button_num;
+ uint32_t int_trigger_type;
+ int32_t irq_gpio;
+ uint32_t irq_flags;
+ int32_t reset_gpio;
+ uint32_t reset_flags;
+ struct mutex lock;
+ const struct nvt_ts_mem_map *mmap;
+ uint8_t carrier_system;
+ uint16_t nvt_pid;
+ uint8_t xbuf[1025];
+ struct mutex xbuf_lock;
+ bool irq_enabled;
+};
+
+#if NVT_TOUCH_PROC
+struct nvt_flash_data{
+ rwlock_t lock;
+ struct i2c_client *client;
+};
+#endif
+
+typedef enum {
+ RESET_STATE_INIT = 0xA0,// IC reset
+ RESET_STATE_REK, // ReK baseline
+ RESET_STATE_REK_FINISH, // baseline is ready
+ RESET_STATE_NORMAL_RUN, // normal run
+ RESET_STATE_MAX = 0xAF
+} RST_COMPLETE_STATE;
+
+typedef enum {
+ EVENT_MAP_HOST_CMD = 0x50,
+ EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51,
+ EVENT_MAP_RESET_COMPLETE = 0x60,
+ EVENT_MAP_FWINFO = 0x78,
+ EVENT_MAP_PROJECTID = 0x9A,
+} I2C_EVENT_MAP;
+
+//---extern structures---
+extern struct nvt_ts_data *ts;
+
+//---extern functions---
+extern int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len);
+extern int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len);
+extern void nvt_bootloader_reset(void);
+extern void nvt_sw_reset_idle(void);
+extern int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state);
+extern int32_t nvt_get_fw_info(void);
+extern int32_t nvt_clear_fw_status(void);
+extern int32_t nvt_check_fw_status(void);
+extern int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr);
+#if NVT_TOUCH_ESD_PROTECT
+extern void nvt_esd_check_enable(uint8_t enable);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+extern void nvt_stop_crc_reboot(void);
+
+#endif /* _LINUX_NVT_TOUCH_H */
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nt36xxx/nt36xxx_ext_proc.c
new file mode 100644
index 0000000..d58c037
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx_ext_proc.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "nt36xxx.h"
+
+#if NVT_TOUCH_EXT_PROC
+#define NVT_FW_VERSION "nvt_fw_version"
+#define NVT_BASELINE "nvt_baseline"
+#define NVT_RAW "nvt_raw"
+#define NVT_DIFF "nvt_diff"
+
+#define BUS_TRANSFER_LENGTH 64
+
+#define NORMAL_MODE 0x00
+#define TEST_MODE_1 0x21
+#define TEST_MODE_2 0x22
+#define HANDSHAKING_HOST_READY 0xBB
+
+#define XDATA_SECTOR_SIZE 256
+
+static uint8_t xdata_tmp[2048] = {0};
+static int32_t xdata[2048] = {0};
+
+static struct proc_dir_entry *NVT_proc_fw_version_entry;
+static struct proc_dir_entry *NVT_proc_baseline_entry;
+static struct proc_dir_entry *NVT_proc_raw_entry;
+static struct proc_dir_entry *NVT_proc_diff_entry;
+
+/*******************************************************
+Description:
+ Novatek touchscreen change mode function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_change_mode(uint8_t mode)
+{
+ uint8_t buf[8] = {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ //---set mode---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = mode;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+ if (mode == NORMAL_MODE) {
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = HANDSHAKING_HOST_READY;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+ msleep(20);
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen get firmware pipe function.
+
+return:
+ Executive outcomes. 0---pipe 0. 1---pipe 1.
+*******************************************************/
+uint8_t nvt_get_fw_pipe(void)
+{
+ uint8_t buf[8]= {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+ //---read fw status---
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+ //NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]);
+
+ return (buf[1] & 0x01);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen read meta data function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr)
+{
+ int32_t i = 0;
+ int32_t j = 0;
+ int32_t k = 0;
+ uint8_t buf[BUS_TRANSFER_LENGTH + 1] = {0};
+ uint32_t head_addr = 0;
+ int32_t dummy_len = 0;
+ int32_t data_len = 0;
+ int32_t residual_len = 0;
+
+ //---set xdata sector address & length---
+ head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE);
+ dummy_len = xdata_addr - head_addr;
+ data_len = ts->x_num * ts->y_num * 2;
+ residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE;
+
+ //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", head_addr, dummy_len, data_len, residual_len);
+
+ //read xdata : step 1
+ for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) {
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, head_addr + XDATA_SECTOR_SIZE * i);
+
+ //---read xdata by BUS_TRANSFER_LENGTH
+ for (j = 0; j < (XDATA_SECTOR_SIZE / BUS_TRANSFER_LENGTH); j++) {
+ //---read data---
+ buf[0] = BUS_TRANSFER_LENGTH * j;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1);
+
+ //---copy buf to xdata_tmp---
+ for (k = 0; k < BUS_TRANSFER_LENGTH; k++) {
+ xdata_tmp[XDATA_SECTOR_SIZE * i + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1];
+ //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + BUS_TRANSFER_LENGTH*j + k));
+ }
+ }
+ //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i));
+ }
+
+ //read xdata : step2
+ if (residual_len != 0) {
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, xdata_addr + data_len - residual_len);
+
+ //---read xdata by BUS_TRANSFER_LENGTH
+ for (j = 0; j < (residual_len / BUS_TRANSFER_LENGTH + 1); j++) {
+ //---read data---
+ buf[0] = BUS_TRANSFER_LENGTH * j;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1);
+
+ //---copy buf to xdata_tmp---
+ for (k = 0; k < BUS_TRANSFER_LENGTH; k++) {
+ xdata_tmp[(dummy_len + data_len - residual_len) + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1];
+ //printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + BUS_TRANSFER_LENGTH*j + k));
+ }
+ }
+ //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len));
+ }
+
+ //---remove dummy data and 2bytes-to-1data---
+ for (i = 0; i < (data_len / 2); i++) {
+ xdata[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]);
+ }
+
+#if TOUCH_KEY_NUM > 0
+ //read button xdata : step3
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, xdata_btn_addr);
+
+ //---read data---
+ buf[0] = (xdata_btn_addr & 0xFF);
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, (TOUCH_KEY_NUM * 2 + 1));
+
+ //---2bytes-to-1data---
+ for (i = 0; i < TOUCH_KEY_NUM; i++) {
+ xdata[ts->x_num * ts->y_num + i] = (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]);
+ }
+#endif
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR);
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen get meta data function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num)
+{
+ *m_x_num = ts->x_num;
+ *m_y_num = ts->y_num;
+ memcpy(buf, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t)));
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen firmware version show function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_fw_version_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num);
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen xdata sequence print show
+ function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_show(struct seq_file *m, void *v)
+{
+ int32_t i = 0;
+ int32_t j = 0;
+
+ for (i = 0; i < ts->y_num; i++) {
+ for (j = 0; j < ts->x_num; j++) {
+ seq_printf(m, "%5d, ", xdata[i * ts->x_num + j]);
+ }
+ seq_puts(m, "\n");
+ }
+
+#if TOUCH_KEY_NUM > 0
+ for (i = 0; i < TOUCH_KEY_NUM; i++) {
+ seq_printf(m, "%5d, ", xdata[ts->x_num * ts->y_num + i]);
+ }
+ seq_puts(m, "\n");
+#endif
+
+ seq_printf(m, "\n\n");
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen xdata sequence print start
+ function.
+
+return:
+ Executive outcomes. 1---call next function.
+ NULL---not call next function and sequence loop
+ stop.
+*******************************************************/
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen xdata sequence print next
+ function.
+
+return:
+ Executive outcomes. NULL---no next and call sequence
+ stop function.
+*******************************************************/
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen xdata sequence print stop
+ function.
+
+return:
+ n.a.
+*******************************************************/
+static void c_stop(struct seq_file *m, void *v)
+{
+ return;
+}
+
+const struct seq_operations nvt_fw_version_seq_ops = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_fw_version_show
+};
+
+const struct seq_operations nvt_seq_ops = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/nvt_fw_version open
+ function.
+
+return:
+ n.a.
+*******************************************************/
+static int32_t nvt_fw_version_open(struct inode *inode, struct file *file)
+{
+ if (mutex_lock_interruptible(&ts->lock)) {
+ return -ERESTARTSYS;
+ }
+
+ NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ if (nvt_get_fw_info()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("--\n");
+
+ return seq_open(file, &nvt_fw_version_seq_ops);
+}
+
+static const struct file_operations nvt_fw_version_fops = {
+ .owner = THIS_MODULE,
+ .open = nvt_fw_version_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/nvt_baseline open function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_baseline_open(struct inode *inode, struct file *file)
+{
+ if (mutex_lock_interruptible(&ts->lock)) {
+ return -ERESTARTSYS;
+ }
+
+ NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ if (nvt_clear_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ nvt_change_mode(TEST_MODE_2);
+
+ if (nvt_check_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_info()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR);
+
+ nvt_change_mode(NORMAL_MODE);
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("--\n");
+
+ return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct file_operations nvt_baseline_fops = {
+ .owner = THIS_MODULE,
+ .open = nvt_baseline_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/nvt_raw open function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_raw_open(struct inode *inode, struct file *file)
+{
+ if (mutex_lock_interruptible(&ts->lock)) {
+ return -ERESTARTSYS;
+ }
+
+ NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ if (nvt_clear_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ nvt_change_mode(TEST_MODE_2);
+
+ if (nvt_check_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_info()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_pipe() == 0)
+ nvt_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR);
+ else
+ nvt_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR);
+
+ nvt_change_mode(NORMAL_MODE);
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("--\n");
+
+ return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct file_operations nvt_raw_fops = {
+ .owner = THIS_MODULE,
+ .open = nvt_raw_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/nvt_diff open function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+static int32_t nvt_diff_open(struct inode *inode, struct file *file)
+{
+ if (mutex_lock_interruptible(&ts->lock)) {
+ return -ERESTARTSYS;
+ }
+
+ NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ if (nvt_clear_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ nvt_change_mode(TEST_MODE_2);
+
+ if (nvt_check_fw_status()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_info()) {
+ mutex_unlock(&ts->lock);
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_pipe() == 0)
+ nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+ else
+ nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+
+ nvt_change_mode(NORMAL_MODE);
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("--\n");
+
+ return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct file_operations nvt_diff_fops = {
+ .owner = THIS_MODULE,
+ .open = nvt_diff_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen extra function proc. file node
+ initial function.
+
+return:
+ Executive outcomes. 0---succeed. -12---failed.
+*******************************************************/
+int32_t nvt_extra_proc_init(void)
+{
+ NVT_proc_fw_version_entry = proc_create(NVT_FW_VERSION, 0444, NULL,&nvt_fw_version_fops);
+ if (NVT_proc_fw_version_entry == NULL) {
+ NVT_ERR("create proc/%s Failed!\n", NVT_FW_VERSION);
+ return -ENOMEM;
+ } else {
+ NVT_LOG("create proc/%s Succeeded!\n", NVT_FW_VERSION);
+ }
+
+ NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL,&nvt_baseline_fops);
+ if (NVT_proc_baseline_entry == NULL) {
+ NVT_ERR("create proc/%s Failed!\n", NVT_BASELINE);
+ return -ENOMEM;
+ } else {
+ NVT_LOG("create proc/%s Succeeded!\n", NVT_BASELINE);
+ }
+
+ NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL,&nvt_raw_fops);
+ if (NVT_proc_raw_entry == NULL) {
+ NVT_ERR("create proc/%s Failed!\n", NVT_RAW);
+ return -ENOMEM;
+ } else {
+ NVT_LOG("create proc/%s Succeeded!\n", NVT_RAW);
+ }
+
+ NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL,&nvt_diff_fops);
+ if (NVT_proc_diff_entry == NULL) {
+ NVT_ERR("create proc/%s Failed!\n", NVT_DIFF);
+ return -ENOMEM;
+ } else {
+ NVT_LOG("create proc/%s Succeeded!\n", NVT_DIFF);
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen extra function proc. file node
+ deinitial function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_extra_proc_deinit(void)
+{
+ if (NVT_proc_fw_version_entry != NULL) {
+ remove_proc_entry(NVT_FW_VERSION, NULL);
+ NVT_proc_fw_version_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", NVT_FW_VERSION);
+ }
+
+ if (NVT_proc_baseline_entry != NULL) {
+ remove_proc_entry(NVT_BASELINE, NULL);
+ NVT_proc_baseline_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", NVT_BASELINE);
+ }
+
+ if (NVT_proc_raw_entry != NULL) {
+ remove_proc_entry(NVT_RAW, NULL);
+ NVT_proc_raw_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", NVT_RAW);
+ }
+
+ if (NVT_proc_diff_entry != NULL) {
+ remove_proc_entry(NVT_DIFF, NULL);
+ NVT_proc_diff_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", NVT_DIFF);
+ }
+}
+#endif
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx_fw_update.c b/drivers/input/touchscreen/nt36xxx/nt36xxx_fw_update.c
new file mode 100644
index 0000000..52086dc
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx_fw_update.c
@@ -0,0 +1,1037 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/firmware.h>
+
+#include "nt36xxx.h"
+
+#if BOOT_UPDATE_FIRMWARE
+
+#define SIZE_4KB 4096
+#define FLASH_SECTOR_SIZE SIZE_4KB
+#define SIZE_64KB 65536
+#define BLOCK_64KB_NUM 4
+#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB)
+#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1)
+
+#define NVT_FLASH_END_FLAG_LEN 3
+#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN)
+
+const struct firmware *fw_entry = NULL;
+static size_t fw_need_write_size = 0;
+
+static int32_t nvt_get_fw_need_write_size(const struct firmware *fw_entry)
+{
+ int32_t i = 0;
+ int32_t total_sectors_to_check = 0;
+
+ total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE;
+ /* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */
+
+ for (i = total_sectors_to_check; i > 0; i--) {
+ /* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */
+ /* check if there is end flag "NVT" at the end of this sector */
+ if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) {
+ fw_need_write_size = i * FLASH_SECTOR_SIZE;
+ NVT_LOG("fw_need_write_size = %zu(0x%zx)\n", fw_need_write_size, fw_need_write_size);
+ return 0;
+ }
+ }
+
+ NVT_ERR("end flag \"NVT\" not found!\n");
+ return -1;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen request update firmware function.
+
+return:
+ Executive outcomes. 0---succeed. -1,-22---failed.
+*******************************************************/
+int32_t update_firmware_request(char *filename)
+{
+ int32_t ret = 0;
+
+ if (NULL == filename) {
+ return -1;
+ }
+
+ NVT_LOG("filename is %s\n", filename);
+
+ ret = request_firmware_nowarn(&fw_entry, filename, &ts->client->dev);
+ if (ret) {
+ NVT_ERR("firmware load failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ // check FW need to write size
+ if (nvt_get_fw_need_write_size(fw_entry)) {
+ NVT_ERR("get fw need to write size fail!\n");
+ return -EINVAL;
+ }
+
+ // check if FW version add FW version bar equals 0xFF
+ if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) {
+ NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n");
+ NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen release update firmware function.
+
+return:
+ n.a.
+*******************************************************/
+void update_firmware_release(void)
+{
+ if (fw_entry) {
+ release_firmware(fw_entry);
+ }
+ fw_entry=NULL;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check firmware version function.
+
+return:
+ Executive outcomes. 0---need update. 1---need not
+ update.
+*******************************************************/
+int32_t Check_FW_Ver(void)
+{
+ uint8_t buf[16] = {0};
+ int32_t ret = 0;
+
+ //write i2c index to EVENT BUF ADDR
+ ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
+ if (ret < 0) {
+ NVT_ERR("i2c write error!(%d)\n", ret);
+ return ret;
+ }
+
+ //read Firmware Version
+ buf[0] = EVENT_MAP_FWINFO;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("i2c read error!(%d)\n", ret);
+ return ret;
+ }
+
+ NVT_LOG("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]);
+ NVT_LOG("Bin FW Ver = 0x%02X, FW ver Bar = 0x%02X\n",
+ fw_entry->data[FW_BIN_VER_OFFSET], fw_entry->data[FW_BIN_VER_BAR_OFFSET]);
+
+ // check IC FW_VER + FW_VER_BAR equals 0xFF or not, need to update if not
+ if ((buf[1] + buf[2]) != 0xFF) {
+ NVT_ERR("IC FW_VER + FW_VER_BAR not equals to 0xFF!\n");
+ return 0;
+ }
+
+ // compare IC and binary FW version
+ if (buf[1] > fw_entry->data[FW_BIN_VER_OFFSET])
+ return 1;
+ else
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen resume from deep power down function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Resume_PD(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t ret = 0;
+ int32_t retry = 0;
+
+ // Resume Command
+ buf[0] = 0x00;
+ buf[1] = 0xAB;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Write Enable error!!(%d)\n", ret);
+ return ret;
+ }
+
+ // Check 0xAA (Resume Command)
+ retry = 0;
+ while(1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Resume Command) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Resume Command) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+ msleep(10);
+
+ NVT_LOG("Resume PD OK\n");
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check firmware checksum function.
+
+return:
+ Executive outcomes. 0---checksum not match.
+ 1---checksum match. -1--- checksum read failed.
+*******************************************************/
+int32_t Check_CheckSum(void)
+{
+ uint8_t buf[64] = {0};
+ uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR;
+ int32_t ret = 0;
+ int32_t i = 0;
+ int32_t k = 0;
+ uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0};
+ uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0};
+ size_t len_in_blk = 0;
+ int32_t retry = 0;
+
+ if (Resume_PD()) {
+ NVT_ERR("Resume PD error!!\n");
+ return -1;
+ }
+
+ for (i = 0; i < BLOCK_64KB_NUM; i++) {
+ if (fw_need_write_size > (i * SIZE_64KB)) {
+ // Calculate WR_Filechksum of each 64KB block
+ len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB);
+ WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF);
+ for (k = 0; k < len_in_blk; k++) {
+ WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB];
+ }
+ WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1;
+
+ // Fast Read Command
+ buf[0] = 0x00;
+ buf[1] = 0x07;
+ buf[2] = i;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = ((len_in_blk - 1) >> 8) & 0xFF;
+ buf[6] = (len_in_blk - 1) & 0xFF;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+ if (ret < 0) {
+ NVT_ERR("Fast Read Command error!!(%d)\n", ret);
+ return ret;
+ }
+ // Check 0xAA (Fast Read Command)
+ retry = 0;
+ while (1) {
+ msleep(80);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 5)) {
+ NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+ return -1;
+ }
+ }
+ // Read Checksum (write addr high byte & middle byte)
+ ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+ if (ret < 0) {
+ NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret);
+ return ret;
+ }
+ // Read Checksum
+ buf[0] = (XDATA_Addr) & 0xFF;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Read Checksum error!!(%d)\n", ret);
+ return ret;
+ }
+
+ RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
+ if (WR_Filechksum[i] != RD_Filechksum[i]) {
+ NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]);
+ NVT_ERR("firmware checksum not match!!\n");
+ return 0;
+ }
+ }
+ }
+
+ NVT_LOG("firmware checksum match\n");
+ return 1;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen initial bootloader and flash
+ block function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Init_BootLoader(void)
+{
+ uint8_t buf[64] = {0};
+ int32_t ret = 0;
+ int32_t retry = 0;
+
+ // SW Reset & Idle
+ nvt_sw_reset_idle();
+
+ // Initiate Flash Block
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = I2C_FW_Address;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Inittial Flash Block error!!(%d)\n", ret);
+ return ret;
+ }
+
+ // Check 0xAA (Initiate Flash Block)
+ retry = 0;
+ while(1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Inittial Flash Block) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Inittial Flash Block) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+
+ NVT_LOG("Init OK \n");
+ msleep(20);
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen erase flash sectors function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Erase_Flash(void)
+{
+ uint8_t buf[64] = {0};
+ int32_t ret = 0;
+ int32_t count = 0;
+ int32_t i = 0;
+ int32_t Flash_Address = 0;
+ int32_t retry = 0;
+
+ // Write Enable
+ buf[0] = 0x00;
+ buf[1] = 0x06;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Write Enable (for Write Status Register) error!!(%d)\n", ret);
+ return ret;
+ }
+ // Check 0xAA (Write Enable)
+ retry = 0;
+ while (1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+
+ // Write Status Register
+ buf[0] = 0x00;
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Write Status Register error!!(%d)\n", ret);
+ return ret;
+ }
+ // Check 0xAA (Write Status Register)
+ retry = 0;
+ while (1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Write Status Register) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Write Status Register) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+
+ // Read Status
+ retry = 0;
+ while (1) {
+ msleep(5);
+ buf[0] = 0x00;
+ buf[1] = 0x05;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Read Status (for Write Status Register) error!!(%d)\n", ret);
+ return ret;
+ }
+
+ // Check 0xAA (Read Status)
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Read Status for Write Status Register) error!!(%d)\n", ret);
+ return ret;
+ }
+ if ((buf[1] == 0xAA) && (buf[2] == 0x00)) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 100)) {
+ NVT_ERR("Check 0xAA (Read Status for Write Status Register) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+ return -1;
+ }
+ }
+
+ if (fw_need_write_size % FLASH_SECTOR_SIZE)
+ count = fw_need_write_size / FLASH_SECTOR_SIZE + 1;
+ else
+ count = fw_need_write_size / FLASH_SECTOR_SIZE;
+
+ for(i = 0; i < count; i++) {
+ // Write Enable
+ buf[0] = 0x00;
+ buf[1] = 0x06;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Write Enable error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ // Check 0xAA (Write Enable)
+ retry = 0;
+ while (1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+
+ Flash_Address = i * FLASH_SECTOR_SIZE;
+
+ // Sector Erase
+ buf[0] = 0x00;
+ buf[1] = 0x20; // Command : Sector Erase
+ buf[2] = ((Flash_Address >> 16) & 0xFF);
+ buf[3] = ((Flash_Address >> 8) & 0xFF);
+ buf[4] = (Flash_Address & 0xFF);
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 5);
+ if (ret < 0) {
+ NVT_ERR("Sector Erase error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ // Check 0xAA (Sector Erase)
+ retry = 0;
+ while (1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Sector Erase) error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Sector Erase) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+ return -1;
+ }
+ }
+
+ // Read Status
+ retry = 0;
+ while (1) {
+ msleep(5);
+ buf[0] = 0x00;
+ buf[1] = 0x05;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Read Status error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+
+ // Check 0xAA (Read Status)
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Read Status) error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ if ((buf[1] == 0xAA) && (buf[2] == 0x00)) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 100)) {
+ NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+ return -1;
+ }
+ }
+ }
+
+ NVT_LOG("Erase OK \n");
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen write flash sectors function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Write_Flash(void)
+{
+ uint8_t buf[64] = {0};
+ uint32_t XDATA_Addr = ts->mmap->RW_FLASH_DATA_ADDR;
+ uint32_t Flash_Address = 0;
+ int32_t i = 0, j = 0, k = 0;
+ uint8_t tmpvalue = 0;
+ int32_t count = 0;
+ int32_t ret = 0;
+ int32_t retry = 0;
+ int32_t percent = 0;
+ int32_t previous_percent = -1;
+
+ // change I2C buffer index
+ ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+ if (ret < 0) {
+ NVT_ERR("change I2C buffer index error!!(%d)\n", ret);
+ return ret;
+ }
+
+ if (fw_need_write_size % 256)
+ count = fw_need_write_size / 256 + 1;
+ else
+ count = fw_need_write_size / 256;
+
+ for (i = 0; i < count; i++) {
+ Flash_Address = i * 256;
+
+ // Write Enable
+ buf[0] = 0x00;
+ buf[1] = 0x06;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Write Enable error!!(%d)\n", ret);
+ return ret;
+ }
+ // Check 0xAA (Write Enable)
+ retry = 0;
+ while (1) {
+ udelay(100);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+ }
+
+ // Write Page : 256 bytes
+ for (j = 0; j < min(fw_need_write_size - i * 256, (size_t)256); j += 32) {
+ buf[0] = (XDATA_Addr + j) & 0xFF;
+ for (k = 0; k < 32; k++) {
+ buf[1 + k] = fw_entry->data[Flash_Address + j + k];
+ }
+ ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 33);
+ if (ret < 0) {
+ NVT_ERR("Write Page error!!(%d), j=%d\n", ret, j);
+ return ret;
+ }
+ }
+ if (fw_need_write_size - Flash_Address >= 256)
+ tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (255);
+ else
+ tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (fw_need_write_size - Flash_Address - 1);
+
+ for (k = 0; k < min(fw_need_write_size - Flash_Address, (size_t)256); k++)
+ tmpvalue += fw_entry->data[Flash_Address + k];
+
+ tmpvalue = 255 - tmpvalue + 1;
+
+ // Page Program
+ buf[0] = 0x00;
+ buf[1] = 0x02;
+ buf[2] = ((Flash_Address >> 16) & 0xFF);
+ buf[3] = ((Flash_Address >> 8) & 0xFF);
+ buf[4] = (Flash_Address & 0xFF);
+ buf[5] = 0x00;
+ buf[6] = min(fw_need_write_size - Flash_Address, (size_t)256) - 1;
+ buf[7] = tmpvalue;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 8);
+ if (ret < 0) {
+ NVT_ERR("Page Program error!!(%d), i=%d\n", ret, i);
+ return ret;
+ }
+ // Check 0xAA (Page Program)
+ retry = 0;
+ while (1) {
+ msleep(1);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Page Program error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA || buf[1] == 0xEA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 20)) {
+ NVT_ERR("Check 0xAA (Page Program) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+ return -1;
+ }
+ }
+ if (buf[1] == 0xEA) {
+ NVT_ERR("Page Program error!! i=%d\n", i);
+ return -3;
+ }
+
+ // Read Status
+ retry = 0;
+ while (1) {
+ msleep(5);
+ buf[0] = 0x00;
+ buf[1] = 0x05;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Read Status error!!(%d)\n", ret);
+ return ret;
+ }
+
+ // Check 0xAA (Read Status)
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Read Status) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (((buf[1] == 0xAA) && (buf[2] == 0x00)) || (buf[1] == 0xEA)) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 100)) {
+ NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+ return -1;
+ }
+ }
+ if (buf[1] == 0xEA) {
+ NVT_ERR("Page Program error!! i=%d\n", i);
+ return -4;
+ }
+
+ percent = ((i + 1) * 100) / count;
+ if (((percent % 10) == 0) && (percent != previous_percent)) {
+ NVT_LOG("Programming...%2d%%\n", percent);
+ previous_percent = percent;
+ }
+ }
+
+ NVT_LOG("Program OK \n");
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen verify checksum of written
+ flash function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Verify_Flash(void)
+{
+ uint8_t buf[64] = {0};
+ uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR;
+ int32_t ret = 0;
+ int32_t i = 0;
+ int32_t k = 0;
+ uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0};
+ uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0};
+ size_t len_in_blk = 0;
+ int32_t retry = 0;
+
+ for (i = 0; i < BLOCK_64KB_NUM; i++) {
+ if (fw_need_write_size > (i * SIZE_64KB)) {
+ // Calculate WR_Filechksum of each 64KB block
+ len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB);
+ WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF);
+ for (k = 0; k < len_in_blk; k++) {
+ WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB];
+ }
+ WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1;
+
+ // Fast Read Command
+ buf[0] = 0x00;
+ buf[1] = 0x07;
+ buf[2] = i;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = ((len_in_blk - 1) >> 8) & 0xFF;
+ buf[6] = (len_in_blk - 1) & 0xFF;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+ if (ret < 0) {
+ NVT_ERR("Fast Read Command error!!(%d)\n", ret);
+ return ret;
+ }
+ // Check 0xAA (Fast Read Command)
+ retry = 0;
+ while (1) {
+ msleep(80);
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] == 0xAA) {
+ break;
+ }
+ retry++;
+ if (unlikely(retry > 5)) {
+ NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+ return -1;
+ }
+ }
+ // Read Checksum (write addr high byte & middle byte)
+ ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+ if (ret < 0) {
+ NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret);
+ return ret;
+ }
+ // Read Checksum
+ buf[0] = (XDATA_Addr) & 0xFF;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+ if (ret < 0) {
+ NVT_ERR("Read Checksum error!!(%d)\n", ret);
+ return ret;
+ }
+
+ RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
+ if (WR_Filechksum[i] != RD_Filechksum[i]) {
+ NVT_ERR("Verify Fail%d!!\n", i);
+ NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]);
+ return -1;
+ }
+ }
+ }
+
+ NVT_LOG("Verify OK \n");
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen update firmware function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Update_Firmware(void)
+{
+ int32_t ret = 0;
+
+ //---Stop CRC check to prevent IC auto reboot---
+ nvt_stop_crc_reboot();
+
+ // Step 1 : initial bootloader
+ ret = Init_BootLoader();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 2 : Resume PD
+ ret = Resume_PD();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 3 : Erase
+ ret = Erase_Flash();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 4 : Program
+ ret = Write_Flash();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 5 : Verify
+ ret = Verify_Flash();
+ if (ret) {
+ return ret;
+ }
+
+ //Step 6 : Bootloader Reset
+ nvt_bootloader_reset();
+ nvt_check_fw_reset_state(RESET_STATE_INIT);
+ nvt_get_fw_info();
+
+ return ret;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen check flash end flag function.
+
+return:
+ Executive outcomes. 0---succeed. 1,negative---failed.
+*******************************************************/
+int32_t nvt_check_flash_end_flag(void)
+{
+ uint8_t buf[8] = {0};
+ uint8_t nvt_end_flag[NVT_FLASH_END_FLAG_LEN + 1] = {0};
+ int32_t ret = 0;
+
+ // Step 1 : initial bootloader
+ ret = Init_BootLoader();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 2 : Resume PD
+ ret = Resume_PD();
+ if (ret) {
+ return ret;
+ }
+
+ // Step 3 : unlock
+ buf[0] = 0x00;
+ buf[1] = 0x35;
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("write unlock error!!(%d)\n", ret);
+ return ret;
+ }
+ msleep(10);
+
+ //Step 4 : Flash Read Command
+ buf[0] = 0x00;
+ buf[1] = 0x03;
+ buf[2] = (NVT_FLASH_END_FLAG_ADDR >> 16) & 0xFF; //Addr_H
+ buf[3] = (NVT_FLASH_END_FLAG_ADDR >> 8) & 0xFF; //Addr_M
+ buf[4] = NVT_FLASH_END_FLAG_ADDR & 0xFF; //Addr_L
+ buf[5] = (NVT_FLASH_END_FLAG_LEN >> 8) & 0xFF; //Len_H
+ buf[6] = NVT_FLASH_END_FLAG_LEN & 0xFF; //Len_L
+ ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+ if (ret < 0) {
+ NVT_ERR("write Read Command error!!(%d)\n", ret);
+ return ret;
+ }
+ msleep(10);
+
+ // Check 0xAA (Read Command)
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+ if (ret < 0) {
+ NVT_ERR("Check 0xAA (Read Command) error!!(%d)\n", ret);
+ return ret;
+ }
+ if (buf[1] != 0xAA) {
+ NVT_ERR("Check 0xAA (Read Command) error!! status=0x%02X\n", buf[1]);
+ return -1;
+ }
+
+ msleep(10);
+
+ //Step 5 : Read Flash Data
+ ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->READ_FLASH_CHECKSUM_ADDR);
+ if (ret < 0) {
+ NVT_ERR("change index error!! (%d)\n", ret);
+ return ret;
+ }
+ msleep(10);
+
+ // Read Back
+ buf[0] = ts->mmap->READ_FLASH_CHECKSUM_ADDR & 0xFF;
+ ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 6);
+ if (ret < 0) {
+ NVT_ERR("Read Back error!! (%d)\n", ret);
+ return ret;
+ }
+
+ //buf[3:5] => NVT End Flag
+ strlcpy(nvt_end_flag, &buf[3], NVT_FLASH_END_FLAG_LEN);
+ NVT_LOG("nvt_end_flag=%s (%02X %02X %02X)\n", nvt_end_flag, buf[3], buf[4], buf[5]);
+
+ if (memcmp(nvt_end_flag, "NVT", NVT_FLASH_END_FLAG_LEN) == 0) {
+ return 0;
+ } else {
+ NVT_ERR("\"NVT\" end flag not found!\n");
+ return 1;
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen update firmware when booting
+ function.
+
+return:
+ n.a.
+*******************************************************/
+void Boot_Update_Firmware(struct work_struct *work)
+{
+ int32_t ret = 0;
+
+ char firmware_name[256] = "";
+
+ snprintf(firmware_name, sizeof(firmware_name),
+ BOOT_UPDATE_FIRMWARE_NAME);
+
+ // request bin file in "/etc/firmware"
+ ret = update_firmware_request(firmware_name);
+ if (ret) {
+ NVT_ERR("update_firmware_request failed. (%d)\n", ret);
+ return;
+ }
+
+ mutex_lock(&ts->lock);
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ nvt_sw_reset_idle();
+
+ ret = Check_CheckSum();
+
+ if (ret < 0) { // read firmware checksum failed
+ NVT_ERR("read firmware checksum failed\n");
+ Update_Firmware();
+ } else if ((ret == 0) && (Check_FW_Ver() == 0)) { // (fw checksum not match) && (bin fw version >= ic fw version)
+ NVT_LOG("firmware version not match\n");
+ Update_Firmware();
+ } else if (nvt_check_flash_end_flag()) {
+ NVT_LOG("check flash end flag failed\n");
+ Update_Firmware();
+ } else {
+ // Bootloader Reset
+ nvt_bootloader_reset();
+ ret = nvt_check_fw_reset_state(RESET_STATE_INIT);
+ if (ret) {
+ NVT_LOG("check fw reset state failed\n");
+ Update_Firmware();
+ }
+ }
+
+ mutex_unlock(&ts->lock);
+
+ update_firmware_release();
+}
+#endif /* BOOT_UPDATE_FIRMWARE */
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx_mem_map.h b/drivers/input/touchscreen/nt36xxx/nt36xxx_mem_map.h
new file mode 100644
index 0000000..94040a4
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx_mem_map.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 48764 $
+ * $Date: 2019-08-08 14:52:12 +0800 (Thu, 08 Aug 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+struct nvt_ts_mem_map {
+ uint32_t EVENT_BUF_ADDR;
+ uint32_t RAW_PIPE0_ADDR;
+ uint32_t RAW_PIPE1_ADDR;
+ uint32_t BASELINE_ADDR;
+ uint32_t BASELINE_BTN_ADDR;
+ uint32_t DIFF_PIPE0_ADDR;
+ uint32_t DIFF_PIPE1_ADDR;
+ uint32_t RAW_BTN_PIPE0_ADDR;
+ uint32_t RAW_BTN_PIPE1_ADDR;
+ uint32_t DIFF_BTN_PIPE0_ADDR;
+ uint32_t DIFF_BTN_PIPE1_ADDR;
+ uint32_t READ_FLASH_CHECKSUM_ADDR;
+ uint32_t RW_FLASH_DATA_ADDR;
+};
+
+struct nvt_ts_hw_info {
+ uint8_t carrier_system;
+ uint8_t hw_crc;
+};
+
+static const struct nvt_ts_mem_map NT36526_memory_map = {
+ .EVENT_BUF_ADDR = 0x22D00,
+ .RAW_PIPE0_ADDR = 0x24000,
+ .RAW_PIPE1_ADDR = 0x24000,
+ .BASELINE_ADDR = 0x21758,
+ .BASELINE_BTN_ADDR = 0,
+ .DIFF_PIPE0_ADDR = 0x20AB0,
+ .DIFF_PIPE1_ADDR = 0x24AB0,
+ .RAW_BTN_PIPE0_ADDR = 0,
+ .RAW_BTN_PIPE1_ADDR = 0,
+ .DIFF_BTN_PIPE0_ADDR = 0,
+ .DIFF_BTN_PIPE1_ADDR = 0,
+ .READ_FLASH_CHECKSUM_ADDR = 0x24000,
+ .RW_FLASH_DATA_ADDR = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36675_memory_map = {
+ .EVENT_BUF_ADDR = 0x22D00,
+ .RAW_PIPE0_ADDR = 0x24000,
+ .RAW_PIPE1_ADDR = 0x24000,
+ .BASELINE_ADDR = 0x21B90,
+ .BASELINE_BTN_ADDR = 0,
+ .DIFF_PIPE0_ADDR = 0x20C60,
+ .DIFF_PIPE1_ADDR = 0x24C60,
+ .RAW_BTN_PIPE0_ADDR = 0,
+ .RAW_BTN_PIPE1_ADDR = 0,
+ .DIFF_BTN_PIPE0_ADDR = 0,
+ .DIFF_BTN_PIPE1_ADDR = 0,
+ .READ_FLASH_CHECKSUM_ADDR = 0x24000,
+ .RW_FLASH_DATA_ADDR = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36672A_memory_map = {
+ .EVENT_BUF_ADDR = 0x21C00,
+ .RAW_PIPE0_ADDR = 0x20000,
+ .RAW_PIPE1_ADDR = 0x23000,
+ .BASELINE_ADDR = 0x20BFC,
+ .BASELINE_BTN_ADDR = 0x23BFC,
+ .DIFF_PIPE0_ADDR = 0x206DC,
+ .DIFF_PIPE1_ADDR = 0x236DC,
+ .RAW_BTN_PIPE0_ADDR = 0x20510,
+ .RAW_BTN_PIPE1_ADDR = 0x23510,
+ .DIFF_BTN_PIPE0_ADDR = 0x20BF0,
+ .DIFF_BTN_PIPE1_ADDR = 0x23BF0,
+ .READ_FLASH_CHECKSUM_ADDR = 0x24000,
+ .RW_FLASH_DATA_ADDR = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36772_memory_map = {
+ .EVENT_BUF_ADDR = 0x11E00,
+ .RAW_PIPE0_ADDR = 0x10000,
+ .RAW_PIPE1_ADDR = 0x12000,
+ .BASELINE_ADDR = 0x10E70,
+ .BASELINE_BTN_ADDR = 0x12E70,
+ .DIFF_PIPE0_ADDR = 0x10830,
+ .DIFF_PIPE1_ADDR = 0x12830,
+ .RAW_BTN_PIPE0_ADDR = 0x10E60,
+ .RAW_BTN_PIPE1_ADDR = 0x12E60,
+ .DIFF_BTN_PIPE0_ADDR = 0x10E68,
+ .DIFF_BTN_PIPE1_ADDR = 0x12E68,
+ .READ_FLASH_CHECKSUM_ADDR = 0x14000,
+ .RW_FLASH_DATA_ADDR = 0x14002,
+};
+
+static const struct nvt_ts_mem_map NT36525_memory_map = {
+ .EVENT_BUF_ADDR = 0x11A00,
+ .RAW_PIPE0_ADDR = 0x10000,
+ .RAW_PIPE1_ADDR = 0x12000,
+ .BASELINE_ADDR = 0x10B08,
+ .BASELINE_BTN_ADDR = 0x12B08,
+ .DIFF_PIPE0_ADDR = 0x1064C,
+ .DIFF_PIPE1_ADDR = 0x1264C,
+ .RAW_BTN_PIPE0_ADDR = 0x10634,
+ .RAW_BTN_PIPE1_ADDR = 0x12634,
+ .DIFF_BTN_PIPE0_ADDR = 0x10AFC,
+ .DIFF_BTN_PIPE1_ADDR = 0x12AFC,
+ .READ_FLASH_CHECKSUM_ADDR = 0x14000,
+ .RW_FLASH_DATA_ADDR = 0x14002,
+};
+
+static const struct nvt_ts_mem_map NT36676F_memory_map = {
+ .EVENT_BUF_ADDR = 0x11A00,
+ .RAW_PIPE0_ADDR = 0x10000,
+ .RAW_PIPE1_ADDR = 0x12000,
+ .BASELINE_ADDR = 0x10B08,
+ .BASELINE_BTN_ADDR = 0x12B08,
+ .DIFF_PIPE0_ADDR = 0x1064C,
+ .DIFF_PIPE1_ADDR = 0x1264C,
+ .RAW_BTN_PIPE0_ADDR = 0x10634,
+ .RAW_BTN_PIPE1_ADDR = 0x12634,
+ .DIFF_BTN_PIPE0_ADDR = 0x10AFC,
+ .DIFF_BTN_PIPE1_ADDR = 0x12AFC,
+ .READ_FLASH_CHECKSUM_ADDR = 0x14000,
+ .RW_FLASH_DATA_ADDR = 0x14002,
+};
+
+static struct nvt_ts_hw_info NT36526_hw_info = {
+ .carrier_system = 2,
+ .hw_crc = 2,
+};
+
+static struct nvt_ts_hw_info NT36675_hw_info = {
+ .carrier_system = 2,
+ .hw_crc = 2,
+};
+
+static struct nvt_ts_hw_info NT36672A_hw_info = {
+ .carrier_system = 0,
+ .hw_crc = 1,
+};
+
+static struct nvt_ts_hw_info NT36772_hw_info = {
+ .carrier_system = 0,
+ .hw_crc = 0,
+};
+
+static struct nvt_ts_hw_info NT36525_hw_info = {
+ .carrier_system = 0,
+ .hw_crc = 0,
+};
+
+static struct nvt_ts_hw_info NT36676F_hw_info = {
+ .carrier_system = 0,
+ .hw_crc = 0,
+};
+
+#define NVT_ID_BYTE_MAX 6
+struct nvt_ts_trim_id_table {
+ uint8_t id[NVT_ID_BYTE_MAX];
+ uint8_t mask[NVT_ID_BYTE_MAX];
+ const struct nvt_ts_mem_map *mmap;
+ const struct nvt_ts_hw_info *hwinfo;
+};
+
+static const struct nvt_ts_trim_id_table trim_id_table[] = {
+ {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info},
+ {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+ {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info},
+ {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+ .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info}
+};
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.c
new file mode 100644
index 0000000..4f128aa
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.c
@@ -0,0 +1,1482 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include "nt36xxx.h"
+#include "nt36xxx_mp_ctrlram.h"
+
+#if NVT_TOUCH_MP
+
+#define NORMAL_MODE 0x00
+#define TEST_MODE_1 0x21
+#define TEST_MODE_2 0x22
+#define MP_MODE_CC 0x41
+#define FREQ_HOP_DISABLE 0x66
+#define FREQ_HOP_ENABLE 0x65
+
+#define SHORT_TEST_CSV_FILE "/data/local/tmp/ShortTest.csv"
+#define OPEN_TEST_CSV_FILE "/data/local/tmp/OpenTest.csv"
+#define FW_RAWDATA_CSV_FILE "/data/local/tmp/FWMutualTest.csv"
+#define FW_CC_CSV_FILE "/data/local/tmp/FWCCTest.csv"
+#define NOISE_TEST_CSV_FILE "/data/local/tmp/NoiseTest.csv"
+
+#define nvt_mp_seq_printf(m, fmt, args...) do { \
+ seq_printf(m, fmt, ##args); \
+ if (!nvt_mp_test_result_printed) \
+ printk(fmt, ##args); \
+} while (0)
+
+static uint8_t *RecordResult_Short = NULL;
+static uint8_t *RecordResult_Open = NULL;
+static uint8_t *RecordResult_FWMutual = NULL;
+static uint8_t *RecordResult_FW_CC = NULL;
+static uint8_t *RecordResult_FW_DiffMax = NULL;
+static uint8_t *RecordResult_FW_DiffMin = NULL;
+
+static int32_t TestResult_Short = 0;
+static int32_t TestResult_Open = 0;
+static int32_t TestResult_FW_Rawdata = 0;
+static int32_t TestResult_FWMutual = 0;
+static int32_t TestResult_FW_CC = 0;
+static int32_t TestResult_Noise = 0;
+static int32_t TestResult_FW_DiffMax = 0;
+static int32_t TestResult_FW_DiffMin = 0;
+
+static int32_t *RawData_Short = NULL;
+static int32_t *RawData_Open = NULL;
+static int32_t *RawData_Diff = NULL;
+static int32_t *RawData_Diff_Min = NULL;
+static int32_t *RawData_Diff_Max = NULL;
+static int32_t *RawData_FWMutual = NULL;
+static int32_t *RawData_FW_CC = NULL;
+
+static struct proc_dir_entry *NVT_proc_selftest_entry = NULL;
+static int8_t nvt_mp_test_result_printed = 0;
+static uint8_t fw_ver = 0;
+
+extern void nvt_change_mode(uint8_t mode);
+extern uint8_t nvt_get_fw_pipe(void);
+extern void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr);
+extern void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num);
+int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible);
+
+/*******************************************************
+Description:
+ Novatek touchscreen allocate buffer for mp selftest.
+
+return:
+ Executive outcomes. 0---succeed. -12---Out of memory
+*******************************************************/
+static int nvt_mp_buffer_init(void)
+{
+ size_t RecordResult_BufSize = IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE;
+ size_t RawData_BufSize = (IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * sizeof(int32_t);
+
+ RecordResult_Short = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_Short) {
+ NVT_ERR("kzalloc for RecordResult_Short failed!\n");
+ return -ENOMEM;
+ }
+
+ RecordResult_Open = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_Open) {
+ NVT_ERR("kzalloc for RecordResult_Open failed!\n");
+ return -ENOMEM;
+ }
+
+ RecordResult_FWMutual = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_FWMutual) {
+ NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n");
+ return -ENOMEM;
+ }
+
+ RecordResult_FW_CC = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_FW_CC) {
+ NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n");
+ return -ENOMEM;
+ }
+
+ RecordResult_FW_DiffMax = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_FW_DiffMax) {
+ NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n");
+ return -ENOMEM;
+ }
+
+ RecordResult_FW_DiffMin = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+ if (!RecordResult_FW_DiffMin) {
+ NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_Short = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_Short) {
+ NVT_ERR("kzalloc for RawData_Short failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_Open = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_Open) {
+ NVT_ERR("kzalloc for RawData_Open failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_Diff = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_Diff) {
+ NVT_ERR("kzalloc for RawData_Diff failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_Diff_Min = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_Diff_Min) {
+ NVT_ERR("kzalloc for RawData_Diff_Min failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_Diff_Max = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_Diff_Max) {
+ NVT_ERR("kzalloc for RawData_Diff_Max failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_FWMutual = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_FWMutual) {
+ NVT_ERR("kzalloc for RawData_FWMutual failed!\n");
+ return -ENOMEM;
+ }
+
+ RawData_FW_CC = kzalloc(RawData_BufSize, GFP_KERNEL);
+ if (!RawData_FW_CC) {
+ NVT_ERR("kzalloc for RawData_FW_CC failed!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen free buffer for mp selftest.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_mp_buffer_deinit(void)
+{
+ if (RecordResult_Short) {
+ kfree(RecordResult_Short);
+ RecordResult_Short = NULL;
+ }
+
+ if (RecordResult_Open) {
+ kfree(RecordResult_Open);
+ RecordResult_Open = NULL;
+ }
+
+ if (RecordResult_FWMutual) {
+ kfree(RecordResult_FWMutual);
+ RecordResult_FWMutual = NULL;
+ }
+
+ if (RecordResult_FW_CC) {
+ kfree(RecordResult_FW_CC);
+ RecordResult_FW_CC = NULL;
+ }
+
+ if (RecordResult_FW_DiffMax) {
+ kfree(RecordResult_FW_DiffMax);
+ RecordResult_FW_DiffMax = NULL;
+ }
+
+ if (RecordResult_FW_DiffMin) {
+ kfree(RecordResult_FW_DiffMin);
+ RecordResult_FW_DiffMin = NULL;
+ }
+
+ if (RawData_Short) {
+ kfree(RawData_Short);
+ RawData_Short = NULL;
+ }
+
+ if (RawData_Open) {
+ kfree(RawData_Open);
+ RawData_Open = NULL;
+ }
+
+ if (RawData_Diff) {
+ kfree(RawData_Diff);
+ RawData_Diff = NULL;
+ }
+
+ if (RawData_Diff_Min) {
+ kfree(RawData_Diff_Min);
+ RawData_Diff_Min = NULL;
+ }
+
+ if (RawData_Diff_Max) {
+ kfree(RawData_Diff_Max);
+ RawData_Diff_Max = NULL;
+ }
+
+ if (RawData_FWMutual) {
+ kfree(RawData_FWMutual);
+ RawData_FWMutual = NULL;
+ }
+
+ if (RawData_FW_CC) {
+ kfree(RawData_FW_CC);
+ RawData_FW_CC = NULL;
+ }
+}
+
+static void nvt_print_data_log_in_one_line(int32_t *data, int32_t data_num)
+{
+ char *tmp_log = NULL;
+ int32_t i = 0;
+ int32_t count = data_num * 7 + 1;
+
+ tmp_log = kzalloc(count, GFP_KERNEL);
+ if (!tmp_log) {
+ NVT_ERR("kzalloc for tmp_log failed!\n ");
+ return;
+ }
+
+ for (i = 0; i < data_num; i++) {
+ snprintf(tmp_log + i * 7, count - i * 7, "%5d, ", data[i]);
+ }
+ tmp_log[data_num * 7] = '\0';
+ printk("%s", tmp_log);
+ if (tmp_log) {
+ kfree(tmp_log);
+ tmp_log = NULL;
+ }
+
+ return;
+}
+
+static void nvt_print_result_log_in_one_line(uint8_t *result, int32_t result_num)
+{
+ char *tmp_log = NULL;
+ int32_t i = 0;
+ int32_t count = 0;
+
+ count = result_num * 6 + 1;
+ tmp_log = kzalloc(count, GFP_KERNEL);
+ if (!tmp_log) {
+ NVT_ERR("kzalloc for tmp_log failed!\n ");
+ return;
+ }
+
+ for (i = 0; i < result_num; i++) {
+ snprintf(tmp_log + i * 6, count - i * 6, "0x%02X, ", result[i]);
+ }
+ tmp_log[result_num * 6] = '\0';
+ printk("%s", tmp_log);
+ if (tmp_log) {
+ kfree(tmp_log);
+ tmp_log = NULL;
+ }
+
+ return;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen self-test criteria print function.
+
+return:
+ n.a.
+*******************************************************/
+static void nvt_print_lmt_array(int32_t *array, int32_t x_ch, int32_t y_ch)
+{
+ int32_t j = 0;
+
+ for (j = 0; j < y_ch; j++) {
+ nvt_print_data_log_in_one_line(array + j * x_ch, x_ch);
+ printk("\n");
+ }
+#if TOUCH_KEY_NUM > 0
+ nvt_print_data_log_in_one_line(array + y_ch * x_ch, Key_Channel);
+ printk("\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+}
+
+static void nvt_print_criteria(void)
+{
+ NVT_LOG("++\n");
+
+ //---PS_Config_Lmt_Short_Rawdata---
+ printk("PS_Config_Lmt_Short_Rawdata_P:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_P, X_Channel, Y_Channel);
+ printk("PS_Config_Lmt_Short_Rawdata_N:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_N, X_Channel, Y_Channel);
+
+ //---PS_Config_Lmt_Open_Rawdata---
+ printk("PS_Config_Lmt_Open_Rawdata_P:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_P, X_Channel, Y_Channel);
+ printk("PS_Config_Lmt_Open_Rawdata_N:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_N, X_Channel, Y_Channel);
+
+ //---PS_Config_Lmt_FW_Rawdata---
+ printk("PS_Config_Lmt_FW_Rawdata_P:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_P, X_Channel, Y_Channel);
+ printk("PS_Config_Lmt_FW_Rawdata_N:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_N, X_Channel, Y_Channel);
+
+ //---PS_Config_Lmt_FW_CC---
+ printk("PS_Config_Lmt_FW_CC_P:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_CC_P, X_Channel, Y_Channel);
+ printk("PS_Config_Lmt_FW_CC_N:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_CC_N, X_Channel, Y_Channel);
+
+ //---PS_Config_Lmt_FW_Diff---
+ printk("PS_Config_Lmt_FW_Diff_P:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_P, X_Channel, Y_Channel);
+ printk("PS_Config_Lmt_FW_Diff_N:\n");
+ nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_N, X_Channel, Y_Channel);
+
+ NVT_LOG("--\n");
+}
+
+static int32_t nvt_polling_hand_shake_status(void)
+{
+ uint8_t buf[8] = {0};
+ int32_t i = 0;
+ const int32_t retry = 70;
+
+ for (i = 0; i < retry; i++) {
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+ //---read fw status---
+ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+ buf[1] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+ if ((buf[1] == 0xA0) || (buf[1] == 0xA1))
+ break;
+
+ usleep_range(10000, 10000);
+ }
+
+ if (i >= retry) {
+ NVT_ERR("polling hand shake status failed, buf[1]=0x%02X\n", buf[1]);
+
+ // Read back 5 bytes from offset EVENT_MAP_HOST_CMD for debug check
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6);
+ NVT_ERR("Read back 5 bytes from offset EVENT_MAP_HOST_CMD: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int8_t nvt_switch_FreqHopEnDis(uint8_t FreqHopEnDis)
+{
+ uint8_t buf[8] = {0};
+ uint8_t retry = 0;
+ int8_t ret = 0;
+
+ NVT_LOG("++\n");
+
+ for (retry = 0; retry < 20; retry++) {
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ //---switch FreqHopEnDis---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = FreqHopEnDis;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+ msleep(35);
+
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0xFF;
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+ if (buf[1] == 0x00)
+ break;
+ }
+
+ if (unlikely(retry == 20)) {
+ NVT_ERR("switch FreqHopEnDis 0x%02X failed, buf[1]=0x%02X\n", FreqHopEnDis, buf[1]);
+ ret = -1;
+ }
+
+ NVT_LOG("--\n");
+
+ return ret;
+}
+
+static int32_t nvt_read_baseline(int32_t *xdata)
+{
+ uint8_t x_num = 0;
+ uint8_t y_num = 0;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("++\n");
+
+ nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR);
+
+ nvt_get_mdata(xdata, &x_num, &y_num);
+
+ for (y = 0; y < y_num; y++) {
+ for (x = 0; x < x_num; x++) {
+ iArrayIndex = y * x_num + x;
+ xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = Y_Channel * X_Channel + k;
+ xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+static int32_t nvt_read_CC(int32_t *xdata)
+{
+ uint8_t x_num = 0;
+ uint8_t y_num = 0;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("++\n");
+
+ if (nvt_get_fw_pipe() == 0)
+ nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+ else
+ nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+
+ nvt_get_mdata(xdata, &x_num, &y_num);
+
+ for (y = 0; y < y_num; y++) {
+ for (x = 0; x < x_num; x++) {
+ iArrayIndex = y * x_num + x;
+ xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = Y_Channel * X_Channel + k;
+ xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+static void nvt_enable_noise_collect(int32_t frame_num)
+{
+ uint8_t buf[8] = {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ //---enable noise collect---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x47;
+ buf[2] = 0xAA;
+ buf[3] = frame_num;
+ buf[4] = 0x00;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static int32_t nvt_read_fw_noise(int32_t *xdata)
+{
+ uint8_t x_num = 0;
+ uint8_t y_num = 0;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ int32_t iArrayIndex = 0;
+ int32_t frame_num = 0;
+ uint32_t rawdata_diff_min_offset = 0;
+#if TOUCH_KEY_NUM > 0
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("++\n");
+
+ //---Enter Test Mode---
+ if (nvt_clear_fw_status()) {
+ return -EAGAIN;
+ }
+
+ frame_num = PS_Config_Diff_Test_Frame / 10;
+ if (frame_num <= 0)
+ frame_num = 1;
+ printk("%s: frame_num=%d\n", __func__, frame_num);
+ nvt_enable_noise_collect(frame_num);
+ // need wait PS_Config_Diff_Test_Frame * 8.3ms
+ msleep(frame_num * 83);
+
+ if (nvt_polling_hand_shake_status()) {
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_info()) {
+ return -EAGAIN;
+ }
+
+ if (nvt_get_fw_pipe() == 0)
+ nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+ else
+ nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+
+ nvt_get_mdata(xdata, &x_num, &y_num);
+
+ for (y = 0; y < y_num; y++) {
+ for (x = 0; x < x_num; x++) {
+ iArrayIndex = y * x_num + x;
+ RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF);
+ RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF);
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = Y_Channel * X_Channel + k;
+ RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF);
+ RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF);
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ //---Leave Test Mode---
+ nvt_change_mode(NORMAL_MODE);
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+static void nvt_enable_open_test(void)
+{
+ uint8_t buf[8] = {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ //---enable open test---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x45;
+ buf[2] = 0xAA;
+ buf[3] = 0x02;
+ buf[4] = 0x00;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static void nvt_enable_short_test(void)
+{
+ uint8_t buf[8] = {0};
+
+ //---set xdata index to EVENT BUF ADDR---
+ nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+ //---enable short test---
+ buf[0] = EVENT_MAP_HOST_CMD;
+ buf[1] = 0x43;
+ buf[2] = 0xAA;
+ buf[3] = 0x02;
+ buf[4] = 0x00;
+ CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static int32_t nvt_read_fw_open(int32_t *xdata)
+{
+ uint32_t raw_pipe_addr = 0;
+ uint8_t *rawdata_buf = NULL;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint8_t buf[128] = {0};
+#if TOUCH_KEY_NUM > 0
+ uint32_t raw_btn_pipe_addr = 0;
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("++\n");
+
+ //---Enter Test Mode---
+ if (nvt_clear_fw_status()) {
+ return -EAGAIN;
+ }
+
+ nvt_enable_open_test();
+
+ if (nvt_polling_hand_shake_status()) {
+ return -EAGAIN;
+ }
+
+#if TOUCH_KEY_NUM > 0
+ rawdata_buf = kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL);
+#else
+ rawdata_buf = kzalloc(IC_X_CFG_SIZE * IC_Y_CFG_SIZE * 2, GFP_KERNEL);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+ if (!rawdata_buf) {
+ NVT_ERR("kzalloc for rawdata_buf failed!\n");
+ return -ENOMEM;
+ }
+
+ if (nvt_get_fw_pipe() == 0)
+ raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR;
+ else
+ raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR;
+
+ for (y = 0; y < IC_Y_CFG_SIZE; y++) {
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * IC_X_CFG_SIZE * 2);
+ buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF);
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_X_CFG_SIZE * 2 + 1);
+ memcpy(rawdata_buf + y * IC_X_CFG_SIZE * 2, buf + 1, IC_X_CFG_SIZE * 2);
+ }
+#if TOUCH_KEY_NUM > 0
+ if (nvt_get_fw_pipe() == 0)
+ raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR;
+ else
+ raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR;
+
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr);
+ buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF);
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_KEY_CFG_SIZE * 2 + 1);
+ memcpy(rawdata_buf + IC_Y_CFG_SIZE * IC_X_CFG_SIZE * 2, buf + 1, IC_KEY_CFG_SIZE * 2);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ for (y = 0; y < IC_Y_CFG_SIZE; y++) {
+ for (x = 0; x < IC_X_CFG_SIZE; x++) {
+ if ((AIN_Y[y] != 0xFF) && (AIN_X[x] != 0xFF)) {
+ xdata[AIN_Y[y] * X_Channel + AIN_X[x]] = (int16_t)((rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2] + 256 * rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2 + 1]));
+ }
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < IC_KEY_CFG_SIZE; k++) {
+ if (AIN_KEY[k] != 0xFF)
+ xdata[Y_Channel * X_Channel + AIN_KEY[k]] = (int16_t)(rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2] + 256 * rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2 + 1]);
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ if (rawdata_buf) {
+ kfree(rawdata_buf);
+ rawdata_buf = NULL;
+ }
+
+ //---Leave Test Mode---
+ nvt_change_mode(NORMAL_MODE);
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+static int32_t nvt_read_fw_short(int32_t *xdata)
+{
+ uint32_t raw_pipe_addr = 0;
+ uint8_t *rawdata_buf = NULL;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint8_t buf[128] = {0};
+ int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+ uint32_t raw_btn_pipe_addr = 0;
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ NVT_LOG("++\n");
+
+ //---Enter Test Mode---
+ if (nvt_clear_fw_status()) {
+ return -EAGAIN;
+ }
+
+ nvt_enable_short_test();
+
+ if (nvt_polling_hand_shake_status()) {
+ return -EAGAIN;
+ }
+
+#if TOUCH_KEY_NUM > 0
+ rawdata_buf = kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL);
+#else
+ rawdata_buf = kzalloc(X_Channel * Y_Channel * 2, GFP_KERNEL);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+ if (!rawdata_buf) {
+ NVT_ERR("kzalloc for rawdata_buf failed!\n");
+ return -ENOMEM;
+ }
+
+ if (nvt_get_fw_pipe() == 0)
+ raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR;
+ else
+ raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR;
+
+ for (y = 0; y < Y_Channel; y++) {
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * X_Channel * 2);
+ buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF);
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, X_Channel * 2 + 1);
+ memcpy(rawdata_buf + y * X_Channel * 2, buf + 1, X_Channel * 2);
+ }
+#if TOUCH_KEY_NUM > 0
+ if (nvt_get_fw_pipe() == 0)
+ raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR;
+ else
+ raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR;
+
+ //---change xdata index---
+ nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr);
+ buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF);
+ CTP_I2C_READ(ts->client, I2C_FW_Address, buf, Key_Channel * 2 + 1);
+ memcpy(rawdata_buf + Y_Channel * X_Channel * 2, buf + 1, Key_Channel * 2);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ for (y = 0; y < Y_Channel; y++) {
+ for (x = 0; x < X_Channel; x++) {
+ iArrayIndex = y * X_Channel + x;
+ xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]);
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = Y_Channel * X_Channel + k;
+ xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]);
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ if (rawdata_buf) {
+ kfree(rawdata_buf);
+ rawdata_buf = NULL;
+ }
+
+ //---Leave Test Mode---
+ nvt_change_mode(NORMAL_MODE);
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen raw data test for each single point function.
+
+return:
+ Executive outcomes. 0---passed. negative---failed.
+*******************************************************/
+static int32_t RawDataTest_SinglePoint_Sub(int32_t rawdata[], uint8_t RecordResult[], uint8_t x_ch, uint8_t y_ch, int32_t Rawdata_Limit_Postive[], int32_t Rawdata_Limit_Negative[])
+{
+ int32_t i = 0;
+ int32_t j = 0;
+#if TOUCH_KEY_NUM > 0
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+ int32_t iArrayIndex = 0;
+ bool isPass = true;
+
+ for (j = 0; j < y_ch; j++) {
+ for (i = 0; i < x_ch; i++) {
+ iArrayIndex = j * x_ch + i;
+
+ RecordResult[iArrayIndex] = 0x00; // default value for PASS
+
+ if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex])
+ RecordResult[iArrayIndex] |= 0x01;
+
+ if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex])
+ RecordResult[iArrayIndex] |= 0x02;
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = y_ch * x_ch + k;
+
+ RecordResult[iArrayIndex] = 0x00; // default value for PASS
+
+ if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex])
+ RecordResult[iArrayIndex] |= 0x01;
+
+ if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex])
+ RecordResult[iArrayIndex] |= 0x02;
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ //---Check RecordResult---
+ for (j = 0; j < y_ch; j++) {
+ for (i = 0; i < x_ch; i++) {
+ if (RecordResult[j * x_ch + i] != 0) {
+ isPass = false;
+ break;
+ }
+ }
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = y_ch * x_ch + k;
+ if (RecordResult[iArrayIndex] != 0) {
+ isPass = false;
+ break;
+ }
+ }
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ if (!isPass) {
+ return -1; // FAIL
+ } else {
+ return 0; // PASS
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen print self-test result function.
+
+return:
+ n.a.
+*******************************************************/
+void print_selftest_result(struct seq_file *m, int32_t TestResult, uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len)
+{
+ int32_t i = 0;
+ int32_t j = 0;
+ int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+ int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+ switch (TestResult) {
+ case 0:
+ nvt_mp_seq_printf(m, " PASS!\n");
+ break;
+
+ case 1:
+ nvt_mp_seq_printf(m, " ERROR! Read Data FAIL!\n");
+ break;
+
+ case -1:
+ nvt_mp_seq_printf(m, " FAIL!\n");
+ nvt_mp_seq_printf(m, "RecordResult:\n");
+ for (i = 0; i < y_len; i++) {
+ for (j = 0; j < x_len; j++) {
+ iArrayIndex = i * x_len + j;
+ seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]);
+ }
+ if (!nvt_mp_test_result_printed)
+ nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len);
+ nvt_mp_seq_printf(m, "\n");
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = y_len * x_len + k;
+ seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]);
+ }
+ if (!nvt_mp_test_result_printed)
+ nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel);
+ nvt_mp_seq_printf(m, "\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+ nvt_mp_seq_printf(m, "ReadData:\n");
+ for (i = 0; i < y_len; i++) {
+ for (j = 0; j < x_len; j++) {
+ iArrayIndex = i * x_len + j;
+ seq_printf(m, "%5d, ", rawdata[iArrayIndex]);
+ }
+ if (!nvt_mp_test_result_printed)
+ nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len);
+ nvt_mp_seq_printf(m, "\n");
+ }
+#if TOUCH_KEY_NUM > 0
+ for (k = 0; k < Key_Channel; k++) {
+ iArrayIndex = y_len * x_len + k;
+ seq_printf(m, "%5d, ", rawdata[iArrayIndex]);
+ }
+ if (!nvt_mp_test_result_printed)
+ nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel);
+ nvt_mp_seq_printf(m, "\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+ break;
+ }
+ nvt_mp_seq_printf(m, "\n");
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen self-test sequence print show
+ function.
+
+return:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_show_selftest(struct seq_file *m, void *v)
+{
+ NVT_LOG("++\n");
+
+ nvt_mp_seq_printf(m, "FW Version: %d\n\n", fw_ver);
+
+ nvt_mp_seq_printf(m, "Short Test");
+
+ print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel);
+
+ nvt_mp_seq_printf(m, "Open Test");
+ print_selftest_result(m, TestResult_Open, RecordResult_Open, RawData_Open, X_Channel, Y_Channel);
+
+ nvt_mp_seq_printf(m, "FW Rawdata Test");
+ if ((TestResult_FW_Rawdata == 0) || (TestResult_FW_Rawdata == 1)) {
+ print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel);
+ } else { // TestResult_FW_Rawdata is -1
+ nvt_mp_seq_printf(m, " FAIL!\n");
+ if (TestResult_FWMutual == -1) {
+ nvt_mp_seq_printf(m, "FW Mutual");
+ print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel);
+ }
+ if (TestResult_FW_CC == -1) {
+ nvt_mp_seq_printf(m, "FW CC");
+ print_selftest_result(m, TestResult_FW_CC, RecordResult_FW_CC, RawData_FW_CC, X_Channel, Y_Channel);
+ }
+ }
+
+ nvt_mp_seq_printf(m, "Noise Test");
+ if ((TestResult_Noise == 0) || (TestResult_Noise == 1)) {
+ print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel);
+ } else { // TestResult_Noise is -1
+ nvt_mp_seq_printf(m, " FAIL!\n");
+
+ if (TestResult_FW_DiffMax == -1) {
+ nvt_mp_seq_printf(m, "FW Diff Max");
+ print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel);
+ }
+ if (TestResult_FW_DiffMin == -1) {
+ nvt_mp_seq_printf(m, "FW Diff Min");
+ print_selftest_result(m, TestResult_FW_DiffMin, RecordResult_FW_DiffMin, RawData_Diff_Min, X_Channel, Y_Channel);
+ }
+ }
+
+ nvt_mp_test_result_printed = 1;
+
+ NVT_LOG("--\n");
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen self-test sequence print start
+ function.
+
+return:
+ Executive outcomes. 1---call next function.
+ NULL---not call next function and sequence loop
+ stop.
+*******************************************************/
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen self-test sequence print next
+ function.
+
+return:
+ Executive outcomes. NULL---no next and call sequence
+ stop function.
+*******************************************************/
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen self-test sequence print stop
+ function.
+
+return:
+ n.a.
+*******************************************************/
+static void c_stop(struct seq_file *m, void *v)
+{
+ return;
+}
+
+const struct seq_operations nvt_selftest_seq_ops = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show_selftest
+};
+
+/*******************************************************
+Description:
+ Novatek touchscreen /proc/nvt_selftest open function.
+
+return:
+ Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+static int32_t nvt_selftest_open(struct inode *inode, struct file *file)
+{
+ struct device_node *np = ts->client->dev.of_node;
+ unsigned char mpcriteria[32] = {0}; //novatek-mp-criteria-default
+
+ TestResult_Short = 0;
+ TestResult_Open = 0;
+ TestResult_FW_Rawdata = 0;
+ TestResult_FWMutual = 0;
+ TestResult_FW_CC = 0;
+ TestResult_Noise = 0;
+ TestResult_FW_DiffMax = 0;
+ TestResult_FW_DiffMin = 0;
+
+ NVT_LOG("++\n");
+
+ if (mutex_lock_interruptible(&ts->lock)) {
+ return -ERESTARTSYS;
+ }
+
+#if NVT_TOUCH_ESD_PROTECT
+ nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+ if (nvt_get_fw_info()) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("get fw info failed!\n");
+ return -EAGAIN;
+ }
+
+ fw_ver = ts->fw_ver;
+
+ /* Parsing criteria from dts */
+ if(of_property_read_bool(np, "novatek,mp-support-dt")) {
+ /*
+ * Parsing Criteria by Novatek PID
+ * The string rule is "novatek-mp-criteria-<nvt_pid>"
+ * nvt_pid is 2 bytes (show hex).
+ *
+ * Ex. nvt_pid = 500A
+ * mpcriteria = "novatek-mp-criteria-500A"
+ */
+ snprintf(mpcriteria, PAGE_SIZE, "novatek-mp-criteria-%04X", ts->nvt_pid);
+
+ if (nvt_mp_parse_dt(np, mpcriteria)) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("mp parse device tree failed!\n");
+ return -EINVAL;
+ }
+ } else {
+ NVT_LOG("Not found novatek,mp-support-dt, use default setting\n");
+ //---Print Test Criteria---
+ nvt_print_criteria();
+ }
+
+ if (nvt_switch_FreqHopEnDis(FREQ_HOP_DISABLE)) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("switch frequency hopping disable failed!\n");
+ return -EAGAIN;
+ }
+
+ if (nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN)) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("check fw reset state failed!\n");
+ return -EAGAIN;
+ }
+
+ msleep(100);
+
+ //---Enter Test Mode---
+ if (nvt_clear_fw_status()) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("clear fw status failed!\n");
+ return -EAGAIN;
+ }
+
+ nvt_change_mode(MP_MODE_CC);
+
+ if (nvt_check_fw_status()) {
+ mutex_unlock(&ts->lock);
+ NVT_ERR("check fw status failed!\n");
+ return -EAGAIN;
+ }
+
+ //---FW Rawdata Test---
+ if (nvt_read_baseline(RawData_FWMutual) != 0) {
+ TestResult_FWMutual = 1;
+ } else {
+ TestResult_FWMutual = RawDataTest_SinglePoint_Sub(RawData_FWMutual, RecordResult_FWMutual, X_Channel, Y_Channel,
+ PS_Config_Lmt_FW_Rawdata_P, PS_Config_Lmt_FW_Rawdata_N);
+ }
+ if (nvt_read_CC(RawData_FW_CC) != 0) {
+ TestResult_FW_CC = 1;
+ } else {
+ TestResult_FW_CC = RawDataTest_SinglePoint_Sub(RawData_FW_CC, RecordResult_FW_CC, X_Channel, Y_Channel,
+ PS_Config_Lmt_FW_CC_P, PS_Config_Lmt_FW_CC_N);
+ }
+
+ if ((TestResult_FWMutual == 1) || (TestResult_FW_CC == 1)) {
+ TestResult_FW_Rawdata = 1;
+ } else {
+ if ((TestResult_FWMutual == -1) || (TestResult_FW_CC == -1))
+ TestResult_FW_Rawdata = -1;
+ else
+ TestResult_FW_Rawdata = 0;
+ }
+
+ //---Leave Test Mode---
+ nvt_change_mode(NORMAL_MODE);
+
+ //---Noise Test---
+ if (nvt_read_fw_noise(RawData_Diff) != 0) {
+ TestResult_Noise = 1; // 1: ERROR
+ TestResult_FW_DiffMax = 1;
+ TestResult_FW_DiffMin = 1;
+ } else {
+ TestResult_FW_DiffMax = RawDataTest_SinglePoint_Sub(RawData_Diff_Max, RecordResult_FW_DiffMax, X_Channel, Y_Channel,
+ PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N);
+
+ TestResult_FW_DiffMin = RawDataTest_SinglePoint_Sub(RawData_Diff_Min, RecordResult_FW_DiffMin, X_Channel, Y_Channel,
+ PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N);
+
+ if ((TestResult_FW_DiffMax == -1) || (TestResult_FW_DiffMin == -1))
+ TestResult_Noise = -1;
+ else
+ TestResult_Noise = 0;
+ }
+
+ //--Short Test---
+ if (nvt_read_fw_short(RawData_Short) != 0) {
+ TestResult_Short = 1; // 1:ERROR
+ } else {
+ //---Self Test Check --- // 0:PASS, -1:FAIL
+ TestResult_Short = RawDataTest_SinglePoint_Sub(RawData_Short, RecordResult_Short, X_Channel, Y_Channel,
+ PS_Config_Lmt_Short_Rawdata_P, PS_Config_Lmt_Short_Rawdata_N);
+ }
+
+ //---Open Test---
+ if (nvt_read_fw_open(RawData_Open) != 0) {
+ TestResult_Open = 1; // 1:ERROR
+ } else {
+ //---Self Test Check --- // 0:PASS, -1:FAIL
+ TestResult_Open = RawDataTest_SinglePoint_Sub(RawData_Open, RecordResult_Open, X_Channel, Y_Channel,
+ PS_Config_Lmt_Open_Rawdata_P, PS_Config_Lmt_Open_Rawdata_N);
+ }
+
+ //---Reset IC---
+ nvt_bootloader_reset();
+
+ mutex_unlock(&ts->lock);
+
+ NVT_LOG("--\n");
+
+ nvt_mp_test_result_printed = 0;
+
+ return seq_open(file, &nvt_selftest_seq_ops);
+}
+
+static const struct file_operations nvt_selftest_fops = {
+ .owner = THIS_MODULE,
+ .open = nvt_selftest_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+#ifdef CONFIG_OF
+/*******************************************************
+Description:
+ Novatek touchscreen parse AIN setting for array type.
+
+return:
+ n.a.
+*******************************************************/
+int32_t nvt_mp_parse_ain(struct device_node *np, const char *name, uint8_t *array, int32_t size)
+{
+ struct property *data;
+ int32_t len, ret;
+ int32_t tmp[40];
+ int32_t i;
+
+ data = of_find_property(np, name, &len);
+ len /= sizeof(u32);
+ if ((!data) || (!len) || (len != size)) {
+ NVT_ERR("error find %s. len=%d\n", name, len);
+ return -1;
+ } else {
+ NVT_LOG("%s. len=%d\n", name, len);
+ ret = of_property_read_u32_array(np, name, tmp, len);
+ if (ret) {
+ NVT_ERR("error reading %s. ret=%d\n", name, ret);
+ return -1;
+ }
+
+ for (i = 0; i < len; i++)
+ array[i] = tmp[i];
+
+#if NVT_DEBUG
+ printk("[NVT-ts] %s = ", name);
+ nvt_print_result_log_in_one_line(array, len);
+ printk("\n");
+#endif
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen parse criterion for u32 type.
+
+return:
+ n.a.
+*******************************************************/
+int32_t nvt_mp_parse_u32(struct device_node *np, const char *name, int32_t *para)
+{
+ int32_t ret;
+
+ ret = of_property_read_u32(np, name, para);
+ if (ret) {
+ NVT_ERR("error reading %s. ret=%d\n", name, ret);
+ return -1;
+ } else {
+#if NVT_DEBUG
+ NVT_LOG("%s=%d\n", name, *para);
+#endif
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen parse criterion for array type.
+
+return:
+ n.a.
+*******************************************************/
+int32_t nvt_mp_parse_array(struct device_node *np, const char *name, int32_t *array,
+ int32_t size)
+{
+ struct property *data;
+ int32_t len, ret;
+#if NVT_DEBUG
+ int32_t j = 0;
+#endif
+
+ data = of_find_property(np, name, &len);
+ len /= sizeof(u32);
+ if ((!data) || (!len) || (len < size)) {
+ NVT_ERR("error find %s. len=%d\n", name, len);
+ return -1;
+ } else {
+ NVT_LOG("%s. len=%d\n", name, len);
+ ret = of_property_read_u32_array(np, name, array, len);
+ if (ret) {
+ NVT_ERR("error reading %s. ret=%d\n", name, ret);
+ return -1;
+ }
+
+#if NVT_DEBUG
+ NVT_LOG("%s =\n", name);
+ for (j = 0; j < Y_Channel; j++) {
+ nvt_print_data_log_in_one_line(array + j * X_Channel, X_Channel);
+ printk("\n");
+ }
+#if TOUCH_KEY_NUM > 0
+ nvt_print_data_log_in_one_line(array + Y_Channel * X_Channel, Key_Channel);
+ printk("\n");
+#endif
+#endif
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen parse device tree mp function.
+
+return:
+ n.a.
+*******************************************************/
+int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible)
+{
+ struct device_node *np = root;
+ struct device_node *child = NULL;
+
+ NVT_LOG("Parse mp criteria for node %s\n", node_compatible);
+
+ /* find each MP sub-nodes */
+ for_each_child_of_node(root, child) {
+ /* find the specified node */
+ if (of_device_is_compatible(child, node_compatible)) {
+ NVT_LOG("found child node %s\n", node_compatible);
+ np = child;
+ break;
+ }
+ }
+ if (child == NULL) {
+ NVT_ERR("Not found compatible node %s!\n", node_compatible);
+ return -1;
+ }
+
+ /* MP Config*/
+ if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE))
+ return -1;
+
+ if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE))
+ return -1;
+
+#if TOUCH_KEY_NUM > 0
+ if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE))
+ return -1;
+#endif
+
+ if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel))
+ return -1;
+
+ if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel))
+ return -1;
+
+ if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE))
+ return -1;
+
+ if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE))
+ return -1;
+
+#if TOUCH_KEY_NUM > 0
+ if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE))
+ return -1;
+#endif
+
+ /* MP Criteria */
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N,
+ X_Channel * Y_Channel + Key_Channel))
+ return -1;
+
+ if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame))
+ return -1;
+
+ NVT_LOG("Parse mp criteria done!\n");
+
+ return 0;
+}
+#endif /* #ifdef CONFIG_OF */
+
+/*******************************************************
+Description:
+ Novatek touchscreen MP function proc. file node
+ initial function.
+
+return:
+ Executive outcomes. 0---succeed. -1---failed.
+*******************************************************/
+int32_t nvt_mp_proc_init(void)
+{
+ NVT_proc_selftest_entry = proc_create("nvt_selftest", 0444, NULL, &nvt_selftest_fops);
+ if (NVT_proc_selftest_entry == NULL) {
+ NVT_ERR("create /proc/nvt_selftest Failed!\n");
+ return -1;
+ } else {
+ if(nvt_mp_buffer_init()) {
+ NVT_ERR("Allocate mp memory failed\n");
+ return -1;
+ }
+ else {
+ NVT_LOG("create /proc/nvt_selftest Succeeded!\n");
+ }
+ return 0;
+ }
+}
+
+/*******************************************************
+Description:
+ Novatek touchscreen MP function proc. file node
+ deinitial function.
+
+return:
+ n.a.
+*******************************************************/
+void nvt_mp_proc_deinit(void)
+{
+ nvt_mp_buffer_deinit();
+
+ if (NVT_proc_selftest_entry != NULL) {
+ remove_proc_entry("nvt_selftest", NULL);
+ NVT_proc_selftest_entry = NULL;
+ NVT_LOG("Removed /proc/%s\n", "nvt_selftest");
+ }
+}
+#endif /* #if NVT_TOUCH_MP */
diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.h
new file mode 100644
index 0000000..69c27ca
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx/nt36xxx_mp_ctrlram.h
@@ -0,0 +1,460 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 46179 $
+ * $Date: 2019-06-14 13:47:17 +0800 (Fri, 14 Jun 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if NVT_TOUCH_MP
+
+static uint32_t IC_X_CFG_SIZE = 18;
+static uint32_t IC_Y_CFG_SIZE = 36;
+static uint32_t IC_KEY_CFG_SIZE = 4;
+static uint32_t X_Channel = 18;
+static uint32_t Y_Channel = 36;
+static uint32_t Key_Channel = TOUCH_KEY_NUM;
+static uint8_t AIN_X[40] =
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+static uint8_t AIN_Y[40] =
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
+
+#if TOUCH_KEY_NUM > 0
+static uint8_t AIN_KEY[8] = {0, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+static int32_t PS_Config_Lmt_Short_Rawdata_P[40 * 40] = {
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+ 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+#if TOUCH_KEY_NUM > 0
+ 14008,14008,14008,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = {
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+ 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+#if TOUCH_KEY_NUM > 0
+ 10000,10000,10000,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = {
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+ 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+#if TOUCH_KEY_NUM > 0
+ 5120,5120,5120,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = {
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+ 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+#if TOUCH_KEY_NUM > 0
+ 50,50,50,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = {
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+ 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+#if TOUCH_KEY_NUM > 0
+ 2560,2560,2560,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = {
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+ 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+#if TOUCH_KEY_NUM > 0
+ 240,240,240,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = {
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+ 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+#if TOUCH_KEY_NUM > 0
+ 314,314,314,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+#if TOUCH_KEY_NUM > 0
+ 0,0,0,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Diff_P[40 * 40] = {
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+ 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+#if TOUCH_KEY_NUM > 0
+ 75,75,75,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = {
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+ -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+#if TOUCH_KEY_NUM > 0
+ -75,-75,-75,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Diff_Test_Frame = 50;
+
+#endif /* #if NVT_TOUCH_MP */
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
index e5c3b06..06f0eb0 100644
--- a/drivers/input/touchscreen/silead.c
+++ b/drivers/input/touchscreen/silead.c
@@ -558,20 +558,33 @@ static int __maybe_unused silead_ts_suspend(struct device *dev)
static int __maybe_unused silead_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ bool second_try = false;
int error, status;
silead_ts_set_power(client, SILEAD_POWER_ON);
+ retry:
error = silead_ts_reset(client);
if (error)
return error;
+ if (second_try) {
+ error = silead_ts_load_fw(client);
+ if (error)
+ return error;
+ }
+
error = silead_ts_startup(client);
if (error)
return error;
status = silead_ts_get_status(client);
if (status != SILEAD_STATUS_OK) {
+ if (!second_try) {
+ second_try = true;
+ dev_dbg(dev, "Reloading firmware after unsuccessful resume\n");
+ goto retry;
+ }
dev_err(dev, "Resume error, status: 0x%02x\n", status);
return -ENODEV;
}
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index d5dfa40..b716739 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -195,6 +195,7 @@ static int st1232_ts_probe(struct i2c_client *client,
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
__set_bit(EV_SYN, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
diff --git a/drivers/input/touchscreen/synaptics_tcm/Kconfig b/drivers/input/touchscreen/synaptics_tcm/Kconfig
index 78c687d..b217f5b 100644
--- a/drivers/input/touchscreen/synaptics_tcm/Kconfig
+++ b/drivers/input/touchscreen/synaptics_tcm/Kconfig
@@ -89,7 +89,7 @@
config TOUCHSCREEN_SYNAPTICS_TCM_REFLASH
tristate "Synaptics TCM reflash module"
depends on TOUCHSCREEN_SYNAPTICS_TCM_CORE
- default y
+ default N
help
Say Y here to enable support for reflash functionality.
@@ -101,7 +101,7 @@
config TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY
tristate "Synaptics TCM recovery module"
depends on TOUCHSCREEN_SYNAPTICS_TCM_CORE
- default y
+ default N
help
Say Y here to enable support for recovery functionality.
diff --git a/drivers/input/touchscreen/synaptics_tcm/synaptics_tcm_core.c b/drivers/input/touchscreen/synaptics_tcm/synaptics_tcm_core.c
index 858fa15..76efa40 100644
--- a/drivers/input/touchscreen/synaptics_tcm/synaptics_tcm_core.c
+++ b/drivers/input/touchscreen/synaptics_tcm/synaptics_tcm_core.c
@@ -2840,6 +2840,8 @@ static int syna_tcm_resume(struct device *dev)
if (!tcm_hcd->init_okay)
syna_tcm_deferred_probe(dev);
+ else if (!tcm_hcd->in_suspend)
+ return 0;
else {
if (tcm_hcd->irq_enabled) {
tcm_hcd->watchdog.run = false;
@@ -2848,9 +2850,6 @@ static int syna_tcm_resume(struct device *dev)
}
}
- if (!tcm_hcd->in_suspend)
- return 0;
-
retval = pinctrl_select_state(
tcm_hcd->ts_pinctrl,
tcm_hcd->pinctrl_state_active);
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 01d5f08..91c2fe6 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -367,6 +367,14 @@
Enables bits of IOMMU API required by VFIO. The iommu_ops
is not implemented as it is not necessary for VFIO.
+config QTI_IOMMU_SUPPORT
+ tristate "Support for QTI iommu drivers"
+ help
+ The QTI GPU device may switch between multiple iommu domains,
+ depending on usecase. This introduces a need to track all such
+ domains in a non-driver specific manner.
+ If in doubt say N.
+
# ARM IOMMU support
config ARM_SMMU
bool "ARM Ltd. System MMU (SMMU) Support"
@@ -375,6 +383,7 @@
select IOMMU_IO_PGTABLE_LPAE
select ARM_DMA_USE_IOMMU if ARM
select ARM64_DMA_USE_IOMMU if ARM64
+ select QTI_IOMMU_SUPPORT
help
Support for implementations of the ARM System MMU architecture
versions 1 and 2.
@@ -495,26 +504,14 @@
if IOMMU_DEBUG
-config IOMMU_DEBUG_TRACKING
- bool "Track key IOMMU events"
- select IOMMU_API
- help
- Enables additional debug tracking in the IOMMU framework code.
- Tracking information and tests can be accessed through various
- debugfs files.
-
- Say Y here if you need to debug IOMMU issues and are okay with
- the performance penalty of the tracking.
-
config IOMMU_TESTS
bool "Interactive IOMMU performance/functional tests"
select IOMMU_API
select ARM64_PTDUMP_CORE
help
Enables a suite of IOMMU unit tests. The tests are runnable
- through debugfs. Unlike the IOMMU_DEBUG_TRACKING option, the
- impact of enabling this option to overal system performance
- should be minimal.
+ through debugfs. The impact of enabling this option to overall
+ system performance should be minimal.
endif # IOMMU_DEBUG
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 2d4b320..5a59ed1 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -34,4 +34,5 @@
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
+obj-$(CONFIG_QTI_IOMMU_SUPPORT) += iommu-logger.o
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 1f2ed44..9991386 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -139,10 +139,14 @@ static struct lock_class_key reserved_rbtree_key;
static inline int match_hid_uid(struct device *dev,
struct acpihid_map_entry *entry)
{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
const char *hid, *uid;
- hid = acpi_device_hid(ACPI_COMPANION(dev));
- uid = acpi_device_uid(ACPI_COMPANION(dev));
+ if (!adev)
+ return -ENODEV;
+
+ hid = acpi_device_hid(adev);
+ uid = acpi_device_uid(adev);
if (!hid || !(*hid))
return -ENODEV;
@@ -545,7 +549,7 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id,
dev_data = get_dev_data(&pdev->dev);
if (dev_data && __ratelimit(&dev_data->rs)) {
- dev_err(&pdev->dev, "AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%016llx flags=0x%04x]\n",
+ dev_err(&pdev->dev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%016llx flags=0x%04x]\n",
domain_id, address, flags);
} else if (printk_ratelimit()) {
pr_err("AMD-Vi: Event logged [IO_PAGE_FAULT device=%02x:%02x.%x domain=0x%04x address=0x%016llx flags=0x%04x]\n",
@@ -585,43 +589,41 @@ static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
if (type == EVENT_TYPE_IO_FAULT) {
amd_iommu_report_page_fault(devid, pasid, address, flags);
return;
- } else {
- dev_err(dev, "AMD-Vi: Event logged [");
}
switch (type) {
case EVENT_TYPE_ILL_DEV:
- dev_err(dev, "ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
+ dev_err(dev, "Event logged [ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
pasid, address, flags);
dump_dte_entry(devid);
break;
case EVENT_TYPE_DEV_TAB_ERR:
- dev_err(dev, "DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
+ dev_err(dev, "Event logged [DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
"address=0x%016llx flags=0x%04x]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
address, flags);
break;
case EVENT_TYPE_PAGE_TAB_ERR:
- dev_err(dev, "PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x domain=0x%04x address=0x%016llx flags=0x%04x]\n",
+ dev_err(dev, "Event logged [PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x domain=0x%04x address=0x%016llx flags=0x%04x]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
pasid, address, flags);
break;
case EVENT_TYPE_ILL_CMD:
- dev_err(dev, "ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
+ dev_err(dev, "Event logged [ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
dump_command(address);
break;
case EVENT_TYPE_CMD_HARD_ERR:
- dev_err(dev, "COMMAND_HARDWARE_ERROR address=0x%016llx flags=0x%04x]\n",
+ dev_err(dev, "Event logged [COMMAND_HARDWARE_ERROR address=0x%016llx flags=0x%04x]\n",
address, flags);
break;
case EVENT_TYPE_IOTLB_INV_TO:
- dev_err(dev, "IOTLB_INV_TIMEOUT device=%02x:%02x.%x address=0x%016llx]\n",
+ dev_err(dev, "Event logged [IOTLB_INV_TIMEOUT device=%02x:%02x.%x address=0x%016llx]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
address);
break;
case EVENT_TYPE_INV_DEV_REQ:
- dev_err(dev, "INVALID_DEVICE_REQUEST device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
+ dev_err(dev, "Event logged [INVALID_DEVICE_REQUEST device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
pasid, address, flags);
break;
@@ -629,12 +631,12 @@ static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
pasid = ((event[0] >> 16) & 0xFFFF)
| ((event[1] << 6) & 0xF0000);
tag = event[1] & 0x03FF;
- dev_err(dev, "INVALID_PPR_REQUEST device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
+ dev_err(dev, "Event logged [INVALID_PPR_REQUEST device=%02x:%02x.%x pasid=0x%05x address=0x%016llx flags=0x%04x]\n",
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
pasid, address, flags);
break;
default:
- dev_err(dev, "UNKNOWN event[0]=0x%08x event[1]=0x%08x event[2]=0x%08x event[3]=0x%08x\n",
+ dev_err(dev, "Event logged [UNKNOWN event[0]=0x%08x event[1]=0x%08x event[2]=0x%08x event[3]=0x%08x\n",
event[0], event[1], event[2], event[3]);
}
diff --git a/drivers/iommu/amd_iommu_quirks.c b/drivers/iommu/amd_iommu_quirks.c
index c235f79..5120ce4 100644
--- a/drivers/iommu/amd_iommu_quirks.c
+++ b/drivers/iommu/amd_iommu_quirks.c
@@ -74,6 +74,19 @@ static const struct dmi_system_id ivrs_quirks[] __initconst = {
.driver_data = (void *)&ivrs_ioapic_quirks[DELL_LATITUDE_5495],
},
{
+ /*
+ * Acer Aspire A315-41 requires the very same workaround as
+ * Dell Latitude 5495
+ */
+ .callback = ivrs_ioapic_quirk_cb,
+ .ident = "Acer Aspire A315-41",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-41"),
+ },
+ .driver_data = (void *)&ivrs_ioapic_quirks[DELL_LATITUDE_5495],
+ },
+ {
.callback = ivrs_ioapic_quirk_cb,
.ident = "Lenovo ideapad 330S-15ARR",
.matches = {
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index ec180b2..52e2686 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -567,7 +567,7 @@ struct arm_smmu_device {
int gerr_irq;
int combined_irq;
- atomic_t sync_nr;
+ u32 sync_nr;
unsigned long ias; /* IPA */
unsigned long oas; /* PA */
@@ -964,14 +964,13 @@ static int __arm_smmu_cmdq_issue_sync_msi(struct arm_smmu_device *smmu)
struct arm_smmu_cmdq_ent ent = {
.opcode = CMDQ_OP_CMD_SYNC,
.sync = {
- .msidata = atomic_inc_return_relaxed(&smmu->sync_nr),
.msiaddr = virt_to_phys(&smmu->sync_count),
},
};
- arm_smmu_cmdq_build_cmd(cmd, &ent);
-
spin_lock_irqsave(&smmu->cmdq.lock, flags);
+ ent.sync.msidata = ++smmu->sync_nr;
+ arm_smmu_cmdq_build_cmd(cmd, &ent);
arm_smmu_cmdq_insert_cmd(smmu, cmd);
spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
@@ -2196,7 +2195,6 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
{
int ret;
- atomic_set(&smmu->sync_nr, 0);
ret = arm_smmu_init_queues(smmu);
if (ret)
return ret;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fbdbdae..d80c98f 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -67,6 +67,7 @@
#include "io-pgtable.h"
#include "arm-smmu-regs.h"
#include "arm-smmu-debug.h"
+#include "iommu-logger.h"
#include <linux/debugfs.h>
#include <linux/uaccess.h>
@@ -394,6 +395,7 @@ struct arm_smmu_domain {
struct list_head secure_pool_list;
/* nonsecure pool protected by pgtbl_lock */
struct list_head nonsecure_pool;
+ struct iommu_debug_attachment *logger;
struct iommu_domain domain;
};
@@ -2079,17 +2081,24 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
unsigned long quirks = 0;
bool dynamic;
+ struct iommu_group *group;
mutex_lock(&smmu_domain->init_mutex);
if (smmu_domain->smmu)
goto out_unlock;
+ group = iommu_group_get(dev);
+ ret = iommu_logger_register(&smmu_domain->logger, domain, group);
+ iommu_group_put(group);
+ if (ret)
+ goto out_unlock;
+
if (domain->type == IOMMU_DOMAIN_DMA) {
ret = arm_smmu_setup_default_domain(dev, domain);
if (ret) {
dev_err(dev, "%s: default domain setup failed\n",
__func__);
- goto out_unlock;
+ goto out_logger;
}
}
@@ -2147,7 +2156,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) {
ret = -EINVAL;
- goto out_unlock;
+ goto out_logger;
}
switch (smmu_domain->stage) {
@@ -2195,7 +2204,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
break;
default:
ret = -EINVAL;
- goto out_unlock;
+ goto out_logger;
}
if (smmu_domain->attributes & (1 << DOMAIN_ATTR_FAST))
@@ -2217,10 +2226,18 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
ret = arm_smmu_alloc_cb(domain, smmu, dev);
if (ret < 0)
- goto out_unlock;
+ goto out_logger;
cfg->cbndx = ret;
+ if (!(smmu_domain->attributes & (1 << DOMAIN_ATTR_GEOMETRY))) {
+ /* Geometry is not set use the default geometry */
+ domain->geometry.aperture_start = 0;
+ domain->geometry.aperture_end = (1UL << ias) - 1;
+ if (domain->geometry.aperture_end >= SZ_1G * 4ULL)
+ domain->geometry.aperture_end = (SZ_1G * 4ULL) - 1;
+ }
+
if (arm_smmu_is_slave_side_secure(smmu_domain)) {
smmu_domain->pgtbl_cfg = (struct io_pgtable_cfg) {
.quirks = quirks,
@@ -2231,6 +2248,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
},
.tlb = smmu_domain->tlb_ops,
.iommu_dev = smmu->dev,
+ .iova_base = domain->geometry.aperture_start,
+ .iova_end = domain->geometry.aperture_end,
};
fmt = ARM_MSM_SECURE;
} else {
@@ -2241,6 +2260,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
.oas = oas,
.tlb = smmu_domain->tlb_ops,
.iommu_dev = smmu->dev,
+ .iova_base = domain->geometry.aperture_start,
+ .iova_end = domain->geometry.aperture_end,
};
}
@@ -2325,6 +2346,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
out_clear_smmu:
arm_smmu_destroy_domain_context(domain);
smmu_domain->smmu = NULL;
+out_logger:
+ iommu_logger_unregister(smmu_domain->logger);
+ smmu_domain->logger = NULL;
out_unlock:
mutex_unlock(&smmu_domain->init_mutex);
return ret;
@@ -2448,6 +2472,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
*/
arm_smmu_put_dma_cookie(domain);
arm_smmu_destroy_domain_context(domain);
+ iommu_logger_unregister(smmu_domain->logger);
kfree(smmu_domain);
}
@@ -2915,6 +2940,8 @@ static int arm_smmu_setup_default_domain(struct device *dev,
const char *str;
int attr = 1;
u32 val;
+ const __be32 *ranges;
+ int naddr, nsize, len;
np = arm_iommu_get_of_node(dev);
if (!np)
@@ -2991,6 +3018,30 @@ static int arm_smmu_setup_default_domain(struct device *dev,
if (of_property_read_bool(np, "qcom,iommu-earlymap"))
__arm_smmu_domain_set_attr(domain,
DOMAIN_ATTR_EARLY_MAP, &attr);
+
+ ranges = of_get_property(np, "qcom,iommu-geometry", &len);
+ if (ranges) {
+ struct iommu_domain_geometry geometry;
+
+ len /= sizeof(u32);
+ naddr = of_n_addr_cells(np);
+ nsize = of_n_size_cells(np);
+ if (len < naddr + nsize) {
+ dev_err(dev, "Invalid length for qcom,iommu-geometry, expected %d cells\n",
+ naddr + nsize);
+ return -EINVAL;
+ }
+ if (naddr == 0 || nsize == 0) {
+ dev_err(dev, "Invalid #address-cells %d or #size-cells %d\n",
+ naddr, nsize);
+ return -EINVAL;
+ }
+
+ geometry.aperture_start = of_read_number(ranges, naddr);
+ geometry.aperture_end = of_read_number(ranges + naddr, nsize);
+ __arm_smmu_domain_set_attr(domain, DOMAIN_ATTR_GEOMETRY,
+ &geometry);
+ }
return 0;
}
@@ -3036,10 +3087,12 @@ static struct iommu_group *of_get_device_group(struct device *dev)
if (ret > 0)
return data.group;
+#ifdef CONFIG_PCI
ret = bus_for_each_dev(&pci_bus_type, NULL, &data,
__bus_lookup_iommu_group);
if (ret > 0)
return data.group;
+#endif
group = generic_device_group(dev);
if (IS_ERR(group))
@@ -3992,6 +4045,40 @@ static int __arm_smmu_domain_set_attr2(struct iommu_domain *domain,
ret = 0;
break;
}
+
+ case DOMAIN_ATTR_GEOMETRY: {
+ struct iommu_domain_geometry *geometry =
+ (struct iommu_domain_geometry *)data;
+
+ if (smmu_domain->smmu != NULL) {
+ dev_err(smmu_domain->smmu->dev,
+ "cannot set geometry attribute while attached\n");
+ ret = -EBUSY;
+ break;
+ }
+
+ if (geometry->aperture_start >= SZ_1G * 4ULL ||
+ geometry->aperture_end >= SZ_1G * 4ULL) {
+ pr_err("fastmap does not support IOVAs >= 4GB\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (geometry->aperture_start
+ < domain->geometry.aperture_start)
+ domain->geometry.aperture_start =
+ geometry->aperture_start;
+
+ if (geometry->aperture_end
+ > domain->geometry.aperture_end)
+ domain->geometry.aperture_end =
+ geometry->aperture_end;
+
+ smmu_domain->attributes |= 1 << DOMAIN_ATTR_GEOMETRY;
+ ret = 0;
+ break;
+ }
+
default:
ret = -ENODEV;
}
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 53ff42a..8c3402d 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*/
#include <linux/dma-contiguous.h>
@@ -210,7 +210,11 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
iommu_tlbiall(mapping->domain);
mapping->have_stale_tlbs = false;
- av8l_fast_clear_stale_ptes(mapping->pgtbl_ops, skip_sync);
+ av8l_fast_clear_stale_ptes(mapping->pgtbl_ops,
+ mapping->domain->geometry.aperture_start,
+ mapping->base,
+ mapping->base + mapping->size - 1,
+ skip_sync);
}
return (bit << FAST_PAGE_SHIFT) + mapping->base;
@@ -1075,7 +1079,7 @@ static const struct dma_map_ops fast_smmu_dma_ops = {
*
* Creates a mapping structure which holds information about used/unused IO
* address ranges, which is required to perform mapping with IOMMU aware
- * functions. The only VA range supported is [0, 4GB).
+ * functions. The only VA range supported is [0, 4GB].
*
* The client device need to be attached to the mapping with
* fast_smmu_attach_device function.
@@ -1219,6 +1223,9 @@ int fast_smmu_init_mapping(struct device *dev,
fast->dev = dev;
domain->iova_cookie = fast;
+ domain->geometry.aperture_start = mapping->base;
+ domain->geometry.aperture_end = mapping->base + size - 1;
+
if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PGTBL_INFO,
&info)) {
dev_err(dev, "Couldn't get page table info\n");
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index aeccbf3b..5f2cba7 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -802,13 +802,12 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
return 0;
tablep = iopte_deref(pte, data);
+ } else if (unmap_idx >= 0) {
+ io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true);
+ return size;
}
- if (unmap_idx < 0)
- return __arm_lpae_unmap(data, iova, size, lvl, tablep);
-
- io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true);
- return size;
+ return __arm_lpae_unmap(data, iova, size, lvl, tablep);
}
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 1207037..07c8d38 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "io-pgtable-fast: " fmt
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/io-pgtable-fast.h>
+#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <linux/vmalloc.h>
@@ -35,6 +36,10 @@ struct av8l_fast_io_pgtable {
av8l_fast_iopte *puds[4];
av8l_fast_iopte *pmds;
struct page **pages; /* page table memory */
+ int nr_pages;
+ dma_addr_t base;
+ dma_addr_t start;
+ dma_addr_t end;
};
/* Page table bits */
@@ -143,7 +148,7 @@ struct av8l_fast_io_pgtable {
#define PTE_SH_IDX(pte) (pte & AV8L_FAST_PTE_SH_MASK)
-#define iopte_pmd_offset(pmds, iova) (pmds + (iova >> 12))
+#define iopte_pmd_offset(pmds, base, iova) (pmds + ((iova - base) >> 12))
#ifdef CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB
@@ -172,13 +177,15 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
}
}
-void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, bool skip_sync)
+void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, u64 base,
+ u64 start, u64 end, bool skip_sync)
{
int i;
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *pmdp = data->pmds;
+ av8l_fast_iopte *pmdp = iopte_pmd_offset(pmds, base, start);
- for (i = 0; i < ((SZ_1G * 4UL) >> AV8L_FAST_PAGE_SHIFT); ++i) {
+ for (i = start >> AV8L_FAST_PAGE_SHIFT;
+ i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
*pmdp = 0;
if (!skip_sync)
@@ -233,7 +240,7 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long i, nptes = size >> AV8L_FAST_PAGE_SHIFT;
av8l_fast_iopte pte;
@@ -265,7 +272,7 @@ __av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
? AV8L_FAST_PTE_UNMAPPED_NEED_TLBI
: 0;
- ptep = iopte_pmd_offset(data->pmds, iova);
+ ptep = iopte_pmd_offset(data->pmds, data->base, iova);
nptes = size >> AV8L_FAST_PAGE_SHIFT;
memset(ptep, val, sizeof(*ptep) * nptes);
@@ -363,7 +370,7 @@ static bool av8l_fast_iova_coherent(struct io_pgtable_ops *ops,
unsigned long iova)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
return ((PTE_MAIR_IDX(*ptep) == AV8L_FAST_MAIR_ATTR_IDX_CACHE) &&
((PTE_SH_IDX(*ptep) == AV8L_FAST_PTE_SH_OS) ||
@@ -397,7 +404,7 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
}
/*
- * We need 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
+ * We need max 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
* 2048 pages for pmds (each pud page contains 512 table entries, each
* pointing to a pmd).
*/
@@ -406,12 +413,38 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
#define NUM_PMD_PAGES 2048
#define NUM_PGTBL_PAGES (NUM_PGD_PAGES + NUM_PUD_PAGES + NUM_PMD_PAGES)
+/* undefine arch specific definitions which depends on page table format */
+#undef pud_index
+#undef pud_mask
+#undef pud_next
+#undef pmd_index
+#undef pmd_mask
+#undef pmd_next
+
+#define pud_index(addr) (((addr) >> 30) & 0x3)
+#define pud_mask(addr) ((addr) & ~((1UL << 30) - 1))
+#define pud_next(addr, end) \
+({ unsigned long __boundary = pud_mask(addr + (1UL << 30));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
+#define pmd_index(addr) (((addr) >> 21) & 0x1ff)
+#define pmd_mask(addr) ((addr) & ~((1UL << 21) - 1))
+#define pmd_next(addr, end) \
+({ unsigned long __boundary = pmd_mask(addr + (1UL << 21));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
static int
av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
struct io_pgtable_cfg *cfg, void *cookie)
{
int i, j, pg = 0;
struct page **pages, *page;
+ dma_addr_t base = cfg->iova_base;
+ dma_addr_t end = cfg->iova_end;
+ dma_addr_t pud, pmd;
+ int pmd_pg_index;
pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN |
__GFP_NORETRY);
@@ -429,10 +462,11 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
data->pgd = page_address(page);
/*
- * We need 2048 entries at level 2 to map 4GB of VA space. A page
- * can hold 512 entries, so we need 4 pages.
+ * We need max 2048 entries at level 2 to map 4GB of VA space. A page
+ * can hold 512 entries, so we need max 4 pages.
*/
- for (i = 0; i < 4; ++i) {
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
av8l_fast_iopte pte, *ptep;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -447,12 +481,15 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->pgd, data->pgd + 4);
/*
- * We have 4 puds, each of which can point to 512 pmds, so we'll
- * have 2048 pmds, each of which can hold 512 ptes, for a grand
+ * We have max 4 puds, each of which can point to 512 pmds, so we'll
+ * have max 2048 pmds, each of which can hold 512 ptes, for a grand
* total of 2048*512=1048576 PTEs.
*/
- for (i = 0; i < 4; ++i) {
- for (j = 0; j < 512; ++j) {
+ pmd_pg_index = pg;
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
+ for (j = pmd_index(pud), pmd = pud; pmd < pud_next(pud, end);
+ ++j, pmd = pmd_next(pmd, end)) {
av8l_fast_iopte pte, *pudp;
void *addr;
@@ -471,21 +508,21 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->puds[i], data->puds[i] + 512);
}
- if (WARN_ON(pg != NUM_PGTBL_PAGES))
- goto err_free_pages;
-
/*
* We map the pmds into a virtually contiguous space so that we
* don't have to traverse the first two levels of the page tables
* to find the appropriate pud. Instead, it will be a simple
* offset from the virtual base of the pmds.
*/
- data->pmds = vmap(&pages[NUM_PGD_PAGES + NUM_PUD_PAGES], NUM_PMD_PAGES,
+ data->pmds = vmap(&pages[pmd_pg_index], pg - pmd_pg_index,
VM_IOREMAP, PAGE_KERNEL);
if (!data->pmds)
goto err_free_pages;
data->pages = pages;
+ data->nr_pages = pg;
+ data->base = base;
+ data->end = end;
return 0;
err_free_pages:
@@ -591,7 +628,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop)
struct av8l_fast_io_pgtable *data = iof_pgtable_to_data(iop);
vunmap(data->pmds);
- for (i = 0; i < NUM_PGTBL_PAGES; ++i)
+ for (i = 0; i < data->nr_pages; ++i)
__free_page(data->pages[i]);
kvfree(data->pages);
kfree(data);
@@ -663,6 +700,7 @@ static int __init av8l_fast_positive_testing(void)
struct av8l_fast_io_pgtable *data;
av8l_fast_iopte *pmds;
u64 max = SZ_1G * 4ULL - 1;
+ u64 base = 0;
cfg = (struct io_pgtable_cfg) {
.quirks = 0,
@@ -670,6 +708,8 @@ static int __init av8l_fast_positive_testing(void)
.ias = 32,
.oas = 32,
.pgsize_bitmap = SZ_4K,
+ .iova_base = base,
+ .iova_end = max,
};
cfg_cookie = &cfg;
@@ -682,81 +722,81 @@ static int __init av8l_fast_positive_testing(void)
pmds = data->pmds;
/* map the entire 4GB VA space with 4K map calls */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_4K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_4K) != SZ_4K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(ops, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 8K map calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_8K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all with 8K unmap calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_8K) != SZ_8K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(ops, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 16K map calls */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_16K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_16K) != SZ_16K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(ops, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 64K map calls */
- for (iova = 0; iova < max; iova += SZ_64K) {
+ for (iova = base; iova < max; iova += SZ_64K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_64K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all at once */
- if (WARN_ON(ops->unmap(ops, 0, max) != max))
+ if (WARN_ON(ops->unmap(ops, base, max - base) != (max - base)))
failed++;
free_io_pgtable_ops(ops);
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 7f8c864..e32e494 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -114,6 +114,8 @@ struct io_pgtable_cfg {
unsigned int oas;
const struct iommu_gather_ops *tlb;
struct device *iommu_dev;
+ dma_addr_t iova_base;
+ dma_addr_t iova_end;
/* Low-level data specific to the table format */
union {
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index dee31ad..b0533d1 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -86,68 +86,6 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
}
#endif
-#ifdef CONFIG_IOMMU_DEBUG_TRACKING
-
-static DEFINE_MUTEX(iommu_debug_attachments_lock);
-static LIST_HEAD(iommu_debug_attachments);
-
-/*
- * Each group may have more than one domain; but each domain may
- * only have one group.
- * Used by debug tools to display the name of the device(s) associated
- * with a particular domain.
- */
-struct iommu_debug_attachment {
- struct iommu_domain *domain;
- struct iommu_group *group;
- struct list_head list;
-};
-
-void iommu_debug_attach_device(struct iommu_domain *domain,
- struct device *dev)
-{
- struct iommu_debug_attachment *attach;
- struct iommu_group *group;
-
- group = dev->iommu_group;
- if (!group)
- return;
-
- mutex_lock(&iommu_debug_attachments_lock);
- list_for_each_entry(attach, &iommu_debug_attachments, list)
- if ((attach->domain == domain) && (attach->group == group))
- goto out;
-
- attach = kzalloc(sizeof(*attach), GFP_KERNEL);
- if (!attach)
- goto out;
-
- attach->domain = domain;
- attach->group = group;
- INIT_LIST_HEAD(&attach->list);
-
- list_add(&attach->list, &iommu_debug_attachments);
-out:
- mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-void iommu_debug_domain_remove(struct iommu_domain *domain)
-{
- struct iommu_debug_attachment *it, *tmp;
-
- mutex_lock(&iommu_debug_attachments_lock);
- list_for_each_entry_safe(it, tmp, &iommu_debug_attachments, list) {
- if (it->domain != domain)
- continue;
- list_del(&it->list);
- kfree(it);
- }
-
- mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-#endif
-
#ifdef CONFIG_IOMMU_TESTS
#ifdef CONFIG_64BIT
@@ -233,21 +171,25 @@ static void iommu_debug_destroy_phoney_sg_table(struct device *dev,
struct iommu_debug_attr {
unsigned long dma_type;
int vmid;
+ struct iommu_domain_geometry geometry;
};
static struct iommu_debug_attr std_attr = {
.dma_type = 0,
.vmid = 0,
+ .geometry = {0, 0, 0},
};
static struct iommu_debug_attr fastmap_attr = {
.dma_type = DOMAIN_ATTR_FAST,
.vmid = 0,
+ .geometry = {0, (dma_addr_t)(SZ_1G * 4ULL - 1), 0},
};
static struct iommu_debug_attr secure_attr = {
.dma_type = 0,
.vmid = VMID_CP_PIXEL,
+ .geometry = {0, 0, 0},
};
static int iommu_debug_set_attrs(struct iommu_debug_device *ddev,
@@ -266,6 +208,10 @@ static int iommu_debug_set_attrs(struct iommu_debug_device *ddev,
iommu_domain_set_attr(domain,
DOMAIN_ATTR_SECURE_VMID, &attrs->vmid);
+ if (attrs->geometry.aperture_end || attrs->geometry.aperture_start)
+ iommu_domain_set_attr(domain,
+ DOMAIN_ATTR_GEOMETRY, &attrs->geometry);
+
return 0;
}
@@ -1448,9 +1394,13 @@ static ssize_t iommu_debug_test_virt_addr_read(struct file *file,
memset(buf, 0, buf_len);
- if (!test_virt_addr)
+ if (IS_ERR_OR_NULL(test_virt_addr))
+ test_virt_addr = kzalloc(SZ_1M, GFP_KERNEL);
+
+ if (!test_virt_addr) {
+ test_virt_addr = ERR_PTR(-ENOMEM);
strlcpy(buf, "FAIL\n", buf_len);
- else
+ } else
snprintf(buf, buf_len, "0x%pK\n", test_virt_addr);
return simple_read_from_buffer(ubuf, count, offset, buf, strlen(buf));
@@ -1780,6 +1730,12 @@ static ssize_t iommu_debug_dma_map_write(struct file *file,
if (kstrtouint(comma2 + 1, 0, &attr))
goto invalid_format;
+ if (IS_ERR(test_virt_addr))
+ goto allocation_failure;
+
+ if (!test_virt_addr)
+ goto missing_allocation;
+
if (v_addr < test_virt_addr || v_addr + size > test_virt_addr + SZ_1M)
goto invalid_addr;
@@ -1827,6 +1783,14 @@ static ssize_t iommu_debug_dma_map_write(struct file *file,
invalid_addr:
pr_err_ratelimited("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat test_virt_addr'.\n");
return retval;
+
+allocation_failure:
+ pr_err_ratelimited("Allocation of test_virt_addr failed.\n");
+ return -ENOMEM;
+
+missing_allocation:
+ pr_err_ratelimited("Please attempt to do 'cat test_virt_addr'.\n");
+ return retval;
}
static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf,
@@ -2336,11 +2300,6 @@ static int iommu_debug_init_tests(void)
return -ENODEV;
}
- test_virt_addr = kzalloc(SZ_1M, GFP_KERNEL);
-
- if (!test_virt_addr)
- return -ENOMEM;
-
return bus_for_each_dev(&platform_bus_type, NULL, NULL,
snarf_iommu_devices);
}
diff --git a/drivers/iommu/iommu-debug.h b/drivers/iommu/iommu-debug.h
deleted file mode 100644
index d21a7e0..0000000
--- a/drivers/iommu/iommu-debug.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
- */
-
-#ifndef IOMMU_DEBUG_H
-#define IOMMU_DEBUG_H
-
-#ifdef CONFIG_IOMMU_DEBUG_TRACKING
-
-void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev);
-void iommu_debug_domain_remove(struct iommu_domain *domain);
-
-#else /* !CONFIG_IOMMU_DEBUG_TRACKING */
-
-static inline void iommu_debug_attach_device(struct iommu_domain *domain,
- struct device *dev)
-{
-}
-
-static inline void iommu_debug_domain_remove(struct iommu_domain *domain)
-{
-}
-
-#endif /* CONFIG_IOMMU_DEBUG_TRACKING */
-
-#endif /* IOMMU_DEBUG_H */
diff --git a/drivers/iommu/iommu-logger.c b/drivers/iommu/iommu-logger.c
new file mode 100644
index 0000000..1542640
--- /dev/null
+++ b/drivers/iommu/iommu-logger.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(iommu_debug_attachments_lock);
+static LIST_HEAD(iommu_debug_attachments);
+
+/*
+ * Each group may have more than one domain; but each domain may
+ * only have one group.
+ * Used by debug tools to display the name of the device(s) associated
+ * with a particular domain.
+ */
+struct iommu_debug_attachment {
+ struct iommu_domain *domain;
+ struct iommu_group *group;
+ struct list_head list;
+};
+
+static struct iommu_debug_attachment *iommu_logger_init(
+ struct iommu_domain *domain,
+ struct iommu_group *group)
+{
+ struct iommu_debug_attachment *logger;
+
+ logger = kzalloc(sizeof(*logger), GFP_KERNEL);
+ if (!logger)
+ return NULL;
+
+ INIT_LIST_HEAD(&logger->list);
+ logger->domain = domain;
+ logger->group = group;
+
+ return logger;
+}
+
+int iommu_logger_register(struct iommu_debug_attachment **logger_out,
+ struct iommu_domain *domain,
+ struct iommu_group *group)
+{
+ struct iommu_debug_attachment *logger;
+
+ if (!logger_out)
+ return -EINVAL;
+
+ logger = iommu_logger_init(domain, group);
+ if (!logger)
+ return -ENOMEM;
+
+ mutex_lock(&iommu_debug_attachments_lock);
+ list_add(&logger->list, &iommu_debug_attachments);
+ mutex_unlock(&iommu_debug_attachments_lock);
+
+ *logger_out = logger;
+ return 0;
+}
+EXPORT_SYMBOL(iommu_logger_register);
+
+void iommu_logger_unregister(struct iommu_debug_attachment *logger)
+{
+ if (!logger)
+ return;
+
+ mutex_lock(&iommu_debug_attachments_lock);
+ list_del(&logger->list);
+ mutex_unlock(&iommu_debug_attachments_lock);
+ kfree(logger);
+}
+EXPORT_SYMBOL(iommu_logger_unregister);
+
+MODULE_DESCRIPTION("QTI IOMMU SUPPORT");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/iommu-logger.h b/drivers/iommu/iommu-logger.h
new file mode 100644
index 0000000..3afe187
--- /dev/null
+++ b/drivers/iommu/iommu-logger.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+
+#ifndef __LINUX_QTI_IOMMU_LOGGER_H
+#define __LINUX_QTI_IOMMU_LOGGER_H
+
+struct iommu_debug_attachment;
+
+#if IS_ENABLED(CONFIG_QTI_IOMMU_SUPPORT)
+
+int iommu_logger_register(struct iommu_debug_attachment **a,
+ struct iommu_domain *domain,
+ struct iommu_group *group);
+void iommu_logger_unregister(struct iommu_debug_attachment *a);
+#else
+static inline int iommu_logger_register(struct iommu_debug_attachment **a,
+ struct iommu_domain *domain,
+ struct iommu_group *group)
+{
+ return 0;
+}
+
+static inline void iommu_logger_unregister(struct iommu_debug_attachment *a) {}
+#endif /* CONFIG_QTI_IOMMU_LOGGER */
+#endif /* __LINUX_QTI_IOMMU_LOGGER_H */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index ab8047ce..436d047 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -35,8 +35,6 @@
#include <linux/property.h>
#include <trace/events/iommu.h>
-#include "iommu-debug.h"
-
static struct kset *iommu_group_kset;
static DEFINE_IDA(iommu_group_ida);
#ifdef CONFIG_IOMMU_DEFAULT_PASSTHROUGH
@@ -1320,7 +1318,6 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc);
void iommu_domain_free(struct iommu_domain *domain)
{
- iommu_debug_domain_remove(domain);
domain->ops->domain_free(domain);
}
EXPORT_SYMBOL_GPL(iommu_domain_free);
@@ -1339,7 +1336,6 @@ static int __iommu_attach_device(struct iommu_domain *domain,
ret = domain->ops->attach_dev(domain, dev);
if (!ret) {
trace_attach_device_to_domain(dev);
- iommu_debug_attach_device(domain, dev);
if (!strnlen(domain->name, IOMMU_DOMAIN_NAME_LEN)) {
strlcpy(domain->name, dev_name(dev),
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index a27debb..19259ae 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -318,7 +318,7 @@ static DEFINE_MUTEX(iova_cache_mutex);
struct iova *alloc_iova_mem(void)
{
- return kmem_cache_alloc(iova_cache, GFP_ATOMIC);
+ return kmem_cache_zalloc(iova_cache, GFP_ATOMIC);
}
EXPORT_SYMBOL(alloc_iova_mem);
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index ad3e2b9..140b287 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -977,13 +977,13 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
if (!dma_dev)
return NULL;
- rk_domain = devm_kzalloc(dma_dev, sizeof(*rk_domain), GFP_KERNEL);
+ rk_domain = kzalloc(sizeof(*rk_domain), GFP_KERNEL);
if (!rk_domain)
return NULL;
if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&rk_domain->domain))
- return NULL;
+ goto err_free_domain;
/*
* rk32xx iommus use a 2 level pagetable.
@@ -1018,6 +1018,8 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
err_put_cookie:
if (type == IOMMU_DOMAIN_DMA)
iommu_put_dma_cookie(&rk_domain->domain);
+err_free_domain:
+ kfree(rk_domain);
return NULL;
}
@@ -1046,6 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
if (domain->type == IOMMU_DOMAIN_DMA)
iommu_put_dma_cookie(&rk_domain->domain);
+ kfree(rk_domain);
}
static int rk_iommu_add_device(struct device *dev)
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 121d3cb..fa0ecb5 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -164,9 +164,9 @@ static bool smmu_dma_addr_valid(struct tegra_smmu *smmu, dma_addr_t addr)
return (addr & smmu->pfn_mask) == addr;
}
-static dma_addr_t smmu_pde_to_dma(u32 pde)
+static dma_addr_t smmu_pde_to_dma(struct tegra_smmu *smmu, u32 pde)
{
- return pde << 12;
+ return (dma_addr_t)(pde & smmu->pfn_mask) << 12;
}
static void smmu_flush_ptc_all(struct tegra_smmu *smmu)
@@ -551,6 +551,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,
dma_addr_t *dmap)
{
unsigned int pd_index = iova_pd_index(iova);
+ struct tegra_smmu *smmu = as->smmu;
struct page *pt_page;
u32 *pd;
@@ -559,7 +560,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,
return NULL;
pd = page_address(as->pd);
- *dmap = smmu_pde_to_dma(pd[pd_index]);
+ *dmap = smmu_pde_to_dma(smmu, pd[pd_index]);
return tegra_smmu_pte_offset(pt_page, iova);
}
@@ -601,7 +602,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
} else {
u32 *pd = page_address(as->pd);
- *dmap = smmu_pde_to_dma(pd[pde]);
+ *dmap = smmu_pde_to_dma(smmu, pd[pde]);
}
return tegra_smmu_pte_offset(as->pts[pde], iova);
@@ -626,7 +627,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)
if (--as->count[pde] == 0) {
struct tegra_smmu *smmu = as->smmu;
u32 *pd = page_address(as->pd);
- dma_addr_t pte_dma = smmu_pde_to_dma(pd[pde]);
+ dma_addr_t pte_dma = smmu_pde_to_dma(smmu, pd[pde]);
tegra_smmu_set_pde(as, iova, 0);
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
index 0f6e30e..f53dfc5 100644
--- a/drivers/irqchip/irq-bcm7038-l1.c
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -284,6 +284,10 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
pr_err("failed to map parent interrupt %d\n", parent_irq);
return -EINVAL;
}
+
+ if (of_property_read_bool(dn, "brcm,irq-can-wake"))
+ enable_irq_wake(parent_irq);
+
irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle,
intc);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e7549a2b..050d6e0 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -182,6 +182,22 @@ static DEFINE_IDA(its_vpeid_ida);
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
#define gic_data_rdist_vlpi_base() (gic_data_rdist_rd_base() + SZ_128K)
+static u16 get_its_list(struct its_vm *vm)
+{
+ struct its_node *its;
+ unsigned long its_list = 0;
+
+ list_for_each_entry(its, &its_nodes, entry) {
+ if (!its->is_v4)
+ continue;
+
+ if (vm->vlpi_count[its->list_nr])
+ __set_bit(its->list_nr, &its_list);
+ }
+
+ return (u16)its_list;
+}
+
static struct its_collection *dev_event_to_col(struct its_device *its_dev,
u32 event)
{
@@ -983,17 +999,15 @@ static void its_send_vmapp(struct its_node *its,
static void its_send_vmovp(struct its_vpe *vpe)
{
- struct its_cmd_desc desc;
+ struct its_cmd_desc desc = {};
struct its_node *its;
unsigned long flags;
int col_id = vpe->col_idx;
desc.its_vmovp_cmd.vpe = vpe;
- desc.its_vmovp_cmd.its_list = (u16)its_list_map;
if (!its_list_map) {
its = list_first_entry(&its_nodes, struct its_node, entry);
- desc.its_vmovp_cmd.seq_num = 0;
desc.its_vmovp_cmd.col = &its->collections[col_id];
its_send_single_vcommand(its, its_build_vmovp_cmd, &desc);
return;
@@ -1010,6 +1024,7 @@ static void its_send_vmovp(struct its_vpe *vpe)
raw_spin_lock_irqsave(&vmovp_lock, flags);
desc.its_vmovp_cmd.seq_num = vmovp_seq_num++;
+ desc.its_vmovp_cmd.its_list = get_its_list(vpe->its_vm);
/* Emit VMOVPs */
list_for_each_entry(its, &its_nodes, entry) {
diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c
index 2ff0898..be6923ab 100644
--- a/drivers/irqchip/irq-ingenic.c
+++ b/drivers/irqchip/irq-ingenic.c
@@ -117,6 +117,14 @@ static int __init ingenic_intc_of_init(struct device_node *node,
goto out_unmap_irq;
}
+ domain = irq_domain_add_legacy(node, num_chips * 32,
+ JZ4740_IRQ_BASE, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!domain) {
+ err = -ENOMEM;
+ goto out_unmap_base;
+ }
+
for (i = 0; i < num_chips; i++) {
/* Mask all irqs */
writel(0xffffffff, intc->base + (i * CHIP_SIZE) +
@@ -143,14 +151,11 @@ static int __init ingenic_intc_of_init(struct device_node *node,
IRQ_NOPROBE | IRQ_LEVEL);
}
- domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
- &irq_domain_simple_ops, NULL);
- if (!domain)
- pr_warn("unable to register IRQ domain\n");
-
setup_irq(parent_irq, &intc_cascade_action);
return 0;
+out_unmap_base:
+ iounmap(intc->base);
out_unmap_irq:
irq_dispose_mapping(parent_irq);
out_free:
diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c
index 1306333..a2a3acd 100644
--- a/drivers/irqchip/irq-mvebu-icu.c
+++ b/drivers/irqchip/irq-mvebu-icu.c
@@ -105,7 +105,7 @@ static int
mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *hwirq, unsigned int *type)
{
- struct mvebu_icu *icu = d->host_data;
+ struct mvebu_icu *icu = platform_msi_get_host_data(d);
unsigned int icu_group;
/* Check the count of the parameters in dt */
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 21786a4..c67fd2f 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -744,7 +744,7 @@ capi_poll(struct file *file, poll_table *wait)
poll_wait(file, &(cdev->recvwait), wait);
mask = EPOLLOUT | EPOLLWRNORM;
- if (!skb_queue_empty(&cdev->recvqueue))
+ if (!skb_queue_empty_lockless(&cdev->recvqueue))
mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index eade36d..4c239f1 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -574,8 +574,7 @@ static int gigaset_initcshw(struct cardstate *cs)
{
struct usb_cardstate *ucs;
- cs->hw.usb = ucs =
- kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
+ cs->hw.usb = ucs = kzalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
if (!ucs) {
pr_err("out of memory\n");
return -ENOMEM;
@@ -587,9 +586,6 @@ static int gigaset_initcshw(struct cardstate *cs)
ucs->bchars[3] = 0;
ucs->bchars[4] = 0x11;
ucs->bchars[5] = 0x13;
- ucs->bulk_out_buffer = NULL;
- ucs->bulk_out_urb = NULL;
- ucs->read_urb = NULL;
tasklet_init(&cs->write_tasklet,
gigaset_modem_fill, (unsigned long) cs);
@@ -688,6 +684,11 @@ static int gigaset_probe(struct usb_interface *interface,
return -ENODEV;
}
+ if (hostif->desc.bNumEndpoints < 2) {
+ dev_err(&interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
/* allocate memory for our device state and initialize it */
@@ -707,6 +708,12 @@ static int gigaset_probe(struct usb_interface *interface,
endpoint = &hostif->endpoint[0].desc;
+ if (!usb_endpoint_is_bulk_out(endpoint)) {
+ dev_err(&interface->dev, "missing bulk-out endpoint\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
ucs->bulk_out_size = buffer_size;
ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
@@ -726,6 +733,12 @@ static int gigaset_probe(struct usb_interface *interface,
endpoint = &hostif->endpoint[1].desc;
+ if (!usb_endpoint_is_int_in(endpoint)) {
+ dev_err(&interface->dev, "missing int-in endpoint\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
ucs->busy = 0;
ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
index 12d9e5f..58635b5 100644
--- a/drivers/isdn/mISDN/tei.c
+++ b/drivers/isdn/mISDN/tei.c
@@ -1180,8 +1180,7 @@ static int
ctrl_teimanager(struct manager *mgr, void *arg)
{
/* currently we only have one option */
- int *val = (int *)arg;
- int ret = 0;
+ unsigned int *val = (unsigned int *)arg;
switch (val[0]) {
case IMCLEAR_L2:
@@ -1197,9 +1196,9 @@ ctrl_teimanager(struct manager *mgr, void *arg)
test_and_clear_bit(OPTION_L1_HOLD, &mgr->options);
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- return ret;
+ return 0;
}
/* This function does create a L2 for fixed TEI in NT Mode */
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 44e132b..0dce3bf 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -208,6 +208,21 @@
This option enables support for the Soekris net4801 and net4826 error
LED.
+config LEDS_QTI_FLASH
+ tristate "Support for QTI Flash LEDs"
+ depends on LEDS_CLASS_FLASH
+ depends on MFD_SPMI_PMIC
+ select LEDS_TRIGGERS
+ help
+ This driver supports flash LED peripheral that is present on
+ some Qualcomm Technologies, Inc. PMICs (e.g. PM8350C). It can
+ configure the flash LED target current for several independent
+ channels. It also supports various over current and over
+ temperature mitigation features.
+
+ To compile this driver as a module, choose M here: the
+ module will be called leds-qti-flash.
+
config LEDS_FSG
tristate "LED Support for the Freecom FSG-3"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6a8b49a..d3900c4 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
+obj-$(CONFIG_LEDS_QTI_FLASH) += leds-qti-flash.o leds-qpnp-flash-common.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
@@ -79,7 +80,7 @@
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
obj-$(CONFIG_LEDS_QTI_TRI_LED) += leds-qti-tri-led.o
-obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o
+obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o leds-qpnp-flash-common.o
obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o
# LED SPI Drivers
diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c
index 4f413a7..d79a66a 100644
--- a/drivers/leds/leds-lm3692x.c
+++ b/drivers/leds/leds-lm3692x.c
@@ -337,9 +337,18 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
return ret;
}
- led->regulator = devm_regulator_get(&led->client->dev, "vled");
- if (IS_ERR(led->regulator))
+ led->regulator = devm_regulator_get_optional(&led->client->dev, "vled");
+ if (IS_ERR(led->regulator)) {
+ ret = PTR_ERR(led->regulator);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&led->client->dev,
+ "Failed to get vled regulator: %d\n",
+ ret);
+ return ret;
+ }
led->regulator = NULL;
+ }
child = device_get_next_child_node(&led->client->dev, child);
if (!child) {
diff --git a/drivers/leds/leds-qpnp-flash-common.c b/drivers/leds/leds-qpnp-flash-common.c
new file mode 100644
index 0000000..e65a00c
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash-common.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include "leds.h"
+
+struct flash_data {
+ struct list_head link;
+ struct device *dev;
+ int (*func)(struct led_trigger *trig, int options,
+ int *max_current);
+};
+
+static LIST_HEAD(flash_common_data);
+
+int qpnp_flash_register_led_prepare(struct device *dev, void *data)
+{
+ struct flash_data *flash_data;
+
+ flash_data = devm_kzalloc(dev, sizeof(struct flash_data),
+ GFP_KERNEL);
+ if (!flash_data)
+ return -ENOMEM;
+
+ flash_data->dev = dev;
+ flash_data->func = data;
+
+ list_add(&flash_data->link, &flash_common_data);
+
+ return 0;
+}
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current)
+{
+ struct flash_data *flash_data;
+ struct led_classdev *led_cdev;
+ int rc = -ENODEV;
+
+ led_cdev = trigger_to_lcdev(trig);
+ list_for_each_entry(flash_data, &flash_common_data, link) {
+ if (led_cdev->dev->parent == flash_data->dev)
+ rc = flash_data->func(trig, options, max_current);
+ }
+
+ return rc;
+}
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 4a2d664..8868a3c 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "flashv2: %s: " fmt, __func__
@@ -1762,7 +1762,7 @@ static int qpnp_flash_led_regulator_control(struct led_classdev *led_cdev,
return 0;
}
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+static int qpnp_flash_leds_prepare(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev;
@@ -3116,6 +3116,13 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
}
}
+ rc = qpnp_flash_register_led_prepare(&pdev->dev,
+ qpnp_flash_leds_prepare);
+ if (rc < 0) {
+ pr_err("Failed to register flash_led_prepare, rc=%d\n", rc);
+ goto sysfs_fail;
+ }
+
dev_set_drvdata(&pdev->dev, led);
return 0;
diff --git a/drivers/leds/leds-qti-flash.c b/drivers/leds/leds-qti-flash.c
new file mode 100644
index 0000000..219621d
--- /dev/null
+++ b/drivers/leds/leds-qti-flash.c
@@ -0,0 +1,1256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+#define pr_fmt(fmt) "qti-flash: %s: " fmt, __func__
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/led-class-flash.h>
+#include <linux/leds-qti-flash.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "leds.h"
+
+#define FLASH_LED_STATUS1 0x06
+
+#define FLASH_LED_STATUS2 0x07
+#define FLASH_LED_VPH_PWR_LOW BIT(0)
+
+#define FLASH_INT_RT_STS 0x10
+#define FLASH_LED_FAULT_RT_STS BIT(0)
+#define FLASH_LED_ALL_RAMP_DN_DONE_RT_STS BIT(3)
+#define FLASH_LED_ALL_RAMP_UP_DONE_RT_STS BIT(4)
+
+#define FLASH_LED_SAFETY_TIMER(id) (0x3E + id)
+#define FLASH_LED_SAFETY_TIMER_EN_MASK BIT(7)
+#define FLASH_LED_SAFETY_TIMER_EN BIT(7)
+#define SAFETY_TIMER_MAX_TIMEOUT_MS 1280
+#define SAFETY_TIMER_MIN_TIMEOUT_MS 10
+#define SAFETY_TIMER_STEP_SIZE 10
+
+/* Default timer duration is 200ms */
+#define SAFETY_TIMER_DEFAULT_DURATION 0x13
+
+#define FLASH_LED_ITARGET(id) (0x42 + id)
+#define FLASH_LED_ITARGET_MASK GENMASK(6, 0)
+
+#define FLASH_ENABLE_CONTROL 0x46
+#define FLASH_MODULE_ENABLE BIT(7)
+#define FLASH_MODULE_DISABLE 0x0
+
+#define FLASH_LED_IRESOLUTION 0x49
+#define FLASH_LED_IRESOLUTION_MASK(id) BIT(id)
+
+#define FLASH_LED_STROBE_CTRL(id) (0x4A + id)
+#define FLASH_LED_STROBE_CFG_MASK GENMASK(6, 4)
+#define FLASH_LED_STROBE_CFG_SHIFT 4
+#define FLASH_LED_HW_SW_STROBE_SEL BIT(2)
+#define FLASH_LED_STROBE_SEL_SHIFT 2
+
+#define FLASH_EN_LED_CTRL 0x4E
+#define FLASH_LED_ENABLE(id) BIT(id)
+#define FLASH_LED_DISABLE 0
+
+#define MAX_IRES_LEVELS 2
+#define IRES_12P5_MAX_CURR_MA 1500
+#define IRES_5P0_MAX_CURR_MA 640
+#define TORCH_MAX_CURR_MA 500
+#define IRES_12P5_UA 12500
+#define IRES_5P0_UA 5000
+#define IRES_DEFAULT_UA IRES_12P5_UA
+#define MAX_FLASH_CURRENT_MA 1000
+
+enum flash_led_type {
+ FLASH_LED_TYPE_UNKNOWN,
+ FLASH_LED_TYPE_FLASH,
+ FLASH_LED_TYPE_TORCH,
+};
+
+enum strobe_type {
+ SW_STROBE = 0,
+ HW_STROBE,
+};
+
+/* Configurations for each individual flash or torch device */
+struct flash_node_data {
+ struct qti_flash_led *led;
+ struct led_classdev_flash fdev;
+ u32 ires_ua;
+ u32 default_ires_ua;
+ u32 current_ma;
+ u32 max_current;
+ u8 duration;
+ u8 id;
+ u8 updated_ires_idx;
+ u8 ires_idx;
+ u8 strobe_config;
+ u8 strobe_sel;
+ enum flash_led_type type;
+ bool configured;
+ bool enabled;
+};
+
+struct flash_switch_data {
+ struct qti_flash_led *led;
+ struct led_classdev cdev;
+ u32 led_mask;
+ bool enabled;
+ bool symmetry_en;
+};
+
+/**
+ * struct qti_flash_led: Main Flash LED data structure
+ * @pdev : Pointer for platform device
+ * @regmap : Pointer for regmap structure
+ * @fnode : Pointer for array of child LED devices
+ * @snode : Pointer for array of child switch devices
+ * @lock : Spinlock to be used for critical section
+ * @num_fnodes : Number of flash/torch nodes defined in device tree
+ * @num_snodes : Number of switch nodes defined in device tree
+ * @hw_strobe_gpio : Pointer for array of GPIOs for HW strobing
+ * @all_ramp_up_done_irq : IRQ number for all ramp up interrupt
+ * @all_ramp_down_done_irq : IRQ number for all ramp down interrupt
+ * @led_fault_irq : IRQ number for LED fault interrupt
+ * @base : Base address of the flash LED module
+ * @max_channels : Maximum number of channels supported by flash module
+ * @ref_count : Reference count used to enable/disable flash LED
+ */
+struct qti_flash_led {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct flash_node_data *fnode;
+ struct flash_switch_data *snode;
+ spinlock_t lock;
+ u32 num_fnodes;
+ u32 num_snodes;
+ int *hw_strobe_gpio;
+ int all_ramp_up_done_irq;
+ int all_ramp_down_done_irq;
+ int led_fault_irq;
+ int max_current;
+ u16 base;
+ u8 max_channels;
+ u8 ref_count;
+};
+
+static const u32 flash_led_max_ires_values[MAX_IRES_LEVELS] = {
+ IRES_5P0_MAX_CURR_MA, IRES_12P5_MAX_CURR_MA
+};
+
+static int timeout_to_code(u32 timeout)
+{
+ if (timeout < SAFETY_TIMER_MIN_TIMEOUT_MS ||
+ timeout > SAFETY_TIMER_MAX_TIMEOUT_MS)
+ return -EINVAL;
+
+ return DIV_ROUND_CLOSEST(timeout, SAFETY_TIMER_STEP_SIZE) - 1;
+}
+
+static int get_ires_idx(u32 ires_ua)
+{
+ if (ires_ua == IRES_5P0_UA)
+ return 0;
+ else if (ires_ua == IRES_12P5_UA)
+ return 1;
+ else
+ return -EINVAL;
+}
+
+static int current_to_code(u32 target_curr_ma, u32 ires_ua)
+{
+ if (!ires_ua || !target_curr_ma ||
+ (target_curr_ma < DIV_ROUND_CLOSEST(ires_ua, 1000)))
+ return 0;
+
+ return DIV_ROUND_CLOSEST(target_curr_ma * 1000, ires_ua) - 1;
+}
+
+static int qti_flash_led_read(struct qti_flash_led *led, u16 offset,
+ u8 *data, u8 len)
+{
+ int rc;
+ u32 val;
+
+ rc = regmap_bulk_read(led->regmap, (led->base + offset), &val, len);
+ if (rc < 0) {
+ pr_err("Failed to read from 0x%04X rc = %d\n",
+ (led->base + offset), rc);
+ } else {
+ pr_debug("Read 0x%02X from addr 0x%04X\n", val,
+ (led->base + offset));
+ *data = (u8)val;
+ }
+
+ return rc;
+}
+
+static int qti_flash_led_write(struct qti_flash_led *led, u16 offset,
+ u8 *data, u8 len)
+{
+ int rc;
+
+ rc = regmap_bulk_write(led->regmap, (led->base + offset), data,
+ len);
+ if (rc < 0)
+ pr_err("Failed to write to 0x%04X rc = %d\n",
+ (led->base + offset), rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n", data,
+ (led->base + offset));
+
+ return rc;
+}
+
+static int qti_flash_led_masked_write(struct qti_flash_led *led,
+ u16 offset, u8 mask, u8 data)
+{
+ int rc;
+
+ rc = regmap_update_bits(led->regmap, (led->base + offset),
+ mask, data);
+ if (rc < 0)
+ pr_err("Failed to update bits from 0x%04X, rc = %d\n",
+ (led->base + offset), rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n", data,
+ (led->base + offset));
+
+ return rc;
+}
+
+static int qti_flash_led_module_control(struct qti_flash_led *led,
+ bool enable)
+{
+ int rc = 0;
+ u8 val;
+
+ if (enable) {
+ if (!led->ref_count) {
+ val = FLASH_MODULE_ENABLE;
+ rc = qti_flash_led_write(led, FLASH_ENABLE_CONTROL,
+ &val, 1);
+ if (rc < 0)
+ return rc;
+ }
+
+ led->ref_count++;
+ } else {
+ if (led->ref_count)
+ led->ref_count--;
+
+ if (!led->ref_count) {
+ val = FLASH_MODULE_DISABLE;
+ rc = qti_flash_led_write(led, FLASH_ENABLE_CONTROL,
+ &val, 1);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int qti_flash_led_strobe(struct qti_flash_led *led,
+ u8 mask, u8 value)
+{
+ int rc;
+ bool enable = mask & value;
+
+ spin_lock(&led->lock);
+
+ if (enable) {
+ rc = qti_flash_led_module_control(led, enable);
+ if (rc < 0)
+ goto error;
+
+ rc = qti_flash_led_masked_write(led, FLASH_EN_LED_CTRL,
+ mask, value);
+ if (rc < 0)
+ goto error;
+ } else {
+ rc = qti_flash_led_masked_write(led, FLASH_EN_LED_CTRL,
+ mask, value);
+ if (rc < 0)
+ goto error;
+
+ rc = qti_flash_led_module_control(led, enable);
+ if (rc < 0)
+ goto error;
+ }
+
+error:
+ spin_unlock(&led->lock);
+
+ return rc;
+}
+
+static int qti_flash_led_enable(struct flash_node_data *fnode)
+{
+ struct qti_flash_led *led = fnode->led;
+ int rc;
+ u8 val, addr_offset;
+
+ addr_offset = fnode->id;
+
+ spin_lock(&led->lock);
+ val = (fnode->updated_ires_idx ? 0 : 1) << fnode->id;
+ rc = qti_flash_led_masked_write(led, FLASH_LED_IRESOLUTION,
+ FLASH_LED_IRESOLUTION_MASK(fnode->id), val);
+ if (rc < 0)
+ goto out;
+
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_ITARGET(addr_offset), FLASH_LED_ITARGET_MASK,
+ current_to_code(fnode->current_ma, fnode->ires_ua));
+ if (rc < 0)
+ goto out;
+
+ /*
+ * For dynamic brightness control of Torch LEDs,
+ * just configure the target current.
+ */
+ if (fnode->type == FLASH_LED_TYPE_TORCH && fnode->enabled) {
+ spin_unlock(&led->lock);
+ return 0;
+ }
+
+ if (fnode->type == FLASH_LED_TYPE_FLASH && fnode->duration) {
+ val = fnode->duration | FLASH_LED_SAFETY_TIMER_EN;
+ rc = qti_flash_led_write(led,
+ FLASH_LED_SAFETY_TIMER(addr_offset), &val, 1);
+ if (rc < 0)
+ goto out;
+ } else {
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_SAFETY_TIMER(addr_offset),
+ FLASH_LED_SAFETY_TIMER_EN_MASK, 0);
+ if (rc < 0)
+ goto out;
+ }
+
+ fnode->configured = true;
+
+ if ((fnode->strobe_sel == HW_STROBE) &&
+ gpio_is_valid(led->hw_strobe_gpio[fnode->id]))
+ gpio_set_value(led->hw_strobe_gpio[fnode->id], 1);
+
+out:
+ spin_unlock(&led->lock);
+ return rc;
+}
+
+static int qti_flash_led_disable(struct flash_node_data *fnode)
+{
+ struct qti_flash_led *led = fnode->led;
+ int rc;
+
+ spin_lock(&led->lock);
+ if ((fnode->strobe_sel == HW_STROBE) &&
+ gpio_is_valid(led->hw_strobe_gpio[fnode->id]))
+ gpio_set_value(led->hw_strobe_gpio[fnode->id], 0);
+
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_ITARGET(fnode->id), FLASH_LED_ITARGET_MASK, 0);
+ if (rc < 0)
+ goto out;
+
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_SAFETY_TIMER(fnode->id),
+ FLASH_LED_SAFETY_TIMER_EN_MASK, 0);
+ if (rc < 0)
+ goto out;
+
+ fnode->current_ma = 0;
+
+out:
+ spin_unlock(&led->lock);
+ return rc;
+}
+
+static enum led_brightness qti_flash_led_brightness_get(
+ struct led_classdev *led_cdev)
+{
+ return led_cdev->brightness;
+}
+
+static void qti_flash_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct qti_flash_led *led = NULL;
+ struct flash_node_data *fnode = NULL;
+ struct led_classdev_flash *fdev = NULL;
+ int rc;
+ u32 current_ma = brightness;
+ u32 min_current_ma;
+
+ fdev = container_of(led_cdev, struct led_classdev_flash, led_cdev);
+ fnode = container_of(fdev, struct flash_node_data, fdev);
+ led = fnode->led;
+
+ if (brightness <= 0) {
+ rc = qti_flash_led_disable(fnode);
+ if (rc < 0)
+ pr_err("Failed to set brightness %d to LED\n",
+ brightness);
+ return;
+ }
+
+ min_current_ma = DIV_ROUND_CLOSEST(fnode->ires_ua, 1000);
+ if (current_ma < min_current_ma)
+ current_ma = min_current_ma;
+
+ fnode->updated_ires_idx = fnode->ires_idx;
+ fnode->ires_ua = fnode->default_ires_ua;
+
+ current_ma = min(current_ma, fnode->max_current);
+ if (current_ma > flash_led_max_ires_values[fnode->ires_idx]) {
+ if (current_ma > IRES_5P0_MAX_CURR_MA)
+ fnode->ires_ua = IRES_12P5_UA;
+ else
+ fnode->ires_ua = IRES_5P0_UA;
+ fnode->ires_idx = get_ires_idx(fnode->ires_ua);
+ }
+
+ fnode->current_ma = current_ma;
+ led_cdev->brightness = current_ma;
+
+ rc = qti_flash_led_enable(fnode);
+ if (rc < 0)
+ pr_err("Failed to set brightness %d to LED\n", brightness);
+}
+
+static int qti_flash_led_symmetry_config(
+ struct flash_switch_data *snode)
+{
+ struct qti_flash_led *led = snode->led;
+ int i, total_curr_ma = 0, symmetric_leds = 0, per_led_curr_ma;
+ enum flash_led_type type = FLASH_LED_TYPE_UNKNOWN;
+
+ /* Determine which LED type has triggered switch ON */
+ for (i = 0; i < led->num_fnodes; i++) {
+ if ((snode->led_mask & BIT(led->fnode[i].id)) &&
+ (led->fnode[i].configured))
+ type = led->fnode[i].type;
+ }
+
+ if (type == FLASH_LED_TYPE_UNKNOWN) {
+ pr_err("Error in symmetry configuration for switch device\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if ((snode->led_mask & BIT(led->fnode[i].id)) &&
+ (led->fnode[i].type == type)) {
+ total_curr_ma += led->fnode[i].current_ma;
+ symmetric_leds++;
+ }
+ }
+
+ if (symmetric_leds > 0 && total_curr_ma > 0) {
+ per_led_curr_ma = total_curr_ma / symmetric_leds;
+ } else {
+ pr_err("Incorrect configuration, symmetric_leds: %d total_curr_ma: %d\n",
+ symmetric_leds, total_curr_ma);
+ return -EINVAL;
+ }
+
+ if (per_led_curr_ma == 0) {
+ pr_warn("per_led_curr_ma cannot be 0\n");
+ return 0;
+ }
+
+ pr_debug("symmetric_leds: %d total: %d per_led_curr_ma: %d\n",
+ symmetric_leds, total_curr_ma, per_led_curr_ma);
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if (snode->led_mask & BIT(led->fnode[i].id) &&
+ led->fnode[i].type == type) {
+ qti_flash_led_brightness_set(
+ &led->fnode[i].fdev.led_cdev, per_led_curr_ma);
+ }
+ }
+
+ return 0;
+}
+
+static int qti_flash_switch_enable(struct flash_switch_data *snode)
+{
+ struct qti_flash_led *led = snode->led;
+ int rc = 0, i;
+ u8 led_en = 0;
+
+ /* If symmetry enabled switch, then turn ON all its LEDs */
+ if (snode->symmetry_en) {
+ rc = qti_flash_led_symmetry_config(snode);
+ if (rc < 0) {
+ pr_err("Failed to configure switch symmetrically, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ /*
+ * Do not turn ON flash/torch device if
+ * i. the device is not under this switch or
+ * ii. brightness is not configured for device under this switch
+ */
+ if (!(snode->led_mask & BIT(led->fnode[i].id)) ||
+ !led->fnode[i].configured)
+ continue;
+
+ led_en |= (1 << led->fnode[i].id);
+ }
+
+ return qti_flash_led_strobe(led, snode->led_mask, led_en);
+}
+
+static int qti_flash_switch_disable(struct flash_switch_data *snode)
+{
+ struct qti_flash_led *led = snode->led;
+ int rc = 0, i;
+ u8 led_dis = 0;
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ /*
+ * Do not turn OFF flash/torch device if
+ * i. the device is not under this switch or
+ * ii. brightness is not configured for device under this switch
+ */
+ if (!(snode->led_mask & BIT(led->fnode[i].id)) ||
+ !led->fnode[i].configured)
+ continue;
+
+ rc = qti_flash_led_disable(&led->fnode[i]);
+ if (rc < 0) {
+ pr_err("Failed to disable LED%d\n",
+ &led->fnode[i].id);
+ break;
+ }
+
+ led_dis |= (1 << led->fnode[i].id);
+ led->fnode[i].configured = false;
+ }
+
+ return qti_flash_led_strobe(led, led_dis, ~led_dis);
+}
+
+static void qti_flash_led_switch_brightness_set(
+ struct led_classdev *led_cdev, enum led_brightness value)
+{
+ struct flash_switch_data *snode = NULL;
+ int rc = 0;
+ bool state = value > 0;
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+
+ if (snode->enabled == state) {
+ pr_debug("Switch is already %s!\n",
+ state ? "enabled" : "disabled");
+ return;
+ }
+
+ if (state)
+ rc = qti_flash_switch_enable(snode);
+ else
+ rc = qti_flash_switch_disable(snode);
+
+ if (rc < 0)
+ pr_err("Failed to %s switch, rc=%d\n",
+ state ? "enable" : "disable", rc);
+ else
+ snode->enabled = state;
+}
+
+static ssize_t qti_flash_led_max_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flash_switch_data *snode;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current);
+}
+
+int qti_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current)
+{
+ struct led_classdev *led_cdev;
+ struct flash_switch_data *snode;
+
+ if (!trig) {
+ pr_err("Invalid led_trigger\n");
+ return -EINVAL;
+ }
+
+ led_cdev = trigger_to_lcdev(trig);
+ if (!led_cdev) {
+ pr_err("Invalid led_cdev in trigger %s\n", trig->name);
+ return -ENODEV;
+ }
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+
+ if (options & QUERY_MAX_AVAIL_CURRENT) {
+ *max_current = snode->led->max_current;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(qti_flash_led_prepare);
+
+static struct device_attribute qti_flash_led_attrs[] = {
+ __ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL),
+};
+
+static int qti_flash_brightness_set_blocking(
+ struct led_classdev *led_cdev, enum led_brightness value)
+{
+ qti_flash_led_brightness_set(led_cdev, value);
+
+ return 0;
+}
+
+static int qti_flash_brightness_set(
+ struct led_classdev_flash *fdev, u32 brightness)
+{
+ qti_flash_led_brightness_set(&fdev->led_cdev, brightness);
+
+ return 0;
+}
+
+static int qti_flash_brightness_get(
+ struct led_classdev_flash *fdev, u32 *brightness)
+{
+ *brightness = qti_flash_led_brightness_get(&fdev->led_cdev);
+
+ return 0;
+}
+
+static int qti_flash_strobe_set(struct led_classdev_flash *fdev,
+ bool state)
+{
+ struct flash_node_data *fnode;
+ int rc;
+ u8 mask, value;
+
+ fnode = container_of(fdev, struct flash_node_data, fdev);
+
+ if (fnode->enabled == state)
+ return 0;
+
+ mask = FLASH_LED_ENABLE(fnode->id);
+ value = state ? FLASH_LED_ENABLE(fnode->id) : 0;
+
+ rc = qti_flash_led_strobe(fnode->led, mask, value);
+ if (!rc) {
+ fnode->enabled = state;
+ if (!state)
+ fnode->configured = false;
+ }
+
+ return rc;
+}
+
+static int qti_flash_strobe_get(struct led_classdev_flash *fdev,
+ bool *state)
+{
+ struct flash_node_data *fnode = container_of(fdev,
+ struct flash_node_data, fdev);
+
+ *state = fnode->enabled;
+
+ return 0;
+}
+
+static int qti_flash_timeout_set(struct led_classdev_flash *fdev,
+ u32 timeout)
+{
+ struct qti_flash_led *led;
+ struct flash_node_data *fnode;
+ int rc = 0;
+ u8 val;
+
+ if (timeout < SAFETY_TIMER_MIN_TIMEOUT_MS ||
+ timeout > SAFETY_TIMER_MAX_TIMEOUT_MS)
+ return -EINVAL;
+
+ fnode = container_of(fdev, struct flash_node_data, fdev);
+ led = fnode->led;
+
+ rc = timeout_to_code(timeout);
+ if (rc < 0)
+ return rc;
+ fnode->duration = rc;
+ val = fnode->duration | FLASH_LED_SAFETY_TIMER_EN;
+ rc = qti_flash_led_write(led,
+ FLASH_LED_SAFETY_TIMER(fnode->id), &val, 1);
+
+ return rc;
+}
+
+static const struct led_flash_ops flash_ops = {
+ .flash_brightness_set = qti_flash_brightness_set,
+ .flash_brightness_get = qti_flash_brightness_get,
+ .strobe_set = qti_flash_strobe_set,
+ .strobe_get = qti_flash_strobe_get,
+ .timeout_set = qti_flash_timeout_set,
+};
+
+static int qti_flash_led_setup(struct qti_flash_led *led)
+{
+ int rc = 0, i, addr_offset;
+ u8 val, mask;
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ addr_offset = led->fnode[i].id;
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_SAFETY_TIMER(addr_offset),
+ FLASH_LED_SAFETY_TIMER_EN_MASK, 0);
+ if (rc < 0)
+ return rc;
+
+ val = (led->fnode[i].strobe_config <<
+ FLASH_LED_STROBE_CFG_SHIFT) |
+ (led->fnode[i].strobe_sel <<
+ FLASH_LED_STROBE_SEL_SHIFT);
+ mask = FLASH_LED_STROBE_CFG_MASK | FLASH_LED_HW_SW_STROBE_SEL;
+ rc = qti_flash_led_masked_write(led,
+ FLASH_LED_STROBE_CTRL(addr_offset), mask, val);
+ if (rc < 0)
+ return rc;
+ }
+
+ led->max_current = MAX_FLASH_CURRENT_MA;
+
+ return rc;
+}
+
+static irqreturn_t qti_flash_led_irq_handler(int irq, void *_led)
+{
+ struct qti_flash_led *led = _led;
+ int rc;
+ u8 irq_status, led_status1, led_status2;
+
+ rc = qti_flash_led_read(led, FLASH_INT_RT_STS, &irq_status, 1);
+ if (rc < 0)
+ goto exit;
+
+ if (irq == led->led_fault_irq) {
+ if (irq_status & FLASH_LED_FAULT_RT_STS)
+ pr_debug("Led fault open/short/vreg_not_ready detected\n");
+ } else if (irq == led->all_ramp_down_done_irq) {
+ if (irq_status & FLASH_LED_ALL_RAMP_DN_DONE_RT_STS)
+ pr_debug("All LED channels ramp down done detected\n");
+ } else if (irq == led->all_ramp_up_done_irq) {
+ if (irq_status & FLASH_LED_ALL_RAMP_UP_DONE_RT_STS)
+ pr_debug("All LED channels ramp up done detected\n");
+ }
+
+ rc = qti_flash_led_read(led, FLASH_LED_STATUS1, &led_status1, 1);
+ if (rc < 0)
+ goto exit;
+
+ if (led_status1)
+ pr_debug("LED channel open/short fault detected\n");
+
+ rc = qti_flash_led_read(led, FLASH_LED_STATUS2, &led_status2, 1);
+ if (rc < 0)
+ goto exit;
+
+ if (led_status2 & FLASH_LED_VPH_PWR_LOW)
+ pr_debug("LED vph_droop fault detected!\n");
+
+ pr_debug("LED irq handled, irq_status=%02x led_status1=%02x led_status2=%02x\n",
+ irq_status, led_status1, led_status2);
+
+exit:
+ return IRQ_HANDLED;
+}
+
+static int qti_flash_led_register_interrupts(struct qti_flash_led *led)
+{
+ int rc;
+
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->all_ramp_up_done_irq, NULL, qti_flash_led_irq_handler,
+ IRQF_ONESHOT, "flash_all_ramp_up", led);
+ if (rc < 0) {
+ pr_err("Failed to request all_ramp_up_done(%d) IRQ(err:%d)\n",
+ led->all_ramp_up_done_irq, rc);
+ return rc;
+ }
+
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->all_ramp_down_done_irq, NULL, qti_flash_led_irq_handler,
+ IRQF_ONESHOT, "flash_all_ramp_down", led);
+ if (rc < 0) {
+ pr_err("Failed to request all_ramp_down_done(%d) IRQ(err:%d)\n",
+ led->all_ramp_down_done_irq,
+ rc);
+ return rc;
+ }
+
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->led_fault_irq, NULL, qti_flash_led_irq_handler,
+ IRQF_ONESHOT, "flash_fault", led);
+ if (rc < 0) {
+ pr_err("Failed to request led_fault(%d) IRQ(err:%d)\n",
+ led->led_fault_irq, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int register_switch_device(struct qti_flash_led *led,
+ struct flash_switch_data *snode, struct device_node *node)
+{
+ int rc, i;
+
+ rc = of_property_read_string(node, "qcom,led-name",
+ &snode->cdev.name);
+ if (rc < 0) {
+ pr_err("Failed to read switch node name, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_string(node, "qcom,default-led-trigger",
+ &snode->cdev.default_trigger);
+ if (rc < 0) {
+ pr_err("Failed to read trigger name, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,led-mask", &snode->led_mask);
+ if (rc < 0) {
+ pr_err("Failed to read led mask rc=%d\n", rc);
+ return rc;
+ }
+ if ((snode->led_mask > ((1 << led->max_channels) - 1))) {
+ pr_err("Error, Invalid value for led-mask mask=0x%x\n",
+ snode->led_mask);
+ return -EINVAL;
+ }
+
+ snode->symmetry_en = of_property_read_bool(node, "qcom,symmetry-en");
+
+ snode->led = led;
+ snode->cdev.brightness_set = qti_flash_led_switch_brightness_set;
+ snode->cdev.brightness_get = qti_flash_led_brightness_get;
+
+ rc = devm_led_classdev_register(&led->pdev->dev, &snode->cdev);
+ if (rc < 0) {
+ pr_err("Failed to register led switch device:%s\n",
+ snode->cdev.name);
+ return rc;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(qti_flash_led_attrs); i++) {
+ rc = sysfs_create_file(&snode->cdev.dev->kobj,
+ &qti_flash_led_attrs[i].attr);
+ if (rc < 0) {
+ pr_err("Failed to create sysfs attrs, rc=%d\n", rc);
+ goto sysfs_fail;
+ }
+ }
+
+ return 0;
+
+sysfs_fail:
+ while (i >= 0)
+ sysfs_remove_file(&snode->cdev.dev->kobj,
+ &qti_flash_led_attrs[i--].attr);
+ return rc;
+}
+
+static int register_flash_device(struct qti_flash_led *led,
+ struct flash_node_data *fnode, struct device_node *node)
+{
+ struct led_flash_setting *setting;
+ const char *temp_string;
+ int rc;
+ u32 val, default_curr_ma;
+
+ rc = of_property_read_string(node, "qcom,led-name",
+ &fnode->fdev.led_cdev.name);
+ if (rc < 0) {
+ pr_err("Failed to read flash LED names\n");
+ return rc;
+ }
+
+ rc = of_property_read_string(node, "label", &temp_string);
+ if (rc < 0) {
+ pr_err("Failed to read flash LED label\n");
+ return rc;
+ }
+
+ if (!strcmp(temp_string, "flash")) {
+ fnode->type = FLASH_LED_TYPE_FLASH;
+ } else if (!strcmp(temp_string, "torch")) {
+ fnode->type = FLASH_LED_TYPE_TORCH;
+ } else {
+ pr_err("Incorrect flash LED type %s\n", temp_string);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,id", &val);
+ if (rc < 0) {
+ pr_err("Failed to read flash LED ID\n");
+ return rc;
+ }
+ fnode->id = (u8)val;
+
+ rc = of_property_read_string(node, "qcom,default-led-trigger",
+ &fnode->fdev.led_cdev.default_trigger);
+ if (rc < 0) {
+ pr_err("Failed to read trigger name\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,ires-ua", &val);
+ if (rc < 0) {
+ pr_err("Failed to read current resolution, rc=%d\n", rc);
+ return rc;
+ } else if (!rc) {
+ rc = get_ires_idx(val);
+ if (rc < 0) {
+ pr_err("Incorrect ires-ua configured, ires-ua=%u\n",
+ val);
+ return rc;
+ }
+ fnode->default_ires_ua = fnode->ires_ua = val;
+ fnode->updated_ires_idx = fnode->ires_idx = rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,max-current-ma", &val);
+ if (rc < 0) {
+ pr_err("Failed to read max current, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (fnode->type == FLASH_LED_TYPE_FLASH &&
+ (val > IRES_12P5_MAX_CURR_MA)) {
+ pr_err("Incorrect max-current-ma for flash %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ if (fnode->type == FLASH_LED_TYPE_TORCH &&
+ (val > TORCH_MAX_CURR_MA)) {
+ pr_err("Incorrect max-current-ma for torch %u\n",
+ val);
+ return -EINVAL;
+ }
+
+ fnode->max_current = val;
+ fnode->fdev.led_cdev.max_brightness = val;
+
+ fnode->duration = SAFETY_TIMER_DEFAULT_DURATION;
+ rc = of_property_read_u32(node, "qcom,duration-ms", &val);
+ if (!rc) {
+ rc = timeout_to_code(val);
+ if (rc < 0) {
+ pr_err("Incorrect timeout configured %u\n", val);
+ return rc;
+ }
+ fnode->duration = rc;
+ }
+
+ fnode->strobe_sel = SW_STROBE;
+ rc = of_property_read_u32(node, "qcom,strobe-sel", &val);
+ if (!rc)
+ fnode->strobe_sel = (u8)val;
+
+ if (fnode->strobe_sel == HW_STROBE) {
+ rc = of_property_read_u32(node, "qcom,strobe-config", &val);
+ if (!rc) {
+ fnode->strobe_config = (u8)val;
+ } else {
+ pr_err("Failed to read qcom,strobe-config property\n");
+ return rc;
+ }
+
+ }
+
+ fnode->led = led;
+ fnode->fdev.led_cdev.brightness_set = qti_flash_led_brightness_set;
+ fnode->fdev.led_cdev.brightness_get = qti_flash_led_brightness_get;
+ fnode->enabled = false;
+ fnode->configured = false;
+ fnode->fdev.ops = &flash_ops;
+
+ if (fnode->type == FLASH_LED_TYPE_FLASH) {
+ fnode->fdev.led_cdev.flags = LED_DEV_CAP_FLASH;
+ fnode->fdev.led_cdev.brightness_set_blocking =
+ qti_flash_brightness_set_blocking;
+ }
+
+ default_curr_ma = DIV_ROUND_CLOSEST(fnode->ires_ua, 1000);
+ setting = &fnode->fdev.brightness;
+ setting->min = 0;
+ setting->max = fnode->max_current;
+ setting->step = 1;
+ setting->val = default_curr_ma;
+
+ setting = &fnode->fdev.timeout;
+ setting->min = SAFETY_TIMER_MIN_TIMEOUT_MS;
+ setting->max = SAFETY_TIMER_MAX_TIMEOUT_MS;
+ setting->step = 1;
+ setting->val = SAFETY_TIMER_DEFAULT_DURATION;
+
+ rc = led_classdev_flash_register(&led->pdev->dev, &fnode->fdev);
+ if (rc < 0) {
+ pr_err("Failed to register flash led device:%s\n",
+ fnode->fdev.led_cdev.name);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qti_flash_led_register_device(struct qti_flash_led *led,
+ struct device_node *node)
+{
+ struct device_node *temp;
+ char buffer[20];
+ const char *label;
+ int rc, i = 0, j = 0;
+ u32 val;
+
+ rc = of_property_read_u32(node, "reg", &val);
+ if (rc < 0) {
+ pr_err("Failed to find reg in node %s, rc = %d\n",
+ node->full_name, rc);
+ return rc;
+ }
+ led->base = val;
+
+ led->hw_strobe_gpio = devm_kcalloc(&led->pdev->dev,
+ led->max_channels, sizeof(u32), GFP_KERNEL);
+ if (!led->hw_strobe_gpio)
+ return -ENOMEM;
+
+ for (i = 0; i < led->max_channels; i++) {
+
+ led->hw_strobe_gpio[i] = -EINVAL;
+
+ rc = of_get_named_gpio(node, "hw-strobe-gpios", i);
+ if (rc < 0) {
+ pr_debug("Failed to get hw strobe gpio, rc = %d\n", rc);
+ continue;
+ }
+
+ if (!gpio_is_valid(rc)) {
+ pr_err("Error, Invalid gpio specified\n");
+ return -EINVAL;
+ }
+ led->hw_strobe_gpio[i] = rc;
+
+ scnprintf(buffer, sizeof(buffer), "hw_strobe_gpio%d", i);
+ rc = devm_gpio_request_one(&led->pdev->dev,
+ led->hw_strobe_gpio[i], GPIOF_DIR_OUT,
+ buffer);
+ if (rc < 0) {
+ pr_err("Failed to acquire gpio rc = %d\n", rc);
+ return rc;
+ }
+
+ gpio_direction_output(led->hw_strobe_gpio[i], 0);
+
+ }
+
+ led->all_ramp_up_done_irq = of_irq_get_byname(node,
+ "all-ramp-up-done-irq");
+ if (led->all_ramp_up_done_irq < 0)
+ pr_debug("all-ramp-up-done-irq not used\n");
+
+ led->all_ramp_down_done_irq = of_irq_get_byname(node,
+ "all-ramp-down-done-irq");
+ if (led->all_ramp_down_done_irq < 0)
+ pr_debug("all-ramp-down-done-irq not used\n");
+
+ led->led_fault_irq = of_irq_get_byname(node,
+ "led-fault-irq");
+ if (led->led_fault_irq < 0)
+ pr_debug("led-fault-irq not used\n");
+
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "label", &label);
+ if (rc < 0) {
+ pr_err("Failed to parse label, rc=%d\n", rc);
+ of_node_put(temp);
+ return rc;
+ }
+
+ if (!strcmp("flash", label) || !strcmp("torch", label)) {
+ led->num_fnodes++;
+ } else if (!strcmp("switch", label)) {
+ led->num_snodes++;
+ } else {
+ pr_err("Invalid label for led node label=%s\n",
+ label);
+ of_node_put(temp);
+ return -EINVAL;
+ }
+ }
+
+ if (!led->num_fnodes) {
+ pr_err("No flash/torch devices defined\n");
+ return -ECHILD;
+ }
+
+ if (!led->num_snodes) {
+ pr_err("No switch devices defined\n");
+ return -ECHILD;
+ }
+
+ led->fnode = devm_kcalloc(&led->pdev->dev, led->num_fnodes,
+ sizeof(*led->fnode), GFP_KERNEL);
+ led->snode = devm_kcalloc(&led->pdev->dev, led->num_snodes,
+ sizeof(*led->snode), GFP_KERNEL);
+ if ((!led->fnode) || (!led->snode))
+ return -ENOMEM;
+
+ i = 0;
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "label", &label);
+ if (rc < 0) {
+ pr_err("Failed to parse label, rc=%d\n", rc);
+ of_node_put(temp);
+ return rc;
+ }
+
+ if (!strcmp("flash", label) || !strcmp("torch", label)) {
+ rc = register_flash_device(led, &led->fnode[i], temp);
+ if (rc < 0) {
+ pr_err("Failed to register flash device %s rc=%d\n",
+ led->fnode[i].fdev.led_cdev.name, rc);
+ of_node_put(temp);
+ goto unreg_led;
+ }
+ led->fnode[i++].fdev.led_cdev.dev->of_node = temp;
+ } else {
+ rc = register_switch_device(led, &led->snode[j], temp);
+ if (rc < 0) {
+ pr_err("Failed to register switch device %s rc=%d\n",
+ led->snode[j].cdev.name, rc);
+ i--;
+ of_node_put(temp);
+ goto unreg_led;
+ }
+ led->snode[j++].cdev.dev->of_node = temp;
+ }
+ }
+
+ return 0;
+
+unreg_led:
+ while (i >= 0)
+ led_classdev_flash_unregister(&led->fnode[i--].fdev);
+
+ return rc;
+}
+
+static int qti_flash_led_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct qti_flash_led *led;
+ int rc;
+
+ led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!led->regmap) {
+ pr_err("Failed to get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ led->max_channels = (u8)of_device_get_match_data(&pdev->dev);
+ if (!led->max_channels) {
+ pr_err("Failed to get max supported led channels\n");
+ return -EINVAL;
+ }
+
+ led->pdev = pdev;
+ spin_lock_init(&led->lock);
+
+ rc = qti_flash_led_register_device(led, node);
+ if (rc < 0) {
+ pr_err("Failed to parse and register LED devices rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qti_flash_led_setup(led);
+ if (rc < 0) {
+ pr_err("Failed to initialize flash LED, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qti_flash_led_register_interrupts(led);
+ if (rc < 0) {
+ pr_err("Failed to register LED interrupts rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_flash_register_led_prepare(&pdev->dev, qti_flash_led_prepare);
+ if (rc < 0) {
+ pr_err("Failed to register flash_led_prepare, rc=%d\n", rc);
+ return rc;
+ }
+
+ dev_set_drvdata(&pdev->dev, led);
+
+ return 0;
+}
+
+static int qti_flash_led_remove(struct platform_device *pdev)
+{
+ struct qti_flash_led *led = dev_get_drvdata(&pdev->dev);
+ int i, j;
+
+ for (i = 0; (i < led->num_snodes); i++) {
+ for (j = 0; j < ARRAY_SIZE(qti_flash_led_attrs); j++)
+ sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+ &qti_flash_led_attrs[j].attr);
+
+ led_classdev_unregister(&led->snode[i].cdev);
+ }
+
+ for (i = 0; (i < led->num_fnodes); i++)
+ led_classdev_flash_unregister(&led->fnode[i].fdev);
+
+ return 0;
+}
+
+const static struct of_device_id qti_flash_led_match_table[] = {
+ { .compatible = "qcom,pm8350c-flash-led", .data = (void *)4, },
+ { .compatible = "qcom,pm2250-flash-led", .data = (void *)1, },
+ { },
+};
+
+static struct platform_driver qti_flash_led_driver = {
+ .driver = {
+ .name = "leds-qti-flash",
+ .of_match_table = qti_flash_led_match_table,
+ },
+ .probe = qti_flash_led_probe,
+ .remove = qti_flash_led_remove,
+};
+
+module_platform_driver(qti_flash_led_driver);
+
+MODULE_DESCRIPTION("QTI Flash LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 136f86a..d5e774d 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -302,10 +302,12 @@ static int netdev_trig_notify(struct notifier_block *nb,
container_of(nb, struct led_netdev_data, notifier);
if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
- && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
+ && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
+ && evt != NETDEV_CHANGENAME)
return NOTIFY_DONE;
if (!(dev == trigger_data->net_dev ||
+ (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) ||
(evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
return NOTIFY_DONE;
@@ -315,6 +317,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
switch (evt) {
+ case NETDEV_CHANGENAME:
case NETDEV_REGISTER:
if (trigger_data->net_dev)
dev_put(trigger_data->net_dev);
diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c
index 80710c6..8dce31d 100644
--- a/drivers/lightnvm/pblk-core.c
+++ b/drivers/lightnvm/pblk-core.c
@@ -893,10 +893,8 @@ static void pblk_setup_e_rq(struct pblk *pblk, struct nvm_rq *rqd,
static int pblk_blk_erase_sync(struct pblk *pblk, struct ppa_addr ppa)
{
- struct nvm_rq rqd;
- int ret = 0;
-
- memset(&rqd, 0, sizeof(struct nvm_rq));
+ struct nvm_rq rqd = {NULL};
+ int ret;
pblk_setup_e_rq(pblk, &rqd, ppa);
@@ -904,19 +902,6 @@ static int pblk_blk_erase_sync(struct pblk *pblk, struct ppa_addr ppa)
* with writes. Thus, there is no need to take the LUN semaphore.
*/
ret = pblk_submit_io_sync(pblk, &rqd);
- if (ret) {
- struct nvm_tgt_dev *dev = pblk->dev;
- struct nvm_geo *geo = &dev->geo;
-
- pblk_err(pblk, "could not sync erase line:%d,blk:%d\n",
- pblk_ppa_to_line(ppa),
- pblk_ppa_to_pos(geo, ppa));
-
- rqd.error = ret;
- goto out;
- }
-
-out:
rqd.private = pblk;
__pblk_end_io_erase(pblk, &rqd);
@@ -1788,6 +1773,17 @@ void pblk_line_close_meta(struct pblk *pblk, struct pblk_line *line)
wa->pad = cpu_to_le64(atomic64_read(&pblk->pad_wa));
wa->gc = cpu_to_le64(atomic64_read(&pblk->gc_wa));
+ if (le32_to_cpu(emeta_buf->header.identifier) != PBLK_MAGIC) {
+ emeta_buf->header.identifier = cpu_to_le32(PBLK_MAGIC);
+ memcpy(emeta_buf->header.uuid, pblk->instance_uuid, 16);
+ emeta_buf->header.id = cpu_to_le32(line->id);
+ emeta_buf->header.type = cpu_to_le16(line->type);
+ emeta_buf->header.version_major = EMETA_VERSION_MAJOR;
+ emeta_buf->header.version_minor = EMETA_VERSION_MINOR;
+ emeta_buf->header.crc = cpu_to_le32(
+ pblk_calc_meta_header_crc(pblk, &emeta_buf->header));
+ }
+
emeta_buf->nr_valid_lbas = cpu_to_le64(line->nr_valid_lbas);
emeta_buf->crc = cpu_to_le32(pblk_calc_emeta_crc(pblk, emeta_buf));
@@ -1805,8 +1801,6 @@ void pblk_line_close_meta(struct pblk *pblk, struct pblk_line *line)
spin_unlock(&l_mg->close_lock);
pblk_line_should_sync_meta(pblk);
-
-
}
static void pblk_save_lba_list(struct pblk *pblk, struct pblk_line *line)
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c
index 537e98f..88b6327 100644
--- a/drivers/lightnvm/pblk-init.c
+++ b/drivers/lightnvm/pblk-init.c
@@ -181,7 +181,8 @@ static int pblk_rwb_init(struct pblk *pblk)
unsigned int power_size, power_seg_sz;
int pgs_in_buffer;
- pgs_in_buffer = max(geo->mw_cunits, geo->ws_opt) * geo->all_luns;
+ pgs_in_buffer = (max(geo->mw_cunits, geo->ws_opt) + geo->ws_opt)
+ * geo->all_luns;
if (write_buffer_size && (write_buffer_size > pgs_in_buffer))
buffer_size = write_buffer_size;
@@ -371,9 +372,11 @@ static int pblk_core_init(struct pblk *pblk)
atomic64_set(&pblk->nr_flush, 0);
pblk->nr_flush_rst = 0;
- pblk->min_write_pgs = geo->ws_opt * (geo->csecs / PAGE_SIZE);
+ pblk->min_write_pgs = geo->ws_opt;
max_write_ppas = pblk->min_write_pgs * geo->all_luns;
pblk->max_write_pgs = min_t(int, max_write_ppas, NVM_MAX_VLBA);
+ pblk->max_write_pgs = min_t(int, pblk->max_write_pgs,
+ queue_max_hw_sectors(dev->q) / (geo->csecs >> SECTOR_SHIFT));
pblk_set_sec_per_write(pblk, pblk->min_write_pgs);
if (pblk->max_write_pgs > PBLK_MAX_REQ_ADDRS) {
@@ -1083,7 +1086,8 @@ static int pblk_lines_init(struct pblk *pblk)
if (!nr_free_chks) {
pblk_err(pblk, "too many bad blocks prevent for sane instance\n");
- return -EINTR;
+ ret = -EINTR;
+ goto fail_free_lines;
}
pblk_set_provision(pblk, nr_free_chks);
diff --git a/drivers/lightnvm/pblk-sysfs.c b/drivers/lightnvm/pblk-sysfs.c
index 8d2ed51..bdc86ee 100644
--- a/drivers/lightnvm/pblk-sysfs.c
+++ b/drivers/lightnvm/pblk-sysfs.c
@@ -343,7 +343,6 @@ static ssize_t pblk_get_write_amp(u64 user, u64 gc, u64 pad,
{
int sz;
-
sz = snprintf(page, PAGE_SIZE,
"user:%lld gc:%lld pad:%lld WA:",
user, gc, pad);
@@ -355,7 +354,7 @@ static ssize_t pblk_get_write_amp(u64 user, u64 gc, u64 pad,
u32 wa_frac;
wa_int = (user + gc + pad) * 100000;
- wa_int = div_u64(wa_int, user);
+ wa_int = div64_u64(wa_int, user);
wa_int = div_u64_rem(wa_int, 100000, &wa_frac);
sz += snprintf(page + sz, PAGE_SIZE - sz, "%llu.%05u\n",
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index da7f4fc..a0f61eb 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -22,14 +22,6 @@
#define VERSION "1.0"
-#define DEBUG
-
-#ifdef DEBUG
-#define DBG(args...) printk(args)
-#else
-#define DBG(args...) do { } while(0)
-#endif
-
/* If the cache is older than 800ms we'll refetch it */
#define MAX_AGE msecs_to_jiffies(800)
@@ -106,13 +98,10 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
buf[i+2] = data[3];
buf[i+3] = data[2];
}
-#ifdef DEBUG
- DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id);
- for (i = 0; i < len; ++i)
- DBG(" %x", buf[i]);
- DBG("\n");
-#endif
+ printk(KERN_DEBUG "sat %d partition %x:", sat_id, id);
+ print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, len, false);
if (size)
*size = len;
return (struct smu_sdbp_header *) buf;
@@ -132,13 +121,13 @@ static int wf_sat_read_cache(struct wf_sat *sat)
if (err < 0)
return err;
sat->last_read = jiffies;
+
#ifdef LOTSA_DEBUG
{
int i;
- DBG(KERN_DEBUG "wf_sat_get: data is");
- for (i = 0; i < 16; ++i)
- DBG(" %.2x", sat->cache[i]);
- DBG("\n");
+ printk(KERN_DEBUG "wf_sat_get: data is");
+ print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET,
+ 16, 1, sat->cache, 16, false);
}
#endif
return 0;
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 363d35d..2f47023 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -214,8 +214,10 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
struct imx_mu_con_priv *cp = chan->con_priv;
- if (cp->type == IMX_MU_TYPE_TXDB)
+ if (cp->type == IMX_MU_TYPE_TXDB) {
tasklet_kill(&cp->txdb_tasklet);
+ return;
+ }
imx_mu_xcr_rmw(priv, 0,
IMX_MU_xCR_TIEn(cp->idx) | IMX_MU_xCR_RIEn(cp->idx));
diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c
index 58bfafc..129b365 100644
--- a/drivers/mailbox/mailbox-test.c
+++ b/drivers/mailbox/mailbox-test.c
@@ -363,22 +363,24 @@ static int mbox_test_probe(struct platform_device *pdev)
/* It's okay for MMIO to be NULL */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- size = resource_size(res);
tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
- if (PTR_ERR(tdev->tx_mmio) == -EBUSY)
+ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
/* if reserved area in SRAM, try just ioremap */
+ size = resource_size(res);
tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
- else if (IS_ERR(tdev->tx_mmio))
+ } else if (IS_ERR(tdev->tx_mmio)) {
tdev->tx_mmio = NULL;
+ }
/* If specified, second reg entry is Rx MMIO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- size = resource_size(res);
tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
- if (PTR_ERR(tdev->rx_mmio) == -EBUSY)
+ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
+ size = resource_size(res);
tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
- else if (IS_ERR(tdev->rx_mmio))
+ } else if (IS_ERR(tdev->rx_mmio)) {
tdev->rx_mmio = tdev->tx_mmio;
+ }
tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c
index ca1f993..e313222 100644
--- a/drivers/mailbox/stm32-ipcc.c
+++ b/drivers/mailbox/stm32-ipcc.c
@@ -50,6 +50,7 @@ struct stm32_ipcc {
void __iomem *reg_base;
void __iomem *reg_proc;
struct clk *clk;
+ spinlock_t lock; /* protect access to IPCC registers */
int irqs[IPCC_IRQ_NUM];
int wkp;
u32 proc_id;
@@ -58,14 +59,24 @@ struct stm32_ipcc {
u32 xmr;
};
-static inline void stm32_ipcc_set_bits(void __iomem *reg, u32 mask)
+static inline void stm32_ipcc_set_bits(spinlock_t *lock, void __iomem *reg,
+ u32 mask)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
writel_relaxed(readl_relaxed(reg) | mask, reg);
+ spin_unlock_irqrestore(lock, flags);
}
-static inline void stm32_ipcc_clr_bits(void __iomem *reg, u32 mask)
+static inline void stm32_ipcc_clr_bits(spinlock_t *lock, void __iomem *reg,
+ u32 mask)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
writel_relaxed(readl_relaxed(reg) & ~mask, reg);
+ spin_unlock_irqrestore(lock, flags);
}
static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)
@@ -92,7 +103,7 @@ static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)
mbox_chan_received_data(&ipcc->controller.chans[chan], NULL);
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
RX_BIT_CHAN(chan));
ret = IRQ_HANDLED;
@@ -121,7 +132,7 @@ static irqreturn_t stm32_ipcc_tx_irq(int irq, void *data)
dev_dbg(dev, "%s: chan:%d tx\n", __func__, chan);
/* mask 'tx channel free' interrupt */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
TX_BIT_CHAN(chan));
mbox_chan_txdone(&ipcc->controller.chans[chan], 0);
@@ -141,10 +152,12 @@ static int stm32_ipcc_send_data(struct mbox_chan *link, void *data)
dev_dbg(ipcc->controller.dev, "%s: chan:%d\n", __func__, chan);
/* set channel n occupied */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR, TX_BIT_CHAN(chan));
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
+ TX_BIT_CHAN(chan));
/* unmask 'tx channel free' interrupt */
- stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, TX_BIT_CHAN(chan));
+ stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
+ TX_BIT_CHAN(chan));
return 0;
}
@@ -163,7 +176,8 @@ static int stm32_ipcc_startup(struct mbox_chan *link)
}
/* unmask 'rx channel occupied' interrupt */
- stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, RX_BIT_CHAN(chan));
+ stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
+ RX_BIT_CHAN(chan));
return 0;
}
@@ -175,7 +189,7 @@ static void stm32_ipcc_shutdown(struct mbox_chan *link)
controller);
/* mask rx/tx interrupt */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
RX_BIT_CHAN(chan) | TX_BIT_CHAN(chan));
clk_disable_unprepare(ipcc->clk);
@@ -208,6 +222,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
if (!ipcc)
return -ENOMEM;
+ spin_lock_init(&ipcc->lock);
+
/* proc_id */
if (of_property_read_u32(np, "st,proc-id", &ipcc->proc_id)) {
dev_err(dev, "Missing st,proc-id\n");
@@ -259,9 +275,10 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
}
/* mask and enable rx/tx irq */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
RX_BIT_MASK | TX_BIT_MASK);
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XCR, XCR_RXOIE | XCR_TXOIE);
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XCR,
+ XCR_RXOIE | XCR_TXOIE);
/* wakeup */
if (of_property_read_bool(np, "wakeup-source")) {
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
index 9c3beb1..46794ca 100644
--- a/drivers/md/bcache/alloc.c
+++ b/drivers/md/bcache/alloc.c
@@ -377,7 +377,10 @@ static int bch_allocator_thread(void *arg)
if (!fifo_full(&ca->free_inc))
goto retry_invalidate;
- bch_prio_write(ca);
+ if (bch_prio_write(ca, false) < 0) {
+ ca->invalidate_needs_gc = 1;
+ wake_up_gc(ca->set);
+ }
}
}
out:
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 83f0b91..4677b18 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -959,7 +959,7 @@ bool bch_cached_dev_error(struct cached_dev *dc);
__printf(2, 3)
bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...);
-void bch_prio_write(struct cache *ca);
+int bch_prio_write(struct cache *ca, bool wait);
void bch_write_bdev_super(struct cached_dev *dc, struct closure *parent);
extern struct workqueue_struct *bcache_wq;
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 45f6846..bb40bd6 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -713,6 +713,8 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
* IO can always make forward progress:
*/
nr /= c->btree_pages;
+ if (nr == 0)
+ nr = 1;
nr = min_t(unsigned long, nr, mca_can_free(c));
i = 0;
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
index 06da66b..8c53d87 100644
--- a/drivers/md/bcache/debug.c
+++ b/drivers/md/bcache/debug.c
@@ -249,8 +249,7 @@ void bch_debug_init_cache_set(struct cache_set *c)
void bch_debug_exit(void)
{
- if (!IS_ERR_OR_NULL(bcache_debug))
- debugfs_remove_recursive(bcache_debug);
+ debugfs_remove_recursive(bcache_debug);
}
void __init bch_debug_init(struct kobject *kobj)
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 2321643..c45d9ad 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -418,6 +418,7 @@ static int __uuid_write(struct cache_set *c)
{
BKEY_PADDED(key) k;
struct closure cl;
+ struct cache *ca;
closure_init_stack(&cl);
lockdep_assert_held(&bch_register_lock);
@@ -429,6 +430,10 @@ static int __uuid_write(struct cache_set *c)
uuid_io(c, REQ_OP_WRITE, 0, &k.key, &cl);
closure_sync(&cl);
+ /* Only one bucket used for uuid write */
+ ca = PTR_CACHE(c, &k.key, 0);
+ atomic_long_add(ca->sb.bucket_size, &ca->meta_sectors_written);
+
bkey_copy(&c->uuid_bucket, &k.key);
bkey_put(c, &k.key);
return 0;
@@ -520,12 +525,29 @@ static void prio_io(struct cache *ca, uint64_t bucket, int op,
closure_sync(cl);
}
-void bch_prio_write(struct cache *ca)
+int bch_prio_write(struct cache *ca, bool wait)
{
int i;
struct bucket *b;
struct closure cl;
+ pr_debug("free_prio=%zu, free_none=%zu, free_inc=%zu",
+ fifo_used(&ca->free[RESERVE_PRIO]),
+ fifo_used(&ca->free[RESERVE_NONE]),
+ fifo_used(&ca->free_inc));
+
+ /*
+ * Pre-check if there are enough free buckets. In the non-blocking
+ * scenario it's better to fail early rather than starting to allocate
+ * buckets and do a cleanup later in case of failure.
+ */
+ if (!wait) {
+ size_t avail = fifo_used(&ca->free[RESERVE_PRIO]) +
+ fifo_used(&ca->free[RESERVE_NONE]);
+ if (prio_buckets(ca) > avail)
+ return -ENOMEM;
+ }
+
closure_init_stack(&cl);
lockdep_assert_held(&ca->set->bucket_lock);
@@ -535,9 +557,6 @@ void bch_prio_write(struct cache *ca)
atomic_long_add(ca->sb.bucket_size * prio_buckets(ca),
&ca->meta_sectors_written);
- //pr_debug("free %zu, free_inc %zu, unused %zu", fifo_used(&ca->free),
- // fifo_used(&ca->free_inc), fifo_used(&ca->unused));
-
for (i = prio_buckets(ca) - 1; i >= 0; --i) {
long bucket;
struct prio_set *p = ca->disk_buckets;
@@ -555,7 +574,7 @@ void bch_prio_write(struct cache *ca)
p->magic = pset_magic(&ca->sb);
p->csum = bch_crc64(&p->magic, bucket_bytes(ca) - 8);
- bucket = bch_bucket_alloc(ca, RESERVE_PRIO, true);
+ bucket = bch_bucket_alloc(ca, RESERVE_PRIO, wait);
BUG_ON(bucket == -1);
mutex_unlock(&ca->set->bucket_lock);
@@ -584,6 +603,7 @@ void bch_prio_write(struct cache *ca)
ca->prio_last_buckets[i] = ca->prio_buckets[i];
}
+ return 0;
}
static void prio_read(struct cache *ca, uint64_t bucket)
@@ -742,20 +762,28 @@ static inline int idx_to_first_minor(int idx)
static void bcache_device_free(struct bcache_device *d)
{
+ struct gendisk *disk = d->disk;
+
lockdep_assert_held(&bch_register_lock);
- pr_info("%s stopped", d->disk->disk_name);
+ if (disk)
+ pr_info("%s stopped", disk->disk_name);
+ else
+ pr_err("bcache device (NULL gendisk) stopped");
if (d->c)
bcache_device_detach(d);
- if (d->disk && d->disk->flags & GENHD_FL_UP)
- del_gendisk(d->disk);
- if (d->disk && d->disk->queue)
- blk_cleanup_queue(d->disk->queue);
- if (d->disk) {
+
+ if (disk) {
+ if (disk->flags & GENHD_FL_UP)
+ del_gendisk(disk);
+
+ if (disk->queue)
+ blk_cleanup_queue(disk->queue);
+
ida_simple_remove(&bcache_device_idx,
- first_minor_to_idx(d->disk->first_minor));
- put_disk(d->disk);
+ first_minor_to_idx(disk->first_minor));
+ put_disk(disk);
}
bioset_exit(&d->bio_split);
@@ -1004,6 +1032,7 @@ static void cached_dev_detach_finish(struct work_struct *w)
bch_write_bdev_super(dc, &cl);
closure_sync(&cl);
+ calc_cached_dev_sectors(dc->disk.c);
bcache_device_detach(&dc->disk);
list_move(&dc->list, &uncached_devices);
@@ -1485,8 +1514,7 @@ static void cache_set_free(struct closure *cl)
struct cache *ca;
unsigned int i;
- if (!IS_ERR_OR_NULL(c->debug))
- debugfs_remove(c->debug);
+ debugfs_remove(c->debug);
bch_open_buckets_free(c);
bch_btree_cache_free(c);
@@ -1871,7 +1899,7 @@ static int run_cache_set(struct cache_set *c)
mutex_lock(&c->bucket_lock);
for_each_cache(ca, c, i)
- bch_prio_write(ca);
+ bch_prio_write(ca, true);
mutex_unlock(&c->bucket_lock);
err = "cannot allocate new UUID bucket";
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 5bb81e5..3e8d1f1 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -289,7 +289,9 @@ STORE(__cached_dev)
sysfs_strtoul_clamp(writeback_rate_p_term_inverse,
dc->writeback_rate_p_term_inverse,
1, UINT_MAX);
- d_strtoul_nonzero(writeback_rate_minimum);
+ sysfs_strtoul_clamp(writeback_rate_minimum,
+ dc->writeback_rate_minimum,
+ 1, UINT_MAX);
sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX);
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index ba5395f..b5fc3c6 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -781,7 +781,7 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc)
bch_keybuf_init(&dc->writeback_keys);
dc->writeback_metadata = true;
- dc->writeback_running = true;
+ dc->writeback_running = false;
dc->writeback_percent = 10;
dc->writeback_delay = 30;
atomic_long_set(&dc->writeback_rate.rate, 1024);
@@ -810,6 +810,7 @@ int bch_cached_dev_writeback_start(struct cached_dev *dc)
destroy_workqueue(dc->writeback_write_wq);
return PTR_ERR(dc->writeback_thread);
}
+ dc->writeback_running = true;
WARN_ON(test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags));
schedule_delayed_work(&dc->writeback_rate_update,
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index b86d243..2fcf62f 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -287,20 +287,31 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
{
- unsigned bio_bytes = bio_cur_bytes(bio);
- char *data = bio_data(bio);
+ unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
+
+ struct bvec_iter iter;
+ struct bio_vec bvec;
+
+ if (!bio_has_data(bio))
+ return;
/*
- * Overwrite the Nth byte of the data returned.
+ * Overwrite the Nth byte of the bio's data, on whichever page
+ * it falls.
*/
- if (data && bio_bytes >= fc->corrupt_bio_byte) {
- data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
-
- DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
- "(rw=%c bi_opf=%u bi_sector=%llu cur_bytes=%u)\n",
- bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
- (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
- (unsigned long long)bio->bi_iter.bi_sector, bio_bytes);
+ bio_for_each_segment(bvec, bio, iter) {
+ if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
+ char *segment = (page_address(bio_iter_page(bio, iter))
+ + bio_iter_offset(bio, iter));
+ segment[corrupt_bio_byte] = fc->corrupt_bio_value;
+ DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
+ "(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
+ bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
+ (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
+ (unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size);
+ break;
+ }
+ corrupt_bio_byte -= bio_iter_len(bio, iter);
}
}
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 481e54d..def4f6e 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -609,45 +609,10 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio)
return pgpath;
}
-static struct pgpath *__map_bio_fast(struct multipath *m, struct bio *bio)
-{
- struct pgpath *pgpath;
- unsigned long flags;
-
- /* Do we need to select a new pgpath? */
- /*
- * FIXME: currently only switching path if no path (due to failure, etc)
- * - which negates the point of using a path selector
- */
- pgpath = READ_ONCE(m->current_pgpath);
- if (!pgpath)
- pgpath = choose_pgpath(m, bio->bi_iter.bi_size);
-
- if (!pgpath) {
- if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) {
- /* Queue for the daemon to resubmit */
- spin_lock_irqsave(&m->lock, flags);
- bio_list_add(&m->queued_bios, bio);
- spin_unlock_irqrestore(&m->lock, flags);
- queue_work(kmultipathd, &m->process_queued_bios);
-
- return ERR_PTR(-EAGAIN);
- }
- return NULL;
- }
-
- return pgpath;
-}
-
static int __multipath_map_bio(struct multipath *m, struct bio *bio,
struct dm_mpath_io *mpio)
{
- struct pgpath *pgpath;
-
- if (!m->hw_handler_name)
- pgpath = __map_bio_fast(m, bio);
- else
- pgpath = __map_bio(m, bio);
+ struct pgpath *pgpath = __map_bio(m, bio);
if (IS_ERR(pgpath))
return DM_MAPIO_SUBMITTED;
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index b78a8a4..23de59a 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -2475,7 +2475,7 @@ static int super_validate(struct raid_set *rs, struct md_rdev *rdev)
}
/* Enable bitmap creation for RAID levels != 0 */
- mddev->bitmap_info.offset = rt_is_raid0(rs->raid_type) ? 0 : to_sector(4096);
+ mddev->bitmap_info.offset = (rt_is_raid0(rs->raid_type) || rs->journal_dev.dev) ? 0 : to_sector(4096);
mddev->bitmap_info.default_offset = mddev->bitmap_info.offset;
if (!test_and_clear_bit(FirstUse, &rdev->flags)) {
@@ -3690,8 +3690,7 @@ static int raid_message(struct dm_target *ti, unsigned int argc, char **argv,
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
md_reap_sync_thread(mddev);
}
- } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
- test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
+ } else if (decipher_sync_action(mddev, mddev->recovery) != st_idle)
return -EBUSY;
else if (!strcasecmp(argv[0], "resync"))
; /* MD_RECOVERY_NEEDED set below */
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 36805b12..d3f28a9 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -19,7 +19,6 @@
#include <linux/vmalloc.h>
#include <linux/log2.h>
#include <linux/dm-kcopyd.h>
-#include <linux/semaphore.h>
#include "dm.h"
@@ -106,8 +105,8 @@ struct dm_snapshot {
/* The on disk metadata handler */
struct dm_exception_store *store;
- /* Maximum number of in-flight COW jobs. */
- struct semaphore cow_count;
+ unsigned in_progress;
+ struct wait_queue_head in_progress_wait;
struct dm_kcopyd_client *kcopyd_client;
@@ -158,8 +157,8 @@ struct dm_snapshot {
*/
#define DEFAULT_COW_THRESHOLD 2048
-static int cow_threshold = DEFAULT_COW_THRESHOLD;
-module_param_named(snapshot_cow_threshold, cow_threshold, int, 0644);
+static unsigned cow_threshold = DEFAULT_COW_THRESHOLD;
+module_param_named(snapshot_cow_threshold, cow_threshold, uint, 0644);
MODULE_PARM_DESC(snapshot_cow_threshold, "Maximum number of chunks being copied on write");
DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(snapshot_copy_throttle,
@@ -1207,7 +1206,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_hash_tables;
}
- sema_init(&s->cow_count, (cow_threshold > 0) ? cow_threshold : INT_MAX);
+ init_waitqueue_head(&s->in_progress_wait);
s->kcopyd_client = dm_kcopyd_client_create(&dm_kcopyd_throttle);
if (IS_ERR(s->kcopyd_client)) {
@@ -1396,9 +1395,56 @@ static void snapshot_dtr(struct dm_target *ti)
dm_put_device(ti, s->origin);
+ WARN_ON(s->in_progress);
+
kfree(s);
}
+static void account_start_copy(struct dm_snapshot *s)
+{
+ spin_lock(&s->in_progress_wait.lock);
+ s->in_progress++;
+ spin_unlock(&s->in_progress_wait.lock);
+}
+
+static void account_end_copy(struct dm_snapshot *s)
+{
+ spin_lock(&s->in_progress_wait.lock);
+ BUG_ON(!s->in_progress);
+ s->in_progress--;
+ if (likely(s->in_progress <= cow_threshold) &&
+ unlikely(waitqueue_active(&s->in_progress_wait)))
+ wake_up_locked(&s->in_progress_wait);
+ spin_unlock(&s->in_progress_wait.lock);
+}
+
+static bool wait_for_in_progress(struct dm_snapshot *s, bool unlock_origins)
+{
+ if (unlikely(s->in_progress > cow_threshold)) {
+ spin_lock(&s->in_progress_wait.lock);
+ if (likely(s->in_progress > cow_threshold)) {
+ /*
+ * NOTE: this throttle doesn't account for whether
+ * the caller is servicing an IO that will trigger a COW
+ * so excess throttling may result for chunks not required
+ * to be COW'd. But if cow_threshold was reached, extra
+ * throttling is unlikely to negatively impact performance.
+ */
+ DECLARE_WAITQUEUE(wait, current);
+ __add_wait_queue(&s->in_progress_wait, &wait);
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock(&s->in_progress_wait.lock);
+ if (unlock_origins)
+ up_read(&_origins_lock);
+ io_schedule();
+ remove_wait_queue(&s->in_progress_wait, &wait);
+ return false;
+ }
+ spin_unlock(&s->in_progress_wait.lock);
+ }
+ return true;
+}
+
/*
* Flush a list of buffers.
*/
@@ -1414,7 +1460,7 @@ static void flush_bios(struct bio *bio)
}
}
-static int do_origin(struct dm_dev *origin, struct bio *bio);
+static int do_origin(struct dm_dev *origin, struct bio *bio, bool limit);
/*
* Flush a list of buffers.
@@ -1427,7 +1473,7 @@ static void retry_origin_bios(struct dm_snapshot *s, struct bio *bio)
while (bio) {
n = bio->bi_next;
bio->bi_next = NULL;
- r = do_origin(s->origin, bio);
+ r = do_origin(s->origin, bio, false);
if (r == DM_MAPIO_REMAPPED)
generic_make_request(bio);
bio = n;
@@ -1594,7 +1640,7 @@ static void copy_callback(int read_err, unsigned long write_err, void *context)
rb_link_node(&pe->out_of_order_node, parent, p);
rb_insert_color(&pe->out_of_order_node, &s->out_of_order_tree);
}
- up(&s->cow_count);
+ account_end_copy(s);
}
/*
@@ -1618,7 +1664,7 @@ static void start_copy(struct dm_snap_pending_exception *pe)
dest.count = src.count;
/* Hand over to kcopyd */
- down(&s->cow_count);
+ account_start_copy(s);
dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe);
}
@@ -1638,7 +1684,7 @@ static void start_full_bio(struct dm_snap_pending_exception *pe,
pe->full_bio = bio;
pe->full_bio_end_io = bio->bi_end_io;
- down(&s->cow_count);
+ account_start_copy(s);
callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
copy_callback, pe);
@@ -1729,6 +1775,11 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
if (!s->valid)
return DM_MAPIO_KILL;
+ if (bio_data_dir(bio) == WRITE) {
+ while (unlikely(!wait_for_in_progress(s, false)))
+ ; /* wait_for_in_progress() has slept */
+ }
+
mutex_lock(&s->lock);
if (!s->valid || (unlikely(s->snapshot_overflowed) &&
@@ -1877,7 +1928,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio)
if (bio_data_dir(bio) == WRITE) {
mutex_unlock(&s->lock);
- return do_origin(s->origin, bio);
+ return do_origin(s->origin, bio, false);
}
out_unlock:
@@ -2214,15 +2265,24 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
/*
* Called on a write from the origin driver.
*/
-static int do_origin(struct dm_dev *origin, struct bio *bio)
+static int do_origin(struct dm_dev *origin, struct bio *bio, bool limit)
{
struct origin *o;
int r = DM_MAPIO_REMAPPED;
+again:
down_read(&_origins_lock);
o = __lookup_origin(origin->bdev);
- if (o)
+ if (o) {
+ if (limit) {
+ struct dm_snapshot *s;
+ list_for_each_entry(s, &o->snapshots, list)
+ if (unlikely(!wait_for_in_progress(s, true)))
+ goto again;
+ }
+
r = __origin_write(&o->snapshots, bio->bi_iter.bi_sector, bio);
+ }
up_read(&_origins_lock);
return r;
@@ -2335,7 +2395,7 @@ static int origin_map(struct dm_target *ti, struct bio *bio)
dm_accept_partial_bio(bio, available_sectors);
/* Only tell snapshots if this is a write */
- return do_origin(o->dev, bio);
+ return do_origin(o->dev, bio, true);
}
static long origin_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index 5f1f80d..503c426 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -1223,7 +1223,8 @@ static int writecache_map(struct dm_target *ti, struct bio *bio)
}
} while (bio->bi_iter.bi_size);
- if (unlikely(wc->uncommitted_blocks >= wc->autocommit_blocks))
+ if (unlikely(bio->bi_opf & REQ_FUA ||
+ wc->uncommitted_blocks >= wc->autocommit_blocks))
writecache_flush(wc);
else
writecache_schedule_autocommit(wc);
diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c
index 7e8d7fc..c2c1714 100644
--- a/drivers/md/dm-zoned-metadata.c
+++ b/drivers/md/dm-zoned-metadata.c
@@ -552,6 +552,7 @@ static struct dmz_mblock *dmz_get_mblock(struct dmz_metadata *zmd,
TASK_UNINTERRUPTIBLE);
if (test_bit(DMZ_META_ERROR, &mblk->state)) {
dmz_release_mblock(zmd, mblk);
+ dmz_check_bdev(zmd->dev);
return ERR_PTR(-EIO);
}
@@ -623,6 +624,8 @@ static int dmz_rdwr_block(struct dmz_metadata *zmd, int op, sector_t block,
ret = submit_bio_wait(bio);
bio_put(bio);
+ if (ret)
+ dmz_check_bdev(zmd->dev);
return ret;
}
@@ -689,6 +692,7 @@ static int dmz_write_dirty_mblocks(struct dmz_metadata *zmd,
TASK_UNINTERRUPTIBLE);
if (test_bit(DMZ_META_ERROR, &mblk->state)) {
clear_bit(DMZ_META_ERROR, &mblk->state);
+ dmz_check_bdev(zmd->dev);
ret = -EIO;
}
nr_mblks_submitted--;
@@ -766,7 +770,7 @@ int dmz_flush_metadata(struct dmz_metadata *zmd)
/* If there are no dirty metadata blocks, just flush the device cache */
if (list_empty(&write_list)) {
ret = blkdev_issue_flush(zmd->dev->bdev, GFP_NOIO, NULL);
- goto out;
+ goto err;
}
/*
@@ -776,7 +780,7 @@ int dmz_flush_metadata(struct dmz_metadata *zmd)
*/
ret = dmz_log_dirty_mblocks(zmd, &write_list);
if (ret)
- goto out;
+ goto err;
/*
* The log is on disk. It is now safe to update in place
@@ -784,11 +788,11 @@ int dmz_flush_metadata(struct dmz_metadata *zmd)
*/
ret = dmz_write_dirty_mblocks(zmd, &write_list, zmd->mblk_primary);
if (ret)
- goto out;
+ goto err;
ret = dmz_write_sb(zmd, zmd->mblk_primary);
if (ret)
- goto out;
+ goto err;
while (!list_empty(&write_list)) {
mblk = list_first_entry(&write_list, struct dmz_mblock, link);
@@ -803,16 +807,20 @@ int dmz_flush_metadata(struct dmz_metadata *zmd)
zmd->sb_gen++;
out:
- if (ret && !list_empty(&write_list)) {
- spin_lock(&zmd->mblk_lock);
- list_splice(&write_list, &zmd->mblk_dirty_list);
- spin_unlock(&zmd->mblk_lock);
- }
-
dmz_unlock_flush(zmd);
up_write(&zmd->mblk_sem);
return ret;
+
+err:
+ if (!list_empty(&write_list)) {
+ spin_lock(&zmd->mblk_lock);
+ list_splice(&write_list, &zmd->mblk_dirty_list);
+ spin_unlock(&zmd->mblk_lock);
+ }
+ if (!dmz_check_bdev(zmd->dev))
+ ret = -EIO;
+ goto out;
}
/*
@@ -1235,6 +1243,7 @@ static int dmz_update_zone(struct dmz_metadata *zmd, struct dm_zone *zone)
if (ret) {
dmz_dev_err(zmd->dev, "Get zone %u report failed",
dmz_id(zmd, zone));
+ dmz_check_bdev(zmd->dev);
return ret;
}
diff --git a/drivers/md/dm-zoned-reclaim.c b/drivers/md/dm-zoned-reclaim.c
index 9470b8f..84ac671 100644
--- a/drivers/md/dm-zoned-reclaim.c
+++ b/drivers/md/dm-zoned-reclaim.c
@@ -81,6 +81,7 @@ static int dmz_reclaim_align_wp(struct dmz_reclaim *zrc, struct dm_zone *zone,
"Align zone %u wp %llu to %llu (wp+%u) blocks failed %d",
dmz_id(zmd, zone), (unsigned long long)wp_block,
(unsigned long long)block, nr_blocks, ret);
+ dmz_check_bdev(zrc->dev);
return ret;
}
@@ -488,12 +489,7 @@ static void dmz_reclaim_work(struct work_struct *work)
ret = dmz_do_reclaim(zrc);
if (ret) {
dmz_dev_debug(zrc->dev, "Reclaim error %d\n", ret);
- if (ret == -EIO)
- /*
- * LLD might be performing some error handling sequence
- * at the underlying device. To not interfere, do not
- * attempt to schedule the next reclaim run immediately.
- */
+ if (!dmz_check_bdev(zrc->dev))
return;
}
diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
index 3dd668f6..971a2da 100644
--- a/drivers/md/dm-zoned-target.c
+++ b/drivers/md/dm-zoned-target.c
@@ -79,6 +79,8 @@ static inline void dmz_bio_endio(struct bio *bio, blk_status_t status)
if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
bio->bi_status = status;
+ if (bio->bi_status != BLK_STS_OK)
+ bioctx->target->dev->flags |= DMZ_CHECK_BDEV;
if (atomic_dec_and_test(&bioctx->ref)) {
struct dm_zone *zone = bioctx->zone;
@@ -564,32 +566,52 @@ static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
}
/*
- * Check the backing device availability. If it's on the way out,
+ * Check if the backing device is being removed. If it's on the way out,
* start failing I/O. Reclaim and metadata components also call this
* function to cleanly abort operation in the event of such failure.
*/
bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev)
{
- struct gendisk *disk;
+ if (dmz_dev->flags & DMZ_BDEV_DYING)
+ return true;
- if (!(dmz_dev->flags & DMZ_BDEV_DYING)) {
- disk = dmz_dev->bdev->bd_disk;
- if (blk_queue_dying(bdev_get_queue(dmz_dev->bdev))) {
- dmz_dev_warn(dmz_dev, "Backing device queue dying");
- dmz_dev->flags |= DMZ_BDEV_DYING;
- } else if (disk->fops->check_events) {
- if (disk->fops->check_events(disk, 0) &
- DISK_EVENT_MEDIA_CHANGE) {
- dmz_dev_warn(dmz_dev, "Backing device offline");
- dmz_dev->flags |= DMZ_BDEV_DYING;
- }
- }
+ if (dmz_dev->flags & DMZ_CHECK_BDEV)
+ return !dmz_check_bdev(dmz_dev);
+
+ if (blk_queue_dying(bdev_get_queue(dmz_dev->bdev))) {
+ dmz_dev_warn(dmz_dev, "Backing device queue dying");
+ dmz_dev->flags |= DMZ_BDEV_DYING;
}
return dmz_dev->flags & DMZ_BDEV_DYING;
}
/*
+ * Check the backing device availability. This detects such events as
+ * backing device going offline due to errors, media removals, etc.
+ * This check is less efficient than dmz_bdev_is_dying() and should
+ * only be performed as a part of error handling.
+ */
+bool dmz_check_bdev(struct dmz_dev *dmz_dev)
+{
+ struct gendisk *disk;
+
+ dmz_dev->flags &= ~DMZ_CHECK_BDEV;
+
+ if (dmz_bdev_is_dying(dmz_dev))
+ return false;
+
+ disk = dmz_dev->bdev->bd_disk;
+ if (disk->fops->check_events &&
+ disk->fops->check_events(disk, 0) & DISK_EVENT_MEDIA_CHANGE) {
+ dmz_dev_warn(dmz_dev, "Backing device offline");
+ dmz_dev->flags |= DMZ_BDEV_DYING;
+ }
+
+ return !(dmz_dev->flags & DMZ_BDEV_DYING);
+}
+
+/*
* Process a new BIO.
*/
static int dmz_map(struct dm_target *ti, struct bio *bio)
@@ -902,8 +924,8 @@ static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct dmz_target *dmz = ti->private;
- if (dmz_bdev_is_dying(dmz->dev))
- return -ENODEV;
+ if (!dmz_check_bdev(dmz->dev))
+ return -EIO;
*bdev = dmz->dev->bdev;
diff --git a/drivers/md/dm-zoned.h b/drivers/md/dm-zoned.h
index 93a6452..2662746 100644
--- a/drivers/md/dm-zoned.h
+++ b/drivers/md/dm-zoned.h
@@ -71,6 +71,7 @@ struct dmz_dev {
/* Device flags. */
#define DMZ_BDEV_DYING (1 << 0)
+#define DMZ_CHECK_BDEV (2 << 0)
/*
* Zone descriptor.
@@ -254,5 +255,6 @@ void dmz_schedule_reclaim(struct dmz_reclaim *zrc);
* Functions defined in dm-zoned-target.c
*/
bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev);
+bool dmz_check_bdev(struct dmz_dev *dmz_dev);
#endif /* DM_ZONED_H */
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index 2fc8c11..fd86071 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -2132,6 +2132,7 @@ int md_bitmap_resize(struct bitmap *bitmap, sector_t blocks,
memcpy(page_address(store.sb_page),
page_address(bitmap->storage.sb_page),
sizeof(bitmap_super_t));
+ spin_lock_irq(&bitmap->counts.lock);
md_bitmap_file_unmap(&bitmap->storage);
bitmap->storage = store;
@@ -2147,7 +2148,6 @@ int md_bitmap_resize(struct bitmap *bitmap, sector_t blocks,
blocks = min(old_counts.chunks << old_counts.chunkshift,
chunks << chunkshift);
- spin_lock_irq(&bitmap->counts.lock);
/* For cluster raid, need to pre-allocate bitmap */
if (mddev_is_clustered(bitmap->mddev)) {
unsigned long page;
diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c
index d45c697..9a60231 100644
--- a/drivers/md/md-linear.c
+++ b/drivers/md/md-linear.c
@@ -252,10 +252,9 @@ static bool linear_make_request(struct mddev *mddev, struct bio *bio)
sector_t start_sector, end_sector, data_offset;
sector_t bio_sector = bio->bi_iter.bi_sector;
- if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
- md_flush_request(mddev, bio);
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH)
+ && md_flush_request(mddev, bio))
return true;
- }
tmp_dev = which_dev(mddev, bio_sector);
start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors;
diff --git a/drivers/md/md-multipath.c b/drivers/md/md-multipath.c
index 881487d..80c35bf 100644
--- a/drivers/md/md-multipath.c
+++ b/drivers/md/md-multipath.c
@@ -112,10 +112,9 @@ static bool multipath_make_request(struct mddev *mddev, struct bio * bio)
struct multipath_bh * mp_bh;
struct multipath_info *multipath;
- if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
- md_flush_request(mddev, bio);
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH)
+ && md_flush_request(mddev, bio))
return true;
- }
mp_bh = mempool_alloc(&conf->pool, GFP_NOIO);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index a8fbaa3..62f214d 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -487,7 +487,13 @@ static void md_submit_flush_data(struct work_struct *ws)
}
}
-void md_flush_request(struct mddev *mddev, struct bio *bio)
+/*
+ * Manages consolidation of flushes and submitting any flushes needed for
+ * a bio with REQ_PREFLUSH. Returns true if the bio is finished or is
+ * being finished in another context. Returns false if the flushing is
+ * complete but still needs the I/O portion of the bio to be processed.
+ */
+bool md_flush_request(struct mddev *mddev, struct bio *bio)
{
ktime_t start = ktime_get_boottime();
spin_lock_irq(&mddev->lock);
@@ -512,9 +518,10 @@ void md_flush_request(struct mddev *mddev, struct bio *bio)
bio_endio(bio);
else {
bio->bi_opf &= ~REQ_PREFLUSH;
- mddev->pers->make_request(mddev, bio);
+ return false;
}
}
+ return true;
}
EXPORT_SYMBOL(md_flush_request);
@@ -8778,6 +8785,18 @@ static void md_start_sync(struct work_struct *ws)
*/
void md_check_recovery(struct mddev *mddev)
{
+ if (test_bit(MD_ALLOW_SB_UPDATE, &mddev->flags) && mddev->sb_flags) {
+ /* Write superblock - thread that called mddev_suspend()
+ * holds reconfig_mutex for us.
+ */
+ set_bit(MD_UPDATING_SB, &mddev->flags);
+ smp_mb__after_atomic();
+ if (test_bit(MD_ALLOW_SB_UPDATE, &mddev->flags))
+ md_update_sb(mddev, 0);
+ clear_bit_unlock(MD_UPDATING_SB, &mddev->flags);
+ wake_up(&mddev->sb_wait);
+ }
+
if (mddev->suspended)
return;
@@ -8938,16 +8957,6 @@ void md_check_recovery(struct mddev *mddev)
unlock:
wake_up(&mddev->sb_wait);
mddev_unlock(mddev);
- } else if (test_bit(MD_ALLOW_SB_UPDATE, &mddev->flags) && mddev->sb_flags) {
- /* Write superblock - thread that called mddev_suspend()
- * holds reconfig_mutex for us.
- */
- set_bit(MD_UPDATING_SB, &mddev->flags);
- smp_mb__after_atomic();
- if (test_bit(MD_ALLOW_SB_UPDATE, &mddev->flags))
- md_update_sb(mddev, 0);
- clear_bit_unlock(MD_UPDATING_SB, &mddev->flags);
- wake_up(&mddev->sb_wait);
}
}
EXPORT_SYMBOL(md_check_recovery);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 4f89463..cce62bb 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -532,7 +532,7 @@ struct md_personality
int level;
struct list_head list;
struct module *owner;
- bool (*make_request)(struct mddev *mddev, struct bio *bio);
+ bool __must_check (*make_request)(struct mddev *mddev, struct bio *bio);
/*
* start up works that do NOT require md_thread. tasks that
* requires md_thread should go into start()
@@ -684,7 +684,7 @@ extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
extern void md_finish_reshape(struct mddev *mddev);
extern int mddev_congested(struct mddev *mddev, int bits);
-extern void md_flush_request(struct mddev *mddev, struct bio *bio);
+extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
extern void md_super_write(struct mddev *mddev, struct md_rdev *rdev,
sector_t sector, int size, struct page *page);
extern int md_super_wait(struct mddev *mddev);
diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c
index 21ea537..eff04fa 100644
--- a/drivers/md/persistent-data/dm-btree-remove.c
+++ b/drivers/md/persistent-data/dm-btree-remove.c
@@ -203,7 +203,13 @@ static void __rebalance2(struct dm_btree_info *info, struct btree_node *parent,
struct btree_node *right = r->n;
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
- unsigned threshold = 2 * merge_threshold(left) + 1;
+ /*
+ * Ensure the number of entries in each child will be greater
+ * than or equal to (max_entries / 3 + 1), so no matter which
+ * child is used for removal, the number will still be not
+ * less than (max_entries / 3).
+ */
+ unsigned int threshold = 2 * (merge_threshold(left) + 1);
if (nr_left + nr_right < threshold) {
/*
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 3cafbfd..2e71405 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -580,10 +580,9 @@ static bool raid0_make_request(struct mddev *mddev, struct bio *bio)
unsigned chunk_sects;
unsigned sectors;
- if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
- md_flush_request(mddev, bio);
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH)
+ && md_flush_request(mddev, bio))
return true;
- }
if (unlikely((bio_op(bio) == REQ_OP_DISCARD))) {
raid0_handle_discard(mddev, bio);
@@ -620,7 +619,7 @@ static bool raid0_make_request(struct mddev *mddev, struct bio *bio)
tmp_dev = map_sector(mddev, zone, sector, §or);
break;
default:
- WARN("md/raid0:%s: Invalid layout\n", mdname(mddev));
+ WARN(1, "md/raid0:%s: Invalid layout\n", mdname(mddev));
bio_io_error(bio);
return true;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 6929d110..abcb4c3 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1537,10 +1537,9 @@ static bool raid1_make_request(struct mddev *mddev, struct bio *bio)
{
sector_t sectors;
- if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
- md_flush_request(mddev, bio);
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH)
+ && md_flush_request(mddev, bio))
return true;
- }
/*
* There is a limit to the maximum size, but
@@ -2757,7 +2756,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
write_targets++;
}
}
- if (bio->bi_end_io) {
+ if (rdev && bio->bi_end_io) {
atomic_inc(&rdev->nr_pending);
bio->bi_iter.bi_sector = sector_nr + rdev->data_offset;
bio_set_dev(bio, rdev->bdev);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 25e97de..02c5e39 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -229,7 +229,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
out_free_pages:
while (--j >= 0)
- resync_free_pages(&rps[j * 2]);
+ resync_free_pages(&rps[j]);
j = 0;
out_free_bio:
@@ -1562,10 +1562,9 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio)
int chunk_sects = chunk_mask + 1;
int sectors = bio_sectors(bio);
- if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
- md_flush_request(mddev, bio);
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH)
+ && md_flush_request(mddev, bio))
return true;
- }
if (!md_write_start(mddev, bio))
return false;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4a5aad2..0102138 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5590,8 +5590,8 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
if (ret == 0)
return true;
if (ret == -ENODEV) {
- md_flush_request(mddev, bi);
- return true;
+ if (md_flush_request(mddev, bi))
+ return true;
}
/* ret == -EAGAIN, fallback */
/*
@@ -5724,7 +5724,7 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
do_flush = false;
}
- if (!sh->batch_head)
+ if (!sh->batch_head || sh == sh->batch_head)
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
if ((!sh->batch_head || sh == sh->batch_head) &&
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 4a15d53..ba7e976 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -365,7 +365,8 @@ static void cec_data_cancel(struct cec_data *data, u8 tx_status)
} else {
list_del_init(&data->list);
if (!(data->msg.tx_status & CEC_TX_STATUS_OK))
- data->adap->transmit_queue_sz--;
+ if (!WARN_ON(!data->adap->transmit_queue_sz))
+ data->adap->transmit_queue_sz--;
}
if (data->msg.tx_status & CEC_TX_STATUS_OK) {
@@ -417,6 +418,14 @@ static void cec_flush(struct cec_adapter *adap)
* need to do anything special in that case.
*/
}
+ /*
+ * If something went wrong and this counter isn't what it should
+ * be, then this will reset it back to 0. Warn if it is not 0,
+ * since it indicates a bug, either in this framework or in a
+ * CEC driver.
+ */
+ if (WARN_ON(adap->transmit_queue_sz))
+ adap->transmit_queue_sz = 0;
}
/*
@@ -441,7 +450,7 @@ int cec_thread_func(void *_adap)
bool timeout = false;
u8 attempts;
- if (adap->transmitting) {
+ if (adap->transmit_in_progress) {
int err;
/*
@@ -476,7 +485,7 @@ int cec_thread_func(void *_adap)
goto unlock;
}
- if (adap->transmitting && timeout) {
+ if (adap->transmit_in_progress && timeout) {
/*
* If we timeout, then log that. Normally this does
* not happen and it is an indication of a faulty CEC
@@ -485,14 +494,18 @@ int cec_thread_func(void *_adap)
* so much traffic on the bus that the adapter was
* unable to transmit for CEC_XFER_TIMEOUT_MS (2.1s).
*/
- pr_warn("cec-%s: message %*ph timed out\n", adap->name,
- adap->transmitting->msg.len,
- adap->transmitting->msg.msg);
+ if (adap->transmitting) {
+ pr_warn("cec-%s: message %*ph timed out\n", adap->name,
+ adap->transmitting->msg.len,
+ adap->transmitting->msg.msg);
+ /* Just give up on this. */
+ cec_data_cancel(adap->transmitting,
+ CEC_TX_STATUS_TIMEOUT);
+ } else {
+ pr_warn("cec-%s: transmit timed out\n", adap->name);
+ }
adap->transmit_in_progress = false;
adap->tx_timeouts++;
- /* Just give up on this. */
- cec_data_cancel(adap->transmitting,
- CEC_TX_STATUS_TIMEOUT);
goto unlock;
}
@@ -507,7 +520,8 @@ int cec_thread_func(void *_adap)
data = list_first_entry(&adap->transmit_queue,
struct cec_data, list);
list_del_init(&data->list);
- adap->transmit_queue_sz--;
+ if (!WARN_ON(!data->adap->transmit_queue_sz))
+ adap->transmit_queue_sz--;
/* Make this the current transmitting message */
adap->transmitting = data;
@@ -1038,11 +1052,11 @@ void cec_received_msg_ts(struct cec_adapter *adap,
valid_la = false;
else if (!cec_msg_is_broadcast(msg) && !(dir_fl & DIRECTED))
valid_la = false;
- else if (cec_msg_is_broadcast(msg) && !(dir_fl & BCAST1_4))
+ else if (cec_msg_is_broadcast(msg) && !(dir_fl & BCAST))
valid_la = false;
else if (cec_msg_is_broadcast(msg) &&
- adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0 &&
- !(dir_fl & BCAST2_0))
+ adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0 &&
+ !(dir_fl & BCAST1_4))
valid_la = false;
}
if (valid_la && min_len) {
@@ -1437,6 +1451,13 @@ static int cec_config_thread_func(void *arg)
las->log_addr[i],
cec_phys_addr_exp(adap->phys_addr));
cec_transmit_msg_fh(adap, &msg, NULL, false);
+
+ /* Report Vendor ID */
+ if (adap->log_addrs.vendor_id != CEC_VENDOR_ID_NONE) {
+ cec_msg_device_vendor_id(&msg,
+ adap->log_addrs.vendor_id);
+ cec_transmit_msg_fh(adap, &msg, NULL, false);
+ }
}
adap->kthread_config = NULL;
complete(&adap->config_completion);
diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c
index 0496d93..8f987bc 100644
--- a/drivers/media/cec/cec-pin.c
+++ b/drivers/media/cec/cec-pin.c
@@ -936,6 +936,17 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
/* Start bit, switch to receive state */
pin->ts = ts;
pin->state = CEC_ST_RX_START_BIT_LOW;
+ /*
+ * If a transmit is pending, then that transmit should
+ * use a signal free time of no more than
+ * CEC_SIGNAL_FREE_TIME_NEW_INITIATOR since it will
+ * have a new initiator due to the receive that is now
+ * starting.
+ */
+ if (pin->tx_msg.len && pin->tx_signal_free_time >
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ pin->tx_signal_free_time =
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
break;
}
if (ktime_to_ns(pin->ts) == 0)
@@ -1158,6 +1169,15 @@ static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
{
struct cec_pin *pin = adap->pin;
+ /*
+ * If a receive is in progress, then this transmit should use
+ * a signal free time of max CEC_SIGNAL_FREE_TIME_NEW_INITIATOR
+ * since when it starts transmitting it will have a new initiator.
+ */
+ if (pin->state != CEC_ST_IDLE &&
+ signal_free_time > CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+
pin->tx_signal_free_time = signal_free_time;
pin->tx_extra_bytes = 0;
pin->tx_msg = *msg;
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 24f80f2..3215948 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -2335,7 +2335,7 @@ static int dvb_dmxdev_ts_fullness_callback(struct dmx_ts_feed *filter,
if (dmxdevfilter->dev->dvr_in_exit)
return -ENODEV;
- spin_lock(&dmxdevfilter->dev->lock);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
if ((!src->data) ||
(dmxdevfilter->state != DMXDEV_STATE_GO))
@@ -2344,17 +2344,17 @@ static int dvb_dmxdev_ts_fullness_callback(struct dmx_ts_feed *filter,
ret = src->error;
if (ret) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
return ret;
}
if ((required_space <= dvb_ringbuffer_free(src)) &&
(!dvb_dmxdev_events_is_full(events))) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
return 0;
}
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
if (!wait)
return -ENOSPC;
@@ -2395,7 +2395,7 @@ static int dvb_dmxdev_sec_fullness_callback(
if (dmxdevfilter->dev->dvr_in_exit)
return -ENODEV;
- spin_lock(&dmxdevfilter->dev->lock);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
if ((!src->data) ||
(dmxdevfilter->state != DMXDEV_STATE_GO))
@@ -2404,17 +2404,17 @@ static int dvb_dmxdev_sec_fullness_callback(
ret = src->error;
if (ret) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
return ret;
}
if ((required_space <= dvb_ringbuffer_free(src)) &&
(!dvb_dmxdev_events_is_full(events))) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
return 0;
}
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
if (!wait)
return -ENOSPC;
@@ -4671,8 +4671,10 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
struct dmxdev_filter *dmxdevfilter = file->private_data;
unsigned int mask = 0;
- if (!dmxdevfilter)
+ if (!dmxdevfilter) {
+ pr_err("%s: dmxdevfilter is NULL\n");
return -EINVAL;
+ }
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait);
@@ -4902,9 +4904,12 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
return -EPOLLERR;
if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait);
-
+#ifdef CONFIG_DVB_MMAP
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
- dmxdev->may_do_mmap) {
+ dmxdev->may_do_mmap) {
+#else
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+#endif
poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
if (dmxdev->dvr_buffer.error) {
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 6ca88daa..65c3024 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -569,7 +569,8 @@ static int adv748x_parse_dt(struct adv748x_state *state)
{
struct device_node *ep_np = NULL;
struct of_endpoint ep;
- bool found = false;
+ bool out_found = false;
+ bool in_found = false;
for_each_endpoint_of_node(state->dev->of_node, ep_np) {
of_graph_parse_endpoint(ep_np, &ep);
@@ -592,10 +593,17 @@ static int adv748x_parse_dt(struct adv748x_state *state)
of_node_get(ep_np);
state->endpoints[ep.port] = ep_np;
- found = true;
+ /*
+ * At least one input endpoint and one output endpoint shall
+ * be defined.
+ */
+ if (ep.port < ADV748X_PORT_TXA)
+ in_found = true;
+ else
+ out_found = true;
}
- return found ? 0 : -ENODEV;
+ return in_found && out_found ? 0 : -ENODEV;
}
static void adv748x_dt_cleanup(struct adv748x_state *state)
@@ -627,6 +635,17 @@ static int adv748x_probe(struct i2c_client *client,
state->i2c_clients[ADV748X_PAGE_IO] = client;
i2c_set_clientdata(client, state);
+ /*
+ * We can not use container_of to get back to the state with two TXs;
+ * Initialize the TXs's fields unconditionally on the endpoint
+ * presence to access them later.
+ */
+ state->txa.state = state->txb.state = state;
+ state->txa.page = ADV748X_PAGE_TXA;
+ state->txb.page = ADV748X_PAGE_TXB;
+ state->txa.port = ADV748X_PORT_TXA;
+ state->txb.port = ADV748X_PORT_TXB;
+
/* Discover and process ports declared by the Device tree endpoints */
ret = adv748x_parse_dt(state);
if (ret) {
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index 469be87..556e13c 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -266,19 +266,10 @@ static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
{
- struct device_node *ep;
int ret;
- /* We can not use container_of to get back to the state with two TXs */
- tx->state = state;
- tx->page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
-
- ep = state->endpoints[is_txa(tx) ? ADV748X_PORT_TXA : ADV748X_PORT_TXB];
- if (!ep) {
- adv_err(state, "No endpoint found for %s\n",
- is_txa(tx) ? "txa" : "txb");
- return -ENODEV;
- }
+ if (!is_tx_enabled(tx))
+ return 0;
/* Initialise the virtual channel */
adv748x_csi2_set_virtual_channel(tx, 0);
@@ -288,7 +279,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
is_txa(tx) ? "txa" : "txb");
/* Ensure that matching is based upon the endpoint fwnodes */
- tx->sd.fwnode = of_fwnode_handle(ep);
+ tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]);
/* Register internal ops for incremental subdev registration */
tx->sd.internal_ops = &adv748x_csi2_internal_ops;
@@ -321,6 +312,9 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
{
+ if (!is_tx_enabled(tx))
+ return;
+
v4l2_async_unregister_subdev(&tx->sd);
media_entity_cleanup(&tx->sd.entity);
v4l2_ctrl_handler_free(&tx->ctrl_hdl);
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 65f8374..1cf46c40 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -82,6 +82,7 @@ struct adv748x_csi2 {
struct adv748x_state *state;
struct v4l2_mbus_framefmt format;
unsigned int page;
+ unsigned int port;
struct media_pad pads[ADV748X_CSI2_NR_PADS];
struct v4l2_ctrl_handler ctrl_hdl;
@@ -91,6 +92,7 @@ struct adv748x_csi2 {
#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier)
#define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)
+#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL)
enum adv748x_hdmi_pads {
ADV748X_HDMI_SINK,
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 91fae01..3dc2100 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -169,7 +169,8 @@ static int dw9714_probe(struct i2c_client *client)
return 0;
err_cleanup:
- dw9714_subdev_cleanup(dw9714_dev);
+ v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm);
+ media_entity_cleanup(&dw9714_dev->sd.entity);
dev_err(&client->dev, "Probe failed: %d\n", rval);
return rval;
}
diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c
index 8ba3920..5477ba3 100644
--- a/drivers/media/i2c/dw9807-vcm.c
+++ b/drivers/media/i2c/dw9807-vcm.c
@@ -218,7 +218,8 @@ static int dw9807_probe(struct i2c_client *client)
return 0;
err_cleanup:
- dw9807_subdev_cleanup(dw9807_dev);
+ v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm);
+ media_entity_cleanup(&dw9807_dev->sd.entity);
return rval;
}
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index a66f620..afd66d2 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -1230,7 +1230,7 @@ static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl)
* Applying V4L2 control value only happens
* when power is up for streaming
*/
- if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ if (!pm_runtime_get_if_in_use(&client->dev))
return 0;
ret = 0;
@@ -1612,7 +1612,8 @@ static int ov13858_init_controls(struct ov13858 *ov13858)
OV13858_NUM_OF_LINK_FREQS - 1,
0,
link_freq_menu_items);
- ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (ov13858->link_freq)
+ ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]);
pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]);
@@ -1635,7 +1636,8 @@ static int ov13858_init_controls(struct ov13858 *ov13858)
ov13858->hblank = v4l2_ctrl_new_std(
ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_HBLANK,
hblank, hblank, 1, hblank);
- ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (ov13858->hblank)
+ ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
exposure_max = mode->vts_def - 8;
ov13858->exposure = v4l2_ctrl_new_std(
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index e6a8b56..4b6be3b 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -419,10 +419,14 @@ static struct sensor_register ov2659_720p[] = {
{ REG_TIMING_YINC, 0x11 },
{ REG_TIMING_VERT_FORMAT, 0x80 },
{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+ { 0x370a, 0x12 },
{ 0x3a03, 0xe8 },
{ 0x3a09, 0x6f },
{ 0x3a0b, 0x5d },
{ 0x3a15, 0x9a },
+ { REG_VFIFO_READ_START_H, 0x00 },
+ { REG_VFIFO_READ_START_L, 0x80 },
+ { REG_ISP_CTRL02, 0x00 },
{ REG_NULL, 0x00 },
};
@@ -1203,11 +1207,15 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on)
goto unlock;
}
- ov2659_set_pixel_clock(ov2659);
- ov2659_set_frame_size(ov2659);
- ov2659_set_format(ov2659);
- ov2659_set_streaming(ov2659, 1);
- ov2659->streaming = on;
+ ret = ov2659_set_pixel_clock(ov2659);
+ if (!ret)
+ ret = ov2659_set_frame_size(ov2659);
+ if (!ret)
+ ret = ov2659_set_format(ov2659);
+ if (!ret) {
+ ov2659_set_streaming(ov2659, 1);
+ ov2659->streaming = on;
+ }
unlock:
mutex_unlock(&ov2659->lock);
diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c
index f753a1c..d8798fb 100644
--- a/drivers/media/i2c/ov2680.c
+++ b/drivers/media/i2c/ov2680.c
@@ -568,10 +568,6 @@ static int ov2680_power_on(struct ov2680_dev *sensor)
if (ret < 0)
return ret;
- ret = ov2680_mode_restore(sensor);
- if (ret < 0)
- goto disable;
-
sensor->is_enabled = true;
/* Set clock lane into LP-11 state */
@@ -580,12 +576,6 @@ static int ov2680_power_on(struct ov2680_dev *sensor)
ov2680_stream_disable(sensor);
return 0;
-
-disable:
- dev_err(dev, "failed to enable sensor: %d\n", ret);
- ov2680_power_off(sensor);
-
- return ret;
}
static int ov2680_s_power(struct v4l2_subdev *sd, int on)
@@ -606,6 +596,8 @@ static int ov2680_s_power(struct v4l2_subdev *sd, int on)
ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
if (ret < 0)
return ret;
+
+ ret = ov2680_mode_restore(sensor);
}
return ret;
@@ -1088,26 +1080,20 @@ static int ov2680_probe(struct i2c_client *client)
mutex_init(&sensor->lock);
- ret = ov2680_v4l2_init(sensor);
+ ret = ov2680_check_id(sensor);
if (ret < 0)
goto lock_destroy;
- ret = ov2680_check_id(sensor);
+ ret = ov2680_v4l2_init(sensor);
if (ret < 0)
- goto error_cleanup;
+ goto lock_destroy;
dev_info(dev, "ov2680 init correctly\n");
return 0;
-error_cleanup:
- dev_err(dev, "ov2680 init fail: %d\n", ret);
-
- media_entity_cleanup(&sensor->sd.entity);
- v4l2_async_unregister_subdev(&sensor->sd);
- v4l2_ctrl_handler_free(&sensor->ctrls.handler);
-
lock_destroy:
+ dev_err(dev, "ov2680 init fail: %d\n", ret);
mutex_destroy(&sensor->lock);
return ret;
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
index 385c188..98a1f2e3 100644
--- a/drivers/media/i2c/ov2685.c
+++ b/drivers/media/i2c/ov2685.c
@@ -549,7 +549,7 @@ static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
break;
}
- if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ if (!pm_runtime_get_if_in_use(&client->dev))
return 0;
switch (ctrl->id) {
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index a3bbef6..2023df1 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2572,8 +2572,6 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
if (frame_rate < 0)
frame_rate = OV5640_15_FPS;
- sensor->current_fr = frame_rate;
- sensor->frame_interval = fi->interval;
mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
mode->vact, true);
if (!mode) {
@@ -2581,7 +2579,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
goto out;
}
- if (mode != sensor->current_mode) {
+ if (mode != sensor->current_mode ||
+ frame_rate != sensor->current_fr) {
+ sensor->current_fr = frame_rate;
+ sensor->frame_interval = fi->interval;
sensor->current_mode = mode;
sensor->pending_mode_change = true;
}
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index 7b7c74d..53dd30d 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -2016,7 +2016,7 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
}
/* V4L2 controls values will be applied only when power is already up */
- if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ if (!pm_runtime_get_if_in_use(&client->dev))
return 0;
switch (ctrl->id) {
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
index 9a80dec..5d107c5 100644
--- a/drivers/media/i2c/ov5695.c
+++ b/drivers/media/i2c/ov5695.c
@@ -1110,7 +1110,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
break;
}
- if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ if (!pm_runtime_get_if_in_use(&client->dev))
return 0;
switch (ctrl->id) {
diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c
index edded86..c5aadd8 100644
--- a/drivers/media/i2c/ov6650.c
+++ b/drivers/media/i2c/ov6650.c
@@ -469,38 +469,39 @@ static int ov6650_set_selection(struct v4l2_subdev *sd,
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
- struct v4l2_rect rect = sel->r;
int ret;
if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- v4l_bound_align_image(&rect.width, 2, W_CIF, 1,
- &rect.height, 2, H_CIF, 1, 0);
- v4l_bound_align_image(&rect.left, DEF_HSTRT << 1,
- (DEF_HSTRT << 1) + W_CIF - (__s32)rect.width, 1,
- &rect.top, DEF_VSTRT << 1,
- (DEF_VSTRT << 1) + H_CIF - (__s32)rect.height, 1,
- 0);
+ v4l_bound_align_image(&sel->r.width, 2, W_CIF, 1,
+ &sel->r.height, 2, H_CIF, 1, 0);
+ v4l_bound_align_image(&sel->r.left, DEF_HSTRT << 1,
+ (DEF_HSTRT << 1) + W_CIF - (__s32)sel->r.width, 1,
+ &sel->r.top, DEF_VSTRT << 1,
+ (DEF_VSTRT << 1) + H_CIF - (__s32)sel->r.height,
+ 1, 0);
- ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1);
+ ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1);
if (!ret) {
- priv->rect.left = rect.left;
+ priv->rect.width += priv->rect.left - sel->r.left;
+ priv->rect.left = sel->r.left;
ret = ov6650_reg_write(client, REG_HSTOP,
- (rect.left + rect.width) >> 1);
+ (sel->r.left + sel->r.width) >> 1);
}
if (!ret) {
- priv->rect.width = rect.width;
- ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1);
+ priv->rect.width = sel->r.width;
+ ret = ov6650_reg_write(client, REG_VSTRT, sel->r.top >> 1);
}
if (!ret) {
- priv->rect.top = rect.top;
+ priv->rect.height += priv->rect.top - sel->r.top;
+ priv->rect.top = sel->r.top;
ret = ov6650_reg_write(client, REG_VSTOP,
- (rect.top + rect.height) >> 1);
+ (sel->r.top + sel->r.height) >> 1);
}
if (!ret)
- priv->rect.height = rect.height;
+ priv->rect.height = sel->r.height;
return ret;
}
@@ -614,7 +615,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
return -EINVAL;
}
- priv->code = code;
if (code == MEDIA_BUS_FMT_Y8_1X8 ||
code == MEDIA_BUS_FMT_SBGGR8_1X8) {
@@ -640,7 +640,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
dev_dbg(&client->dev, "max resolution: CIF\n");
coma_mask |= COMA_QCIF;
}
- priv->half_scale = half_scale;
clkrc = CLKRC_12MHz;
mclk = 12000000;
@@ -658,8 +657,13 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
if (!ret)
ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
- if (!ret)
+ if (!ret) {
+ priv->half_scale = half_scale;
+
ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
+ }
+ if (!ret)
+ priv->code = code;
if (!ret) {
mf->colorspace = priv->colorspace;
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 7158c31..4eae5f2 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -896,6 +896,7 @@ static int ov772x_power_on(struct ov772x_priv *priv)
GPIOD_OUT_LOW);
if (IS_ERR(priv->rstb_gpio)) {
dev_info(&client->dev, "Unable to get GPIO \"reset\"");
+ clk_disable_unprepare(priv->clk);
return PTR_ERR(priv->rstb_gpio);
}
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index 8a6a7a5..7804013 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -510,7 +510,7 @@ static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl)
int ret;
u8 val = 0;
- if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ if (!pm_runtime_get_if_in_use(&client->dev))
return 0;
switch (ctrl->id) {
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 1236683..4731e1c 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -3108,19 +3108,23 @@ static int smiapp_probe(struct i2c_client *client,
if (rval < 0)
goto out_media_entity_cleanup;
- rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd);
- if (rval < 0)
- goto out_media_entity_cleanup;
-
pm_runtime_set_active(&client->dev);
pm_runtime_get_noresume(&client->dev);
pm_runtime_enable(&client->dev);
+
+ rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd);
+ if (rval < 0)
+ goto out_disable_runtime_pm;
+
pm_runtime_set_autosuspend_delay(&client->dev, 1000);
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_put_autosuspend(&client->dev);
return 0;
+out_disable_runtime_pm:
+ pm_runtime_disable(&client->dev);
+
out_media_entity_cleanup:
media_entity_cleanup(&sensor->src->sd.entity);
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index 0c389a3f..e64f909 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -1252,7 +1252,7 @@ static void cx18_cancel_out_work_orders(struct cx18 *cx)
{
int i;
for (i = 0; i < CX18_MAX_STREAMS; i++)
- if (&cx->streams[i].video_dev)
+ if (cx->streams[i].video_dev.v4l2_dev)
cancel_work_sync(&cx->streams[i].out_work_order);
}
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index 7b113ba..248fb3b 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1312,7 +1312,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
core = cx88_core_get(dev->pci);
if (!core) {
err = -EINVAL;
- goto fail_free;
+ goto fail_disable;
}
dev->core = core;
@@ -1358,7 +1358,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
cc->step, cc->default_value);
if (!vc) {
err = core->audio_hdl.error;
- goto fail_core;
+ goto fail_irq;
}
vc->priv = (void *)cc;
}
@@ -1372,7 +1372,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
cc->step, cc->default_value);
if (!vc) {
err = core->video_hdl.error;
- goto fail_core;
+ goto fail_irq;
}
vc->priv = (void *)cc;
if (vc->id == V4L2_CID_CHROMA_AGC)
@@ -1535,11 +1535,14 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
fail_unreg:
cx8800_unregister_video(dev);
- free_irq(pci_dev->irq, dev);
mutex_unlock(&core->lock);
+fail_irq:
+ free_irq(pci_dev->irq, dev);
fail_core:
core->v4ldev = NULL;
cx88_core_put(core, dev->pci);
+fail_disable:
+ pci_disable_device(pci_dev);
fail_free:
kfree(dev);
return err;
diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c
index 44936d6..1380474 100644
--- a/drivers/media/pci/ivtv/ivtv-yuv.c
+++ b/drivers/media/pci/ivtv/ivtv-yuv.c
@@ -935,7 +935,7 @@ static void ivtv_yuv_init(struct ivtv *itv)
}
/* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
- yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN);
+ yi->blanking_ptr = kzalloc(720 * 16, GFP_ATOMIC|__GFP_NOWARN);
if (yi->blanking_ptr) {
yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
} else {
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index 8001d3e..db2a7ad1 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1460,7 +1460,7 @@ static int meye_mmap(struct file *file, struct vm_area_struct *vma)
unsigned long page, pos;
mutex_lock(&meye.lock);
- if (size > gbuffers * gbufsize) {
+ if (size > gbuffers * gbufsize || offset > gbuffers * gbufsize - size) {
mutex_unlock(&meye.lock);
return -EINVAL;
}
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 32bfe00..9a735a2 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -152,6 +152,18 @@
In TI Technical Reference Manual this module is referred as
Camera Interface Subsystem (CAMSS).
+config VIDEO_V4L2_VIDEOBUF2_CORE
+ tristate "V4L2 Videobuf2 Core"
+ depends on VIDEO_V4L2
+ select VIDEOBUF2_CORE
+ help
+ This enables videobuf2_core and videobuf2_v4l2
+ framework.
+
+ V4L2 videobuf2 core is needed by default for video.
+ So this will enable v4l2 module dependencies
+ for video.
+
endif # V4L_PLATFORM_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index b05738a..809320d 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1848,6 +1848,10 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
return -ENODATA;
+ /* if trying to set the same std then nothing to do */
+ if (vpfe_standards[vpfe->std_index].std_id == std_id)
+ return 0;
+
/* If streaming is started, return error */
if (vb2_is_busy(&vpfe->buffer_queue)) {
vpfe_err(vpfe, "%s device busy\n", __func__);
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index d89e145..1fd0782 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -1895,6 +1895,8 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
struct vb2_queue *q = &isc->vb2_vidq;
int ret;
+ INIT_WORK(&isc->awb_work, isc_awb_work);
+
ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
if (ret < 0) {
v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
@@ -1948,8 +1950,6 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
return ret;
}
- INIT_WORK(&isc->awb_work, isc_awb_work);
-
/* Register video device */
strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
vdev->release = video_device_release_empty;
@@ -2062,8 +2062,11 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
break;
}
- subdev_entity->asd = devm_kzalloc(dev,
- sizeof(*subdev_entity->asd), GFP_KERNEL);
+ /* asd will be freed by the subsystem once it's added to the
+ * notifier list
+ */
+ subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd),
+ GFP_KERNEL);
if (!subdev_entity->asd) {
of_node_put(rem);
ret = -ENOMEM;
@@ -2209,6 +2212,7 @@ static int atmel_isc_probe(struct platform_device *pdev)
&subdev_entity->notifier);
if (ret) {
dev_err(dev, "fail to register async notifier\n");
+ kfree(subdev_entity->asd);
goto cleanup_subdev;
}
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 4b0220f..fccc771 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -17,6 +17,7 @@
#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/genalloc.h>
+#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
@@ -2101,17 +2102,6 @@ int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
return coda_queue_init(priv, dst_vq);
}
-static int coda_next_free_instance(struct coda_dev *dev)
-{
- int idx = ffz(dev->instance_mask);
-
- if ((idx < 0) ||
- (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
- return -EBUSY;
-
- return idx;
-}
-
/*
* File operations
*/
@@ -2120,7 +2110,8 @@ static int coda_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct coda_dev *dev = video_get_drvdata(vdev);
- struct coda_ctx *ctx = NULL;
+ struct coda_ctx *ctx;
+ unsigned int max = ~0;
char *name;
int ret;
int idx;
@@ -2129,12 +2120,13 @@ static int coda_open(struct file *file)
if (!ctx)
return -ENOMEM;
- idx = coda_next_free_instance(dev);
+ if (dev->devtype->product == CODA_DX6)
+ max = CODADX6_MAX_INSTANCES - 1;
+ idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL);
if (idx < 0) {
ret = idx;
goto err_coda_max;
}
- set_bit(idx, &dev->instance_mask);
name = kasprintf(GFP_KERNEL, "context%d", idx);
if (!name) {
@@ -2243,8 +2235,8 @@ static int coda_open(struct file *file)
err_pm_get:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
- clear_bit(ctx->idx, &dev->instance_mask);
err_coda_name_init:
+ ida_free(&dev->ida, ctx->idx);
err_coda_max:
kfree(ctx);
return ret;
@@ -2286,7 +2278,7 @@ static int coda_release(struct file *file)
pm_runtime_put_sync(&dev->plat_dev->dev);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
- clear_bit(ctx->idx, &dev->instance_mask);
+ ida_free(&dev->ida, ctx->idx);
if (ctx->ops->release)
ctx->ops->release(ctx);
debugfs_remove_recursive(ctx->debugfs_entry);
@@ -2747,6 +2739,7 @@ static int coda_probe(struct platform_device *pdev)
mutex_init(&dev->dev_mutex);
mutex_init(&dev->coda_mutex);
+ ida_init(&dev->ida);
dev->debugfs_root = debugfs_create_dir("coda", NULL);
if (!dev->debugfs_root)
@@ -2834,6 +2827,7 @@ static int coda_remove(struct platform_device *pdev)
coda_free_aux_buf(dev, &dev->tempbuf);
coda_free_aux_buf(dev, &dev->workbuf);
debugfs_remove_recursive(dev->debugfs_root);
+ ida_destroy(&dev->ida);
return 0;
}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 2469ca1d..8df02c3 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -16,6 +16,7 @@
#define __CODA_H__
#include <linux/debugfs.h>
+#include <linux/idr.h>
#include <linux/irqreturn.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>
@@ -95,7 +96,7 @@ struct coda_dev {
struct workqueue_struct *workqueue;
struct v4l2_m2m_dev *m2m_dev;
struct list_head instances;
- unsigned long instance_mask;
+ struct ida ida;
struct dentry *debugfs_root;
};
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index f924e76..340f821 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -1100,7 +1100,8 @@ static int isif_probe(struct platform_device *pdev)
while (i >= 0) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- release_mem_region(res->start, resource_size(res));
+ if (res)
+ release_mem_region(res->start, resource_size(res));
i--;
}
vpfe_unregister_ccdc_device(&isif_hw_dev);
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index b0eb3d8..6f82693 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -521,7 +521,7 @@ vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
else if (v_scale == 4)
layer_info->v_zoom = ZOOM_X4;
if (v_exp)
- layer_info->h_exp = V_EXP_6_OVER_5;
+ layer_info->v_exp = V_EXP_6_OVER_5;
} else {
/* no scaling, only cropping. Set display area to crop area */
cfg->ysize = expected_ysize;
diff --git a/drivers/media/platform/msm/cvp/cvp_hfi_helper.h b/drivers/media/platform/msm/cvp/cvp_hfi_helper.h
index 84ea89b..f8fe274 100644
--- a/drivers/media/platform/msm/cvp/cvp_hfi_helper.h
+++ b/drivers/media/platform/msm/cvp/cvp_hfi_helper.h
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __H_CVP_HFI_HELPER_H__
#define __H_CVP_HFI_HELPER_H__
+#include <linux/dma-mapping.h>
+
#define HFI_COMMON_BASE (0)
#define HFI_DOMAIN_BASE_COMMON (HFI_COMMON_BASE + 0)
#define HFI_DOMAIN_BASE_CVP (HFI_COMMON_BASE + 0x04000000)
@@ -495,4 +497,38 @@ struct cvp_hfi_cmd_sys_test_ssr_packet {
u32 trigger_type;
};
+struct cvp_buf_type {
+ s32 fd;
+ u32 size;
+ u32 offset;
+ u32 flags;
+ union {
+ struct dma_buf *dbuf;
+ struct {
+ u32 reserved1;
+ u32 reserved2;
+ };
+ } __packed;
+} __packed;
+
+struct cvp_hfi_msg_dme_pkt {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+ struct cvp_hfi_client client_data;
+ u32 stream_idx;
+ u32 skipmv;
+ struct cvp_buf_type srcbuffer;
+ struct cvp_buf_type srcctxbuffer;
+ struct cvp_buf_type refbuffer;
+ struct cvp_buf_type refctxbuffer;
+ struct cvp_buf_type statsbuffer;
+ u32 fullreswidth;
+ u32 fullresheight;
+ u32 processwidth;
+ u32 processheight;
+ u32 confidence;
+} __packed;
+
#endif
diff --git a/drivers/media/platform/msm/cvp/hfi_response_handler.c b/drivers/media/platform/msm/cvp/hfi_response_handler.c
index fc4b075..e6ef2064 100644
--- a/drivers/media/platform/msm/cvp/hfi_response_handler.c
+++ b/drivers/media/platform/msm/cvp/hfi_response_handler.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/bitops.h>
@@ -443,6 +443,36 @@ static struct msm_cvp_inst *cvp_get_inst_from_id(struct msm_cvp_core *core,
}
+static int __dme_output_cache_operation(struct cvp_hfi_msg_session_hdr *pkt)
+{
+ struct cvp_hfi_msg_dme_pkt *dme_pkt;
+ int rc;
+
+ if (!pkt) {
+ dprintk(CVP_ERR, "%s: invalid param\n", __func__);
+ return -EINVAL;
+ } else if (pkt->size < get_msg_size()) {
+ dprintk(CVP_ERR, "%s: bad_pkt_size %d\n", __func__, pkt->size);
+ return -E2BIG;
+ }
+
+ dme_pkt = (struct cvp_hfi_msg_dme_pkt *)pkt;
+ rc = dma_buf_begin_cpu_access_partial(dme_pkt->statsbuffer.dbuf,
+ DMA_TO_DEVICE, 0,
+ dme_pkt->statsbuffer.size);
+ if (rc) {
+ dprintk(CVP_ERR, "%s: begin_cpu_access failed\n", __func__);
+ return rc;
+ }
+ rc = dma_buf_end_cpu_access_partial(dme_pkt->statsbuffer.dbuf,
+ DMA_FROM_DEVICE, 0,
+ dme_pkt->statsbuffer.size);
+ if (rc)
+ dprintk(CVP_ERR, "%s: end_cpu_access failed\n", __func__);
+
+ return rc;
+}
+
static int hfi_process_session_cvp_msg(u32 device_id,
struct cvp_hfi_msg_session_hdr *pkt,
struct msm_cvp_cb_info *info)
@@ -478,6 +508,11 @@ static int hfi_process_session_cvp_msg(u32 device_id,
kdata1 = pkt->client_data.kdata1;
kdata2 = pkt->client_data.kdata2;
ktid = ((u64)kdata2 << 32) | kdata1;
+
+
+ if (pkt->packet_type == HFI_MSG_SESSION_CVP_DME)
+ __dme_output_cache_operation(pkt);
+
msm_cvp_unmap_buf_cpu(inst, ktid);
return _deprecated_hfi_msg_process(device_id,
diff --git a/drivers/media/platform/msm/cvp/msm_cvp.c b/drivers/media/platform/msm/cvp/msm_cvp.c
index 1ac9697a..e2a3cb6 100644
--- a/drivers/media/platform/msm/cvp/msm_cvp.c
+++ b/drivers/media/platform/msm/cvp/msm_cvp.c
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include "msm_cvp.h"
#include "cvp_hfi.h"
#include <synx_api.h>
#include "cvp_core_hfi.h"
+#include "cvp_hfi_helper.h"
struct cvp_power_level {
unsigned long core_sum;
@@ -717,8 +718,7 @@ static int msm_cvp_map_user_persist(struct msm_cvp_inst *inst,
return 0;
for (i = 0; i < buf_num; i++) {
- buf_ptr = (struct cvp_buf_desc *)
- &in_pkt->pkt_data[offset];
+ buf_ptr = (struct cvp_buf_desc *)&in_pkt->pkt_data[offset];
offset += sizeof(*new_buf) >> 2;
new_buf = (struct cvp_buf_type *)buf_ptr;
@@ -904,6 +904,7 @@ static int msm_cvp_session_process_hfi(
unsigned int offset, buf_num, signal;
struct cvp_session_queue *sq;
struct msm_cvp_inst *s;
+ unsigned int max_buf_num;
if (!inst || !inst->core || !in_pkt) {
dprintk(CVP_ERR, "%s: invalid params\n", __func__);
@@ -948,6 +949,16 @@ static int msm_cvp_session_process_hfi(
buf_num = in_buf_num;
}
+ max_buf_num = sizeof(struct cvp_kmd_hfi_packet)
+ / sizeof(struct cvp_buf_type);
+
+ if (buf_num > max_buf_num)
+ return -EINVAL;
+
+ if ((offset + buf_num * sizeof(struct cvp_buf_type)) >
+ sizeof(struct cvp_kmd_hfi_packet))
+ return -EINVAL;
+
if (in_pkt->pkt_data[1] == HFI_CMD_SESSION_CVP_SET_PERSIST_BUFFERS)
rc = msm_cvp_map_user_persist(inst, in_pkt, offset, buf_num);
else
diff --git a/drivers/media/platform/msm/cvp/msm_cvp_internal.h b/drivers/media/platform/msm/cvp/msm_cvp_internal.h
index b65fc30..0a27a82 100644
--- a/drivers/media/platform/msm/cvp/msm_cvp_internal.h
+++ b/drivers/media/platform/msm/cvp/msm_cvp_internal.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _MSM_CVP_INTERNAL_H_
@@ -179,20 +179,6 @@ enum profiling_points {
MAX_PROFILING_POINTS,
};
-struct cvp_buf_type {
- s32 fd;
- u32 size;
- u32 offset;
- u32 flags;
- union {
- struct dma_buf *dbuf;
- struct {
- u32 reserved1;
- u32 reserved2;
- };
- };
-};
-
struct cvp_clock_data {
int buffer_counter;
int load;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index b58333b..c0d4a50 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
@@ -17,6 +17,7 @@
#include <linux/sched/signal.h>
#include <linux/debugfs.h>
#include <linux/err.h>
+#include <soc/qcom/qtee_shmbridge.h>
#define SDMX_MAJOR_VERSION_MATCH (8)
@@ -507,7 +508,7 @@ static ssize_t mpq_sdmx_log_level_write(struct file *fp,
if (level < SDMX_LOG_NO_PRINT || level > SDMX_LOG_VERBOSE)
return -EINVAL;
- mutex_lock(&mpq_demux->mutex);
+ mutex_lock_interruptible(&mpq_demux->mutex);
mpq_demux->sdmx_log_level = level;
if (mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) {
ret = sdmx_set_log_level(mpq_demux->sdmx_session_handle,
@@ -1080,7 +1081,7 @@ int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie)
struct mpq_streambuffer *stream_buffer;
int ret;
- mutex_lock(&mpq_demux->mutex);
+ mutex_lock_interruptible(&mpq_demux->mutex);
mpq_feed = feed->priv;
feed_data = &mpq_feed->video_info;
@@ -1107,6 +1108,117 @@ int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie)
return -EINVAL;
}
+static int mpq_sdmx_destroy_shm_bridge_callback(struct dma_buf *dmabuf,
+ void *dtor_data)
+{
+ int ret = 0;
+ uint64_t handle = (uint64_t)dtor_data;
+
+ if (!dmabuf) {
+ MPQ_DVB_DBG_PRINT("dmabuf NULL\n");
+ return -EINVAL;
+ }
+ MPQ_DVB_DBG_PRINT("to destroy shm bridge %lld\n", handle);
+ ret = qtee_shmbridge_deregister(handle);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT("failed to destroy shm bridge %lld\n",
+ handle);
+ return ret;
+ }
+ dma_buf_set_destructor(dmabuf, NULL, NULL);
+ return ret;
+}
+
+static int mpq_sdmx_create_shm_bridge(struct dma_buf *dmabuf,
+ struct sg_table *sgt)
+{
+ int ret = 0, i;
+ phys_addr_t phys;
+ size_t size = 0;
+ uint64_t handle = 0;
+ int tz_perm = PERM_READ|PERM_WRITE;
+ unsigned long dma_buf_flags = 0;
+ uint32_t *vmid_list;
+ uint32_t *perms_list;
+ uint32_t nelems;
+ struct scatterlist *sg;
+
+ ret = dma_buf_get_flags(dmabuf, &dma_buf_flags);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: failed to get dmabuf flag\n",
+ __func__);
+ return ret;
+ }
+
+ if (!(dma_buf_flags & ION_FLAG_SECURE)) {
+ MPQ_DVB_ERR_PRINT("Not a secure buffer\n");
+ return 0;
+ }
+
+ nelems = ion_get_flags_num_vm_elems(dma_buf_flags);
+
+ vmid_list = kcalloc(nelems, sizeof(*vmid_list), GFP_KERNEL);
+ if (!vmid_list) {
+ ret = -ENOMEM;
+ MPQ_DVB_ERR_PRINT("%s: failed at %u with ret = %d\n",
+ __func__, __LINE__, ret);
+ goto exit;
+ }
+
+ ret = ion_populate_vm_list(dma_buf_flags, vmid_list, nelems);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: failed at %u with ret = %d\n",
+ __func__, __LINE__, ret);
+ goto exit_free_vmid_list;
+ }
+ perms_list = kcalloc(nelems, sizeof(*perms_list), GFP_KERNEL);
+ if (!perms_list) {
+ ret = -ENOMEM;
+ MPQ_DVB_ERR_PRINT("%s: failed at %u with ret = %d\n",
+ __func__, __LINE__, ret);
+ goto exit_free_vmid_list;
+ }
+
+ for (i = 0; i < nelems; i++)
+ perms_list[i] = msm_secure_get_vmid_perms(vmid_list[i]);
+
+
+ sg = sgt->sgl;
+ for (i = 0; i < sgt->nents; i++) {
+ phys = sg_phys(sg);
+ size = sg->length;
+
+ ret = qtee_shmbridge_query(phys);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("shm bridge exists\n");
+ goto exit_free_perms_list;
+ }
+
+ ret = qtee_shmbridge_register(phys, size, vmid_list,
+ perms_list, nelems,
+ tz_perm, &handle);
+ if (ret && ret != -EEXIST) {
+ MPQ_DVB_ERR_PRINT("shm register failed: ret: %d\n",
+ ret);
+ goto exit_free_perms_list;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: created shm bridge %lld\n",
+ __func__, handle);
+ dma_buf_set_destructor(dmabuf,
+ mpq_sdmx_destroy_shm_bridge_callback,
+ (void *)handle);
+ sg = sg_next(sg);
+ }
+
+exit_free_perms_list:
+ kfree(perms_list);
+exit_free_vmid_list:
+ kfree(vmid_list);
+exit:
+ return ret;
+}
+
/**
* Handles the details of internal decoder buffer allocation via ION.
* Internal helper function.
@@ -1183,6 +1295,13 @@ static int mpq_dmx_init_internal_buffers(
feed_data->buffer_desc.desc[0].size = size;
feed_data->buffer_desc.desc[0].read_ptr = 0;
feed_data->buffer_desc.desc[0].write_ptr = 0;
+
+ ret = mpq_sdmx_create_shm_bridge(dbuf->dmabuf, dbuf->sgt);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s mpq_sdmx_create_shm_bridge failed\n");
+ return ret;
+ }
+
return 0;
err_detach:
@@ -1679,48 +1798,67 @@ struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed)
static int mpq_sdmx_alloc_data_buf(struct mpq_feed *mpq_feed, size_t size)
{
- struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
int ret = 0;
struct sdmx_buff_descriptor *desc = &mpq_feed->data_desc;
+ struct qtee_shm *shminfo = NULL;
- desc->virt_base = dma_alloc_coherent(&mpq_demux->pdev->dev,
- size, &desc->phys_base,
- GFP_KERNEL);
+ shminfo = vmalloc(sizeof(struct qtee_shm));
+ if (!shminfo) {
+ MPQ_DVB_ERR_PRINT("%s: shminfo alloc failed\n");
+ return -ENOMEM;
+ }
+
+ qtee_shmbridge_allocate_shm(size, shminfo);
+ desc->size = size;
+ desc->phys_base = shminfo->paddr;
+ desc->virt_base = shminfo->vaddr;
+ desc->user = (void *)shminfo;
+
if (IS_ERR_OR_NULL(desc->virt_base)) {
ret = PTR_ERR(desc->virt_base);
- MPQ_DVB_ERR_PRINT("%s: dma_alloc_coherent failed ret = %d\n",
- __func__, ret);
+ MPQ_DVB_ERR_PRINT("%s: qtee_shmbridge_allocate_shm failed\n",
+ __func__);
return ret;
}
- desc->size = size;
+
dvb_ringbuffer_init(&mpq_feed->sdmx_buf, desc->virt_base, size);
+ mpq_feed->sdmx_dma_buff.va = desc->virt_base;
return 0;
}
static int mpq_sdmx_free_data_buf(struct mpq_feed *mpq_feed)
{
- struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
struct sdmx_buff_descriptor *desc = &mpq_feed->data_desc;
- dma_free_coherent(&mpq_demux->pdev->dev,
- desc->size, desc->virt_base,
- desc->phys_base);
+ qtee_shmbridge_free_shm((struct qtee_shm *) desc->user);
+ vfree(desc->user);
+ MPQ_DVB_DBG_PRINT("%s: = qtee_shmbridge_free\n", __func__);
memset(desc, 0, sizeof(struct sdmx_buff_descriptor));
return 0;
}
-static int mpq_sdmx_init_metadata_buffer(struct mpq_demux *mpq_demux,
- struct mpq_feed *feed, struct sdmx_buff_descr *metadata_buff_desc)
-{
+static int mpq_sdmx_init_metadata_buffer(struct mpq_demux *mpq_demux,
+ struct mpq_feed *feed,
+ struct sdmx_buff_descr *metadata_buff_desc)
+{
int ret = 0;
struct sdmx_buff_descriptor *desc = &feed->metadata_desc;
+ struct qtee_shm *shminfo = NULL;
- desc->virt_base = dma_alloc_coherent(&mpq_demux->pdev->dev,
- SDMX_METADATA_BUFFER_SIZE,
- &desc->phys_base,
- GFP_KERNEL);
+ shminfo = vmalloc(sizeof(struct qtee_shm));
+
+ if (!shminfo) {
+ MPQ_DVB_ERR_PRINT("%s: shminfo alloc failed\n");
+ return -ENOMEM;
+ }
+ qtee_shmbridge_allocate_shm(SDMX_METADATA_BUFFER_SIZE, shminfo);
+
+ desc->phys_base = shminfo->paddr;
+ desc->virt_base = shminfo->vaddr;
+ desc->user = (void *)shminfo;
+
if (IS_ERR_OR_NULL(desc->virt_base)) {
ret = PTR_ERR(desc->virt_base);
MPQ_DVB_ERR_PRINT(
@@ -1740,18 +1878,16 @@ static int mpq_sdmx_init_metadata_buffer(struct mpq_demux *mpq_demux,
static int mpq_sdmx_terminate_metadata_buffer(struct mpq_feed *mpq_feed)
{
-
- struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
-
struct sdmx_buff_descriptor *desc = &mpq_feed->metadata_desc;
- dma_free_coherent(&mpq_demux->pdev->dev,
- desc->size, desc->virt_base,
- desc->phys_base);
+ qtee_shmbridge_free_shm((struct qtee_shm *) desc->user);
+ vfree(desc->user);
+ MPQ_DVB_DBG_PRINT("%s: = qtee_shmbridge_free\n", __func__);
memset(desc, 0, sizeof(struct sdmx_buff_descriptor));
return 0;
}
+
int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed)
{
int ret = 0;
@@ -1931,7 +2067,7 @@ static int mpq_dmx_decoder_fullness_check(
}
if (lock_feed) {
- mutex_lock(&mpq_demux->mutex);
+ mutex_trylock(&mpq_demux->mutex);
} else if (!mutex_is_locked(&mpq_demux->mutex)) {
MPQ_DVB_ERR_PRINT(
"%s: Mutex should have been locked\n",
@@ -1970,7 +2106,7 @@ static int mpq_dmx_decoder_fullness_check(
if (!signal_pending(current)) {
mutex_unlock(&mpq_demux->mutex);
schedule();
- mutex_lock(&mpq_demux->mutex);
+ mutex_trylock(&mpq_demux->mutex);
continue;
}
@@ -3557,6 +3693,12 @@ static int mpq_sdmx_get_buffer_chunks(struct mpq_demux *mpq_demux,
actual_buff_size -= chunk_size;
}
+ ret = mpq_sdmx_create_shm_bridge(buff_info->dmabuf, buff_info->sgt);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s mpq_sdmx_create_shm_bridge failed\n");
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/media/platform/msm/npu/npu_dev.c b/drivers/media/platform/msm/npu/npu_dev.c
index 4b8866f..beb17951 100644
--- a/drivers/media/platform/msm/npu/npu_dev.c
+++ b/drivers/media/platform/msm/npu/npu_dev.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
/* -------------------------------------------------------------------------
@@ -1738,13 +1738,70 @@ int npu_set_bw(struct npu_device *npu_dev, int new_ib, int new_ab)
return ret;
}
+#define NPU_FMAX_THRESHOLD 1000000
+static int npu_adjust_max_power_level(struct npu_device *npu_dev)
+{
+ struct npu_pwrctrl *pwr = &npu_dev->pwrctrl;
+ uint32_t fmax_reg_value, fmax, fmax_pwrlvl;
+ struct npu_pwrlevel *level;
+ int i, j;
+
+ if (!npu_dev->qfprom_io.base)
+ return 0;
+
+ /* search for cal clock index */
+ for (j = 0; j < npu_dev->core_clk_num; j++) {
+ if (!strcmp(npu_dev->core_clks[j].clk_name,
+ "cal_hm0_clk"))
+ break;
+ }
+
+ if (j == npu_dev->core_clk_num) {
+ NPU_WARN("can't find clock cal_hm0_clk\n");
+ return 0;
+ }
+
+ /* Read FMAX info if available */
+ fmax_reg_value = (npu_qfprom_reg_read(npu_dev,
+ QFPROM_FMAX_REG_OFFSET) & QFPROM_FMAX_BITS_MASK) >>
+ QFPROM_FMAX_BITS_SHIFT;
+ NPU_DBG("fmax_reg_value %x\n", fmax_reg_value);
+
+ if (fmax_reg_value == 0)
+ return 0;
+
+ /* calculate fmax and truncate to MHz */
+ fmax = fmax_reg_value * 19200000 / 2;
+
+ /* search for the nearest power level */
+ for (i = 0; i < pwr->num_pwrlevels; i++) {
+ level = &pwr->pwrlevels[i];
+
+ if (level->clk_freq[j] >= fmax ||
+ ((fmax - level->clk_freq[j]) < NPU_FMAX_THRESHOLD)) {
+ fmax_pwrlvl = level->pwr_level;
+ break;
+ }
+ }
+
+ if (i == pwr->num_pwrlevels)
+ return 0;
+
+ if (fmax_pwrlvl < pwr->max_pwrlevel) {
+ pwr->max_pwrlevel = fmax_pwrlvl;
+ NPU_INFO("Adjust max_pwrlevel to %d[%x]\n", fmax_pwrlvl,
+ fmax_reg_value);
+ }
+
+ return 0;
+}
+
static int npu_of_parse_pwrlevels(struct npu_device *npu_dev,
struct device_node *node)
{
struct npu_pwrctrl *pwr = &npu_dev->pwrctrl;
struct device_node *child;
uint32_t init_level_index = 0, init_power_level;
- uint32_t fmax, fmax_pwrlvl;
pwr->num_pwrlevels = 0;
pwr->min_pwrlevel = NPU_PWRLEVEL_TURBO_L1;
@@ -1805,28 +1862,7 @@ static int npu_of_parse_pwrlevels(struct npu_device *npu_dev,
}
}
- /* Read FMAX info if available */
- if (npu_dev->qfprom_io.base) {
- fmax = (npu_qfprom_reg_read(npu_dev,
- QFPROM_FMAX_REG_OFFSET) & QFPROM_FMAX_BITS_MASK) >>
- QFPROM_FMAX_BITS_SHIFT;
- NPU_DBG("fmax %x\n", fmax);
-
- switch (fmax) {
- case 0x34:
- fmax_pwrlvl = NPU_PWRLEVEL_SVS_L1;
- break;
- case 0x48:
- fmax_pwrlvl = NPU_PWRLEVEL_NOM;
- break;
- default:
- fmax_pwrlvl = pwr->max_pwrlevel;
- break;
- }
-
- if (fmax_pwrlvl < pwr->max_pwrlevel)
- pwr->max_pwrlevel = fmax_pwrlvl;
- }
+ npu_adjust_max_power_level(npu_dev);
of_property_read_u32(node, "initial-pwrlevel", &init_level_index);
NPU_DBG("initial-pwrlevel %d\n", init_level_index);
@@ -1860,6 +1896,8 @@ static int npu_pwrctrl_init(struct npu_device *npu_dev)
struct platform_device *p2dev;
struct npu_pwrctrl *pwr = &npu_dev->pwrctrl;
+ pwr->devbw_num = 0;
+
/* Power levels */
node = of_find_node_by_name(pdev->dev.of_node, "qcom,npu-pwrlevels");
@@ -1873,15 +1911,17 @@ static int npu_pwrctrl_init(struct npu_device *npu_dev)
return ret;
/* Parse Bandwidth Monitor */
- pwr->devbw_num = of_property_count_strings(pdev->dev.of_node,
+ ret = of_property_count_strings(pdev->dev.of_node,
"qcom,npubw-dev-names");
- if (pwr->devbw_num <= 0) {
+ if (ret <= 0) {
NPU_INFO("npubw-dev-names are not defined\n");
return 0;
- } else if (pwr->devbw_num > NPU_MAX_BW_DEVS) {
- NPU_ERR("number of devbw %d exceeds limit\n", pwr->devbw_num);
+ } else if (ret > NPU_MAX_BW_DEVS) {
+ NPU_ERR("number of devbw %d exceeds limit\n", ret);
return -EINVAL;
}
+ pwr->devbw_num = ret;
+ ret = 0;
for (i = 0; i < pwr->devbw_num; i++) {
node = of_parse_phandle(pdev->dev.of_node,
diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c
index dfb830f..43c8014 100644
--- a/drivers/media/platform/msm/npu/npu_mgr.c
+++ b/drivers/media/platform/msm/npu/npu_mgr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
/* -------------------------------------------------------------------------
@@ -424,7 +424,7 @@ static int disable_fw_nolock(struct npu_device *npu_dev)
msleep(500);
}
- if (!host_ctx->auto_pil_disable) {
+ if (!ret && !host_ctx->auto_pil_disable) {
ret = wait_for_completion_timeout(
&host_ctx->fw_shutdown_done, NW_RSC_TIMEOUT_MS);
if (!ret) {
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index b6e9e93..406ac67 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2397,7 +2397,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
pcdev->res = res;
pcdev->pdata = pdev->dev.platform_data;
- if (&pdev->dev.of_node && !pcdev->pdata) {
+ if (pdev->dev.of_node && !pcdev->pdata) {
err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd);
} else {
pcdev->platform_flags = pcdev->pdata->flags;
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 5b8350e..6006986 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -430,10 +430,11 @@ static const struct venus_resources msm8916_res = {
};
static const struct freq_tbl msm8996_freq_table[] = {
- { 1944000, 490000000 }, /* 4k UHD @ 60 */
- { 972000, 320000000 }, /* 4k UHD @ 30 */
- { 489600, 150000000 }, /* 1080p @ 60 */
- { 244800, 75000000 }, /* 1080p @ 30 */
+ { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */
+ { 972000, 520000000 }, /* 4k UHD @ 30 */
+ { 489600, 346666667 }, /* 1080p @ 60 */
+ { 244800, 150000000 }, /* 1080p @ 30 */
+ { 108000, 75000000 }, /* 720p @ 30 */
};
static const struct reg_val msm8996_reg_preset[] = {
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 1240855..fbcc67c 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -1484,6 +1484,7 @@ static int venus_suspend_3xx(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
struct device *dev = core->dev;
+ u32 ctrl_status;
bool val;
int ret;
@@ -1499,6 +1500,10 @@ static int venus_suspend_3xx(struct venus_core *core)
return -EINVAL;
}
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)
+ goto power_off;
+
/*
* Power collapse sequence for Venus 3xx and 4xx versions:
* 1. Check for ARM9 and video core to be idle by checking WFI bit
@@ -1523,6 +1528,7 @@ static int venus_suspend_3xx(struct venus_core *core)
if (ret)
return ret;
+power_off:
mutex_lock(&hdev->lock);
ret = venus_power_off(hdev);
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index dfbbbf0..e3972db 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -888,8 +888,7 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
unsigned int opb_sz = venus_helper_get_opb_size(inst);
vb = &vbuf->vb2_buf;
- vb->planes[0].bytesused =
- max_t(unsigned int, opb_sz, bytesused);
+ vb2_set_plane_payload(vb, 0, bytesused ? : opb_sz);
vb->planes[0].data_offset = data_offset;
vb->timestamp = timestamp_us * NSEC_PER_USEC;
vbuf->sequence = inst->sequence_cap++;
@@ -1116,9 +1115,6 @@ static const struct v4l2_file_operations vdec_fops = {
.unlocked_ioctl = video_ioctl2,
.poll = v4l2_m2m_fop_poll,
.mmap = v4l2_m2m_fop_mmap,
-#ifdef CONFIG_COMPAT
- .compat_ioctl32 = v4l2_compat_ioctl32,
-#endif
};
static int vdec_probe(struct platform_device *pdev)
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 41249d1..4208972 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -1220,9 +1220,6 @@ static const struct v4l2_file_operations venc_fops = {
.unlocked_ioctl = video_ioctl2,
.poll = v4l2_m2m_fop_poll,
.mmap = v4l2_m2m_fop_mmap,
-#ifdef CONFIG_COMPAT
- .compat_ioctl32 = v4l2_compat_ioctl32,
-#endif
};
static int venc_probe(struct platform_device *pdev)
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index e1085e3..485fa3f 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -174,7 +174,6 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags,
if (csi_id == -ENODEV) {
struct v4l2_subdev *sd;
- unsigned int i;
/*
* Make sure the source entity subdevice is registered as
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
index 81413ab..b677d01 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/rcar_drif.c
@@ -912,6 +912,7 @@ static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
{
struct rcar_drif_sdr *sdr = video_drvdata(file);
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
f->fmt.sdr.pixelformat = sdr->fmt->pixelformat;
f->fmt.sdr.buffersize = sdr->fmt->buffersize;
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
index 66b6409..40c4eef 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -651,8 +651,7 @@ static int bdisp_release(struct file *file)
dev_dbg(bdisp->dev, "%s\n", __func__);
- if (mutex_lock_interruptible(&bdisp->lock))
- return -ERESTARTSYS;
+ mutex_lock(&bdisp->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index 1d9c028..18d0b56 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -164,6 +164,9 @@ struct stm32_dcmi {
int errors_count;
int overrun_count;
int buffers_count;
+
+ /* Ensure DMA operations atomicity */
+ struct mutex dma_lock;
};
static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
@@ -314,6 +317,13 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
return ret;
}
+ /*
+ * Avoid call of dmaengine_terminate_all() between
+ * dmaengine_prep_slave_single() and dmaengine_submit()
+ * by locking the whole DMA submission sequence
+ */
+ mutex_lock(&dcmi->dma_lock);
+
/* Prepare a DMA transaction */
desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
buf->size,
@@ -322,6 +332,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
if (!desc) {
dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n",
__func__, &buf->paddr, buf->size);
+ mutex_unlock(&dcmi->dma_lock);
return -EINVAL;
}
@@ -333,9 +344,12 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
dcmi->dma_cookie = dmaengine_submit(desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
+ mutex_unlock(&dcmi->dma_lock);
return -ENXIO;
}
+ mutex_unlock(&dcmi->dma_lock);
+
dma_async_issue_pending(dcmi->dma_chan);
return 0;
@@ -570,9 +584,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
int ret;
ret = pm_runtime_get_sync(dcmi->dev);
- if (ret) {
- dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync\n",
- __func__);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync (%d)\n",
+ __func__, ret);
goto err_release_buffers;
}
@@ -717,7 +731,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&dcmi->irqlock);
/* Stop all pending DMA operations */
+ mutex_lock(&dcmi->dma_lock);
dmaengine_terminate_all(dcmi->dma_chan);
+ mutex_unlock(&dcmi->dma_lock);
pm_runtime_put(dcmi->dev);
@@ -1719,6 +1735,7 @@ static int dcmi_probe(struct platform_device *pdev)
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
+ mutex_init(&dcmi->dma_lock);
init_completion(&dcmi->complete);
INIT_LIST_HEAD(&dcmi->buffers);
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h
index 7e61150..f29074c 100644
--- a/drivers/media/platform/ti-vpe/vpdma.h
+++ b/drivers/media/platform/ti-vpe/vpdma.h
@@ -60,6 +60,7 @@ struct vpdma_data_format {
* line stride of source and dest
* buffers should be 16 byte aligned
*/
+#define VPDMA_MAX_STRIDE 65520 /* Max line stride 16 byte aligned */
#define VPDMA_DTD_DESC_SIZE 32 /* 8 words */
#define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index d70871d0..a285b9d 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -352,20 +352,25 @@ enum {
};
/* find our format description corresponding to the passed v4l2_format */
-static struct vpe_fmt *find_format(struct v4l2_format *f)
+static struct vpe_fmt *__find_format(u32 fourcc)
{
struct vpe_fmt *fmt;
unsigned int k;
for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) {
fmt = &vpe_formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
+ if (fmt->fourcc == fourcc)
return fmt;
}
return NULL;
}
+static struct vpe_fmt *find_format(struct v4l2_format *f)
+{
+ return __find_format(f->fmt.pix.pixelformat);
+}
+
/*
* there is one vpe_dev structure in the driver, it is shared by
* all instances.
@@ -1027,11 +1032,14 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
dma_addr_t dma_addr;
u32 flags = 0;
u32 offset = 0;
+ u32 stride;
if (port == VPE_PORT_MV_OUT) {
vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
dma_addr = ctx->mv_buf_dma[mv_buf_selector];
q_data = &ctx->q_data[Q_DATA_SRC];
+ stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+ VPDMA_STRIDE_ALIGN);
} else {
/* to incorporate interleaved formats */
int plane = fmt->coplanar ? p_data->vb_part : 0;
@@ -1058,6 +1066,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
}
/* Apply the offset */
dma_addr += offset;
+ stride = q_data->bytesperline[VPE_LUMA];
}
if (q_data->flags & Q_DATA_FRAME_1D)
@@ -1069,7 +1078,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
MAX_W, MAX_H);
vpdma_add_out_dtd(&ctx->desc_list, q_data->width,
- q_data->bytesperline[VPE_LUMA], &q_data->c_rect,
+ stride, &q_data->c_rect,
vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1,
MAX_OUT_HEIGHT_REG1, p_data->channel, flags);
}
@@ -1088,10 +1097,13 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
dma_addr_t dma_addr;
u32 flags = 0;
u32 offset = 0;
+ u32 stride;
if (port == VPE_PORT_MV_IN) {
vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+ stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+ VPDMA_STRIDE_ALIGN);
} else {
/* to incorporate interleaved formats */
int plane = fmt->coplanar ? p_data->vb_part : 0;
@@ -1118,6 +1130,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
}
/* Apply the offset */
dma_addr += offset;
+ stride = q_data->bytesperline[VPE_LUMA];
if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
/*
@@ -1153,10 +1166,10 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12)
frame_height /= 2;
- vpdma_add_in_dtd(&ctx->desc_list, q_data->width,
- q_data->bytesperline[VPE_LUMA], &q_data->c_rect,
- vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width,
- frame_height, 0, 0);
+ vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride,
+ &q_data->c_rect, vpdma_fmt, dma_addr,
+ p_data->channel, field, flags, frame_width,
+ frame_height, 0, 0);
}
/*
@@ -1405,9 +1418,6 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
/* the previous dst mv buffer becomes the next src mv buffer */
ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector;
- if (ctx->aborting)
- goto finished;
-
s_vb = ctx->src_vbs[0];
d_vb = ctx->dst_vb;
@@ -1418,6 +1428,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
d_vb->timecode = s_vb->timecode;
d_vb->sequence = ctx->sequence;
+ s_vb->sequence = ctx->sequence;
d_q_data = &ctx->q_data[Q_DATA_DST];
if (d_q_data->flags & Q_IS_INTERLACED) {
@@ -1471,6 +1482,9 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
ctx->src_vbs[0] = NULL;
ctx->dst_vb = NULL;
+ if (ctx->aborting)
+ goto finished;
+
ctx->bufs_completed++;
if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) {
device_run(ctx);
@@ -1583,9 +1597,9 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
unsigned int stride = 0;
if (!fmt || !(fmt->types & type)) {
- vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
+ vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
pix->pixelformat);
- return -EINVAL;
+ fmt = __find_format(V4L2_PIX_FMT_YUYV);
}
if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE
@@ -1632,7 +1646,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
&pix->height, MIN_H, MAX_H, H_ALIGN,
S_ALIGN);
- if (!pix->num_planes)
+ if (!pix->num_planes || pix->num_planes > 2)
pix->num_planes = fmt->coplanar ? 2 : 1;
else if (pix->num_planes > 1 && !fmt->coplanar)
pix->num_planes = 1;
@@ -1671,6 +1685,10 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
if (stride > plane_fmt->bytesperline)
plane_fmt->bytesperline = stride;
+ plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline,
+ stride,
+ VPDMA_MAX_STRIDE);
+
plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline,
VPDMA_STRIDE_ALIGN);
@@ -2291,7 +2309,7 @@ static int vpe_open(struct file *file)
v4l2_ctrl_handler_setup(hdl);
s_q_data = &ctx->q_data[Q_DATA_SRC];
- s_q_data->fmt = &vpe_formats[2];
+ s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV);
s_q_data->width = 1920;
s_q_data->height = 1080;
s_q_data->nplanes = 1;
@@ -2369,6 +2387,12 @@ static int vpe_release(struct file *file)
mutex_lock(&dev->dev_mutex);
free_mv_buffers(ctx);
+
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v);
+
vpdma_free_desc_list(&ctx->desc_list);
vpdma_free_desc_buf(&ctx->mmr_adb);
diff --git a/drivers/media/platform/vicodec/vicodec-codec.c b/drivers/media/platform/vicodec/vicodec-codec.c
index 2d04764..d854b23 100644
--- a/drivers/media/platform/vicodec/vicodec-codec.c
+++ b/drivers/media/platform/vicodec/vicodec-codec.c
@@ -588,8 +588,14 @@ static void fill_decoder_block(u8 *dst, const s16 *input, int stride)
int i, j;
for (i = 0; i < 8; i++) {
- for (j = 0; j < 8; j++)
- *dst++ = *input++;
+ for (j = 0; j < 8; j++, input++, dst++) {
+ if (*input < 0)
+ *dst = 0;
+ else if (*input > 255)
+ *dst = 255;
+ else
+ *dst = *input;
+ }
dst += stride - 8;
}
}
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
index 204aa6f..fa8435a 100644
--- a/drivers/media/platform/vimc/vimc-common.c
+++ b/drivers/media/platform/vimc/vimc-common.c
@@ -241,6 +241,8 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
/* Start the stream in the subdevice direct connected */
pad = media_entity_remote_pad(&ent->pads[i]);
+ if (!pad)
+ continue;
if (!is_media_entity_v4l2_subdev(pad->entity))
return -EINVAL;
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index 27db883..8548fa9 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -243,10 +243,7 @@ static void vimc_comp_unbind(struct device *master)
static int vimc_comp_compare(struct device *comp, void *data)
{
- const struct platform_device *pdev = to_platform_device(comp);
- const char *name = data;
-
- return !strcmp(pdev->dev.platform_data, name);
+ return comp == data;
}
static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
@@ -276,7 +273,7 @@ static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
}
component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
- (void *)vimc->pipe_cfg->ents[i].name);
+ &vimc->subdevs[i]->dev);
}
return match;
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 9e0d70e..3f0ffd4 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -204,13 +204,6 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved,
{
struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device,
ved);
- const struct vimc_pix_map *vpix;
- unsigned int frame_size;
-
- /* Calculate the frame size */
- vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
- frame_size = vsen->mbus_format.width * vpix->bpp *
- vsen->mbus_format.height;
tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
return vsen->frame;
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 2a92e5a..ac17883 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -765,7 +765,11 @@ static int vivid_thread_vid_cap(void *data)
if (kthread_should_stop())
break;
- mutex_lock(&dev->mutex);
+ if (!mutex_trylock(&dev->mutex)) {
+ schedule_timeout_uninterruptible(1);
+ continue;
+ }
+
cur_jiffies = jiffies;
if (dev->cap_seq_resync) {
dev->jiffies_vid_cap = cur_jiffies;
@@ -918,8 +922,6 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
/* shutdown control thread */
vivid_grab_controls(dev, false);
- mutex_unlock(&dev->mutex);
kthread_stop(dev->kthread_vid_cap);
dev->kthread_vid_cap = NULL;
- mutex_lock(&dev->mutex);
}
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
index 4885905..c5f466a 100644
--- a/drivers/media/platform/vivid/vivid-kthread-out.c
+++ b/drivers/media/platform/vivid/vivid-kthread-out.c
@@ -135,7 +135,11 @@ static int vivid_thread_vid_out(void *data)
if (kthread_should_stop())
break;
- mutex_lock(&dev->mutex);
+ if (!mutex_trylock(&dev->mutex)) {
+ schedule_timeout_uninterruptible(1);
+ continue;
+ }
+
cur_jiffies = jiffies;
if (dev->out_seq_resync) {
dev->jiffies_vid_out = cur_jiffies;
@@ -289,8 +293,6 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
/* shutdown control thread */
vivid_grab_controls(dev, false);
- mutex_unlock(&dev->mutex);
kthread_stop(dev->kthread_vid_out);
dev->kthread_vid_out = NULL;
- mutex_lock(&dev->mutex);
}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index cfb7cb4..e1794f8 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -137,7 +137,11 @@ static int vivid_thread_sdr_cap(void *data)
if (kthread_should_stop())
break;
- mutex_lock(&dev->mutex);
+ if (!mutex_trylock(&dev->mutex)) {
+ schedule_timeout_uninterruptible(1);
+ continue;
+ }
+
cur_jiffies = jiffies;
if (dev->sdr_cap_seq_resync) {
dev->jiffies_sdr_cap = cur_jiffies;
@@ -297,10 +301,8 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq)
}
/* shutdown control thread */
- mutex_unlock(&dev->mutex);
kthread_stop(dev->kthread_sdr_cap);
dev->kthread_sdr_cap = NULL;
- mutex_lock(&dev->mutex);
}
const struct vb2_ops vivid_sdr_cap_qops = {
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 2e273f4..c58ae48 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -222,9 +222,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
if (vb2_is_streaming(&dev->vb_vid_out_q))
dev->can_loop_video = vivid_vid_can_loop(dev);
- if (dev->kthread_vid_cap)
- return 0;
-
dev->vid_cap_seq_count = 0;
dprintk(dev, 1, "%s\n", __func__);
for (i = 0; i < VIDEO_MAX_FRAME; i++)
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 50248e2..0f90950 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -146,9 +146,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
if (vb2_is_streaming(&dev->vb_vid_cap_q))
dev->can_loop_video = vivid_vid_can_loop(dev);
- if (dev->kthread_vid_out)
- return 0;
-
dev->vid_out_seq_count = 0;
dprintk(dev, 1, "%s\n", __func__);
if (dev->start_streaming_error) {
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index b9c0f69..8d86f61 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -770,6 +770,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
const struct vsp1_format_info *fmtinfo;
+ unsigned int chroma_hsub;
struct vsp1_rwpf *rpf;
if (rpf_index >= vsp1->info->rpf_count)
@@ -810,10 +811,18 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
return -EINVAL;
}
+ /*
+ * Only formats with three planes can affect the chroma planes pitch.
+ * All formats with two planes have a horizontal subsampling value of 2,
+ * but combine U and V in a single chroma plane, which thus results in
+ * the luma plane and chroma plane having the same pitch.
+ */
+ chroma_hsub = (fmtinfo->planes == 3) ? fmtinfo->hsub : 1;
+
rpf->fmtinfo = fmtinfo;
rpf->format.num_planes = fmtinfo->planes;
rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
- rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
+ rpf->format.plane_fmt[1].bytesperline = cfg->pitch / chroma_hsub;
rpf->alpha = cfg->alpha;
rpf->mem.addr[0] = cfg->mem[0];
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 3738ff2..f6e4157 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 11aa94f..5000269 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1156,8 +1156,7 @@ static int wl1273_fm_fops_release(struct file *file)
if (radio->rds_users > 0) {
radio->rds_users--;
if (radio->rds_users == 0) {
- if (mutex_lock_interruptible(&core->lock))
- return -EINTR;
+ mutex_lock(&core->lock);
radio->irq_flags &= ~WL1273_RDS_EVENT;
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index e3b3ecd..ae7540b 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -485,6 +485,8 @@ static int si470x_i2c_remove(struct i2c_client *client)
video_unregister_device(&radio->videodev);
kfree(radio);
+ v4l2_ctrl_handler_free(&radio->hdl);
+ v4l2_device_unregister(&radio->v4l2_dev);
return 0;
}
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index f23a220..6b10363 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1607,8 +1607,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
spin_unlock_irqrestore(&ictx->kc_lock, flags);
/* send touchscreen events through input subsystem if touchpad data */
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
- buf[7] == 0x86) {
+ if (ictx->touch && len == 8 && buf[7] == 0x86) {
imon_touch_event(ictx, buf);
return;
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 68487ce..d96aed1 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -40,6 +40,7 @@
#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */
#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */
+#define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */
#ifndef CHAR_BIT
#define CHAR_BIT 8 /* Normally in <limits.h> */
#endif
@@ -242,13 +243,17 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
toggle = 0;
break;
case 32:
- if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
+ switch (scancode & RC6_6A_LCC_MASK) {
+ case RC6_6A_MCE_CC:
+ case RC6_6A_KATHREIN_CC:
protocol = RC_PROTO_RC6_MCE;
toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
- } else {
+ break;
+ default:
protocol = RC_PROTO_RC6_6A_32;
toggle = 0;
+ break;
}
break;
default:
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
index 11ce510..c437309 100644
--- a/drivers/media/spi/cxd2880-spi.c
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -536,6 +536,7 @@ cxd2880_spi_probe(struct spi_device *spi)
if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
pr_err("cxd2880_attach failed\n");
+ ret = -ENODEV;
goto fail_attach;
}
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index e3f6329..07e3322 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -632,7 +632,7 @@ static int au0828_usb_probe(struct usb_interface *interface,
/* Analog TV */
retval = au0828_analog_register(dev, interface);
if (retval) {
- pr_err("%s() au0282_dev_register failed to register on V4L2\n",
+ pr_err("%s() au0828_analog_register failed to register on V4L2\n",
__func__);
mutex_unlock(&dev->lock);
goto done;
@@ -641,7 +641,7 @@ static int au0828_usb_probe(struct usb_interface *interface,
/* Digital TV */
retval = au0828_dvb_register(dev);
if (retval)
- pr_err("%s() au0282_dev_register failed\n",
+ pr_err("%s() au0828_dvb_register failed\n",
__func__);
/* Remote controller */
diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c
index a8f3169..427cda4 100644
--- a/drivers/media/usb/b2c2/flexcop-usb.c
+++ b/drivers/media/usb/b2c2/flexcop-usb.c
@@ -294,7 +294,7 @@ static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
mutex_unlock(&fc_usb->data_mutex);
- return 0;
+ return ret;
}
/* actual bus specific access functions,
@@ -503,7 +503,13 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
static int flexcop_usb_init(struct flexcop_usb *fc_usb)
{
/* use the alternate setting with the larges buffer */
- usb_set_interface(fc_usb->udev,0,1);
+ int ret = usb_set_interface(fc_usb->udev, 0, 1);
+
+ if (ret) {
+ err("set interface failed.");
+ return ret;
+ }
+
switch (fc_usb->udev->speed) {
case USB_SPEED_LOW:
err("cannot handle USB speed because it is too slow.");
@@ -537,6 +543,9 @@ static int flexcop_usb_probe(struct usb_interface *intf,
struct flexcop_device *fc = NULL;
int ret;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
err("out of memory\n");
return -ENOMEM;
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index f7fcd73..963739f 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -1389,7 +1389,7 @@ int cx231xx_g_register(struct file *file, void *priv,
ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
(u16)reg->reg, value, 4);
reg->val = value[0] | value[1] << 8 |
- value[2] << 16 | value[3] << 24;
+ value[2] << 16 | (u32)value[3] << 24;
reg->size = 4;
break;
case 1: /* AFE - read byte */
diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c
index 16e946e..2587197 100644
--- a/drivers/media/usb/dvb-usb/af9005.c
+++ b/drivers/media/usb/dvb-usb/af9005.c
@@ -985,8 +985,9 @@ static int af9005_identify_state(struct usb_device *udev,
else if (reply == 0x02)
*cold = 0;
else
- return -EIO;
- deb_info("Identify state cold = %d\n", *cold);
+ ret = -EIO;
+ if (!ret)
+ deb_info("Identify state cold = %d\n", *cold);
err:
kfree(buf);
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 5b51ed7..5400ec9 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -457,7 +457,8 @@ static int cxusb_rc_query(struct dvb_usb_device *d)
{
u8 ircode[4];
- cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4);
+ if (cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4) < 0)
+ return 0;
if (ircode[2] || ircode[3])
rc_keydown(d->rc_dev, RC_PROTO_NEC,
diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c
index 3506358..f589932 100644
--- a/drivers/media/usb/pulse8-cec/pulse8-cec.c
+++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c
@@ -121,6 +121,7 @@ struct pulse8 {
unsigned int vers;
struct completion cmd_done;
struct work_struct work;
+ u8 work_result;
struct delayed_work ping_eeprom_work;
struct cec_msg rx_msg;
u8 data[DATA_SIZE];
@@ -142,8 +143,10 @@ static void pulse8_irq_work_handler(struct work_struct *work)
{
struct pulse8 *pulse8 =
container_of(work, struct pulse8, work);
+ u8 result = pulse8->work_result;
- switch (pulse8->data[0] & 0x3f) {
+ pulse8->work_result = 0;
+ switch (result & 0x3f) {
case MSGCODE_FRAME_DATA:
cec_received_msg(pulse8->adap, &pulse8->rx_msg);
break;
@@ -177,12 +180,12 @@ static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
pulse8->escape = false;
} else if (data == MSGEND) {
struct cec_msg *msg = &pulse8->rx_msg;
+ u8 msgcode = pulse8->buf[0];
if (debug)
dev_info(pulse8->dev, "received: %*ph\n",
pulse8->idx, pulse8->buf);
- pulse8->data[0] = pulse8->buf[0];
- switch (pulse8->buf[0] & 0x3f) {
+ switch (msgcode & 0x3f) {
case MSGCODE_FRAME_START:
msg->len = 1;
msg->msg[0] = pulse8->buf[1];
@@ -191,14 +194,20 @@ static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
if (msg->len == CEC_MAX_MSG_SIZE)
break;
msg->msg[msg->len++] = pulse8->buf[1];
- if (pulse8->buf[0] & MSGCODE_FRAME_EOM)
+ if (msgcode & MSGCODE_FRAME_EOM) {
+ WARN_ON(pulse8->work_result);
+ pulse8->work_result = msgcode;
schedule_work(&pulse8->work);
+ break;
+ }
break;
case MSGCODE_TRANSMIT_SUCCEEDED:
case MSGCODE_TRANSMIT_FAILED_LINE:
case MSGCODE_TRANSMIT_FAILED_ACK:
case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+ WARN_ON(pulse8->work_result);
+ pulse8->work_result = msgcode;
schedule_work(&pulse8->work);
break;
case MSGCODE_HIGH_ERROR:
@@ -585,7 +594,7 @@ static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
else
pulse8->config_pending = true;
mutex_unlock(&pulse8->config_lock);
- return err;
+ return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err;
}
static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index e53a80b..04d3341 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -916,8 +916,12 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
if (!list_empty(&vp->dev_video->devbase.fh_list) ||
- !list_empty(&vp->dev_radio->devbase.fh_list))
+ (vp->dev_radio &&
+ !list_empty(&vp->dev_radio->devbase.fh_list))) {
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "pvr2_v4l2 internal_check exit-empty id=%p", vp);
return;
+ }
pvr2_v4l2_destroy_no_lock(vp);
}
@@ -953,7 +957,8 @@ static int pvr2_v4l2_release(struct file *file)
kfree(fhp);
if (vp->channel.mc_head->disconnect_flag &&
list_empty(&vp->dev_video->devbase.fh_list) &&
- list_empty(&vp->dev_radio->devbase.fh_list)) {
+ (!vp->dev_radio ||
+ list_empty(&vp->dev_radio->devbase.fh_list))) {
pvr2_v4l2_destroy_no_lock(vp);
}
return 0;
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 6e3f234..e33fa78 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -164,7 +164,11 @@ int stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value)
*value = *buf;
kfree(buf);
- return ret;
+
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
}
static int stk_start_stream(struct stk_camera *dev)
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index f29d1be..cce29b6 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -327,6 +327,10 @@ static int usbvision_v4l2_open(struct file *file)
if (mutex_lock_interruptible(&usbvision->v4l2_lock))
return -ERESTARTSYS;
+ if (usbvision->remove_pending) {
+ err_code = -ENODEV;
+ goto unlock;
+ }
if (usbvision->user) {
err_code = -EBUSY;
} else {
@@ -390,6 +394,7 @@ static int usbvision_v4l2_open(struct file *file)
static int usbvision_v4l2_close(struct file *file)
{
struct usb_usbvision *usbvision = video_drvdata(file);
+ int r;
PDEBUG(DBG_IO, "close");
@@ -404,9 +409,10 @@ static int usbvision_v4l2_close(struct file *file)
usbvision_scratch_free(usbvision);
usbvision->user--;
+ r = usbvision->remove_pending;
mutex_unlock(&usbvision->v4l2_lock);
- if (usbvision->remove_pending) {
+ if (r) {
printk(KERN_INFO "%s: Final disconnect\n", __func__);
usbvision_release(usbvision);
return 0;
@@ -1090,6 +1096,11 @@ static int usbvision_radio_open(struct file *file)
if (mutex_lock_interruptible(&usbvision->v4l2_lock))
return -ERESTARTSYS;
+
+ if (usbvision->remove_pending) {
+ err_code = -ENODEV;
+ goto out;
+ }
err_code = v4l2_fh_open(file);
if (err_code)
goto out;
@@ -1122,6 +1133,7 @@ static int usbvision_radio_open(struct file *file)
static int usbvision_radio_close(struct file *file)
{
struct usb_usbvision *usbvision = video_drvdata(file);
+ int r;
PDEBUG(DBG_IO, "");
@@ -1134,9 +1146,10 @@ static int usbvision_radio_close(struct file *file)
usbvision_audio_off(usbvision);
usbvision->radio = 0;
usbvision->user--;
+ r = usbvision->remove_pending;
mutex_unlock(&usbvision->v4l2_lock);
- if (usbvision->remove_pending) {
+ if (r) {
printk(KERN_INFO "%s: Final disconnect\n", __func__);
v4l2_fh_release(file);
usbvision_release(usbvision);
@@ -1562,6 +1575,7 @@ static int usbvision_probe(struct usb_interface *intf,
static void usbvision_disconnect(struct usb_interface *intf)
{
struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf));
+ int u;
PDEBUG(DBG_PROBE, "");
@@ -1578,13 +1592,14 @@ static void usbvision_disconnect(struct usb_interface *intf)
v4l2_device_disconnect(&usbvision->v4l2_dev);
usbvision_i2c_unregister(usbvision);
usbvision->remove_pending = 1; /* Now all ISO data will be ignored */
+ u = usbvision->user;
usb_put_dev(usbvision->dev);
usbvision->dev = NULL; /* USB device is no more */
mutex_unlock(&usbvision->v4l2_lock);
- if (usbvision->user) {
+ if (u) {
printk(KERN_INFO "%s: In use, disconnect pending\n",
__func__);
wake_up_interruptible(&usbvision->wait_frame);
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index c3ddbf6..063e229 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -391,6 +391,39 @@ static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
}
/* ------------------------------------------------------------------------
+ * Streaming Object Management
+ */
+
+static void uvc_stream_delete(struct uvc_streaming *stream)
+{
+ mutex_destroy(&stream->mutex);
+
+ usb_put_intf(stream->intf);
+
+ kfree(stream->format);
+ kfree(stream->header.bmaControls);
+ kfree(stream);
+}
+
+static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev,
+ struct usb_interface *intf)
+{
+ struct uvc_streaming *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (stream == NULL)
+ return NULL;
+
+ mutex_init(&stream->mutex);
+
+ stream->dev = dev;
+ stream->intf = usb_get_intf(intf);
+ stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ return stream;
+}
+
+/* ------------------------------------------------------------------------
* Descriptors parsing
*/
@@ -682,17 +715,12 @@ static int uvc_parse_streaming(struct uvc_device *dev,
return -EINVAL;
}
- streaming = kzalloc(sizeof(*streaming), GFP_KERNEL);
+ streaming = uvc_stream_new(dev, intf);
if (streaming == NULL) {
usb_driver_release_interface(&uvc_driver.driver, intf);
- return -EINVAL;
+ return -ENOMEM;
}
- mutex_init(&streaming->mutex);
- streaming->dev = dev;
- streaming->intf = usb_get_intf(intf);
- streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
-
/* The Pico iMage webcam has its class-specific interface descriptors
* after the endpoint descriptors.
*/
@@ -899,10 +927,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
error:
usb_driver_release_interface(&uvc_driver.driver, intf);
- usb_put_intf(intf);
- kfree(streaming->format);
- kfree(streaming->header.bmaControls);
- kfree(streaming);
+ uvc_stream_delete(streaming);
return ret;
}
@@ -1818,7 +1843,7 @@ static int uvc_scan_device(struct uvc_device *dev)
* is released.
*
* As this function is called after or during disconnect(), all URBs have
- * already been canceled by the USB core. There is no need to kill the
+ * already been cancelled by the USB core. There is no need to kill the
* interrupt URB manually.
*/
static void uvc_delete(struct kref *kref)
@@ -1856,10 +1881,7 @@ static void uvc_delete(struct kref *kref)
streaming = list_entry(p, struct uvc_streaming, list);
usb_driver_release_interface(&uvc_driver.driver,
streaming->intf);
- usb_put_intf(streaming->intf);
- kfree(streaming->format);
- kfree(streaming->header.bmaControls);
- kfree(streaming);
+ uvc_stream_delete(streaming);
}
kfree(dev);
@@ -2124,6 +2146,20 @@ static int uvc_probe(struct usb_interface *intf,
sizeof(dev->name) - len);
}
+ /* Initialize the media device. */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->mdev.dev = &intf->dev;
+ strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
+ if (udev->serial)
+ strscpy(dev->mdev.serial, udev->serial,
+ sizeof(dev->mdev.serial));
+ usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info));
+ dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ media_device_init(&dev->mdev);
+
+ dev->vdev.mdev = &dev->mdev;
+#endif
+
/* Parse the Video Class control descriptor. */
if (uvc_parse_control(dev) < 0) {
uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
@@ -2144,19 +2180,7 @@ static int uvc_probe(struct usb_interface *intf,
"linux-uvc-devel mailing list.\n");
}
- /* Initialize the media device and register the V4L2 device. */
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->mdev.dev = &intf->dev;
- strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
- if (udev->serial)
- strlcpy(dev->mdev.serial, udev->serial,
- sizeof(dev->mdev.serial));
- strcpy(dev->mdev.bus_info, udev->devpath);
- dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- media_device_init(&dev->mdev);
-
- dev->vdev.mdev = &dev->mdev;
-#endif
+ /* Register the V4L2 device. */
if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
goto error;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index e9ea261..dddf0f5 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1156,6 +1156,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_FLASH_STROBE_STOP:
case V4L2_CID_AUTO_FOCUS_START:
case V4L2_CID_AUTO_FOCUS_STOP:
+ case V4L2_CID_DO_WHITE_BALANCE:
*type = V4L2_CTRL_TYPE_BUTTON;
*flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 96cf69a..b7bf43b 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1505,10 +1505,26 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
return ret;
}
+static void v4l_pix_format_touch(struct v4l2_pix_format *p)
+{
+ /*
+ * The v4l2_pix_format structure contains fields that make no sense for
+ * touch. Set them to default values in this case.
+ */
+
+ p->field = V4L2_FIELD_NONE;
+ p->colorspace = V4L2_COLORSPACE_RAW;
+ p->flags = 0;
+ p->ycbcr_enc = 0;
+ p->quantization = 0;
+ p->xfer_func = 0;
+}
+
static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct v4l2_format *p = arg;
+ struct video_device *vfd = video_devdata(file);
int ret = check_fmt(file, p->type);
if (ret)
@@ -1546,6 +1562,8 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
+ if (vfd->vfl_type == VFL_TYPE_TOUCH)
+ v4l_pix_format_touch(&p->fmt.pix);
return ret;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
@@ -1581,21 +1599,6 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
}
-static void v4l_pix_format_touch(struct v4l2_pix_format *p)
-{
- /*
- * The v4l2_pix_format structure contains fields that make no sense for
- * touch. Set them to default values in this case.
- */
-
- p->field = V4L2_FIELD_NONE;
- p->colorspace = V4L2_COLORSPACE_RAW;
- p->flags = 0;
- p->ycbcr_enc = 0;
- p->quantization = 0;
- p->xfer_func = 0;
-}
-
static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index c215287..1c6a7c1 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -21,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 47d6d40..a4403a5 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -52,8 +52,10 @@ int arizona_clk32k_enable(struct arizona *arizona)
if (ret != 0)
goto err_ref;
ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]);
- if (ret != 0)
- goto err_pm;
+ if (ret != 0) {
+ pm_runtime_put_sync(arizona->dev);
+ goto err_ref;
+ }
break;
case ARIZONA_32KZ_MCLK2:
ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]);
@@ -67,8 +69,6 @@ int arizona_clk32k_enable(struct arizona *arizona)
ARIZONA_CLK_32K_ENA);
}
-err_pm:
- pm_runtime_put_sync(arizona->dev);
err_ref:
if (ret != 0)
arizona->clk32k_ref--;
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 15bc052..9ca1f8c 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -31,8 +31,8 @@
/* Interrupt Status Registers */
#define BXTWC_IRQLVL1 0x4E02
-#define BXTWC_PWRBTNIRQ 0x4E03
+#define BXTWC_PWRBTNIRQ 0x4E03
#define BXTWC_THRM0IRQ 0x4E04
#define BXTWC_THRM1IRQ 0x4E05
#define BXTWC_THRM2IRQ 0x4E06
@@ -47,10 +47,9 @@
/* Interrupt MASK Registers */
#define BXTWC_MIRQLVL1 0x4E0E
-#define BXTWC_MPWRTNIRQ 0x4E0F
-
#define BXTWC_MIRQLVL1_MCHGR BIT(5)
+#define BXTWC_MPWRBTNIRQ 0x4E0F
#define BXTWC_MTHRM0IRQ 0x4E12
#define BXTWC_MTHRM1IRQ 0x4E13
#define BXTWC_MTHRM2IRQ 0x4E14
@@ -66,9 +65,7 @@
/* Whiskey Cove PMIC share same ACPI ID between different platforms */
#define BROXTON_PMIC_WC_HRV 4
-/* Manage in two IRQ chips since mask registers are not consecutive */
enum bxtwc_irqs {
- /* Level 1 */
BXTWC_PWRBTN_LVL1_IRQ = 0,
BXTWC_TMU_LVL1_IRQ,
BXTWC_THRM_LVL1_IRQ,
@@ -77,9 +74,11 @@ enum bxtwc_irqs {
BXTWC_CHGR_LVL1_IRQ,
BXTWC_GPIO_LVL1_IRQ,
BXTWC_CRIT_LVL1_IRQ,
+};
- /* Level 2 */
- BXTWC_PWRBTN_IRQ,
+enum bxtwc_irqs_pwrbtn {
+ BXTWC_PWRBTN_IRQ = 0,
+ BXTWC_UIBTN_IRQ,
};
enum bxtwc_irqs_bcu {
@@ -113,7 +112,10 @@ static const struct regmap_irq bxtwc_regmap_irqs[] = {
REGMAP_IRQ_REG(BXTWC_CHGR_LVL1_IRQ, 0, BIT(5)),
REGMAP_IRQ_REG(BXTWC_GPIO_LVL1_IRQ, 0, BIT(6)),
REGMAP_IRQ_REG(BXTWC_CRIT_LVL1_IRQ, 0, BIT(7)),
- REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 1, 0x03),
+};
+
+static const struct regmap_irq bxtwc_regmap_irqs_pwrbtn[] = {
+ REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 0, 0x01),
};
static const struct regmap_irq bxtwc_regmap_irqs_bcu[] = {
@@ -125,7 +127,7 @@ static const struct regmap_irq bxtwc_regmap_irqs_adc[] = {
};
static const struct regmap_irq bxtwc_regmap_irqs_chgr[] = {
- REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, BIT(5)),
+ REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, 0x20),
REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 0, 0x1f),
REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 1, 0x1f),
};
@@ -144,7 +146,16 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip = {
.mask_base = BXTWC_MIRQLVL1,
.irqs = bxtwc_regmap_irqs,
.num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs),
- .num_regs = 2,
+ .num_regs = 1,
+};
+
+static struct regmap_irq_chip bxtwc_regmap_irq_chip_pwrbtn = {
+ .name = "bxtwc_irq_chip_pwrbtn",
+ .status_base = BXTWC_PWRBTNIRQ,
+ .mask_base = BXTWC_MPWRBTNIRQ,
+ .irqs = bxtwc_regmap_irqs_pwrbtn,
+ .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_pwrbtn),
+ .num_regs = 1,
};
static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = {
@@ -473,6 +484,16 @@ static int bxtwc_probe(struct platform_device *pdev)
}
ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
+ BXTWC_PWRBTN_LVL1_IRQ,
+ IRQF_ONESHOT,
+ &bxtwc_regmap_irq_chip_pwrbtn,
+ &pmic->irq_chip_data_pwrbtn);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add PWRBTN IRQ chip\n");
+ return ret;
+ }
+
+ ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
BXTWC_TMU_LVL1_IRQ,
IRQF_ONESHOT,
&bxtwc_regmap_irq_chip_tmu,
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 3f554c4..d1495d7 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -153,12 +153,6 @@ static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(
pd->ono = irq_of_parse_and_map(dev->of_node, 1);
- /*
- * ToDo: the 'wakeup' member in the platform data is more of a linux
- * specfic information. Hence, there is no binding for that yet and
- * not parsed here.
- */
-
return pd;
}
@@ -246,7 +240,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
*/
/* MAX8997 has a power button input. */
- device_init_wakeup(max8997->dev, pdata->wakeup);
+ device_init_wakeup(max8997->dev, true);
return ret;
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 234febf..d0bf50e 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -278,7 +278,8 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
if (ret)
goto out;
- adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2;
+ adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2 |
+ MC13XXX_ADC0_CHRGRAWDIV;
adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
/*
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index fe8d335..4660ad9 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -295,11 +295,24 @@ static int ti_tscadc_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data)
+{
+ return device_may_wakeup(dev);
+}
+
static int __maybe_unused tscadc_suspend(struct device *dev)
{
struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev);
regmap_write(tscadc->regmap, REG_SE, 0x00);
+ if (device_for_each_child(dev, NULL, ti_tscadc_can_wakeup)) {
+ u32 ctrl;
+
+ regmap_read(tscadc->regmap, REG_CTRL, &ctrl);
+ ctrl &= ~(CNTRLREG_POWERDOWN);
+ ctrl |= CNTRLREG_TSCSSENB;
+ regmap_write(tscadc->regmap, REG_CTRL, ctrl);
+ }
pm_runtime_put_sync(dev);
return 0;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f2b5ac5..96d64a8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -596,6 +596,14 @@
The output of the driver is CIRs (Channel Impulse Response) which
must be processed in user space in order to get meaningful results.
+config QTI_XR_SMRTVWR_MISC
+ tristate "QTI XR Reference device Misc driver support"
+ help
+ This driver supports the misc chips in XR Reference devices
+ such as NDI controller chip, Power up Sensor rails etc. Note: This
+ driver initializes gpios, enables/disables LDOs that are part of
+ XR standalone reference device.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
@@ -610,6 +618,7 @@
source "drivers/misc/cxl/Kconfig"
source "drivers/misc/ocxl/Kconfig"
source "drivers/misc/cardreader/Kconfig"
+source "drivers/misc/fpr_FingerprintCard/Kconfig"
endmenu
config OKL4_USER_VIPC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 11270e4..8cf0060 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -72,4 +72,5 @@
obj-$(CONFIG_OKL4_GUEST) += okl4-panic.o
obj-$(CONFIG_OKL4_LINK_SHBUF) += okl4-link-shbuf.o
obj-$(CONFIG_WIGIG_SENSING_SPI) += wigig_sensing.o
-
+obj-$(CONFIG_QTI_XR_SMRTVWR_MISC) += qxr-stdalonevwr.o
+obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
diff --git a/drivers/misc/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c
index ef83a90..d2ed3b9 100644
--- a/drivers/misc/altera-stapl/altera.c
+++ b/drivers/misc/altera-stapl/altera.c
@@ -2176,8 +2176,7 @@ static int altera_get_note(u8 *p, s32 program_size,
key_ptr = &p[note_strings +
get_unaligned_be32(
&p[note_table + (8 * i)])];
- if ((strncasecmp(key, key_ptr, strlen(key_ptr)) == 0) &&
- (key != NULL)) {
+ if (key && !strncasecmp(key, key_ptr, strlen(key_ptr))) {
status = 0;
value_ptr = &p[note_strings +
diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c
index b83a373..08f4a51 100644
--- a/drivers/misc/cxl/guest.c
+++ b/drivers/misc/cxl/guest.c
@@ -1020,8 +1020,6 @@ int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_n
void cxl_guest_remove_afu(struct cxl_afu *afu)
{
- pr_devel("in %s - AFU(%d)\n", __func__, afu->slice);
-
if (!afu)
return;
diff --git a/drivers/misc/fpr_FingerprintCard/Kconfig b/drivers/misc/fpr_FingerprintCard/Kconfig
new file mode 100644
index 0000000..65b0ba5
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config FPR_FPC
+ tristate "FPC fingerprint sensor support"
+ depends on SPI_MASTER
+ help
+ support fpc1020 and fpc1028.
+ This driver can also be built as a module. If so, the module
+ will be called fpc1020.
diff --git a/drivers/misc/fpr_FingerprintCard/Makefile b/drivers/misc/fpr_FingerprintCard/Makefile
new file mode 100644
index 0000000..58bbc70
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Makefile for FingerprintCard fingerprint driver
+
+fpc1020-objs := fpc1020_platform_tee.o
+obj-$(CONFIG_FPR_FPC) += fpc1020.o
diff --git a/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c
new file mode 100644
index 0000000..d49d857
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FPC1020 Fingerprint sensor device driver
+ *
+ * This driver will control the platform resources that the FPC fingerprint
+ * sensor needs to operate. The major things are probing the sensor to check
+ * that it is actually connected and let the Kernel know this and with that also
+ * enabling and disabling of regulators, controlling GPIOs such as sensor reset
+ * line, sensor IRQ line.
+ *
+ * The driver will expose most of its available functionality in sysfs which
+ * enables dynamic control of these features from eg. a user space process.
+ *
+ * The sensor's IRQ events will be pushed to Kernel's event handling system and
+ * are exposed in the drivers event node.
+ *
+ * This driver will NOT send any commands to the sensor it only controls the
+ * electrical parts.
+ *
+ *
+ * Copyright (c) 2015 Fingerprint Cards AB <tech@fingerprints.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License Version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+
+#define FPC_TTW_HOLD_TIME 1000
+#define RESET_LOW_SLEEP_MIN_US 5000
+#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100)
+#define RESET_HIGH_SLEEP1_MIN_US 100
+#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100)
+#define RESET_HIGH_SLEEP2_MIN_US 5000
+#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100)
+#define PWR_ON_SLEEP_MIN_US 100
+#define PWR_ON_SLEEP_MAX_US (PWR_ON_SLEEP_MIN_US + 900)
+#define NUM_PARAMS_REG_ENABLE_SET 2
+
+#define RELEASE_WAKELOCK_W_V "release_wakelock_with_verification"
+#define RELEASE_WAKELOCK "release_wakelock"
+#define START_IRQS_RECEIVED_CNT "start_irqs_received_counter"
+
+static const char * const pctl_names[] = {
+ "fpc1020_reset_reset",
+ "fpc1020_reset_active",
+ "fpc1020_irq_active",
+};
+
+struct vreg_config {
+ char *name;
+ unsigned long vmin;
+ unsigned long vmax;
+ int ua_load;
+};
+
+static const struct vreg_config vreg_conf[] = {
+ { "vdd_ana", 1800000UL, 1800000UL, 6000, },
+ { "vcc_spi", 1800000UL, 1800000UL, 10, },
+ { "vdd_io", 1800000UL, 1800000UL, 6000, },
+};
+
+struct fpc1020_data {
+ struct device *dev;
+ struct pinctrl *fingerprint_pinctrl;
+ struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)];
+ struct regulator *vreg[ARRAY_SIZE(vreg_conf)];
+ struct wakeup_source ttw_wl;
+ struct mutex lock; /* To set/get exported values in sysfs */
+ int irq_gpio;
+ int rst_gpio;
+ int nbr_irqs_received;
+ int nbr_irqs_received_counter_start;
+ bool prepared;
+ atomic_t wakeup_enabled; /* Used both in ISR and non-ISR */
+};
+
+static int vreg_setup(struct fpc1020_data *fpc1020, const char *name,
+ bool enable)
+{
+ size_t i;
+ int rc;
+ struct regulator *vreg;
+ struct device *dev = fpc1020->dev;
+
+ for (i = 0; i < ARRAY_SIZE(vreg_conf); i++) {
+ const char *n = vreg_conf[i].name;
+
+ if (!memcmp(n, name, strlen(n)))
+ goto found;
+ }
+
+ dev_err(dev, "Regulator %s not found\n", name);
+
+ return -EINVAL;
+
+found:
+ vreg = fpc1020->vreg[i];
+ if (enable) {
+ if (!vreg) {
+ vreg = devm_regulator_get(dev, name);
+ if (IS_ERR_OR_NULL(vreg)) {
+ dev_info(dev,
+ "No regulator %s, maybe fixed regulator\n",
+ name);
+ return 0;
+ }
+ }
+
+ if (regulator_count_voltages(vreg) > 0) {
+ rc = regulator_set_voltage(vreg, vreg_conf[i].vmin,
+ vreg_conf[i].vmax);
+ if (rc)
+ dev_err(dev,
+ "Unable to set voltage on %s, %d\n",
+ name, rc);
+ }
+
+ rc = regulator_set_load(vreg, vreg_conf[i].ua_load);
+ if (rc < 0)
+ dev_err(dev, "Unable to set current on %s, %d\n",
+ name, rc);
+
+ rc = regulator_enable(vreg);
+ if (rc) {
+ dev_err(dev, "error enabling %s: %d\n", name, rc);
+ vreg = NULL;
+ }
+ fpc1020->vreg[i] = vreg;
+ } else {
+ if (vreg) {
+ if (regulator_is_enabled(vreg)) {
+ regulator_disable(vreg);
+ dev_dbg(dev, "disabled %s\n", name);
+ }
+ fpc1020->vreg[i] = NULL;
+ }
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/*
+ * sysfs node for controlling clocks.
+ *
+ * This is disabled in platform variant of this driver but kept for
+ * backwards compatibility. Only prints a debug print that it is
+ * disabled.
+ */
+static ssize_t clk_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev_dbg(dev,
+ "clk_enable sysfs node not enabled in platform driver\n");
+
+ return count;
+}
+static DEVICE_ATTR_WO(clk_enable);
+
+/*
+ * Will try to select the set of pins (GPIOS) defined in a pin control node of
+ * the device tree named @p name.
+ *
+ * The node can contain several eg. GPIOs that is controlled when selecting it.
+ * The node may activate or deactivate the pins it contains, the action is
+ * defined in the device tree node itself and not here. The states used
+ * internally is fetched at probe time.
+ *
+ * @see pctl_names
+ * @see fpc1020_probe
+ */
+static int select_pin_ctl(struct fpc1020_data *fpc1020, const char *name)
+{
+ size_t i;
+ int rc;
+ struct device *dev = fpc1020->dev;
+
+ for (i = 0; i < ARRAY_SIZE(pctl_names); i++) {
+ const char *n = pctl_names[i];
+
+ if (!memcmp(n, name, strlen(n))) {
+ rc = pinctrl_select_state(fpc1020->fingerprint_pinctrl,
+ fpc1020->pinctrl_state[i]);
+ if (rc)
+ dev_err(dev, "cannot select '%s'\n", name);
+ else
+ dev_dbg(dev, "Selected '%s'\n", name);
+
+ return rc;
+ }
+ }
+
+ dev_err(dev, "%s:'%s' not found\n", __func__, name);
+ return -EINVAL;
+}
+
+static ssize_t pinctl_set_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ int rc;
+
+ mutex_lock(&fpc1020->lock);
+ rc = select_pin_ctl(fpc1020, buf);
+ mutex_unlock(&fpc1020->lock);
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR_WO(pinctl_set);
+
+static ssize_t regulator_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ char op;
+ char name[16];
+ int rc;
+ bool enable;
+
+ if (sscanf(buf, "%15[^,],%c", name, &op) != NUM_PARAMS_REG_ENABLE_SET)
+ return -EINVAL;
+ if (op == 'e')
+ enable = true;
+ else if (op == 'd')
+ enable = false;
+ else
+ return -EINVAL;
+
+ mutex_lock(&fpc1020->lock);
+ rc = vreg_setup(fpc1020, name, enable);
+ mutex_unlock(&fpc1020->lock);
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR_WO(regulator_enable);
+
+static void hw_reset(struct fpc1020_data *fpc1020)
+{
+ (void)gpio_get_value(fpc1020->irq_gpio);
+
+ select_pin_ctl(fpc1020, "fpc1020_reset_active");
+
+ usleep_range(RESET_HIGH_SLEEP1_MIN_US, RESET_HIGH_SLEEP1_MAX_US);
+
+ select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US);
+
+ select_pin_ctl(fpc1020, "fpc1020_reset_active");
+
+ usleep_range(RESET_HIGH_SLEEP2_MIN_US, RESET_HIGH_SLEEP2_MAX_US);
+
+ (void)gpio_get_value(fpc1020->irq_gpio);
+}
+
+static ssize_t hw_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+
+ if (!memcmp(buf, "reset", strlen("reset"))) {
+ mutex_lock(&fpc1020->lock);
+ hw_reset(fpc1020);
+ mutex_unlock(&fpc1020->lock);
+ return count;
+ }
+
+ return -EINVAL;
+}
+static DEVICE_ATTR_WO(hw_reset);
+
+/*
+ * Will setup GPIOs, and regulators to correctly initialize the touch sensor to
+ * be ready for work.
+ *
+ * In the correct order according to the sensor spec this function will
+ * enable/disable regulators, and reset line, all to set the sensor in a
+ * correct power on or off state "electrical" wise.
+ *
+ * @see device_prepare_set
+ * @note This function will not send any commands to the sensor it will only
+ * control it "electrically".
+ */
+static void device_prepare(struct fpc1020_data *fpc1020, bool enable)
+{
+ mutex_lock(&fpc1020->lock);
+ if (enable && !fpc1020->prepared) {
+ fpc1020->prepared = true;
+ select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ vreg_setup(fpc1020, "vcc_spi", true);
+ vreg_setup(fpc1020, "vdd_io", true);
+ vreg_setup(fpc1020, "vdd_ana", true);
+
+ usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US);
+
+ select_pin_ctl(fpc1020, "fpc1020_reset_active");
+ } else if (!enable && fpc1020->prepared) {
+ select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US);
+
+ vreg_setup(fpc1020, "vdd_ana", false);
+ vreg_setup(fpc1020, "vdd_io", false);
+ vreg_setup(fpc1020, "vcc_spi", false);
+
+ fpc1020->prepared = false;
+ }
+ mutex_unlock(&fpc1020->lock);
+}
+
+/*
+ * sysfs node to enable/disable (power up/power down) the touch sensor
+ *
+ * @see device_prepare
+ */
+static ssize_t device_prepare_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = 0;
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+
+ if (!memcmp(buf, "enable", strlen("enable")))
+ device_prepare(fpc1020, true);
+ else if (!memcmp(buf, "disable", strlen("disable")))
+ device_prepare(fpc1020, false);
+ else
+ rc = -EINVAL;
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR_WO(device_prepare);
+
+/**
+ * sysfs node for controlling whether the driver is allowed
+ * to wake up the platform on interrupt.
+ */
+static ssize_t wakeup_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ ssize_t ret = count;
+
+ mutex_lock(&fpc1020->lock);
+ if (!memcmp(buf, "enable", strlen("enable")))
+ atomic_set(&fpc1020->wakeup_enabled, 1);
+ else if (!memcmp(buf, "disable", strlen("disable")))
+ atomic_set(&fpc1020->wakeup_enabled, 0);
+ else
+ ret = -EINVAL;
+ mutex_unlock(&fpc1020->lock);
+
+ return ret;
+}
+static DEVICE_ATTR_WO(wakeup_enable);
+
+
+/*
+ * sysfs node for controlling the wakelock.
+ */
+static ssize_t handle_wakelock_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ ssize_t ret = count;
+
+ mutex_lock(&fpc1020->lock);
+ if (!memcmp(buf, RELEASE_WAKELOCK_W_V,
+ min(count, strlen(RELEASE_WAKELOCK_W_V)))) {
+ if (fpc1020->nbr_irqs_received_counter_start ==
+ fpc1020->nbr_irqs_received) {
+ __pm_relax(&fpc1020->ttw_wl);
+ } else {
+ dev_dbg(dev, "Ignore releasing of wakelock %d != %d",
+ fpc1020->nbr_irqs_received_counter_start,
+ fpc1020->nbr_irqs_received);
+ }
+ } else if (!memcmp(buf, RELEASE_WAKELOCK, min(count,
+ strlen(RELEASE_WAKELOCK)))) {
+ __pm_relax(&fpc1020->ttw_wl);
+ } else if (!memcmp(buf, START_IRQS_RECEIVED_CNT,
+ min(count, strlen(START_IRQS_RECEIVED_CNT)))) {
+ fpc1020->nbr_irqs_received_counter_start =
+ fpc1020->nbr_irqs_received;
+ } else
+ ret = -EINVAL;
+ mutex_unlock(&fpc1020->lock);
+
+ return ret;
+}
+static DEVICE_ATTR_WO(handle_wakelock);
+
+/*
+ * sysf node to check the interrupt status of the sensor, the interrupt
+ * handler should perform sysf_notify to allow userland to poll the node.
+ */
+static ssize_t irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ int irq = gpio_get_value(fpc1020->irq_gpio);
+
+ return scnprintf(buf, PAGE_SIZE, "%i\n", irq);
+}
+static DEVICE_ATTR_RO(irq);
+
+static struct attribute *attributes[] = {
+ &dev_attr_pinctl_set.attr,
+ &dev_attr_device_prepare.attr,
+ &dev_attr_regulator_enable.attr,
+ &dev_attr_hw_reset.attr,
+ &dev_attr_wakeup_enable.attr,
+ &dev_attr_handle_wakelock.attr,
+ &dev_attr_clk_enable.attr,
+ &dev_attr_irq.attr,
+ NULL
+};
+
+static const struct attribute_group attribute_group = {
+ .attrs = attributes,
+};
+
+static irqreturn_t fpc1020_irq_handler(int irq, void *handle)
+{
+ struct fpc1020_data *fpc1020 = handle;
+
+ pr_info("fpc1020 irq handler: %s\n", __func__);
+ mutex_lock(&fpc1020->lock);
+ if (atomic_read(&fpc1020->wakeup_enabled)) {
+ fpc1020->nbr_irqs_received++;
+ __pm_wakeup_event(&fpc1020->ttw_wl,
+ msecs_to_jiffies(FPC_TTW_HOLD_TIME));
+ }
+ mutex_unlock(&fpc1020->lock);
+
+ sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name);
+
+ return IRQ_HANDLED;
+}
+
+static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020,
+ const char *label, int *gpio)
+{
+ struct device *dev = fpc1020->dev;
+ struct device_node *np = dev->of_node;
+ int rc;
+
+ rc = of_get_named_gpio(np, label, 0);
+
+ if (rc < 0) {
+ dev_err(dev, "failed to get '%s'\n", label);
+ return rc;
+ }
+ *gpio = rc;
+
+ rc = devm_gpio_request(dev, *gpio, label);
+ if (rc) {
+ dev_err(dev, "failed to request gpio %d\n", *gpio);
+ return rc;
+ }
+ dev_dbg(dev, "%s %d\n", label, *gpio);
+
+ return 0;
+}
+
+static int fpc1020_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fpc1020_data *fpc1020;
+ int rc;
+ size_t i;
+ int irqf = 0;
+
+ fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020), GFP_KERNEL);
+ if (!fpc1020)
+ return -ENOMEM;
+
+ fpc1020->dev = dev;
+ platform_set_drvdata(pdev, fpc1020);
+
+ rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_irq",
+ &fpc1020->irq_gpio);
+ if (rc)
+ return -EINVAL;
+ rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_rst",
+ &fpc1020->rst_gpio);
+ if (rc)
+ return -EINVAL;
+
+ fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(fpc1020->fingerprint_pinctrl)) {
+ rc = PTR_ERR(fpc1020->fingerprint_pinctrl);
+ dev_err(dev, "Cannot get pinctrl\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pctl_names); i++) {
+ const char *n = pctl_names[i];
+ struct pinctrl_state *state =
+ pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n);
+ if (IS_ERR(state)) {
+ dev_err(dev, "cannot find '%s'\n", n);
+ return PTR_ERR(state);
+ }
+ dev_dbg(dev, "found pin control %s\n", n);
+ fpc1020->pinctrl_state[i] = state;
+ }
+
+ select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+ select_pin_ctl(fpc1020, "fpc1020_irq_active");
+
+ atomic_set(&fpc1020->wakeup_enabled, 0);
+
+ if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) {
+ irqf = IRQF_NO_SUSPEND;
+ device_init_wakeup(dev, 1);
+ }
+
+ mutex_init(&fpc1020->lock);
+ rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio),
+ NULL, fpc1020_irq_handler,
+ irqf | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(dev), fpc1020);
+ if (rc) {
+ dev_err(dev, "could not request irq %d\n",
+ gpio_to_irq(fpc1020->irq_gpio));
+ return rc;
+ }
+
+ dev_dbg(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio));
+
+ enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio));
+
+ wakeup_source_init(&fpc1020->ttw_wl, "fpc_ttw_wl");
+
+ rc = sysfs_create_group(&dev->kobj, &attribute_group);
+ if (rc) {
+ dev_err(dev, "could not create sysfs\n");
+ return rc;
+ }
+
+ if (of_property_read_bool(dev->of_node, "fpc,enable-on-boot")) {
+ dev_dbg(dev, "Enabling hardware\n");
+ device_prepare(fpc1020, true);
+ }
+
+ hw_reset(fpc1020);
+
+ return 0;
+}
+
+static int fpc1020_remove(struct platform_device *pdev)
+{
+ struct fpc1020_data *fpc1020 = platform_get_drvdata(pdev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &attribute_group);
+ mutex_destroy(&fpc1020->lock);
+ wakeup_source_trash(&fpc1020->ttw_wl);
+ vreg_setup(fpc1020, "vdd_ana", false);
+ vreg_setup(fpc1020, "vdd_io", false);
+ vreg_setup(fpc1020, "vcc_spi", false);
+
+ return 0;
+}
+
+static const struct of_device_id fpc1020_of_match[] = {
+ { .compatible = "fpc,fpc1020", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fpc1020_of_match);
+
+static struct platform_driver fpc1020_driver = {
+ .driver = {
+ .name = "fpc1020",
+ .of_match_table = fpc1020_of_match,
+ },
+ .probe = fpc1020_probe,
+ .remove = fpc1020_remove,
+};
+
+module_platform_driver(fpc1020_driver);
+
+MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index f68435d..22301bb 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -298,7 +298,7 @@ static int genwqe_sgl_size(int num_pages)
int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
void __user *user_addr, size_t user_size, int write)
{
- int rc;
+ int ret = -ENOMEM;
struct pci_dev *pci_dev = cd->pci_dev;
sgl->fpage_offs = offset_in_page((unsigned long)user_addr);
@@ -318,7 +318,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
if (get_order(sgl->sgl_size) > MAX_ORDER) {
dev_err(&pci_dev->dev,
"[%s] err: too much memory requested!\n", __func__);
- return -ENOMEM;
+ return ret;
}
sgl->sgl = __genwqe_alloc_consistent(cd, sgl->sgl_size,
@@ -326,7 +326,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
if (sgl->sgl == NULL) {
dev_err(&pci_dev->dev,
"[%s] err: no memory available!\n", __func__);
- return -ENOMEM;
+ return ret;
}
/* Only use buffering on incomplete pages */
@@ -339,7 +339,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
/* Sync with user memory */
if (copy_from_user(sgl->fpage + sgl->fpage_offs,
user_addr, sgl->fpage_size)) {
- rc = -EFAULT;
+ ret = -EFAULT;
goto err_out;
}
}
@@ -352,7 +352,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
/* Sync with user memory */
if (copy_from_user(sgl->lpage, user_addr + user_size -
sgl->lpage_size, sgl->lpage_size)) {
- rc = -EFAULT;
+ ret = -EFAULT;
goto err_out2;
}
}
@@ -374,7 +374,8 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
sgl->sgl = NULL;
sgl->sgl_dma_addr = 0;
sgl->sgl_size = 0;
- return -ENOMEM;
+
+ return ret;
}
int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index eb4d90b..8b01257 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -985,6 +985,12 @@ static void kgdbts_run_tests(void)
int nmi_sleep = 0;
int i;
+ verbose = 0;
+ if (strstr(config, "V1"))
+ verbose = 1;
+ if (strstr(config, "V2"))
+ verbose = 2;
+
ptr = strchr(config, 'F');
if (ptr)
fork_test = simple_strtol(ptr + 1, NULL, 10);
@@ -1068,13 +1074,6 @@ static int kgdbts_option_setup(char *opt)
return -ENOSPC;
}
strcpy(config, opt);
-
- verbose = 0;
- if (strstr(config, "V1"))
- verbose = 1;
- if (strstr(config, "V2"))
- verbose = 2;
-
return 0;
}
@@ -1086,9 +1085,6 @@ static int configure_kgdbts(void)
if (!strlen(config) || isspace(config[0]))
goto noconfig;
- err = kgdbts_option_setup(config);
- if (err)
- goto noconfig;
final_ack = 0;
run_plant_and_detach_test(1);
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index c383322..bb2e138 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -883,15 +883,16 @@ static const struct device_type mei_cl_device_type = {
/**
* mei_cl_bus_set_name - set device name for me client device
+ * <controller>-<client device>
+ * Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb
*
* @cldev: me client device
*/
static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
{
- dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X",
- cldev->name,
- mei_me_cl_uuid(cldev->me_cl),
- mei_me_cl_ver(cldev->me_cl));
+ dev_set_name(&cldev->dev, "%s-%pUl",
+ dev_name(cldev->bus->dev),
+ mei_me_cl_uuid(cldev->me_cl));
}
/**
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index f85aa3f..9c40424 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -141,6 +141,7 @@
#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */
#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */
+#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */
#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 28cdd87..41a10e3 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -107,6 +107,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)},
diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c
index cac3bcc..7bb929f 100644
--- a/drivers/misc/mic/scif/scif_fence.c
+++ b/drivers/misc/mic/scif/scif_fence.c
@@ -272,7 +272,7 @@ static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
dma_fail:
if (!x100)
dma_pool_free(ep->remote_dev->signal_pool, status,
- status->src_dma_addr);
+ src - offsetof(struct scif_status, val));
alloc_fail:
return err;
}
diff --git a/drivers/misc/msm_hdcp.c b/drivers/misc/msm_hdcp.c
index 85b246b..63e149b 100644
--- a/drivers/misc/msm_hdcp.c
+++ b/drivers/misc/msm_hdcp.c
@@ -202,6 +202,8 @@ static ssize_t min_level_change_store(struct device *dev,
if (hdcp->cb && hdcp->client_ctx)
hdcp->cb(hdcp->client_ctx, min_enc_lvl);
+ pr_debug("min_enc_lvl = %d\n", min_enc_lvl);
+
return ret;
}
diff --git a/drivers/misc/qxr-stdalonevwr.c b/drivers/misc/qxr-stdalonevwr.c
new file mode 100644
index 0000000..c3fd763
--- /dev/null
+++ b/drivers/misc/qxr-stdalonevwr.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+struct qxr_stdalonevwr {
+ struct platform_device *pdev;
+ struct regulator *reg_imu;
+ int ndi_5v_en;
+ bool initDone;
+};
+
+static struct qxr_stdalonevwr *pdata;
+
+static int qxr_stdalonevwr_allocate_res(void)
+{
+ int rc = -EINVAL;
+ bool gpioEnabled = false;
+
+ if (pdata->initDone) {
+ pr_debug("%s init is done already\n", __func__);
+ return 0;
+ }
+ /* Invensense 3.3 PowerRail */
+ pdata->reg_imu = devm_regulator_get(&pdata->pdev->dev, "pm8150a_l11");
+ if (!IS_ERR(pdata->reg_imu)) {
+ regulator_set_load(pdata->reg_imu, 600000);
+ rc = regulator_enable(pdata->reg_imu);
+ if (rc < 0) {
+ pr_err("%s IMU rail pm8150a_l11 failed\n", __func__);
+ devm_regulator_put(pdata->reg_imu);
+ }
+ }
+
+ if (gpio_is_valid(pdata->ndi_5v_en)) {
+ rc = gpio_request(pdata->ndi_5v_en, "ndi_5v_en");
+ if (!rc) {
+ rc = gpio_direction_output(pdata->ndi_5v_en, 0);
+ if (!rc) {
+ gpio_set_value(pdata->ndi_5v_en, 1);
+ gpioEnabled = true;
+ msleep(20);
+ }
+ }
+ }
+ if (!gpioEnabled) {
+ pr_err("%s NDI_5V_EN gpio failed to allocate\n", __func__);
+ gpio_free(pdata->ndi_5v_en);
+ }
+ pdata->initDone = true;
+ pr_debug("%s rc:%d\n", __func__, rc);
+ return rc;
+}
+
+static void qxr_stdalonevwr_free_res(void)
+{
+ if (pdata->initDone) {
+ if (pdata->reg_imu) {
+ regulator_disable(pdata->reg_imu);
+ devm_regulator_put(pdata->reg_imu);
+ }
+ gpio_free(pdata->ndi_5v_en);
+ pdata->initDone = false;
+ }
+ pr_debug("%s initDone:%d\n", __func__, pdata->initDone);
+}
+
+static int qxr_stdalonevwr_probe(struct platform_device *pdev)
+{
+ pdata = devm_kzalloc(&pdev->dev, sizeof(struct qxr_stdalonevwr),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->pdev = pdev;
+ pdata->ndi_5v_en = 1237;
+ pdata->initDone = false;
+ qxr_stdalonevwr_allocate_res();
+ pr_info("%s done\n", __func__);
+ return 0;
+}
+
+static int qxr_stdalonevrw_pm_suspend(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ if (pdata)
+ qxr_stdalonevwr_free_res();
+ return 0;
+}
+
+static int qxr_stdalonevrw_pm_resume(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ if (pdata)
+ qxr_stdalonevwr_allocate_res();
+ return 0;
+}
+
+static int qxr_stdalonevrw_pm_freeze(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ return qxr_stdalonevrw_pm_suspend(dev);
+};
+
+static int qxr_stdalonevrw_pm_restore(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ return qxr_stdalonevrw_pm_resume(dev);
+};
+
+static int qxr_stdalonevrw_pm_poweroff(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ return qxr_stdalonevrw_pm_suspend(dev);
+};
+
+static int qxr_stdalonevrw_pm_thaw(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+ return qxr_stdalonevrw_pm_resume(dev);
+};
+
+static const struct dev_pm_ops qxr_stdalonevwr_dev_pm_ops = {
+ .suspend = qxr_stdalonevrw_pm_suspend,
+ .resume = qxr_stdalonevrw_pm_resume,
+ .freeze = qxr_stdalonevrw_pm_freeze,
+ .restore = qxr_stdalonevrw_pm_restore,
+ .thaw = qxr_stdalonevrw_pm_thaw,
+ .poweroff = qxr_stdalonevrw_pm_poweroff,
+};
+
+static const struct of_device_id qxr_stdalonevwr_match_table[] = {
+ { .compatible = "qcom,xr-stdalonevwr-misc", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, qxr_stdalonevwr_match_table);
+
+static struct platform_driver qxr_stdalonevwr_driver = {
+ .driver = {
+ .name = "qxr-stdalonevwr",
+ .of_match_table = qxr_stdalonevwr_match_table,
+ .pm = &qxr_stdalonevwr_dev_pm_ops,
+ },
+ .probe = qxr_stdalonevwr_probe,
+};
+
+module_platform_driver(qxr_stdalonevwr_driver);
+
+MODULE_DESCRIPTION("QTI XR STANDALONE MISC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/wigig_sensing.c b/drivers/misc/wigig_sensing.c
index c0cf0ef..b8d4d6f 100644
--- a/drivers/misc/wigig_sensing.c
+++ b/drivers/misc/wigig_sensing.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2019, The Linux foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux foundation. All rights reserved.
*/
#include <linux/cdev.h>
#include <linux/circ_buf.h>
@@ -15,6 +15,7 @@
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
+#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -44,6 +45,35 @@
#define circ_space_to_end(circ, size) \
((CIRC_SPACE_TO_END((circ)->head, (circ)->tail, size)) & ~3)
+#ifdef CONFIG_DEBUG_FS
+#define SPI_STATS_MEAS_INIT(ctx, idx, name_str) \
+ do { \
+ strlcpy((ctx)->spi_stats[idx].name, name_str, \
+ SPI_STATS_MAX_NAME_LEN); \
+ atomic64_set(&(ctx)->spi_stats[idx].min, U64_MAX); \
+ } while (0)
+
+#define SPI_STATS_MEAS_START(ctx, idx) (ctx)->spi_stats[idx].start = ktime_get()
+
+#define SPI_STATS_MEAS_STOP(ctx, idx) \
+ do { \
+ struct spi_stats *ss = &(ctx)->spi_stats[idx]; \
+ u64 min = atomic64_read(&ss->min); \
+ u64 max = atomic64_read(&ss->max); \
+ \
+ ss->delta = ktime_sub(ktime_get(), ss->start); \
+ atomic64_set(&ss->min, (ktime_to_us(ss->delta) != 0) ? \
+ min_t(u64, min, ss->delta) : min); \
+ atomic64_set(&ss->max, max_t(u64, max, ss->delta)); \
+ atomic64_add(ss->delta, &ss->acc); \
+ atomic_inc(&ss->num_meas); \
+ } while (0)
+#else /* CONFIG_DEBUG_FS */
+#define SPI_STATS_MEAS_INIT(ctx, idx, name_str)
+#define SPI_STATS_MEAS_START(ctx, idx)
+#define SPI_STATS_MEAS_STOP(ctx, idx)
+#endif /* CONFIG_DEBUG_FS */
+
struct wigig_sensing_platform_data {
struct gpio_desc *dri_gpio;
};
@@ -809,17 +839,16 @@ static long wigig_sensing_ioctl(struct file *file, unsigned int cmd,
case WIGIG_SENSING_IOCTL_CHANGE_MODE:
{
struct wigig_sensing_change_mode req;
-
pr_info("Received WIGIG_SENSING_IOCTL_CHANGE_MODE command\n");
if (copy_from_user(&req, (void *)arg, sizeof(req)))
return -EFAULT;
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_CHANGE_MODE);
rc = wigig_sensing_ioc_change_mode(ctx, &req);
-
if (copy_to_user((void *)arg, &req, sizeof(req)))
return -EFAULT;
-
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_CHANGE_MODE);
break;
}
case WIGIG_SENSING_IOCTL_CLEAR_DATA:
@@ -892,35 +921,35 @@ static int wigig_sensing_deassert_dri(
return rc;
}
-#define MAX_SPI_READ_CHUNKS (10)
/*
- * Calculate SPI transaction size so that the size is smaller than the maximum
- * size and the size is equal per iteration. The equal sizes causes less SPI
- * transactions since the length register does not need reprogramming.
+ * Calculate SPI transaction size so that the size is between the minimum size
+ * and two times the minimum size. The size must also divide the burst size.
+ * The motivaion for using equal sized transactions is to prevent reprogramming
+ * of the length register for every transaction.
*/
-static u32 calc_spi_transaction_size(
- u32 fill_level, u32 max_spi_transaction_size)
+static u32 calc_spi_transaction_size(u32 burst_size,
+ u32 min_spi_transaction_size)
{
- int i;
- u32 res;
+ u32 i, res;
- if (fill_level <= max_spi_transaction_size)
- return fill_level;
+ if (burst_size <= min_spi_transaction_size)
+ return burst_size;
for (i = 2; i < MAX_SPI_READ_CHUNKS; i++) {
- res = fill_level / i;
- if (res <= max_spi_transaction_size && fill_level % i == 0)
+ res = burst_size / i;
+ if (burst_size % res == 0 &&
+ res >= min_spi_transaction_size &&
+ res < 2 * min_spi_transaction_size)
return res;
}
- return max_spi_transaction_size;
+ return min_spi_transaction_size;
}
static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx)
{
int rc = 0;
u32 burst_size = 0;
- u32 spi_transaction_size;
mutex_lock(&ctx->spi_lock);
@@ -944,15 +973,17 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx)
}
/* Program burst size into the transfer length register */
- spi_transaction_size = calc_spi_transaction_size(burst_size,
- SPI_MAX_TRANSACTION_SIZE);
+ ctx->spi_transaction_size =
+ calc_spi_transaction_size(burst_size, SPI_MIN_TRANSACTION_SIZE);
+ pr_info_ratelimited("spi_transaction_size = %u\n",
+ ctx->spi_transaction_size);
rc = spis_write_reg(ctx->spi_dev, SPIS_TRNS_LEN_REG_ADDR,
- spi_transaction_size << 16);
+ ctx->spi_transaction_size << 16);
if (rc) {
pr_err("Failed setting SPIS_TRNS_LEN_REG_ADDR\n");
goto End;
}
- ctx->last_read_length = spi_transaction_size;
+ ctx->last_read_length = ctx->spi_transaction_size;
ctx->stm.burst_size_ready = true;
@@ -960,15 +991,14 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx)
* Allocate a temporary buffer to be used in case of cir_data buffer
* wrap around
*/
+ vfree(ctx->temp_buffer);
+ ctx->temp_buffer = 0;
if (burst_size != 0) {
ctx->temp_buffer = vmalloc(burst_size);
if (!ctx->temp_buffer) {
rc = -ENOMEM;
goto End;
}
- } else if (ctx->temp_buffer) {
- vfree(ctx->temp_buffer);
- ctx->temp_buffer = 0;
}
/* Change internal state */
@@ -1000,20 +1030,15 @@ static int wigig_sensing_chip_data_ready_internal(struct wigig_sensing_ctx *ctx,
struct cir_data *d = &ctx->cir_data;
struct circ_buf local;
u32 bytes_to_read;
- u32 spi_transaction_size;
u32 available_space_to_end;
- u32 orig_head;
/*
* Make sure that fill_level is in 32 bit units
*/
fill_level = fill_level & ~0x3;
- spi_transaction_size =
- calc_spi_transaction_size(fill_level, SPI_MAX_TRANSACTION_SIZE);
local = d->b;
local.head = (local.head + *offset) & (d->size_bytes - 1);
- orig_head = local.head;
mutex_lock(&ctx->spi_lock);
while (fill_level > 0) {
if (ctx->stm.change_mode_in_progress) {
@@ -1021,8 +1046,8 @@ static int wigig_sensing_chip_data_ready_internal(struct wigig_sensing_ctx *ctx,
break;
}
- bytes_to_read = (fill_level < spi_transaction_size) ?
- fill_level : spi_transaction_size;
+ bytes_to_read = (fill_level < SPI_MAX_TRANSACTION_SIZE) ?
+ fill_level : SPI_MAX_TRANSACTION_SIZE;
available_space_to_end =
circ_space_to_end(&local, d->size_bytes);
pr_debug("fill_level=%u, bytes_to_read=%u, offset=%u, available_space_to_end = %u\n",
@@ -1101,15 +1126,24 @@ static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx,
}
while (read_bytes < burst_size) {
- rc = wigig_sensing_chip_data_ready_internal(ctx, fill_level,
- &read_bytes);
- if (rc) {
- if (ctx->stm.change_mode_in_progress)
- pr_err("change_mode_in_progress, aborting SPI transactions\n");
- else
- pr_err("wigig_sensing_chip_data_ready_internal failed, err %d\n",
- rc);
- return rc;
+ if (fill_level >= ctx->spi_transaction_size) {
+ u32 txn_size = (fill_level >= burst_size) ? burst_size :
+ ctx->spi_transaction_size;
+ rc = wigig_sensing_chip_data_ready_internal(
+ ctx, txn_size, &read_bytes);
+ if (rc) {
+ if (ctx->stm.change_mode_in_progress)
+ pr_err("change_mode_in_progress, aborting SPI transactions\n");
+ else
+ pr_err("wigig_sensing_chip_data_ready_internal failed, err %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (ctx->stm.change_mode_in_progress) {
+ read_bytes = 0;
+ break;
}
if (read_bytes == burst_size)
@@ -1117,11 +1151,13 @@ static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx,
/* Read fill_level again */
pr_debug("Reading RGF_USER_SPI_SPI_MBOX_FILL_STATUS register\n");
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_MBOX_FILL_STATUS);
mutex_lock(&ctx->spi_lock);
rc = spis_read_reg(ctx->spi_dev,
RGF_USER_SPI_SPI_MBOX_FILL_STATUS,
&spi_status.v);
mutex_unlock(&ctx->spi_lock);
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_MBOX_FILL_STATUS);
if (rc) {
pr_err("Fail to read RGF_USER_SPI_SPI_MBOX_FILL_STATUS, err %d\n",
rc);
@@ -1258,10 +1294,12 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie)
}
pr_debug("Reading SANITY register\n");
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_SANITY);
mutex_lock(&ctx->spi_lock);
rc = spis_read_reg(ctx->spi_dev, SPIS_SANITY_REG_ADDR,
&sanity_reg);
mutex_unlock(&ctx->spi_lock);
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_SANITY);
if (rc || sanity_reg != SPIS_SANITY_REG_VAL) {
pr_err("Fail to read SANITY, expected 0x%X found 0x%X err %d\n",
SPIS_SANITY_REG_VAL, sanity_reg, (int)rc);
@@ -1280,11 +1318,12 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie)
}
pr_debug("Reading RGF_USER_SPI_SPI_MBOX_FILL_STATUS register\n");
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_MBOX_FILL_STATUS);
mutex_lock(&ctx->spi_lock);
rc = spis_read_reg(ctx->spi_dev, RGF_USER_SPI_SPI_MBOX_FILL_STATUS,
&spi_status.v);
mutex_unlock(&ctx->spi_lock);
-
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_MBOX_FILL_STATUS);
if (rc) {
pr_err("Fail to read RGF_USER_SPI_SPI_MBOX_FILL_STATUS, err %d\n",
rc);
@@ -1330,12 +1369,15 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie)
spi_status.v &= ~INT_FW_READY;
}
if (spi_status.b.int_data_ready) {
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_DATA_READY);
pr_debug("DATA READY INTERRUPT\n");
if (!ctx->stm.change_mode_in_progress)
- wigig_sensing_chip_data_ready(ctx,
- spi_status.b.fill_level, ctx->stm.burst_size);
+ wigig_sensing_chip_data_ready(
+ ctx, spi_status.b.fill_level, ctx->stm.burst_size);
else
pr_debug("Change mode in progress, aborting data processing\n");
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_DATA_READY);
+
spi_status.v &= ~INT_DATA_READY;
}
if (spi_status.b.int_deep_sleep_exit ||
@@ -1372,19 +1414,125 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie)
deassert_and_bail_out:
/* Notify FW we are done with interrupt handling */
if (!dont_deassert || additional_inb_command.b.mode != 0) {
+ SPI_STATS_MEAS_START(ctx, SPI_STATS_MEAS_DEASSERT);
rc = wigig_sensing_deassert_dri(ctx, additional_inb_command);
if (rc)
pr_err("wigig_sensing_deassert_dri() failed, rc=%d\n",
rc);
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_DEASSERT);
}
bail_out:
mutex_unlock(&ctx->dri_lock);
+ SPI_STATS_MEAS_STOP(ctx, SPI_STATS_MEAS_DRI_PROC);
+
return IRQ_HANDLED;
}
+static int wigig_sensing_debugfs_spi_stats_show(struct seq_file *s, void *data)
+{
+ struct wigig_sensing_ctx *ctx = s->private;
+ int i;
+
+ if (ctx == NULL)
+ return -ENODEV;
+
+ seq_printf(s, "|%18s|%10s|%10s|%15s|%10s|%10s|\n",
+ "Name", "Min [uS]", "Max [uS]", "Acc [uS]", "#", "Avg [uS]");
+ for (i = 0; i < SPI_STATS_MEAS_MAX; i++) {
+ struct spi_stats *ss = &ctx->spi_stats[i];
+ u64 min = atomic64_read(&ss->min);
+ u64 max = atomic64_read(&ss->max);
+ u64 acc = atomic64_read(&ss->acc);
+ u64 num_meas = atomic_read(&ss->num_meas);
+
+ seq_printf(s, "|%18s|%10lu|%10lu|%15llu|%10lu|%10lu|\n",
+ ss->name,
+ ktime_to_us(min),
+ ktime_to_us(max),
+ ktime_to_us(acc),
+ num_meas,
+ ktime_to_us((num_meas != 0) ? acc / num_meas : 0));
+ }
+
+ return 0;
+}
+
+static int wigig_sensing_seq_spi_stats_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, wigig_sensing_debugfs_spi_stats_show,
+ inode->i_private);
+}
+
+static const struct file_operations debugfs_spi_stats_fops = {
+ .open = wigig_sensing_seq_spi_stats_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+static int wigig_sensing_debugfs_spi_stats_init_open(struct seq_file *s,
+ void *data)
+{
+ struct wigig_sensing_ctx *ctx = s->private;
+ int i;
+
+ if (ctx == NULL)
+ return -ENODEV;
+
+ for (i = 0; i < SPI_STATS_MEAS_MAX; i++) {
+ struct spi_stats *ss = &ctx->spi_stats[i];
+
+ atomic64_set(&ss->min, U64_MAX);
+ atomic64_set(&ss->max, 0);
+ atomic64_set(&ss->acc, 0);
+ atomic_set(&ss->num_meas, 0);
+ ss->start = 0;
+ ss->delta = 0;
+ }
+
+ return 0;
+}
+
+static int wigig_sensing_seq_spi_stats_init_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, wigig_sensing_debugfs_spi_stats_init_open,
+ inode->i_private);
+}
+
+static const struct file_operations debugfs_spi_stats_init_fops = {
+ .open = wigig_sensing_seq_spi_stats_init_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+static int wigig_sensing_debugfs_init(struct wigig_sensing_ctx *ctx)
+{
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_SANITY, "Sanity");
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_DEASSERT, "Deassert DRI");
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_DRI_PROC, "DRI proc");
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_MBOX_FILL_STATUS,
+ "MBOX FILL STATUS");
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_CHANGE_MODE, "CHANGE MODE");
+ SPI_STATS_MEAS_INIT(ctx, SPI_STATS_MEAS_DATA_READY, "Data Ready");
+
+ ctx->debugfs_dent = debugfs_create_dir("wigig_sensing", NULL);
+ debugfs_create_file("spi_stats", 0644, ctx->debugfs_dent, ctx,
+ &debugfs_spi_stats_fops);
+ debugfs_create_file("spi_stats_init", 0644, ctx->debugfs_dent, ctx,
+ &debugfs_spi_stats_init_fops);
+
+ return 0;
+}
+
static irqreturn_t wigig_sensing_dri_isr_hard(int irq, void *cookie)
{
+ SPI_STATS_MEAS_START((struct wigig_sensing_ctx *)cookie,
+ SPI_STATS_MEAS_DRI_PROC);
+
return IRQ_WAKE_THREAD;
}
@@ -1469,6 +1617,9 @@ static int wigig_sensing_probe(struct spi_device *spi)
goto fail_gpiod_get;
}
+ /* Initialize debugfs */
+ wigig_sensing_debugfs_init(ctx);
+
return 0;
fail_gpiod_get:
@@ -1496,6 +1647,7 @@ static int wigig_sensing_remove(struct spi_device *spi)
/* Make sure that FW is in STOP mode */
wigig_sensing_ioc_change_mode(ctx, &req);
+ debugfs_remove_recursive(ctx->debugfs_dent);
device_destroy(ctx->class, ctx->wigig_sensing_dev);
unregister_chrdev_region(ctx->wigig_sensing_dev, 1);
class_destroy(ctx->class);
diff --git a/drivers/misc/wigig_sensing.h b/drivers/misc/wigig_sensing.h
index d53eead..4c449bc 100644
--- a/drivers/misc/wigig_sensing.h
+++ b/drivers/misc/wigig_sensing.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2019, The Linux foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux foundation. All rights reserved.
*/
#ifndef __WIGIG_SENSING_H__
@@ -8,6 +8,7 @@
#include <linux/cdev.h>
#include <linux/circ_buf.h>
#include <linux/kfifo.h>
+#include <linux/ktime.h>
#include <linux/slab.h>
#include <uapi/misc/wigig_sensing_uapi.h>
@@ -41,6 +42,8 @@
#define SPIS_CONFIG_REG_OPT_VAL (0x44200800)
#define SPIS_EXTENDED_RESET_COMMAND_LEN (225)
+#define MAX_SPI_READ_CHUNKS (10)
+#define SPI_MIN_TRANSACTION_SIZE (512)
#define SPI_MAX_TRANSACTION_SIZE (8*1024)
#define SPI_CMD_TRANSACTION_SIZE (512)
#define SPI_BUFFER_SIZE (SPI_MAX_TRANSACTION_SIZE + OPCODE_WIDTH + \
@@ -160,12 +163,34 @@ struct wigig_sensing_stm {
enum wigig_sensing_mode mode_request;
};
+enum spi_stats_meas {
+ SPI_STATS_MEAS_MIN,
+ SPI_STATS_MEAS_SANITY = SPI_STATS_MEAS_MIN,
+ SPI_STATS_MEAS_DEASSERT,
+ SPI_STATS_MEAS_DRI_PROC,
+ SPI_STATS_MEAS_MBOX_FILL_STATUS,
+ SPI_STATS_MEAS_CHANGE_MODE,
+ SPI_STATS_MEAS_DATA_READY,
+ SPI_STATS_MEAS_MAX,
+};
+
+#define SPI_STATS_MAX_NAME_LEN (20)
+struct spi_stats {
+ char name[SPI_STATS_MAX_NAME_LEN];
+ atomic64_t min;
+ atomic64_t max;
+ atomic64_t acc;
+ atomic_t num_meas;
+ ktime_t start, delta;
+};
+
struct wigig_sensing_ctx {
dev_t wigig_sensing_dev;
struct cdev cdev;
struct class *class;
struct device *dev;
struct spi_device *spi_dev;
+ struct dentry *debugfs_dent;
/* Locks */
struct mutex ioctl_lock;
@@ -191,6 +216,7 @@ struct wigig_sensing_ctx {
struct wigig_sensing_stm stm;
u32 last_read_length;
union user_rgf_spi_mbox_inb inb_cmd;
+ u32 spi_transaction_size;
/* CIR buffer */
struct cir_data cir_data;
@@ -198,6 +224,9 @@ struct wigig_sensing_ctx {
bool event_pending;
DECLARE_KFIFO(events_fifo, enum wigig_sensing_event, 8);
u32 dropped_bursts;
+
+ /* Statistics */
+ struct spi_stats spi_stats[SPI_STATS_MEAS_MAX];
};
#endif /* __WIGIG_SENSING_H__ */
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index f1953c9..6ef9a94 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -424,38 +424,6 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
return 0;
}
-static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
- u32 retries_max)
-{
- int err;
- u32 retry_count = 0;
-
- if (!status || !retries_max)
- return -EINVAL;
-
- do {
- err = __mmc_send_status(card, status, 5);
- if (err)
- break;
-
- if (!R1_STATUS(*status) &&
- (R1_CURRENT_STATE(*status) != R1_STATE_PRG))
- break; /* RPMB programming operation complete */
-
- /*
- * Rechedule to give the MMC device a chance to continue
- * processing the previous command without being polled too
- * frequently.
- */
- usleep_range(1000, 5000);
- } while (++retry_count < retries_max);
-
- if (retry_count == retries_max)
- err = -EPERM;
-
- return err;
-}
-
static int ioctl_do_sanitize(struct mmc_card *card)
{
int err;
@@ -485,6 +453,58 @@ static int ioctl_do_sanitize(struct mmc_card *card)
return err;
}
+static inline bool mmc_blk_in_tran_state(u32 status)
+{
+ /*
+ * Some cards mishandle the status bits, so make sure to check both the
+ * busy indication and the card state.
+ */
+ return status & R1_READY_FOR_DATA &&
+ (R1_CURRENT_STATE(status) == R1_STATE_TRAN);
+}
+
+static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+ u32 *resp_errs)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ int err = 0;
+ u32 status;
+
+ do {
+ bool done = time_after(jiffies, timeout);
+
+ err = __mmc_send_status(card, &status, 5);
+ if (err) {
+ dev_err(mmc_dev(card->host),
+ "error %d requesting status\n", err);
+ return err;
+ }
+
+ /* Accumulate any response error bits seen */
+ if (resp_errs)
+ *resp_errs |= status;
+
+ /*
+ * Timeout if the device never becomes ready for data and never
+ * leaves the program state.
+ */
+ if (done) {
+ dev_err(mmc_dev(card->host),
+ "Card stuck in wrong state! %s status: %#x\n",
+ __func__, status);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ } while (!mmc_blk_in_tran_state(status));
+
+ return err;
+}
+
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_blk_ioc_data *idata)
{
@@ -494,7 +514,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct scatterlist sg;
int err;
unsigned int target_part;
- u32 status = 0;
if (!card || !md || !idata)
return -EINVAL;
@@ -628,16 +647,12 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
- if (idata->rpmb) {
+ if (idata->rpmb || (cmd.flags & MMC_RSP_R1B)) {
/*
- * Ensure RPMB command has completed by polling CMD13
+ * Ensure RPMB/R1B command has completed by polling CMD13
* "Send Status".
*/
- err = ioctl_rpmb_card_status_poll(card, &status, 5);
- if (err)
- dev_err(mmc_dev(card->host),
- "%s: Card Status=0x%08X, error %d\n",
- __func__, status, err);
+ err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, NULL);
}
return err;
@@ -992,58 +1007,6 @@ static unsigned int mmc_blk_data_timeout_ms(struct mmc_host *host,
return ms;
}
-static inline bool mmc_blk_in_tran_state(u32 status)
-{
- /*
- * Some cards mishandle the status bits, so make sure to check both the
- * busy indication and the card state.
- */
- return status & R1_READY_FOR_DATA &&
- (R1_CURRENT_STATE(status) == R1_STATE_TRAN);
-}
-
-static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
- struct request *req, u32 *resp_errs)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
- int err = 0;
- u32 status;
-
- do {
- bool done = time_after(jiffies, timeout);
-
- err = __mmc_send_status(card, &status, 5);
- if (err) {
- pr_err("%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- return err;
- }
-
- /* Accumulate any response error bits seen */
- if (resp_errs)
- *resp_errs |= status;
-
- /*
- * Timeout if the device never becomes ready for data and never
- * leaves the program state.
- */
- if (done) {
- pr_err("%s: Card stuck in wrong state! %s %s status: %#x\n",
- mmc_hostname(card->host),
- req->rq_disk->disk_name, __func__, status);
- return -ETIMEDOUT;
- }
-
- /*
- * Some cards mishandle the status bits,
- * so make sure to check both the busy
- * indication and the card state.
- */
- } while (!mmc_blk_in_tran_state(status));
-
- return err;
-}
-
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
int type)
{
@@ -1715,7 +1678,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
mmc_blk_send_stop(card, timeout);
- err = card_busy_detect(card, timeout, req, NULL);
+ err = card_busy_detect(card, timeout, NULL);
mmc_retune_release(card->host);
@@ -1941,7 +1904,7 @@ static int mmc_blk_card_busy(struct mmc_card *card, struct request *req)
if (mmc_host_is_spi(card->host) || rq_data_dir(req) == READ)
return 0;
- err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, req, &status);
+ err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, &status);
/*
* Do not assume data transferred correctly if there are any error bits
@@ -2442,12 +2405,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
"mmcblk%u%s", card->host->index, subname ? subname : "");
- if (mmc_card_mmc(card))
- blk_queue_logical_block_size(md->queue.queue,
- card->ext_csd.data_sector_size);
- else
- blk_queue_logical_block_size(md->queue.queue, 512);
-
set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) {
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ec7eaaf..ed8dbb3 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1258,7 +1258,8 @@ static int mmc_select_hs400(struct mmc_card *card)
goto out_err;
val = EXT_CSD_DDR_BUS_WIDTH_8;
- if (card->ext_csd.strobe_support) {
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES
+ && card->ext_csd.strobe_support) {
err = mmc_select_bus_width(card);
if (IS_ERR_VALUE((unsigned long)err))
return err;
@@ -1293,7 +1294,9 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
- if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) {
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES
+ && card->ext_csd.strobe_support
+ && host->ops->enhanced_strobe) {
err = host->ops->enhanced_strobe(host);
if (!err)
host->ios.enhanced_strobe = true;
@@ -1611,8 +1614,7 @@ static int mmc_select_timing(struct mmc_card *card)
goto bus_speed;
/* For Enhance Strobe HS400 flow */
- if (card->ext_csd.strobe_support &&
- card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES &&
card->host->caps & MMC_CAP_8_BIT_DATA) {
err = mmc_select_hs400(card);
if (err) {
@@ -2697,14 +2699,14 @@ static int mmc_suspend(struct mmc_host *host)
*/
static int _mmc_resume(struct mmc_host *host)
{
- int err = -EINVAL;
+ int err = 0;
int retries = 3;
mmc_claim_host(host);
if (!mmc_card_suspended(host->card)) {
mmc_release_host(host);
- goto out;
+ return err;
}
mmc_log_string(host, "Enter\n");
@@ -2719,7 +2721,7 @@ static int _mmc_resume(struct mmc_host *host)
mmc_hostname(host), __func__,
err);
}
- if (err) {
+ if (!mmc_can_sleepawake(host) || err) {
err = mmc_init_card(host, host->card->ocr, host->card);
if (err) {
pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n",
@@ -2744,7 +2746,7 @@ static int _mmc_resume(struct mmc_host *host)
if (err)
pr_err("%s: %s: fail to resume clock scaling (%d)\n",
mmc_hostname(host), __func__, err);
-out:
+
return err;
}
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 7914131..a1e7d4f 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -366,7 +366,7 @@ int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
* As the ext_csd is so large and mostly unused, we don't store the
* raw block in mmc_card.
*/
- ext_csd = kzalloc(512, GFP_KERNEL);
+ ext_csd = kzalloc(512, GFP_NOIO | __GFP_NOFAIL);
if (!ext_csd)
return -ENOMEM;
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 13ed3ac..f8d35fd 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -108,7 +108,7 @@ static enum blk_eh_timer_return mmc_cqe_timed_out(struct request *req)
case MMC_ISSUE_DCMD:
if (host->cqe_ops->cqe_timeout(host, mrq, &recovery_needed)) {
if (recovery_needed)
- __mmc_cqe_recovery_notifier(mq);
+ mmc_cqe_recovery_notifier(mrq);
return BLK_EH_RESET_TIMER;
}
/* No timeout (XXX: huh? comment doesn't make much sense) */
@@ -130,12 +130,13 @@ static enum blk_eh_timer_return mmc_mq_timed_out(struct request *req,
spin_lock_irqsave(q->queue_lock, flags);
- if (mq->recovery_needed || !mq->use_cqe)
+ if (mq->recovery_needed || !mq->use_cqe) {
ret = BLK_EH_RESET_TIMER;
- else
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ } else {
+ spin_unlock_irqrestore(q->queue_lock, flags);
ret = mmc_cqe_timed_out(req);
-
- spin_unlock_irqrestore(q->queue_lock, flags);
+ }
return ret;
}
@@ -360,6 +361,7 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
{
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
+ unsigned block_size = 512;
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
@@ -373,13 +375,19 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_segments(mq->queue, host->max_segs);
- blk_queue_max_segment_size(mq->queue, host->max_seg_size);
if (host->inlinecrypt_support)
queue_flag_set_unlocked(QUEUE_FLAG_INLINECRYPT, mq->queue);
if (host->ops->init)
host->ops->init(host);
+ if (mmc_card_mmc(card))
+ block_size = card->ext_csd.data_sector_size;
+
+ blk_queue_logical_block_size(mq->queue, block_size);
+ blk_queue_max_segment_size(mq->queue,
+ round_down(host->max_seg_size, block_size));
+
INIT_WORK(&mq->recovery_work, mmc_mq_recovery_handler);
INIT_WORK(&mq->complete_work, mmc_blk_mq_complete_work);
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index ddd98cdd..72f34a5 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -90,9 +91,11 @@
#define CFG_CLK_ALWAYS_ON BIT(18)
#define CFG_CHK_DS BIT(20)
#define CFG_AUTO_CLK BIT(23)
+#define CFG_ERR_ABORT BIT(27)
#define SD_EMMC_STATUS 0x48
#define STATUS_BUSY BIT(31)
+#define STATUS_DESC_BUSY BIT(30)
#define STATUS_DATI GENMASK(23, 16)
#define SD_EMMC_IRQ_EN 0x4c
@@ -930,6 +933,7 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode);
cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
+ cmd_cfg |= CMD_CFG_ERROR; /* stop in case of error */
meson_mmc_set_response_bits(cmd, &cmd_cfg);
@@ -1024,6 +1028,17 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
u32 irq_en, status, raw_status;
irqreturn_t ret = IRQ_NONE;
+ irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
+ raw_status = readl(host->regs + SD_EMMC_STATUS);
+ status = raw_status & irq_en;
+
+ if (!status) {
+ dev_dbg(host->dev,
+ "Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n",
+ irq_en, raw_status);
+ return IRQ_NONE;
+ }
+
if (WARN_ON(!host) || WARN_ON(!host->cmd))
return IRQ_NONE;
@@ -1031,22 +1046,18 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
cmd = host->cmd;
data = cmd->data;
- irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
- raw_status = readl(host->regs + SD_EMMC_STATUS);
- status = raw_status & irq_en;
-
cmd->error = 0;
if (status & IRQ_CRC_ERR) {
dev_dbg(host->dev, "CRC Error - status 0x%08x\n", status);
cmd->error = -EILSEQ;
- ret = IRQ_HANDLED;
+ ret = IRQ_WAKE_THREAD;
goto out;
}
if (status & IRQ_TIMEOUTS) {
dev_dbg(host->dev, "Timeout - status 0x%08x\n", status);
cmd->error = -ETIMEDOUT;
- ret = IRQ_HANDLED;
+ ret = IRQ_WAKE_THREAD;
goto out;
}
@@ -1071,17 +1082,49 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
/* ack all enabled interrupts */
writel(irq_en, host->regs + SD_EMMC_STATUS);
+ if (cmd->error) {
+ /* Stop desc in case of errors */
+ u32 start = readl(host->regs + SD_EMMC_START);
+
+ start &= ~START_DESC_BUSY;
+ writel(start, host->regs + SD_EMMC_START);
+ }
+
if (ret == IRQ_HANDLED)
meson_mmc_request_done(host->mmc, cmd->mrq);
- else if (ret == IRQ_NONE)
- dev_warn(host->dev,
- "Unexpected IRQ! status=0x%08x, irq_en=0x%08x\n",
- raw_status, irq_en);
spin_unlock(&host->lock);
return ret;
}
+static int meson_mmc_wait_desc_stop(struct meson_host *host)
+{
+ int loop;
+ u32 status;
+
+ /*
+ * It may sometimes take a while for it to actually halt. Here, we
+ * are giving it 5ms to comply
+ *
+ * If we don't confirm the descriptor is stopped, it might raise new
+ * IRQs after we have called mmc_request_done() which is bad.
+ */
+ for (loop = 50; loop; loop--) {
+ status = readl(host->regs + SD_EMMC_STATUS);
+ if (status & (STATUS_BUSY | STATUS_DESC_BUSY))
+ udelay(100);
+ else
+ break;
+ }
+
+ if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) {
+ dev_err(host->dev, "Timed out waiting for host to stop\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
{
struct meson_host *host = dev_id;
@@ -1092,6 +1135,13 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
if (WARN_ON(!cmd))
return IRQ_NONE;
+ if (cmd->error) {
+ meson_mmc_wait_desc_stop(host);
+ meson_mmc_request_done(host->mmc, cmd->mrq);
+
+ return IRQ_HANDLED;
+ }
+
data = cmd->data;
if (meson_mmc_bounce_buf_read(data)) {
xfer_bytes = data->blksz * data->blocks;
@@ -1132,6 +1182,9 @@ static void meson_mmc_cfg_init(struct meson_host *host)
cfg |= FIELD_PREP(CFG_RC_CC_MASK, ilog2(SD_EMMC_CFG_CMD_GAP));
cfg |= FIELD_PREP(CFG_BLK_LEN_MASK, ilog2(SD_EMMC_CFG_BLK_SIZE));
+ /* abort chain on R/W errors */
+ cfg |= CFG_ERR_ABORT;
+
writel(cfg, host->regs + SD_EMMC_CFG);
}
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index eb1a65c..fa6268c 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -895,14 +895,18 @@ static void
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
+ unsigned int status_err;
+
/* Make sure we have data to handle */
if (!data)
return;
/* First check for errors */
- if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
- host->variant->start_err |
- MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
+ status_err = status & (host->variant->start_err |
+ MCI_DATACRCFAIL | MCI_DATATIMEOUT |
+ MCI_TXUNDERRUN | MCI_RXOVERRUN);
+
+ if (status_err) {
u32 remain, success;
/* Terminate the DMA transfer */
@@ -922,18 +926,18 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
success = data->blksz * data->blocks - remain;
dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
- status, success);
- if (status & MCI_DATACRCFAIL) {
+ status_err, success);
+ if (status_err & MCI_DATACRCFAIL) {
/* Last block was not successful */
success -= 1;
data->error = -EILSEQ;
- } else if (status & MCI_DATATIMEOUT) {
+ } else if (status_err & MCI_DATATIMEOUT) {
data->error = -ETIMEDOUT;
- } else if (status & MCI_STARTBITERR) {
+ } else if (status_err & MCI_STARTBITERR) {
data->error = -ECOMM;
- } else if (status & MCI_TXUNDERRUN) {
+ } else if (status_err & MCI_TXUNDERRUN) {
data->error = -EIO;
- } else if (status & MCI_RXOVERRUN) {
+ } else if (status_err & MCI_RXOVERRUN) {
if (success > host->variant->fifosize)
success -= host->variant->fifosize;
else
@@ -1790,7 +1794,7 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable;
}
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc);
@@ -1877,7 +1881,8 @@ static void mmci_restore(struct mmci_host *host)
writel(host->datactrl_reg, host->base + MMCIDATACTRL);
writel(host->pwr_reg, host->base + MMCIPOWER);
}
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ writel(MCI_IRQENABLE | host->variant->start_err,
+ host->base + MMCIMASK0);
mmci_reg_delay(host);
spin_unlock_irqrestore(&host->lock, flags);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 517591d..613d37a 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -181,9 +181,9 @@
#define MMCIFIFO 0x080 /* to 0x0bc */
#define MCI_IRQENABLE \
- (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
- MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
- MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_STARTBITERRMASK)
+ (MCI_CMDCRCFAILMASK | MCI_DATACRCFAILMASK | MCI_CMDTIMEOUTMASK | \
+ MCI_DATATIMEOUTMASK | MCI_TXUNDERRUNMASK | MCI_RXOVERRUNMASK | \
+ MCI_CMDRESPENDMASK | MCI_CMDSENTMASK)
/* These interrupts are directed to IRQ1 when two IRQ lines are available */
#define MCI_IRQ1MASK \
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index f171cce..9ecf86b 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -228,6 +228,7 @@
#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+#define MSDC_PATCH_BIT1_CMDTA (0x7 << 3) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY (0xf << 8) /* RW */
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
@@ -390,7 +391,6 @@ struct msdc_host {
struct clk *src_clk_cg; /* msdc source clock control gate */
u32 mclk; /* mmc subsystem clock frequency */
u32 src_clk_freq; /* source clock frequency */
- u32 sclk; /* SD/MS bus clock frequency */
unsigned char timing;
bool vqmmc_enabled;
u32 latch_ck;
@@ -635,10 +635,10 @@ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
host->timeout_ns = ns;
host->timeout_clks = clks;
- if (host->sclk == 0) {
+ if (host->mmc->actual_clock == 0) {
timeout = 0;
} else {
- clk_ns = 1000000000UL / host->sclk;
+ clk_ns = 1000000000UL / host->mmc->actual_clock;
timeout = (ns + clk_ns - 1) / clk_ns + clks;
/* in 1048576 sclk cycle unit */
timeout = (timeout + (0x1 << 20) - 1) >> 20;
@@ -683,6 +683,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
host->mclk = 0;
+ host->mmc->actual_clock = 0;
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
return;
}
@@ -761,7 +762,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
cpu_relax();
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
- host->sclk = sclk;
+ host->mmc->actual_clock = sclk;
host->mclk = hz;
host->timing = timing;
/* need because clk changed. */
@@ -772,7 +773,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
* tune result of hs200/200Mhz is not suitable for 50Mhz
*/
- if (host->sclk <= 52000000) {
+ if (host->mmc->actual_clock <= 52000000) {
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
writel(host->def_tune_para.pad_tune, host->base + tune_reg);
} else {
@@ -787,7 +788,8 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
- dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
+ dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->mmc->actual_clock,
+ timing);
}
static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
@@ -1055,6 +1057,7 @@ static void msdc_start_command(struct msdc_host *host,
WARN_ON(host->cmd);
host->cmd = cmd;
+ mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
if (!msdc_cmd_is_ready(host, mrq, cmd))
return;
@@ -1066,7 +1069,6 @@ static void msdc_start_command(struct msdc_host *host,
cmd->error = 0;
rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
- mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
writel(cmd->arg, host->base + SDC_ARG);
@@ -1672,6 +1674,7 @@ static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
/* select EMMC50 PAD CMD tune */
sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
+ sdr_set_field(host->base + MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMDTA, 2);
if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
mmc->ios.timing == MMC_TIMING_UHS_SDR104)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b23c57e..0135693 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1661,6 +1661,36 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
if (mmc_pdata(host)->init_card)
mmc_pdata(host)->init_card(card);
+ else if (card->type == MMC_TYPE_SDIO ||
+ card->type == MMC_TYPE_SD_COMBO) {
+ struct device_node *np = mmc_dev(mmc)->of_node;
+
+ /*
+ * REVISIT: should be moved to sdio core and made more
+ * general e.g. by expanding the DT bindings of child nodes
+ * to provide a mechanism to provide this information:
+ * Documentation/devicetree/bindings/mmc/mmc-card.txt
+ */
+
+ np = of_get_compatible_child(np, "ti,wl1251");
+ if (np) {
+ /*
+ * We have TI wl1251 attached to MMC3. Pass this
+ * information to the SDIO core because it can't be
+ * probed by normal methods.
+ */
+
+ dev_info(host->dev, "found wl1251\n");
+ card->quirks |= MMC_QUIRK_NONSTD_SDIO;
+ card->cccr.wide_bus = 1;
+ card->cis.vendor = 0x104c;
+ card->cis.device = 0x9066;
+ card->cis.blksize = 512;
+ card->cis.max_dtr = 24000000;
+ card->ocr = 0x80;
+ of_node_put(np);
+ }
+ }
}
static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index ca0b439..382172f 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -298,6 +298,7 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = {
{ .soc_id = "r8a7796", .revision = "ES1.0",
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
/* generic ones */
+ { .soc_id = "r8a774a1" },
{ .soc_id = "r8a7795" },
{ .soc_id = "r8a7796" },
{ .soc_id = "r8a77965" },
@@ -309,12 +310,20 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = {
static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
{
const struct soc_device_attribute *soc = soc_device_match(gen3_soc_whitelist);
+ struct device *dev = &pdev->dev;
if (!soc)
return -ENODEV;
global_flags |= (unsigned long)soc->data;
+ dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
+ if (!dev->dma_parms)
+ return -ENOMEM;
+
+ /* value is max of SD_SECCNT. Confirmed by HW engineers */
+ dma_set_max_seg_size(dev, 0xffffffff);
+
return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
}
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 716add5..bfba885 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -31,6 +31,7 @@
#include <linux/msm-bus.h>
#include <linux/pm_runtime.h>
#include <trace/events/mmc.h>
+#include <linux/clk/qcom.h>
#include "sdhci-msm.h"
#include "sdhci-msm-ice.h"
@@ -145,8 +146,8 @@
#define CORE_DDR_CAL_EN (1 << 0)
#define CORE_FLL_CYCLE_CNT (1 << 18)
#define CORE_DLL_CLOCK_DISABLE (1 << 21)
+#define DDR_CONFIG_POR_VAL 0x80040873
-#define DDR_CONFIG_POR_VAL 0x80040873
#define DLL_USR_CTL_POR_VAL 0x10800
#define ENABLE_DLL_LOCK_STATUS (1 << 26)
#define FINE_TUNE_MODE_EN (1 << 27)
@@ -341,8 +342,11 @@ static const u32 tuning_block_128[] = {
static struct sdhci_msm_host *sdhci_slot[2];
static int disable_slots;
+static int bus_mode;
/* root can write, others read */
module_param(disable_slots, int, 0644);
+module_param(bus_mode, int, 0444);
+MODULE_PARM_DESC(bus_mode, "Set this parameter during bootup to set the desired bus speed mode for eMMC only.");
enum vdd_io_level {
/* set vdd_io_data->low_vol_level */
@@ -1980,6 +1984,42 @@ static int sdhci_msm_dt_parse_hsr_info(struct device *dev,
return ret;
}
+static int sdhci_msm_parse_regulator_info(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ int ret = 0;
+
+ pdata->vreg_data = devm_kzalloc(dev, sizeof(struct
+ sdhci_msm_slot_reg_data),
+ GFP_KERNEL);
+ if (!pdata->vreg_data) {
+ dev_err(dev, "failed to allocate memory for vreg data\n");
+ goto out;
+ }
+
+ if (sdhci_msm_dt_parse_vreg_info(dev, &pdata->vreg_data->vdd_data,
+ "vdd")) {
+ dev_err(dev, "failed parsing vdd data\n");
+ goto out;
+ }
+ if (sdhci_msm_dt_parse_vreg_info(dev,
+ &pdata->vreg_data->vdd_io_data,
+ "vdd-io")) {
+ dev_err(dev, "failed parsing vdd-io data\n");
+ goto out;
+ }
+
+ if (sdhci_msm_dt_parse_vreg_info(dev,
+ &pdata->vreg_data->vdd_io_bias_data,
+ "vdd-io-bias")) {
+ dev_err(dev, "No vdd-io-bias regulator data\n");
+ }
+
+ return ret;
+out:
+ return -EINVAL;
+}
+
/* Parse platform data */
static
struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
@@ -2080,25 +2120,8 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
MMC_SCALING_LOWER_DDR52_MODE;
}
- pdata->vreg_data = devm_kzalloc(dev, sizeof(struct
- sdhci_msm_slot_reg_data),
- GFP_KERNEL);
- if (!pdata->vreg_data) {
- dev_err(dev, "failed to allocate memory for vreg data\n");
+ if (sdhci_msm_parse_regulator_info(dev, pdata))
goto out;
- }
-
- if (sdhci_msm_dt_parse_vreg_info(dev, &pdata->vreg_data->vdd_data,
- "vdd")) {
- dev_err(dev, "failed parsing vdd data\n");
- goto out;
- }
- if (sdhci_msm_dt_parse_vreg_info(dev,
- &pdata->vreg_data->vdd_io_data,
- "vdd-io")) {
- dev_err(dev, "failed parsing vdd-io data\n");
- goto out;
- }
if (sdhci_msm_dt_parse_gpio_info(dev, pdata)) {
dev_err(dev, "failed parsing gpio data\n");
@@ -2652,7 +2675,7 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_pltfm_data *pdata,
{
int ret = 0, i;
struct sdhci_msm_slot_reg_data *curr_slot;
- struct sdhci_msm_reg_data *vreg_table[2];
+ struct sdhci_msm_reg_data *vreg_table[3];
curr_slot = pdata->vreg_data;
if (!curr_slot) {
@@ -2663,6 +2686,7 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_pltfm_data *pdata,
vreg_table[0] = curr_slot->vdd_data;
vreg_table[1] = curr_slot->vdd_io_data;
+ vreg_table[2] = curr_slot->vdd_io_bias_data;
for (i = 0; i < ARRAY_SIZE(vreg_table); i++) {
if (vreg_table[i]) {
@@ -2685,7 +2709,8 @@ static int sdhci_msm_vreg_init(struct device *dev,
{
int ret = 0;
struct sdhci_msm_slot_reg_data *curr_slot;
- struct sdhci_msm_reg_data *curr_vdd_reg, *curr_vdd_io_reg;
+ struct sdhci_msm_reg_data *curr_vdd_reg, *curr_vdd_io_reg,
+ *curr_vdd_io_bias_reg;
curr_slot = pdata->vreg_data;
if (!curr_slot)
@@ -2693,10 +2718,11 @@ static int sdhci_msm_vreg_init(struct device *dev,
curr_vdd_reg = curr_slot->vdd_data;
curr_vdd_io_reg = curr_slot->vdd_io_data;
+ curr_vdd_io_bias_reg = curr_slot->vdd_io_bias_data;
if (!is_init)
/* Deregister all regulators from regulator framework */
- goto vdd_io_reg_deinit;
+ goto vdd_io_bias_reg_deinit;
/*
* Get the regulator handle from voltage regulator framework
@@ -2712,11 +2738,19 @@ static int sdhci_msm_vreg_init(struct device *dev,
if (ret)
goto vdd_reg_deinit;
}
+ if (curr_vdd_io_bias_reg) {
+ ret = sdhci_msm_vreg_init_reg(dev, curr_vdd_io_bias_reg);
+ if (ret)
+ goto vdd_io_reg_deinit;
+ }
if (ret)
dev_err(dev, "vreg reset failed (%d)\n", ret);
goto out;
+vdd_io_bias_reg_deinit:
+ if (curr_vdd_io_bias_reg)
+ sdhci_msm_vreg_deinit_reg(curr_vdd_io_bias_reg);
vdd_io_reg_deinit:
if (curr_vdd_io_reg)
sdhci_msm_vreg_deinit_reg(curr_vdd_io_reg);
@@ -2890,6 +2924,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
int ret = 0;
int pwr_state = 0, io_level = 0;
unsigned long flags;
+ struct sdhci_msm_reg_data *vreg_io_bias =
+ msm_host->pdata->vreg_data->vdd_io_bias_data;
irq_status = sdhci_msm_readb_relaxed(host,
msm_host_offset->CORE_PWRCTL_STATUS);
@@ -2936,6 +2972,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
if (irq_status & CORE_PWRCTL_IO_LOW) {
/* Switch voltage Low */
ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, VDD_IO_LOW, 0);
+ if (!ret && vreg_io_bias)
+ ret = sdhci_msm_vreg_disable(vreg_io_bias);
if (ret)
irq_ack |= CORE_PWRCTL_IO_FAIL;
else
@@ -4798,6 +4836,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
if ((major == 1) && (minor >= 0x42)) {
msm_host->use_updated_dll_reset = true;
msm_host->enhanced_strobe = true;
+ msm_host->pdata->caps2 |= MMC_CAP2_HS400_ES;
}
/*
@@ -4888,6 +4927,11 @@ static int sdhci_msm_setup_ice_clk(struct sdhci_msm_host *msm_host,
ret = clk_prepare_enable(msm_host->ice_clk);
if (ret)
return ret;
+ ret = clk_set_flags(msm_host->ice_clk,
+ CLKFLAG_RETAIN_MEM);
+ if (ret)
+ dev_err(&pdev->dev, "ICE_CLK set RETAIN_MEM failed: %d\n",
+ ret);
msm_host->ice_clk_rate =
msm_host->pdata->ice_clk_max;
@@ -4950,6 +4994,46 @@ static int sdhci_msm_get_ice_device_vops(struct sdhci_host *host,
return ret;
}
+/*
+ * Changes the bus speed mode for eMMC only as per the
+ * kernel command line parameter passed in the boot image.
+ * If not set remains in HS400ES mode.
+ */
+static void sdhci_msm_select_bus_mode(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ enum select_bus_mode {SELECT_HS400ES, SELECT_HS400,
+ SELECT_HS200, SELECT_DDR52,
+ SELECT_HS};
+
+ if (bus_mode) {
+ if (bus_mode == SELECT_HS400) {
+ msm_host->enhanced_strobe = false;
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400_ES);
+ } else if (bus_mode == SELECT_HS200) {
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400_ES);
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400);
+ } else if (bus_mode == SELECT_DDR52) {
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400_ES);
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400);
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS200);
+ } else if (bus_mode == SELECT_HS) {
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400_ES);
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS400);
+ msm_host->pdata->caps2 &= ~(MMC_CAP2_HS200);
+ msm_host->pdata->caps &= ~(MMC_CAP_3_3V_DDR |
+ MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR);
+ msm_host->mmc->clk_scaling.lower_bus_speed_mode &=
+ ~(MMC_SCALING_LOWER_DDR52_MODE);
+ }
+ pr_info("%s: %s: bus_mode=%d set using kernel command line\n",
+ mmc_hostname(host->mmc), __func__, bus_mode);
+ }
+}
+
static int sdhci_msm_probe(struct platform_device *pdev)
{
const struct sdhci_msm_offset *msm_host_offset;
@@ -5015,6 +5099,16 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* skip the probe if eMMC isn't a boot device */
if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev)
&& !force_probe) {
+ /* Turn off MEMCORE_ON for core_clk */
+ msm_host->clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (!IS_ERR(msm_host->clk)) {
+ ret = clk_set_flags(msm_host->clk,
+ CLKFLAG_NORETAIN_MEM);
+ if (ret)
+ dev_err(&pdev->dev,
+ "Core clk set NORETAIN_MEM failed: %d\n",
+ ret);
+ }
ret = -ENODEV;
goto pltfm_free;
}
@@ -5102,6 +5196,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Core clk not enabled (%d)\n", ret);
goto bus_aggr_clk_disable;
}
+ ret = clk_set_flags(msm_host->clk, CLKFLAG_NORETAIN_MEM);
+ if (ret)
+ dev_err(&pdev->dev, "Core clk set NORETAIN_MEM failed: %d\n",
+ ret);
+
msm_host->clk_rate = sdhci_msm_get_min_clock(host);
atomic_set(&msm_host->clks_on, 1);
@@ -5199,6 +5298,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
}
sdhci_set_default_hw_caps(msm_host, host);
+ sdhci_msm_select_bus_mode(host);
/*
* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH bit can
* be used as required later on.
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 87f651c..50cb575 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*
*/
@@ -42,6 +42,8 @@ struct sdhci_msm_slot_reg_data {
struct sdhci_msm_reg_data *vdd_data;
/* keeps VDD IO regulator info */
struct sdhci_msm_reg_data *vdd_io_data;
+ /* Keeps VDD IO parent regulator info*/
+ struct sdhci_msm_reg_data *vdd_io_bias_data;
};
struct sdhci_msm_gpio {
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index e284102..1ebcf0e 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -366,7 +366,7 @@ static int sdhci_at91_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(&pdev->dev);
/* HS200 is broken at this moment */
- host->quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
ret = sdhci_add_host(host);
if (ret)
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 6627523..2c9110f 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -648,9 +648,6 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
- if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc"))
- mdelay(5);
-
if (mask & SDHCI_RESET_ALL) {
val = sdhci_readl(host, ESDHC_TBCTL);
val &= ~ESDHC_TB_EN;
@@ -926,8 +923,8 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) {
- host->quirks2 |= SDHCI_QUIRK_RESET_AFTER_REQUEST;
- host->quirks2 |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+ host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST;
+ host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
}
if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 7179439..65985dc 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -30,6 +30,7 @@
#include <linux/mmc/slot-gpio.h>
#include <linux/mmc/sdhci-pci-data.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include "cqhci.h"
@@ -732,11 +733,18 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
return 0;
}
+static bool glk_broken_cqhci(struct sdhci_pci_slot *slot)
+{
+ return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC &&
+ dmi_match(DMI_BIOS_VENDOR, "LENOVO");
+}
+
static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
int ret = byt_emmc_probe_slot(slot);
- slot->host->mmc->caps2 |= MMC_CAP2_CQE;
+ if (!glk_broken_cqhci(slot))
+ slot->host->mmc->caps2 |= MMC_CAP2_CQE;
if (slot->chip->pdev->device != PCI_DEVICE_ID_INTEL_GLK_EMMC) {
slot->host->mmc->caps2 |= MMC_CAP2_HS400_ES,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 7fd1626..8acb309 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2089,9 +2089,7 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
- else if (timing == MMC_TIMING_SD_HS ||
- timing == MMC_TIMING_MMC_HS ||
- timing == MMC_TIMING_UHS_SDR25)
+ else if (timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
@@ -2718,8 +2716,8 @@ static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
sdhci_send_tuning(host, opcode);
if (!host->tuning_done) {
- pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
- mmc_hostname(host->mmc));
+ pr_debug("%s: Tuning timeout, falling back to fixed sampling clock\n",
+ mmc_hostname(host->mmc));
sdhci_abort_tuning(host, opcode);
return;
}
@@ -3442,9 +3440,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
intmask, host->data->error,
ktime_to_ms(ktime_sub(ktime_get(),
host->data_start_time)));
-
- if (host->mmc->ios.timing != MMC_TIMING_UHS_SDR104)
- sdhci_dumpregs(host);
+ sdhci_dumpregs(host);
}
sdhci_finish_data(host);
} else {
@@ -4239,6 +4235,9 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc_hostname(mmc), host->version);
}
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CQE)
+ mmc->caps2 &= ~MMC_CAP2_CQE;
+
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
else if (!(host->caps & SDHCI_CAN_DO_SDMA))
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 278ed0f..81a49ca 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -404,6 +404,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
/* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
+/* Controller has unusable command queue engine */
+#define SDHCI_QUIRK_BROKEN_CQE (1<<17)
/* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
/* Controller has to be forced to use block size of 2048 bytes */
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 7d13ca9..33c9ca8 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -926,8 +926,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (mrq->cmd->error || (mrq->data && mrq->data->error))
tmio_mmc_abort_dma(host);
- if (host->check_scc_error)
- host->check_scc_error(host);
+ /* SCC error means retune, but executed command was still successful */
+ if (host->check_scc_error && host->check_scc_error(host))
+ mmc_retune_needed(host->mmc);
/* If SET_BLOCK_COUNT, continue with main command */
if (host->mrq && !mrq->cmd->error) {
@@ -1266,7 +1267,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
return ret;
}
- mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
+ mmc->caps |= MMC_CAP_ERASE | MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 |= pdata->capabilities2;
mmc->max_segs = pdata->max_segs ? : 32;
mmc->max_blk_size = 512;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 270d3c9..c4a1d04 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -90,7 +90,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, buf, 1));
- size_t remaining = len;
int ret;
/* get transfer protocols. */
@@ -101,22 +100,16 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op.addr.nbytes = 0;
- while (remaining) {
- op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
- ret = spi_mem_adjust_op_size(flash->spimem, &op);
- if (ret)
- return ret;
+ ret = spi_mem_adjust_op_size(flash->spimem, &op);
+ if (ret)
+ return ret;
+ op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
- ret = spi_mem_exec_op(flash->spimem, &op);
- if (ret)
- return ret;
+ ret = spi_mem_exec_op(flash->spimem, &op);
+ if (ret)
+ return ret;
- op.addr.val += op.data.nbytes;
- remaining -= op.data.nbytes;
- op.data.buf.out += op.data.nbytes;
- }
-
- return len;
+ return op.data.nbytes;
}
/*
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 986f81d..47ad076 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -592,6 +592,26 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
return 0;
}
+/*
+ * The purpose of this function is to ensure a memcpy_toio() with byte writes
+ * only. Its structure is inspired from the ARM implementation of _memcpy_toio()
+ * which also does single byte writes but cannot be used here as this is just an
+ * implementation detail and not part of the API. Not mentioning the comment
+ * stating that _memcpy_toio() should be optimized.
+ */
+static void spear_smi_memcpy_toio_b(volatile void __iomem *dest,
+ const void *src, size_t len)
+{
+ const unsigned char *from = src;
+
+ while (len) {
+ len--;
+ writeb(*from, dest);
+ from++;
+ dest++;
+ }
+}
+
static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
void __iomem *dest, const void *src, size_t len)
{
@@ -614,7 +634,23 @@ static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
ctrlreg1 = readl(dev->io_base + SMI_CR1);
writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1);
- memcpy_toio(dest, src, len);
+ /*
+ * In Write Burst mode (WB_MODE), the specs states that writes must be:
+ * - incremental
+ * - of the same size
+ * The ARM implementation of memcpy_toio() will optimize the number of
+ * I/O by using as much 4-byte writes as possible, surrounded by
+ * 2-byte/1-byte access if:
+ * - the destination is not 4-byte aligned
+ * - the length is not a multiple of 4-byte.
+ * Avoid this alternance of write access size by using our own 'byte
+ * access' helper if at least one of the two conditions above is true.
+ */
+ if (IS_ALIGNED(len, sizeof(u32)) &&
+ IS_ALIGNED((uintptr_t)dest, sizeof(u32)))
+ memcpy_toio(dest, src, len);
+ else
+ spear_smi_memcpy_toio_b(dest, src, len);
writel(ctrlreg1, dev->io_base + SMI_CR1);
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index 4129535..ece605d 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -31,7 +31,6 @@
struct of_flash_list {
struct mtd_info *mtd;
struct map_info map;
- struct resource *res;
};
struct of_flash {
@@ -56,18 +55,10 @@ static int of_flash_remove(struct platform_device *dev)
mtd_concat_destroy(info->cmtd);
}
- for (i = 0; i < info->list_size; i++) {
+ for (i = 0; i < info->list_size; i++)
if (info->list[i].mtd)
map_destroy(info->list[i].mtd);
- if (info->list[i].map.virt)
- iounmap(info->list[i].map.virt);
-
- if (info->list[i].res) {
- release_resource(info->list[i].res);
- kfree(info->list[i].res);
- }
- }
return 0;
}
@@ -215,10 +206,11 @@ static int of_flash_probe(struct platform_device *dev)
err = -EBUSY;
res_size = resource_size(&res);
- info->list[i].res = request_mem_region(res.start, res_size,
- dev_name(&dev->dev));
- if (!info->list[i].res)
+ info->list[i].map.virt = devm_ioremap_resource(&dev->dev, &res);
+ if (IS_ERR(info->list[i].map.virt)) {
+ err = PTR_ERR(info->list[i].map.virt);
goto err_out;
+ }
err = -ENXIO;
width = of_get_property(dp, "bank-width", NULL);
@@ -246,15 +238,6 @@ static int of_flash_probe(struct platform_device *dev)
if (err)
goto err_out;
- err = -ENOMEM;
- info->list[i].map.virt = ioremap(info->list[i].map.phys,
- info->list[i].map.size);
- if (!info->list[i].map.virt) {
- dev_err(&dev->dev, "Failed to ioremap() flash"
- " region\n");
- goto err_out;
- }
-
simple_map_init(&info->list[i].map);
/*
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index 9887bda..b31c868 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -7,7 +7,7 @@
extern struct mutex mtd_table_mutex;
struct mtd_info *__mtd_next_device(int i);
-int add_mtd_device(struct mtd_info *mtd);
+int __must_check add_mtd_device(struct mtd_info *mtd);
int del_mtd_device(struct mtd_info *mtd);
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 0bbb23b..10c5336 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -612,10 +612,21 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
list_add(&new->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
- add_mtd_device(&new->mtd);
+ ret = add_mtd_device(&new->mtd);
+ if (ret)
+ goto err_remove_part;
mtd_add_partition_attrs(new);
+ return 0;
+
+err_remove_part:
+ mutex_lock(&mtd_partitions_mutex);
+ list_del(&new->list);
+ mutex_unlock(&mtd_partitions_mutex);
+
+ free_partition(new);
+
return ret;
}
EXPORT_SYMBOL_GPL(mtd_add_partition);
@@ -706,22 +717,31 @@ int add_mtd_partitions(struct mtd_info *master,
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
- int i;
+ int i, ret;
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = allocate_partition(master, parts + i, i, cur_offset);
if (IS_ERR(slave)) {
- del_mtd_partitions(master);
- return PTR_ERR(slave);
+ ret = PTR_ERR(slave);
+ goto err_del_partitions;
}
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
- add_mtd_device(&slave->mtd);
+ ret = add_mtd_device(&slave->mtd);
+ if (ret) {
+ mutex_lock(&mtd_partitions_mutex);
+ list_del(&slave->list);
+ mutex_unlock(&mtd_partitions_mutex);
+
+ free_partition(slave);
+ goto err_del_partitions;
+ }
+
mtd_add_partition_attrs(slave);
/* Look for subpartitions */
parse_mtd_partitions(&slave->mtd, parts[i].types, NULL);
@@ -730,6 +750,11 @@ int add_mtd_partitions(struct mtd_info *master,
}
return 0;
+
+err_del_partitions:
+ del_mtd_partitions(master);
+
+ return ret;
}
static DEFINE_SPINLOCK(part_parser_lock);
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index 32e95af..ea02271 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -1826,7 +1826,7 @@ static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
ret = of_property_read_u32(np, "#size-cells", &val);
if (ret) {
- dev_err(dev, "missing #address-cells property\n");
+ dev_err(dev, "missing #size-cells property\n");
return ret;
}
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
index 555a74e..9d39978 100644
--- a/drivers/mtd/nand/raw/atmel/pmecc.c
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -876,23 +876,32 @@ static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
{
struct platform_device *pdev;
struct atmel_pmecc *pmecc, **ptr;
+ int ret;
pdev = of_find_device_by_node(np);
- if (!pdev || !platform_get_drvdata(pdev))
+ if (!pdev)
return ERR_PTR(-EPROBE_DEFER);
+ pmecc = platform_get_drvdata(pdev);
+ if (!pmecc) {
+ ret = -EPROBE_DEFER;
+ goto err_put_device;
+ }
ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- get_device(&pdev->dev);
- pmecc = platform_get_drvdata(pdev);
+ if (!ptr) {
+ ret = -ENOMEM;
+ goto err_put_device;
+ }
*ptr = pmecc;
devres_add(userdev, ptr);
return pmecc;
+
+err_put_device:
+ put_device(&pdev->dev);
+ return ERR_PTR(ret);
}
static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 24f59d0..7e7729d 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -30,6 +30,7 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/fsl_ifc.h>
+#include <linux/iopoll.h>
#define ERR_BYTE 0xFF /* Value returned for read
bytes when read failed */
@@ -761,7 +762,7 @@ static const struct nand_controller_ops fsl_ifc_controller_ops = {
.attach_chip = fsl_ifc_attach_chip,
};
-static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
+static int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
{
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
@@ -769,6 +770,27 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
uint32_t csor = 0, csor_8k = 0, csor_ext = 0;
uint32_t cs = priv->bank;
+ if (ctrl->version < FSL_IFC_VERSION_1_1_0)
+ return 0;
+
+ if (ctrl->version > FSL_IFC_VERSION_1_1_0) {
+ u32 ncfgr, status;
+ int ret;
+
+ /* Trigger auto initialization */
+ ncfgr = ifc_in32(&ifc_runtime->ifc_nand.ncfgr);
+ ifc_out32(ncfgr | IFC_NAND_NCFGR_SRAM_INIT_EN, &ifc_runtime->ifc_nand.ncfgr);
+
+ /* Wait until done */
+ ret = readx_poll_timeout(ifc_in32, &ifc_runtime->ifc_nand.ncfgr,
+ status, !(status & IFC_NAND_NCFGR_SRAM_INIT_EN),
+ 10, IFC_TIMEOUT_MSECS * 1000);
+ if (ret)
+ dev_err(priv->dev, "Failed to initialize SRAM!\n");
+
+ return ret;
+ }
+
/* Save CSOR and CSOR_ext */
csor = ifc_in32(&ifc_global->csor_cs[cs].csor);
csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext);
@@ -805,12 +827,16 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
- if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
+ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) {
pr_err("fsl-ifc: Failed to Initialise SRAM\n");
+ return -ETIMEDOUT;
+ }
/* Restore CSOR and CSOR_ext */
ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext);
+
+ return 0;
}
static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
@@ -821,6 +847,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
struct nand_chip *chip = &priv->chip;
struct mtd_info *mtd = nand_to_mtd(&priv->chip);
u32 csor;
+ int ret;
/* Fill in fsl_ifc_mtd structure */
mtd->dev.parent = priv->dev;
@@ -914,8 +941,9 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.algo = NAND_ECC_HAMMING;
}
- if (ctrl->version >= FSL_IFC_VERSION_1_1_0)
- fsl_ifc_sram_init(priv);
+ ret = fsl_ifc_sram_init(priv);
+ if (ret)
+ return ret;
/*
* As IFC version 2.0.0 has 16KB of internal SRAM as compared to older
diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index 9c90695..7a84a8f 100644
--- a/drivers/mtd/nand/raw/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -2710,24 +2710,23 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
struct regmap *sysctrl_base =
syscon_regmap_lookup_by_phandle(np,
"marvell,system-controller");
- u32 reg;
if (IS_ERR(sysctrl_base))
return PTR_ERR(sysctrl_base);
- reg = GENCONF_SOC_DEVICE_MUX_NFC_EN |
- GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
- GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
- GENCONF_SOC_DEVICE_MUX_NFC_INT_EN;
- regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg);
+ regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX,
+ GENCONF_SOC_DEVICE_MUX_NFC_EN |
+ GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
+ GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
+ GENCONF_SOC_DEVICE_MUX_NFC_INT_EN);
- regmap_read(sysctrl_base, GENCONF_CLK_GATING_CTRL, ®);
- reg |= GENCONF_CLK_GATING_CTRL_ND_GATE;
- regmap_write(sysctrl_base, GENCONF_CLK_GATING_CTRL, reg);
+ regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL,
+ GENCONF_CLK_GATING_CTRL_ND_GATE,
+ GENCONF_CLK_GATING_CTRL_ND_GATE);
- regmap_read(sysctrl_base, GENCONF_ND_CLK_CTRL, ®);
- reg |= GENCONF_ND_CLK_CTRL_EN;
- regmap_write(sysctrl_base, GENCONF_ND_CLK_CTRL, reg);
+ regmap_update_bits(sysctrl_base, GENCONF_ND_CLK_CTRL,
+ GENCONF_ND_CLK_CTRL_EN,
+ GENCONF_ND_CLK_CTRL_EN);
}
/* Configure the DMA if appropriate */
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index 880e75f..07d8750 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -23,7 +23,6 @@
#include <linux/of_device.h>
#include <linux/delay.h>
#include <linux/dma/qcom_bam_dma.h>
-#include <linux/dma-direct.h> /* XXX: drivers shall never use this directly! */
/* NANDc reg offsets */
#define NAND_FLASH_CMD 0x00
diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
index bb8866e..1e72732 100644
--- a/drivers/mtd/nand/raw/sh_flctl.c
+++ b/drivers/mtd/nand/raw/sh_flctl.c
@@ -480,7 +480,7 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
/* initiate DMA transfer */
if (flctl->chan_fifo0_rx && rlen >= 32 &&
- flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0)
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE) > 0)
goto convert; /* DMA success */
/* do polling transfer */
@@ -539,7 +539,7 @@ static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
/* initiate DMA transfer */
if (flctl->chan_fifo0_tx && rlen >= 32 &&
- flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0)
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE) > 0)
return; /* DMA success */
/* do polling transfer */
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 1f0b7ee..5b5f4d2 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -1397,7 +1397,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
sunxi_nfc_randomizer_enable(mtd);
writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
- nfc->regs + NFC_REG_RCMD_SET);
+ nfc->regs + NFC_REG_WCMD_SET);
dma_async_issue_pending(nfc->dmac);
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 0806c7a..04cedd3 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -972,7 +972,7 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf,
return 0;
}
- dma_dst = dma_map_single(nor->dev, buf, len, DMA_DEV_TO_MEM);
+ dma_dst = dma_map_single(nor->dev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(nor->dev, dma_dst)) {
dev_err(nor->dev, "dma mapping failed\n");
return -ENOMEM;
@@ -1007,7 +1007,7 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf,
}
err_unmap:
- dma_unmap_single(nor->dev, dma_dst, len, DMA_DEV_TO_MEM);
+ dma_unmap_single(nor->dev, dma_dst, len, DMA_FROM_DEVICE);
return ret;
}
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index f028277..2e18342 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2459,7 +2459,7 @@ static int spi_nor_init_params(struct spi_nor *nor,
memset(params, 0, sizeof(*params));
/* Set SPI NOR sizes. */
- params->size = info->sector_size * info->n_sectors;
+ params->size = (u64)info->sector_size * info->n_sectors;
params->page_size = info->page_size;
/* (Fast) Read settings. */
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index d2a7266..c120c87 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1101,10 +1101,10 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
ubi_wl_close(ubi);
ubi_free_internal_volumes(ubi);
vfree(ubi->vtbl);
- put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf);
vfree(ubi->fm_buf);
ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index);
+ put_mtd_device(ubi->mtd);
put_device(&ubi->dev);
return 0;
}
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index e9e9ecb..0b8f0c4 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -227,9 +227,9 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
out_free:
kfree(desc);
out_put_ubi:
- ubi_put_device(ubi);
ubi_err(ubi, "cannot open device %d, volume %d, error %d",
ubi_num, vol_id, err);
+ ubi_put_device(ubi);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(ubi_open_volume);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 619bf14..0652caa 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -197,9 +197,9 @@
config GENEVE
tristate "Generic Network Virtualization Encapsulation"
- depends on INET && NET_UDP_TUNNEL
+ depends on INET
depends on IPV6 || !IPV6
- select NET_IP_TUNNEL
+ select NET_UDP_TUNNEL
select GRO_CELLS
---help---
This allows one to create geneve virtual interfaces that provide
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 0d2392c..f57b86f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1798,7 +1798,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
slave_disable_netpoll(new_slave);
err_close:
- slave_dev->priv_flags &= ~IFF_BONDING;
+ if (!netif_is_bond_master(slave_dev))
+ slave_dev->priv_flags &= ~IFF_BONDING;
dev_close(slave_dev);
err_restore_mac:
@@ -2004,7 +2005,8 @@ static int __bond_release_one(struct net_device *bond_dev,
else
dev_set_mtu(slave_dev, slave->original_mtu);
- slave_dev->priv_flags &= ~IFF_BONDING;
+ if (!netif_is_bond_master(slave_dev))
+ slave_dev->priv_flags &= ~IFF_BONDING;
bond_free_slave(slave);
@@ -2074,8 +2076,7 @@ static int bond_miimon_inspect(struct bonding *bond)
ignore_updelay = !rcu_dereference(bond->curr_active_slave);
bond_for_each_slave_rcu(bond, slave, iter) {
- slave->new_link = BOND_LINK_NOCHANGE;
- slave->link_new_state = slave->link;
+ bond_propose_link_state(slave, BOND_LINK_NOCHANGE);
link_state = bond_check_dev_link(bond, slave->dev, 0);
@@ -2111,7 +2112,7 @@ static int bond_miimon_inspect(struct bonding *bond)
}
if (slave->delay <= 0) {
- slave->new_link = BOND_LINK_DOWN;
+ bond_propose_link_state(slave, BOND_LINK_DOWN);
commit++;
continue;
}
@@ -2150,7 +2151,7 @@ static int bond_miimon_inspect(struct bonding *bond)
slave->delay = 0;
if (slave->delay <= 0) {
- slave->new_link = BOND_LINK_UP;
+ bond_propose_link_state(slave, BOND_LINK_UP);
commit++;
ignore_updelay = false;
continue;
@@ -2188,7 +2189,7 @@ static void bond_miimon_commit(struct bonding *bond)
struct slave *slave, *primary;
bond_for_each_slave(bond, slave, iter) {
- switch (slave->new_link) {
+ switch (slave->link_new_state) {
case BOND_LINK_NOCHANGE:
/* For 802.3ad mode, check current slave speed and
* duplex again in case its port was disabled after
@@ -2222,9 +2223,6 @@ static void bond_miimon_commit(struct bonding *bond)
} else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
/* make it immediately active */
bond_set_active_slave(slave);
- } else if (slave != primary) {
- /* prevent it from being the active one */
- bond_set_backup_slave(slave);
}
netdev_info(bond->dev, "link status definitely up for interface %s, %u Mbps %s duplex\n",
@@ -2263,8 +2261,8 @@ static void bond_miimon_commit(struct bonding *bond)
default:
netdev_err(bond->dev, "invalid new link %d on slave %s\n",
- slave->new_link, slave->dev->name);
- slave->new_link = BOND_LINK_NOCHANGE;
+ slave->link_new_state, slave->dev->name);
+ bond_propose_link_state(slave, BOND_LINK_NOCHANGE);
continue;
}
@@ -2664,13 +2662,13 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
bond_for_each_slave_rcu(bond, slave, iter) {
unsigned long trans_start = dev_trans_start(slave->dev);
- slave->new_link = BOND_LINK_NOCHANGE;
+ bond_propose_link_state(slave, BOND_LINK_NOCHANGE);
if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, trans_start, 1) &&
bond_time_in_interval(bond, slave->last_rx, 1)) {
- slave->new_link = BOND_LINK_UP;
+ bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
/* primary_slave has no meaning in round-robin
@@ -2697,7 +2695,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
if (!bond_time_in_interval(bond, trans_start, 2) ||
!bond_time_in_interval(bond, slave->last_rx, 2)) {
- slave->new_link = BOND_LINK_DOWN;
+ bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
if (slave->link_failure_count < UINT_MAX)
@@ -2729,8 +2727,8 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
goto re_arm;
bond_for_each_slave(bond, slave, iter) {
- if (slave->new_link != BOND_LINK_NOCHANGE)
- slave->link = slave->new_link;
+ if (slave->link_new_state != BOND_LINK_NOCHANGE)
+ slave->link = slave->link_new_state;
}
if (slave_state_changed) {
@@ -2753,9 +2751,9 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
}
/* Called to inspect slaves for active-backup mode ARP monitor link state
- * changes. Sets new_link in slaves to specify what action should take
- * place for the slave. Returns 0 if no changes are found, >0 if changes
- * to link states must be committed.
+ * changes. Sets proposed link state in slaves to specify what action
+ * should take place for the slave. Returns 0 if no changes are found, >0
+ * if changes to link states must be committed.
*
* Called with rcu_read_lock held.
*/
@@ -2767,12 +2765,12 @@ static int bond_ab_arp_inspect(struct bonding *bond)
int commit = 0;
bond_for_each_slave_rcu(bond, slave, iter) {
- slave->new_link = BOND_LINK_NOCHANGE;
+ bond_propose_link_state(slave, BOND_LINK_NOCHANGE);
last_rx = slave_last_rx(bond, slave);
if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_rx, 1)) {
- slave->new_link = BOND_LINK_UP;
+ bond_propose_link_state(slave, BOND_LINK_UP);
commit++;
}
continue;
@@ -2800,7 +2798,7 @@ static int bond_ab_arp_inspect(struct bonding *bond)
if (!bond_is_active_slave(slave) &&
!rcu_access_pointer(bond->current_arp_slave) &&
!bond_time_in_interval(bond, last_rx, 3)) {
- slave->new_link = BOND_LINK_DOWN;
+ bond_propose_link_state(slave, BOND_LINK_DOWN);
commit++;
}
@@ -2813,7 +2811,7 @@ static int bond_ab_arp_inspect(struct bonding *bond)
if (bond_is_active_slave(slave) &&
(!bond_time_in_interval(bond, trans_start, 2) ||
!bond_time_in_interval(bond, last_rx, 2))) {
- slave->new_link = BOND_LINK_DOWN;
+ bond_propose_link_state(slave, BOND_LINK_DOWN);
commit++;
}
}
@@ -2833,7 +2831,7 @@ static void bond_ab_arp_commit(struct bonding *bond)
struct slave *slave;
bond_for_each_slave(bond, slave, iter) {
- switch (slave->new_link) {
+ switch (slave->link_new_state) {
case BOND_LINK_NOCHANGE:
continue;
@@ -2886,7 +2884,7 @@ static void bond_ab_arp_commit(struct bonding *bond)
default:
netdev_err(bond->dev, "impossible: new_link %d on slave %s\n",
- slave->new_link, slave->dev->name);
+ slave->link_new_state, slave->dev->name);
continue;
}
@@ -4033,7 +4031,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
* this to-be-skipped slave to send a packet out.
*/
old_arr = rtnl_dereference(bond->slave_arr);
- for (idx = 0; idx < old_arr->count; idx++) {
+ for (idx = 0; old_arr != NULL && idx < old_arr->count; idx++) {
if (skipslave == old_arr->arr[idx]) {
old_arr->arr[idx] =
old_arr->arr[old_arr->count-1];
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 606b7d8..24c6015 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -52,6 +52,7 @@
#define CONTROL_EX_PDR BIT(8)
/* control register */
+#define CONTROL_SWR BIT(15)
#define CONTROL_TEST BIT(7)
#define CONTROL_CCE BIT(6)
#define CONTROL_DISABLE_AR BIT(5)
@@ -97,6 +98,9 @@
#define BTR_TSEG2_SHIFT 12
#define BTR_TSEG2_MASK (0x7 << BTR_TSEG2_SHIFT)
+/* interrupt register */
+#define INT_STS_PENDING 0x8000
+
/* brp extension register */
#define BRP_EXT_BRPE_MASK 0x0f
#define BRP_EXT_BRPE_SHIFT 0
@@ -569,6 +573,26 @@ static void c_can_configure_msg_objects(struct net_device *dev)
IF_MCONT_RCV_EOB);
}
+static int c_can_software_reset(struct net_device *dev)
+{
+ struct c_can_priv *priv = netdev_priv(dev);
+ int retry = 0;
+
+ if (priv->type != BOSCH_D_CAN)
+ return 0;
+
+ priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_SWR | CONTROL_INIT);
+ while (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_SWR) {
+ msleep(20);
+ if (retry++ > 100) {
+ netdev_err(dev, "CCTRL: software reset failed\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
/*
* Configure C_CAN chip:
* - enable/disable auto-retransmission
@@ -578,6 +602,11 @@ static void c_can_configure_msg_objects(struct net_device *dev)
static int c_can_chip_config(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = c_can_software_reset(dev);
+ if (err)
+ return err;
/* enable automatic retransmission */
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR);
@@ -1029,10 +1058,16 @@ static int c_can_poll(struct napi_struct *napi, int quota)
u16 curr, last = priv->last_status;
int work_done = 0;
- priv->last_status = curr = priv->read_reg(priv, C_CAN_STS_REG);
- /* Ack status on C_CAN. D_CAN is self clearing */
- if (priv->type != BOSCH_D_CAN)
- priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
+ /* Only read the status register if a status interrupt was pending */
+ if (atomic_xchg(&priv->sie_pending, 0)) {
+ priv->last_status = curr = priv->read_reg(priv, C_CAN_STS_REG);
+ /* Ack status on C_CAN. D_CAN is self clearing */
+ if (priv->type != BOSCH_D_CAN)
+ priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
+ } else {
+ /* no change detected ... */
+ curr = last;
+ }
/* handle state changes */
if ((curr & STATUS_EWARN) && (!(last & STATUS_EWARN))) {
@@ -1083,10 +1118,16 @@ static irqreturn_t c_can_isr(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
struct c_can_priv *priv = netdev_priv(dev);
+ int reg_int;
- if (!priv->read_reg(priv, C_CAN_INT_REG))
+ reg_int = priv->read_reg(priv, C_CAN_INT_REG);
+ if (!reg_int)
return IRQ_NONE;
+ /* save for later use */
+ if (reg_int & INT_STS_PENDING)
+ atomic_set(&priv->sie_pending, 1);
+
/* disable all interrupts and schedule the NAPI */
c_can_irq_control(priv, false);
napi_schedule(&priv->napi);
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
index 8acdc7f..d5567a7 100644
--- a/drivers/net/can/c_can/c_can.h
+++ b/drivers/net/can/c_can/c_can.h
@@ -198,6 +198,7 @@ struct c_can_priv {
struct net_device *dev;
struct device *device;
atomic_t tx_active;
+ atomic_t sie_pending;
unsigned long tx_dir;
int last_status;
u16 (*read_reg) (const struct c_can_priv *priv, enum reg index);
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index bd127ce..49b853f 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -853,6 +853,7 @@ void of_can_transceiver(struct net_device *dev)
return;
ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max);
+ of_node_put(dn);
if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max))
netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n");
}
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 6f265d2..bfe13c6 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -566,6 +566,7 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
struct can_frame *cf;
bool rx_errors = false, tx_errors = false;
u32 timestamp;
+ int err;
timestamp = priv->read(®s->timer) << 16;
@@ -614,7 +615,9 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
if (tx_errors)
dev->stats.tx_errors++;
- can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
+ err = can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
+ if (err)
+ dev->stats.rx_fifo_errors++;
}
static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
@@ -627,6 +630,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
int flt;
struct can_berr_counter bec;
u32 timestamp;
+ int err;
timestamp = priv->read(®s->timer) << 16;
@@ -658,7 +662,9 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
if (unlikely(new_state == CAN_STATE_BUS_OFF))
can_bus_off(dev);
- can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
+ err = can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
+ if (err)
+ dev->stats.rx_fifo_errors++;
}
static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
@@ -1048,6 +1054,7 @@ static int flexcan_chip_start(struct net_device *dev)
reg_mecr = priv->read(®s->mecr);
reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
priv->write(reg_mecr, ®s->mecr);
+ reg_mecr |= FLEXCAN_MECR_ECCDIS;
reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
FLEXCAN_MECR_FANCEI_MSK);
priv->write(reg_mecr, ®s->mecr);
diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c
index 727691d..5f7e97d 100644
--- a/drivers/net/can/rx-offload.c
+++ b/drivers/net/can/rx-offload.c
@@ -116,37 +116,95 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
return cb_b->timestamp - cb_a->timestamp;
}
-static struct sk_buff *can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
+/**
+ * can_rx_offload_offload_one() - Read one CAN frame from HW
+ * @offload: pointer to rx_offload context
+ * @n: number of mailbox to read
+ *
+ * The task of this function is to read a CAN frame from mailbox @n
+ * from the device and return the mailbox's content as a struct
+ * sk_buff.
+ *
+ * If the struct can_rx_offload::skb_queue exceeds the maximal queue
+ * length (struct can_rx_offload::skb_queue_len_max) or no skb can be
+ * allocated, the mailbox contents is discarded by reading it into an
+ * overflow buffer. This way the mailbox is marked as free by the
+ * driver.
+ *
+ * Return: A pointer to skb containing the CAN frame on success.
+ *
+ * NULL if the mailbox @n is empty.
+ *
+ * ERR_PTR() in case of an error
+ */
+static struct sk_buff *
+can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb = NULL, *skb_error = NULL;
struct can_rx_offload_cb *cb;
struct can_frame *cf;
int ret;
- /* If queue is full or skb not available, read to discard mailbox */
- if (likely(skb_queue_len(&offload->skb_queue) <=
- offload->skb_queue_len_max))
+ if (likely(skb_queue_len(&offload->skb_queue) <
+ offload->skb_queue_len_max)) {
skb = alloc_can_skb(offload->dev, &cf);
+ if (unlikely(!skb))
+ skb_error = ERR_PTR(-ENOMEM); /* skb alloc failed */
+ } else {
+ skb_error = ERR_PTR(-ENOBUFS); /* skb_queue is full */
+ }
- if (!skb) {
+ /* If queue is full or skb not available, drop by reading into
+ * overflow buffer.
+ */
+ if (unlikely(skb_error)) {
struct can_frame cf_overflow;
u32 timestamp;
ret = offload->mailbox_read(offload, &cf_overflow,
×tamp, n);
- if (ret)
- offload->dev->stats.rx_dropped++;
- return NULL;
+ /* Mailbox was empty. */
+ if (unlikely(!ret))
+ return NULL;
+
+ /* Mailbox has been read and we're dropping it or
+ * there was a problem reading the mailbox.
+ *
+ * Increment error counters in any case.
+ */
+ offload->dev->stats.rx_dropped++;
+ offload->dev->stats.rx_fifo_errors++;
+
+ /* There was a problem reading the mailbox, propagate
+ * error value.
+ */
+ if (unlikely(ret < 0))
+ return ERR_PTR(ret);
+
+ return skb_error;
}
cb = can_rx_offload_get_cb(skb);
ret = offload->mailbox_read(offload, cf, &cb->timestamp, n);
- if (!ret) {
+
+ /* Mailbox was empty. */
+ if (unlikely(!ret)) {
kfree_skb(skb);
return NULL;
}
+ /* There was a problem reading the mailbox, propagate error value. */
+ if (unlikely(ret < 0)) {
+ kfree_skb(skb);
+
+ offload->dev->stats.rx_dropped++;
+ offload->dev->stats.rx_fifo_errors++;
+
+ return ERR_PTR(ret);
+ }
+
+ /* Mailbox was read. */
return skb;
}
@@ -166,8 +224,8 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pen
continue;
skb = can_rx_offload_offload_one(offload, i);
- if (!skb)
- break;
+ if (IS_ERR_OR_NULL(skb))
+ continue;
__skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare);
}
@@ -197,7 +255,13 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
struct sk_buff *skb;
int received = 0;
- while ((skb = can_rx_offload_offload_one(offload, 0))) {
+ while (1) {
+ skb = can_rx_offload_offload_one(offload, 0);
+ if (IS_ERR(skb))
+ continue;
+ if (!skb)
+ break;
+
skb_queue_tail(&offload->skb_queue, skb);
received++;
}
@@ -216,8 +280,10 @@ int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
unsigned long flags;
if (skb_queue_len(&offload->skb_queue) >
- offload->skb_queue_len_max)
- return -ENOMEM;
+ offload->skb_queue_len_max) {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
cb = can_rx_offload_get_cb(skb);
cb->timestamp = timestamp;
@@ -259,8 +325,10 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload,
struct sk_buff *skb)
{
if (skb_queue_len(&offload->skb_queue) >
- offload->skb_queue_len_max)
- return -ENOMEM;
+ offload->skb_queue_len_max) {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
skb_queue_tail(&offload->skb_queue, skb);
can_rx_offload_schedule(offload);
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index aa97dbc..cf0769a 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -613,6 +613,8 @@ static int slcan_open(struct tty_struct *tty)
sl->tty = NULL;
tty->disc_data = NULL;
clear_bit(SLF_INUSE, &sl->flags);
+ slc_free_netdev(sl->dev);
+ free_netdev(sl->dev);
err_exit:
rtnl_unlock();
diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c
index de8d9dc..0b0dd3f 100644
--- a/drivers/net/can/spi/mcp251x.c
+++ b/drivers/net/can/spi/mcp251x.c
@@ -773,6 +773,7 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
if (priv->after_suspend) {
mcp251x_hw_reset(spi);
mcp251x_setup(net, spi);
+ priv->force_quit = 0;
if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
mcp251x_set_normal_mode(spi);
} else if (priv->after_suspend & AFTER_SUSPEND_UP) {
@@ -784,7 +785,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
mcp251x_hw_sleep(spi);
}
priv->after_suspend = 0;
- priv->force_quit = 0;
}
if (priv->restart_tx) {
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index 17c21ad..3a39f51 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -631,6 +631,7 @@ static int gs_can_open(struct net_device *netdev)
rc);
usb_unanchor_urb(urb);
+ usb_free_urb(urb);
break;
}
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
index 07d2f3a..ae4c37e 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
@@ -608,7 +608,7 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv,
struct kvaser_cmd *cmd;
int err;
- cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
+ cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
if (!cmd)
return -ENOMEM;
@@ -1140,7 +1140,7 @@ static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv)
struct kvaser_cmd *cmd;
int rc;
- cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
@@ -1206,7 +1206,7 @@ static int kvaser_usb_leaf_flush_queue(struct kvaser_usb_net_priv *priv)
struct kvaser_cmd *cmd;
int rc;
- cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
index 8d8c2086..1b0afea 100644
--- a/drivers/net/can/usb/mcba_usb.c
+++ b/drivers/net/can/usb/mcba_usb.c
@@ -887,9 +887,8 @@ static void mcba_usb_disconnect(struct usb_interface *intf)
netdev_info(priv->netdev, "device disconnected\n");
unregister_candev(priv->netdev);
- free_candev(priv->netdev);
-
mcba_urb_unlink(priv);
+ free_candev(priv->netdev);
}
static struct usb_driver mcba_usb_driver = {
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 13238a72..215cd74 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -108,7 +108,7 @@ struct pcan_usb_msg_context {
u8 *end;
u8 rec_cnt;
u8 rec_idx;
- u8 rec_data_idx;
+ u8 rec_ts_idx;
struct net_device *netdev;
struct pcan_usb *pdev;
};
@@ -444,8 +444,8 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
}
if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) {
/* no error (back to active state) */
- mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE;
- return 0;
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ break;
}
break;
@@ -468,9 +468,9 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
}
if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) {
- /* no error (back to active state) */
- mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE;
- return 0;
+ /* no error (back to warning state) */
+ new_state = CAN_STATE_ERROR_WARNING;
+ break;
}
break;
@@ -509,6 +509,11 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
mc->pdev->dev.can.can_stats.error_warning++;
break;
+ case CAN_STATE_ERROR_ACTIVE:
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_ACTIVE;
+ break;
+
default:
/* CAN_STATE_MAX (trick to handle other errors) */
cf->can_id |= CAN_ERR_CRTL;
@@ -555,10 +560,15 @@ static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc,
mc->ptr += PCAN_USB_CMD_ARGS;
if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
- int err = pcan_usb_decode_ts(mc, !mc->rec_idx);
+ int err = pcan_usb_decode_ts(mc, !mc->rec_ts_idx);
if (err)
return err;
+
+ /* Next packet in the buffer will have a timestamp on a single
+ * byte
+ */
+ mc->rec_ts_idx++;
}
switch (f) {
@@ -640,10 +650,13 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len)
cf->can_dlc = get_can_dlc(rec_len);
- /* first data packet timestamp is a word */
- if (pcan_usb_decode_ts(mc, !mc->rec_data_idx))
+ /* Only first packet timestamp is a word */
+ if (pcan_usb_decode_ts(mc, !mc->rec_ts_idx))
goto decode_failed;
+ /* Next packet in the buffer will have a timestamp on a single byte */
+ mc->rec_ts_idx++;
+
/* read data */
memset(cf->data, 0x0, sizeof(cf->data));
if (status_len & PCAN_USB_STATUSLEN_RTR) {
@@ -696,7 +709,6 @@ static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf)
/* handle normal can frames here */
} else {
err = pcan_usb_decode_data(&mc, sl);
- mc.rec_data_idx++;
}
}
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 43b0fa2b..afc8d97 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -758,7 +758,7 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
dev = netdev_priv(netdev);
/* allocate a buffer large enough to send commands */
- dev->cmd_buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL);
+ dev->cmd_buf = kzalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL);
if (!dev->cmd_buf) {
err = -ENOMEM;
goto lbl_free_candev;
diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
index c9fd83e..43350f5 100644
--- a/drivers/net/can/usb/ucan.c
+++ b/drivers/net/can/usb/ucan.c
@@ -796,7 +796,7 @@ static void ucan_read_bulk_callback(struct urb *urb)
up);
usb_anchor_urb(urb, &up->rx_urbs);
- ret = usb_submit_urb(urb, GFP_KERNEL);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
netdev_err(up->netdev,
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index 27861c4..3e44164 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -1007,9 +1007,8 @@ static void usb_8dev_disconnect(struct usb_interface *intf)
netdev_info(priv->netdev, "device disconnected\n");
unregister_netdev(priv->netdev);
- free_candev(priv->netdev);
-
unlink_all_urbs(priv);
+ free_candev(priv->netdev);
}
}
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 3df23487..b01c6da 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -612,7 +612,7 @@ static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev)
*
* Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY when the tx queue is full
*/
-static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index d3ce1e4..dbfb6ad 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -66,6 +66,7 @@
config NET_DSA_SMSC_LAN9303
tristate
select NET_DSA_TAG_LAN9303
+ select REGMAP
---help---
This enables support for the SMSC/Microchip LAN9303 3 port ethernet
switch chips.
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index ad534b9..2d3a2cb 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1584,7 +1584,6 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
loc = B53_EG_MIR_CTL;
b53_read16(dev, B53_MGMT_PAGE, loc, ®);
- reg &= ~MIRROR_MASK;
reg |= BIT(port);
b53_write16(dev, B53_MGMT_PAGE, loc, reg);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 2fa2caf..02a4187 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -41,22 +41,11 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
unsigned int i;
u32 reg, offset;
- if (priv->type == BCM7445_DEVICE_ID)
- offset = CORE_STS_OVERRIDE_IMP;
- else
- offset = CORE_STS_OVERRIDE_IMP2;
-
/* Enable the port memories */
reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
reg &= ~P_TXQ_PSM_VDD(port);
core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
- /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
- reg = core_readl(priv, CORE_IMP_CTL);
- reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
- reg &= ~(RX_DIS | TX_DIS);
- core_writel(priv, reg, CORE_IMP_CTL);
-
/* Enable forwarding */
core_writel(priv, SW_FWDG_EN, CORE_SWMODE);
@@ -75,10 +64,27 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
b53_brcm_hdr_setup(ds, port);
- /* Force link status for IMP port */
- reg = core_readl(priv, offset);
- reg |= (MII_SW_OR | LINK_STS);
- core_writel(priv, reg, offset);
+ if (port == 8) {
+ if (priv->type == BCM7445_DEVICE_ID)
+ offset = CORE_STS_OVERRIDE_IMP;
+ else
+ offset = CORE_STS_OVERRIDE_IMP2;
+
+ /* Force link status for IMP port */
+ reg = core_readl(priv, offset);
+ reg |= (MII_SW_OR | LINK_STS);
+ core_writel(priv, reg, offset);
+
+ /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
+ reg = core_readl(priv, CORE_IMP_CTL);
+ reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
+ reg &= ~(RX_DIS | TX_DIS);
+ core_writel(priv, reg, CORE_IMP_CTL);
+ } else {
+ reg = core_readl(priv, CORE_G_PCTL_PORT(port));
+ reg &= ~(RX_DIS | TX_DIS);
+ core_writel(priv, reg, CORE_G_PCTL_PORT(port));
+ }
}
static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)
@@ -303,11 +309,10 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
* send them to our master MDIO bus controller
*/
if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr))
- bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val);
+ return bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val);
else
- mdiobus_write_nested(priv->master_mii_bus, addr, regnum, val);
-
- return 0;
+ return mdiobus_write_nested(priv->master_mii_bus, addr,
+ regnum, val);
}
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
@@ -1093,12 +1098,16 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
return ret;
}
+ bcm_sf2_gphy_enable_set(priv->dev->ds, true);
+
ret = bcm_sf2_mdio_register(ds);
if (ret) {
pr_err("failed to register MDIO bus\n");
return ret;
}
+ bcm_sf2_gphy_enable_set(priv->dev->ds, false);
+
ret = bcm_sf2_cfp_rst(priv);
if (ret) {
pr_err("failed to reset CFP\n");
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 703e6bd..43b00e8 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -456,10 +456,12 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
*/
irq_set_lockdep_class(chip->irq, &lock_key, &request_key);
+ mutex_unlock(&chip->reg_lock);
err = request_threaded_irq(chip->irq, NULL,
mv88e6xxx_g1_irq_thread_fn,
IRQF_ONESHOT,
dev_name(chip->dev), chip);
+ mutex_lock(&chip->reg_lock);
if (err)
mv88e6xxx_g1_irq_free_common(chip);
@@ -2643,11 +2645,22 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
mutex_unlock(&chip->reg_lock);
if (reg == MII_PHYSID2) {
- /* Some internal PHYS don't have a model number. Use
- * the mv88e6390 family model number instead.
- */
- if (!(val & 0x3f0))
- val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
+ /* Some internal PHYs don't have a model number. */
+ if (chip->info->family != MV88E6XXX_FAMILY_6165)
+ /* Then there is the 6165 family. It gets is
+ * PHYs correct. But it can also have two
+ * SERDES interfaces in the PHY address
+ * space. And these don't have a model
+ * number. But they are not PHYs, so we don't
+ * want to give them something a PHY driver
+ * will recognise.
+ *
+ * Use the mv88e6390 family model number
+ * instead, for anything which really could be
+ * a PHY,
+ */
+ if (!(val & 0x3f0))
+ val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
}
return err ? err : val;
@@ -3026,7 +3039,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
- .port_set_speed = mv88e6390_port_set_speed,
+ .port_set_speed = mv88e6341_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3647,7 +3660,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
- .port_set_speed = mv88e6390_port_set_speed,
+ .port_set_speed = mv88e6341_port_set_speed,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index 38e399e..8298d67 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -371,6 +371,11 @@ int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
{
u16 ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST;
+ /* Use the default high priority for management frames sent to
+ * the CPU.
+ */
+ port |= MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI;
+
return mv88e6390_g1_monitor_write(chip, ptr, port);
}
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index bef0133..70b870c 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -197,6 +197,7 @@
#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST 0x2000
#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST 0x2100
#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST 0x3000
+#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI 0x00e0
#define MV88E6390_G1_MONITOR_MGMT_CTL_DATA_MASK 0x00ff
/* Offset 0x1C: Global Control 2 */
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index fdeddbf..2f16a31 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -228,8 +228,11 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
break;
case 2500:
- ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
- MV88E6390_PORT_MAC_CTL_ALTSPEED;
+ if (alt_bit)
+ ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
+ MV88E6390_PORT_MAC_CTL_ALTSPEED;
+ else
+ ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000;
break;
case 10000:
/* all bits set, fall through... */
@@ -291,6 +294,24 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
}
+/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */
+int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (speed == SPEED_MAX)
+ speed = port < 5 ? 1000 : 2500;
+
+ if (speed > 2500)
+ return -EOPNOTSUPP;
+
+ if (speed == 200 && port != 0)
+ return -EOPNOTSUPP;
+
+ if (speed == 2500 && port < 5)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_speed(chip, port, speed, !port, true);
+}
+
/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 95b59f5..cbb64a7 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -280,6 +280,7 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);
int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
diff --git a/drivers/net/ethernet/amazon/Kconfig b/drivers/net/ethernet/amazon/Kconfig
index 99b30353..9e87d7b 100644
--- a/drivers/net/ethernet/amazon/Kconfig
+++ b/drivers/net/ethernet/amazon/Kconfig
@@ -17,7 +17,7 @@
config ENA_ETHERNET
tristate "Elastic Network Adapter (ENA) support"
- depends on (PCI_MSI && X86)
+ depends on PCI_MSI && !CPU_BIG_ENDIAN
---help---
This driver supports Elastic Network Adapter (ENA)"
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index b5d7281..e26c195 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -1197,8 +1197,8 @@ static int ena_io_poll(struct napi_struct *napi, int budget)
struct ena_napi *ena_napi = container_of(napi, struct ena_napi, napi);
struct ena_ring *tx_ring, *rx_ring;
- u32 tx_work_done;
- u32 rx_work_done;
+ int tx_work_done;
+ int rx_work_done = 0;
int tx_budget;
int napi_comp_call = 0;
int ret;
@@ -1215,7 +1215,11 @@ static int ena_io_poll(struct napi_struct *napi, int budget)
}
tx_work_done = ena_clean_tx_irq(tx_ring, tx_budget);
- rx_work_done = ena_clean_rx_irq(rx_ring, napi, budget);
+ /* On netpoll the budget is zero and the handler should only clean the
+ * tx completions.
+ */
+ if (likely(budget))
+ rx_work_done = ena_clean_rx_irq(rx_ring, napi, budget);
/* If the device is about to reset or down, avoid unmask
* the interrupt and return 0 so NAPI won't reschedule
diff --git a/drivers/net/ethernet/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c
index 01d132c..265039c 100644
--- a/drivers/net/ethernet/amd/am79c961a.c
+++ b/drivers/net/ethernet/amd/am79c961a.c
@@ -440,7 +440,7 @@ static void am79c961_timeout(struct net_device *dev)
/*
* Transmit a packet
*/
-static int
+static netdev_tx_t
am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
{
struct dev_priv *priv = netdev_priv(dev);
diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c
index c5b8126..d3d44e0 100644
--- a/drivers/net/ethernet/amd/atarilance.c
+++ b/drivers/net/ethernet/amd/atarilance.c
@@ -339,7 +339,8 @@ static unsigned long lance_probe1( struct net_device *dev, struct lance_addr
*init_rec );
static int lance_open( struct net_device *dev );
static void lance_init_ring( struct net_device *dev );
-static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev );
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
static irqreturn_t lance_interrupt( int irq, void *dev_id );
static int lance_rx( struct net_device *dev );
static int lance_close( struct net_device *dev );
@@ -769,7 +770,8 @@ static void lance_tx_timeout (struct net_device *dev)
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
-static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
+static netdev_tx_t
+lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c
index 00332a1..9f23703 100644
--- a/drivers/net/ethernet/amd/declance.c
+++ b/drivers/net/ethernet/amd/declance.c
@@ -894,7 +894,7 @@ static void lance_tx_timeout(struct net_device *dev)
netif_wake_queue(dev);
}
-static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
diff --git a/drivers/net/ethernet/amd/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c
index 77b1db2..da7e3d4 100644
--- a/drivers/net/ethernet/amd/sun3lance.c
+++ b/drivers/net/ethernet/amd/sun3lance.c
@@ -236,7 +236,8 @@ struct lance_private {
static int lance_probe( struct net_device *dev);
static int lance_open( struct net_device *dev );
static void lance_init_ring( struct net_device *dev );
-static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev );
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
static irqreturn_t lance_interrupt( int irq, void *dev_id);
static int lance_rx( struct net_device *dev );
static int lance_close( struct net_device *dev );
@@ -511,7 +512,8 @@ static void lance_init_ring( struct net_device *dev )
}
-static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
+static netdev_tx_t
+lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
int entry, len;
diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c
index 19f89d9..9d48998 100644
--- a/drivers/net/ethernet/amd/sunlance.c
+++ b/drivers/net/ethernet/amd/sunlance.c
@@ -1106,7 +1106,7 @@ static void lance_tx_timeout(struct net_device *dev)
netif_wake_queue(dev);
}
-static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
int entry, skblen, len;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 24f1053..d96a84a 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2009,7 +2009,7 @@ static int xgbe_close(struct net_device *netdev)
return 0;
}
-static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
struct xgbe_hw_if *hw_if = &pdata->hw_if;
@@ -2018,7 +2018,7 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
struct xgbe_ring *ring;
struct xgbe_packet_data *packet;
struct netdev_queue *txq;
- int ret;
+ netdev_tx_t ret;
DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len);
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
index 91eb891..90a0e1d 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
@@ -42,8 +42,8 @@
#define AQ_CFG_IS_LRO_DEF 1U
/* RSS */
-#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX 128U
-#define AQ_CFG_RSS_HASHKEY_SIZE 320U
+#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX 64U
+#define AQ_CFG_RSS_HASHKEY_SIZE 40U
#define AQ_CFG_IS_RSS_DEF 1U
#define AQ_CFG_NUM_RSS_QUEUES_DEF AQ_CFG_VECS_DEF
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
index 4f34808..8cc34b0 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
@@ -44,7 +44,7 @@ static void aq_nic_rss_init(struct aq_nic_s *self, unsigned int num_rss_queues)
struct aq_rss_parameters *rss_params = &cfg->aq_rss;
int i = 0;
- static u8 rss_key[40] = {
+ static u8 rss_key[AQ_CFG_RSS_HASHKEY_SIZE] = {
0x1e, 0xad, 0x71, 0x87, 0x65, 0xfc, 0x26, 0x7d,
0x0d, 0x45, 0x67, 0x74, 0xcd, 0x06, 0x1a, 0x18,
0xb6, 0xc1, 0xf0, 0xc7, 0xbb, 0x18, 0xbe, 0xf8,
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
index 10ec5dc..5502ec5f 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
@@ -1468,3 +1468,11 @@ void hw_atl_reg_glb_cpu_scratch_scp_set(struct aq_hw_s *aq_hw,
aq_hw_write_reg(aq_hw, HW_ATL_GLB_CPU_SCRATCH_SCP_ADR(scratch_scp),
glb_cpu_scratch_scp);
}
+
+void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr)
+{
+ aq_hw_write_reg_bit(aq_hw, HW_ATL_MCP_UP_FORCE_INTERRUPT_ADR,
+ HW_ATL_MCP_UP_FORCE_INTERRUPT_MSK,
+ HW_ATL_MCP_UP_FORCE_INTERRUPT_SHIFT,
+ up_force_intr);
+}
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
index b3bf64b..41f2399 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
@@ -701,4 +701,7 @@ void hw_atl_msm_reg_wr_strobe_set(struct aq_hw_s *aq_hw, u32 reg_wr_strobe);
/* set pci register reset disable */
void hw_atl_pci_pci_reg_res_dis_set(struct aq_hw_s *aq_hw, u32 pci_reg_res_dis);
+/* set uP Force Interrupt */
+void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr);
+
#endif /* HW_ATL_LLH_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
index e2ecdb1..a715fa3 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
@@ -2405,4 +2405,17 @@
#define HW_ATL_GLB_CPU_SCRATCH_SCP_ADR(scratch_scp) \
(0x00000300u + (scratch_scp) * 0x4)
+/* register address for bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_ADR 0x00000404
+/* bitmask for bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_MSK 0x00000002
+/* inverted bitmask for bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_MSKN 0xFFFFFFFD
+/* lower bit position of bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_SHIFT 1
+/* width of bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_WIDTH 1
+/* default value of bitfield uP Force Interrupt */
+#define HW_ATL_MCP_UP_FORCE_INTERRUPT_DEFAULT 0x0
+
#endif /* HW_ATL_LLH_INTERNAL_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
index 9939cca..096ec18 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
@@ -327,17 +327,31 @@ static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
err = -ETIME;
goto err_exit;
}
+ if (IS_CHIP_FEATURE(REVISION_B1)) {
+ u32 offset = 0;
- aq_hw_write_reg(self, 0x00000208U, a);
+ for (; offset < cnt; ++offset) {
+ aq_hw_write_reg(self, 0x328, p[offset]);
+ aq_hw_write_reg(self, 0x32C,
+ (0x80000000 | (0xFFFF & (offset * 4))));
+ hw_atl_mcp_up_force_intr_set(self, 1);
+ /* 1000 times by 10us = 10ms */
+ AQ_HW_WAIT_FOR((aq_hw_read_reg(self,
+ 0x32C) & 0xF0000000) !=
+ 0x80000000,
+ 10, 1000);
+ }
+ } else {
+ u32 offset = 0;
- for (++cnt; --cnt;) {
- u32 i = 0U;
+ aq_hw_write_reg(self, 0x208, a);
- aq_hw_write_reg(self, 0x0000020CU, *(p++));
- aq_hw_write_reg(self, 0x00000200U, 0xC000U);
+ for (; offset < cnt; ++offset) {
+ aq_hw_write_reg(self, 0x20C, p[offset]);
+ aq_hw_write_reg(self, 0x200, 0xC000);
- for (i = 1024U;
- (0x100U & aq_hw_read_reg(self, 0x00000200U)) && --i;) {
+ AQ_HW_WAIT_FOR((aq_hw_read_reg(self, 0x200U) &
+ 0x100) == 0, 10, 1000);
}
}
@@ -401,7 +415,7 @@ struct aq_hw_atl_utils_fw_rpc_tid_s {
#define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
-static int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
+int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
{
int err = 0;
struct aq_hw_atl_utils_fw_rpc_tid_s sw;
@@ -425,8 +439,8 @@ static int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
return err;
}
-static int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
- struct hw_aq_atl_utils_fw_rpc **rpc)
+int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
+ struct hw_aq_atl_utils_fw_rpc **rpc)
{
int err = 0;
struct aq_hw_atl_utils_fw_rpc_tid_s sw;
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
index b875590..505c8a2 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
@@ -319,6 +319,11 @@ struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self);
int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
u32 *p, u32 cnt);
+int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size);
+
+int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
+ struct hw_aq_atl_utils_fw_rpc **rpc);
+
extern const struct aq_fw_ops aq_fw_1x_ops;
extern const struct aq_fw_ops aq_fw_2x_ops;
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
index e379437..6300d94 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
@@ -21,6 +21,7 @@
#define HW_ATL_FW2X_MPI_EFUSE_ADDR 0x364
#define HW_ATL_FW2X_MPI_MBOX_ADDR 0x360
+#define HW_ATL_FW2X_MPI_RPC_ADDR 0x334
#define HW_ATL_FW2X_MPI_CONTROL_ADDR 0x368
#define HW_ATL_FW2X_MPI_CONTROL2_ADDR 0x36C
@@ -40,6 +41,10 @@ static int aq_fw2x_init(struct aq_hw_s *self)
AQ_HW_WAIT_FOR(0U != (self->mbox_addr =
aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR)),
1000U, 10U);
+ AQ_HW_WAIT_FOR(0U != (self->rpc_addr =
+ aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR)),
+ 1000U, 100U);
+
return err;
}
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index 0f65768..a1df2eb 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -265,6 +265,9 @@ static int emac_rockchip_remove(struct platform_device *pdev)
if (priv->regulator)
regulator_disable(priv->regulator);
+ if (priv->soc_data->need_div_macclk)
+ clk_disable_unprepare(priv->macclk);
+
free_netdev(ndev);
return err;
}
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index 9dc6da0..3164aad 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -473,7 +473,9 @@ static void atl1e_mdio_write(struct net_device *netdev, int phy_id,
{
struct atl1e_adapter *adapter = netdev_priv(netdev);
- atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val);
+ if (atl1e_write_phy_reg(&adapter->hw,
+ reg_num & MDIO_REG_ADDR_MASK, val))
+ netdev_err(netdev, "write phy register failed\n");
}
static int atl1e_mii_ioctl(struct net_device *netdev,
diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index 897302a..50f8a37 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -568,12 +568,13 @@ static irqreturn_t bcm_enet_isr_dma(int irq, void *dev_id)
/*
* tx request callback
*/
-static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bcm_enet_priv *priv;
struct bcm_enet_desc *desc;
u32 len_stat;
- int ret;
+ netdev_tx_t ret;
priv = netdev_priv(dev);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index ee5159e..df5e8c2 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -1113,7 +1113,7 @@ static inline u8 bnx2x_get_path_func_num(struct bnx2x *bp)
for (i = 0; i < E1H_FUNC_MAX / 2; i++) {
u32 func_config =
MF_CFG_RD(bp,
- func_mf_config[BP_PORT(bp) + 2 * i].
+ func_mf_config[BP_PATH(bp) + 2 * i].
config);
func_num +=
((func_config & FUNC_MF_CFG_FUNC_HIDE) ? 0 : 1);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 68c62e3..df4f77a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -3540,6 +3540,16 @@ static void bnx2x_drv_info_iscsi_stat(struct bnx2x *bp)
*/
static void bnx2x_config_mf_bw(struct bnx2x *bp)
{
+ /* Workaround for MFW bug.
+ * MFW is not supposed to generate BW attention in
+ * single function mode.
+ */
+ if (!IS_MF(bp)) {
+ DP(BNX2X_MSG_MCP,
+ "Ignoring MF BW config in single function mode\n");
+ return;
+ }
+
if (bp->link_vars.link_up) {
bnx2x_cmng_fns_init(bp, true, CMNG_FNS_MINMAX);
bnx2x_link_sync_notify(bp);
@@ -9985,10 +9995,18 @@ static void bnx2x_recovery_failed(struct bnx2x *bp)
*/
static void bnx2x_parity_recover(struct bnx2x *bp)
{
- bool global = false;
u32 error_recovered, error_unrecovered;
- bool is_parity;
+ bool is_parity, global = false;
+#ifdef CONFIG_BNX2X_SRIOV
+ int vf_idx;
+ for (vf_idx = 0; vf_idx < bp->requested_nr_virtfn; vf_idx++) {
+ struct bnx2x_virtf *vf = BP_VF(bp, vf_idx);
+
+ if (vf)
+ vf->state = VF_LOST;
+ }
+#endif
DP(NETIF_MSG_HW, "Handling parity\n");
while (1) {
switch (bp->recovery_state) {
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
index 62da465..ab60f4f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
@@ -2394,15 +2394,21 @@ static int bnx2x_set_pf_tx_switching(struct bnx2x *bp, bool enable)
/* send the ramrod on all the queues of the PF */
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
+ int tx_idx;
/* Set the appropriate Queue object */
q_params.q_obj = &bnx2x_sp_obj(bp, fp).q_obj;
- /* Update the Queue state */
- rc = bnx2x_queue_state_change(bp, &q_params);
- if (rc) {
- BNX2X_ERR("Failed to configure Tx switching\n");
- return rc;
+ for (tx_idx = FIRST_TX_COS_INDEX;
+ tx_idx < fp->max_cos; tx_idx++) {
+ q_params.params.update.cid_index = tx_idx;
+
+ /* Update the Queue state */
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc) {
+ BNX2X_ERR("Failed to configure Tx switching\n");
+ return rc;
+ }
}
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
index eb814c6..4dc34de 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
@@ -139,6 +139,7 @@ struct bnx2x_virtf {
#define VF_ACQUIRED 1 /* VF acquired, but not initialized */
#define VF_ENABLED 2 /* VF Enabled */
#define VF_RESET 3 /* VF FLR'd, pending cleanup */
+#define VF_LOST 4 /* Recovery while VFs are loaded */
bool flr_clnup_stage; /* true during flr cleanup */
bool malicious; /* true if FW indicated so, until FLR */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
index 8e0a317..152758a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
@@ -2114,6 +2114,18 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
{
int i;
+ if (vf->state == VF_LOST) {
+ /* Just ack the FW and return if VFs are lost
+ * in case of parity error. VFs are supposed to be timedout
+ * on waiting for PF response.
+ */
+ DP(BNX2X_MSG_IOV,
+ "VF 0x%x lost, not handling the request\n", vf->abs_vfid);
+
+ storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
+ return;
+ }
+
/* check if tlv type is known */
if (bnx2x_tlv_supported(mbx->first_tlv.tl.type)) {
/* Lock the per vf op mutex and note the locker's identity.
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index c54a74d..2f61175 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -7148,6 +7148,9 @@ static bool bnxt_drv_busy(struct bnxt *bp)
test_bit(BNXT_STATE_READ_STATS, &bp->state));
}
+static void bnxt_get_ring_stats(struct bnxt *bp,
+ struct rtnl_link_stats64 *stats);
+
static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init,
bool link_re_init)
{
@@ -7173,6 +7176,9 @@ static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init,
del_timer_sync(&bp->timer);
bnxt_free_skbs(bp);
+ /* Save ring stats before shutdown */
+ if (bp->bnapi)
+ bnxt_get_ring_stats(bp, &bp->net_stats_prev);
if (irq_re_init) {
bnxt_free_irq(bp);
bnxt_del_napi(bp);
@@ -7234,23 +7240,12 @@ static int bnxt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EOPNOTSUPP;
}
-static void
-bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+static void bnxt_get_ring_stats(struct bnxt *bp,
+ struct rtnl_link_stats64 *stats)
{
- u32 i;
- struct bnxt *bp = netdev_priv(dev);
+ int i;
- set_bit(BNXT_STATE_READ_STATS, &bp->state);
- /* Make sure bnxt_close_nic() sees that we are reading stats before
- * we check the BNXT_STATE_OPEN flag.
- */
- smp_mb__after_atomic();
- if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
- clear_bit(BNXT_STATE_READ_STATS, &bp->state);
- return;
- }
- /* TODO check if we need to synchronize with bnxt_close path */
for (i = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_napi *bnapi = bp->bnapi[i];
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
@@ -7279,6 +7274,40 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
stats->tx_dropped += le64_to_cpu(hw_stats->tx_drop_pkts);
}
+}
+
+static void bnxt_add_prev_stats(struct bnxt *bp,
+ struct rtnl_link_stats64 *stats)
+{
+ struct rtnl_link_stats64 *prev_stats = &bp->net_stats_prev;
+
+ stats->rx_packets += prev_stats->rx_packets;
+ stats->tx_packets += prev_stats->tx_packets;
+ stats->rx_bytes += prev_stats->rx_bytes;
+ stats->tx_bytes += prev_stats->tx_bytes;
+ stats->rx_missed_errors += prev_stats->rx_missed_errors;
+ stats->multicast += prev_stats->multicast;
+ stats->tx_dropped += prev_stats->tx_dropped;
+}
+
+static void
+bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+ struct bnxt *bp = netdev_priv(dev);
+
+ set_bit(BNXT_STATE_READ_STATS, &bp->state);
+ /* Make sure bnxt_close_nic() sees that we are reading stats before
+ * we check the BNXT_STATE_OPEN flag.
+ */
+ smp_mb__after_atomic();
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
+ clear_bit(BNXT_STATE_READ_STATS, &bp->state);
+ *stats = bp->net_stats_prev;
+ return;
+ }
+
+ bnxt_get_ring_stats(bp, stats);
+ bnxt_add_prev_stats(bp, stats);
if (bp->flags & BNXT_FLAG_PORT_STATS) {
struct rx_port_stats *rx = bp->hw_rx_port_stats;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index cf2d4a6..f9e253b 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1302,6 +1302,7 @@ struct bnxt {
void *hwrm_cmd_resp_addr;
dma_addr_t hwrm_cmd_resp_dma_addr;
+ struct rtnl_link_stats64 net_stats_prev;
struct rx_port_stats *hw_rx_port_stats;
struct tx_port_stats *hw_tx_port_stats;
struct rx_port_stats_ext *hw_rx_port_stats_ext;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
index 790c684..b178c2e 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
@@ -78,8 +78,12 @@ static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,
memcpy(buf, data_addr, bytesize);
dma_free_coherent(&bp->pdev->dev, bytesize, data_addr, data_dma_addr);
- if (rc)
+ if (rc == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+ netdev_err(bp->dev, "PF does not have admin privileges to modify NVM config\n");
+ return -EACCES;
+ } else if (rc) {
return -EIO;
+ }
return 0;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index da9b876..0a409ba 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -1444,14 +1444,22 @@ static int bnxt_flash_nvram(struct net_device *dev,
rc = hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT);
dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle);
+ if (rc == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+ netdev_info(dev,
+ "PF does not have admin privileges to flash the device\n");
+ rc = -EACCES;
+ } else if (rc) {
+ rc = -EIO;
+ }
return rc;
}
static int bnxt_firmware_reset(struct net_device *dev,
u16 dir_type)
{
- struct bnxt *bp = netdev_priv(dev);
struct hwrm_fw_reset_input req = {0};
+ struct bnxt *bp = netdev_priv(dev);
+ int rc;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1);
@@ -1491,7 +1499,15 @@ static int bnxt_firmware_reset(struct net_device *dev,
return -EINVAL;
}
- return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+ rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+ if (rc == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+ netdev_info(dev,
+ "PF does not have admin privileges to reset the device\n");
+ rc = -EACCES;
+ } else if (rc) {
+ rc = -EIO;
+ }
+ return rc;
}
static int bnxt_flash_firmware(struct net_device *dev,
@@ -1698,9 +1714,9 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
struct hwrm_nvm_install_update_output *resp = bp->hwrm_cmd_resp_addr;
struct hwrm_nvm_install_update_input install = {0};
const struct firmware *fw;
+ int rc, hwrm_err = 0;
u32 item_len;
u16 index;
- int rc;
bnxt_hwrm_fw_set_time(bp);
@@ -1743,15 +1759,16 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
memcpy(kmem, fw->data, fw->size);
modify.host_src_addr = cpu_to_le64(dma_handle);
- rc = hwrm_send_message(bp, &modify, sizeof(modify),
- FLASH_PACKAGE_TIMEOUT);
+ hwrm_err = hwrm_send_message(bp, &modify,
+ sizeof(modify),
+ FLASH_PACKAGE_TIMEOUT);
dma_free_coherent(&bp->pdev->dev, fw->size, kmem,
dma_handle);
}
}
release_firmware(fw);
- if (rc)
- return rc;
+ if (rc || hwrm_err)
+ goto err_exit;
if ((install_type & 0xffff) == 0)
install_type >>= 16;
@@ -1759,12 +1776,10 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
install.install_type = cpu_to_le32(install_type);
mutex_lock(&bp->hwrm_cmd_lock);
- rc = _hwrm_send_message(bp, &install, sizeof(install),
- INSTALL_PACKAGE_TIMEOUT);
- if (rc) {
- rc = -EOPNOTSUPP;
+ hwrm_err = _hwrm_send_message(bp, &install, sizeof(install),
+ INSTALL_PACKAGE_TIMEOUT);
+ if (hwrm_err)
goto flash_pkg_exit;
- }
if (resp->error_code) {
u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err;
@@ -1772,12 +1787,11 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
if (error_code == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) {
install.flags |= cpu_to_le16(
NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG);
- rc = _hwrm_send_message(bp, &install, sizeof(install),
- INSTALL_PACKAGE_TIMEOUT);
- if (rc) {
- rc = -EOPNOTSUPP;
+ hwrm_err = _hwrm_send_message(bp, &install,
+ sizeof(install),
+ INSTALL_PACKAGE_TIMEOUT);
+ if (hwrm_err)
goto flash_pkg_exit;
- }
}
}
@@ -1788,6 +1802,14 @@ static int bnxt_flash_package_from_file(struct net_device *dev,
}
flash_pkg_exit:
mutex_unlock(&bp->hwrm_cmd_lock);
+err_exit:
+ if (hwrm_err == HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED) {
+ netdev_info(dev,
+ "PF does not have admin privileges to flash the device\n");
+ rc = -EACCES;
+ } else if (hwrm_err) {
+ rc = -EOPNOTSUPP;
+ }
return rc;
}
@@ -2368,17 +2390,37 @@ static int bnxt_hwrm_mac_loopback(struct bnxt *bp, bool enable)
return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
}
+static int bnxt_query_force_speeds(struct bnxt *bp, u16 *force_speeds)
+{
+ struct hwrm_port_phy_qcaps_output *resp = bp->hwrm_cmd_resp_addr;
+ struct hwrm_port_phy_qcaps_input req = {0};
+ int rc;
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_QCAPS, -1, -1);
+ mutex_lock(&bp->hwrm_cmd_lock);
+ rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+ if (!rc)
+ *force_speeds = le16_to_cpu(resp->supported_speeds_force_mode);
+
+ mutex_unlock(&bp->hwrm_cmd_lock);
+ return rc;
+}
+
static int bnxt_disable_an_for_lpbk(struct bnxt *bp,
struct hwrm_port_phy_cfg_input *req)
{
struct bnxt_link_info *link_info = &bp->link_info;
- u16 fw_advertising = link_info->advertising;
+ u16 fw_advertising;
u16 fw_speed;
int rc;
if (!link_info->autoneg)
return 0;
+ rc = bnxt_query_force_speeds(bp, &fw_advertising);
+ if (rc)
+ return rc;
+
fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB;
if (netif_carrier_ok(bp->dev))
fw_speed = bp->link_info.link_speed;
@@ -2736,8 +2778,15 @@ static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg, int msg_len,
}
}
- if (info->dest_buf)
- memcpy(info->dest_buf + off, dma_buf, len);
+ if (info->dest_buf) {
+ if ((info->seg_start + off + len) <=
+ BNXT_COREDUMP_BUF_LEN(info->buf_len)) {
+ memcpy(info->dest_buf + off, dma_buf, len);
+ } else {
+ rc = -ENOBUFS;
+ break;
+ }
+ }
if (cmn_req->req_type ==
cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE))
@@ -2791,7 +2840,7 @@ static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id,
static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
u16 segment_id, u32 *seg_len,
- void *buf, u32 offset)
+ void *buf, u32 buf_len, u32 offset)
{
struct hwrm_dbg_coredump_retrieve_input req = {0};
struct bnxt_hwrm_dbg_dma_info info = {NULL};
@@ -2806,8 +2855,11 @@ static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
seq_no);
info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output,
data_len);
- if (buf)
+ if (buf) {
info.dest_buf = buf + offset;
+ info.buf_len = buf_len;
+ info.seg_start = offset;
+ }
rc = bnxt_hwrm_dbg_dma_data(bp, &req, sizeof(req), &info);
if (!rc)
@@ -2897,14 +2949,17 @@ bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
{
u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output);
+ u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0;
struct coredump_segment_record *seg_record = NULL;
- u32 offset = 0, seg_hdr_len, seg_record_len;
struct bnxt_coredump_segment_hdr seg_hdr;
struct bnxt_coredump coredump = {NULL};
time64_t start_time;
u16 start_utc;
int rc = 0, i;
+ if (buf)
+ buf_len = *dump_len;
+
start_time = ktime_get_real_seconds();
start_utc = sys_tz.tz_minuteswest * 60;
seg_hdr_len = sizeof(seg_hdr);
@@ -2937,6 +2992,12 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
u32 duration = 0, seg_len = 0;
unsigned long start, end;
+ if (buf && ((offset + seg_hdr_len) >
+ BNXT_COREDUMP_BUF_LEN(buf_len))) {
+ rc = -ENOBUFS;
+ goto err;
+ }
+
start = jiffies;
rc = bnxt_hwrm_dbg_coredump_initiate(bp, comp_id, seg_id);
@@ -2949,9 +3010,11 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
/* Write segment data into the buffer */
rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id,
- &seg_len, buf,
+ &seg_len, buf, buf_len,
offset + seg_hdr_len);
- if (rc)
+ if (rc && rc == -ENOBUFS)
+ goto err;
+ else if (rc)
netdev_err(bp->dev,
"Failed to retrieve coredump for seg = %d\n",
seg_record->segment_id);
@@ -2981,7 +3044,8 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
rc);
kfree(coredump.data);
*dump_len += sizeof(struct bnxt_coredump_record);
-
+ if (rc == -ENOBUFS)
+ netdev_err(bp->dev, "Firmware returned large coredump buffer");
return rc;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h
index b5b65b3..3998f6e 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h
@@ -31,6 +31,8 @@ struct bnxt_coredump {
u16 total_segs;
};
+#define BNXT_COREDUMP_BUF_LEN(len) ((len) - sizeof(struct bnxt_coredump_record))
+
struct bnxt_hwrm_dbg_dma_info {
void *dest_buf;
int dest_buf_size;
@@ -38,6 +40,8 @@ struct bnxt_hwrm_dbg_dma_info {
u16 seq_off;
u16 data_len_off;
u16 segs;
+ u32 seg_start;
+ u32 buf_len;
};
struct hwrm_dbg_cmn_input {
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index fd587be..b7d7501 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1169,7 +1169,7 @@ static int bcmgenet_power_down(struct bcmgenet_priv *priv,
break;
}
- return 0;
+ return ret;
}
static void bcmgenet_power_up(struct bcmgenet_priv *priv,
@@ -1998,8 +1998,6 @@ static void reset_umac(struct bcmgenet_priv *priv)
/* issue soft reset with (rg)mii loopback to ensure a stable rxclk */
bcmgenet_umac_writel(priv, CMD_SW_RESET | CMD_LCL_LOOP_EN, UMAC_CMD);
- udelay(2);
- bcmgenet_umac_writel(priv, 0, UMAC_CMD);
}
static void bcmgenet_intr_disable(struct bcmgenet_priv *priv)
@@ -2020,6 +2018,8 @@ static void bcmgenet_link_intr_enable(struct bcmgenet_priv *priv)
*/
if (priv->internal_phy) {
int0_enable |= UMAC_IRQ_LINK_EVENT;
+ if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
+ int0_enable |= UMAC_IRQ_PHY_DET_R;
} else if (priv->ext_phy) {
int0_enable |= UMAC_IRQ_LINK_EVENT;
} else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
@@ -2618,11 +2618,16 @@ static void bcmgenet_irq_task(struct work_struct *work)
priv->irq0_stat = 0;
spin_unlock_irq(&priv->lock);
- /* Link UP/DOWN event */
- if (status & UMAC_IRQ_LINK_EVENT) {
- priv->dev->phydev->link = !!(status & UMAC_IRQ_LINK_UP);
- phy_mac_interrupt(priv->dev->phydev);
+ if (status & UMAC_IRQ_PHY_DET_R &&
+ priv->dev->phydev->autoneg != AUTONEG_ENABLE) {
+ phy_init_hw(priv->dev->phydev);
+ genphy_config_aneg(priv->dev->phydev);
}
+
+ /* Link UP/DOWN event */
+ if (status & UMAC_IRQ_LINK_EVENT)
+ phy_mac_interrupt(priv->dev->phydev);
+
}
/* bcmgenet_isr1: handle Rx and Tx priority queues */
@@ -2717,7 +2722,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
}
/* all other interested interrupts handled in bottom half */
- status &= UMAC_IRQ_LINK_EVENT;
+ status &= (UMAC_IRQ_LINK_EVENT | UMAC_IRQ_PHY_DET_R);
if (status) {
/* Save irq status for bottom-half processing. */
spin_lock_irqsave(&priv->lock, flags);
@@ -3670,6 +3675,7 @@ static int bcmgenet_resume(struct device *d)
phy_init_hw(dev->phydev);
/* Speed settings must be restored */
+ genphy_config_aneg(dev->phydev);
bcmgenet_mii_config(priv->dev, false);
bcmgenet_set_hw_addr(priv, dev->dev_addr);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 0d527fa..a5049d6 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -184,8 +184,38 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
+ int bmcr = -1;
+ int ret;
u32 reg;
+ /* MAC clocking workaround during reset of umac state machines */
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ if (reg & CMD_SW_RESET) {
+ /* An MII PHY must be isolated to prevent TXC contention */
+ if (priv->phy_interface == PHY_INTERFACE_MODE_MII) {
+ ret = phy_read(phydev, MII_BMCR);
+ if (ret >= 0) {
+ bmcr = ret;
+ ret = phy_write(phydev, MII_BMCR,
+ bmcr | BMCR_ISOLATE);
+ }
+ if (ret) {
+ netdev_err(dev, "failed to isolate PHY\n");
+ return ret;
+ }
+ }
+ /* Switch MAC clocking to RGMII generated clock */
+ bcmgenet_sys_writel(priv, PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
+ /* Ensure 5 clks with Rx disabled
+ * followed by 5 clks with Reset asserted
+ */
+ udelay(4);
+ reg &= ~(CMD_SW_RESET | CMD_LCL_LOOP_EN);
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+ /* Ensure 5 more clocks before Rx is enabled */
+ udelay(2);
+ }
+
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
@@ -217,6 +247,9 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
phydev->supported &= PHY_BASIC_FEATURES;
bcmgenet_sys_writel(priv,
PORT_MODE_EXT_EPHY, SYS_PORT_CTRL);
+ /* Restore the MII PHY after isolation */
+ if (bmcr >= 0)
+ phy_write(phydev, MII_BMCR, bmcr);
break;
case PHY_INTERFACE_MODE_REVMII:
@@ -226,11 +259,10 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
- if ((dev->phydev->supported & PHY_BASIC_FEATURES) ==
- PHY_BASIC_FEATURES)
- port_ctrl = PORT_MODE_EXT_RVMII_25;
- else
+ if (dev->phydev->supported & PHY_1000BT_FEATURES)
port_ctrl = PORT_MODE_EXT_RVMII_50;
+ else
+ port_ctrl = PORT_MODE_EXT_RVMII_25;
bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
break;
diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c
index ef4a0c32..7e3f9642 100644
--- a/drivers/net/ethernet/broadcom/sb1250-mac.c
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c
@@ -299,7 +299,7 @@ static enum sbmac_state sbmac_set_channel_state(struct sbmac_softc *,
static void sbmac_promiscuous_mode(struct sbmac_softc *sc, int onoff);
static uint64_t sbmac_addr2reg(unsigned char *ptr);
static irqreturn_t sbmac_intr(int irq, void *dev_instance);
-static int sbmac_start_tx(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t sbmac_start_tx(struct sk_buff *skb, struct net_device *dev);
static void sbmac_setmulti(struct sbmac_softc *sc);
static int sbmac_init(struct platform_device *pldev, long long base);
static int sbmac_set_speed(struct sbmac_softc *s, enum sbmac_speed speed);
@@ -2028,7 +2028,7 @@ static irqreturn_t sbmac_intr(int irq,void *dev_instance)
* Return value:
* nothing
********************************************************************* */
-static int sbmac_start_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t sbmac_start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct sbmac_softc *sc = netdev_priv(dev);
unsigned long flags;
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 9bbaad9..efb44d5 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -499,7 +499,11 @@
/* Bitfields in TISUBN */
#define GEM_SUBNSINCR_OFFSET 0
-#define GEM_SUBNSINCR_SIZE 16
+#define GEM_SUBNSINCRL_OFFSET 24
+#define GEM_SUBNSINCRL_SIZE 8
+#define GEM_SUBNSINCRH_OFFSET 0
+#define GEM_SUBNSINCRH_SIZE 16
+#define GEM_SUBNSINCR_SIZE 24
/* Bitfields in TI */
#define GEM_NSINCR_OFFSET 0
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 74eeb3a..c2eb188 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -860,7 +860,9 @@ static void macb_tx_interrupt(struct macb_queue *queue)
/* First, update TX stats if needed */
if (skb) {
- if (gem_ptp_do_txstamp(queue, skb, desc) == 0) {
+ if (unlikely(skb_shinfo(skb)->tx_flags &
+ SKBTX_HW_TSTAMP) &&
+ gem_ptp_do_txstamp(queue, skb, desc) == 0) {
/* skb now belongs to timestamp buffer
* and will be removed later
*/
@@ -1721,7 +1723,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
padlen = 0;
/* No room for FCS, need to reallocate skb. */
else
- padlen = ETH_FCS_LEN - tailroom;
+ padlen = ETH_FCS_LEN;
} else {
/* Add room for FCS. */
padlen += ETH_FCS_LEN;
@@ -3328,7 +3330,7 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk,
if (!err)
err = -ENODEV;
- dev_err(&pdev->dev, "failed to get macb_clk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to get macb_clk (%d)\n", err);
return err;
}
@@ -3337,7 +3339,7 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk,
if (!err)
err = -ENODEV;
- dev_err(&pdev->dev, "failed to get hclk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to get hclk (%d)\n", err);
return err;
}
@@ -3351,25 +3353,25 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk,
err = clk_prepare_enable(*pclk);
if (err) {
- dev_err(&pdev->dev, "failed to enable pclk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err);
return err;
}
err = clk_prepare_enable(*hclk);
if (err) {
- dev_err(&pdev->dev, "failed to enable hclk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to enable hclk (%d)\n", err);
goto err_disable_pclk;
}
err = clk_prepare_enable(*tx_clk);
if (err) {
- dev_err(&pdev->dev, "failed to enable tx_clk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to enable tx_clk (%d)\n", err);
goto err_disable_hclk;
}
err = clk_prepare_enable(*rx_clk);
if (err) {
- dev_err(&pdev->dev, "failed to enable rx_clk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to enable rx_clk (%d)\n", err);
goto err_disable_txclk;
}
@@ -3839,7 +3841,7 @@ static int at91ether_clk_init(struct platform_device *pdev, struct clk **pclk,
err = clk_prepare_enable(*pclk);
if (err) {
- dev_err(&pdev->dev, "failed to enable pclk (%u)\n", err);
+ dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err);
return err;
}
@@ -4192,6 +4194,7 @@ static int macb_remove(struct platform_device *pdev)
mdiobus_free(bp->mii_bus);
unregister_netdev(dev);
+ tasklet_kill(&bp->hresp_err_tasklet);
clk_disable_unprepare(bp->tx_clk);
clk_disable_unprepare(bp->hclk);
clk_disable_unprepare(bp->pclk);
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
index a6dc47e..8f912de 100644
--- a/drivers/net/ethernet/cadence/macb_ptp.c
+++ b/drivers/net/ethernet/cadence/macb_ptp.c
@@ -115,7 +115,10 @@ static int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec)
* to take effect.
*/
spin_lock_irqsave(&bp->tsu_clk_lock, flags);
- gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, incr_spec->sub_ns));
+ /* RegBit[15:0] = Subns[23:8]; RegBit[31:24] = Subns[7:0] */
+ gem_writel(bp, TISUBN, GEM_BF(SUBNSINCRL, incr_spec->sub_ns) |
+ GEM_BF(SUBNSINCRH, (incr_spec->sub_ns >>
+ GEM_SUBNSINCRL_SIZE)));
gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns));
spin_unlock_irqrestore(&bp->tsu_clk_lock, flags);
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 6fb13fa..304e4b9 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -2324,7 +2324,7 @@ static inline int send_nic_timestamp_pkt(struct octeon_device *oct,
* @returns whether the packet was transmitted to the device okay or not
* (NETDEV_TX_OK or NETDEV_TX_BUSY)
*/
-static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct lio *lio;
struct octnet_buf_free_info *finfo;
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index b778357..d83773b 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -1390,7 +1390,7 @@ static int send_nic_timestamp_pkt(struct octeon_device *oct,
* @returns whether the packet was transmitted to the device okay or not
* (NETDEV_TX_OK or NETDEV_TX_BUSY)
*/
-static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct octnet_buf_free_info *finfo;
union octnic_cmd_setup cmdsetup;
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
index c99b59f..a1bda16 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
@@ -31,7 +31,8 @@
static int lio_vf_rep_open(struct net_device *ndev);
static int lio_vf_rep_stop(struct net_device *ndev);
-static int lio_vf_rep_pkt_xmit(struct sk_buff *skb, struct net_device *ndev);
+static netdev_tx_t lio_vf_rep_pkt_xmit(struct sk_buff *skb,
+ struct net_device *ndev);
static void lio_vf_rep_tx_timeout(struct net_device *netdev);
static int lio_vf_rep_phys_port_name(struct net_device *dev,
char *buf, size_t len);
@@ -382,7 +383,7 @@ lio_vf_rep_packet_sent_callback(struct octeon_device *oct,
netif_wake_queue(ndev);
}
-static int
+static netdev_tx_t
lio_vf_rep_pkt_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c
index f878a55..d0ed6c4 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c
@@ -1450,8 +1450,9 @@ void lio_enable_irq(struct octeon_droq *droq, struct octeon_instr_queue *iq)
}
if (iq) {
spin_lock_bh(&iq->lock);
- writel(iq->pkt_in_done, iq->inst_cnt_reg);
- iq->pkt_in_done = 0;
+ writel(iq->pkts_processed, iq->inst_cnt_reg);
+ iq->pkt_in_done -= iq->pkts_processed;
+ iq->pkts_processed = 0;
/* this write needs to be flushed before we release the lock */
mmiowb();
spin_unlock_bh(&iq->lock);
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
index 2327062..aecd0d3 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
@@ -94,6 +94,8 @@ struct octeon_instr_queue {
u32 pkt_in_done;
+ u32 pkts_processed;
+
/** A spinlock to protect access to the input ring.*/
spinlock_t iq_flush_running_lock;
diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c
index 3deb3c0..1d9ab7f 100644
--- a/drivers/net/ethernet/cavium/liquidio/request_manager.c
+++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c
@@ -123,6 +123,7 @@ int octeon_init_instr_queue(struct octeon_device *oct,
iq->do_auto_flush = 1;
iq->db_timeout = (u32)conf->db_timeout;
atomic_set(&iq->instr_pending, 0);
+ iq->pkts_processed = 0;
/* Initialize the spinlock for this instruction queue */
spin_lock_init(&iq->lock);
@@ -497,6 +498,7 @@ octeon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq,
lio_process_iq_request_list(oct, iq, 0);
if (inst_processed) {
+ iq->pkts_processed += inst_processed;
atomic_sub(inst_processed, &iq->instr_pending);
iq->stats.instr_processed += inst_processed;
}
diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
index bb43ddb..0957e73 100644
--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
@@ -1268,12 +1268,13 @@ static int octeon_mgmt_stop(struct net_device *netdev)
return 0;
}
-static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t
+octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
union mgmt_port_ring_entry re;
unsigned long flags;
- int rv = NETDEV_TX_BUSY;
+ netdev_tx_t rv = NETDEV_TX_BUSY;
re.d64 = 0;
re.s.tstamp = ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) != 0);
@@ -1495,7 +1496,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev)
netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM;
- netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM;
+ netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM - VLAN_HLEN;
mac = of_get_mac_address(pdev->dev.of_node);
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index e337da6..8ae28f8 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -1118,7 +1118,7 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
phy_interface_mode(lmac->lmac_type)))
return -ENODEV;
- phy_start_aneg(lmac->phydev);
+ phy_start(lmac->phydev);
return 0;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
index b34f0f0..8386929 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
@@ -273,8 +273,8 @@ void cxgb4_dcb_handle_fw_update(struct adapter *adap,
enum cxgb4_dcb_state_input input =
((pcmd->u.dcb.control.all_syncd_pkd &
FW_PORT_CMD_ALL_SYNCD_F)
- ? CXGB4_DCB_STATE_FW_ALLSYNCED
- : CXGB4_DCB_STATE_FW_INCOMPLETE);
+ ? CXGB4_DCB_INPUT_FW_ALLSYNCED
+ : CXGB4_DCB_INPUT_FW_INCOMPLETE);
if (dcb->dcb_version != FW_PORT_DCB_VER_UNKNOWN) {
dcb_running_version = FW_PORT_CMD_DCB_VERSION_G(
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h
index 02040b9..484ee82 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h
@@ -67,7 +67,7 @@
do { \
if ((__dcb)->dcb_version == FW_PORT_DCB_VER_IEEE) \
cxgb4_dcb_state_fsm((__dev), \
- CXGB4_DCB_STATE_FW_ALLSYNCED); \
+ CXGB4_DCB_INPUT_FW_ALLSYNCED); \
} while (0)
/* States we can be in for a port's Data Center Bridging.
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index b429b72..d320e9a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -3035,6 +3035,9 @@ static int sge_queue_entries(const struct adapter *adap)
int tot_uld_entries = 0;
int i;
+ if (!is_uld(adap))
+ goto lld_only;
+
mutex_lock(&uld_mutex);
for (i = 0; i < CXGB4_TX_MAX; i++)
tot_uld_entries += sge_qinfo_uld_txq_entries(adap, i);
@@ -3045,6 +3048,7 @@ static int sge_queue_entries(const struct adapter *adap)
}
mutex_unlock(&uld_mutex);
+lld_only:
return DIV_ROUND_UP(adap->sge.ethqsets, 4) +
tot_uld_entries +
DIV_ROUND_UP(MAX_CTRL_QUEUES, 4) + 1;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
index dba8a0c..3d834d4 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
@@ -673,10 +673,10 @@ static void uld_init(struct adapter *adap, struct cxgb4_lld_info *lld)
lld->write_cmpl_support = adap->params.write_cmpl_support;
}
-static void uld_attach(struct adapter *adap, unsigned int uld)
+static int uld_attach(struct adapter *adap, unsigned int uld)
{
- void *handle;
struct cxgb4_lld_info lli;
+ void *handle;
uld_init(adap, &lli);
uld_queue_init(adap, uld, &lli);
@@ -686,7 +686,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
dev_warn(adap->pdev_dev,
"could not attach to the %s driver, error %ld\n",
adap->uld[uld].name, PTR_ERR(handle));
- return;
+ return PTR_ERR(handle);
}
adap->uld[uld].handle = handle;
@@ -694,23 +694,24 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
if (adap->flags & FULL_INIT_DONE)
adap->uld[uld].state_change(handle, CXGB4_STATE_UP);
+
+ return 0;
}
-/**
- * cxgb4_register_uld - register an upper-layer driver
- * @type: the ULD type
- * @p: the ULD methods
+/* cxgb4_register_uld - register an upper-layer driver
+ * @type: the ULD type
+ * @p: the ULD methods
*
- * Registers an upper-layer driver with this driver and notifies the ULD
- * about any presently available devices that support its type. Returns
- * %-EBUSY if a ULD of the same type is already registered.
+ * Registers an upper-layer driver with this driver and notifies the ULD
+ * about any presently available devices that support its type. Returns
+ * %-EBUSY if a ULD of the same type is already registered.
*/
int cxgb4_register_uld(enum cxgb4_uld type,
const struct cxgb4_uld_info *p)
{
- int ret = 0;
unsigned int adap_idx = 0;
struct adapter *adap;
+ int ret = 0;
if (type >= CXGB4_ULD_MAX)
return -EINVAL;
@@ -744,12 +745,16 @@ int cxgb4_register_uld(enum cxgb4_uld type,
if (ret)
goto free_irq;
adap->uld[type] = *p;
- uld_attach(adap, type);
+ ret = uld_attach(adap, type);
+ if (ret)
+ goto free_txq;
adap_idx++;
}
mutex_unlock(&uld_mutex);
return 0;
+free_txq:
+ release_sge_txq_uld(adap, type);
free_irq:
if (adap->flags & FULL_INIT_DONE)
quiesce_rx_uld(adap, type);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 5fe5d16..8350c0c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -3889,7 +3889,7 @@ int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op)
c.param[0].mnem =
cpu_to_be32(FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_FWCACHE));
- c.param[0].val = (__force __be32)op;
+ c.param[0].val = cpu_to_be32(op);
return t4_wr_mbox(adap, adap->mbox, &c, sizeof(c), NULL);
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index ff84791..972dc7b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -722,6 +722,10 @@ static int adapter_up(struct adapter *adapter)
if (adapter->flags & USING_MSIX)
name_msix_vecs(adapter);
+
+ /* Initialize hash mac addr list*/
+ INIT_LIST_HEAD(&adapter->mac_hlist);
+
adapter->flags |= FULL_INIT_DONE;
}
@@ -747,8 +751,6 @@ static int adapter_up(struct adapter *adapter)
enable_rx(adapter);
t4vf_sge_start(adapter);
- /* Initialize hash mac addr list*/
- INIT_LIST_HEAD(&adapter->mac_hlist);
return 0;
}
diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c
index 13dfdfc..edc1d19 100644
--- a/drivers/net/ethernet/cirrus/ep93xx_eth.c
+++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c
@@ -767,6 +767,7 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
{
struct net_device *dev;
struct ep93xx_priv *ep;
+ struct resource *mem;
dev = platform_get_drvdata(pdev);
if (dev == NULL)
@@ -782,8 +783,8 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
iounmap(ep->base_addr);
if (ep->res != NULL) {
- release_resource(ep->res);
- kfree(ep->res);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
}
free_netdev(dev);
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index dfd1ad0..01a2120 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -577,6 +577,8 @@ static int gmac_setup_txqs(struct net_device *netdev)
if (port->txq_dma_base & ~DMA_Q_BASE_MASK) {
dev_warn(geth->dev, "TX queue base is not aligned\n");
+ dma_free_coherent(geth->dev, len * sizeof(*desc_ring),
+ desc_ring, port->txq_dma_base);
kfree(skb_tab);
return -ENOMEM;
}
@@ -2530,6 +2532,7 @@ static int gemini_ethernet_port_remove(struct platform_device *pdev)
struct gemini_ethernet_port *port = platform_get_drvdata(pdev);
gemini_port_remove(port);
+ free_netdev(port->netdev);
return 0;
}
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index ed6c76d..e4fc38cb 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -712,8 +712,8 @@ static bool ftgmac100_prep_tx_csum(struct sk_buff *skb, u32 *csum_vlan)
return skb_checksum_help(skb) == 0;
}
-static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
- struct net_device *netdev)
+static netdev_tx_t ftgmac100_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
{
struct ftgmac100 *priv = netdev_priv(netdev);
struct ftgmac100_txdes *txdes, *first;
@@ -739,6 +739,18 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
*/
nfrags = skb_shinfo(skb)->nr_frags;
+ /* Setup HW checksumming */
+ csum_vlan = 0;
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ !ftgmac100_prep_tx_csum(skb, &csum_vlan))
+ goto drop;
+
+ /* Add VLAN tag */
+ if (skb_vlan_tag_present(skb)) {
+ csum_vlan |= FTGMAC100_TXDES1_INS_VLANTAG;
+ csum_vlan |= skb_vlan_tag_get(skb) & 0xffff;
+ }
+
/* Get header len */
len = skb_headlen(skb);
@@ -765,19 +777,6 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
if (nfrags == 0)
f_ctl_stat |= FTGMAC100_TXDES0_LTS;
txdes->txdes3 = cpu_to_le32(map);
-
- /* Setup HW checksumming */
- csum_vlan = 0;
- if (skb->ip_summed == CHECKSUM_PARTIAL &&
- !ftgmac100_prep_tx_csum(skb, &csum_vlan))
- goto drop;
-
- /* Add VLAN tag */
- if (skb_vlan_tag_present(skb)) {
- csum_vlan |= FTGMAC100_TXDES1_INS_VLANTAG;
- csum_vlan |= skb_vlan_tag_get(skb) & 0xffff;
- }
-
txdes->txdes1 = cpu_to_le32(csum_vlan);
/* Next descriptor */
diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c
index 9015bd9..084f24d 100644
--- a/drivers/net/ethernet/faraday/ftmac100.c
+++ b/drivers/net/ethernet/faraday/ftmac100.c
@@ -634,8 +634,8 @@ static void ftmac100_tx_complete(struct ftmac100 *priv)
;
}
-static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
- dma_addr_t map)
+static netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+ dma_addr_t map)
{
struct net_device *netdev = priv->netdev;
struct ftmac100_txdes *txdes;
@@ -1015,7 +1015,8 @@ static int ftmac100_stop(struct net_device *netdev)
return 0;
}
-static int ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t
+ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct ftmac100 *priv = netdev_priv(netdev);
dma_addr_t map;
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
index d7915cd..462bb8c 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
@@ -2046,7 +2046,8 @@ static inline int dpaa_xmit(struct dpaa_priv *priv,
return 0;
}
-static int dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
+static netdev_tx_t
+dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
{
const int queue_mapping = skb_get_queue_mapping(skb);
bool nonlinear = skb_is_nonlinear(skb);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 4cf80de..296ae1e 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3597,6 +3597,11 @@ fec_drv_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ return ret;
cancel_work_sync(&fep->tx_timeout_work);
fec_ptp_stop(pdev);
@@ -3604,13 +3609,17 @@ fec_drv_remove(struct platform_device *pdev)
fec_enet_mii_remove(fep);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
- pm_runtime_put(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
+
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(fep->phy_node);
free_netdev(ndev);
+ clk_disable_unprepare(fep->clk_ahb);
+ clk_disable_unprepare(fep->clk_ipg);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c
index 6d7269d..b90bab7 100644
--- a/drivers/net/ethernet/freescale/fec_mpc52xx.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c
@@ -305,7 +305,8 @@ static int mpc52xx_fec_close(struct net_device *dev)
* invariant will hold if you make sure that the netif_*_queue()
* calls are done at the proper times.
*/
-static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
struct bcom_fec_bd *bd;
diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
index 2c2976a..7c548ed5 100644
--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
@@ -481,7 +481,8 @@ static struct sk_buff *tx_skb_align_workaround(struct net_device *dev,
}
#endif
-static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
cbd_t __iomem *bdp;
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index f27f9ba..c97c4ed 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -112,7 +112,7 @@
const char gfar_driver_version[] = "2.0";
static int gfar_enet_open(struct net_device *dev);
-static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void gfar_reset_task(struct work_struct *work);
static void gfar_timeout(struct net_device *dev);
static int gfar_close(struct net_device *dev);
@@ -2334,7 +2334,7 @@ static inline bool gfar_csum_errata_76(struct gfar_private *priv,
/* This is called by the kernel when a frame is ready for transmission.
* It is pointed to by the dev->hard_start_xmit function pointer
*/
-static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_tx_q *tx_queue = NULL;
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 1e2b53a..a5bf02a 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -3085,7 +3085,8 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
/* This is called by the kernel when a frame is ready for transmission. */
/* It is pointed to by the dev->hard_start_xmit function pointer */
-static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ucc_geth_private *ugeth = netdev_priv(dev);
#ifdef CONFIG_UGETH_TX_ON_DEMAND
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
index a91d49d..718afa4 100644
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -174,6 +174,7 @@ struct hip04_priv {
dma_addr_t rx_phys[RX_DESC_NUM];
unsigned int rx_head;
unsigned int rx_buf_size;
+ unsigned int rx_cnt_remaining;
struct device_node *phy_node;
struct phy_device *phy;
@@ -423,7 +424,8 @@ static void hip04_start_tx_timer(struct hip04_priv *priv)
ns, HRTIMER_MODE_REL);
}
-static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t
+hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct hip04_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
@@ -454,9 +456,9 @@ static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_tx_timestamp(skb);
hip04_set_xmit_desc(priv, phys);
- priv->tx_head = TX_NEXT(tx_head);
count++;
netdev_sent_queue(ndev, skb->len);
+ priv->tx_head = TX_NEXT(tx_head);
stats->tx_bytes += skb->len;
stats->tx_packets++;
@@ -487,7 +489,6 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
struct net_device *ndev = priv->ndev;
struct net_device_stats *stats = &ndev->stats;
- unsigned int cnt = hip04_recv_cnt(priv);
struct rx_desc *desc;
struct sk_buff *skb;
unsigned char *buf;
@@ -500,8 +501,8 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
/* clean up tx descriptors */
tx_remaining = hip04_tx_reclaim(ndev, false);
-
- while (cnt && !last) {
+ priv->rx_cnt_remaining += hip04_recv_cnt(priv);
+ while (priv->rx_cnt_remaining && !last) {
buf = priv->rx_buf[priv->rx_head];
skb = build_skb(buf, priv->rx_buf_size);
if (unlikely(!skb)) {
@@ -547,11 +548,13 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
hip04_set_recv_desc(priv, phys);
priv->rx_head = RX_NEXT(priv->rx_head);
- if (rx >= budget)
+ if (rx >= budget) {
+ --priv->rx_cnt_remaining;
goto done;
+ }
- if (--cnt == 0)
- cnt = hip04_recv_cnt(priv);
+ if (--priv->rx_cnt_remaining == 0)
+ priv->rx_cnt_remaining += hip04_recv_cnt(priv);
}
if (!(priv->reg_inten & RCV_INT)) {
@@ -636,6 +639,7 @@ static int hip04_mac_open(struct net_device *ndev)
int i;
priv->rx_head = 0;
+ priv->rx_cnt_remaining = 0;
priv->tx_head = 0;
priv->tx_tail = 0;
hip04_reset_ppe(priv);
@@ -942,7 +946,6 @@ static int hip04_remove(struct platform_device *pdev)
hip04_free_ring(ndev, d);
unregister_netdev(ndev);
- free_irq(ndev->irq, ndev);
of_node_put(priv->phy_node);
cancel_work_sync(&priv->tx_timeout_task);
free_netdev(ndev);
diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
index c572700..471805e 100644
--- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
+++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
@@ -736,7 +736,7 @@ static int hix5hd2_fill_sg_desc(struct hix5hd2_priv *priv,
return 0;
}
-static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct hix5hd2_priv *priv = netdev_priv(dev);
struct hix5hd2_desc *desc;
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c
index c7fa97a..b758b3e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hnae.c
+++ b/drivers/net/ethernet/hisilicon/hns/hnae.c
@@ -203,7 +203,6 @@ hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags)
ring->q = q;
ring->flags = flags;
- spin_lock_init(&ring->lock);
ring->coal_param = q->handle->coal_param;
assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr);
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
index 08a750f..c8cbbe5 100644
--- a/drivers/net/ethernet/hisilicon/hns/hnae.h
+++ b/drivers/net/ethernet/hisilicon/hns/hnae.h
@@ -278,9 +278,6 @@ struct hnae_ring {
/* statistic */
struct ring_stats stats;
- /* ring lock for poll one */
- spinlock_t lock;
-
dma_addr_t desc_dma_addr;
u32 buf_size; /* size for hnae_desc->addr, preset by AE */
u16 desc_num; /* total number of desc */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index 1c70f9a..7f8cf80 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -947,15 +947,6 @@ static int is_valid_clean_head(struct hnae_ring *ring, int h)
return u > c ? (h > c && h <= u) : (h > c || h <= u);
}
-/* netif_tx_lock will turn down the performance, set only when necessary */
-#ifdef CONFIG_NET_POLL_CONTROLLER
-#define NETIF_TX_LOCK(ring) spin_lock(&(ring)->lock)
-#define NETIF_TX_UNLOCK(ring) spin_unlock(&(ring)->lock)
-#else
-#define NETIF_TX_LOCK(ring)
-#define NETIF_TX_UNLOCK(ring)
-#endif
-
/* reclaim all desc in one budget
* return error or number of desc left
*/
@@ -969,21 +960,16 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
int head;
int bytes, pkts;
- NETIF_TX_LOCK(ring);
-
head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
rmb(); /* make sure head is ready before touch any data */
- if (is_ring_empty(ring) || head == ring->next_to_clean) {
- NETIF_TX_UNLOCK(ring);
+ if (is_ring_empty(ring) || head == ring->next_to_clean)
return 0; /* no data to poll */
- }
if (!is_valid_clean_head(ring, head)) {
netdev_err(ndev, "wrong head (%d, %d-%d)\n", head,
ring->next_to_use, ring->next_to_clean);
ring->stats.io_err_cnt++;
- NETIF_TX_UNLOCK(ring);
return -EIO;
}
@@ -998,8 +984,6 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
ring->stats.tx_pkts += pkts;
ring->stats.tx_bytes += bytes;
- NETIF_TX_UNLOCK(ring);
-
dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
netdev_tx_completed_queue(dev_queue, pkts, bytes);
@@ -1059,16 +1043,12 @@ static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data)
int head;
int bytes, pkts;
- NETIF_TX_LOCK(ring);
-
head = ring->next_to_use; /* ntu :soft setted ring position*/
bytes = 0;
pkts = 0;
while (head != ring->next_to_clean)
hns_nic_reclaim_one_desc(ring, &bytes, &pkts);
- NETIF_TX_UNLOCK(ring);
-
dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
netdev_tx_reset_queue(dev_queue);
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.c b/drivers/net/ethernet/hisilicon/hns3/hnae3.c
index 0594a6c..f9259e5 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.c
@@ -29,8 +29,8 @@ static bool hnae3_client_match(enum hnae3_client_type client_type,
return false;
}
-static void hnae3_set_client_init_flag(struct hnae3_client *client,
- struct hnae3_ae_dev *ae_dev, int inited)
+void hnae3_set_client_init_flag(struct hnae3_client *client,
+ struct hnae3_ae_dev *ae_dev, int inited)
{
switch (client->type) {
case HNAE3_CLIENT_KNIC:
@@ -46,6 +46,7 @@ static void hnae3_set_client_init_flag(struct hnae3_client *client,
break;
}
}
+EXPORT_SYMBOL(hnae3_set_client_init_flag);
static int hnae3_get_client_init_flag(struct hnae3_client *client,
struct hnae3_ae_dev *ae_dev)
@@ -86,14 +87,11 @@ static int hnae3_match_n_instantiate(struct hnae3_client *client,
/* now, (un-)instantiate client by calling lower layer */
if (is_reg) {
ret = ae_dev->ops->init_client_instance(client, ae_dev);
- if (ret) {
+ if (ret)
dev_err(&ae_dev->pdev->dev,
"fail to instantiate client, ret = %d\n", ret);
- return ret;
- }
- hnae3_set_client_init_flag(client, ae_dev, 1);
- return 0;
+ return ret;
}
if (hnae3_get_client_init_flag(client, ae_dev)) {
@@ -175,8 +173,12 @@ void hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo)
if (!id)
continue;
- /* ae_dev init should set flag */
+ if (!ae_algo->ops) {
+ dev_err(&ae_dev->pdev->dev, "ae_algo ops are null\n");
+ continue;
+ }
ae_dev->ops = ae_algo->ops;
+
ret = ae_algo->ops->init_ae_dev(ae_dev);
if (ret) {
dev_err(&ae_dev->pdev->dev,
@@ -184,6 +186,7 @@ void hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo)
continue;
}
+ /* ae_dev init should set flag */
hnae3_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1);
/* check the client list for the match with this ae_dev type and
@@ -241,7 +244,7 @@ EXPORT_SYMBOL(hnae3_unregister_ae_algo);
* @ae_dev: the AE device
* NOTE: the duplicated name will not be checked
*/
-void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
+int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
{
const struct pci_device_id *id;
struct hnae3_ae_algo *ae_algo;
@@ -258,14 +261,13 @@ void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
if (!id)
continue;
- ae_dev->ops = ae_algo->ops;
-
- if (!ae_dev->ops) {
- dev_err(&ae_dev->pdev->dev, "ae_dev ops are null\n");
+ if (!ae_algo->ops) {
+ dev_err(&ae_dev->pdev->dev, "ae_algo ops are null\n");
+ ret = -EOPNOTSUPP;
goto out_err;
}
+ ae_dev->ops = ae_algo->ops;
- /* ae_dev init should set flag */
ret = ae_dev->ops->init_ae_dev(ae_dev);
if (ret) {
dev_err(&ae_dev->pdev->dev,
@@ -273,6 +275,7 @@ void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
goto out_err;
}
+ /* ae_dev init should set flag */
hnae3_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1);
break;
}
@@ -288,8 +291,15 @@ void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
ret);
}
-out_err:
mutex_unlock(&hnae3_common_lock);
+
+ return 0;
+
+out_err:
+ list_del(&ae_dev->node);
+ mutex_unlock(&hnae3_common_lock);
+
+ return ret;
}
EXPORT_SYMBOL(hnae3_register_ae_dev);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 67befff..5e1a7ab 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -513,7 +513,7 @@ struct hnae3_handle {
#define hnae3_get_bit(origin, shift) \
hnae3_get_field((origin), (0x1 << (shift)), (shift))
-void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
+int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev);
void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo);
@@ -521,4 +521,7 @@ void hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo);
void hnae3_unregister_client(struct hnae3_client *client);
int hnae3_register_client(struct hnae3_client *client);
+
+void hnae3_set_client_init_flag(struct hnae3_client *client,
+ struct hnae3_ae_dev *ae_dev, int inited);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 0ccfa6a..1aaf6e2 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -195,8 +195,6 @@ void hns3_set_vector_coalesce_tx_gl(struct hns3_enet_tqp_vector *tqp_vector,
static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
struct hns3_nic_priv *priv)
{
- struct hnae3_handle *h = priv->ae_handle;
-
/* initialize the configuration for interrupt coalescing.
* 1. GL (Interrupt Gap Limiter)
* 2. RL (Interrupt Rate Limiter)
@@ -209,9 +207,6 @@ static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
tqp_vector->tx_group.coal.int_gl = HNS3_INT_GL_50K;
tqp_vector->rx_group.coal.int_gl = HNS3_INT_GL_50K;
- /* Default: disable RL */
- h->kinfo.int_rl_setting = 0;
-
tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
tqp_vector->rx_group.coal.flow_level = HNS3_FLOW_LOW;
tqp_vector->tx_group.coal.flow_level = HNS3_FLOW_LOW;
@@ -1447,13 +1442,11 @@ static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
}
ret = h->ae_algo->ops->set_mtu(h, new_mtu);
- if (ret) {
+ if (ret)
netdev_err(netdev, "failed to change MTU in hardware %d\n",
ret);
- return ret;
- }
-
- netdev->mtu = new_mtu;
+ else
+ netdev->mtu = new_mtu;
/* if the netdev was running earlier, bring it up again */
if (if_running && hns3_nic_net_open(netdev))
@@ -1481,6 +1474,9 @@ static bool hns3_get_tx_timeo_queue_info(struct net_device *ndev)
time_after(jiffies,
(trans_start + ndev->watchdog_timeo))) {
timeout_queue = i;
+ netdev_info(ndev, "queue state: 0x%lx, delta msecs: %u\n",
+ q->state,
+ jiffies_to_msecs(jiffies - trans_start));
break;
}
}
@@ -1611,9 +1607,13 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ae_dev->dev_type = HNAE3_DEV_KNIC;
pci_set_drvdata(pdev, ae_dev);
- hnae3_register_ae_dev(ae_dev);
+ ret = hnae3_register_ae_dev(ae_dev);
+ if (ret) {
+ devm_kfree(&pdev->dev, ae_dev);
+ pci_set_drvdata(pdev, NULL);
+ }
- return 0;
+ return ret;
}
/* hns3_remove - Device removal routine
@@ -1627,6 +1627,7 @@ static void hns3_remove(struct pci_dev *pdev)
hns3_disable_sriov(pdev);
hnae3_unregister_ae_dev(ae_dev);
+ pci_set_drvdata(pdev, NULL);
}
/**
@@ -2131,18 +2132,18 @@ static void hns3_rx_skb(struct hns3_enet_ring *ring, struct sk_buff *skb)
napi_gro_receive(&ring->tqp_vector->napi, skb);
}
-static u16 hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
- struct hns3_desc *desc, u32 l234info)
+static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
+ struct hns3_desc *desc, u32 l234info,
+ u16 *vlan_tag)
{
struct pci_dev *pdev = ring->tqp->handle->pdev;
- u16 vlan_tag;
if (pdev->revision == 0x20) {
- vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
- if (!(vlan_tag & VLAN_VID_MASK))
- vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
+ *vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
+ if (!(*vlan_tag & VLAN_VID_MASK))
+ *vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
- return vlan_tag;
+ return (*vlan_tag != 0);
}
#define HNS3_STRP_OUTER_VLAN 0x1
@@ -2151,17 +2152,14 @@ static u16 hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
switch (hnae3_get_field(l234info, HNS3_RXD_STRP_TAGP_M,
HNS3_RXD_STRP_TAGP_S)) {
case HNS3_STRP_OUTER_VLAN:
- vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
- break;
+ *vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
+ return true;
case HNS3_STRP_INNER_VLAN:
- vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
- break;
+ *vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
+ return true;
default:
- vlan_tag = 0;
- break;
+ return false;
}
-
- return vlan_tag;
}
static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
@@ -2263,8 +2261,7 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
u16 vlan_tag;
- vlan_tag = hns3_parse_vlan_tag(ring, desc, l234info);
- if (vlan_tag & VLAN_VID_MASK)
+ if (hns3_parse_vlan_tag(ring, desc, l234info, &vlan_tag))
__vlan_hwaccel_put_tag(skb,
htons(ETH_P_8021Q),
vlan_tag);
@@ -2377,7 +2374,7 @@ static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group)
u32 time_passed_ms;
u16 new_int_gl;
- if (!ring_group->coal.int_gl || !tqp_vector->last_jiffies)
+ if (!tqp_vector->last_jiffies)
return false;
if (ring_group->total_packets == 0) {
@@ -2558,7 +2555,7 @@ static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector,
chain = devm_kzalloc(&pdev->dev, sizeof(*chain),
GFP_KERNEL);
if (!chain)
- return -ENOMEM;
+ goto err_free_chain;
cur_chain->next = chain;
chain->tqp_index = tx_ring->tqp->tqp_index;
@@ -2588,7 +2585,7 @@ static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector,
while (rx_ring) {
chain = devm_kzalloc(&pdev->dev, sizeof(*chain), GFP_KERNEL);
if (!chain)
- return -ENOMEM;
+ goto err_free_chain;
cur_chain->next = chain;
chain->tqp_index = rx_ring->tqp->tqp_index;
@@ -2603,6 +2600,16 @@ static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector,
}
return 0;
+
+err_free_chain:
+ cur_chain = head->next;
+ while (cur_chain) {
+ chain = cur_chain->next;
+ devm_kfree(&pdev->dev, chain);
+ cur_chain = chain;
+ }
+
+ return -ENOMEM;
}
static void hns3_free_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector,
@@ -2847,8 +2854,10 @@ static int hns3_queue_to_ring(struct hnae3_queue *tqp,
return ret;
ret = hns3_ring_get_cfg(tqp, priv, HNAE3_RING_TYPE_RX);
- if (ret)
+ if (ret) {
+ devm_kfree(priv->dev, priv->ring_data[tqp->tqp_index].ring);
return ret;
+ }
return 0;
}
@@ -2875,6 +2884,12 @@ static int hns3_get_ring_config(struct hns3_nic_priv *priv)
return 0;
err:
+ while (i--) {
+ devm_kfree(priv->dev, priv->ring_data[i].ring);
+ devm_kfree(priv->dev,
+ priv->ring_data[i + h->kinfo.num_tqps].ring);
+ }
+
devm_kfree(&pdev->dev, priv->ring_data);
return ret;
}
@@ -3425,6 +3440,31 @@ int hns3_nic_reset_all_ring(struct hnae3_handle *h)
return 0;
}
+static void hns3_store_coal(struct hns3_nic_priv *priv)
+{
+ /* ethtool only support setting and querying one coal
+ * configuation for now, so save the vector 0' coal
+ * configuation here in order to restore it.
+ */
+ memcpy(&priv->tx_coal, &priv->tqp_vector[0].tx_group.coal,
+ sizeof(struct hns3_enet_coalesce));
+ memcpy(&priv->rx_coal, &priv->tqp_vector[0].rx_group.coal,
+ sizeof(struct hns3_enet_coalesce));
+}
+
+static void hns3_restore_coal(struct hns3_nic_priv *priv)
+{
+ u16 vector_num = priv->vector_num;
+ int i;
+
+ for (i = 0; i < vector_num; i++) {
+ memcpy(&priv->tqp_vector[i].tx_group.coal, &priv->tx_coal,
+ sizeof(struct hns3_enet_coalesce));
+ memcpy(&priv->tqp_vector[i].rx_group.coal, &priv->rx_coal,
+ sizeof(struct hns3_enet_coalesce));
+ }
+}
+
static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
{
struct hnae3_knic_private_info *kinfo = &handle->kinfo;
@@ -3471,6 +3511,8 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle)
/* Carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
+ hns3_restore_coal(priv);
+
ret = hns3_nic_init_vector_data(priv);
if (ret)
return ret;
@@ -3498,6 +3540,8 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
return ret;
}
+ hns3_store_coal(priv);
+
ret = hns3_uninit_all_ring(priv);
if (ret)
netdev_err(netdev, "uninit ring error\n");
@@ -3532,24 +3576,7 @@ static int hns3_reset_notify(struct hnae3_handle *handle,
return ret;
}
-static void hns3_restore_coal(struct hns3_nic_priv *priv,
- struct hns3_enet_coalesce *tx,
- struct hns3_enet_coalesce *rx)
-{
- u16 vector_num = priv->vector_num;
- int i;
-
- for (i = 0; i < vector_num; i++) {
- memcpy(&priv->tqp_vector[i].tx_group.coal, tx,
- sizeof(struct hns3_enet_coalesce));
- memcpy(&priv->tqp_vector[i].rx_group.coal, rx,
- sizeof(struct hns3_enet_coalesce));
- }
-}
-
-static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num,
- struct hns3_enet_coalesce *tx,
- struct hns3_enet_coalesce *rx)
+static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num)
{
struct hns3_nic_priv *priv = netdev_priv(netdev);
struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -3567,7 +3594,7 @@ static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num,
if (ret)
goto err_alloc_vector;
- hns3_restore_coal(priv, tx, rx);
+ hns3_restore_coal(priv);
ret = hns3_nic_init_vector_data(priv);
if (ret)
@@ -3599,7 +3626,6 @@ int hns3_set_channels(struct net_device *netdev,
struct hns3_nic_priv *priv = netdev_priv(netdev);
struct hnae3_handle *h = hns3_get_handle(netdev);
struct hnae3_knic_private_info *kinfo = &h->kinfo;
- struct hns3_enet_coalesce tx_coal, rx_coal;
bool if_running = netif_running(netdev);
u32 new_tqp_num = ch->combined_count;
u16 org_tqp_num;
@@ -3631,15 +3657,7 @@ int hns3_set_channels(struct net_device *netdev,
goto open_netdev;
}
- /* Changing the tqp num may also change the vector num,
- * ethtool only support setting and querying one coal
- * configuation for now, so save the vector 0' coal
- * configuation here in order to restore it.
- */
- memcpy(&tx_coal, &priv->tqp_vector[0].tx_group.coal,
- sizeof(struct hns3_enet_coalesce));
- memcpy(&rx_coal, &priv->tqp_vector[0].rx_group.coal,
- sizeof(struct hns3_enet_coalesce));
+ hns3_store_coal(priv);
hns3_nic_dealloc_vector_data(priv);
@@ -3647,10 +3665,9 @@ int hns3_set_channels(struct net_device *netdev,
hns3_put_ring_config(priv);
org_tqp_num = h->kinfo.num_tqps;
- ret = hns3_modify_tqp_num(netdev, new_tqp_num, &tx_coal, &rx_coal);
+ ret = hns3_modify_tqp_num(netdev, new_tqp_num);
if (ret) {
- ret = hns3_modify_tqp_num(netdev, org_tqp_num,
- &tx_coal, &rx_coal);
+ ret = hns3_modify_tqp_num(netdev, org_tqp_num);
if (ret) {
/* If revert to old tqp failed, fatal error occurred */
dev_err(&netdev->dev,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index cb450d7..94d7446 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -541,6 +541,8 @@ struct hns3_nic_priv {
/* Vxlan/Geneve information */
struct hns3_udp_tunnel udp_tnl[HNS3_UDP_TNL_MAX];
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ struct hns3_enet_coalesce tx_coal;
+ struct hns3_enet_coalesce rx_coal;
};
union l3_hdr_info {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 6a3c6b0..0c34ea1 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -100,41 +100,26 @@ static int hns3_lp_up(struct net_device *ndev, enum hnae3_loop loop_mode)
struct hnae3_handle *h = hns3_get_handle(ndev);
int ret;
- if (!h->ae_algo->ops->start)
- return -EOPNOTSUPP;
-
ret = hns3_nic_reset_all_ring(h);
if (ret)
return ret;
- ret = h->ae_algo->ops->start(h);
- if (ret) {
- netdev_err(ndev,
- "hns3_lb_up ae start return error: %d\n", ret);
- return ret;
- }
-
ret = hns3_lp_setup(ndev, loop_mode, true);
usleep_range(10000, 20000);
- return ret;
+ return 0;
}
static int hns3_lp_down(struct net_device *ndev, enum hnae3_loop loop_mode)
{
- struct hnae3_handle *h = hns3_get_handle(ndev);
int ret;
- if (!h->ae_algo->ops->stop)
- return -EOPNOTSUPP;
-
ret = hns3_lp_setup(ndev, loop_mode, false);
if (ret) {
netdev_err(ndev, "lb_setup return error: %d\n", ret);
return ret;
}
- h->ae_algo->ops->stop(h);
usleep_range(10000, 20000);
return 0;
@@ -152,6 +137,7 @@ static void hns3_lp_setup_skb(struct sk_buff *skb)
packet = skb_put(skb, HNS3_NIC_LB_TEST_PACKET_SIZE);
memcpy(ethh->h_dest, ndev->dev_addr, ETH_ALEN);
+ ethh->h_dest[5] += 0x1f;
eth_zero_addr(ethh->h_source);
ethh->h_proto = htons(ETH_P_ARP);
skb_reset_mac_header(skb);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
index 68026a5..09a4d87 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
@@ -24,15 +24,15 @@ static int hclge_ring_space(struct hclge_cmq_ring *ring)
return ring->desc_num - used - 1;
}
-static int is_valid_csq_clean_head(struct hclge_cmq_ring *ring, int h)
+static int is_valid_csq_clean_head(struct hclge_cmq_ring *ring, int head)
{
- int u = ring->next_to_use;
- int c = ring->next_to_clean;
+ int ntu = ring->next_to_use;
+ int ntc = ring->next_to_clean;
- if (unlikely(h >= ring->desc_num))
- return 0;
+ if (ntu > ntc)
+ return head >= ntc && head <= ntu;
- return u > c ? (h > c && h <= u) : (h > c || h <= u);
+ return head >= ntc || head <= ntu;
}
static int hclge_alloc_cmd_desc(struct hclge_cmq_ring *ring)
@@ -260,6 +260,8 @@ int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num)
if (desc_ret == HCLGE_CMD_EXEC_SUCCESS)
retval = 0;
+ else if (desc_ret == HCLGE_CMD_NOT_SUPPORTED)
+ retval = -EOPNOTSUPP;
else
retval = -EIO;
hw->cmq.last_status = desc_ret;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 821d4c2..d752068 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -39,7 +39,7 @@ struct hclge_cmq_ring {
enum hclge_cmd_return_status {
HCLGE_CMD_EXEC_SUCCESS = 0,
HCLGE_CMD_NO_AUTH = 1,
- HCLGE_CMD_NOT_EXEC = 2,
+ HCLGE_CMD_NOT_SUPPORTED = 2,
HCLGE_CMD_QUEUE_FULL = 3,
};
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
index 92f1938..a75d7c8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
@@ -245,6 +245,9 @@ static int hclge_ieee_setpfc(struct hnae3_handle *h, struct ieee_pfc *pfc)
hdev->flag & HCLGE_FLAG_MQPRIO_ENABLE)
return -EINVAL;
+ if (pfc->pfc_en == hdev->tm_info.pfc_en)
+ return 0;
+
prio_tc = hdev->tm_info.prio_tc;
pfc_map = 0;
@@ -257,10 +260,8 @@ static int hclge_ieee_setpfc(struct hnae3_handle *h, struct ieee_pfc *pfc)
}
}
- if (pfc_map == hdev->tm_info.hw_pfc_map)
- return 0;
-
hdev->tm_info.hw_pfc_map = pfc_map;
+ hdev->tm_info.pfc_en = pfc->pfc_en;
return hclge_pause_setup_hw(hdev);
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 89ca69f..f8cc8d1 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -2367,7 +2367,7 @@ static int hclge_get_mac_phy_link(struct hclge_dev *hdev)
mac_state = hclge_get_mac_link_status(hdev);
if (hdev->hw.mac.phydev) {
- if (!genphy_read_status(hdev->hw.mac.phydev))
+ if (hdev->hw.mac.phydev->state == PHY_RUNNING)
link_stat = mac_state &
hdev->hw.mac.phydev->link;
else
@@ -2574,7 +2574,7 @@ static irqreturn_t hclge_misc_irq_handle(int irq, void *data)
}
/* clear the source of interrupt if it is not cause by reset */
- if (event_cause != HCLGE_VECTOR0_EVENT_RST) {
+ if (event_cause == HCLGE_VECTOR0_EVENT_MBX) {
hclge_clear_event_cause(hdev, event_cause, clearval);
hclge_enable_vector(&hdev->misc_vector, true);
}
@@ -3666,6 +3666,8 @@ static int hclge_set_mac_loopback(struct hclge_dev *hdev, bool en)
/* 2 Then setup the loopback flag */
loop_en = le32_to_cpu(req->txrx_pad_fcs_loop_en);
hnae3_set_bit(loop_en, HCLGE_MAC_APP_LP_B, en ? 1 : 0);
+ hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, en ? 1 : 0);
+ hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, en ? 1 : 0);
req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);
@@ -3726,33 +3728,10 @@ static int hclge_set_serdes_loopback(struct hclge_dev *hdev, bool en)
return -EIO;
}
+ hclge_cfg_mac_mode(hdev, en);
return 0;
}
-static int hclge_set_loopback(struct hnae3_handle *handle,
- enum hnae3_loop loop_mode, bool en)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- int ret;
-
- switch (loop_mode) {
- case HNAE3_MAC_INTER_LOOP_MAC:
- ret = hclge_set_mac_loopback(hdev, en);
- break;
- case HNAE3_MAC_INTER_LOOP_SERDES:
- ret = hclge_set_serdes_loopback(hdev, en);
- break;
- default:
- ret = -ENOTSUPP;
- dev_err(&hdev->pdev->dev,
- "loop_mode %d is not supported\n", loop_mode);
- break;
- }
-
- return ret;
-}
-
static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id,
int stream_id, bool enable)
{
@@ -3773,6 +3752,36 @@ static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id,
return ret;
}
+static int hclge_set_loopback(struct hnae3_handle *handle,
+ enum hnae3_loop loop_mode, bool en)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ int i, ret;
+
+ switch (loop_mode) {
+ case HNAE3_MAC_INTER_LOOP_MAC:
+ ret = hclge_set_mac_loopback(hdev, en);
+ break;
+ case HNAE3_MAC_INTER_LOOP_SERDES:
+ ret = hclge_set_serdes_loopback(hdev, en);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ dev_err(&hdev->pdev->dev,
+ "loop_mode %d is not supported\n", loop_mode);
+ break;
+ }
+
+ for (i = 0; i < vport->alloc_tqps; i++) {
+ ret = hclge_tqp_enable(hdev, i, 0, en);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static void hclge_reset_tqp_stats(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
@@ -4374,7 +4383,7 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport,
hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
- hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
+ hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
hclge_prepare_mac_addr(&req, addr);
status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
if (!status) {
@@ -4441,7 +4450,7 @@ int hclge_rm_mc_addr_common(struct hclge_vport *vport,
hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
- hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
+ hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
hclge_prepare_mac_addr(&req, addr);
status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
if (!status) {
@@ -4784,7 +4793,7 @@ static int hclge_set_vlan_filter_hw(struct hclge_dev *hdev, __be16 proto,
return -EINVAL;
}
- for_each_set_bit(vport_idx, hdev->vlan_table[vlan_id], VLAN_N_VID)
+ for_each_set_bit(vport_idx, hdev->vlan_table[vlan_id], HCLGE_VPORT_NUM)
vport_num++;
if ((is_kill && vport_num == 0) || (!is_kill && vport_num == 1))
@@ -5467,26 +5476,31 @@ static int hclge_init_client_instance(struct hnae3_client *client,
vport->nic.client = client;
ret = client->ops->init_instance(&vport->nic);
if (ret)
- return ret;
+ goto clear_nic;
ret = hclge_init_instance_hw(hdev);
if (ret) {
client->ops->uninit_instance(&vport->nic,
0);
- return ret;
+ goto clear_nic;
}
+ hnae3_set_client_init_flag(client, ae_dev, 1);
+
if (hdev->roce_client &&
hnae3_dev_roce_supported(hdev)) {
struct hnae3_client *rc = hdev->roce_client;
ret = hclge_init_roce_base_info(vport);
if (ret)
- return ret;
+ goto clear_roce;
ret = rc->ops->init_instance(&vport->roce);
if (ret)
- return ret;
+ goto clear_roce;
+
+ hnae3_set_client_init_flag(hdev->roce_client,
+ ae_dev, 1);
}
break;
@@ -5496,7 +5510,9 @@ static int hclge_init_client_instance(struct hnae3_client *client,
ret = client->ops->init_instance(&vport->nic);
if (ret)
- return ret;
+ goto clear_nic;
+
+ hnae3_set_client_init_flag(client, ae_dev, 1);
break;
case HNAE3_CLIENT_ROCE:
@@ -5508,16 +5524,27 @@ static int hclge_init_client_instance(struct hnae3_client *client,
if (hdev->roce_client && hdev->nic_client) {
ret = hclge_init_roce_base_info(vport);
if (ret)
- return ret;
+ goto clear_roce;
ret = client->ops->init_instance(&vport->roce);
if (ret)
- return ret;
+ goto clear_roce;
+
+ hnae3_set_client_init_flag(client, ae_dev, 1);
}
}
}
return 0;
+
+clear_nic:
+ hdev->nic_client = NULL;
+ vport->nic.client = NULL;
+ return ret;
+clear_roce:
+ hdev->roce_client = NULL;
+ vport->roce.client = NULL;
+ return ret;
}
static void hclge_uninit_client_instance(struct hnae3_client *client,
@@ -5537,7 +5564,7 @@ static void hclge_uninit_client_instance(struct hnae3_client *client,
}
if (client->type == HNAE3_CLIENT_ROCE)
return;
- if (client->ops->uninit_instance) {
+ if (hdev->nic_client && client->ops->uninit_instance) {
hclge_uninit_instance_hw(hdev);
client->ops->uninit_instance(&vport->nic, 0);
hdev->nic_client = NULL;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 1528fb3..260b1e7 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -249,6 +249,7 @@ struct hclge_tm_info {
struct hclge_tc_info tc_info[HNAE3_MAX_TC];
enum hclge_fc_mode fc_mode;
u8 hw_pfc_map; /* Allow for packet drop or not on this TC */
+ u8 pfc_en; /* PFC enabled or not for user priority */
};
struct hclge_comm_stats_str {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
index 398971a..03491e8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
@@ -54,7 +54,7 @@ static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum,
struct hclge_desc desc;
int ret;
- if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ if (test_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state))
return 0;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false);
@@ -92,7 +92,7 @@ static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum)
struct hclge_desc desc;
int ret;
- if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ if (test_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state))
return 0;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
index 11e9259..3180ae4 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
@@ -298,7 +298,7 @@ static int hclge_tm_qs_to_pri_map_cfg(struct hclge_dev *hdev,
}
static int hclge_tm_q_to_qs_map_cfg(struct hclge_dev *hdev,
- u8 q_id, u16 qs_id)
+ u16 q_id, u16 qs_id)
{
struct hclge_nq_to_qs_link_cmd *map;
struct hclge_desc desc;
@@ -1162,7 +1162,7 @@ static int hclge_pfc_setup_hw(struct hclge_dev *hdev)
HCLGE_RX_MAC_PAUSE_EN_MSK;
return hclge_pfc_pause_en_cfg(hdev, enable_bitmap,
- hdev->tm_info.hw_pfc_map);
+ hdev->tm_info.pfc_en);
}
/* Each Tc has a 1024 queue sets to backpress, it divides to
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
index fb471fe..d8c0cc8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
@@ -132,8 +132,8 @@ static int hclgevf_init_cmd_queue(struct hclgevf_dev *hdev,
reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_DEPTH_REG, reg_val);
- hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_TAIL_REG, 0);
hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_HEAD_REG, 0);
+ hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_TAIL_REG, 0);
break;
case HCLGEVF_TYPE_CRQ:
reg_val = (u32)ring->desc_dma_addr;
@@ -145,8 +145,8 @@ static int hclgevf_init_cmd_queue(struct hclgevf_dev *hdev,
reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_DEPTH_REG, reg_val);
- hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_TAIL_REG, 0);
hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_HEAD_REG, 0);
+ hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_TAIL_REG, 0);
break;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index 5570fb5..67db197 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -26,7 +26,12 @@ MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl);
static inline struct hclgevf_dev *hclgevf_ae_get_hdev(
struct hnae3_handle *handle)
{
- return container_of(handle, struct hclgevf_dev, nic);
+ if (!handle->client)
+ return container_of(handle, struct hclgevf_dev, nic);
+ else if (handle->client->type == HNAE3_CLIENT_ROCE)
+ return container_of(handle, struct hclgevf_dev, roce);
+ else
+ return container_of(handle, struct hclgevf_dev, nic);
}
static int hclgevf_tqps_update_stats(struct hnae3_handle *handle)
@@ -1629,17 +1634,22 @@ static int hclgevf_init_client_instance(struct hnae3_client *client,
ret = client->ops->init_instance(&hdev->nic);
if (ret)
- return ret;
+ goto clear_nic;
+
+ hnae3_set_client_init_flag(client, ae_dev, 1);
if (hdev->roce_client && hnae3_dev_roce_supported(hdev)) {
struct hnae3_client *rc = hdev->roce_client;
ret = hclgevf_init_roce_base_info(hdev);
if (ret)
- return ret;
+ goto clear_roce;
ret = rc->ops->init_instance(&hdev->roce);
if (ret)
- return ret;
+ goto clear_roce;
+
+ hnae3_set_client_init_flag(hdev->roce_client, ae_dev,
+ 1);
}
break;
case HNAE3_CLIENT_UNIC:
@@ -1648,7 +1658,9 @@ static int hclgevf_init_client_instance(struct hnae3_client *client,
ret = client->ops->init_instance(&hdev->nic);
if (ret)
- return ret;
+ goto clear_nic;
+
+ hnae3_set_client_init_flag(client, ae_dev, 1);
break;
case HNAE3_CLIENT_ROCE:
if (hnae3_dev_roce_supported(hdev)) {
@@ -1659,15 +1671,26 @@ static int hclgevf_init_client_instance(struct hnae3_client *client,
if (hdev->roce_client && hdev->nic_client) {
ret = hclgevf_init_roce_base_info(hdev);
if (ret)
- return ret;
+ goto clear_roce;
ret = client->ops->init_instance(&hdev->roce);
if (ret)
- return ret;
+ goto clear_roce;
}
+
+ hnae3_set_client_init_flag(client, ae_dev, 1);
}
return 0;
+
+clear_nic:
+ hdev->nic_client = NULL;
+ hdev->nic.client = NULL;
+ return ret;
+clear_roce:
+ hdev->roce_client = NULL;
+ hdev->roce.client = NULL;
+ return ret;
}
static void hclgevf_uninit_client_instance(struct hnae3_client *client,
@@ -1676,13 +1699,19 @@ static void hclgevf_uninit_client_instance(struct hnae3_client *client,
struct hclgevf_dev *hdev = ae_dev->priv;
/* un-init roce, if it exists */
- if (hdev->roce_client)
+ if (hdev->roce_client) {
hdev->roce_client->ops->uninit_instance(&hdev->roce, 0);
+ hdev->roce_client = NULL;
+ hdev->roce.client = NULL;
+ }
/* un-init nic/unic, if this was not called by roce client */
- if ((client->ops->uninit_instance) &&
- (client->type != HNAE3_CLIENT_ROCE))
+ if (client->ops->uninit_instance && hdev->nic_client &&
+ client->type != HNAE3_CLIENT_ROCE) {
client->ops->uninit_instance(&hdev->nic, 0);
+ hdev->nic_client = NULL;
+ hdev->nic.client = NULL;
+ }
}
static int hclgevf_pci_init(struct hclgevf_dev *hdev)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index 4a8f829..2352046 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -600,9 +600,6 @@ static int add_mac_addr(struct net_device *netdev, const u8 *addr)
u16 vid = 0;
int err;
- if (!is_valid_ether_addr(addr))
- return -EADDRNOTAVAIL;
-
netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
@@ -726,6 +723,7 @@ static void set_rx_mode(struct work_struct *work)
{
struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
+ struct netdev_hw_addr *ha;
netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
@@ -733,6 +731,9 @@ static void set_rx_mode(struct work_struct *work)
__dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
__dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
+
+ netdev_for_each_mc_addr(ha, nic_dev->netdev)
+ add_mac_addr(nic_dev->netdev, ha->addr);
}
static void hinic_set_rx_mode(struct net_device *netdev)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c
index 4c0f7ed..06b24a9 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c
@@ -207,9 +207,9 @@ static int rx_alloc_pkts(struct hinic_rxq *rxq)
wmb(); /* write all the wqes before update PI */
hinic_rq_update(rxq->rq, prod_idx);
+ tasklet_schedule(&rxq->rx_task);
}
- tasklet_schedule(&rxq->rx_task);
return i;
}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index 506f783..e8ee69d 100644
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -2027,7 +2027,7 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
dev_consume_skb_any(skb);
}
-static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ehea_port *port = netdev_priv(dev);
struct ehea_swqe *swqe;
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 129f4e9..a96f501 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1409,7 +1409,7 @@ static inline u16 emac_tx_csum(struct emac_instance *dev,
return 0;
}
-static inline int emac_xmit_finish(struct emac_instance *dev, int len)
+static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len)
{
struct emac_regs __iomem *p = dev->emacp;
struct net_device *ndev = dev->ndev;
@@ -1436,7 +1436,7 @@ static inline int emac_xmit_finish(struct emac_instance *dev, int len)
}
/* Tx lock BH */
-static int emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
unsigned int len = skb->len;
@@ -1494,7 +1494,8 @@ static inline int emac_xmit_split(struct emac_instance *dev, int slot,
}
/* Tx lock BH disabled (SG version for TAH equipped EMACs) */
-static int emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t
+emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
int nr_frags = skb_shinfo(skb)->nr_frags;
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 8fa1473..8a19164 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -1420,7 +1420,7 @@ static int ibmvnic_xmit_workarounds(struct sk_buff *skb,
return 0;
}
-static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
int queue_num = skb_get_queue_mapping(skb);
@@ -1444,7 +1444,7 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
u64 *handle_array;
int index = 0;
u8 proto = 0;
- int ret = 0;
+ netdev_tx_t ret = NETDEV_TX_OK;
if (adapter->resetting) {
if (!netif_subqueue_stopped(netdev, skb))
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index 27d5f271..78b44d7 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -1345,8 +1345,8 @@ static inline int e100_load_ucode_wait(struct nic *nic)
fw = e100_request_firmware(nic);
/* If it's NULL, then no ucode is required */
- if (!fw || IS_ERR(fw))
- return PTR_ERR(fw);
+ if (IS_ERR_OR_NULL(fw))
+ return PTR_ERR_OR_ZERO(fw);
if ((err = e100_exec_cb(nic, (void *)fw, e100_setup_ucode)))
netif_err(nic, probe, nic->netdev,
diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
index 2569a16..903b0a9 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
@@ -607,6 +607,7 @@ static int e1000_set_ringparam(struct net_device *netdev,
for (i = 0; i < adapter->num_rx_queues; i++)
rxdr[i].count = rxdr->count;
+ err = 0;
if (netif_running(adapter->netdev)) {
/* Try to get new resources before deleting old */
err = e1000_setup_all_rx_resources(adapter);
@@ -627,14 +628,13 @@ static int e1000_set_ringparam(struct net_device *netdev,
adapter->rx_ring = rxdr;
adapter->tx_ring = txdr;
err = e1000_up(adapter);
- if (err)
- goto err_setup;
}
kfree(tx_old);
kfree(rx_old);
clear_bit(__E1000_RESETTING, &adapter->flags);
- return 0;
+ return err;
+
err_setup_tx:
e1000_free_all_rx_resources(adapter);
err_setup_rx:
@@ -646,7 +646,6 @@ static int e1000_set_ringparam(struct net_device *netdev,
err_alloc_tx:
if (netif_running(adapter->netdev))
e1000_up(adapter);
-err_setup:
clear_bit(__E1000_RESETTING, &adapter->flags);
return err;
}
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
index e707d71..6180326 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
@@ -302,6 +302,28 @@ void fm10k_iov_suspend(struct pci_dev *pdev)
}
}
+static void fm10k_mask_aer_comp_abort(struct pci_dev *pdev)
+{
+ u32 err_mask;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+
+ /* Mask the completion abort bit in the ERR_UNCOR_MASK register,
+ * preventing the device from reporting these errors to the upstream
+ * PCIe root device. This avoids bringing down platforms which upgrade
+ * non-fatal completer aborts into machine check exceptions. Completer
+ * aborts can occur whenever a VF reads a queue it doesn't own.
+ */
+ pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_MASK, &err_mask);
+ err_mask |= PCI_ERR_UNC_COMP_ABORT;
+ pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_MASK, err_mask);
+
+ mmiowb();
+}
+
int fm10k_iov_resume(struct pci_dev *pdev)
{
struct fm10k_intfc *interface = pci_get_drvdata(pdev);
@@ -317,6 +339,12 @@ int fm10k_iov_resume(struct pci_dev *pdev)
if (!iov_data)
return -ENOMEM;
+ /* Lower severity of completer abort error reporting as
+ * the VFs can trigger this any time they read a queue
+ * that they don't own.
+ */
+ fm10k_mask_aer_comp_abort(pdev);
+
/* allocate hardware resources for the VFs */
hw->iov.ops.assign_resources(hw, num_vfs, num_vfs);
@@ -460,20 +488,6 @@ void fm10k_iov_disable(struct pci_dev *pdev)
fm10k_iov_free_data(pdev);
}
-static void fm10k_disable_aer_comp_abort(struct pci_dev *pdev)
-{
- u32 err_sev;
- int pos;
-
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
- return;
-
- pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &err_sev);
- err_sev &= ~PCI_ERR_UNC_COMP_ABORT;
- pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, err_sev);
-}
-
int fm10k_iov_configure(struct pci_dev *pdev, int num_vfs)
{
int current_vfs = pci_num_vf(pdev);
@@ -495,12 +509,6 @@ int fm10k_iov_configure(struct pci_dev *pdev, int num_vfs)
/* allocate VFs if not already allocated */
if (num_vfs && num_vfs != current_vfs) {
- /* Disable completer abort error reporting as
- * the VFs can trigger this any time they read a queue
- * that they don't own.
- */
- fm10k_disable_aer_comp_abort(pdev);
-
err = pci_enable_sriov(pdev, num_vfs);
if (err) {
dev_err(&pdev->dev,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 5ff6caa83..a6b0f60 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -1136,6 +1136,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
i40e_status status;
u8 aq_failures;
int err = 0;
+ u32 is_an;
/* Changing the port's flow control is not supported if this isn't the
* port's controlling PF
@@ -1148,15 +1149,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
- if (pause->autoneg != ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
- AUTONEG_ENABLE : AUTONEG_DISABLE)) {
+ is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED;
+ if (pause->autoneg != is_an) {
netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
return -EOPNOTSUPP;
}
/* If we have link and don't have autoneg */
- if (!test_bit(__I40E_DOWN, pf->state) &&
- !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) {
+ if (!test_bit(__I40E_DOWN, pf->state) && !is_an) {
/* Send message that it might not necessarily work*/
netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
}
@@ -1207,7 +1207,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
err = -EAGAIN;
}
- if (!test_bit(__I40E_DOWN, pf->state)) {
+ if (!test_bit(__I40E_DOWN, pf->state) && is_an) {
/* Give it a little more time to try to come back */
msleep(75);
if (!test_bit(__I40E_DOWN, pf->state))
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 055562c9..23b31b2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -3441,14 +3441,14 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
q_vector->rx.target_itr =
ITR_TO_REG(vsi->rx_rings[i]->itr_setting);
wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1),
- q_vector->rx.target_itr);
+ q_vector->rx.target_itr >> 1);
q_vector->rx.current_itr = q_vector->rx.target_itr;
q_vector->tx.next_update = jiffies + 1;
q_vector->tx.target_itr =
ITR_TO_REG(vsi->tx_rings[i]->itr_setting);
wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1),
- q_vector->tx.target_itr);
+ q_vector->tx.target_itr >> 1);
q_vector->tx.current_itr = q_vector->tx.target_itr;
wr32(hw, I40E_PFINT_RATEN(vector - 1),
@@ -3553,11 +3553,11 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
/* set the ITR configuration */
q_vector->rx.next_update = jiffies + 1;
q_vector->rx.target_itr = ITR_TO_REG(vsi->rx_rings[0]->itr_setting);
- wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.target_itr);
+ wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.target_itr >> 1);
q_vector->rx.current_itr = q_vector->rx.target_itr;
q_vector->tx.next_update = jiffies + 1;
q_vector->tx.target_itr = ITR_TO_REG(vsi->tx_rings[0]->itr_setting);
- wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.target_itr);
+ wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.target_itr >> 1);
q_vector->tx.current_itr = q_vector->tx.target_itr;
i40e_enable_misc_int_causes(pf);
@@ -6587,6 +6587,24 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
struct i40e_hw *hw = &pf->hw;
i40e_status err;
u64 mask;
+ u8 speed;
+
+ /* Card might've been put in an unstable state by other drivers
+ * and applications, which causes incorrect speed values being
+ * set on startup. In order to clear speed registers, we call
+ * get_phy_capabilities twice, once to get initial state of
+ * available speeds, and once to get current PHY config.
+ */
+ err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities,
+ NULL);
+ if (err) {
+ dev_err(&pf->pdev->dev,
+ "failed to get phy cap., ret = %s last_status = %s\n",
+ i40e_stat_str(hw, err),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ return err;
+ }
+ speed = abilities.link_speed;
/* Get the current phy config */
err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
@@ -6600,9 +6618,9 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
}
/* If link needs to go up, but was not forced to go down,
- * no need for a flap
+ * and its speed values are OK, no need for a flap
*/
- if (is_up && abilities.phy_type != 0)
+ if (is_up && abilities.phy_type != 0 && abilities.link_speed != 0)
return I40E_SUCCESS;
/* To force link we need to set bits for all supported PHY types,
@@ -6614,7 +6632,10 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
config.phy_type_ext = is_up ? (u8)((mask >> 32) & 0xff) : 0;
/* Copy the old settings, except of phy_type */
config.abilities = abilities.abilities;
- config.link_speed = abilities.link_speed;
+ if (abilities.link_speed != 0)
+ config.link_speed = abilities.link_speed;
+ else
+ config.link_speed = speed;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
@@ -10714,7 +10735,7 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf)
/* associate no queues to the misc vector */
wr32(hw, I40E_PFINT_LNKLST0, I40E_QUEUE_END_OF_LIST);
- wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), I40E_ITR_8K);
+ wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), I40E_ITR_8K >> 1);
i40e_flush(hw);
@@ -14187,6 +14208,7 @@ static void i40e_remove(struct pci_dev *pdev)
mutex_destroy(&hw->aq.asq_mutex);
/* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
+ rtnl_lock();
i40e_clear_interrupt_scheme(pf);
for (i = 0; i < pf->num_alloc_vsi; i++) {
if (pf->vsi[i]) {
@@ -14195,6 +14217,7 @@ static void i40e_remove(struct pci_dev *pdev)
pf->vsi[i] = NULL;
}
}
+ rtnl_unlock();
for (i = 0; i < I40E_MAX_VEB; i++) {
kfree(pf->veb[i]);
@@ -14406,7 +14429,13 @@ static void i40e_shutdown(struct pci_dev *pdev)
wr32(hw, I40E_PFPM_WUFC,
(pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0));
+ /* Since we're going to destroy queues during the
+ * i40e_clear_interrupt_scheme() we should hold the RTNL lock for this
+ * whole section
+ */
+ rtnl_lock();
i40e_clear_interrupt_scheme(pf);
+ rtnl_unlock();
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, pf->wol_en);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 35f2866..1199f05 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -694,7 +694,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
if (!IS_ERR_OR_NULL(pf->ptp_clock))
return 0;
- strncpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name));
+ strncpy(pf->ptp_caps.name, i40e_driver_name,
+ sizeof(pf->ptp_caps.name) - 1);
pf->ptp_caps.owner = THIS_MODULE;
pf->ptp_caps.max_adj = 999999999;
pf->ptp_caps.n_ext_ts = 0;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index d86f3fa..6a677fd 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -2571,6 +2571,16 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
ret = I40E_ERR_INVALID_MAC_ADDR;
goto error_param;
}
+
+ if (vf->pf_set_mac &&
+ ether_addr_equal(al->list[i].addr,
+ vf->default_lan_addr.addr)) {
+ dev_err(&pf->pdev->dev,
+ "MAC addr %pM has been set by PF, cannot delete it for VF %d, reset VF to change MAC addr\n",
+ vf->default_lan_addr.addr, vf->vf_id);
+ ret = I40E_ERR_PARAM;
+ goto error_param;
+ }
}
vsi = pf->vsi[vf->lan_vsi_idx];
@@ -4201,7 +4211,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
vf->link_forced = true;
vf->link_up = true;
pfe.event_data.link_event.link_status = true;
- pfe.event_data.link_event.link_speed = I40E_LINK_SPEED_40GB;
+ pfe.event_data.link_event.link_speed = VIRTCHNL_LINK_SPEED_40GB;
break;
case IFLA_VF_LINK_STATE_DISABLE:
vf->link_forced = true;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index fef6d89..f50c19b 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -3097,18 +3097,19 @@ static int i40evf_set_features(struct net_device *netdev,
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- /* Don't allow changing VLAN_RX flag when VLAN is set for VF
- * and return an error in this case
+ /* Don't allow changing VLAN_RX flag when adapter is not capable
+ * of VLAN offload
*/
- if (VLAN_ALLOWED(adapter)) {
+ if (!VLAN_ALLOWED(adapter)) {
+ if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX)
+ return -EINVAL;
+ } else if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX) {
if (features & NETIF_F_HW_VLAN_CTAG_RX)
adapter->aq_required |=
I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING;
else
adapter->aq_required |=
I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING;
- } else if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX) {
- return -EINVAL;
}
return 0;
@@ -3332,6 +3333,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN)
netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
/* Do not turn on offloads when they are requested to be turned off.
* TSO needs minimum 576 bytes to work correctly.
*/
@@ -3881,6 +3884,8 @@ static void i40evf_remove(struct pci_dev *pdev)
if (adapter->watchdog_timer.function)
del_timer_sync(&adapter->watchdog_timer);
+ cancel_work_sync(&adapter->adminq_task);
+
i40evf_free_rss(adapter);
if (hw->aq.asq.count)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 565677d..94dabc9 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -154,6 +154,32 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
}
/**
+ * i40evf_validate_num_queues
+ * @adapter: adapter structure
+ *
+ * Validate that the number of queues the PF has sent in
+ * VIRTCHNL_OP_GET_VF_RESOURCES is not larger than the VF can handle.
+ **/
+static void i40evf_validate_num_queues(struct i40evf_adapter *adapter)
+{
+ if (adapter->vf_res->num_queue_pairs > I40EVF_MAX_REQ_QUEUES) {
+ struct virtchnl_vsi_resource *vsi_res;
+ int i;
+
+ dev_info(&adapter->pdev->dev, "Received %d queues, but can only have a max of %d\n",
+ adapter->vf_res->num_queue_pairs,
+ I40EVF_MAX_REQ_QUEUES);
+ dev_info(&adapter->pdev->dev, "Fixing by reducing queues to %d\n",
+ I40EVF_MAX_REQ_QUEUES);
+ adapter->vf_res->num_queue_pairs = I40EVF_MAX_REQ_QUEUES;
+ for (i = 0; i < adapter->vf_res->num_vsis; i++) {
+ vsi_res = &adapter->vf_res->vsi_res[i];
+ vsi_res->num_queue_pairs = I40EVF_MAX_REQ_QUEUES;
+ }
+ }
+}
+
+/**
* i40evf_get_vf_config
* @adapter: private adapter structure
*
@@ -195,6 +221,11 @@ int i40evf_get_vf_config(struct i40evf_adapter *adapter)
err = (i40e_status)le32_to_cpu(event.desc.cookie_low);
memcpy(adapter->vf_res, event.msg_buf, min(event.msg_len, len));
+ /* some PFs send more queues than we should have so validate that
+ * we aren't getting too many queues
+ */
+ if (!err)
+ i40evf_validate_num_queues(adapter);
i40e_vf_parse_hw_config(hw, adapter->vf_res);
out_alloc:
kfree(event.msg_buf);
@@ -1329,6 +1360,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
I40E_MAX_VF_VSI *
sizeof(struct virtchnl_vsi_resource);
memcpy(adapter->vf_res, msg, min(msglen, len));
+ i40evf_validate_num_queues(adapter);
i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res);
/* restore current mac address */
ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index a0614f4..328d293 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1056,10 +1056,10 @@ struct ice_aqc_nvm {
#define ICE_AQC_NVM_LAST_CMD BIT(0)
#define ICE_AQC_NVM_PCIR_REQ BIT(0) /* Used by NVM Update reply */
#define ICE_AQC_NVM_PRESERVATION_S 1
-#define ICE_AQC_NVM_PRESERVATION_M (3 << CSR_AQ_NVM_PRESERVATION_S)
-#define ICE_AQC_NVM_NO_PRESERVATION (0 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVATION_M (3 << ICE_AQC_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_NO_PRESERVATION (0 << ICE_AQC_NVM_PRESERVATION_S)
#define ICE_AQC_NVM_PRESERVE_ALL BIT(1)
-#define ICE_AQC_NVM_PRESERVE_SELECTED (3 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVE_SELECTED (3 << ICE_AQC_NVM_PRESERVATION_S)
#define ICE_AQC_NVM_FLASH_ONLY BIT(7)
__le16 module_typeid;
__le16 length;
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 661beea..f8d0026 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -904,7 +904,22 @@ enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading)
* @timeout: the maximum time in ms that the driver may hold the resource
* @cd: pointer to command details structure or NULL
*
- * requests common resource using the admin queue commands (0x0008)
+ * Requests common resource using the admin queue commands (0x0008).
+ * When attempting to acquire the Global Config Lock, the driver can
+ * learn of three states:
+ * 1) ICE_SUCCESS - acquired lock, and can perform download package
+ * 2) ICE_ERR_AQ_ERROR - did not get lock, driver should fail to load
+ * 3) ICE_ERR_AQ_NO_WORK - did not get lock, but another driver has
+ * successfully downloaded the package; the driver does
+ * not have to download the package and can continue
+ * loading
+ *
+ * Note that if the caller is in an acquire lock, perform action, release lock
+ * phase of operation, it is possible that the FW may detect a timeout and issue
+ * a CORER. In this case, the driver will receive a CORER interrupt and will
+ * have to determine its cause. The calling thread that is handling this flow
+ * will likely get an error propagated back to it indicating the Download
+ * Package, Update Package or the Release Resource AQ commands timed out.
*/
static enum ice_status
ice_aq_req_res(struct ice_hw *hw, enum ice_aq_res_ids res,
@@ -922,13 +937,43 @@ ice_aq_req_res(struct ice_hw *hw, enum ice_aq_res_ids res,
cmd_resp->res_id = cpu_to_le16(res);
cmd_resp->access_type = cpu_to_le16(access);
cmd_resp->res_number = cpu_to_le32(sdp_number);
+ cmd_resp->timeout = cpu_to_le32(*timeout);
+ *timeout = 0;
status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+
/* The completion specifies the maximum time in ms that the driver
* may hold the resource in the Timeout field.
- * If the resource is held by someone else, the command completes with
- * busy return value and the timeout field indicates the maximum time
- * the current owner of the resource has to free it.
+ */
+
+ /* Global config lock response utilizes an additional status field.
+ *
+ * If the Global config lock resource is held by some other driver, the
+ * command completes with ICE_AQ_RES_GLBL_IN_PROG in the status field
+ * and the timeout field indicates the maximum time the current owner
+ * of the resource has to free it.
+ */
+ if (res == ICE_GLOBAL_CFG_LOCK_RES_ID) {
+ if (le16_to_cpu(cmd_resp->status) == ICE_AQ_RES_GLBL_SUCCESS) {
+ *timeout = le32_to_cpu(cmd_resp->timeout);
+ return 0;
+ } else if (le16_to_cpu(cmd_resp->status) ==
+ ICE_AQ_RES_GLBL_IN_PROG) {
+ *timeout = le32_to_cpu(cmd_resp->timeout);
+ return ICE_ERR_AQ_ERROR;
+ } else if (le16_to_cpu(cmd_resp->status) ==
+ ICE_AQ_RES_GLBL_DONE) {
+ return ICE_ERR_AQ_NO_WORK;
+ }
+
+ /* invalid FW response, force a timeout immediately */
+ *timeout = 0;
+ return ICE_ERR_AQ_ERROR;
+ }
+
+ /* If the resource is held by some other driver, the command completes
+ * with a busy return value and the timeout field indicates the maximum
+ * time the current owner of the resource has to free it.
*/
if (!status || hw->adminq.sq_last_status == ICE_AQ_RC_EBUSY)
*timeout = le32_to_cpu(cmd_resp->timeout);
@@ -967,30 +1012,28 @@ ice_aq_release_res(struct ice_hw *hw, enum ice_aq_res_ids res, u8 sdp_number,
* @hw: pointer to the HW structure
* @res: resource id
* @access: access type (read or write)
+ * @timeout: timeout in milliseconds
*
* This function will attempt to acquire the ownership of a resource.
*/
enum ice_status
ice_acquire_res(struct ice_hw *hw, enum ice_aq_res_ids res,
- enum ice_aq_res_access_type access)
+ enum ice_aq_res_access_type access, u32 timeout)
{
#define ICE_RES_POLLING_DELAY_MS 10
u32 delay = ICE_RES_POLLING_DELAY_MS;
+ u32 time_left = timeout;
enum ice_status status;
- u32 time_left = 0;
- u32 timeout;
status = ice_aq_req_res(hw, res, access, 0, &time_left, NULL);
- /* An admin queue return code of ICE_AQ_RC_EEXIST means that another
- * driver has previously acquired the resource and performed any
- * necessary updates; in this case the caller does not obtain the
- * resource and has no further work to do.
+ /* A return code of ICE_ERR_AQ_NO_WORK means that another driver has
+ * previously acquired the resource and performed any necessary updates;
+ * in this case the caller does not obtain the resource and has no
+ * further work to do.
*/
- if (hw->adminq.sq_last_status == ICE_AQ_RC_EEXIST) {
- status = ICE_ERR_AQ_NO_WORK;
+ if (status == ICE_ERR_AQ_NO_WORK)
goto ice_acquire_res_exit;
- }
if (status)
ice_debug(hw, ICE_DBG_RES,
@@ -1003,11 +1046,9 @@ ice_acquire_res(struct ice_hw *hw, enum ice_aq_res_ids res,
timeout = (timeout > delay) ? timeout - delay : 0;
status = ice_aq_req_res(hw, res, access, 0, &time_left, NULL);
- if (hw->adminq.sq_last_status == ICE_AQ_RC_EEXIST) {
+ if (status == ICE_ERR_AQ_NO_WORK)
/* lock free, but no work to do */
- status = ICE_ERR_AQ_NO_WORK;
break;
- }
if (!status)
/* lock acquired */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 9a55191..6455b69 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -23,7 +23,7 @@ enum ice_status
ice_get_link_status(struct ice_port_info *pi, bool *link_up);
enum ice_status
ice_acquire_res(struct ice_hw *hw, enum ice_aq_res_ids res,
- enum ice_aq_res_access_type access);
+ enum ice_aq_res_access_type access, u32 timeout);
void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res);
enum ice_status ice_init_nvm(struct ice_hw *hw);
enum ice_status
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.c b/drivers/net/ethernet/intel/ice/ice_controlq.c
index e783976..921cc0c 100644
--- a/drivers/net/ethernet/intel/ice/ice_controlq.c
+++ b/drivers/net/ethernet/intel/ice/ice_controlq.c
@@ -814,6 +814,9 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
u16 retval = 0;
u32 val = 0;
+ /* if reset is in progress return a soft error */
+ if (hw->reset_ongoing)
+ return ICE_ERR_RESET_ONGOING;
mutex_lock(&cq->sq_lock);
cq->sq_last_status = ICE_AQ_RC_OK;
@@ -908,7 +911,7 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
if (ice_sq_done(hw, cq))
break;
- mdelay(1);
+ udelay(ICE_CTL_Q_SQ_CMD_USEC);
total_delay++;
} while (total_delay < cq->sq_cmd_timeout);
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.h b/drivers/net/ethernet/intel/ice/ice_controlq.h
index ea02b89..0f2cdb0 100644
--- a/drivers/net/ethernet/intel/ice/ice_controlq.h
+++ b/drivers/net/ethernet/intel/ice/ice_controlq.h
@@ -30,8 +30,9 @@ enum ice_ctl_q {
ICE_CTL_Q_ADMIN,
};
-/* Control Queue default settings */
-#define ICE_CTL_Q_SQ_CMD_TIMEOUT 250 /* msecs */
+/* Control Queue timeout settings - max delay 250ms */
+#define ICE_CTL_Q_SQ_CMD_TIMEOUT 2500 /* Count 2500 times */
+#define ICE_CTL_Q_SQ_CMD_USEC 100 /* Check every 100usec */
struct ice_ctl_q_ring {
void *dma_head; /* Virtual address to dma head */
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 875f97a..00c833c 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -7,7 +7,7 @@
#include "ice.h"
-#define DRV_VERSION "ice-0.7.0-k"
+#define DRV_VERSION "0.7.1-k"
#define DRV_SUMMARY "Intel(R) Ethernet Connection E800 Series Linux Driver"
const char ice_drv_ver[] = DRV_VERSION;
static const char ice_driver_string[] = DRV_SUMMARY;
@@ -535,10 +535,13 @@ static void ice_reset_subtask(struct ice_pf *pf)
ice_prepare_for_reset(pf);
/* make sure we are ready to rebuild */
- if (ice_check_reset(&pf->hw))
+ if (ice_check_reset(&pf->hw)) {
set_bit(__ICE_RESET_FAILED, pf->state);
- else
+ } else {
+ /* done with reset. start rebuild */
+ pf->hw.reset_ongoing = false;
ice_rebuild(pf);
+ }
clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
goto unlock;
}
@@ -1757,7 +1760,8 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
* We also make note of which reset happened so that peer
* devices/drivers can be informed.
*/
- if (!test_bit(__ICE_RESET_RECOVERY_PENDING, pf->state)) {
+ if (!test_and_set_bit(__ICE_RESET_RECOVERY_PENDING,
+ pf->state)) {
if (reset == ICE_RESET_CORER)
set_bit(__ICE_CORER_RECV, pf->state);
else if (reset == ICE_RESET_GLOBR)
@@ -1765,7 +1769,20 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
else
set_bit(__ICE_EMPR_RECV, pf->state);
- set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ /* There are couple of different bits at play here.
+ * hw->reset_ongoing indicates whether the hardware is
+ * in reset. This is set to true when a reset interrupt
+ * is received and set back to false after the driver
+ * has determined that the hardware is out of reset.
+ *
+ * __ICE_RESET_RECOVERY_PENDING in pf->state indicates
+ * that a post reset rebuild is required before the
+ * driver is operational again. This is set above.
+ *
+ * As this is the start of the reset/rebuild cycle, set
+ * both to indicate that.
+ */
+ hw->reset_ongoing = true;
}
}
@@ -4188,7 +4205,14 @@ static int ice_vsi_stop_tx_rings(struct ice_vsi *vsi)
}
status = ice_dis_vsi_txq(vsi->port_info, vsi->num_txq, q_ids, q_teids,
NULL);
- if (status) {
+ /* if the disable queue command was exercised during an active reset
+ * flow, ICE_ERR_RESET_ONGOING is returned. This is not an error as
+ * the reset operation disables queues at the hardware level anyway.
+ */
+ if (status == ICE_ERR_RESET_ONGOING) {
+ dev_dbg(&pf->pdev->dev,
+ "Reset in progress. LAN Tx queues already disabled\n");
+ } else if (status) {
dev_err(&pf->pdev->dev,
"Failed to disable LAN Tx queues, error: %d\n",
status);
diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c
index 295a8cd..3274c54 100644
--- a/drivers/net/ethernet/intel/ice/ice_nvm.c
+++ b/drivers/net/ethernet/intel/ice/ice_nvm.c
@@ -137,7 +137,7 @@ ice_acquire_nvm(struct ice_hw *hw, enum ice_aq_res_access_type access)
if (hw->nvm.blank_nvm_mode)
return 0;
- return ice_acquire_res(hw, ICE_NVM_RES_ID, access);
+ return ice_acquire_res(hw, ICE_NVM_RES_ID, access, ICE_NVM_TIMEOUT);
}
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_status.h b/drivers/net/ethernet/intel/ice/ice_status.h
index 9a95c4f..d2dae91 100644
--- a/drivers/net/ethernet/intel/ice/ice_status.h
+++ b/drivers/net/ethernet/intel/ice/ice_status.h
@@ -20,6 +20,7 @@ enum ice_status {
ICE_ERR_ALREADY_EXISTS = -14,
ICE_ERR_DOES_NOT_EXIST = -15,
ICE_ERR_MAX_LIMIT = -17,
+ ICE_ERR_RESET_ONGOING = -18,
ICE_ERR_BUF_TOO_SHORT = -52,
ICE_ERR_NVM_BLANK_MODE = -53,
ICE_ERR_AQ_ERROR = -100,
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c
index 6b7ec2a..1bfc59d 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.c
+++ b/drivers/net/ethernet/intel/ice/ice_switch.c
@@ -468,6 +468,7 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,
void *daddr = NULL;
u32 act = 0;
__be16 *off;
+ u8 q_rgn;
if (opc == ice_aqc_opc_remove_sw_rules) {
s_rule->pdata.lkup_tx_rx.act = 0;
@@ -503,13 +504,18 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,
act |= (f_info->fwd_id.q_id << ICE_SINGLE_ACT_Q_INDEX_S) &
ICE_SINGLE_ACT_Q_INDEX_M;
break;
- case ICE_FWD_TO_QGRP:
- act |= ICE_SINGLE_ACT_TO_Q;
- act |= (f_info->qgrp_size << ICE_SINGLE_ACT_Q_REGION_S) &
- ICE_SINGLE_ACT_Q_REGION_M;
- break;
case ICE_DROP_PACKET:
- act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP;
+ act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP |
+ ICE_SINGLE_ACT_VALID_BIT;
+ break;
+ case ICE_FWD_TO_QGRP:
+ q_rgn = f_info->qgrp_size > 0 ?
+ (u8)ilog2(f_info->qgrp_size) : 0;
+ act |= ICE_SINGLE_ACT_TO_Q;
+ act |= (f_info->fwd_id.q_id << ICE_SINGLE_ACT_Q_INDEX_S) &
+ ICE_SINGLE_ACT_Q_INDEX_M;
+ act |= (q_rgn << ICE_SINGLE_ACT_Q_REGION_S) &
+ ICE_SINGLE_ACT_Q_REGION_M;
break;
default:
return;
@@ -1017,6 +1023,9 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw,
u16 vsi_id = new_fltr->fwd_id.vsi_id;
enum ice_adminq_opc opcode;
+ if (!m_entry->vsi_list_info)
+ return ICE_ERR_CFG;
+
/* A rule already exists with the new VSI being added */
if (test_bit(vsi_id, m_entry->vsi_list_info->vsi_map))
return 0;
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index 0c95c8f..1d84fed 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -1106,7 +1106,8 @@ int ice_napi_poll(struct napi_struct *napi, int budget)
napi_complete_done(napi, work_done);
if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
- return 0;
+
+ return min(work_done, budget - 1);
}
/* helper function for building cmd/type/offset */
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index ba11b58..5ca9d68 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -34,10 +34,15 @@ static inline bool ice_is_tc_ena(u8 bitmap, u8 tc)
enum ice_aq_res_ids {
ICE_NVM_RES_ID = 1,
ICE_SPD_RES_ID,
- ICE_GLOBAL_CFG_LOCK_RES_ID,
- ICE_CHANGE_LOCK_RES_ID
+ ICE_CHANGE_LOCK_RES_ID,
+ ICE_GLOBAL_CFG_LOCK_RES_ID
};
+/* FW update timeout definitions are in milliseconds */
+#define ICE_NVM_TIMEOUT 180000
+#define ICE_CHANGE_LOCK_TIMEOUT 1000
+#define ICE_GLOBAL_CFG_LOCK_TIMEOUT 3000
+
enum ice_aq_res_access_type {
ICE_RES_READ = 1,
ICE_RES_WRITE
@@ -288,6 +293,7 @@ struct ice_hw {
u8 sw_entry_point_layer;
u8 evb_veb; /* true for VEB, false for VEPA */
+ u8 reset_ongoing; /* true if hw is in reset, false otherwise */
struct ice_bus_info bus;
struct ice_nvm_info nvm;
struct ice_hw_dev_caps dev_caps; /* device capabilities */
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ab76a5f..36db874 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -2064,7 +2064,8 @@ static void igb_check_swap_media(struct igb_adapter *adapter)
if ((hw->phy.media_type == e1000_media_type_copper) &&
(!(connsw & E1000_CONNSW_AUTOSENSE_EN))) {
swap_now = true;
- } else if (!(connsw & E1000_CONNSW_SERDESD)) {
+ } else if ((hw->phy.media_type != e1000_media_type_copper) &&
+ !(connsw & E1000_CONNSW_SERDESD)) {
/* copper signal takes time to appear */
if (adapter->copper_tries < 4) {
adapter->copper_tries++;
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 9f4d700..29ced6b 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -51,9 +51,15 @@
*
* The 40 bit 82580 SYSTIM overflows every
* 2^40 * 10^-9 / 60 = 18.3 minutes.
+ *
+ * SYSTIM is converted to real time using a timecounter. As
+ * timecounter_cyc2time() allows old timestamps, the timecounter
+ * needs to be updated at least once per half of the SYSTIM interval.
+ * Scheduling of delayed work is not very accurate, so we aim for 8
+ * minutes to be sure the actual interval is shorter than 9.16 minutes.
*/
-#define IGB_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 9)
+#define IGB_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 8)
#define IGB_PTP_TX_TIMEOUT (HZ * 15)
#define INCPERIOD_82576 BIT(E1000_TIMINCA_16NS_SHIFT)
#define INCVALUE_82576_MASK GENMASK(E1000_TIMINCA_16NS_SHIFT - 1, 0)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 8528076..de65ca1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -3582,12 +3582,18 @@ static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter)
else
mtqc |= IXGBE_MTQC_64VF;
} else {
- if (tcs > 4)
+ if (tcs > 4) {
mtqc = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_8TC_8TQ;
- else if (tcs > 1)
+ } else if (tcs > 1) {
mtqc = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_4TC_4TQ;
- else
- mtqc = IXGBE_MTQC_64Q_1PB;
+ } else {
+ u8 max_txq = adapter->num_tx_queues +
+ adapter->num_xdp_queues;
+ if (max_txq > 63)
+ mtqc = IXGBE_MTQC_RT_ENA | IXGBE_MTQC_4TC_4TQ;
+ else
+ mtqc = IXGBE_MTQC_64Q_1PB;
+ }
}
IXGBE_WRITE_REG(hw, IXGBE_MTQC, mtqc);
@@ -5181,6 +5187,7 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct hlist_node *node2;
struct ixgbe_fdir_filter *filter;
+ u64 action;
spin_lock(&adapter->fdir_perfect_lock);
@@ -5189,12 +5196,17 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter)
hlist_for_each_entry_safe(filter, node2,
&adapter->fdir_filter_list, fdir_node) {
+ action = filter->action;
+ if (action != IXGBE_FDIR_DROP_QUEUE && action != 0)
+ action =
+ (action >> ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF) - 1;
+
ixgbe_fdir_write_perfect_filter_82599(hw,
&filter->filter,
filter->sw_idx,
- (filter->action == IXGBE_FDIR_DROP_QUEUE) ?
+ (action == IXGBE_FDIR_DROP_QUEUE) ?
IXGBE_FDIR_DROP_QUEUE :
- adapter->rx_ring[filter->action]->reg_idx);
+ adapter->rx_ring[action]->reg_idx);
}
spin_unlock(&adapter->fdir_perfect_lock);
@@ -8539,7 +8551,8 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
adapter->ptp_clock) {
- if (!test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS,
+ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
+ !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS,
&adapter->state)) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= IXGBE_TX_FLAGS_TSTAMP;
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 2876231..4313bbb 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -2394,7 +2394,7 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
}
/* Main tx processing */
-static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t mvneta_tx(struct sk_buff *skb, struct net_device *dev)
{
struct mvneta_port *pp = netdev_priv(dev);
u16 txq_id = skb_get_queue_mapping(skb);
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
index 67b9e81..46911b6 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
@@ -253,7 +253,8 @@
#define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff)
#define MVPP2_ISR_DISABLE_INTERRUPT(mask) (((mask) << 16) & 0xffff0000)
#define MVPP2_ISR_RX_TX_CAUSE_REG(port) (0x5480 + 4 * (port))
-#define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff
+#define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(version) \
+ ((version) == MVPP21 ? 0xffff : 0xff)
#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000
#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET 16
#define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24)
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 9b608d2..04bee45 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -908,7 +908,7 @@ static void mvpp2_interrupts_unmask(void *arg)
u32 val;
val = MVPP2_CAUSE_MISC_SUM_MASK |
- MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
+ MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version);
if (port->has_tx_irqs)
val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
@@ -928,7 +928,7 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask)
if (mask)
val = 0;
else
- val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
+ val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(MVPP22);
for (i = 0; i < port->nqvecs; i++) {
struct mvpp2_queue_vector *v = port->qvecs + i;
@@ -2901,7 +2901,7 @@ static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev,
}
/* Main tx processing */
-static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
{
struct mvpp2_port *port = netdev_priv(dev);
struct mvpp2_tx_queue *txq, *aggr_txq;
@@ -3059,7 +3059,8 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
}
/* Process RX packets */
- cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
+ cause_rx = cause_rx_tx &
+ MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version);
cause_rx <<= qv->first_rxq;
cause_rx |= qv->pending_cause_rx;
while (cause_rx && budget > 0) {
@@ -3340,7 +3341,7 @@ static int mvpp2_open(struct net_device *dev)
valid = true;
}
- if (priv->hw_version == MVPP22 && port->link_irq && !port->phylink) {
+ if (priv->hw_version == MVPP22 && port->link_irq) {
err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
dev->name, port);
if (err) {
@@ -5131,6 +5132,8 @@ static int mvpp2_probe(struct platform_device *pdev)
if (has_acpi_companion(&pdev->dev)) {
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
&pdev->dev);
+ if (!acpi_id)
+ return -EINVAL;
priv->hw_version = (unsigned long)acpi_id->driver_data;
} else {
priv->hw_version =
diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c
index 3a97306..ff2fea0 100644
--- a/drivers/net/ethernet/marvell/pxa168_eth.c
+++ b/drivers/net/ethernet/marvell/pxa168_eth.c
@@ -1260,7 +1260,8 @@ static int pxa168_rx_poll(struct napi_struct *napi, int budget)
return work_done;
}
-static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index 94c59939..e639a36 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -1745,6 +1745,7 @@ static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
err = mlx4_en_get_flow(dev, cmd, cmd->fs.location);
break;
case ETHTOOL_GRXCLSRLALL:
+ cmd->data = MAX_NUM_OF_FS_RULES;
while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) {
err = mlx4_en_get_flow(dev, cmd, i);
if (!err)
@@ -1811,6 +1812,7 @@ static int mlx4_en_set_channels(struct net_device *dev,
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile new_prof;
struct mlx4_en_priv *tmp;
+ int total_tx_count;
int port_up = 0;
int xdp_count;
int err = 0;
@@ -1825,13 +1827,12 @@ static int mlx4_en_set_channels(struct net_device *dev,
mutex_lock(&mdev->state_lock);
xdp_count = priv->tx_ring_num[TX_XDP] ? channel->rx_count : 0;
- if (channel->tx_count * priv->prof->num_up + xdp_count >
- priv->mdev->profile.max_num_tx_rings_p_up * priv->prof->num_up) {
+ total_tx_count = channel->tx_count * priv->prof->num_up + xdp_count;
+ if (total_tx_count > MAX_TX_RINGS) {
err = -EINVAL;
en_err(priv,
"Total number of TX and XDP rings (%d) exceeds the maximum supported (%d)\n",
- channel->tx_count * priv->prof->num_up + xdp_count,
- MAX_TX_RINGS);
+ total_tx_count, MAX_TX_RINGS);
goto out;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 0d7fd3f..5868ec1 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -92,6 +92,7 @@ int mlx4_en_alloc_tx_queue_per_tc(struct net_device *dev, u8 tc)
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile new_prof;
struct mlx4_en_priv *tmp;
+ int total_count;
int port_up = 0;
int err = 0;
@@ -105,6 +106,14 @@ int mlx4_en_alloc_tx_queue_per_tc(struct net_device *dev, u8 tc)
MLX4_EN_NUM_UP_HIGH;
new_prof.tx_ring_num[TX] = new_prof.num_tx_rings_p_up *
new_prof.num_up;
+ total_count = new_prof.tx_ring_num[TX] + new_prof.tx_ring_num[TX_XDP];
+ if (total_count > MAX_TX_RINGS) {
+ err = -EINVAL;
+ en_err(priv,
+ "Total number of TX and XDP rings (%d) exceeds the maximum supported (%d)\n",
+ total_count, MAX_TX_RINGS);
+ goto out;
+ }
err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true);
if (err)
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 6a04603..4afe56a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -313,7 +313,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
for (i = 0; i < dev->caps.num_ports - 1; i++) {
if (port_type[i] != port_type[i + 1]) {
mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
- return -EINVAL;
+ return -EOPNOTSUPP;
}
}
}
@@ -322,7 +322,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
if (!(port_type[i] & dev->caps.supported_type[i+1])) {
mlx4_err(dev, "Requested port type for port %d is not supported on this HCA\n",
i + 1);
- return -EINVAL;
+ return -EOPNOTSUPP;
}
}
return 0;
@@ -1188,8 +1188,7 @@ static int __set_port_type(struct mlx4_port_info *info,
mlx4_err(mdev,
"Requested port type for port %d is not supported on this HCA\n",
info->port);
- err = -EINVAL;
- goto err_sup;
+ return -EOPNOTSUPP;
}
mlx4_stop_sense(mdev);
@@ -1211,7 +1210,7 @@ static int __set_port_type(struct mlx4_port_info *info,
for (i = 1; i <= mdev->caps.num_ports; i++) {
if (mdev->caps.possible_type[i] == MLX4_PORT_TYPE_AUTO) {
mdev->caps.possible_type[i] = mdev->caps.port_type[i];
- err = -EINVAL;
+ err = -EOPNOTSUPP;
}
}
}
@@ -1237,7 +1236,7 @@ static int __set_port_type(struct mlx4_port_info *info,
out:
mlx4_start_sense(mdev);
mutex_unlock(&priv->port_mutex);
-err_sup:
+
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 676428a..a4c1ed6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -471,12 +471,31 @@ void mlx4_init_quotas(struct mlx4_dev *dev)
priv->mfunc.master.res_tracker.res_alloc[RES_MPT].quota[pf];
}
-static int get_max_gauranteed_vfs_counter(struct mlx4_dev *dev)
+static int
+mlx4_calc_res_counter_guaranteed(struct mlx4_dev *dev,
+ struct resource_allocator *res_alloc,
+ int vf)
{
- /* reduce the sink counter */
- return (dev->caps.max_counters - 1 -
- (MLX4_PF_COUNTERS_PER_PORT * MLX4_MAX_PORTS))
- / MLX4_MAX_PORTS;
+ struct mlx4_active_ports actv_ports;
+ int ports, counters_guaranteed;
+
+ /* For master, only allocate according to the number of phys ports */
+ if (vf == mlx4_master_func_num(dev))
+ return MLX4_PF_COUNTERS_PER_PORT * dev->caps.num_ports;
+
+ /* calculate real number of ports for the VF */
+ actv_ports = mlx4_get_active_ports(dev, vf);
+ ports = bitmap_weight(actv_ports.ports, dev->caps.num_ports);
+ counters_guaranteed = ports * MLX4_VF_COUNTERS_PER_PORT;
+
+ /* If we do not have enough counters for this VF, do not
+ * allocate any for it. '-1' to reduce the sink counter.
+ */
+ if ((res_alloc->res_reserved + counters_guaranteed) >
+ (dev->caps.max_counters - 1))
+ return 0;
+
+ return counters_guaranteed;
}
int mlx4_init_resource_tracker(struct mlx4_dev *dev)
@@ -484,7 +503,6 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
struct mlx4_priv *priv = mlx4_priv(dev);
int i, j;
int t;
- int max_vfs_guarantee_counter = get_max_gauranteed_vfs_counter(dev);
priv->mfunc.master.res_tracker.slave_list =
kcalloc(dev->num_slaves, sizeof(struct slave_list),
@@ -603,16 +621,8 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
break;
case RES_COUNTER:
res_alloc->quota[t] = dev->caps.max_counters;
- if (t == mlx4_master_func_num(dev))
- res_alloc->guaranteed[t] =
- MLX4_PF_COUNTERS_PER_PORT *
- MLX4_MAX_PORTS;
- else if (t <= max_vfs_guarantee_counter)
- res_alloc->guaranteed[t] =
- MLX4_VF_COUNTERS_PER_PORT *
- MLX4_MAX_PORTS;
- else
- res_alloc->guaranteed[t] = 0;
+ res_alloc->guaranteed[t] =
+ mlx4_calc_res_counter_guaranteed(dev, res_alloc, t);
break;
default:
break;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c
index 4ab0d03..28d56e4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c
@@ -155,8 +155,11 @@ static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer,
}
if (port_buffer->buffer[i].size <
- (xoff + max_mtu + (1 << MLX5E_BUFFER_CELL_SHIFT)))
+ (xoff + max_mtu + (1 << MLX5E_BUFFER_CELL_SHIFT))) {
+ pr_err("buffer_size[%d]=%d is not enough for lossless buffer\n",
+ i, port_buffer->buffer[i].size);
return -ENOMEM;
+ }
port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff;
port_buffer->buffer[i].xon =
@@ -232,6 +235,26 @@ static int update_buffer_lossy(unsigned int max_mtu,
return 0;
}
+static int fill_pfc_en(struct mlx5_core_dev *mdev, u8 *pfc_en)
+{
+ u32 g_rx_pause, g_tx_pause;
+ int err;
+
+ err = mlx5_query_port_pause(mdev, &g_rx_pause, &g_tx_pause);
+ if (err)
+ return err;
+
+ /* If global pause enabled, set all active buffers to lossless.
+ * Otherwise, check PFC setting.
+ */
+ if (g_rx_pause || g_tx_pause)
+ *pfc_en = 0xff;
+ else
+ err = mlx5_query_port_pfc(mdev, pfc_en, NULL);
+
+ return err;
+}
+
#define MINIMUM_MAX_MTU 9216
int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv,
u32 change, unsigned int mtu,
@@ -277,7 +300,7 @@ int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv,
if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) {
update_prio2buffer = true;
- err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL);
+ err = fill_pfc_en(priv->mdev, &curr_pfc_en);
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index 10d72c8..a383276 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -1320,7 +1320,7 @@ static int mlx5e_get_module_info(struct net_device *netdev,
break;
case MLX5_MODULE_ID_SFP:
modinfo->type = ETH_MODULE_SFF_8472;
- modinfo->eeprom_len = MLX5_EEPROM_PAGE_LENGTH;
+ modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
break;
default:
netdev_err(priv->netdev, "%s: cable type not recognized:0x%x\n",
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index df49dc1..9cbc417 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -1267,8 +1267,11 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state)))
return 0;
- if (cq->decmprs_left)
+ if (cq->decmprs_left) {
work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget);
+ if (cq->decmprs_left || work_done >= budget)
+ goto out;
+ }
cqe = mlx5_cqwq_get_cqe(&cq->wq);
if (!cqe) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
index 4382ef8..5fb088b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
@@ -35,6 +35,7 @@
#include <linux/udp.h>
#include <net/udp.h>
#include "en.h"
+#include "en/port.h"
enum {
MLX5E_ST_LINK_STATE,
@@ -80,22 +81,12 @@ static int mlx5e_test_link_state(struct mlx5e_priv *priv)
static int mlx5e_test_link_speed(struct mlx5e_priv *priv)
{
- u32 out[MLX5_ST_SZ_DW(ptys_reg)];
- u32 eth_proto_oper;
- int i;
+ u32 speed;
if (!netif_carrier_ok(priv->netdev))
return 1;
- if (mlx5_query_port_ptys(priv->mdev, out, sizeof(out), MLX5_PTYS_EN, 1))
- return 1;
-
- eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper);
- for (i = 0; i < MLX5E_LINK_MODES_NUMBER; i++) {
- if (eth_proto_oper & MLX5E_PROT_MASK(i))
- return 0;
- }
- return 1;
+ return mlx5e_port_linkspeed(priv->mdev, &speed);
}
struct mlx5ehdr {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index 0b03d654..73dce92 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -462,7 +462,10 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
static void mlx5e_dump_error_cqe(struct mlx5e_txqsq *sq,
struct mlx5_err_cqe *err_cqe)
{
- u32 ci = mlx5_cqwq_get_ci(&sq->cq.wq);
+ struct mlx5_cqwq *wq = &sq->cq.wq;
+ u32 ci;
+
+ ci = mlx5_cqwq_ctr2ix(wq, wq->cc - 1);
netdev_err(sq->channel->netdev,
"Error cqe on cqn 0x%x, ci 0x%x, sqn 0x%x, syndrome 0x%x, vendor syndrome 0x%x\n",
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index 55ccd90b..7366033 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1861,7 +1861,7 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
unlock:
mutex_unlock(&esw->state_lock);
- return 0;
+ return err;
}
int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
index 8ca1d19..d8d0b6b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
@@ -462,8 +462,10 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size)
}
err = mlx5_vector2eqn(mdev, smp_processor_id(), &eqn, &irqn);
- if (err)
+ if (err) {
+ kvfree(in);
goto err_cqwq;
+ }
cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
MLX5_SET(cqc, cqc, log_cq_size, ilog2(cq_size));
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index c079f85..82a5331 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -520,7 +520,7 @@ static void del_sw_flow_group(struct fs_node *node)
rhashtable_destroy(&fg->ftes_hash);
ida_destroy(&fg->fte_allocator);
- if (ft->autogroup.active)
+ if (ft->autogroup.active && fg->max_ftes == ft->autogroup.group_size)
ft->autogroup.num_groups--;
err = rhltable_remove(&ft->fgs_hash,
&fg->hash,
@@ -1065,6 +1065,8 @@ mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
ft->autogroup.active = true;
ft->autogroup.required_groups = max_num_groups;
+ /* We save place for flow groups in addition to max types */
+ ft->autogroup.group_size = ft->max_fte / (max_num_groups + 1);
return ft;
}
@@ -1270,8 +1272,7 @@ static struct mlx5_flow_group *alloc_auto_flow_group(struct mlx5_flow_table *ft
return ERR_PTR(-ENOENT);
if (ft->autogroup.num_groups < ft->autogroup.required_groups)
- /* We save place for flow groups in addition to max types */
- group_size = ft->max_fte / (ft->autogroup.required_groups + 1);
+ group_size = ft->autogroup.group_size;
/* ft->max_fte == ft->autogroup.max_types */
if (group_size == 0)
@@ -1298,7 +1299,8 @@ static struct mlx5_flow_group *alloc_auto_flow_group(struct mlx5_flow_table *ft
if (IS_ERR(fg))
goto out;
- ft->autogroup.num_groups++;
+ if (group_size == ft->autogroup.group_size)
+ ft->autogroup.num_groups++;
out:
return fg;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index 32070e5..ba62fbc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -121,6 +121,7 @@ struct mlx5_flow_table {
struct {
bool active;
unsigned int required_groups;
+ unsigned int group_size;
unsigned int num_groups;
} autogroup;
/* Protect fwd_rules */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 231ed50..5fac00e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -859,11 +859,9 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
priv->numa_node = dev_to_node(&dev->pdev->dev);
- priv->dbg_root = debugfs_create_dir(dev_name(&pdev->dev), mlx5_debugfs_root);
- if (!priv->dbg_root) {
- dev_err(&pdev->dev, "Cannot create debugfs dir, aborting\n");
- return -ENOMEM;
- }
+ if (mlx5_debugfs_root)
+ priv->dbg_root =
+ debugfs_create_dir(pci_name(pdev), mlx5_debugfs_root);
err = mlx5_pci_enable_device(dev);
if (err) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
index 4ca07bf..f33707c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
@@ -132,7 +132,7 @@ void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type)
if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type)) {
mlx5_core_warn(dev, "event 0x%.2x is not allowed on resource 0x%.8x\n",
event_type, rsn);
- return;
+ goto out;
}
switch (common->res) {
@@ -150,7 +150,7 @@ void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type)
default:
mlx5_core_warn(dev, "invalid resource type for 0x%x\n", rsn);
}
-
+out:
mlx5_core_put_rsc(common);
}
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c
index 2cf8912..d765e7a 100644
--- a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c
+++ b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c
@@ -86,6 +86,8 @@ static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
return err;
if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK) {
+ fsm_state_err = min_t(enum mlxfw_fsm_state_err,
+ fsm_state_err, MLXFW_FSM_STATE_ERR_MAX);
pr_err("Firmware flash failed: %s\n",
mlxfw_fsm_state_err_str[fsm_state_err]);
return -EINVAL;
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
index 993cb5b..b99169a 100644
--- a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
+++ b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
@@ -37,6 +37,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netlink.h>
+#include <linux/vmalloc.h>
#include <linux/xz.h>
#include "mlxfw_mfa2.h"
#include "mlxfw_mfa2_file.h"
@@ -579,7 +580,7 @@ mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file,
comp_size = be32_to_cpu(comp->size);
comp_buf_size = comp_size + mlxfw_mfa2_comp_magic_len;
- comp_data = kmalloc(sizeof(*comp_data) + comp_buf_size, GFP_KERNEL);
+ comp_data = vzalloc(sizeof(*comp_data) + comp_buf_size);
if (!comp_data)
return ERR_PTR(-ENOMEM);
comp_data->comp.data_size = comp_size;
@@ -601,7 +602,7 @@ mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file,
comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len;
return &comp_data->comp;
err_out:
- kfree(comp_data);
+ vfree(comp_data);
return ERR_PTR(err);
}
@@ -610,7 +611,7 @@ void mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *comp)
const struct mlxfw_mfa2_comp_data *comp_data;
comp_data = container_of(comp, struct mlxfw_mfa2_comp_data, comp);
- kfree(comp_data);
+ vfree(comp_data);
}
void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index ee126bc..e498ee9 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -336,7 +336,10 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
return -EINVAL;
}
if (MLXSW_SP_FWREV_MINOR_TO_BRANCH(rev->minor) ==
- MLXSW_SP_FWREV_MINOR_TO_BRANCH(req_rev->minor))
+ MLXSW_SP_FWREV_MINOR_TO_BRANCH(req_rev->minor) &&
+ (rev->minor > req_rev->minor ||
+ (rev->minor == req_rev->minor &&
+ rev->subminor >= req_rev->subminor)))
return 0;
dev_info(mlxsw_sp->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver\n",
@@ -2815,6 +2818,13 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
MLXSW_REG_QEEC_MAS_DIS);
if (err)
return err;
+
+ err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
+ MLXSW_REG_QEEC_HIERARCY_TC,
+ i + 8, i,
+ MLXSW_REG_QEEC_MAS_DIS);
+ if (err)
+ return err;
}
/* Map all priorities to traffic class 0. */
@@ -4410,9 +4420,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
if (err)
goto err_col_port_add;
- err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id);
- if (err)
- goto err_col_port_enable;
mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
mlxsw_sp_port->local_port);
@@ -4427,8 +4434,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
return 0;
-err_col_port_enable:
- mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
err_col_port_add:
if (!lag->ref_count)
mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
@@ -4447,7 +4452,6 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
WARN_ON(lag->ref_count == 0);
- mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id);
mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
/* Any VLANs configured on the port are no longer valid */
@@ -4492,21 +4496,56 @@ static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
}
-static int mlxsw_sp_port_lag_tx_en_set(struct mlxsw_sp_port *mlxsw_sp_port,
- bool lag_tx_enabled)
+static int
+mlxsw_sp_port_lag_col_dist_enable(struct mlxsw_sp_port *mlxsw_sp_port)
{
- if (lag_tx_enabled)
- return mlxsw_sp_lag_dist_port_add(mlxsw_sp_port,
- mlxsw_sp_port->lag_id);
- else
- return mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
- mlxsw_sp_port->lag_id);
+ int err;
+
+ err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+ if (err)
+ return err;
+
+ err = mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+ if (err)
+ goto err_dist_port_add;
+
+ return 0;
+
+err_dist_port_add:
+ mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+ return err;
+}
+
+static int
+mlxsw_sp_port_lag_col_dist_disable(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ int err;
+
+ err = mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+ if (err)
+ return err;
+
+ err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+ if (err)
+ goto err_col_port_disable;
+
+ return 0;
+
+err_col_port_disable:
+ mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+ return err;
}
static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
struct netdev_lag_lower_state_info *info)
{
- return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled);
+ if (info->tx_enabled)
+ return mlxsw_sp_port_lag_col_dist_enable(mlxsw_sp_port);
+ else
+ return mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
}
static int mlxsw_sp_port_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -4668,8 +4707,7 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
upper_dev);
} else {
- mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port,
- false);
+ mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
mlxsw_sp_port_lag_leave(mlxsw_sp_port,
upper_dev);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
index bdf53cf..720514b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
@@ -650,6 +650,13 @@ mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
return 0;
+ if (!p->child_handle) {
+ /* This is an invisible FIFO replacing the original Qdisc.
+ * Ignore it--the original Qdisc's destroy will follow.
+ */
+ return 0;
+ }
+
/* See if the grafted qdisc is already offloaded on any tclass. If so,
* unoffload it.
*/
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 2ab9cf2..76960d3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -970,7 +970,7 @@ u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
if (d)
return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
else
- return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
+ return RT_TABLE_MAIN;
}
static struct mlxsw_sp_rif *
@@ -1215,15 +1215,12 @@ mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
{
u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
- struct net_device *ipip_ul_dev;
if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
return false;
- ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
- ul_tb_id, ipip_entry) &&
- (!ipip_ul_dev || ipip_ul_dev == ul_dev);
+ ul_tb_id, ipip_entry);
}
/* Given decap parameters, find the corresponding IPIP entry. */
@@ -1532,27 +1529,10 @@ static int mlxsw_sp_netdevice_ipip_ol_vrf_event(struct mlxsw_sp *mlxsw_sp,
{
struct mlxsw_sp_ipip_entry *ipip_entry =
mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
- enum mlxsw_sp_l3proto ul_proto;
- union mlxsw_sp_l3addr saddr;
- u32 ul_tb_id;
if (!ipip_entry)
return 0;
- /* For flat configuration cases, moving overlay to a different VRF might
- * cause local address conflict, and the conflicting tunnels need to be
- * demoted.
- */
- ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
- ul_proto = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
- saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
- if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto,
- saddr, ul_tb_id,
- ipip_entry)) {
- mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
- return 0;
- }
-
return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
true, false, false, extack);
}
@@ -2248,7 +2228,7 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
- bool removing);
+ bool removing, bool dead);
static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
{
@@ -2379,7 +2359,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
memcpy(neigh_entry->ha, ha, ETH_ALEN);
mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
- mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
+ mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected,
+ dead);
if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
@@ -3343,13 +3324,79 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
nh->update = 1;
}
+static int
+mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_neigh_entry *neigh_entry)
+{
+ struct neighbour *n, *old_n = neigh_entry->key.n;
+ struct mlxsw_sp_nexthop *nh;
+ bool entry_connected;
+ u8 nud_state, dead;
+ int err;
+
+ nh = list_first_entry(&neigh_entry->nexthop_list,
+ struct mlxsw_sp_nexthop, neigh_list_node);
+
+ n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
+ if (!n) {
+ n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
+ nh->rif->dev);
+ if (IS_ERR(n))
+ return PTR_ERR(n);
+ neigh_event_send(n, NULL);
+ }
+
+ mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
+ neigh_entry->key.n = n;
+ err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
+ if (err)
+ goto err_neigh_entry_insert;
+
+ read_lock_bh(&n->lock);
+ nud_state = n->nud_state;
+ dead = n->dead;
+ read_unlock_bh(&n->lock);
+ entry_connected = nud_state & NUD_VALID && !dead;
+
+ list_for_each_entry(nh, &neigh_entry->nexthop_list,
+ neigh_list_node) {
+ neigh_release(old_n);
+ neigh_clone(n);
+ __mlxsw_sp_nexthop_neigh_update(nh, !entry_connected);
+ mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+ }
+
+ neigh_release(n);
+
+ return 0;
+
+err_neigh_entry_insert:
+ neigh_entry->key.n = old_n;
+ mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
+ neigh_release(n);
+ return err;
+}
+
static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
- bool removing)
+ bool removing, bool dead)
{
struct mlxsw_sp_nexthop *nh;
+ if (list_empty(&neigh_entry->nexthop_list))
+ return;
+
+ if (dead) {
+ int err;
+
+ err = mlxsw_sp_nexthop_dead_neigh_replace(mlxsw_sp,
+ neigh_entry);
+ if (err)
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to replace dead neigh\n");
+ return;
+ }
+
list_for_each_entry(nh, &neigh_entry->nexthop_list,
neigh_list_node) {
__mlxsw_sp_nexthop_neigh_update(nh, removing);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index a4f237f..8d556eb 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -2324,8 +2324,15 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct mlxsw_sp_switchdev_event_work *switchdev_work;
struct switchdev_notifier_fdb_info *fdb_info = ptr;
+ struct net_device *br_dev;
- if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
+ /* Tunnel devices are not our uppers, so check their master instead */
+ br_dev = netdev_master_upper_dev_get_rcu(dev);
+ if (!br_dev)
+ return NOTIFY_DONE;
+ if (!netif_is_bridge_master(br_dev))
+ return NOTIFY_DONE;
+ if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev))
return NOTIFY_DONE;
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c
index bd51e05..b881f5d 100644
--- a/drivers/net/ethernet/micrel/ks8695net.c
+++ b/drivers/net/ethernet/micrel/ks8695net.c
@@ -1164,7 +1164,7 @@ ks8695_timeout(struct net_device *ndev)
* sk_buff and adds it to the TX ring. It then kicks the TX DMA
* engine to ensure transmission begins.
*/
-static int
+static netdev_tx_t
ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct ks8695_priv *ksp = netdev_priv(ndev);
diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c
index 0e9719f..35f8c9e 100644
--- a/drivers/net/ethernet/micrel/ks8851_mll.c
+++ b/drivers/net/ethernet/micrel/ks8851_mll.c
@@ -1021,9 +1021,9 @@ static void ks_write_qmu(struct ks_net *ks, u8 *pdata, u16 len)
* spin_lock_irqsave is required because tx and rx should be mutual exclusive.
* So while tx is in-progress, prevent IRQ interrupt from happenning.
*/
-static int ks_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t ks_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- int retv = NETDEV_TX_OK;
+ netdev_tx_t retv = NETDEV_TX_OK;
struct ks_net *ks = netdev_priv(netdev);
disable_irq(netdev->irq);
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 732ba21..a29a6a6 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -253,8 +253,15 @@ static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid,
port->pvid = vid;
/* Untagged egress vlan clasification */
- if (untagged)
+ if (untagged && port->vid != vid) {
+ if (port->vid) {
+ dev_err(ocelot->dev,
+ "Port already has a native VLAN: %d\n",
+ port->vid);
+ return -EBUSY;
+ }
port->vid = vid;
+ }
ocelot_vlan_port_apply(ocelot, port);
@@ -886,7 +893,7 @@ static int ocelot_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
- return ocelot_vlan_vid_add(dev, vid, false, true);
+ return ocelot_vlan_vid_add(dev, vid, false, false);
}
static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
@@ -1506,9 +1513,6 @@ static int ocelot_netdevice_port_event(struct net_device *dev,
struct ocelot_port *ocelot_port = netdev_priv(dev);
int err = 0;
- if (!ocelot_netdevice_dev_check(dev))
- return 0;
-
switch (event) {
case NETDEV_CHANGEUPPER:
if (netif_is_bridge_master(info->upper_dev)) {
@@ -1545,12 +1549,16 @@ static int ocelot_netdevice_event(struct notifier_block *unused,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
int ret = 0;
+ if (!ocelot_netdevice_dev_check(dev))
+ return 0;
+
if (event == NETDEV_PRECHANGEUPPER &&
netif_is_lag_master(info->upper_dev)) {
struct netdev_lag_upper_info *lag_upper_info = info->upper_info;
struct netlink_ext_ack *extack;
- if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ if (lag_upper_info &&
+ lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
extack = netdev_notifier_info_to_extack(&info->info);
NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index 616bec3..3d8c6f3 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -541,7 +541,7 @@ void __ocelot_write_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 offset);
#define ocelot_write_rix(ocelot, val, reg, ri) __ocelot_write_ix(ocelot, val, reg, reg##_RSZ * (ri))
#define ocelot_write(ocelot, val, reg) __ocelot_write_ix(ocelot, val, reg, 0)
-void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 mask,
+void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 mask, u32 reg,
u32 offset);
#define ocelot_rmw_ix(ocelot, val, m, reg, gi, ri) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri))
#define ocelot_rmw_gix(ocelot, val, m, reg, gi) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi))
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index dbd0098..2134045 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -206,6 +206,11 @@ enum nfp_bpf_map_use {
NFP_MAP_USE_ATOMIC_CNT,
};
+struct nfp_bpf_map_word {
+ unsigned char type :4;
+ unsigned char non_zero_update :1;
+};
+
/**
* struct nfp_bpf_map - private per-map data attached to BPF maps for offload
* @offmap: pointer to the offloaded BPF map
@@ -219,7 +224,7 @@ struct nfp_bpf_map {
struct nfp_app_bpf *bpf;
u32 tid;
struct list_head l;
- enum nfp_bpf_map_use use_map[];
+ struct nfp_bpf_map_word use_map[];
};
struct nfp_bpf_neutral_map {
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
index 1ccd637..6140e465 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
@@ -299,10 +299,25 @@ static void nfp_map_bpf_byte_swap(struct nfp_bpf_map *nfp_map, void *value)
unsigned int i;
for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++)
- if (nfp_map->use_map[i] == NFP_MAP_USE_ATOMIC_CNT)
+ if (nfp_map->use_map[i].type == NFP_MAP_USE_ATOMIC_CNT)
word[i] = (__force u32)cpu_to_be32(word[i]);
}
+/* Mark value as unsafely initialized in case it becomes atomic later
+ * and we didn't byte swap something non-byte swap neutral.
+ */
+static void
+nfp_map_bpf_byte_swap_record(struct nfp_bpf_map *nfp_map, void *value)
+{
+ u32 *word = value;
+ unsigned int i;
+
+ for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++)
+ if (nfp_map->use_map[i].type == NFP_MAP_UNUSED &&
+ word[i] != (__force u32)cpu_to_be32(word[i]))
+ nfp_map->use_map[i].non_zero_update = 1;
+}
+
static int
nfp_bpf_map_lookup_entry(struct bpf_offloaded_map *offmap,
void *key, void *value)
@@ -322,6 +337,7 @@ nfp_bpf_map_update_entry(struct bpf_offloaded_map *offmap,
void *key, void *value, u64 flags)
{
nfp_map_bpf_byte_swap(offmap->dev_priv, value);
+ nfp_map_bpf_byte_swap_record(offmap->dev_priv, value);
return nfp_bpf_ctrl_update_entry(offmap, key, value, flags);
}
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index a6e9248..db7e186 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -108,6 +108,46 @@ nfp_record_adjust_head(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog,
nfp_prog->adjust_head_location = location;
}
+static bool nfp_bpf_map_update_value_ok(struct bpf_verifier_env *env)
+{
+ const struct bpf_reg_state *reg1 = cur_regs(env) + BPF_REG_1;
+ const struct bpf_reg_state *reg3 = cur_regs(env) + BPF_REG_3;
+ struct bpf_offloaded_map *offmap;
+ struct bpf_func_state *state;
+ struct nfp_bpf_map *nfp_map;
+ int off, i;
+
+ state = env->cur_state->frame[reg3->frameno];
+
+ /* We need to record each time update happens with non-zero words,
+ * in case such word is used in atomic operations.
+ * Implicitly depend on nfp_bpf_stack_arg_ok(reg3) being run before.
+ */
+
+ offmap = map_to_offmap(reg1->map_ptr);
+ nfp_map = offmap->dev_priv;
+ off = reg3->off + reg3->var_off.value;
+
+ for (i = 0; i < offmap->map.value_size; i++) {
+ struct bpf_stack_state *stack_entry;
+ unsigned int soff;
+
+ soff = -(off + i) - 1;
+ stack_entry = &state->stack[soff / BPF_REG_SIZE];
+ if (stack_entry->slot_type[soff % BPF_REG_SIZE] == STACK_ZERO)
+ continue;
+
+ if (nfp_map->use_map[i / 4].type == NFP_MAP_USE_ATOMIC_CNT) {
+ pr_vlog(env, "value at offset %d/%d may be non-zero, bpf_map_update_elem() is required to initialize atomic counters to zero to avoid offload endian issues\n",
+ i, soff);
+ return false;
+ }
+ nfp_map->use_map[i / 4].non_zero_update = 1;
+ }
+
+ return true;
+}
+
static int
nfp_bpf_stack_arg_ok(const char *fname, struct bpf_verifier_env *env,
const struct bpf_reg_state *reg,
@@ -198,7 +238,8 @@ nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
bpf->helpers.map_update, reg1) ||
!nfp_bpf_stack_arg_ok("map_update", env, reg2,
meta->func_id ? &meta->arg2 : NULL) ||
- !nfp_bpf_stack_arg_ok("map_update", env, reg3, NULL))
+ !nfp_bpf_stack_arg_ok("map_update", env, reg3, NULL) ||
+ !nfp_bpf_map_update_value_ok(env))
return -EOPNOTSUPP;
break;
@@ -376,15 +417,22 @@ nfp_bpf_map_mark_used_one(struct bpf_verifier_env *env,
struct nfp_bpf_map *nfp_map,
unsigned int off, enum nfp_bpf_map_use use)
{
- if (nfp_map->use_map[off / 4] != NFP_MAP_UNUSED &&
- nfp_map->use_map[off / 4] != use) {
+ if (nfp_map->use_map[off / 4].type != NFP_MAP_UNUSED &&
+ nfp_map->use_map[off / 4].type != use) {
pr_vlog(env, "map value use type conflict %s vs %s off: %u\n",
- nfp_bpf_map_use_name(nfp_map->use_map[off / 4]),
+ nfp_bpf_map_use_name(nfp_map->use_map[off / 4].type),
nfp_bpf_map_use_name(use), off);
return -EOPNOTSUPP;
}
- nfp_map->use_map[off / 4] = use;
+ if (nfp_map->use_map[off / 4].non_zero_update &&
+ use == NFP_MAP_USE_ATOMIC_CNT) {
+ pr_vlog(env, "atomic counter in map value may already be initialized to non-zero value off: %u\n",
+ off);
+ return -EOPNOTSUPP;
+ }
+
+ nfp_map->use_map[off / 4].type = use;
return 0;
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index c6d29fd..d288c7e 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -2187,9 +2187,13 @@ nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring)
tx_ring->size = array_size(tx_ring->cnt, sizeof(*tx_ring->txds));
tx_ring->txds = dma_zalloc_coherent(dp->dev, tx_ring->size,
- &tx_ring->dma, GFP_KERNEL);
- if (!tx_ring->txds)
+ &tx_ring->dma,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!tx_ring->txds) {
+ netdev_warn(dp->netdev, "failed to allocate TX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n",
+ tx_ring->cnt);
goto err_alloc;
+ }
tx_ring->txbufs = kvcalloc(tx_ring->cnt, sizeof(*tx_ring->txbufs),
GFP_KERNEL);
@@ -2341,9 +2345,13 @@ nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring)
rx_ring->cnt = dp->rxd_cnt;
rx_ring->size = array_size(rx_ring->cnt, sizeof(*rx_ring->rxds));
rx_ring->rxds = dma_zalloc_coherent(dp->dev, rx_ring->size,
- &rx_ring->dma, GFP_KERNEL);
- if (!rx_ring->rxds)
+ &rx_ring->dma,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!rx_ring->rxds) {
+ netdev_warn(dp->netdev, "failed to allocate RX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n",
+ rx_ring->cnt);
goto err_alloc;
+ }
rx_ring->rxbufs = kvcalloc(rx_ring->cnt, sizeof(*rx_ring->rxbufs),
GFP_KERNEL);
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index a60e1c8..32e786a 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -914,7 +914,7 @@ u16 qed_get_cm_pq_idx_llt_mtc(struct qed_hwfn *p_hwfn, u8 tc);
/* Prototypes */
int qed_fill_dev_info(struct qed_dev *cdev,
struct qed_dev_info *dev_info);
-void qed_link_update(struct qed_hwfn *hwfn);
+void qed_link_update(struct qed_hwfn *hwfn, struct qed_ptt *ptt);
u32 qed_unzip_data(struct qed_hwfn *p_hwfn,
u32 input_len, u8 *input_buf,
u32 max_size, u8 *unzip_buf);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
index 015de1e..2847509 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
@@ -796,7 +796,18 @@ qed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn,
tx_pkt.vlan = p_buffer->vlan;
tx_pkt.bd_flags = bd_flags;
tx_pkt.l4_hdr_offset_w = l4_hdr_offset_w;
- tx_pkt.tx_dest = p_ll2_conn->tx_dest;
+ switch (p_ll2_conn->tx_dest) {
+ case CORE_TX_DEST_NW:
+ tx_pkt.tx_dest = QED_LL2_TX_DEST_NW;
+ break;
+ case CORE_TX_DEST_LB:
+ tx_pkt.tx_dest = QED_LL2_TX_DEST_LB;
+ break;
+ case CORE_TX_DEST_DROP:
+ default:
+ tx_pkt.tx_dest = QED_LL2_TX_DEST_DROP;
+ break;
+ }
tx_pkt.first_frag = first_frag;
tx_pkt.first_frag_len = p_buffer->packet_length;
tx_pkt.cookie = p_buffer;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 637687b..049a83b 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -1462,6 +1462,7 @@ static int qed_get_link_data(struct qed_hwfn *hwfn,
}
static void qed_fill_link(struct qed_hwfn *hwfn,
+ struct qed_ptt *ptt,
struct qed_link_output *if_link)
{
struct qed_mcp_link_params params;
@@ -1542,7 +1543,7 @@ static void qed_fill_link(struct qed_hwfn *hwfn,
/* TODO - fill duplex properly */
if_link->duplex = DUPLEX_FULL;
- qed_mcp_get_media_type(hwfn->cdev, &media_type);
+ qed_mcp_get_media_type(hwfn, ptt, &media_type);
if_link->port = qed_get_port_type(media_type);
if_link->autoneg = params.speed.autoneg;
@@ -1598,21 +1599,34 @@ static void qed_fill_link(struct qed_hwfn *hwfn,
static void qed_get_current_link(struct qed_dev *cdev,
struct qed_link_output *if_link)
{
+ struct qed_hwfn *hwfn;
+ struct qed_ptt *ptt;
int i;
- qed_fill_link(&cdev->hwfns[0], if_link);
+ hwfn = &cdev->hwfns[0];
+ if (IS_PF(cdev)) {
+ ptt = qed_ptt_acquire(hwfn);
+ if (ptt) {
+ qed_fill_link(hwfn, ptt, if_link);
+ qed_ptt_release(hwfn, ptt);
+ } else {
+ DP_NOTICE(hwfn, "Failed to fill link; No PTT\n");
+ }
+ } else {
+ qed_fill_link(hwfn, NULL, if_link);
+ }
for_each_hwfn(cdev, i)
qed_inform_vf_link_state(&cdev->hwfns[i]);
}
-void qed_link_update(struct qed_hwfn *hwfn)
+void qed_link_update(struct qed_hwfn *hwfn, struct qed_ptt *ptt)
{
void *cookie = hwfn->cdev->ops_cookie;
struct qed_common_cb_ops *op = hwfn->cdev->protocol_ops.common;
struct qed_link_output if_link;
- qed_fill_link(hwfn, &if_link);
+ qed_fill_link(hwfn, ptt, &if_link);
qed_inform_vf_link_state(hwfn);
if (IS_LEAD_HWFN(hwfn) && cookie)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 58c7eb9..938ace3 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -1382,7 +1382,7 @@ static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn,
if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE)
qed_mcp_read_eee_config(p_hwfn, p_ptt, p_link);
- qed_link_update(p_hwfn);
+ qed_link_update(p_hwfn, p_ptt);
out:
spin_unlock_bh(&p_hwfn->mcp_info->link_lock);
}
@@ -1849,12 +1849,10 @@ int qed_mcp_get_mbi_ver(struct qed_hwfn *p_hwfn,
return 0;
}
-int qed_mcp_get_media_type(struct qed_dev *cdev, u32 *p_media_type)
+int qed_mcp_get_media_type(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u32 *p_media_type)
{
- struct qed_hwfn *p_hwfn = &cdev->hwfns[0];
- struct qed_ptt *p_ptt;
-
- if (IS_VF(cdev))
+ if (IS_VF(p_hwfn->cdev))
return -EINVAL;
if (!qed_mcp_is_init(p_hwfn)) {
@@ -1862,16 +1860,15 @@ int qed_mcp_get_media_type(struct qed_dev *cdev, u32 *p_media_type)
return -EBUSY;
}
- *p_media_type = MEDIA_UNSPECIFIED;
+ if (!p_ptt) {
+ *p_media_type = MEDIA_UNSPECIFIED;
+ return -EINVAL;
+ }
- p_ptt = qed_ptt_acquire(p_hwfn);
- if (!p_ptt)
- return -EBUSY;
-
- *p_media_type = qed_rd(p_hwfn, p_ptt, p_hwfn->mcp_info->port_addr +
- offsetof(struct public_port, media_type));
-
- qed_ptt_release(p_hwfn, p_ptt);
+ *p_media_type = qed_rd(p_hwfn, p_ptt,
+ p_hwfn->mcp_info->port_addr +
+ offsetof(struct public_port,
+ media_type));
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index 85e6b39..80a6b5d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -322,14 +322,15 @@ int qed_mcp_get_mbi_ver(struct qed_hwfn *p_hwfn,
* @brief Get media type value of the port.
*
* @param cdev - qed dev pointer
+ * @param p_ptt
* @param mfw_ver - media type value
*
* @return int -
* 0 - Operation was successul.
* -EBUSY - Operation failed
*/
-int qed_mcp_get_media_type(struct qed_dev *cdev,
- u32 *media_type);
+int qed_mcp_get_media_type(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u32 *media_type);
/**
* @brief General function for sending commands to the MCP
diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c
index 6ab3fb0..5dda547 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_vf.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c
@@ -1698,7 +1698,7 @@ static void qed_handle_bulletin_change(struct qed_hwfn *hwfn)
ops->ports_update(cookie, vxlan_port, geneve_port);
/* Always update link configuration according to bulletin */
- qed_link_update(hwfn);
+ qed_link_update(hwfn, NULL);
}
void qed_iov_vf_task(struct work_struct *work)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c
index b16ce7d..c3d5d40a 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_filter.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c
@@ -1230,7 +1230,7 @@ qede_configure_mcast_filtering(struct net_device *ndev,
netif_addr_lock_bh(ndev);
mc_count = netdev_mc_count(ndev);
- if (mc_count < 64) {
+ if (mc_count <= 64) {
netdev_for_each_mc_addr(ha, ndev) {
ether_addr_copy(temp, ha->addr);
temp += ETH_ALEN;
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index f3d9c40..0d8e39f 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -1170,8 +1170,16 @@ enum qede_remove_mode {
static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
{
struct net_device *ndev = pci_get_drvdata(pdev);
- struct qede_dev *edev = netdev_priv(ndev);
- struct qed_dev *cdev = edev->cdev;
+ struct qede_dev *edev;
+ struct qed_dev *cdev;
+
+ if (!ndev) {
+ dev_info(&pdev->dev, "Device has already been removed\n");
+ return;
+ }
+
+ edev = netdev_priv(ndev);
+ cdev = edev->cdev;
DP_INFO(edev, "Starting qede_remove\n");
@@ -1354,6 +1362,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
rxq->rx_buf_seg_size = roundup_pow_of_two(size);
} else {
rxq->rx_buf_seg_size = PAGE_SIZE;
+ edev->ndev->features &= ~NETIF_F_GRO_HW;
}
/* Allocate the parallel driver ring for Rx buffers */
@@ -1398,6 +1407,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
}
}
+ edev->gro_disable = !(edev->ndev->features & NETIF_F_GRO_HW);
if (!edev->gro_disable)
qede_set_tpa_param(rxq);
err:
@@ -1598,8 +1608,6 @@ static void qede_init_fp(struct qede_dev *edev)
snprintf(fp->name, sizeof(fp->name), "%s-fp-%d",
edev->ndev->name, queue_id);
}
-
- edev->gro_disable = !(edev->ndev->features & NETIF_F_GRO_HW);
}
static int qede_set_real_num_queues(struct qede_dev *edev)
diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c
index 783ee6a..1b5e098 100644
--- a/drivers/net/ethernet/qlogic/qla3xxx.c
+++ b/drivers/net/ethernet/qlogic/qla3xxx.c
@@ -2757,6 +2757,9 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
int err;
for (i = 0; i < qdev->num_large_buffers; i++) {
+ lrg_buf_cb = &qdev->lrg_buf[i];
+ memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
+
skb = netdev_alloc_skb(qdev->ndev,
qdev->lrg_buffer_len);
if (unlikely(!skb)) {
@@ -2767,11 +2770,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
ql_free_large_buffers(qdev);
return -ENOMEM;
} else {
-
- lrg_buf_cb = &qdev->lrg_buf[i];
- memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
lrg_buf_cb->index = i;
- lrg_buf_cb->skb = skb;
/*
* We save some space to copy the ethhdr from first
* buffer
@@ -2793,6 +2792,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
return -ENOMEM;
}
+ lrg_buf_cb->skb = skb;
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
dma_unmap_len_set(lrg_buf_cb, maplen,
qdev->lrg_buffer_len -
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index 4b76c69..834208e 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -883,7 +883,7 @@ static u8 qlcnic_dcb_get_capability(struct net_device *netdev, int capid,
struct qlcnic_adapter *adapter = netdev_priv(netdev);
if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
- return 0;
+ return 1;
switch (capid) {
case DCB_CAP_ATTR_PG:
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index 016e209..d3487fc 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*
* RMNET configuration engine
*
@@ -349,7 +349,7 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
if (data[IFLA_RMNET_UL_AGG_PARAMS]) {
agg_params = nla_data(data[IFLA_RMNET_UL_AGG_PARAMS]);
- if (agg_params->agg_time < 3000000)
+ if (agg_params->agg_time < 1000000)
return -EINVAL;
}
}
@@ -367,10 +367,13 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
struct rmnet_port *port;
u16 mux_id;
+ if (!dev)
+ return -ENODEV;
+
real_dev = __dev_get_by_index(dev_net(dev),
nla_get_u32(tb[IFLA_LINK]));
- if (!real_dev || !dev || !rmnet_is_real_dev_registered(real_dev))
+ if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
return -ENODEV;
port = rmnet_get_port_rtnl(real_dev);
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 3488cf4..740ddad 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*
* RMNET Data MAP protocol
*
@@ -535,7 +535,6 @@ void rmnet_map_v5_checksum_uplink_packet(struct sk_buff *skb,
check = rmnet_map_get_csum_field(proto, trans);
if (check) {
- *check = 0;
skb->ip_summed = CHECKSUM_NONE;
/* Ask for checksum offloading */
ul_header->csum_valid_required = 1;
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 0c8b714..4ab87fe 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1010,6 +1010,10 @@ static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg)
{
int value;
+ /* Work around issue with chip reporting wrong PHY ID */
+ if (reg == MII_PHYSID2)
+ return 0xc912;
+
r8168dp_2_mdio_start(tp);
value = r8169_mdio_read(tp, reg);
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 9b6bf55..e04af95 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -1029,7 +1029,6 @@ struct ravb_private {
phy_interface_t phy_interface;
int msg_enable;
int speed;
- int duplex;
int emac_irq;
enum ravb_chip_id chip_id;
int rx_irqs[NUM_RX_QUEUE];
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 5462d2e..faaf740 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -82,13 +82,6 @@ static int ravb_config(struct net_device *ndev)
return error;
}
-static void ravb_set_duplex(struct net_device *ndev)
-{
- struct ravb_private *priv = netdev_priv(ndev);
-
- ravb_modify(ndev, ECMR, ECMR_DM, priv->duplex ? ECMR_DM : 0);
-}
-
static void ravb_set_rate(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
@@ -398,13 +391,11 @@ static int ravb_ring_init(struct net_device *ndev, int q)
/* E-MAC init function */
static void ravb_emac_init(struct net_device *ndev)
{
- struct ravb_private *priv = netdev_priv(ndev);
-
/* Receive frame limit set register */
ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
/* EMAC Mode: PAUSE prohibition; Duplex; RX Checksum; TX; RX */
- ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
+ ravb_write(ndev, ECMR_ZPF | ECMR_DM |
(ndev->features & NETIF_F_RXCSUM ? ECMR_RCSC : 0) |
ECMR_TE | ECMR_RE, ECMR);
@@ -992,12 +983,6 @@ static void ravb_adjust_link(struct net_device *ndev)
ravb_rcv_snd_disable(ndev);
if (phydev->link) {
- if (phydev->duplex != priv->duplex) {
- new_state = true;
- priv->duplex = phydev->duplex;
- ravb_set_duplex(ndev);
- }
-
if (phydev->speed != priv->speed) {
new_state = true;
priv->speed = phydev->speed;
@@ -1012,7 +997,6 @@ static void ravb_adjust_link(struct net_device *ndev)
new_state = true;
priv->link = 0;
priv->speed = 0;
- priv->duplex = -1;
}
/* Enable TX and RX right over here, if E-MAC change is ignored */
@@ -1042,7 +1026,6 @@ static int ravb_phy_init(struct net_device *ndev)
priv->link = 0;
priv->speed = 0;
- priv->duplex = -1;
/* Try connecting to PHY */
pn = of_parse_phandle(np, "phy-handle", 0);
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 7eeac3d..1f971d3 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -6042,22 +6042,25 @@ static const struct efx_ef10_nvram_type_info efx_ef10_nvram_types[] = {
{ NVRAM_PARTITION_TYPE_LICENSE, 0, 0, "sfc_license" },
{ NVRAM_PARTITION_TYPE_PHY_MIN, 0xff, 0, "sfc_phy_fw" },
};
+#define EF10_NVRAM_PARTITION_COUNT ARRAY_SIZE(efx_ef10_nvram_types)
static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
struct efx_mcdi_mtd_partition *part,
- unsigned int type)
+ unsigned int type,
+ unsigned long *found)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_METADATA_IN_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_METADATA_OUT_LENMAX);
const struct efx_ef10_nvram_type_info *info;
size_t size, erase_size, outlen;
+ int type_idx = 0;
bool protected;
int rc;
- for (info = efx_ef10_nvram_types; ; info++) {
- if (info ==
- efx_ef10_nvram_types + ARRAY_SIZE(efx_ef10_nvram_types))
+ for (type_idx = 0; ; type_idx++) {
+ if (type_idx == EF10_NVRAM_PARTITION_COUNT)
return -ENODEV;
+ info = efx_ef10_nvram_types + type_idx;
if ((type & ~info->type_mask) == info->type)
break;
}
@@ -6070,6 +6073,13 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
if (protected)
return -ENODEV; /* hide it */
+ /* If we've already exposed a partition of this type, hide this
+ * duplicate. All operations on MTDs are keyed by the type anyway,
+ * so we can't act on the duplicate.
+ */
+ if (__test_and_set_bit(type_idx, found))
+ return -EEXIST;
+
part->nvram_type = type;
MCDI_SET_DWORD(inbuf, NVRAM_METADATA_IN_TYPE, type);
@@ -6098,6 +6108,7 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
static int efx_ef10_mtd_probe(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX);
+ DECLARE_BITMAP(found, EF10_NVRAM_PARTITION_COUNT) = { 0 };
struct efx_mcdi_mtd_partition *parts;
size_t outlen, n_parts_total, i, n_parts;
unsigned int type;
@@ -6126,11 +6137,13 @@ static int efx_ef10_mtd_probe(struct efx_nic *efx)
for (i = 0; i < n_parts_total; i++) {
type = MCDI_ARRAY_DWORD(outbuf, NVRAM_PARTITIONS_OUT_TYPE_ID,
i);
- rc = efx_ef10_mtd_probe_partition(efx, &parts[n_parts], type);
- if (rc == 0)
- n_parts++;
- else if (rc != -ENODEV)
+ rc = efx_ef10_mtd_probe_partition(efx, &parts[n_parts], type,
+ found);
+ if (rc == -EEXIST || rc == -ENODEV)
+ continue;
+ if (rc)
goto fail;
+ n_parts++;
}
rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index f216615..cc8fbf3 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -1534,7 +1534,8 @@ void efx_ptp_remove(struct efx_nic *efx)
(void)efx_ptp_disable(efx);
cancel_work_sync(&efx->ptp_data->work);
- cancel_work_sync(&efx->ptp_data->pps_work);
+ if (efx->ptp_data->pps_workwq)
+ cancel_work_sync(&efx->ptp_data->pps_work);
skb_queue_purge(&efx->ptp_data->rxq);
skb_queue_purge(&efx->ptp_data->txq);
diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c
index b1b53f6..8355dfb 100644
--- a/drivers/net/ethernet/smsc/smc911x.c
+++ b/drivers/net/ethernet/smsc/smc911x.c
@@ -513,7 +513,8 @@ static void smc911x_hardware_send_pkt(struct net_device *dev)
* now, or set the card to generates an interrupt when ready
* for the packet.
*/
-static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct smc911x_local *lp = netdev_priv(dev);
unsigned int free;
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index b944828..8d6cff8 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -638,7 +638,8 @@ done: if (!THROTTLE_TX_PKTS)
* now, or set the card to generates an interrupt when ready
* for the packet.
*/
-static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct smc_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->base;
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index f0afb88..ce4bfec 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -1786,7 +1786,8 @@ static int smsc911x_stop(struct net_device *dev)
}
/* Entry point for transmitting a packet */
-static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
unsigned int freespace;
diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c
index d2caeb9ed..28d582c 100644
--- a/drivers/net/ethernet/socionext/netsec.c
+++ b/drivers/net/ethernet/socionext/netsec.c
@@ -274,6 +274,7 @@ struct netsec_priv {
struct clk *clk;
u32 msg_enable;
u32 freq;
+ u32 phy_addr;
bool rx_cksum_offload_flag;
};
@@ -1346,11 +1347,11 @@ static int netsec_netdev_stop(struct net_device *ndev)
netsec_uninit_pkt_dring(priv, NETSEC_RING_TX);
netsec_uninit_pkt_dring(priv, NETSEC_RING_RX);
- ret = netsec_reset_hardware(priv, false);
-
phy_stop(ndev->phydev);
phy_disconnect(ndev->phydev);
+ ret = netsec_reset_hardware(priv, false);
+
pm_runtime_put_sync(priv->dev);
return ret;
@@ -1360,6 +1361,7 @@ static int netsec_netdev_init(struct net_device *ndev)
{
struct netsec_priv *priv = netdev_priv(ndev);
int ret;
+ u16 data;
ret = netsec_alloc_dring(priv, NETSEC_RING_TX);
if (ret)
@@ -1369,6 +1371,11 @@ static int netsec_netdev_init(struct net_device *ndev)
if (ret)
goto err1;
+ /* set phy power down */
+ data = netsec_phy_read(priv->mii_bus, priv->phy_addr, MII_BMCR) |
+ BMCR_PDOWN;
+ netsec_phy_write(priv->mii_bus, priv->phy_addr, MII_BMCR, data);
+
ret = netsec_reset_hardware(priv, true);
if (ret)
goto err2;
@@ -1418,7 +1425,7 @@ static const struct net_device_ops netsec_netdev_ops = {
};
static int netsec_of_probe(struct platform_device *pdev,
- struct netsec_priv *priv)
+ struct netsec_priv *priv, u32 *phy_addr)
{
priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
if (!priv->phy_np) {
@@ -1426,6 +1433,8 @@ static int netsec_of_probe(struct platform_device *pdev,
return -EINVAL;
}
+ *phy_addr = of_mdio_parse_addr(&pdev->dev, priv->phy_np);
+
priv->clk = devm_clk_get(&pdev->dev, NULL); /* get by 'phy_ref_clk' */
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "phy_ref_clk not found\n");
@@ -1626,12 +1635,14 @@ static int netsec_probe(struct platform_device *pdev)
}
if (dev_of_node(&pdev->dev))
- ret = netsec_of_probe(pdev, priv);
+ ret = netsec_of_probe(pdev, priv, &phy_addr);
else
ret = netsec_acpi_probe(pdev, priv, &phy_addr);
if (ret)
goto free_ndev;
+ priv->phy_addr = phy_addr;
+
if (!priv->freq) {
dev_err(&pdev->dev, "missing PHY reference clock frequency\n");
ret = -ENODEV;
diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
index f27d67a..09d25b8 100644
--- a/drivers/net/ethernet/socionext/sni_ave.c
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -906,11 +906,11 @@ static void ave_rxfifo_reset(struct net_device *ndev)
/* assert reset */
writel(AVE_GRR_RXFFR, priv->base + AVE_GRR);
- usleep_range(40, 50);
+ udelay(50);
/* negate reset */
writel(0, priv->base + AVE_GRR);
- usleep_range(10, 20);
+ udelay(20);
/* negate interrupt status */
writel(AVE_GI_RXOVF, priv->base + AVE_GISR);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
index c597956..94b4625 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -118,6 +118,14 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
struct device *dev = dwmac->dev;
const char *parent_name, *mux_parent_names[MUX_CLK_NUM_PARENTS];
struct meson8b_dwmac_clk_configs *clk_configs;
+ static const struct clk_div_table div_table[] = {
+ { .div = 2, .val = 2, },
+ { .div = 3, .val = 3, },
+ { .div = 4, .val = 4, },
+ { .div = 5, .val = 5, },
+ { .div = 6, .val = 6, },
+ { .div = 7, .val = 7, },
+ };
clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
if (!clk_configs)
@@ -152,9 +160,9 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
- clk_configs->m250_div.flags = CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ALLOW_ZERO |
- CLK_DIVIDER_ROUND_CLOSEST;
+ clk_configs->m250_div.table = div_table;
+ clk_configs->m250_div.flags = CLK_DIVIDER_ALLOW_ZERO |
+ CLK_DIVIDER_ROUND_CLOSEST;
clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_name, 1,
&clk_divider_ops,
&clk_configs->m250_div.hw);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 79c9152..ef13a46 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -946,6 +946,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
/* default */
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII;
break;
case PHY_INTERFACE_MODE_RMII:
@@ -1199,7 +1202,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
dwmac_mux:
sun8i_dwmac_unset_syscon(gmac);
dwmac_exit:
- sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
+ stmmac_pltfr_remove(pdev);
return ret;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
index d07520f..fc1fa0f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
@@ -53,13 +53,15 @@ static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
* rate, which then uses the auto-reparenting feature of the
* clock driver, and enabling/disabling the clock.
*/
- if (gmac->interface == PHY_INTERFACE_MODE_RGMII) {
+ if (phy_interface_mode_is_rgmii(gmac->interface)) {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
clk_prepare_enable(gmac->tx_clk);
gmac->clk_enabled = 1;
} else {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
- clk_prepare(gmac->tx_clk);
+ ret = clk_prepare(gmac->tx_clk);
+ if (ret)
+ return ret;
}
return 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 0a80fa2..2097452 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -169,6 +169,8 @@
#define XGMAC_DMA_CH_RX_CONTROL(x) (0x00003108 + (0x80 * (x)))
#define XGMAC_RxPBL GENMASK(21, 16)
#define XGMAC_RxPBL_SHIFT 16
+#define XGMAC_RBSZ GENMASK(14, 1)
+#define XGMAC_RBSZ_SHIFT 1
#define XGMAC_RXST BIT(0)
#define XGMAC_DMA_CH_TxDESC_LADDR(x) (0x00003114 + (0x80 * (x)))
#define XGMAC_DMA_CH_RxDESC_LADDR(x) (0x0000311c + (0x80 * (x)))
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index 1c39305..27942c5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -379,7 +379,8 @@ static void dwxgmac2_set_bfsize(void __iomem *ioaddr, int bfsize, u32 chan)
u32 value;
value = readl(ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan));
- value |= bfsize << 1;
+ value &= ~XGMAC_RBSZ;
+ value |= bfsize << XGMAC_RBSZ_SHIFT;
writel(value, ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan));
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 014fe93..7ee0e46 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -54,7 +54,7 @@
#include "dwxgmac2.h"
#include "hwif.h"
-#define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES)
+#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
/* Module parameters */
@@ -2997,6 +2997,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * sizeof(*desc));
stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue);
+ stmmac_tx_timer_arm(priv, queue);
return NETDEV_TX_OK;
@@ -3210,6 +3211,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * sizeof(*desc));
stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue);
+ stmmac_tx_timer_arm(priv, queue);
return NETDEV_TX_OK;
@@ -3604,12 +3606,24 @@ static void stmmac_set_rx_mode(struct net_device *dev)
static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ int txfifosz = priv->plat->tx_fifo_size;
+
+ if (txfifosz == 0)
+ txfifosz = priv->dma_cap.tx_fifo_size;
+
+ txfifosz /= priv->plat->tx_queues_to_use;
if (netif_running(dev)) {
netdev_err(priv->dev, "must be stopped to change its MTU\n");
return -EBUSY;
}
+ new_mtu = STMMAC_ALIGN(new_mtu);
+
+ /* If condition true, FIFO is too small or MTU too large */
+ if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
+ return -EINVAL;
+
dev->mtu = new_mtu;
netdev_update_features(dev);
diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c
index d42f47f..644e42c 100644
--- a/drivers/net/ethernet/sun/ldmvsw.c
+++ b/drivers/net/ethernet/sun/ldmvsw.c
@@ -113,7 +113,7 @@ static u16 vsw_select_queue(struct net_device *dev, struct sk_buff *skb,
}
/* Wrappers to common functions */
-static int vsw_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t vsw_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
return sunvnet_start_xmit_common(skb, dev, vsw_tx_port_find);
}
diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c
index f047b27..720b7ac 100644
--- a/drivers/net/ethernet/sun/sunbmac.c
+++ b/drivers/net/ethernet/sun/sunbmac.c
@@ -950,7 +950,8 @@ static void bigmac_tx_timeout(struct net_device *dev)
}
/* Put a packet on the wire. */
-static int bigmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+bigmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bigmac *bp = netdev_priv(dev);
int len, entry;
diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c
index 7fe0d5e..1468fa0 100644
--- a/drivers/net/ethernet/sun/sunqe.c
+++ b/drivers/net/ethernet/sun/sunqe.c
@@ -570,7 +570,7 @@ static void qe_tx_timeout(struct net_device *dev)
}
/* Get a packet queued to go onto the wire. */
-static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t qe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct sunqe *qep = netdev_priv(dev);
struct sunqe_buffers *qbufs = qep->buffers;
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index 12539b3..5901728 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -247,7 +247,7 @@ static u16 vnet_select_queue(struct net_device *dev, struct sk_buff *skb,
}
/* Wrappers to common functions */
-static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
return sunvnet_start_xmit_common(skb, dev, vnet_tx_port_find);
}
diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c
index d8f4c3f..baa3088 100644
--- a/drivers/net/ethernet/sun/sunvnet_common.c
+++ b/drivers/net/ethernet/sun/sunvnet_common.c
@@ -1216,9 +1216,10 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies)
return skb;
}
-static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb,
- struct vnet_port *(*vnet_tx_port)
- (struct sk_buff *, struct net_device *))
+static netdev_tx_t
+vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb,
+ struct vnet_port *(*vnet_tx_port)
+ (struct sk_buff *, struct net_device *))
{
struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
@@ -1321,9 +1322,10 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb,
return NETDEV_TX_OK;
}
-int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
- struct vnet_port *(*vnet_tx_port)
- (struct sk_buff *, struct net_device *))
+netdev_tx_t
+sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
+ struct vnet_port *(*vnet_tx_port)
+ (struct sk_buff *, struct net_device *))
{
struct vnet_port *port = NULL;
struct vio_dring_state *dr;
diff --git a/drivers/net/ethernet/sun/sunvnet_common.h b/drivers/net/ethernet/sun/sunvnet_common.h
index 1ea0b01..2b808d2 100644
--- a/drivers/net/ethernet/sun/sunvnet_common.h
+++ b/drivers/net/ethernet/sun/sunvnet_common.h
@@ -136,9 +136,10 @@ int sunvnet_close_common(struct net_device *dev);
void sunvnet_set_rx_mode_common(struct net_device *dev, struct vnet *vp);
int sunvnet_set_mac_addr_common(struct net_device *dev, void *p);
void sunvnet_tx_timeout_common(struct net_device *dev);
-int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
- struct vnet_port *(*vnet_tx_port)
- (struct sk_buff *, struct net_device *));
+netdev_tx_t
+sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
+ struct vnet_port *(*vnet_tx_port)
+ (struct sk_buff *, struct net_device *));
#ifdef CONFIG_NET_POLL_CONTROLLER
void sunvnet_poll_controller_common(struct net_device *dev, struct vnet *vp);
#endif
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 1afed85..0b7a3eb 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -642,6 +642,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
/* Clear all mcast from ALE */
cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1);
+ __dev_mc_unsync(ndev, NULL);
/* Flood All Unicast Packets to Host port */
cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
@@ -953,8 +954,8 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
{
struct cpsw_common *cpsw = dev_id;
- cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX);
writel(0, &cpsw->wr_regs->rx_en);
+ cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX);
if (cpsw->quirk_irq) {
disable_irq_nosync(cpsw->irqs_table[0]);
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index 5766225..c245629 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -793,6 +793,7 @@ EXPORT_SYMBOL_GPL(cpsw_ale_start);
void cpsw_ale_stop(struct cpsw_ale *ale)
{
del_timer_sync(&ale->timer);
+ cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
}
EXPORT_SYMBOL_GPL(cpsw_ale_stop);
@@ -877,6 +878,7 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS;
}
+ cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
return ale;
}
EXPORT_SYMBOL_GPL(cpsw_ale_create);
diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index b96b93c..d754381 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -119,9 +119,7 @@ static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
if (time_after(jiffies, skb_cb->tmo)) {
/* timeout any expired skbs over 1s */
- dev_dbg(cpts->dev,
- "expiring tx timestamp mtype %u seqid %04x\n",
- mtype, seqid);
+ dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
__skb_unlink(skb, &cpts->txq);
dev_consume_skb_any(skb);
}
@@ -572,7 +570,9 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
return ERR_CAST(cpts->refclk);
}
- clk_prepare(cpts->refclk);
+ ret = clk_prepare(cpts->refclk);
+ if (ret)
+ return ERR_PTR(ret);
cpts->cc.read = cpts_systim_read;
cpts->cc.mask = CLOCKSOURCE_MASK(32);
diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
index 88d74ae..75237c8 100644
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
@@ -845,9 +845,9 @@ static int gelic_card_kick_txdma(struct gelic_card *card,
* @skb: packet to send out
* @netdev: interface device structure
*
- * returns 0 on success, <0 on failure
+ * returns NETDEV_TX_OK on success, NETDEV_TX_BUSY on failure
*/
-int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
+netdev_tx_t gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct gelic_card *card = netdev_card(netdev);
struct gelic_descr *descr;
diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
index 003d045..fbbf9b5 100644
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.h
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
@@ -370,7 +370,7 @@ void gelic_card_up(struct gelic_card *card);
void gelic_card_down(struct gelic_card *card);
int gelic_net_open(struct net_device *netdev);
int gelic_net_stop(struct net_device *netdev);
-int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
+netdev_tx_t gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
void gelic_net_set_multi(struct net_device *netdev);
void gelic_net_tx_timeout(struct net_device *netdev);
int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card);
diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c
index d925b82..2341726 100644
--- a/drivers/net/ethernet/toshiba/spider_net.c
+++ b/drivers/net/ethernet/toshiba/spider_net.c
@@ -880,9 +880,9 @@ spider_net_kick_tx_dma(struct spider_net_card *card)
* @skb: packet to send out
* @netdev: interface device structure
*
- * returns 0 on success, !0 on failure
+ * returns NETDEV_TX_OK on success, NETDEV_TX_BUSY on failure
*/
-static int
+static netdev_tx_t
spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
int cnt;
diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c
index 9146068..03afc4d 100644
--- a/drivers/net/ethernet/toshiba/tc35815.c
+++ b/drivers/net/ethernet/toshiba/tc35815.c
@@ -474,7 +474,8 @@ static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_
/* Index to functions, as function prototypes. */
static int tc35815_open(struct net_device *dev);
-static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t tc35815_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
static irqreturn_t tc35815_interrupt(int irq, void *dev_id);
static int tc35815_rx(struct net_device *dev, int limit);
static int tc35815_poll(struct napi_struct *napi, int budget);
@@ -1248,7 +1249,8 @@ tc35815_open(struct net_device *dev)
* invariant will hold if you make sure that the netif_*_queue()
* calls are done at the proper times.
*/
-static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+tc35815_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct tc35815_local *lp = netdev_priv(dev);
struct TxFD *txfd;
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index 60abc92..2241f98 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -674,7 +674,8 @@ static inline int temac_check_tx_bd_space(struct temac_local *lp, int num_frag)
return 0;
}
-static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t
+temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct temac_local *lp = netdev_priv(ndev);
struct cdmac_bd *cur_p;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 66b30eb..2876426 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -657,7 +657,8 @@ static inline int axienet_check_tx_bd_space(struct axienet_local *lp,
* start the transmission. Additionally if checksum offloading is supported,
* it populates AXI Stream Control fields with appropriate values.
*/
-static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t
+axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
u32 ii;
u32 num_frag;
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 42f1f51..c77c81e 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -1020,9 +1020,10 @@ static int xemaclite_close(struct net_device *dev)
* deferred and the Tx queue is stopped so that the deferred socket buffer can
* be transmitted when the Emaclite device is free to transmit data.
*
- * Return: 0, always.
+ * Return: NETDEV_TX_OK, always.
*/
-static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
+static netdev_tx_t
+xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
struct sk_buff *new_skb;
@@ -1044,7 +1045,7 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
/* Take the time stamp now, since we can't do this in an ISR. */
skb_tx_timestamp(new_skb);
spin_unlock_irqrestore(&lp->reset_lock, flags);
- return 0;
+ return NETDEV_TX_OK;
}
spin_unlock_irqrestore(&lp->reset_lock, flags);
@@ -1053,7 +1054,7 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
dev->stats.tx_bytes += len;
dev_consume_skb_any(new_skb);
- return 0;
+ return NETDEV_TX_OK;
}
/**
diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c
index d3eae12..1979f8f 100644
--- a/drivers/net/fjes/fjes_main.c
+++ b/drivers/net/fjes/fjes_main.c
@@ -181,6 +181,9 @@ static int fjes_acpi_add(struct acpi_device *device)
/* create platform_device */
plat_dev = platform_device_register_simple(DRV_NAME, 0, fjes_resource,
ARRAY_SIZE(fjes_resource));
+ if (IS_ERR(plat_dev))
+ return PTR_ERR(plat_dev);
+
device->driver_data = plat_dev;
return 0;
@@ -1252,8 +1255,17 @@ static int fjes_probe(struct platform_device *plat_dev)
adapter->open_guard = false;
adapter->txrx_wq = alloc_workqueue(DRV_NAME "/txrx", WQ_MEM_RECLAIM, 0);
+ if (unlikely(!adapter->txrx_wq)) {
+ err = -ENOMEM;
+ goto err_free_netdev;
+ }
+
adapter->control_wq = alloc_workqueue(DRV_NAME "/control",
WQ_MEM_RECLAIM, 0);
+ if (unlikely(!adapter->control_wq)) {
+ err = -ENOMEM;
+ goto err_free_txrx_wq;
+ }
INIT_WORK(&adapter->tx_stall_task, fjes_tx_stall_task);
INIT_WORK(&adapter->raise_intr_rxdata_task,
@@ -1270,7 +1282,7 @@ static int fjes_probe(struct platform_device *plat_dev)
hw->hw_res.irq = platform_get_irq(plat_dev, 0);
err = fjes_hw_init(&adapter->hw);
if (err)
- goto err_free_netdev;
+ goto err_free_control_wq;
/* setup MAC address (02:00:00:00:00:[epid])*/
netdev->dev_addr[0] = 2;
@@ -1292,6 +1304,10 @@ static int fjes_probe(struct platform_device *plat_dev)
err_hw_exit:
fjes_hw_exit(&adapter->hw);
+err_free_control_wq:
+ destroy_workqueue(adapter->control_wq);
+err_free_txrx_wq:
+ destroy_workqueue(adapter->txrx_wq);
err_free_netdev:
free_netdev(netdev);
err_out:
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index d178d5b..6571cac 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -42,7 +42,6 @@ struct pdp_ctx {
struct hlist_node hlist_addr;
union {
- u64 tid;
struct {
u64 tid;
u16 flow;
@@ -545,7 +544,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
mtu = dst_mtu(&rt->dst);
}
- rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu);
+ rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, false);
if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) &&
mtu < ntohs(iph->tot_len)) {
@@ -645,9 +644,16 @@ static void gtp_link_setup(struct net_device *dev)
}
static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
-static void gtp_hashtable_free(struct gtp_dev *gtp);
static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]);
+static void gtp_destructor(struct net_device *dev)
+{
+ struct gtp_dev *gtp = netdev_priv(dev);
+
+ kfree(gtp->addr_hash);
+ kfree(gtp->tid_hash);
+}
+
static int gtp_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
@@ -665,10 +671,13 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
if (err < 0)
return err;
- if (!data[IFLA_GTP_PDP_HASHSIZE])
+ if (!data[IFLA_GTP_PDP_HASHSIZE]) {
hashsize = 1024;
- else
+ } else {
hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]);
+ if (!hashsize)
+ hashsize = 1024;
+ }
err = gtp_hashtable_new(gtp, hashsize);
if (err < 0)
@@ -682,13 +691,15 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
gn = net_generic(dev_net(dev), gtp_net_id);
list_add_rcu(>p->list, &gn->gtp_dev_list);
+ dev->priv_destructor = gtp_destructor;
netdev_dbg(dev, "registered new GTP interface\n");
return 0;
out_hashtable:
- gtp_hashtable_free(gtp);
+ kfree(gtp->addr_hash);
+ kfree(gtp->tid_hash);
out_encap:
gtp_encap_disable(gtp);
return err;
@@ -697,9 +708,14 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
static void gtp_dellink(struct net_device *dev, struct list_head *head)
{
struct gtp_dev *gtp = netdev_priv(dev);
+ struct pdp_ctx *pctx;
+ int i;
+
+ for (i = 0; i < gtp->hash_size; i++)
+ hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid)
+ pdp_context_delete(pctx);
gtp_encap_disable(gtp);
- gtp_hashtable_free(gtp);
list_del_rcu(>p->list);
unregister_netdevice_queue(dev, head);
}
@@ -777,20 +793,6 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
return -ENOMEM;
}
-static void gtp_hashtable_free(struct gtp_dev *gtp)
-{
- struct pdp_ctx *pctx;
- int i;
-
- for (i = 0; i < gtp->hash_size; i++)
- hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid)
- pdp_context_delete(pctx);
-
- synchronize_rcu();
- kfree(gtp->addr_hash);
- kfree(gtp->tid_hash);
-}
-
static struct sock *gtp_encap_enable_socket(int fd, int type,
struct gtp_dev *gtp)
{
@@ -816,7 +818,7 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
lock_sock(sock->sk);
if (sock->sk->sk_user_data) {
sk = ERR_PTR(-EBUSY);
- goto out_sock;
+ goto out_rel_sock;
}
sk = sock->sk;
@@ -829,8 +831,9 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg);
-out_sock:
+out_rel_sock:
release_sock(sock->sk);
+out_sock:
sockfd_put(sock);
return sk;
}
@@ -931,24 +934,31 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
}
}
-static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
- struct genl_info *info)
+static int gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
+ struct genl_info *info)
{
+ struct pdp_ctx *pctx, *pctx_tid = NULL;
struct net_device *dev = gtp->dev;
u32 hash_ms, hash_tid = 0;
- struct pdp_ctx *pctx;
+ unsigned int version;
bool found = false;
__be32 ms_addr;
ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;
+ version = nla_get_u32(info->attrs[GTPA_VERSION]);
- hlist_for_each_entry_rcu(pctx, >p->addr_hash[hash_ms], hlist_addr) {
- if (pctx->ms_addr_ip4.s_addr == ms_addr) {
- found = true;
- break;
- }
- }
+ pctx = ipv4_pdp_find(gtp, ms_addr);
+ if (pctx)
+ found = true;
+ if (version == GTP_V0)
+ pctx_tid = gtp0_pdp_find(gtp,
+ nla_get_u64(info->attrs[GTPA_TID]));
+ else if (version == GTP_V1)
+ pctx_tid = gtp1_pdp_find(gtp,
+ nla_get_u32(info->attrs[GTPA_I_TEI]));
+ if (pctx_tid)
+ found = true;
if (found) {
if (info->nlhdr->nlmsg_flags & NLM_F_EXCL)
@@ -956,6 +966,11 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
+ if (pctx && pctx_tid)
+ return -EEXIST;
+ if (!pctx)
+ pctx = pctx_tid;
+
ipv4_pdp_fill(pctx, info);
if (pctx->gtp_version == GTP_V0)
@@ -1079,7 +1094,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
goto out_unlock;
}
- err = ipv4_pdp_add(gtp, sk, info);
+ err = gtp_pdp_add(gtp, sk, info);
out_unlock:
rcu_read_unlock();
@@ -1237,43 +1252,46 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct gtp_dev *last_gtp = (struct gtp_dev *)cb->args[2], *gtp;
+ int i, j, bucket = cb->args[0], skip = cb->args[1];
struct net *net = sock_net(skb->sk);
- struct gtp_net *gn = net_generic(net, gtp_net_id);
- unsigned long tid = cb->args[1];
- int i, k = cb->args[0], ret;
struct pdp_ctx *pctx;
+ struct gtp_net *gn;
+
+ gn = net_generic(net, gtp_net_id);
if (cb->args[4])
return 0;
+ rcu_read_lock();
list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) {
if (last_gtp && last_gtp != gtp)
continue;
else
last_gtp = NULL;
- for (i = k; i < gtp->hash_size; i++) {
- hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) {
- if (tid && tid != pctx->u.tid)
- continue;
- else
- tid = 0;
-
- ret = gtp_genl_fill_info(skb,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- cb->nlh->nlmsg_type, pctx);
- if (ret < 0) {
+ for (i = bucket; i < gtp->hash_size; i++) {
+ j = 0;
+ hlist_for_each_entry_rcu(pctx, >p->tid_hash[i],
+ hlist_tid) {
+ if (j >= skip &&
+ gtp_genl_fill_info(skb,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ cb->nlh->nlmsg_type, pctx)) {
cb->args[0] = i;
- cb->args[1] = pctx->u.tid;
+ cb->args[1] = j;
cb->args[2] = (unsigned long)gtp;
goto out;
}
+ j++;
}
+ skip = 0;
}
+ bucket = 0;
}
cb->args[4] = 1;
out:
+ rcu_read_unlock();
return skb->len;
}
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 54e63ec..8c636c4 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -654,10 +654,10 @@ static void sixpack_close(struct tty_struct *tty)
{
struct sixpack *sp;
- write_lock_bh(&disc_data_lock);
+ write_lock_irq(&disc_data_lock);
sp = tty->disc_data;
tty->disc_data = NULL;
- write_unlock_bh(&disc_data_lock);
+ write_unlock_irq(&disc_data_lock);
if (!sp)
return;
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index 13e4c1e..3b14e6e 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -783,10 +783,10 @@ static void mkiss_close(struct tty_struct *tty)
{
struct mkiss *ax;
- write_lock_bh(&disc_data_lock);
+ write_lock_irq(&disc_data_lock);
ax = tty->disc_data;
tty->disc_data = NULL;
- write_unlock_bh(&disc_data_lock);
+ write_unlock_irq(&disc_data_lock);
if (!ax)
return;
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 31d8d83..50709c7 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -181,7 +181,6 @@ struct rndis_device {
u8 hw_mac_adr[ETH_ALEN];
u8 rss_key[NETVSC_HASH_KEYLEN];
- u16 rx_table[ITAB_NUM];
};
@@ -933,6 +932,8 @@ struct net_device_context {
u32 tx_table[VRSS_SEND_TAB_SIZE];
+ u16 rx_table[ITAB_NUM];
+
/* Ethtool settings */
u8 duplex;
u32 speed;
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 6f6c0db..1f9f7fc 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -993,7 +993,7 @@ static int netvsc_attach(struct net_device *ndev,
if (netif_running(ndev)) {
ret = rndis_filter_open(nvdev);
if (ret)
- return ret;
+ goto err;
rdev = nvdev->extension;
if (!rdev->link_state)
@@ -1001,6 +1001,13 @@ static int netvsc_attach(struct net_device *ndev,
}
return 0;
+
+err:
+ netif_device_detach(ndev);
+
+ rndis_filter_device_remove(hdev, nvdev);
+
+ return ret;
}
static int netvsc_set_channels(struct net_device *net,
@@ -1681,7 +1688,7 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
rndis_dev = ndev->extension;
if (indir) {
for (i = 0; i < ITAB_NUM; i++)
- indir[i] = rndis_dev->rx_table[i];
+ indir[i] = ndc->rx_table[i];
}
if (key)
@@ -1711,7 +1718,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir,
return -EINVAL;
for (i = 0; i < ITAB_NUM; i++)
- rndis_dev->rx_table[i] = indir[i];
+ ndc->rx_table[i] = indir[i];
}
if (!key) {
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 53c6039..f47e36a 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -719,6 +719,7 @@ static int rndis_set_rss_param_msg(struct rndis_device *rdev,
const u8 *rss_key, u16 flag)
{
struct net_device *ndev = rdev->ndev;
+ struct net_device_context *ndc = netdev_priv(ndev);
struct rndis_request *request;
struct rndis_set_request *set;
struct rndis_set_complete *set_complete;
@@ -758,7 +759,7 @@ static int rndis_set_rss_param_msg(struct rndis_device *rdev,
/* Set indirection table entries */
itab = (u32 *)(rssp + 1);
for (i = 0; i < ITAB_NUM; i++)
- itab[i] = rdev->rx_table[i];
+ itab[i] = ndc->rx_table[i];
/* Set hask key values */
keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset);
@@ -1244,6 +1245,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
struct netvsc_device_info *device_info)
{
struct net_device *net = hv_get_drvdata(dev);
+ struct net_device_context *ndc = netdev_priv(net);
struct netvsc_device *net_device;
struct rndis_device *rndis_device;
struct ndis_recv_scale_cap rsscap;
@@ -1330,9 +1332,11 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
/* We will use the given number of channels if available. */
net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
- for (i = 0; i < ITAB_NUM; i++)
- rndis_device->rx_table[i] = ethtool_rxfh_indir_default(
+ if (!netif_is_rxfh_configured(net)) {
+ for (i = 0; i < ITAB_NUM; i++)
+ ndc->rx_table[i] = ethtool_rxfh_indir_default(
i, net_device->num_chn);
+ }
atomic_set(&net_device->open_chn, 1);
vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 0dc92d2..10a8ef2 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -2813,9 +2813,6 @@ static int macsec_dev_open(struct net_device *dev)
struct net_device *real_dev = macsec->real_dev;
int err;
- if (!(real_dev->flags & IFF_UP))
- return -ENETDOWN;
-
err = dev_uc_add(real_dev, dev->dev_addr);
if (err < 0)
return err;
@@ -3008,12 +3005,10 @@ static const struct nla_policy macsec_rtnl_policy[IFLA_MACSEC_MAX + 1] = {
static void macsec_free_netdev(struct net_device *dev)
{
struct macsec_dev *macsec = macsec_priv(dev);
- struct net_device *real_dev = macsec->real_dev;
free_percpu(macsec->stats);
free_percpu(macsec->secy.tx_sc.stats);
- dev_put(real_dev);
}
static void macsec_setup(struct net_device *dev)
@@ -3268,8 +3263,6 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
if (err < 0)
return err;
- dev_hold(real_dev);
-
macsec->nest_level = dev_get_nest_level(real_dev) + 1;
netdev_lockdep_set_classes(dev);
lockdep_set_class_and_subclass(&dev->addr_list_lock,
@@ -3309,6 +3302,9 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
if (err < 0)
goto del_dev;
+ netif_stacked_transfer_operstate(real_dev, dev);
+ linkwatch_fire_event(dev);
+
macsec_generation++;
return 0;
@@ -3493,6 +3489,20 @@ static int macsec_notify(struct notifier_block *this, unsigned long event,
return NOTIFY_DONE;
switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_UP:
+ case NETDEV_CHANGE: {
+ struct macsec_dev *m, *n;
+ struct macsec_rxh_data *rxd;
+
+ rxd = macsec_data_rtnl(real_dev);
+ list_for_each_entry_safe(m, n, &rxd->secys, secys) {
+ struct net_device *dev = m->secy.netdev;
+
+ netif_stacked_transfer_operstate(real_dev, dev);
+ }
+ break;
+ }
case NETDEV_UNREGISTER: {
struct macsec_dev *m, *n;
struct macsec_rxh_data *rxd;
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 6372cdc..41c0a3b 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -263,7 +263,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
struct net_device *src,
enum macvlan_mode mode)
{
- const struct ethhdr *eth = eth_hdr(skb);
+ const struct ethhdr *eth = skb_eth_hdr(skb);
const struct macvlan_dev *vlan;
struct sk_buff *nskb;
unsigned int i;
@@ -363,10 +363,11 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port,
}
spin_unlock(&port->bc_queue.lock);
+ schedule_work(&port->bc_work);
+
if (err)
goto free_nskb;
- schedule_work(&port->bc_work);
return;
free_nskb:
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index 5a749dc..beeb7eb 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -765,8 +765,10 @@ struct failover *net_failover_create(struct net_device *standby_dev)
netif_carrier_off(failover_dev);
failover = failover_register(failover_dev, &net_failover_ops);
- if (IS_ERR(failover))
+ if (IS_ERR(failover)) {
+ err = PTR_ERR(failover);
goto err_failover_register;
+ }
return failover;
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index b12023b..df8d49a 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -236,7 +236,7 @@ static void ntb_netdev_tx_timer(struct timer_list *t)
struct net_device *ndev = dev->ndev;
if (ntb_transport_tx_free_entry(dev->qp) < tx_stop) {
- mod_timer(&dev->tx_timer, jiffies + msecs_to_jiffies(tx_time));
+ mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time));
} else {
/* Make sure anybody stopping the queue after this sees the new
* value of ntb_transport_tx_free_entry()
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index b2b6307..acaf072 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -643,6 +643,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.name = _name, \
.features = PHY_BASIC_FEATURES, \
.flags = PHY_IS_INTERNAL, \
+ .soft_reset = genphy_soft_reset, \
.config_init = bcm7xxx_config_init, \
.suspend = bcm7xxx_suspend, \
.resume = bcm7xxx_config_init, \
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index e4bf9e7..879096d 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -33,10 +33,18 @@
/* Extended Registers */
#define DP83867_CFG4 0x0031
+#define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6))
+#define DP83867_CFG4_SGMII_ANEG_TIMER_11MS (3 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_800US (2 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_2US (1 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_16MS (0 << 5)
+
#define DP83867_RGMIICTL 0x0032
#define DP83867_STRAP_STS1 0x006E
#define DP83867_RGMIIDCTL 0x0086
#define DP83867_IO_MUX_CFG 0x0170
+#define DP83867_10M_SGMII_CFG 0x016F
+#define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7)
#define DP83867_SW_RESET BIT(15)
#define DP83867_SW_RESTART BIT(14)
@@ -78,6 +86,10 @@
#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8)
#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
+/* CFG3 bits */
+#define DP83867_CFG3_INT_OE BIT(7)
+#define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9)
+
/* CFG4 bits */
#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
@@ -294,13 +306,43 @@ static int dp83867_config_init(struct phy_device *phydev)
}
}
- /* Enable Interrupt output INT_OE in CFG3 register */
- if (phy_interrupt_is_valid(phydev)) {
- val = phy_read(phydev, DP83867_CFG3);
- val |= BIT(7);
- phy_write(phydev, DP83867_CFG3, val);
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* For support SPEED_10 in SGMII mode
+ * DP83867_10M_SGMII_RATE_ADAPT bit
+ * has to be cleared by software. That
+ * does not affect SPEED_100 and
+ * SPEED_1000.
+ */
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_10M_SGMII_CFG);
+ val &= ~DP83867_10M_SGMII_RATE_ADAPT_MASK;
+ ret = phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_10M_SGMII_CFG, val);
+
+ if (ret)
+ return ret;
+
+ /* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5
+ * are 01). That is not enough to finalize autoneg on some
+ * devices. Increase this timer duration to maximum 16ms.
+ */
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
+ val &= ~DP83867_CFG4_SGMII_ANEG_MASK;
+ val |= DP83867_CFG4_SGMII_ANEG_TIMER_16MS;
+ ret = phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
+
+ if (ret)
+ return ret;
}
+ val = phy_read(phydev, DP83867_CFG3);
+ /* Enable Interrupt output INT_OE in CFG3 register */
+ if (phy_interrupt_is_valid(phydev))
+ val |= DP83867_CFG3_INT_OE;
+
+ val |= DP83867_CFG3_ROBUST_AUTO_MDIX;
+ phy_write(phydev, DP83867_CFG3, val);
+
if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP)
dp83867_config_port_mirroring(phydev);
diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c
index 8d37066..df75efa 100644
--- a/drivers/net/phy/mdio-bcm-unimac.c
+++ b/drivers/net/phy/mdio-bcm-unimac.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -45,6 +46,8 @@ struct unimac_mdio_priv {
void __iomem *base;
int (*wait_func) (void *wait_func_data);
void *wait_func_data;
+ struct clk *clk;
+ u32 clk_freq;
};
static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset)
@@ -189,6 +192,35 @@ static int unimac_mdio_reset(struct mii_bus *bus)
return 0;
}
+static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
+{
+ unsigned long rate;
+ u32 reg, div;
+
+ /* Keep the hardware default values */
+ if (!priv->clk_freq)
+ return;
+
+ if (!priv->clk)
+ rate = 250000000;
+ else
+ rate = clk_get_rate(priv->clk);
+
+ div = (rate / (2 * priv->clk_freq)) - 1;
+ if (div & ~MDIO_CLK_DIV_MASK) {
+ pr_warn("Incorrect MDIO clock frequency, ignoring\n");
+ return;
+ }
+
+ /* The MDIO clock is the reference clock (typicaly 250Mhz) divided by
+ * 2 x (MDIO_CLK_DIV + 1)
+ */
+ reg = unimac_mdio_readl(priv, MDIO_CFG);
+ reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
+ reg |= div << MDIO_CLK_DIV_SHIFT;
+ unimac_mdio_writel(priv, reg, MDIO_CFG);
+}
+
static int unimac_mdio_probe(struct platform_device *pdev)
{
struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
@@ -217,9 +249,26 @@ static int unimac_mdio_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (PTR_ERR(priv->clk) == -EPROBE_DEFER)
+ return PTR_ERR(priv->clk);
+ else
+ priv->clk = NULL;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
+ priv->clk_freq = 0;
+
+ unimac_mdio_clk_set(priv);
+
priv->mii_bus = mdiobus_alloc();
- if (!priv->mii_bus)
- return -ENOMEM;
+ if (!priv->mii_bus) {
+ ret = -ENOMEM;
+ goto out_clk_disable;
+ }
bus = priv->mii_bus;
bus->priv = priv;
@@ -253,6 +302,8 @@ static int unimac_mdio_probe(struct platform_device *pdev)
out_mdio_free:
mdiobus_free(bus);
+out_clk_disable:
+ clk_disable_unprepare(priv->clk);
return ret;
}
@@ -262,10 +313,37 @@ static int unimac_mdio_remove(struct platform_device *pdev)
mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus);
+ clk_disable_unprepare(priv->clk);
return 0;
}
+static int __maybe_unused unimac_mdio_suspend(struct device *d)
+{
+ struct unimac_mdio_priv *priv = dev_get_drvdata(d);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static int __maybe_unused unimac_mdio_resume(struct device *d)
+{
+ struct unimac_mdio_priv *priv = dev_get_drvdata(d);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ unimac_mdio_clk_set(priv);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
+ unimac_mdio_suspend, unimac_mdio_resume);
+
static const struct of_device_id unimac_mdio_ids[] = {
{ .compatible = "brcm,genet-mdio-v5", },
{ .compatible = "brcm,genet-mdio-v4", },
@@ -281,6 +359,7 @@ static struct platform_driver unimac_mdio_driver = {
.driver = {
.name = UNIMAC_MDIO_DRV_NAME,
.of_match_table = unimac_mdio_ids,
+ .pm = &unimac_mdio_pm_ops,
},
.probe = unimac_mdio_probe,
.remove = unimac_mdio_remove,
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index 84ca9ff..36647b7 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -111,8 +111,8 @@ struct vsc8531_private {
#ifdef CONFIG_OF_MDIO
struct vsc8531_edge_rate_table {
- u16 vddmac;
- u8 slowdown[8];
+ u32 vddmac;
+ u32 slowdown[8];
};
static const struct vsc8531_edge_rate_table edge_table[] = {
@@ -375,8 +375,7 @@ static void vsc85xx_wol_get(struct phy_device *phydev,
#ifdef CONFIG_OF_MDIO
static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
{
- u8 sd;
- u16 vdd;
+ u32 vdd, sd;
int rc, i, j;
struct device *dev = &phydev->mdio.dev;
struct device_node *of_node = dev->of_node;
@@ -385,11 +384,11 @@ static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
if (!of_node)
return -ENODEV;
- rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd);
+ rc = of_property_read_u32(of_node, "vsc8531,vddmac", &vdd);
if (rc != 0)
vdd = MSCC_VDDMAC_3300;
- rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd);
+ rc = of_property_read_u32(of_node, "vsc8531,edge-slowdown", &sd);
if (rc != 0)
sd = 0;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 6144146..43c4f35 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -420,8 +420,8 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
mdiodev->device_free = phy_mdio_device_free;
mdiodev->device_remove = phy_mdio_device_remove;
- dev->speed = 0;
- dev->duplex = -1;
+ dev->speed = SPEED_UNKNOWN;
+ dev->duplex = DUPLEX_UNKNOWN;
dev->pause = 0;
dev->asym_pause = 0;
dev->link = 0;
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index b008266..77207f9 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -855,6 +855,8 @@ static int slip_open(struct tty_struct *tty)
sl->tty = NULL;
tty->disc_data = NULL;
clear_bit(SLF_INUSE, &sl->flags);
+ sl_free_netdev(sl->dev);
+ free_netdev(sl->dev);
err_exit:
rtnl_unlock();
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 37edf7a..dda409c 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -319,8 +319,8 @@ static void tun_napi_init(struct tun_struct *tun, struct tun_file *tfile,
tfile->napi_enabled = napi_en;
tfile->napi_frags_enabled = napi_en && napi_frags;
if (napi_en) {
- netif_napi_add(tun->dev, &tfile->napi, tun_napi_poll,
- NAPI_POLL_WEIGHT);
+ netif_tx_napi_add(tun->dev, &tfile->napi, tun_napi_poll,
+ NAPI_POLL_WEIGHT);
napi_enable(&tfile->napi);
}
}
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 501576f..914cac5 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -208,7 +208,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
/* Get the MAC address */
ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
- if (ret < 0) {
+ if (ret < ETH_ALEN) {
netdev_err(dev->net, "Failed to read MAC address: %d\n", ret);
goto free;
}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 85fba64..c3cf9ae6 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -800,6 +800,13 @@ static const struct usb_device_id products[] = {
.driver_info = 0,
},
+/* ThinkPad USB-C Dock Gen 2 (based on Realtek RTL8153) */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0xa387, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* NVIDIA Tegra USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
{
USB_DEVICE_AND_INTERFACE_INFO(NVIDIA_VENDOR_ID, 0x09ff, USB_CLASS_COMM,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index f53e3e4..1f57a6a 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -578,8 +578,8 @@ static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
/* read current mtu value from device */
err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
- 0, iface_no, &max_datagram_size, 2);
- if (err < 0) {
+ 0, iface_no, &max_datagram_size, sizeof(max_datagram_size));
+ if (err != sizeof(max_datagram_size)) {
dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
goto out;
}
@@ -590,7 +590,7 @@ static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
- 0, iface_no, &max_datagram_size, 2);
+ 0, iface_no, &max_datagram_size, sizeof(max_datagram_size));
if (err < 0)
dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 267978e..57236d6 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -510,7 +510,7 @@ static int lan78xx_read_stats(struct lan78xx_net *dev,
}
} else {
netdev_warn(dev->net,
- "Failed to read stat ret = 0x%x", ret);
+ "Failed to read stat ret = %d", ret);
}
kfree(stats);
@@ -1264,8 +1264,11 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
- if (dev->domain_data.phyirq > 0)
+ if (dev->domain_data.phyirq > 0) {
+ local_irq_disable();
generic_handle_irq(dev->domain_data.phyirq);
+ local_irq_enable();
+ }
} else
netdev_warn(dev->net,
"unexpected interrupt: 0x%08x\n", intdata);
@@ -1806,6 +1809,7 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
dev->mdiobus->read = lan78xx_mdiobus_read;
dev->mdiobus->write = lan78xx_mdiobus_write;
dev->mdiobus->name = "lan78xx-mdiobus";
+ dev->mdiobus->parent = &dev->udev->dev;
snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
@@ -2717,11 +2721,6 @@ static int lan78xx_stop(struct net_device *net)
return 0;
}
-static int lan78xx_linearize(struct sk_buff *skb)
-{
- return skb_linearize(skb);
-}
-
static struct sk_buff *lan78xx_tx_prep(struct lan78xx_net *dev,
struct sk_buff *skb, gfp_t flags)
{
@@ -2732,8 +2731,10 @@ static struct sk_buff *lan78xx_tx_prep(struct lan78xx_net *dev,
return NULL;
}
- if (lan78xx_linearize(skb) < 0)
+ if (skb_linearize(skb)) {
+ dev_kfree_skb_any(skb);
return NULL;
+ }
tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN_MASK_) | TX_CMD_A_FCS_;
@@ -3785,10 +3786,14 @@ static int lan78xx_probe(struct usb_interface *intf,
/* driver requires remote-wakeup capability during autosuspend. */
intf->needs_remote_wakeup = 1;
+ ret = lan78xx_phy_init(dev);
+ if (ret < 0)
+ goto out4;
+
ret = register_netdev(netdev);
if (ret != 0) {
netif_err(dev, probe, netdev, "couldn't register the device\n");
- goto out4;
+ goto out5;
}
usb_set_intfdata(intf, dev);
@@ -3801,14 +3806,10 @@ static int lan78xx_probe(struct usb_interface *intf,
pm_runtime_set_autosuspend_delay(&udev->dev,
DEFAULT_AUTOSUSPEND_DELAY);
- ret = lan78xx_phy_init(dev);
- if (ret < 0)
- goto out5;
-
return 0;
out5:
- unregister_netdev(netdev);
+ phy_disconnect(netdev->phydev);
out4:
usb_free_urb(dev->urb_intr);
out3:
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 6f517e6..b55fd76 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1297,6 +1297,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x413c, 0x81d7, 0)}, /* Dell Wireless 5821e */
+ {QMI_FIXED_INTF(0x413c, 0x81e0, 0)}, /* Dell Wireless 5821e with eSIM support*/
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
{QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
@@ -1305,6 +1306,8 @@ static const struct usb_device_id products[] = {
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */
{QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */
+ {QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */
+ {QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index a291e5f..91d47a7 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -5339,6 +5339,7 @@ static const struct usb_device_id rtl8152_table[] = {
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x720c)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7214)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0xa387)},
{REALTEK_USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041)},
{REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA, 0x09ff)},
{REALTEK_USB_DEVICE(VENDOR_ID_TPLINK, 0x0601)},
diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c
index 35f39f2..8f8c9ed 100644
--- a/drivers/net/usb/sr9800.c
+++ b/drivers/net/usb/sr9800.c
@@ -336,7 +336,7 @@ static void sr_set_multicast(struct net_device *net)
static int sr_mdio_read(struct net_device *net, int phy_id, int loc)
{
struct usbnet *dev = netdev_priv(net);
- __le16 res;
+ __le16 res = 0;
mutex_lock(&dev->phy_mutex);
sr_set_sw_mii(dev);
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 9f89508..7f5ee6b 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -993,24 +993,23 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
struct sk_buff *skb)
{
int orig_iif = skb->skb_iif;
- bool need_strict;
+ bool need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
+ bool is_ndisc = ipv6_ndisc_frame(skb);
- /* loopback traffic; do not push through packet taps again.
- * Reset pkt_type for upper layers to process skb
+ /* loopback, multicast & non-ND link-local traffic; do not push through
+ * packet taps again. Reset pkt_type for upper layers to process skb
*/
- if (skb->pkt_type == PACKET_LOOPBACK) {
+ if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
- skb->pkt_type = PACKET_HOST;
+ if (skb->pkt_type == PACKET_LOOPBACK)
+ skb->pkt_type = PACKET_HOST;
goto out;
}
- /* if packet is NDISC or addressed to multicast or link-local
- * then keep the ingress interface
- */
- need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
- if (!ipv6_ndisc_frame(skb) && !need_strict) {
+ /* if packet is NDISC then keep the ingress interface */
+ if (!is_ndisc) {
vrf_rx_stats(vrf_dev, skb->len);
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 0b1ec44..613f366 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2174,9 +2174,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
vni = tunnel_id_to_key32(info->key.tun_id);
ifindex = 0;
dst_cache = &info->dst_cache;
- if (info->options_len &&
- info->key.tun_flags & TUNNEL_VXLAN_OPT)
+ if (info->key.tun_flags & TUNNEL_VXLAN_OPT) {
+ if (info->options_len < sizeof(*md))
+ goto drop;
md = ip_tunnel_info_opts(info);
+ }
ttl = info->key.ttl;
tos = info->key.tos;
label = info->key.label;
@@ -2215,7 +2217,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
ndst = &rt->dst;
skb_tunnel_check_pmtu(skb, ndst, VXLAN_HEADROOM);
- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(RT_TOS(tos), old_iph, skb);
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr),
vni, md, flags, udp_sum);
@@ -2252,7 +2254,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
skb_tunnel_check_pmtu(skb, ndst, VXLAN6_HEADROOM);
- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(RT_TOS(tos), old_iph, skb);
ttl = ttl ? : ip6_dst_hoplimit(ndst);
skb_scrub_packet(skb, xnet);
err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr),
@@ -3211,6 +3213,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb *f = NULL;
+ bool unregister = false;
int err;
err = vxlan_dev_configure(net, dev, conf, false, extack);
@@ -3236,12 +3239,11 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
err = register_netdevice(dev);
if (err)
goto errout;
+ unregister = true;
err = rtnl_configure_link(dev, NULL);
- if (err) {
- unregister_netdevice(dev);
+ if (err)
goto errout;
- }
/* notify default fdb entry */
if (f)
@@ -3249,9 +3251,16 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
list_add(&vxlan->next, &vn->vxlan_list);
return 0;
+
errout:
+ /* unregister_netdevice() destroys the default FDB entry with deletion
+ * notification. But the addition notification was not sent yet, so
+ * destroy the entry by hand here.
+ */
if (f)
vxlan_fdb_destroy(vxlan, f, false);
+ if (unregister)
+ unregister_netdevice(dev);
return err;
}
diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
index 5f0366a..0212f57 100644
--- a/drivers/net/wan/fsl_ucc_hdlc.c
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -1113,7 +1113,6 @@ static int ucc_hdlc_probe(struct platform_device *pdev)
if (register_hdlc_device(dev)) {
ret = -ENOBUFS;
pr_err("ucc_hdlc: unable to register hdlc device\n");
- free_netdev(dev);
goto free_dev;
}
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c
index b94759d..da2d179 100644
--- a/drivers/net/wireless/ath/ar5523/ar5523.c
+++ b/drivers/net/wireless/ath/ar5523/ar5523.c
@@ -255,7 +255,8 @@ static int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata,
if (flags & AR5523_CMD_FLAG_MAGIC)
hdr->magic = cpu_to_be32(1 << 24);
- memcpy(hdr + 1, idata, ilen);
+ if (ilen)
+ memcpy(hdr + 1, idata, ilen);
cmd->odata = odata;
cmd->olen = olen;
diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c
index c9bd0e2..be90c9e 100644
--- a/drivers/net/wireless/ath/ath10k/ahb.c
+++ b/drivers/net/wireless/ath/ath10k/ahb.c
@@ -655,10 +655,10 @@ static void ath10k_ahb_hif_stop(struct ath10k *ar)
ath10k_ahb_irq_disable(ar);
synchronize_irq(ar_ahb->irq);
- ath10k_pci_flush(ar);
-
napi_synchronize(&ar->napi);
napi_disable(&ar->napi);
+
+ ath10k_pci_flush(ar);
}
static int ath10k_ahb_hif_power_up(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 5210cff..436eac3 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -91,6 +91,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA988X_HW_2_0_VERSION,
@@ -124,6 +125,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA9887_HW_1_0_VERSION,
@@ -157,6 +159,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -189,6 +192,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -221,6 +225,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA6174_HW_3_0_VERSION,
@@ -253,6 +258,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA6174_HW_3_2_VERSION,
@@ -288,6 +294,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
@@ -326,6 +333,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA9984_HW_1_0_DEV_VERSION,
@@ -369,6 +377,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA9888_HW_2_0_DEV_VERSION,
@@ -411,6 +420,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
@@ -443,6 +453,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA9377_HW_1_1_DEV_VERSION,
@@ -477,6 +488,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = QCA4019_HW_1_0_DEV_VERSION,
@@ -516,6 +528,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = false,
.shadow_reg_support = false,
.rri_on_ddr = false,
+ .hw_filter_reset_required = true,
},
{
.id = WCN3990_HW_1_0_DEV_VERSION,
@@ -532,7 +545,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.hw_ops = &wcn3990_ops,
.decap_align_bytes = 1,
.num_peers = TARGET_HL_10_TLV_NUM_PEERS,
- .n_cipher_suites = 8,
+ .n_cipher_suites = 11,
.ast_skid_limit = TARGET_HL_10_TLV_AST_SKID_LIMIT,
.num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
.target_64bit = true,
@@ -540,6 +553,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.per_ce_irq = true,
.shadow_reg_support = true,
.rri_on_ddr = true,
+ .hw_filter_reset_required = false,
},
};
@@ -2406,7 +2420,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
* possible to implicitly make it correct by creating a dummy vdev and
* then deleting it.
*/
- if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
+ if (ar->hw_params.hw_filter_reset_required &&
+ mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_core_reset_rx_filter(ar);
if (status) {
ath10k_err(ar,
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 9feea02..5c9fc40 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -1003,6 +1003,7 @@ struct ath10k {
struct completion install_key_done;
+ int last_wmi_vdev_start_status;
struct completion vdev_setup_done;
struct workqueue_struct *workqueue;
diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c
index 4d28063..385b84f 100644
--- a/drivers/net/wireless/ath/ath10k/coredump.c
+++ b/drivers/net/wireless/ath/ath10k/coredump.c
@@ -1105,9 +1105,11 @@ static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar)
dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar);
dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_RAM_DATA);
dump_tlv->tlv_len = cpu_to_le32(crash_data->ramdump_buf_len);
- memcpy(dump_tlv->tlv_data, crash_data->ramdump_buf,
- crash_data->ramdump_buf_len);
- sofar += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;
+ if (crash_data->ramdump_buf_len) {
+ memcpy(dump_tlv->tlv_data, crash_data->ramdump_buf,
+ crash_data->ramdump_buf_len);
+ sofar += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;
+ }
}
spin_unlock_bh(&ar->data_lock);
@@ -1154,6 +1156,9 @@ int ath10k_coredump_register(struct ath10k *ar)
if (test_bit(ATH10K_FW_CRASH_DUMP_RAM_DATA, &ath10k_coredump_mask)) {
crash_data->ramdump_buf_len = ath10k_coredump_get_ramdump_size(ar);
+ if (!crash_data->ramdump_buf_len)
+ return 0;
+
crash_data->ramdump_buf = vzalloc(crash_data->ramdump_buf_len);
if (!crash_data->ramdump_buf)
return -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 977f79e..fac58c3 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -589,6 +589,11 @@ struct ath10k_hw_params {
/* Number of bytes to be the offset for each FFT sample */
int spectral_bin_offset;
+
+ /* targets which require hw filter reset during boot up,
+ * to avoid it sending spurious acks.
+ */
+ bool hw_filter_reset_required;
};
struct htt_rx_desc;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1419f9d..448e3a8 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -18,6 +18,7 @@
#include "mac.h"
+#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <linux/acpi.h>
@@ -967,7 +968,7 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
if (time_left == 0)
return -ETIMEDOUT;
- return 0;
+ return ar->last_wmi_vdev_start_status;
}
static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
@@ -3650,7 +3651,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_vif *vif,
enum ath10k_hw_txrx_mode txmode,
enum ath10k_mac_tx_path txpath,
- struct sk_buff *skb)
+ struct sk_buff *skb, bool noque_offchan)
{
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -3678,10 +3679,10 @@ static int ath10k_mac_tx(struct ath10k *ar,
}
}
- if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
if (!ath10k_mac_tx_frm_has_freq(ar)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %pK\n",
- skb);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %pK len %d\n",
+ skb, skb->len);
skb_queue_tail(&ar->offchan_tx_queue, skb);
ieee80211_queue_work(hw, &ar->offchan_tx_work);
@@ -3743,8 +3744,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
mutex_lock(&ar->conf_mutex);
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK\n",
- skb);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK len %d\n",
+ skb, skb->len);
hdr = (struct ieee80211_hdr *)skb->data;
peer_addr = ieee80211_get_DA(hdr);
@@ -3790,7 +3791,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
- ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb);
+ ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, true);
if (ret) {
ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
ret);
@@ -3800,8 +3801,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
if (time_left == 0)
- ath10k_warn(ar, "timed out waiting for offchannel skb %pK\n",
- skb);
+ ath10k_warn(ar, "timed out waiting for offchannel skb %pK, len: %d\n",
+ skb, skb->len);
if (!peer && tmp_peer_created) {
ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
@@ -3843,8 +3844,10 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
ar->running_fw->fw_file.fw_features)) {
paddr = dma_map_single(ar->dev, skb->data,
skb->len, DMA_TO_DEVICE);
- if (!paddr)
+ if (dma_mapping_error(ar->dev, paddr)) {
+ ieee80211_free_txskb(ar->hw, skb);
continue;
+ }
ret = ath10k_wmi_mgmt_tx_send(ar, skb, paddr);
if (ret) {
ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n",
@@ -3997,7 +4000,7 @@ int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->htt.tx_lock);
}
- ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb);
+ ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, false);
if (unlikely(ret)) {
ath10k_warn(ar, "failed to push frame: %d\n", ret);
@@ -4279,7 +4282,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->htt.tx_lock);
}
- ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb);
+ ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, false);
if (ret) {
ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
if (is_htt) {
@@ -4685,6 +4688,14 @@ static int ath10k_start(struct ieee80211_hw *hw)
goto err_core_stop;
}
+ if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
+ ret = ath10k_wmi_scan_prob_req_oui(ar, ar->mac_addr);
+ if (ret) {
+ ath10k_err(ar, "failed to set prob req oui: %i\n", ret);
+ goto err_core_stop;
+ }
+ }
+
if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
ret = ath10k_wmi_adaptive_qcs(ar, true);
if (ret) {
@@ -8363,6 +8374,7 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
}
+ wiphy_read_of_freq_limits(ar->hw->wiphy);
ath10k_mac_setup_ht_vht_cap(ar);
ar->hw->wiphy->interface_modes =
@@ -8549,12 +8561,6 @@ int ath10k_mac_register(struct ath10k *ar)
}
if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
- ret = ath10k_wmi_scan_prob_req_oui(ar, ar->mac_addr);
- if (ret) {
- ath10k_err(ar, "failed to set prob req oui: %i\n", ret);
- goto err_dfs_detector_exit;
- }
-
ar->hw->wiphy->features |=
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
}
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index af2cf55..2a503aa 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1054,10 +1054,9 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
struct ath10k_ce *ce = ath10k_ce_priv(ar);
int ret = 0;
u32 *buf;
- unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+ unsigned int completed_nbytes, alloc_nbytes, remaining_bytes;
struct ath10k_ce_pipe *ce_diag;
void *data_buf = NULL;
- u32 ce_data; /* Host buffer address in CE space */
dma_addr_t ce_data_base = 0;
int i;
@@ -1071,9 +1070,10 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
* 1) 4-byte alignment
* 2) Buffer in DMA-able space
*/
- orig_nbytes = nbytes;
+ alloc_nbytes = min_t(unsigned int, nbytes, DIAG_TRANSFER_LIMIT);
+
data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
- orig_nbytes,
+ alloc_nbytes,
&ce_data_base,
GFP_ATOMIC);
if (!data_buf) {
@@ -1081,9 +1081,6 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
goto done;
}
- /* Copy caller's data to allocated DMA buf */
- memcpy(data_buf, data, orig_nbytes);
-
/*
* The address supplied by the caller is in the
* Target CPU virtual address space.
@@ -1096,12 +1093,14 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
*/
address = ath10k_pci_targ_cpu_to_ce_addr(ar, address);
- remaining_bytes = orig_nbytes;
- ce_data = ce_data_base;
+ remaining_bytes = nbytes;
while (remaining_bytes) {
/* FIXME: check cast */
nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+ /* Copy caller's data to allocated DMA buf */
+ memcpy(data_buf, data, nbytes);
+
/* Set up to receive directly into Target(!) address */
ret = ce_diag->ops->ce_rx_post_buf(ce_diag, &address, address);
if (ret != 0)
@@ -1111,7 +1110,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
* Request CE to send caller-supplied data that
* was copied to bounce buffer to Target(!) address.
*/
- ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)ce_data,
+ ret = ath10k_ce_send_nolock(ce_diag, NULL, ce_data_base,
nbytes, 0, 0);
if (ret != 0)
goto done;
@@ -1152,12 +1151,12 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
remaining_bytes -= nbytes;
address += nbytes;
- ce_data += nbytes;
+ data += nbytes;
}
done:
if (data_buf) {
- dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+ dma_free_coherent(ar->dev, alloc_nbytes, data_buf,
ce_data_base);
}
@@ -2053,6 +2052,11 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
+ ath10k_pci_irq_disable(ar);
+ ath10k_pci_irq_sync(ar);
+ napi_synchronize(&ar->napi);
+ napi_disable(&ar->napi);
+
/* Most likely the device has HTT Rx ring configured. The only way to
* prevent the device from accessing (and possible corrupting) host
* memory is to reset the chip now.
@@ -2066,11 +2070,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
*/
ath10k_pci_safe_chip_reset(ar);
- ath10k_pci_irq_disable(ar);
- ath10k_pci_irq_sync(ar);
ath10k_pci_flush(ar);
- napi_synchronize(&ar->napi);
- napi_disable(&ar->napi);
spin_lock_irqsave(&ar_pci->ps_lock, flags);
WARN_ON(ar_pci->ps_wake_refcount > 0);
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index fa1843a..e2d78f77 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -1190,7 +1190,7 @@ static int ath10k_wcn3990_clk_init(struct ath10k *ar)
return 0;
err_clock_config:
- for (; i >= 0; i--) {
+ for (i = i - 1; i >= 0; i--) {
clk_info = &ar_snoc->clk[i];
if (!clk_info->handle)
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 6f62ddc..6c47e4b 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -101,6 +101,8 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
+ info->status.rates[0].idx = -1;
+
trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c
index f09a4ad..f9c79e2 100644
--- a/drivers/net/wireless/ath/ath10k/usb.c
+++ b/drivers/net/wireless/ath/ath10k/usb.c
@@ -49,6 +49,10 @@ ath10k_usb_alloc_urb_from_pipe(struct ath10k_usb_pipe *pipe)
struct ath10k_urb_context *urb_context = NULL;
unsigned long flags;
+ /* bail if this pipe is not initialized */
+ if (!pipe->ar_usb)
+ return NULL;
+
spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
if (!list_empty(&pipe->urb_list_head)) {
urb_context = list_first_entry(&pipe->urb_list_head,
@@ -66,6 +70,10 @@ static void ath10k_usb_free_urb_to_pipe(struct ath10k_usb_pipe *pipe,
{
unsigned long flags;
+ /* bail if this pipe is not initialized */
+ if (!pipe->ar_usb)
+ return;
+
spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
pipe->urb_cnt++;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 9f31b9a..aefc92d 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2487,7 +2487,8 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
status->freq, status->band, status->signal,
status->rate_idx);
- ieee80211_rx(ar->hw, skb);
+ ieee80211_rx_ni(ar->hw, skb);
+
return 0;
}
@@ -3247,18 +3248,31 @@ void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_vdev_start_ev_arg arg = {};
int ret;
+ u32 status;
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
+ ar->last_wmi_vdev_start_status = 0;
+
ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg);
if (ret) {
ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret);
- return;
+ ar->last_wmi_vdev_start_status = ret;
+ goto out;
}
- if (WARN_ON(__le32_to_cpu(arg.status)))
- return;
+ status = __le32_to_cpu(arg.status);
+ if (WARN_ON_ONCE(status)) {
+ ath10k_warn(ar, "vdev-start-response reports status error: %d (%s)\n",
+ status, (status == WMI_VDEV_START_CHAN_INVALID) ?
+ "chan-invalid" : "unknown");
+ /* Setup is done one way or another though, so we should still
+ * do the completion, so don't return here.
+ */
+ ar->last_wmi_vdev_start_status = -EINVAL;
+ }
+out:
complete(&ar->vdev_setup_done);
}
@@ -4785,6 +4799,13 @@ ath10k_wmi_tpc_final_get_rate(struct ath10k *ar,
}
}
+ if (pream == -1) {
+ ath10k_warn(ar, "unknown wmi tpc final index and frequency: %u, %u\n",
+ pream_idx, __le32_to_cpu(ev->chan_freq));
+ tpc = 0;
+ goto out;
+ }
+
if (pream == 4)
tpc = min_t(u8, ev->rates_array[rate_idx],
ev->max_reg_allow_pow[ch]);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 3622025..e341cfb 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -6642,11 +6642,17 @@ struct wmi_ch_info_ev_arg {
__le32 rx_frame_count;
};
+/* From 10.4 firmware, not sure all have the same values. */
+enum wmi_vdev_start_status {
+ WMI_VDEV_START_OK = 0,
+ WMI_VDEV_START_CHAN_INVALID,
+};
+
struct wmi_vdev_start_ev_arg {
__le32 vdev_id;
__le32 req_id;
__le32 resp_type; /* %WMI_VDEV_RESP_ */
- __le32 status;
+ __le32 status; /* See wmi_vdev_start_status enum above */
};
struct wmi_peer_kick_ev_arg {
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index e121187..d7c626d 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -939,7 +939,7 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar,
else
ssid_list[i].flag = ANY_SSID_FLAG;
- if (n_match_ssid == 0)
+ if (ar->wiphy->max_match_sets != 0 && n_match_ssid == 0)
ssid_list[i].flag |= MATCH_SSID_FLAG;
}
@@ -1093,7 +1093,7 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
for (i = 0; i < vif->scan_req->n_ssids; i++) {
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
- i + 1, DISABLE_SSID_FLAG,
+ i, DISABLE_SSID_FLAG,
0, NULL);
}
}
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
index 4defb7a..53b66e9 100644
--- a/drivers/net/wireless/ath/ath6kl/usb.c
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -132,6 +132,10 @@ ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe)
struct ath6kl_urb_context *urb_context = NULL;
unsigned long flags;
+ /* bail if this pipe is not initialized */
+ if (!pipe->ar_usb)
+ return NULL;
+
spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
if (!list_empty(&pipe->urb_list_head)) {
urb_context =
@@ -150,6 +154,10 @@ static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe,
{
unsigned long flags;
+ /* bail if this pipe is not initialized */
+ if (!pipe->ar_usb)
+ return;
+
spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
pipe->urb_cnt++;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index f019a20..983e1ab 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4183,7 +4183,7 @@ static void ar9003_hw_thermometer_apply(struct ath_hw *ah)
static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah)
{
- u32 data, ko, kg;
+ u32 data = 0, ko, kg;
if (!AR_SREV_9462_20_OR_LATER(ah))
return;
diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
index 440e16e..f75eb06 100644
--- a/drivers/net/wireless/ath/ath9k/common-spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -411,7 +411,7 @@ ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs,
ath_dbg(common, SPECTRAL_SCAN,
"Calculated new upper max 0x%X at %i\n",
- tmp_mag, i);
+ tmp_mag, fft_sample_40.upper_max_index);
} else
for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {
if (fft_sample_40.data[i] == (upper_mag >> max_exp))
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 799010e..b5d7ef4 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -973,6 +973,8 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
struct ath_htc_rx_status *rxstatus;
struct ath_rx_status rx_stats;
bool decrypt_error = false;
+ __be16 rs_datalen;
+ bool is_phyerr;
if (skb->len < HTC_RX_FRAME_HEADER_SIZE) {
ath_err(common, "Corrupted RX frame, dropping (len: %d)\n",
@@ -982,11 +984,24 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
rxstatus = (struct ath_htc_rx_status *)skb->data;
- if (be16_to_cpu(rxstatus->rs_datalen) -
- (skb->len - HTC_RX_FRAME_HEADER_SIZE) != 0) {
+ rs_datalen = be16_to_cpu(rxstatus->rs_datalen);
+ if (unlikely(rs_datalen -
+ (skb->len - HTC_RX_FRAME_HEADER_SIZE) != 0)) {
ath_err(common,
"Corrupted RX data len, dropping (dlen: %d, skblen: %d)\n",
- rxstatus->rs_datalen, skb->len);
+ rs_datalen, skb->len);
+ goto rx_next;
+ }
+
+ is_phyerr = rxstatus->rs_status & ATH9K_RXERR_PHY;
+ /*
+ * Discard zero-length packets and packets smaller than an ACK
+ * which are not PHY_ERROR (short radar pulses have a length of 3)
+ */
+ if (unlikely(!rs_datalen || (rs_datalen < 10 && !is_phyerr))) {
+ ath_warn(common,
+ "Short RX data len, dropping (dlen: %d)\n",
+ rs_datalen);
goto rx_next;
}
@@ -1011,7 +1026,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
* Process PHY errors and return so that the packet
* can be dropped.
*/
- if (rx_stats.rs_status & ATH9K_RXERR_PHY) {
+ if (unlikely(is_phyerr)) {
/* TODO: Not using DFS processing now. */
if (ath_cmn_process_fft(&priv->spec_priv, hdr,
&rx_stats, rx_status->mactime)) {
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 1049773..74f98bb 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1252,7 +1252,6 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ath_node *an = &avp->mcast_node;
mutex_lock(&sc->mutex);
-
if (IS_ENABLED(CONFIG_ATH9K_TX99)) {
if (sc->cur_chan->nvifs >= 1) {
mutex_unlock(&sc->mutex);
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index ce50d8f..95544ce 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -56,11 +56,6 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
struct sk_buff *skb;
struct ath_vif *avp;
- if (!sc->tx99_vif)
- return NULL;
-
- avp = (struct ath_vif *)sc->tx99_vif->drv_priv;
-
skb = alloc_skb(len, GFP_KERNEL);
if (!skb)
return NULL;
@@ -77,7 +72,10 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
- hdr->seq_ctrl |= cpu_to_le16(avp->seq_no);
+ if (sc->tx99_vif) {
+ avp = (struct ath_vif *) sc->tx99_vif->drv_priv;
+ hdr->seq_ctrl |= cpu_to_le16(avp->seq_no);
+ }
tx_info = IEEE80211_SKB_CB(skb);
memset(tx_info, 0, sizeof(*tx_info));
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 8a0bfa4..520b669 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/etherdevice.h>
@@ -1034,7 +1034,7 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wireless_dev *wdev = vif_to_wdev(vif);
int rc;
- bool fw_reset = false;
+ bool compressed_rx_status, fw_reset = false;
wil_dbg_misc(wil, "change_iface: type=%d\n", type);
@@ -1046,6 +1046,13 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
}
}
+ /* monitor mode uses uncompressed rx status to get phy statistics */
+ compressed_rx_status = wil->use_compressed_rx_status;
+ if (type == NL80211_IFTYPE_MONITOR)
+ wil->use_compressed_rx_status = false;
+ else if (wdev->iftype == NL80211_IFTYPE_MONITOR)
+ wil->use_compressed_rx_status = true;
+
/* do not reset FW when there are active VIFs,
* because it can cause significant disruption
*/
@@ -1059,7 +1066,7 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
mutex_unlock(&wil->mutex);
if (rc)
- return rc;
+ goto fail;
fw_reset = true;
}
@@ -1074,7 +1081,8 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
wil->monitor_flags = params->flags;
break;
default:
- return -EOPNOTSUPP;
+ rc = -EOPNOTSUPP;
+ goto fail;
}
if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
@@ -1082,14 +1090,18 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
wil_vif_prepare_stop(vif);
rc = wmi_port_delete(wil, vif->mid);
if (rc)
- return rc;
+ goto fail;
rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
if (rc)
- return rc;
+ goto fail;
}
wdev->iftype = type;
return 0;
+
+fail:
+ wil->use_compressed_rx_status = compressed_rx_status;
+ return rc;
}
static int wil_cfg80211_scan(struct wiphy *wiphy,
@@ -1378,7 +1390,7 @@ static int wil_ft_connect(struct wiphy *wiphy,
if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities))
if (wil->force_edmg_channel) {
rc = wil_spec2wmi_ch(wil->force_edmg_channel,
- &auth_cmd.channel);
+ &auth_cmd.edmg_channel);
if (rc)
wil_err(wil, "FT: wmi channel for channel %d not found",
wil->force_edmg_channel);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 78920dd..1e359ab17 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -1628,6 +1628,11 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
if (wil->hw_version < HW_VER_TALYN_MB) {
wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+ } else {
+ wil_s(wil,
+ RGF_CAF_ICR_TALYN_MB + offsetof(struct RGF_ICR, ICR), 0);
+ wil_w(wil, RGF_CAF_ICR_TALYN_MB +
+ offsetof(struct RGF_ICR, IMV), ~0);
}
/* clear PAL_UNIT_ICR (potential D0->D3 leftover)
* In Talyn-MB host cannot access this register due to
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index bac1ec3..311941b 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1,11 +1,10 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/etherdevice.h>
-#include <net/ieee80211_radiotap.h>
#include <linux/if_arp.h>
#include <linux/moduleparam.h>
#include <linux/ip.h>
@@ -331,19 +330,6 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct wil_ring *vring,
static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
struct sk_buff *skb)
{
- struct wil6210_rtap {
- struct ieee80211_radiotap_header rthdr;
- /* fields should be in the order of bits in rthdr.it_present */
- /* flags */
- u8 flags;
- /* channel */
- __le16 chnl_freq __aligned(2);
- __le16 chnl_flags;
- /* MCS */
- u8 mcs_present;
- u8 mcs_flags;
- u8 mcs_index;
- } __packed;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
struct wil6210_rtap *rtap;
int rtap_len = sizeof(struct wil6210_rtap);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index d9e116b..ba0403b 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -1,12 +1,13 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#ifndef WIL6210_TXRX_H
#define WIL6210_TXRX_H
+#include <net/ieee80211_radiotap.h>
#include "wil6210.h"
#include "txrx_edma.h"
@@ -491,6 +492,20 @@ struct packet_rx_info {
u8 cid;
};
+struct wil6210_rtap {
+ struct ieee80211_radiotap_header rthdr;
+ /* fields should be in the order of bits in rthdr.it_present */
+ /* flags */
+ u8 flags;
+ /* channel */
+ __le16 chnl_freq __aligned(2);
+ __le16 chnl_flags;
+ /* MCS */
+ u8 mcs_present;
+ u8 mcs_flags;
+ u8 mcs_index;
+} __packed;
+
/* this struct will be stored in the skb cb buffer
* max length of the struct is limited to 48 bytes
*/
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c
index b72c248..8e7a27b 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.c
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: ISC
/*
- * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
*/
#include <linux/etherdevice.h>
#include <linux/moduleparam.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
#include <linux/prefetch.h>
#include <linux/types.h>
#include <linux/list.h>
@@ -181,17 +183,20 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil,
struct wil_rx_enhanced_desc dd, *d = ⅆ
struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *)
&ring->va[i].rx.enhanced;
+ struct net_device *ndev = wil->main_ndev;
+ int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ?
+ WIL6210_RTAP_SIZE : headroom_size;
if (unlikely(list_empty(free))) {
wil->rx_buff_mgmt.free_list_empty_cnt++;
return -EAGAIN;
}
- skb = dev_alloc_skb(sz + headroom_size);
+ skb = dev_alloc_skb(sz + headroom);
if (unlikely(!skb))
return -ENOMEM;
- skb_reserve(skb, headroom_size);
+ skb_reserve(skb, headroom);
skb_put(skb, sz);
/**
@@ -902,6 +907,48 @@ static int wil_rx_error_check_edma(struct wil6210_priv *wil,
return 0;
}
+/**
+ * Adds radiotap header
+ *
+ * Any error indicated as "Bad FCS"
+ */
+static void wil_rx_add_radiotap_header_edma(struct wil6210_priv *wil,
+ struct wil_rx_status_extended *s,
+ struct sk_buff *skb)
+{
+ struct wil6210_rtap *rtap;
+ int rtap_len = sizeof(struct wil6210_rtap);
+ struct ieee80211_channel *ch = wil->monitor_chandef.chan;
+
+ if (skb_headroom(skb) < rtap_len &&
+ pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) {
+ wil_err(wil, "Unable to expand headroom to %d\n", rtap_len);
+ return;
+ }
+
+#if defined(BACKPORT_HAS_SKB_PUT_PUSH_RETURN_VOID)
+ rtap = skb_push(skb, rtap_len);
+#else
+ rtap = (void *)skb_push(skb, rtap_len);
+#endif
+ memset(rtap, 0, rtap_len);
+
+ rtap->rthdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ rtap->rthdr.it_len = cpu_to_le16(rtap_len);
+ rtap->rthdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_MCS));
+ if (wil_rx_status_get_error(s))
+ rtap->flags |= IEEE80211_RADIOTAP_F_BADFCS;
+
+ rtap->chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320);
+ rtap->chnl_flags = cpu_to_le16(0);
+
+ rtap->mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS;
+ rtap->mcs_flags = 0;
+ rtap->mcs_index = wil_rx_status_get_mcs(s);
+}
+
static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
struct wil_status_ring *sring)
{
@@ -922,6 +969,8 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
u8 data_offset;
struct wil_rx_status_extended *s;
u16 sring_idx = sring - wil->srings;
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb));
@@ -1011,13 +1060,6 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
}
stats = &wil->sta[cid].stats;
- if (unlikely(dmalen < ETH_HLEN)) {
- wil_dbg_txrx(wil, "Short frame, len = %d\n", dmalen);
- stats->rx_short_frame++;
- rxdata->skipping = true;
- goto skipping;
- }
-
if (unlikely(dmalen > sz)) {
wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1,
@@ -1027,6 +1069,17 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
stats->rx_large_frame++;
rxdata->skipping = true;
+ goto skipping;
+ }
+
+ /* no extra checks if in sniffer mode */
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR)
+ goto skipping;
+
+ if (unlikely(dmalen < ETH_HLEN)) {
+ wil_dbg_txrx(wil, "Short frame, len = %d\n", dmalen);
+ stats->rx_short_frame++;
+ rxdata->skipping = true;
}
skipping:
@@ -1106,6 +1159,10 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
+ /* use radiotap header only if required */
+ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
+ wil_rx_add_radiotap_header_edma(wil, msg, skb);
+
/* Has to be done after dma_unmap_single as skb->cb is also
* used for holding the pa
*/
@@ -1122,6 +1179,7 @@ void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota)
struct wil_status_ring *sring;
struct sk_buff *skb;
int i;
+ struct wireless_dev *wdev;
if (unlikely(!ring->va)) {
wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
@@ -1155,6 +1213,14 @@ void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota)
continue;
}
ndev = vif_to_ndev(vif);
+ wdev = ndev->ieee80211_ptr;
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ skb->dev = ndev;
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+ }
wil_netif_rx_any(skb, ndev);
} else {
wil_rx_reorder(wil, skb);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 818e38d..1119f00 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/moduleparam.h>
@@ -4053,8 +4053,10 @@ int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil,
u16 max_rx_pl_per_desc, bool checksum)
{
struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct wil6210_vif *vif = ndev_to_vif(ndev);
int rc;
+ u8 edmg_channel = 0;
struct wmi_cfg_def_rx_offload_cmd cmd = {
.max_msdu_size = cpu_to_le16(wil_mtu2macbuf(WIL_MAX_ETH_MTU)),
.max_rx_pl_per_desc = cpu_to_le16(max_rx_pl_per_desc),
@@ -4070,6 +4072,27 @@ int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil,
.evt = {.status = WMI_FW_STATUS_FAILURE},
};
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ struct ieee80211_channel *ch = wil->monitor_chandef.chan;
+
+ cmd.sniffer_cfg.phy_support =
+ wil->monitor_flags & MONITOR_FLAG_CONTROL ?
+ WMI_SNIFFER_EDMA_CP : WMI_SNIFFER_EDMA_BOTH;
+ if (ch)
+ cmd.sniffer_cfg.channel = ch->hw_value - 1;
+
+ if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING,
+ wil->fw_capabilities))
+ if (wil->force_edmg_channel) {
+ rc = wil_spec2wmi_ch(wil->force_edmg_channel,
+ &edmg_channel);
+ if (rc)
+ wil_err(wil, "wmi channel for channel %d not found\n",
+ wil->force_edmg_channel);
+ }
+ cmd.sniffer_cfg.edmg_channel = edmg_channel;
+ }
+
rc = wmi_call(wil, WMI_CFG_DEF_RX_OFFLOAD_CMDID, vif->mid, &cmd,
sizeof(cmd), WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID, &reply,
sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 3cb6444..e15d4da 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: ISC */
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2006-2012 Wilocity
*/
@@ -1032,6 +1032,26 @@ struct wmi_rx_status_ring_add_cmd {
u8 reserved[2];
} __packed;
+enum wmi_sniffer_edma_cfg_phy_support {
+ WMI_SNIFFER_EDMA_NONE = 0x00,
+ WMI_SNIFFER_EDMA_CP = 0x01,
+ WMI_SNIFFER_EDMA_DP = 0x02,
+ WMI_SNIFFER_EDMA_BOTH = 0x03,
+};
+
+/* wmi_sniffer_edma_cfg */
+struct wmi_sniffer_edma_cfg {
+ /* wmi_sniffer_edma_cfg_phy_support */
+ u8 phy_support;
+ /* enum wmi_channel WMI_CHANNEL_1..WMI_CHANNEL_6; for EDMG this is
+ * the primary channel number
+ */
+ u8 channel;
+ /* enum wmi_channel WMI_CHANNEL_9..WMI_CHANNEL_12 */
+ u8 edmg_channel;
+ u8 reserved;
+} __packed;
+
struct wmi_cfg_def_rx_offload_cmd {
__le16 max_msdu_size;
__le16 max_rx_pl_per_desc;
@@ -1041,7 +1061,8 @@ struct wmi_cfg_def_rx_offload_cmd {
u8 vlan_id;
u8 nwifi_ds_trans_type;
u8 l3_l4_ctrl;
- u8 reserved[6];
+ u8 reserved[2];
+ struct wmi_sniffer_edma_cfg sniffer_cfg;
} __packed;
struct wmi_tx_desc_ring_add_cmd {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index c7c520f..bbdc600 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -6314,6 +6314,16 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
}
};
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 27893af..8510d20 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -296,9 +296,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
/* Replace all newline/linefeed characters with space
* character
*/
- ptr = clmver;
- while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL)
- *ptr = ' ';
+ strreplace(clmver, '\n', ' ');
brcmf_dbg(INFO, "CLM version = %s\n", clmver);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 0f56be1..584e05fd 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -1246,6 +1246,11 @@ void brcmf_detach(struct device *dev)
brcmf_proto_detach_pre_delif(drvr);
+ if (drvr->mon_if) {
+ brcmf_net_detach(drvr->mon_if->ndev, false);
+ drvr->mon_if = NULL;
+ }
+
/* make sure primary interface removed last */
for (i = BRCMF_MAX_IFS-1; i > -1; i--)
brcmf_remove_interface(drvr->iflist[i], false);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 8347da6..4c5a399 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -178,7 +178,7 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
ifp->fwil_fwerr = false;
}
-#define MAX_CAPS_BUFFER_SIZE 512
+#define MAX_CAPS_BUFFER_SIZE 768
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
char caps[MAX_CAPS_BUFFER_SIZE];
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 3e9c4f2..456a1bf 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -74,7 +74,7 @@
#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000)
#define P2P_INVALID_CHANNEL -1
#define P2P_CHANNEL_SYNC_RETRY 5
-#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500)
+#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(450)
#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
/* WiFi P2P Public Action Frame OUI Subtypes */
@@ -1134,7 +1134,6 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
{
struct afx_hdl *afx_hdl = &p2p->afx_hdl;
struct brcmf_cfg80211_vif *pri_vif;
- unsigned long duration;
s32 retry;
brcmf_dbg(TRACE, "Enter\n");
@@ -1150,7 +1149,6 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
* pending action frame tx is cancelled.
*/
retry = 0;
- duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT);
while ((retry < P2P_CHANNEL_SYNC_RETRY) &&
(afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) {
afx_hdl->is_listen = false;
@@ -1158,7 +1156,8 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
retry);
/* search peer on peer's listen channel */
schedule_work(&afx_hdl->afx_work);
- wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration);
+ wait_for_completion_timeout(&afx_hdl->act_frm_scan,
+ P2P_AF_FRM_SCAN_MAX_WAIT);
if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
(!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
&p2p->status)))
@@ -1171,7 +1170,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
afx_hdl->is_listen = true;
schedule_work(&afx_hdl->afx_work);
wait_for_completion_timeout(&afx_hdl->act_frm_scan,
- duration);
+ P2P_AF_FRM_SCAN_MAX_WAIT);
}
if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
(!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
@@ -1458,10 +1457,12 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
return 0;
if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) {
- if (e->status == BRCMF_E_STATUS_SUCCESS)
+ if (e->status == BRCMF_E_STATUS_SUCCESS) {
set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
&p2p->status);
- else {
+ if (!p2p->wait_for_offchan_complete)
+ complete(&p2p->send_af_done);
+ } else {
set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
/* If there is no ack, we don't need to wait for
* WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event
@@ -1512,6 +1513,17 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
p2p->af_sent_channel = le32_to_cpu(af_params->channel);
p2p->af_tx_sent_jiffies = jiffies;
+ if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status) &&
+ p2p->af_sent_channel ==
+ ieee80211_frequency_to_channel(p2p->remain_on_channel.center_freq))
+ p2p->wait_for_offchan_complete = false;
+ else
+ p2p->wait_for_offchan_complete = true;
+
+ brcmf_dbg(TRACE, "Waiting for %s tx completion event\n",
+ (p2p->wait_for_offchan_complete) ?
+ "off-channel" : "on-channel");
+
timeout = wait_for_completion_timeout(&p2p->send_af_done,
P2P_AF_MAX_WAIT_TIME);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
index 0e8b34d2..39f0d02 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -124,6 +124,7 @@ struct afx_hdl {
* @gon_req_action: about to send go negotiation requets frame.
* @block_gon_req_tx: drop tx go negotiation requets frame.
* @p2pdev_dynamically: is p2p device if created by module param or supplicant.
+ * @wait_for_offchan_complete: wait for off-channel tx completion event.
*/
struct brcmf_p2p_info {
struct brcmf_cfg80211_info *cfg;
@@ -144,6 +145,7 @@ struct brcmf_p2p_info {
bool gon_req_action;
bool block_gon_req_tx;
bool p2pdev_dynamically;
+ bool wait_for_offchan_complete;
};
s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 53e4962..abaed2f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -49,6 +49,10 @@
#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
+/* watermark expressed in number of words */
+#define DEFAULT_F2_WATERMARK 0x8
+#define CY_4373_F2_WATERMARK 0x40
+
#ifdef DEBUG
#define BRCMF_TRAP_INFO_SIZE 80
@@ -138,6 +142,8 @@ struct rte_console {
/* 1: isolate internal sdio signals, put external pads in tri-state; requires
* sdio bus power cycle to clear (rev 9) */
#define SBSDIO_DEVCTL_PADS_ISO 0x08
+/* 1: enable F2 Watermark */
+#define SBSDIO_DEVCTL_F2WM_ENAB 0x10
/* Force SD->SB reset mapping (rev 11) */
#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
/* Determined by CoreControl bit */
@@ -4060,6 +4066,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
void *nvram;
u32 nvram_len;
u8 saveclk;
+ u8 devctl;
brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
@@ -4115,8 +4122,26 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_sdiod_writel(sdiod, core->base + SD_REG(hostintmask),
bus->hostintmask, NULL);
-
- brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, 8, &err);
+ switch (sdiod->func1->device) {
+ case SDIO_DEVICE_ID_CYPRESS_4373:
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
+ CY_4373_F2_WATERMARK);
+ brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
+ CY_4373_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL,
+ &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl,
+ &err);
+ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL,
+ CY_4373_F2_WATERMARK |
+ SBSDIO_MESBUSYCTRL_ENAB, &err);
+ break;
+ default:
+ brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
+ DEFAULT_F2_WATERMARK, &err);
+ break;
+ }
} else {
/* Disable F2 again */
sdio_disable_func(sdiod->func2);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index 7faed83..34b03115 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -77,7 +77,7 @@
#define SBSDIO_GPIO_OUT 0x10006
/* gpio enable */
#define SBSDIO_GPIO_EN 0x10007
-/* rev < 7, watermark for sdio device */
+/* rev < 7, watermark for sdio device TX path */
#define SBSDIO_WATERMARK 0x10008
/* control busy signal generation */
#define SBSDIO_DEVICE_CTL 0x10009
@@ -104,6 +104,13 @@
#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
/* MesBusyCtl (rev 11) */
#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
+/* Watermark for sdio device RX path */
+#define SBSDIO_MESBUSY_RXFIFO_WM_MASK 0x7F
+#define SBSDIO_MESBUSY_RXFIFO_WM_SHIFT 0
+/* Enable busy capability for MES access */
+#define SBSDIO_MESBUSYCTRL_ENAB 0x80
+#define SBSDIO_MESBUSYCTRL_ENAB_SHIFT 7
+
/* Sdio Core Rev 12 */
#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
index ecc89e7..6188275 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
@@ -502,6 +502,7 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
}
spin_lock_bh(&wl->lock);
+ wl->wlc->vif = vif;
wl->mute_tx = false;
brcms_c_mute(wl->wlc, false);
if (vif->type == NL80211_IFTYPE_STATION)
@@ -519,6 +520,11 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
static void
brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
+ struct brcms_info *wl = hw->priv;
+
+ spin_lock_bh(&wl->lock);
+ wl->wlc->vif = NULL;
+ spin_unlock_bh(&wl->lock);
}
static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
@@ -840,8 +846,8 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
status = brcms_c_aggregatable(wl->wlc, tid);
spin_unlock_bh(&wl->lock);
if (!status) {
- brcms_err(wl->wlc->hw->d11core,
- "START: tid %d is not agg\'able\n", tid);
+ brcms_dbg_ht(wl->wlc->hw->d11core,
+ "START: tid %d is not agg\'able\n", tid);
return -EINVAL;
}
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
@@ -937,6 +943,25 @@ static void brcms_ops_set_tsf(struct ieee80211_hw *hw,
spin_unlock_bh(&wl->lock);
}
+static int brcms_ops_beacon_set_tim(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, bool set)
+{
+ struct brcms_info *wl = hw->priv;
+ struct sk_buff *beacon = NULL;
+ u16 tim_offset = 0;
+
+ spin_lock_bh(&wl->lock);
+ if (wl->wlc->vif)
+ beacon = ieee80211_beacon_get_tim(hw, wl->wlc->vif,
+ &tim_offset, NULL);
+ if (beacon)
+ brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
+ wl->wlc->vif->bss_conf.dtim_period);
+ spin_unlock_bh(&wl->lock);
+
+ return 0;
+}
+
static const struct ieee80211_ops brcms_ops = {
.tx = brcms_ops_tx,
.start = brcms_ops_start,
@@ -955,6 +980,7 @@ static const struct ieee80211_ops brcms_ops = {
.flush = brcms_ops_flush,
.get_tsf = brcms_ops_get_tsf,
.set_tsf = brcms_ops_set_tsf,
+ .set_tim = brcms_ops_beacon_set_tim,
};
void brcms_dpc(unsigned long data)
@@ -1578,10 +1604,10 @@ int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
if (le32_to_cpu(hdr->idx) == idx) {
pdata = wl->fw.fw_bin[i]->data +
le32_to_cpu(hdr->offset);
- *pbuf = kmemdup(pdata, len, GFP_KERNEL);
+ *pbuf = kvmalloc(len, GFP_KERNEL);
if (*pbuf == NULL)
goto fail;
-
+ memcpy(*pbuf, pdata, len);
return 0;
}
}
@@ -1629,7 +1655,7 @@ int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx)
*/
void brcms_ucode_free_buf(void *p)
{
- kfree(p);
+ kvfree(p);
}
/*
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
index c4d135c..9f76b88 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
@@ -563,6 +563,7 @@ struct brcms_c_info {
struct wiphy *wiphy;
struct scb pri_scb;
+ struct ieee80211_vif *vif;
struct sk_buff *beacon;
u16 beacon_tim_offset;
diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c
index 04dd7a9..5512c7f 100644
--- a/drivers/net/wireless/cisco/airo.c
+++ b/drivers/net/wireless/cisco/airo.c
@@ -5462,7 +5462,7 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) {
we have to add a spin lock... */
rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
- ptr += sprintf(ptr, "%pM %*s rssi = %d",
+ ptr += sprintf(ptr, "%pM %.*s rssi = %d",
BSSList_rid.bssid,
(int)BSSList_rid.ssidLen,
BSSList_rid.ssid,
diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile
index 1e0239d..c031c5c 100644
--- a/drivers/net/wireless/cnss2/Makefile
+++ b/drivers/net/wireless/cnss2/Makefile
@@ -8,5 +8,5 @@
cnss2-y += debug.o
cnss2-y += pci.o
cnss2-y += power.o
-cnss2-$(CONFIG_CNSS2_DEBUG) += genl.o
+cnss2-y += genl.o
cnss2-$(CONFIG_CNSS2_QMI) += qmi.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.o
diff --git a/drivers/net/wireless/cnss2/genl.h b/drivers/net/wireless/cnss2/genl.h
index 33ca30a..d38ba5d 100644
--- a/drivers/net/wireless/cnss2/genl.h
+++ b/drivers/net/wireless/cnss2/genl.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#ifndef __CNSS_GENL_H__
#define __CNSS_GENL_H__
@@ -9,26 +9,9 @@ enum cnss_genl_msg_type {
CNSS_GENL_MSG_TYPE_QDSS,
};
-#ifdef CONFIG_CNSS2_DEBUG
int cnss_genl_init(void);
void cnss_genl_exit(void);
int cnss_genl_send_msg(void *buff, u8 type,
char *file_name, u32 total_size);
-#else
-static inline int cnss_genl_init(void)
-{
- return 0;
-}
-
-static inline void cnss_genl_exit(void)
-{
-}
-
-static inline int cnss_genl_send_msg(void *buff, u8 type,
- char *file_name, u32 total_size)
-{
- return 0;
-}
-#endif
#endif
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index 1c3fb3a..e0e1afe 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */
#include <linux/delay.h>
#include <linux/jiffies.h>
@@ -32,6 +32,7 @@
#define FW_ASSERT_TIMEOUT 5000
#define CNSS_EVENT_PENDING 2989
#define COLD_BOOT_CAL_SHUTDOWN_DELAY_MS 50
+#define WLAN_WD_TIMEOUT_MS 60000
#define CNSS_QUIRKS_DEFAULT BIT(DISABLE_IO_COHERENCY)
#ifdef CONFIG_CNSS_EMULATION
@@ -665,6 +666,14 @@ int cnss_idle_restart(struct device *dev)
cnss_pr_dbg("Doing idle restart\n");
+ reinit_completion(&plat_priv->power_up_complete);
+
+ if (test_bit(CNSS_IN_REBOOT, &plat_priv->driver_state)) {
+ cnss_pr_dbg("Reboot or shutdown is in progress, ignore idle restart\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = cnss_driver_event_post(plat_priv,
CNSS_DRIVER_EVENT_IDLE_RESTART,
CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
@@ -677,13 +686,19 @@ int cnss_idle_restart(struct device *dev)
}
timeout = cnss_get_boot_timeout(dev);
-
- reinit_completion(&plat_priv->power_up_complete);
ret = wait_for_completion_timeout(&plat_priv->power_up_complete,
- msecs_to_jiffies(timeout) << 2);
+ msecs_to_jiffies((timeout << 1) +
+ WLAN_WD_TIMEOUT_MS));
if (!ret) {
cnss_pr_err("Timeout waiting for idle restart to complete\n");
- ret = -EAGAIN;
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (test_bit(CNSS_IN_REBOOT, &plat_priv->driver_state)) {
+ cnss_pr_dbg("Reboot or shutdown is in progress, ignore idle restart\n");
+ del_timer(&plat_priv->fw_boot_timer);
+ ret = -EINVAL;
goto out;
}
@@ -1949,6 +1964,23 @@ static void cnss_unregister_bus_scale(struct cnss_plat_data *plat_priv)
msm_bus_scale_unregister_client(bus_bw_info->bus_client);
}
+static ssize_t shutdown_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL);
+
+ if (plat_priv) {
+ set_bit(CNSS_IN_REBOOT, &plat_priv->driver_state);
+ del_timer(&plat_priv->fw_boot_timer);
+ complete_all(&plat_priv->power_up_complete);
+ }
+
+ cnss_pr_dbg("Received shutdown notification\n");
+
+ return count;
+}
+
static ssize_t fs_ready_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1992,18 +2024,55 @@ static ssize_t fs_ready_store(struct device *dev,
return count;
}
+static struct kobj_attribute shutdown_attribute = __ATTR_WO(shutdown);
static DEVICE_ATTR_WO(fs_ready);
+static int cnss_create_shutdown_sysfs(struct cnss_plat_data *plat_priv)
+{
+ int ret = 0;
+
+ plat_priv->shutdown_kobj = kobject_create_and_add("shutdown_wlan",
+ kernel_kobj);
+ if (!plat_priv->shutdown_kobj) {
+ cnss_pr_err("Failed to create shutdown_wlan kernel object\n");
+ return -ENOMEM;
+ }
+
+ ret = sysfs_create_file(plat_priv->shutdown_kobj,
+ &shutdown_attribute.attr);
+ if (ret) {
+ cnss_pr_err("Failed to create sysfs shutdown file, err = %d\n",
+ ret);
+ kobject_put(plat_priv->shutdown_kobj);
+ plat_priv->shutdown_kobj = NULL;
+ }
+
+ return ret;
+}
+
+static void cnss_remove_shutdown_sysfs(struct cnss_plat_data *plat_priv)
+{
+ if (plat_priv->shutdown_kobj) {
+ sysfs_remove_file(plat_priv->shutdown_kobj,
+ &shutdown_attribute.attr);
+ kobject_put(plat_priv->shutdown_kobj);
+ plat_priv->shutdown_kobj = NULL;
+ }
+}
+
static int cnss_create_sysfs(struct cnss_plat_data *plat_priv)
{
int ret = 0;
ret = device_create_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready);
if (ret) {
- cnss_pr_err("Failed to create device file, err = %d\n", ret);
+ cnss_pr_err("Failed to create device fs_ready file, err = %d\n",
+ ret);
goto out;
}
+ cnss_create_shutdown_sysfs(plat_priv);
+
return 0;
out:
return ret;
@@ -2011,6 +2080,7 @@ static int cnss_create_sysfs(struct cnss_plat_data *plat_priv)
static void cnss_remove_sysfs(struct cnss_plat_data *plat_priv)
{
+ cnss_remove_shutdown_sysfs(plat_priv);
device_remove_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready);
}
@@ -2105,6 +2175,17 @@ static void cnss_init_control_params(struct cnss_plat_data *plat_priv)
plat_priv->ctrl_params.time_sync_period = CNSS_TIME_SYNC_PERIOD_DEFAULT;
}
+static void cnss_get_wlaon_pwr_ctrl_info(struct cnss_plat_data *plat_priv)
+{
+ struct device *dev = &plat_priv->plat_dev->dev;
+
+ plat_priv->set_wlaon_pwr_ctrl =
+ of_property_read_bool(dev->of_node, "qcom,set-wlaon-pwr-ctrl");
+
+ cnss_pr_dbg("set_wlaon_pwr_ctrl is %d\n",
+ plat_priv->set_wlaon_pwr_ctrl);
+}
+
static const struct platform_device_id cnss_platform_id_table[] = {
{ .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, },
{ .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, },
@@ -2175,6 +2256,7 @@ static int cnss_probe(struct platform_device *plat_dev)
INIT_LIST_HEAD(&plat_priv->vreg_list);
INIT_LIST_HEAD(&plat_priv->clk_list);
+ cnss_get_wlaon_pwr_ctrl_info(plat_priv);
cnss_get_cpr_info(plat_priv);
cnss_init_control_params(plat_priv);
diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h
index b21fcbb..ef8939d 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_MAIN_H
#define _CNSS_MAIN_H
@@ -362,6 +362,8 @@ struct cnss_plat_data {
void *get_info_cb_ctx;
int (*get_info_cb)(void *ctx, void *event, int event_len);
u8 use_nv_mac;
+ u8 set_wlaon_pwr_ctrl;
+ struct kobject *shutdown_kobj;
};
#ifdef CONFIG_ARCH_QCOM
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 5517d8d..8120c8e 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -62,6 +62,9 @@ static DEFINE_SPINLOCK(time_sync_lock);
#define MHI_TIMEOUT_OVERWRITE_MS (plat_priv->ctrl_params.mhi_timeout)
#define MHI_M2_TIMEOUT_MS (plat_priv->ctrl_params.mhi_m2_timeout)
+#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US 1000
+#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US 2000
+
#define FORCE_WAKE_DELAY_MIN_US 4000
#define FORCE_WAKE_DELAY_MAX_US 6000
#define FORCE_WAKE_DELAY_TIMEOUT_US 60000
@@ -69,6 +72,8 @@ static DEFINE_SPINLOCK(time_sync_lock);
#define POWER_ON_RETRY_MAX_TIMES 3
#define POWER_ON_RETRY_DELAY_MS 200
+#define LINK_TRAINING_RETRY_MAX_TIMES 3
+
static struct cnss_pci_reg ce_src[] = {
{ "SRC_RING_BASE_LSB", QCA6390_CE_SRC_RING_BASE_LSB_OFFSET },
{ "SRC_RING_BASE_MSB", QCA6390_CE_SRC_RING_BASE_MSB_OFFSET },
@@ -465,6 +470,7 @@ static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv)
if (cnss_pci_is_device_awake(dev) != true) {
cnss_pr_err("Timed out to request force wake\n");
+ cnss_pci_force_wake_release(dev);
return -ETIMEDOUT;
}
@@ -487,6 +493,7 @@ int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset,
u32 *val)
{
int ret = 0;
+ bool do_force_wake_put = true;
ret = cnss_pci_is_device_down(&pci_priv->pci_dev->dev);
if (ret)
@@ -496,7 +503,9 @@ int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset,
if (ret < 0)
goto runtime_pm_put;
- cnss_pci_force_wake_get(pci_priv);
+ ret = cnss_pci_force_wake_get(pci_priv);
+ if (ret)
+ do_force_wake_put = false;
ret = cnss_pci_reg_read(pci_priv, offset, val);
if (ret) {
@@ -506,7 +515,8 @@ int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset,
}
force_wake_put:
- cnss_pci_force_wake_put(pci_priv);
+ if (do_force_wake_put)
+ cnss_pci_force_wake_put(pci_priv);
runtime_pm_put:
cnss_pci_pm_runtime_mark_last_busy(pci_priv);
cnss_pci_pm_runtime_put_autosuspend(pci_priv);
@@ -518,6 +528,7 @@ int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset,
u32 val)
{
int ret = 0;
+ bool do_force_wake_put = true;
ret = cnss_pci_is_device_down(&pci_priv->pci_dev->dev);
if (ret)
@@ -527,7 +538,9 @@ int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset,
if (ret < 0)
goto runtime_pm_put;
- cnss_pci_force_wake_get(pci_priv);
+ ret = cnss_pci_force_wake_get(pci_priv);
+ if (ret)
+ do_force_wake_put = false;
ret = cnss_pci_reg_write(pci_priv, offset, val);
if (ret) {
@@ -537,7 +550,8 @@ int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset,
}
force_wake_put:
- cnss_pci_force_wake_put(pci_priv);
+ if (do_force_wake_put)
+ cnss_pci_force_wake_put(pci_priv);
runtime_pm_put:
cnss_pci_pm_runtime_mark_last_busy(pci_priv);
cnss_pci_pm_runtime_put_autosuspend(pci_priv);
@@ -638,6 +652,7 @@ static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up)
int ret = 0;
struct pci_dev *pci_dev = pci_priv->pci_dev;
enum msm_pcie_pm_opt pm_ops;
+ int retry = 0;
cnss_pr_vdbg("%s PCI link\n", link_up ? "Resuming" : "Suspending");
@@ -653,11 +668,17 @@ static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up)
}
}
+retry:
ret = msm_pcie_pm_control(pm_ops, pci_dev->bus->number, pci_dev,
NULL, PM_OPTIONS_DEFAULT);
- if (ret)
+ if (ret) {
cnss_pr_err("Failed to %s PCI link with default option, err = %d\n",
link_up ? "resume" : "suspend", ret);
+ if (link_up && retry++ < LINK_TRAINING_RETRY_MAX_TIMES) {
+ cnss_pr_dbg("Retry PCI link training #%d\n", retry);
+ goto retry;
+ }
+ }
if (pci_priv->drv_connected_last) {
if ((link_up && !ret) || (!link_up && ret))
@@ -751,6 +772,22 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv)
return ret;
}
+int cnss_pci_prevent_l1(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+
+ return msm_pcie_prevent_l1(pci_dev);
+}
+EXPORT_SYMBOL(cnss_pci_prevent_l1);
+
+void cnss_pci_allow_l1(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+
+ msm_pcie_allow_l1(pci_dev);
+}
+EXPORT_SYMBOL(cnss_pci_allow_l1);
+
int cnss_pci_link_down(struct device *dev)
{
unsigned long flags;
@@ -1078,6 +1115,62 @@ static void cnss_pci_deinit_mhi(struct cnss_pci_data *pci_priv)
cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
}
+static void cnss_pci_set_wlaon_pwr_ctrl(struct cnss_pci_data *pci_priv,
+ bool set_vddd4blow, bool set_shutdown,
+ bool do_force_wake)
+{
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+ int ret;
+ u32 val;
+
+ if (!plat_priv->set_wlaon_pwr_ctrl)
+ return;
+
+ if (do_force_wake)
+ if (cnss_pci_force_wake_get(pci_priv))
+ return;
+
+ ret = cnss_pci_reg_read(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG,
+ &val);
+ if (ret) {
+ cnss_pr_err("Failed to read register offset 0x%x, err = %d\n",
+ QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret);
+ goto force_wake_put;
+ }
+
+ cnss_pr_dbg("Read register offset 0x%x, val = 0x%x\n",
+ QCA6390_WLAON_QFPROM_PWR_CTRL_REG, val);
+
+ if (set_vddd4blow)
+ val |= QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK;
+ else
+ val &= ~QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK;
+
+ if (set_shutdown)
+ val |= QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK;
+ else
+ val &= ~QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK;
+
+ ret = cnss_pci_reg_write(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG,
+ val);
+ if (ret) {
+ cnss_pr_err("Failed to write register offset 0x%x, err = %d\n",
+ QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret);
+ goto force_wake_put;
+ }
+
+ cnss_pr_dbg("Write val 0x%x to register offset 0x%x\n", val,
+ QCA6390_WLAON_QFPROM_PWR_CTRL_REG);
+
+ if (set_shutdown)
+ usleep_range(WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US,
+ WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US);
+
+force_wake_put:
+ if (do_force_wake)
+ cnss_pci_force_wake_put(pci_priv);
+}
+
static int cnss_pci_get_device_timestamp(struct cnss_pci_data *pci_priv,
u64 *time_us)
{
@@ -1389,7 +1482,8 @@ static void cnss_pci_misc_reg_dump(struct cnss_pci_data *pci_priv,
if (cnss_pci_check_link_status(pci_priv))
return;
- cnss_pci_force_wake_get(pci_priv);
+ if (cnss_pci_force_wake_get(pci_priv))
+ return;
cnss_pr_dbg("start to dump %s registers\n", reg_name);
@@ -1445,6 +1539,7 @@ static void cnss_pci_dump_shadow_reg(struct cnss_pci_data *pci_priv)
{
int i, j = 0, array_size = SHADOW_REG_COUNT + SHADOW_REG_INTER_COUNT;
u32 reg_offset;
+ bool do_force_wake_put = true;
if (in_interrupt() || irqs_disabled())
return;
@@ -1461,7 +1556,7 @@ static void cnss_pci_dump_shadow_reg(struct cnss_pci_data *pci_priv)
}
if (cnss_pci_force_wake_get(pci_priv))
- return;
+ do_force_wake_put = false;
cnss_pr_dbg("Start to dump shadow registers\n");
@@ -1482,7 +1577,8 @@ static void cnss_pci_dump_shadow_reg(struct cnss_pci_data *pci_priv)
}
force_wake_put:
- cnss_pci_force_wake_put(pci_priv);
+ if (do_force_wake_put)
+ cnss_pci_force_wake_put(pci_priv);
}
#ifdef CONFIG_CNSS2_DEBUG
@@ -1618,6 +1714,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv)
goto power_off;
}
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
timeout = cnss_get_boot_timeout(&pci_priv->pci_dev->dev);
ret = cnss_pci_start_mhi(pci_priv);
@@ -1652,6 +1749,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv)
return 0;
stop_mhi:
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, true);
cnss_pci_power_off_mhi(pci_priv);
cnss_suspend_pci_link(pci_priv);
cnss_pci_deinit_mhi(pci_priv);
@@ -1665,6 +1763,7 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv)
{
int ret = 0;
struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+ int do_force_wake = true;
cnss_pci_pm_runtime_resume(pci_priv);
@@ -1691,6 +1790,10 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv)
goto skip_power_off;
}
+ if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state))
+ do_force_wake = false;
+
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, do_force_wake);
cnss_pci_power_off_mhi(pci_priv);
ret = cnss_suspend_pci_link(pci_priv);
if (ret)
@@ -4083,6 +4186,7 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
case QCA6290_DEVICE_ID:
case QCA6390_DEVICE_ID:
case QCA6490_DEVICE_ID:
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
timer_setup(&pci_priv->dev_rddm_timer,
cnss_dev_rddm_timeout_hdlr, 0);
INIT_DELAYED_WORK(&pci_priv->time_sync_work,
@@ -4097,11 +4201,10 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
goto disable_bus;
}
cnss_pci_get_link_status(pci_priv);
-
cnss_pci_config_regs(pci_priv);
-
if (EMULATION_HW)
break;
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, false);
ret = cnss_suspend_pci_link(pci_priv);
if (ret)
cnss_pr_err("Failed to suspend PCI link, err = %d\n",
@@ -4202,6 +4305,7 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
int ret = 0;
struct device *dev = &plat_priv->plat_dev->dev;
u32 rc_num;
+ int retry = 0;
ret = of_property_read_u32(dev->of_node, "qcom,wlan-rc-num", &rc_num);
if (ret) {
@@ -4209,11 +4313,17 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
goto out;
}
+retry:
ret = msm_pcie_enumerate(rc_num);
if (ret) {
cnss_pr_err("Failed to enable PCIe RC%x, err = %d\n",
rc_num, ret);
- goto out;
+ if (retry++ < LINK_TRAINING_RETRY_MAX_TIMES) {
+ cnss_pr_dbg("Retry PCI link training #%d\n", retry);
+ goto retry;
+ } else {
+ goto out;
+ }
}
ret = pci_register_driver(&cnss_pci_driver);
diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c
index fb109fd..22f059c 100644
--- a/drivers/net/wireless/cnss2/qmi.c
+++ b/drivers/net/wireless/cnss2/qmi.c
@@ -1029,7 +1029,7 @@ int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv,
return -ENODEV;
if (!data || data_len == 0 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
- cnss_pr_err("Invalid parameters for athdiag read: data %p, data_len %u\n",
+ cnss_pr_err("Invalid parameters for athdiag read: data %pK, data_len %u\n",
data, data_len);
return -EINVAL;
}
@@ -1116,12 +1116,12 @@ int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv,
return -ENODEV;
if (!data || data_len == 0 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
- cnss_pr_err("Invalid parameters for athdiag write: data %p, data_len %u\n",
+ cnss_pr_err("Invalid parameters for athdiag write: data %pK, data_len %u\n",
data, data_len);
return -EINVAL;
}
- cnss_pr_dbg("athdiag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
+ cnss_pr_dbg("athdiag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %pK\n",
plat_priv->driver_state, offset, mem_type, data_len, data);
req = kzalloc(sizeof(*req), GFP_KERNEL);
diff --git a/drivers/net/wireless/cnss2/reg.h b/drivers/net/wireless/cnss2/reg.h
index 4052de4..6e7b709 100644
--- a/drivers/net/wireless/cnss2/reg.h
+++ b/drivers/net/wireless/cnss2/reg.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_REG_H
#define _CNSS_REG_H
@@ -204,6 +204,8 @@
#define QCA6390_WLAON_WL_CLK_CNTL_KDF_REG 0x1F80314
#define QCA6390_WLAON_WL_CLK_CNTL_PMU_HFRC_REG 0x1F80318
#define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C
+#define QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK 0x4
+#define QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK 0x1
#define QCA6390_WLAON_DLY_CONFIG 0x1F80400
#define QCA6390_WLAON_WLAON_Q6_IRQ_REG 0x1F80404
#define QCA6390_WLAON_PCIE_INTF_SW_CFG_REG 0x1F80408
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
index b4347806..d7335fa 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
@@ -143,7 +143,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = {
.ucode_api_min = IWL_22000_UCODE_API_MIN, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000, \
- .non_shared_ant = ANT_A, \
+ .non_shared_ant = ANT_B, \
.dccm_offset = IWL_22000_DCCM_OFFSET, \
.dccm_len = IWL_22000_DCCM_LEN, \
.dccm2_offset = IWL_22000_DCCM2_OFFSET, \
@@ -321,7 +321,6 @@ MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
index 1bbd17a..20e16c4 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
@@ -185,6 +185,9 @@ void iwl_leds_init(struct iwl_priv *priv)
priv->led.name = kasprintf(GFP_KERNEL, "%s-led",
wiphy_name(priv->hw->wiphy));
+ if (!priv->led.name)
+ return;
+
priv->led.brightness_set = iwl_led_brightness_set;
priv->led.blink_set = iwl_led_blink_set;
priv->led.max_brightness = 1;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index 030482b..06dd4e8 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -1227,6 +1227,23 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
return 0;
}
+static int iwl_nvm_check_version(struct iwl_nvm_data *data,
+ struct iwl_trans *trans)
+{
+ if (data->nvm_version >= trans->cfg->nvm_ver ||
+ data->calib_version >= trans->cfg->nvm_calib_ver) {
+ IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
+ data->nvm_version, data->calib_version);
+ return 0;
+ }
+
+ IWL_ERR(trans,
+ "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
+ data->nvm_version, trans->cfg->nvm_ver,
+ data->calib_version, trans->cfg->nvm_calib_ver);
+ return -EINVAL;
+}
+
static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
const struct iwl_cfg *cfg,
const struct iwl_fw *fw,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
index 2f59935..2ba1401 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
@@ -574,6 +574,69 @@ struct iwl_rx_mpdu_desc {
#define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1)
+#define IWL_CD_STTS_OPTIMIZED_POS 0
+#define IWL_CD_STTS_OPTIMIZED_MSK 0x01
+#define IWL_CD_STTS_TRANSFER_STATUS_POS 1
+#define IWL_CD_STTS_TRANSFER_STATUS_MSK 0x0E
+#define IWL_CD_STTS_WIFI_STATUS_POS 4
+#define IWL_CD_STTS_WIFI_STATUS_MSK 0xF0
+
+/**
+ * enum iwl_completion_desc_transfer_status - transfer status (bits 1-3)
+ * @IWL_CD_STTS_UNUSED: unused
+ * @IWL_CD_STTS_UNUSED_2: unused
+ * @IWL_CD_STTS_END_TRANSFER: successful transfer complete.
+ * In sniffer mode, when split is used, set in last CD completion. (RX)
+ * @IWL_CD_STTS_OVERFLOW: In sniffer mode, when using split - used for
+ * all CD completion. (RX)
+ * @IWL_CD_STTS_ABORTED: CR abort / close flow. (RX)
+ * @IWL_CD_STTS_ERROR: general error (RX)
+ */
+enum iwl_completion_desc_transfer_status {
+ IWL_CD_STTS_UNUSED,
+ IWL_CD_STTS_UNUSED_2,
+ IWL_CD_STTS_END_TRANSFER,
+ IWL_CD_STTS_OVERFLOW,
+ IWL_CD_STTS_ABORTED,
+ IWL_CD_STTS_ERROR,
+};
+
+/**
+ * enum iwl_completion_desc_wifi_status - wifi status (bits 4-7)
+ * @IWL_CD_STTS_VALID: the packet is valid (RX)
+ * @IWL_CD_STTS_FCS_ERR: frame check sequence error (RX)
+ * @IWL_CD_STTS_SEC_KEY_ERR: error handling the security key of rx (RX)
+ * @IWL_CD_STTS_DECRYPTION_ERR: error decrypting the frame (RX)
+ * @IWL_CD_STTS_DUP: duplicate packet (RX)
+ * @IWL_CD_STTS_ICV_MIC_ERR: MIC error (RX)
+ * @IWL_CD_STTS_INTERNAL_SNAP_ERR: problems removing the snap (RX)
+ * @IWL_CD_STTS_SEC_PORT_FAIL: security port fail (RX)
+ * @IWL_CD_STTS_BA_OLD_SN: block ack received old SN (RX)
+ * @IWL_CD_STTS_QOS_NULL: QoS null packet (RX)
+ * @IWL_CD_STTS_MAC_HDR_ERR: MAC header conversion error (RX)
+ * @IWL_CD_STTS_MAX_RETRANS: reached max number of retransmissions (TX)
+ * @IWL_CD_STTS_EX_LIFETIME: exceeded lifetime (TX)
+ * @IWL_CD_STTS_NOT_USED: completed but not used (RX)
+ * @IWL_CD_STTS_REPLAY_ERR: pn check failed, replay error (RX)
+ */
+enum iwl_completion_desc_wifi_status {
+ IWL_CD_STTS_VALID,
+ IWL_CD_STTS_FCS_ERR,
+ IWL_CD_STTS_SEC_KEY_ERR,
+ IWL_CD_STTS_DECRYPTION_ERR,
+ IWL_CD_STTS_DUP,
+ IWL_CD_STTS_ICV_MIC_ERR,
+ IWL_CD_STTS_INTERNAL_SNAP_ERR,
+ IWL_CD_STTS_SEC_PORT_FAIL,
+ IWL_CD_STTS_BA_OLD_SN,
+ IWL_CD_STTS_QOS_NULL,
+ IWL_CD_STTS_MAC_HDR_ERR,
+ IWL_CD_STTS_MAX_RETRANS,
+ IWL_CD_STTS_EX_LIFETIME,
+ IWL_CD_STTS_NOT_USED,
+ IWL_CD_STTS_REPLAY_ERR,
+};
+
struct iwl_frame_release {
u8 baid;
u8 reserved;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
index 514b861..80853f6 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
@@ -747,9 +747,9 @@ enum iwl_mvm_ba_resp_flags {
* @tfd_cnt: number of TFD-Q elements
* @ra_tid_cnt: number of RATID-Q elements
* @tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd
- * for details.
+ * for details. Length in @tfd_cnt.
* @ra_tid: array of RA-TID queue status updates. For debug purposes only. See
- * &iwl_mvm_compressed_ba_ratid for more details.
+ * &iwl_mvm_compressed_ba_ratid for more details. Length in @ra_tid_cnt.
*/
struct iwl_mvm_compressed_ba_notif {
__le32 flags;
@@ -766,7 +766,7 @@ struct iwl_mvm_compressed_ba_notif {
__le32 tx_rate;
__le16 tfd_cnt;
__le16 ra_tid_cnt;
- struct iwl_mvm_compressed_ba_tfd tfd[1];
+ struct iwl_mvm_compressed_ba_tfd tfd[0];
struct iwl_mvm_compressed_ba_ratid ra_tid[0];
} __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index a31a42e..3443cbd 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -824,7 +824,7 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
}
/* We only dump the FIFOs if the FW is in error state */
- if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
+ if (fifo_data_len) {
iwl_fw_dump_fifos(fwrt, &dump_data);
if (radio_len)
iwl_read_radio_regs(fwrt, &dump_data);
@@ -1016,7 +1016,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
* If the loading of the FW completed successfully, the next step is to
* get the SMEM config data. Thus, if fwrt->smem_cfg.num_lmacs is non
* zero, the FW was already loaded successully. If the state is "NO_FW"
- * in such a case - WARN and exit, since FW may be dead. Otherwise, we
+ * in such a case - exit, since FW may be dead. Otherwise, we
* can try to collect the data, since FW might just not be fully
* loaded (no "ALIVE" yet), and the debug data is accessible.
*
@@ -1024,9 +1024,8 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
* config. In such a case, due to HW access problems, we might
* collect garbage.
*/
- if (WARN((fwrt->trans->state == IWL_TRANS_NO_FW) &&
- fwrt->smem_cfg.num_lmacs,
- "Can't collect dbg data when FW isn't alive\n"))
+ if (fwrt->trans->state == IWL_TRANS_NO_FW &&
+ fwrt->smem_cfg.num_lmacs)
return -EIO;
if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status))
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
index a4c9621..a59bab8 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
@@ -928,22 +928,3 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
return NULL;
}
IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data);
-
-/* helper functions */
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
- struct iwl_trans *trans)
-{
- if (data->nvm_version >= trans->cfg->nvm_ver ||
- data->calib_version >= trans->cfg->nvm_calib_ver) {
- IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
- data->nvm_version, data->calib_version);
- return 0;
- }
-
- IWL_ERR(trans,
- "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
- data->nvm_version, trans->cfg->nvm_ver,
- data->calib_version, trans->cfg->nvm_calib_ver);
- return -EINVAL;
-}
-IWL_EXPORT_SYMBOL(iwl_nvm_check_version);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
index 8be50ed1..c59dd47 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -122,9 +124,6 @@ struct iwl_nvm_data *
iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
const u8 *eeprom, size_t eeprom_size);
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
- struct iwl_trans *trans);
-
int iwl_init_sband_channels(struct iwl_nvm_data *data,
struct ieee80211_supported_band *sband,
int n_channels, enum nl80211_band band);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
index 421a869..2e512f6 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -8,6 +8,7 @@
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +36,7 @@
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -399,6 +401,7 @@ enum aux_misc_master1_en {
#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800
#define RSA_ENABLE 0xA24B08
#define PREG_AUX_BUS_WPROT_0 0xA04CC0
+#define PREG_PRPH_WPROT_0 0xA04CE0
#define SB_CPU_1_STATUS 0xA01E30
#define SB_CPU_2_STATUS 0xA01E34
#define UMAG_SB_CPU_1_STATUS 0xA038C0
@@ -425,4 +428,8 @@ enum {
#define UREG_CHICK (0xA05C00)
#define UREG_CHICK_MSI_ENABLE BIT(24)
#define UREG_CHICK_MSIX_ENABLE BIT(25)
+
+#define HPM_DEBUG 0xA03440
+#define PERSISTENCE_BIT BIT(12)
+#define PREG_WFPM_ACCESS BIT(12)
#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 279dd7b..0b8cf7f 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -269,6 +269,7 @@ struct iwl_rx_cmd_buffer {
bool _page_stolen;
u32 _rx_page_order;
unsigned int truesize;
+ u8 status;
};
static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 79bdae9..868cb11 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -731,8 +731,10 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
{
struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
+ bool unified = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
struct wowlan_key_data key_data = {
- .configure_keys = !d0i3,
+ .configure_keys = !d0i3 && !unified,
.use_rsc_tsc = false,
.tkip = &tkip_cmd,
.use_tkip = false,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 9cb9f05..9808d95 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -547,7 +547,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
if (mvm->nvm_file_name)
iwl_mvm_load_nvm_to_nic(mvm);
- WARN_ON(iwl_nvm_check_version(mvm->nvm_data, mvm->trans));
+ WARN_ONCE(mvm->nvm_data->nvm_version < mvm->trans->cfg->nvm_ver,
+ "Too old NVM version (0x%0x, required = 0x%0x)",
+ mvm->nvm_data->nvm_version, mvm->trans->cfg->nvm_ver);
/*
* abort after reading the nvm in case RF Kill is on, we will complete
@@ -843,15 +845,17 @@ static bool iwl_mvm_sar_geo_support(struct iwl_mvm *mvm)
* firmware versions. Unfortunately, we don't have a TLV API
* flag to rely on, so rely on the major version which is in
* the first byte of ucode_ver. This was implemented
- * initially on version 38 and then backported to29 and 17.
- * The intention was to have it in 36 as well, but not all
- * 8000 family got this feature enabled. The 8000 family is
- * the only one using version 36, so skip this version
- * entirely.
+ * initially on version 38 and then backported to 17. It was
+ * also backported to 29, but only for 7265D devices. The
+ * intention was to have it in 36 as well, but not all 8000
+ * family got this feature enabled. The 8000 family is the
+ * only one using version 36, so skip this version entirely.
*/
return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 38 ||
- IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 ||
- IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17;
+ IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17 ||
+ (IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 &&
+ ((mvm->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
+ CSR_HW_REV_TYPE_7265D));
}
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
index b272695..072f80c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
@@ -131,6 +131,9 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm)
mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
wiphy_name(mvm->hw->wiphy));
+ if (!mvm->led.name)
+ return -ENOMEM;
+
mvm->led.brightness_set = iwl_led_brightness_set;
mvm->led.max_brightness = 1;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index b3fd205..d90d583 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -85,6 +85,10 @@ const u8 iwl_mvm_ac_to_gen2_tx_fifo[] = {
IWL_GEN2_EDCA_TX_FIFO_VI,
IWL_GEN2_EDCA_TX_FIFO_BE,
IWL_GEN2_EDCA_TX_FIFO_BK,
+ IWL_GEN2_TRIG_TX_FIFO_VO,
+ IWL_GEN2_TRIG_TX_FIFO_VI,
+ IWL_GEN2_TRIG_TX_FIFO_BE,
+ IWL_GEN2_TRIG_TX_FIFO_BK,
};
struct iwl_mvm_mac_iface_iterator_data {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 0f357e8..476c44d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -820,6 +820,21 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
!ieee80211_is_bufferable_mmpdu(hdr->frame_control))
sta = NULL;
+ /* If there is no sta, and it's not offchannel - send through AP */
+ if (info->control.vif->type == NL80211_IFTYPE_STATION &&
+ info->hw_queue != IWL_MVM_OFFCHANNEL_QUEUE && !sta) {
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(info->control.vif);
+ u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
+
+ if (ap_sta_id < IWL_MVM_STATION_COUNT) {
+ /* mac80211 holds rcu read lock */
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]);
+ if (IS_ERR_OR_NULL(sta))
+ goto drop;
+ }
+ }
+
if (sta) {
if (iwl_mvm_defer_tx(mvm, sta, skb))
return;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index bfb1634..e6a67bc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -62,6 +62,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
+#include <asm/unaligned.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "iwl-trans.h"
@@ -360,7 +361,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
len = le16_to_cpu(rx_res->byte_count);
- rx_pkt_status = le32_to_cpup((__le32 *)
+ rx_pkt_status = get_unaligned_le32((__le32 *)
(pkt->data + sizeof(*rx_res) + len));
/* Dont use dev_alloc_skb(), we'll have enough headroom once
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 18db1ed..e850aa50 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -440,6 +440,16 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
rcu_read_unlock();
+ /*
+ * The TX path may have been using this TXQ_ID from the tid_data,
+ * so make sure it's no longer running so that we can safely reuse
+ * this TXQ later. We've set all the TIDs to IWL_MVM_INVALID_QUEUE
+ * above, but nothing guarantees we've stopped using them. Thus,
+ * without this, we could get to iwl_mvm_disable_txq() and remove
+ * the queue while still sending frames to it.
+ */
+ synchronize_net();
+
return disable_agg_tids;
}
@@ -3133,10 +3143,6 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
switch (keyconf->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (vif->type == NL80211_IFTYPE_AP) {
- ret = -EINVAL;
- break;
- }
addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
/* get phase 1 key from mac80211 */
ieee80211_get_key_rx_seq(keyconf, 0, &seq);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 5615ce5..449e3d3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -778,6 +778,36 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
return 0;
}
+static unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ unsigned int tid)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band;
+ u8 ac = tid_to_mac80211_ac[tid];
+ unsigned int txf;
+ int lmac = IWL_LMAC_24G_INDEX;
+
+ if (iwl_mvm_is_cdb_supported(mvm) &&
+ band == NL80211_BAND_5GHZ)
+ lmac = IWL_LMAC_5G_INDEX;
+
+ /* For HE redirect to trigger based fifos */
+ if (sta->he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm)))
+ ac += 4;
+
+ txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac);
+
+ /*
+ * Don't send an AMSDU that will be longer than the TXF.
+ * Add a security margin of 256 for the TX command + headers.
+ * We also want to have the start of the next packet inside the
+ * fifo to be able to send bursts.
+ */
+ return min_t(unsigned int, mvmsta->max_amsdu_len,
+ mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
+}
+
static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
@@ -790,7 +820,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
u16 snap_ip_tcp, pad;
unsigned int dbg_max_amsdu_len;
netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG;
- u8 tid, txf;
+ u8 tid;
snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
tcp_hdrlen(skb);
@@ -829,20 +859,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
!(mvmsta->amsdu_enabled & BIT(tid)))
return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
- max_amsdu_len = mvmsta->max_amsdu_len;
-
- /* the Tx FIFO to which this A-MSDU will be routed */
- txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, tid_to_mac80211_ac[tid]);
-
- /*
- * Don't send an AMSDU that will be longer than the TXF.
- * Add a security margin of 256 for the TX command + headers.
- * We also want to have the start of the next packet inside the
- * fifo to be able to send bursts.
- */
- max_amsdu_len = min_t(unsigned int, max_amsdu_len,
- mvm->fwrt.smem_cfg.lmac[0].txfifo_size[txf] -
- 256);
+ max_amsdu_len = iwl_mvm_max_amsdu_size(mvm, sta, tid);
if (unlikely(dbg_max_amsdu_len))
max_amsdu_len = min_t(unsigned int, max_amsdu_len,
@@ -1438,6 +1455,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
break;
}
+ /*
+ * If we are freeing multiple frames, mark all the frames
+ * but the first one as acked, since they were acknowledged
+ * before
+ * */
+ if (skb_freed > 1)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
iwl_mvm_tx_status_check_trigger(mvm, status);
info->status.rates[0].count = tx_resp->failure_frame + 1;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index 6a53494..0071220 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -1804,6 +1804,7 @@ void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel)
void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
{
int mac;
+ bool low_latency = false;
spin_lock_bh(&mvm->tcm.lock);
mvm->tcm.ts = jiffies;
@@ -1815,10 +1816,23 @@ void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
memset(&mdata->tx.pkts, 0, sizeof(mdata->tx.pkts));
memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime));
memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime));
+
+ if (mvm->tcm.result.low_latency[mac])
+ low_latency = true;
}
/* The TCM data needs to be reset before "paused" flag changes */
smp_mb();
mvm->tcm.paused = false;
+
+ /*
+ * if the current load is not low or low latency is active, force
+ * re-evaluation to cover the case of no traffic.
+ */
+ if (mvm->tcm.result.global_load > IWL_MVM_TRAFFIC_LOW)
+ schedule_delayed_work(&mvm->tcm.work, MVM_TCM_PERIOD);
+ else if (low_latency)
+ schedule_delayed_work(&mvm->tcm.work, MVM_LL_PERIOD);
+
spin_unlock_bh(&mvm->tcm.lock);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 0982bd9..844a100 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -888,7 +888,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22000_2ax_cfg_hr)},
- {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ac_cfg_jf)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22560_2ax_cfg_su_cdb)},
{IWL_PCI_DEVICE(0x40C0, 0x0010, iwl22560_2ax_cfg_su_cdb)},
{IWL_PCI_DEVICE(0x40c0, 0x0090, iwl22560_2ax_cfg_su_cdb)},
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index 00f9566..e9d67ba 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -102,66 +102,6 @@ struct isr_statistics {
u32 unhandled;
};
-#define IWL_CD_STTS_OPTIMIZED_POS 0
-#define IWL_CD_STTS_OPTIMIZED_MSK 0x01
-#define IWL_CD_STTS_TRANSFER_STATUS_POS 1
-#define IWL_CD_STTS_TRANSFER_STATUS_MSK 0x0E
-#define IWL_CD_STTS_WIFI_STATUS_POS 4
-#define IWL_CD_STTS_WIFI_STATUS_MSK 0xF0
-
-/**
- * enum iwl_completion_desc_transfer_status - transfer status (bits 1-3)
- * @IWL_CD_STTS_END_TRANSFER: successful transfer complete.
- * In sniffer mode, when split is used, set in last CD completion. (RX)
- * @IWL_CD_STTS_OVERFLOW: In sniffer mode, when using split - used for
- * all CD completion. (RX)
- * @IWL_CD_STTS_ABORTED: CR abort / close flow. (RX)
- */
-enum iwl_completion_desc_transfer_status {
- IWL_CD_STTS_UNUSED,
- IWL_CD_STTS_UNUSED_2,
- IWL_CD_STTS_END_TRANSFER,
- IWL_CD_STTS_OVERFLOW,
- IWL_CD_STTS_ABORTED,
- IWL_CD_STTS_ERROR,
-};
-
-/**
- * enum iwl_completion_desc_wifi_status - wifi status (bits 4-7)
- * @IWL_CD_STTS_VALID: the packet is valid (RX)
- * @IWL_CD_STTS_FCS_ERR: frame check sequence error (RX)
- * @IWL_CD_STTS_SEC_KEY_ERR: error handling the security key of rx (RX)
- * @IWL_CD_STTS_DECRYPTION_ERR: error decrypting the frame (RX)
- * @IWL_CD_STTS_DUP: duplicate packet (RX)
- * @IWL_CD_STTS_ICV_MIC_ERR: MIC error (RX)
- * @IWL_CD_STTS_INTERNAL_SNAP_ERR: problems removing the snap (RX)
- * @IWL_CD_STTS_SEC_PORT_FAIL: security port fail (RX)
- * @IWL_CD_STTS_BA_OLD_SN: block ack received old SN (RX)
- * @IWL_CD_STTS_QOS_NULL: QoS null packet (RX)
- * @IWL_CD_STTS_MAC_HDR_ERR: MAC header conversion error (RX)
- * @IWL_CD_STTS_MAX_RETRANS: reached max number of retransmissions (TX)
- * @IWL_CD_STTS_EX_LIFETIME: exceeded lifetime (TX)
- * @IWL_CD_STTS_NOT_USED: completed but not used (RX)
- * @IWL_CD_STTS_REPLAY_ERR: pn check failed, replay error (RX)
- */
-enum iwl_completion_desc_wifi_status {
- IWL_CD_STTS_VALID,
- IWL_CD_STTS_FCS_ERR,
- IWL_CD_STTS_SEC_KEY_ERR,
- IWL_CD_STTS_DECRYPTION_ERR,
- IWL_CD_STTS_DUP,
- IWL_CD_STTS_ICV_MIC_ERR,
- IWL_CD_STTS_INTERNAL_SNAP_ERR,
- IWL_CD_STTS_SEC_PORT_FAIL,
- IWL_CD_STTS_BA_OLD_SN,
- IWL_CD_STTS_QOS_NULL,
- IWL_CD_STTS_MAC_HDR_ERR,
- IWL_CD_STTS_MAX_RETRANS,
- IWL_CD_STTS_EX_LIFETIME,
- IWL_CD_STTS_NOT_USED,
- IWL_CD_STTS_REPLAY_ERR,
-};
-
#define IWL_RX_TD_TYPE_MSK 0xff000000
#define IWL_RX_TD_SIZE_MSK 0x00ffffff
#define IWL_RX_TD_SIZE_2K BIT(11)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
index 1d14498..80a1a50 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
@@ -1198,7 +1198,8 @@ static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans,
static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
struct iwl_rxq *rxq,
struct iwl_rx_mem_buffer *rxb,
- bool emergency)
+ bool emergency,
+ int i)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue];
@@ -1224,6 +1225,9 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
.truesize = max_len,
};
+ if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
+ rxcb.status = rxq->cd[i].status;
+
pkt = rxb_addr(&rxcb);
if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) {
@@ -1430,7 +1434,7 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue)
goto out;
IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
- iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency);
+ iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency, i);
i = (i + 1) & (rxq->queue_size - 1);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index 7d319b6..4f55711 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -1747,6 +1747,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 hpm;
int err;
lockdep_assert_held(&trans_pcie->mutex);
@@ -1757,6 +1758,17 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
return err;
}
+ hpm = iwl_trans_read_prph(trans, HPM_DEBUG);
+ if (hpm != 0xa5a5a5a0 && (hpm & PERSISTENCE_BIT)) {
+ if (iwl_trans_read_prph(trans, PREG_PRPH_WPROT_0) &
+ PREG_WFPM_ACCESS) {
+ IWL_ERR(trans,
+ "Error, can not clear persistence bit\n");
+ return -EPERM;
+ }
+ iwl_trans_write_prph(trans, HPM_DEBUG, hpm & ~PERSISTENCE_BIT);
+ }
+
iwl_trans_pcie_sw_reset(trans);
err = iwl_pcie_apm_init(trans);
@@ -1830,18 +1842,30 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
}
+static u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans)
+{
+ if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
+ return 0x00FFFFFF;
+ else
+ return 0x000FFFFF;
+}
+
static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg)
{
+ u32 mask = iwl_trans_pcie_prph_msk(trans);
+
iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR,
- ((reg & 0x000FFFFF) | (3 << 24)));
+ ((reg & mask) | (3 << 24)));
return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT);
}
static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,
u32 val)
{
+ u32 mask = iwl_trans_pcie_prph_msk(trans);
+
iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR,
- ((addr & 0x000FFFFF) | (3 << 24)));
+ ((addr & mask) | (3 << 24)));
iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
}
@@ -3391,8 +3415,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
#if IS_ENABLED(CONFIG_IWLMVM)
trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
- if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
- CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
+ if (cfg == &iwl22000_2ax_cfg_hr) {
+ if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+ CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
+ trans->cfg = &iwl22000_2ax_cfg_hr;
+ } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+ CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF)) {
+ trans->cfg = &iwl22000_2ax_cfg_jf;
+ } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+ CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HRCDB)) {
+ IWL_ERR(trans, "RF ID HRCDB is not supported\n");
+ ret = -EINVAL;
+ goto out_no_pci;
+ } else {
+ IWL_ERR(trans, "Unrecognized RF ID 0x%08x\n",
+ CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id));
+ ret = -EINVAL;
+ goto out_no_pci;
+ }
+ } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+ CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
u32 hw_status;
hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
index b99f33f..7b1dff9 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
@@ -242,27 +242,23 @@ static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans,
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
unsigned int mss = skb_shinfo(skb)->gso_size;
- u16 length, iv_len, amsdu_pad;
+ u16 length, amsdu_pad;
u8 *start_hdr;
struct iwl_tso_hdr_page *hdr_page;
struct page **page_ptr;
struct tso_t tso;
- /* if the packet is protected, then it must be CCMP or GCMP */
- iv_len = ieee80211_has_protected(hdr->frame_control) ?
- IEEE80211_CCMP_HDR_LEN : 0;
-
trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd),
&dev_cmd->hdr, start_len, 0);
ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb);
snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb);
- total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len - iv_len;
+ total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len;
amsdu_pad = 0;
/* total amount of header we may need for this A-MSDU */
hdr_room = DIV_ROUND_UP(total_len, mss) *
- (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len;
+ (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr));
/* Our device supports 9 segments at most, it will fit in 1 page */
hdr_page = get_page_hdr(trans, hdr_room);
@@ -273,14 +269,12 @@ static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans,
start_hdr = hdr_page->pos;
page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs);
*page_ptr = hdr_page->page;
- memcpy(hdr_page->pos, skb->data + hdr_len, iv_len);
- hdr_page->pos += iv_len;
/*
- * Pull the ieee80211 header + IV to be able to use TSO core,
+ * Pull the ieee80211 header to be able to use TSO core,
* we will restore it for the tx_status flow.
*/
- skb_pull(skb, hdr_len + iv_len);
+ skb_pull(skb, hdr_len);
/*
* Remove the length of all the headers that we don't actually
@@ -355,8 +349,8 @@ static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans,
}
}
- /* re -add the WiFi header and IV */
- skb_push(skb, hdr_len + iv_len);
+ /* re -add the WiFi header */
+ skb_push(skb, hdr_len);
return 0;
@@ -526,7 +520,12 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans,
hdr_len = ieee80211_hdrlen(hdr->frame_control);
- if (amsdu)
+ /*
+ * Only build A-MSDUs here if doing so by GSO, otherwise it may be
+ * an A-MSDU for other reasons, e.g. NAN or an A-MSDU having been
+ * built in the higher layers already.
+ */
+ if (amsdu && skb_shinfo(skb)->gso_size)
return iwl_pcie_gen2_build_tx_amsdu(trans, txq, dev_cmd, skb,
out_meta, hdr_len, len);
@@ -555,18 +554,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
spin_lock(&txq->lock);
- if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) {
- struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
- (void *)dev_cmd->payload;
-
- cmd_len = le16_to_cpu(tx_cmd_gen3->len);
- } else {
- struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
- (void *)dev_cmd->payload;
-
- cmd_len = le16_to_cpu(tx_cmd_gen2->len);
- }
-
if (iwl_queue_space(trans, txq) < txq->high_mark) {
iwl_stop_queue(trans, txq);
@@ -604,6 +591,18 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
return -1;
}
+ if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) {
+ struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
+ (void *)dev_cmd->payload;
+
+ cmd_len = le16_to_cpu(tx_cmd_gen3->len);
+ } else {
+ struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
+ (void *)dev_cmd->payload;
+
+ cmd_len = le16_to_cpu(tx_cmd_gen2->len);
+ }
+
/* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_gen2_update_byte_tbl(trans_pcie, txq, cmd_len,
iwl_pcie_gen2_get_num_tbs(trans, tfd));
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index 42fdb79..b73582ec 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -1103,7 +1103,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
if (!iwl_queue_used(txq, last_to_free)) {
IWL_ERR(trans,
- "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
+ "%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, last_to_free,
trans->cfg->base_params->max_tfd_queue_size,
txq->write_ptr, txq->read_ptr);
@@ -1247,11 +1247,11 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
if (idx >= trans->cfg->base_params->max_tfd_queue_size ||
(!iwl_queue_used(txq, idx))) {
- IWL_ERR(trans,
- "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
- __func__, txq_id, idx,
- trans->cfg->base_params->max_tfd_queue_size,
- txq->write_ptr, txq->read_ptr);
+ WARN_ONCE(test_bit(txq_id, trans_pcie->queue_used),
+ "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
+ __func__, txq_id, idx,
+ trans->cfg->base_params->max_tfd_queue_size,
+ txq->write_ptr, txq->read_ptr);
return;
}
diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c
index 39bf85d..c7f8a29 100644
--- a/drivers/net/wireless/marvell/libertas/if_sdio.c
+++ b/drivers/net/wireless/marvell/libertas/if_sdio.c
@@ -1183,6 +1183,10 @@ static int if_sdio_probe(struct sdio_func *func,
spin_lock_init(&card->lock);
card->workqueue = alloc_workqueue("libertas_sdio", WQ_MEM_RECLAIM, 0);
+ if (unlikely(!card->workqueue)) {
+ ret = -ENOMEM;
+ goto err_queue;
+ }
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
init_waitqueue_head(&card->pwron_waitq);
@@ -1234,6 +1238,7 @@ static int if_sdio_probe(struct sdio_func *func,
lbs_remove_card(priv);
free:
destroy_workqueue(card->workqueue);
+err_queue:
while (card->packets) {
packet = card->packets;
card->packets = card->packets->next;
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 47ec529..7b74ef7 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -376,11 +376,20 @@ mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
struct mwifiex_power_cfg power_cfg;
int dbm = MBM_TO_DBM(mbm);
- if (type == NL80211_TX_POWER_FIXED) {
+ switch (type) {
+ case NL80211_TX_POWER_FIXED:
power_cfg.is_power_auto = 0;
+ power_cfg.is_power_fixed = 1;
power_cfg.power_level = dbm;
- } else {
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ power_cfg.is_power_auto = 0;
+ power_cfg.is_power_fixed = 0;
+ power_cfg.power_level = dbm;
+ break;
+ case NL80211_TX_POWER_AUTOMATIC:
power_cfg.is_power_auto = 1;
+ break;
}
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index cce7025..cbe4493 100644
--- a/drivers/net/wireless/marvell/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -273,15 +273,13 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
"total samples = %d\n",
atomic_read(&phist_data->num_samples));
- p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M");
- p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n");
- p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M");
- p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+ p += sprintf(p,
+ "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n"
+ "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
- p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
- p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
- p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
+ p += sprintf(p,
+ "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n");
} else {
p += sprintf(p, "\n");
}
@@ -310,7 +308,7 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
value = atomic_read(&phist_data->noise_flr[i]);
if (value)
- p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
+ p += sprintf(p, "noise_flr[%02ddBm] = %d\n",
(int)(i-128), value);
}
for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h
index 48e154e..0dd592e 100644
--- a/drivers/net/wireless/marvell/mwifiex/ioctl.h
+++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h
@@ -267,6 +267,7 @@ struct mwifiex_ds_encrypt_key {
struct mwifiex_power_cfg {
u32 is_power_auto;
+ u32 is_power_fixed;
u32 power_level;
};
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 20cee5c3..e48b47f 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -960,10 +960,10 @@ int mwifiex_set_mac_address(struct mwifiex_private *priv,
mac_addr = old_mac_addr;
- if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) {
mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
-
- if (mwifiex_get_intf_num(priv->adapter, priv->bss_type) > 1) {
+ mac_addr += priv->bss_num;
+ } else if (priv->adapter->priv[0] != priv) {
/* Set mac address based on bss_type/bss_num */
mac_addr ^= BIT_ULL(priv->bss_type + 8);
mac_addr += priv->bss_num;
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index 3fe81b2..918c699 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -691,8 +691,11 @@ static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
skb_put(skb, MAX_EVENT_SIZE);
if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE,
- PCI_DMA_FROMDEVICE))
+ PCI_DMA_FROMDEVICE)) {
+ kfree_skb(skb);
+ kfree(card->evtbd_ring_vbase);
return -1;
+ }
buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
index ed27147..dd02bbd 100644
--- a/drivers/net/wireless/marvell/mwifiex/scan.c
+++ b/drivers/net/wireless/marvell/mwifiex/scan.c
@@ -1906,15 +1906,17 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
ETH_ALEN))
mwifiex_update_curr_bss_params(priv,
bss);
- cfg80211_put_bss(priv->wdev.wiphy, bss);
- }
- if ((chan->flags & IEEE80211_CHAN_RADAR) ||
- (chan->flags & IEEE80211_CHAN_NO_IR)) {
- mwifiex_dbg(adapter, INFO,
- "radar or passive channel %d\n",
- channel);
- mwifiex_save_hidden_ssid_channels(priv, bss);
+ if ((chan->flags & IEEE80211_CHAN_RADAR) ||
+ (chan->flags & IEEE80211_CHAN_NO_IR)) {
+ mwifiex_dbg(adapter, INFO,
+ "radar or passive channel %d\n",
+ channel);
+ mwifiex_save_hidden_ssid_channels(priv,
+ bss);
+ }
+
+ cfg80211_put_bss(priv->wdev.wiphy, bss);
}
}
} else {
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index 843d65b..74e5056 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -688,6 +688,9 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
if (!power_cfg->is_power_auto) {
+ u16 dbm_min = power_cfg->is_power_fixed ?
+ dbm : priv->min_tx_power_level;
+
txp_cfg->mode = cpu_to_le32(1);
pg_tlv = (struct mwifiex_types_power_group *)
(buf + sizeof(struct host_cmd_ds_txpwr_cfg));
@@ -702,7 +705,7 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
pg->last_rate_code = 0x03;
pg->modulation_class = MOD_CLASS_HR_DSSS;
pg->power_step = 0;
- pg->power_min = (s8) dbm;
+ pg->power_min = (s8) dbm_min;
pg->power_max = (s8) dbm;
pg++;
/* Power group for modulation class OFDM */
@@ -710,7 +713,7 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
pg->last_rate_code = 0x07;
pg->modulation_class = MOD_CLASS_OFDM;
pg->power_step = 0;
- pg->power_min = (s8) dbm;
+ pg->power_min = (s8) dbm_min;
pg->power_max = (s8) dbm;
pg++;
/* Power group for modulation class HTBW20 */
@@ -718,7 +721,7 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
pg->last_rate_code = 0x20;
pg->modulation_class = MOD_CLASS_HT;
pg->power_step = 0;
- pg->power_min = (s8) dbm;
+ pg->power_min = (s8) dbm_min;
pg->power_max = (s8) dbm;
pg->ht_bandwidth = HT_BW_20;
pg++;
@@ -727,7 +730,7 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
pg->last_rate_code = 0x20;
pg->modulation_class = MOD_CLASS_HT;
pg->power_step = 0;
- pg->power_min = (s8) dbm;
+ pg->power_min = (s8) dbm_min;
pg->power_max = (s8) dbm;
pg->ht_bandwidth = HT_BW_40;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 27779d7..6058c48 100644
--- a/drivers/net/wireless/marvell/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
@@ -956,59 +956,117 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
switch (*pos) {
case WLAN_EID_SUPP_RATES:
+ if (pos[1] > 32)
+ return;
sta_ptr->tdls_cap.rates_len = pos[1];
for (i = 0; i < pos[1]; i++)
sta_ptr->tdls_cap.rates[i] = pos[i + 2];
break;
case WLAN_EID_EXT_SUPP_RATES:
+ if (pos[1] > 32)
+ return;
basic = sta_ptr->tdls_cap.rates_len;
+ if (pos[1] > 32 - basic)
+ return;
for (i = 0; i < pos[1]; i++)
sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2];
sta_ptr->tdls_cap.rates_len += pos[1];
break;
case WLAN_EID_HT_CAPABILITY:
- memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos,
+ if (pos > end - sizeof(struct ieee80211_ht_cap) - 2)
+ return;
+ if (pos[1] != sizeof(struct ieee80211_ht_cap))
+ return;
+ /* copy the ie's value into ht_capb*/
+ memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos + 2,
sizeof(struct ieee80211_ht_cap));
sta_ptr->is_11n_enabled = 1;
break;
case WLAN_EID_HT_OPERATION:
- memcpy(&sta_ptr->tdls_cap.ht_oper, pos,
+ if (pos > end -
+ sizeof(struct ieee80211_ht_operation) - 2)
+ return;
+ if (pos[1] != sizeof(struct ieee80211_ht_operation))
+ return;
+ /* copy the ie's value into ht_oper*/
+ memcpy(&sta_ptr->tdls_cap.ht_oper, pos + 2,
sizeof(struct ieee80211_ht_operation));
break;
case WLAN_EID_BSS_COEX_2040:
+ if (pos > end - 3)
+ return;
+ if (pos[1] != 1)
+ return;
sta_ptr->tdls_cap.coex_2040 = pos[2];
break;
case WLAN_EID_EXT_CAPABILITY:
+ if (pos > end - sizeof(struct ieee_types_header))
+ return;
+ if (pos[1] < sizeof(struct ieee_types_header))
+ return;
+ if (pos[1] > 8)
+ return;
memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos,
sizeof(struct ieee_types_header) +
min_t(u8, pos[1], 8));
break;
case WLAN_EID_RSN:
+ if (pos > end - sizeof(struct ieee_types_header))
+ return;
+ if (pos[1] < sizeof(struct ieee_types_header))
+ return;
+ if (pos[1] > IEEE_MAX_IE_SIZE -
+ sizeof(struct ieee_types_header))
+ return;
memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos,
sizeof(struct ieee_types_header) +
min_t(u8, pos[1], IEEE_MAX_IE_SIZE -
sizeof(struct ieee_types_header)));
break;
case WLAN_EID_QOS_CAPA:
+ if (pos > end - 3)
+ return;
+ if (pos[1] != 1)
+ return;
sta_ptr->tdls_cap.qos_info = pos[2];
break;
case WLAN_EID_VHT_OPERATION:
- if (priv->adapter->is_hw_11ac_capable)
- memcpy(&sta_ptr->tdls_cap.vhtoper, pos,
+ if (priv->adapter->is_hw_11ac_capable) {
+ if (pos > end -
+ sizeof(struct ieee80211_vht_operation) - 2)
+ return;
+ if (pos[1] !=
+ sizeof(struct ieee80211_vht_operation))
+ return;
+ /* copy the ie's value into vhtoper*/
+ memcpy(&sta_ptr->tdls_cap.vhtoper, pos + 2,
sizeof(struct ieee80211_vht_operation));
+ }
break;
case WLAN_EID_VHT_CAPABILITY:
if (priv->adapter->is_hw_11ac_capable) {
- memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos,
+ if (pos > end -
+ sizeof(struct ieee80211_vht_cap) - 2)
+ return;
+ if (pos[1] != sizeof(struct ieee80211_vht_cap))
+ return;
+ /* copy the ie's value into vhtcap*/
+ memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos + 2,
sizeof(struct ieee80211_vht_cap));
sta_ptr->is_11ac_enabled = 1;
}
break;
case WLAN_EID_AID:
- if (priv->adapter->is_hw_11ac_capable)
+ if (priv->adapter->is_hw_11ac_capable) {
+ if (pos > end - 4)
+ return;
+ if (pos[1] != 2)
+ return;
sta_ptr->tdls_cap.aid =
get_unaligned_le16((pos + 2));
+ }
+ break;
default:
break;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index 433c6a16..d445acc 100644
--- a/drivers/net/wireless/marvell/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
@@ -298,6 +298,19 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size)
struct mwifiex_adapter *adapter = ctx->adapter;
struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+ if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) {
+ if (card->rx_cmd_ep == ctx->ep) {
+ mwifiex_dbg(adapter, INFO, "%s: free rx_cmd skb\n",
+ __func__);
+ dev_kfree_skb_any(ctx->skb);
+ ctx->skb = NULL;
+ }
+ mwifiex_dbg(adapter, ERROR,
+ "%s: card removed/suspended, EP %d rx_cmd URB submit skipped\n",
+ __func__, ctx->ep);
+ return -1;
+ }
+
if (card->rx_cmd_ep != ctx->ep) {
ctx->skb = dev_alloc_skb(size);
if (!ctx->skb) {
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index ade4a20..1b5abd4 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -548,6 +548,12 @@ mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
struct mt76_wcid *wcid = status->wcid;
bool ps;
+ if (ieee80211_is_pspoll(hdr->frame_control) && !wcid) {
+ sta = ieee80211_find_sta_by_ifaddr(dev->hw, hdr->addr2, NULL);
+ if (sta)
+ wcid = status->wcid = (struct mt76_wcid *) sta->drv_priv;
+ }
+
if (!wcid || !wcid->sta)
return;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
index 14e8c57..924c761 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
@@ -793,9 +793,8 @@ void mt76x0_phy_recalibrate_after_assoc(struct mt76x0_dev *dev)
mt76_wr(dev, MT_TX_ALC_CFG_0, 0);
usleep_range(500, 700);
- reg_val = mt76_rr(dev, 0x2124);
- reg_val &= 0xffffff7e;
- mt76_wr(dev, 0x2124, reg_val);
+ reg_val = mt76_rr(dev, MT_BBP(IBI, 9));
+ mt76_wr(dev, MT_BBP(IBI, 9), 0xffffff7e);
mt76x0_mcu_calibrate(dev, MCU_CAL_RXDCOC, 0);
@@ -806,7 +805,7 @@ void mt76x0_phy_recalibrate_after_assoc(struct mt76x0_dev *dev)
mt76x0_mcu_calibrate(dev, MCU_CAL_RXIQ, is_5ghz);
mt76x0_mcu_calibrate(dev, MCU_CAL_RX_GROUP_DELAY, is_5ghz);
- mt76_wr(dev, 0x2124, reg_val);
+ mt76_wr(dev, MT_BBP(IBI, 9), reg_val);
mt76_wr(dev, MT_TX_ALC_CFG_0, tx_alc);
msleep(100);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c b/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c
index 751b49c..c45d05d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c
@@ -166,7 +166,7 @@ void mt76x0_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
if (sta) {
msta = (struct mt76_sta *) sta->drv_priv;
wcid = &msta->wcid;
- } else if (vif && (!info->control.hw_key && wcid->hw_key_idx != -1)) {
+ } else if (vif && (!info->control.hw_key && wcid->hw_key_idx != 0xff)) {
struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
wcid = &mvif->group_wcid;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c
index 324b2a4..54a9e1d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c
@@ -72,6 +72,9 @@ void mt76x2_reset_wlan(struct mt76x2_dev *dev, bool enable)
{
u32 val;
+ if (!enable)
+ goto out;
+
val = mt76_rr(dev, MT_WLAN_FUN_CTRL);
val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL;
@@ -87,6 +90,7 @@ void mt76x2_reset_wlan(struct mt76x2_dev *dev, bool enable)
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
+out:
mt76x2_set_wlan_state(dev, enable);
}
EXPORT_SYMBOL_GPL(mt76x2_reset_wlan);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
index e66f047..26cfda2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
@@ -53,6 +53,7 @@ mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM;
mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
+ mt76x2_reset_wlan(dev, false);
dev->mt76.rev = mt76_rr(dev, MT_ASIC_VERSION);
dev_info(dev->mt76.dev, "ASIC revision: %08x\n", dev->mt76.rev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c
index 9fd6ab4..ca68dd1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c
@@ -232,9 +232,9 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
mt76_wr(dev, MT_TX_PWR_CFG_7,
mt76x2_tx_power_mask(t.ofdm[6], t.vht[8], t.ht[6], t.vht[8]));
mt76_wr(dev, MT_TX_PWR_CFG_8,
- mt76x2_tx_power_mask(t.ht[14], t.vht[8], t.vht[8], 0));
+ mt76x2_tx_power_mask(t.ht[14], 0, t.vht[8], t.vht[8]));
mt76_wr(dev, MT_TX_PWR_CFG_9,
- mt76x2_tx_power_mask(t.ht[6], t.vht[8], t.vht[8], 0));
+ mt76x2_tx_power_mask(t.ht[6], 0, t.vht[8], t.vht[8]));
}
EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c
index 36afb16..c0ca0df 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c
@@ -32,7 +32,7 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
msta = (struct mt76x2_sta *)control->sta->drv_priv;
wcid = &msta->wcid;
/* sw encrypted frames */
- if (!info->control.hw_key && wcid->hw_key_idx != -1)
+ if (!info->control.hw_key && wcid->hw_key_idx != 0xff)
control->sta = NULL;
}
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 20447fd..227e5eb 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -148,7 +148,8 @@ mt76_check_agg_ssn(struct mt76_txq *mtxq, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- if (!ieee80211_is_data_qos(hdr->frame_control))
+ if (!ieee80211_is_data_qos(hdr->frame_control) ||
+ !ieee80211_is_data_present(hdr->frame_control))
return;
mtxq->agg_ssn = le16_to_cpu(hdr->seq_ctrl) + 0x10;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 4aa332f..ff8a46c 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -521,9 +521,16 @@ static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev,
int ret;
ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr);
- if (ret)
- pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n",
- vif->mac->macid, vif->vifid, key_index, pairwise);
+ if (ret) {
+ if (ret == -ENOENT) {
+ pr_debug("VIF%u.%u: key index %d out of bounds\n",
+ vif->mac->macid, vif->vifid, key_index);
+ } else {
+ pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n",
+ vif->mac->macid, vif->vifid,
+ key_index, pairwise);
+ }
+ }
return ret;
}
@@ -1109,6 +1116,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
if (hw_info->hw_capab & QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR)
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ if (!(hw_info->hw_capab & QLINK_HW_CAPAB_OBSS_SCAN))
+ wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN;
+
#ifdef CONFIG_PM
if (macinfo->wowlan)
wiphy->wowlan = macinfo->wowlan;
@@ -1123,6 +1133,15 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
}
+ if (mac->macinfo.extended_capabilities_len) {
+ wiphy->extended_capabilities =
+ mac->macinfo.extended_capabilities;
+ wiphy->extended_capabilities_mask =
+ mac->macinfo.extended_capabilities_mask;
+ wiphy->extended_capabilities_len =
+ mac->macinfo.extended_capabilities_len;
+ }
+
strlcpy(wiphy->fw_version, hw_info->fw_version,
sizeof(wiphy->fw_version));
wiphy->hw_version = hw_info->hw_version;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index ae9e773..734844b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -544,6 +544,9 @@ qtnf_sta_info_parse_rate(struct rate_info *rate_dst,
rate_dst->flags |= RATE_INFO_FLAGS_MCS;
else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS)
rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS;
+
+ if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_SHORT_GI)
+ rate_dst->flags |= RATE_INFO_FLAGS_SHORT_GI;
}
static void
@@ -1353,8 +1356,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
ext_capa_mask = NULL;
}
- kfree(mac->macinfo.extended_capabilities);
- kfree(mac->macinfo.extended_capabilities_mask);
+ qtnf_mac_ext_caps_free(mac);
mac->macinfo.extended_capabilities = ext_capa;
mac->macinfo.extended_capabilities_mask = ext_capa_mask;
mac->macinfo.extended_capabilities_len = ext_capa_len;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index 19abbc4..08928d5 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -304,6 +304,19 @@ void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac)
}
}
+void qtnf_mac_ext_caps_free(struct qtnf_wmac *mac)
+{
+ if (mac->macinfo.extended_capabilities_len) {
+ kfree(mac->macinfo.extended_capabilities);
+ mac->macinfo.extended_capabilities = NULL;
+
+ kfree(mac->macinfo.extended_capabilities_mask);
+ mac->macinfo.extended_capabilities_mask = NULL;
+
+ mac->macinfo.extended_capabilities_len = 0;
+ }
+}
+
static void qtnf_vif_reset_handler(struct work_struct *work)
{
struct qtnf_vif *vif = container_of(work, struct qtnf_vif, reset_work);
@@ -493,8 +506,7 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
}
qtnf_mac_iface_comb_free(mac);
- kfree(mac->macinfo.extended_capabilities);
- kfree(mac->macinfo.extended_capabilities_mask);
+ qtnf_mac_ext_caps_free(mac);
kfree(mac->macinfo.wowlan);
wiphy_free(wiphy);
bus->mac[macid] = NULL;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index a1e338a..ecb5c41 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -151,6 +151,7 @@ struct qtnf_hw_info {
struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac);
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac);
void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac);
+void qtnf_mac_ext_caps_free(struct qtnf_wmac *mac);
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus);
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv,
const char *name, unsigned char name_assign_type);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 99d37e3..c5ae4ea 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -71,6 +71,7 @@ struct qlink_msg_header {
* @QLINK_HW_CAPAB_DFS_OFFLOAD: device implements DFS offload functionality
* @QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR: device supports MAC Address
* Randomization in probe requests.
+ * @QLINK_HW_CAPAB_OBSS_SCAN: device can perform OBSS scanning.
*/
enum qlink_hw_capab {
QLINK_HW_CAPAB_REG_UPDATE = BIT(0),
@@ -78,6 +79,7 @@ enum qlink_hw_capab {
QLINK_HW_CAPAB_DFS_OFFLOAD = BIT(2),
QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR = BIT(3),
QLINK_HW_CAPAB_PWR_MGMT = BIT(4),
+ QLINK_HW_CAPAB_OBSS_SCAN = BIT(5),
};
enum qlink_iface_type {
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
index 9a1d15b..518caaa 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -444,12 +444,13 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
skb_queue_tail(&priv->rx_queue, skb);
usb_anchor_urb(entry, &priv->anchored);
ret = usb_submit_urb(entry, GFP_KERNEL);
- usb_put_urb(entry);
if (ret) {
skb_unlink(skb, &priv->rx_queue);
usb_unanchor_urb(entry);
+ usb_put_urb(entry);
goto err;
}
+ usb_put_urb(entry);
}
return ret;
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c
index c2d5b49..c089540 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c
@@ -146,7 +146,7 @@ static int rtl8187_register_led(struct ieee80211_hw *dev,
led->dev = dev;
led->ledpin = ledpin;
led->is_radio = is_radio;
- strncpy(led->name, name, sizeof(led->name));
+ strlcpy(led->name, name, sizeof(led->name));
led->led_dev.name = led->name;
led->led_dev.default_trigger = default_trigger;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 8828baf..47c2bfe 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1349,6 +1349,7 @@ struct rtl8xxxu_fileops {
u8 has_s0s1:1;
u8 has_tx_report:1;
u8 gen2_thermal_meter:1;
+ u8 needs_full_init:1;
u32 adda_1t_init;
u32 adda_1t_path_on;
u32 adda_2t_path_on_a;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
index 26b674a..14e207f 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
@@ -1673,6 +1673,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = {
.has_s0s1 = 1,
.has_tx_report = 1,
.gen2_thermal_meter = 1,
+ .needs_full_init = 1,
.adda_1t_init = 0x01c00014,
.adda_1t_path_on = 0x01c00014,
.adda_2t_path_on_a = 0x01c00014,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index 505ab1b..66c6ee7 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -3905,6 +3905,9 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
else
macpower = true;
+ if (fops->needs_full_init)
+ macpower = false;
+
ret = fops->power_on(priv);
if (ret < 0) {
dev_warn(dev, "%s: Failed power on\n", __func__);
@@ -5691,6 +5694,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
break;
case WLAN_CIPHER_SUITE_TKIP:
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ break;
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
index b026e80..6fbf884 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
@@ -1324,13 +1324,13 @@ bool exhalbtc_initlize_variables_wifi_only(struct rtl_priv *rtlpriv)
switch (rtlpriv->rtlhal.interface) {
case INTF_PCI:
- wifionly_cfg->chip_interface = BTC_INTF_PCI;
+ wifionly_cfg->chip_interface = WIFIONLY_INTF_PCI;
break;
case INTF_USB:
- wifionly_cfg->chip_interface = BTC_INTF_USB;
+ wifionly_cfg->chip_interface = WIFIONLY_INTF_USB;
break;
default:
- wifionly_cfg->chip_interface = BTC_INTF_UNKNOWN;
+ wifionly_cfg->chip_interface = WIFIONLY_INTF_UNKNOWN;
break;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
index 1e60f70..8c60a84 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
@@ -1556,6 +1556,8 @@ static bool usb_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
* This is maybe necessary:
* rtlpriv->cfg->ops->fill_tx_cmddesc(hw, buffer, 1, 1, skb);
*/
+ dev_kfree_skb(skb);
+
return true;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c
index 85cedd0..75bfa9d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c
@@ -173,7 +173,7 @@ static int _rtl92d_fw_init(struct ieee80211_hw *hw)
rtl_read_byte(rtlpriv, FW_MAC1_READY));
}
RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
- "Polling FW ready fail!! REG_MCUFWDL:0x%08ul\n",
+ "Polling FW ready fail!! REG_MCUFWDL:0x%08x\n",
rtl_read_dword(rtlpriv, REG_MCUFWDL));
return -1;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
index 80123fd..ee5ff72 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
@@ -1198,6 +1198,7 @@ void rtl92de_enable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+ rtlpci->irq_enabled = true;
}
void rtl92de_disable_interrupt(struct ieee80211_hw *hw)
@@ -1207,7 +1208,7 @@ void rtl92de_disable_interrupt(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
- synchronize_irq(rtlpci->pdev->irq);
+ rtlpci->irq_enabled = false;
}
static void _rtl92de_poweroff_adapter(struct ieee80211_hw *hw)
@@ -1373,7 +1374,7 @@ void rtl92de_set_beacon_related_registers(struct ieee80211_hw *hw)
bcn_interval = mac->beacon_interval;
atim_window = 2;
- /*rtl92de_disable_interrupt(hw); */
+ rtl92de_disable_interrupt(hw);
rtl_write_word(rtlpriv, REG_ATIMWND, atim_window);
rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f);
@@ -1393,9 +1394,9 @@ void rtl92de_set_beacon_interval(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
"beacon_interval:%d\n", bcn_interval);
- /* rtl92de_disable_interrupt(hw); */
+ rtl92de_disable_interrupt(hw);
rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
- /* rtl92de_enable_interrupt(hw); */
+ rtl92de_enable_interrupt(hw);
}
void rtl92de_update_interrupt_mask(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
index d5ba2ba..2b0c030 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
@@ -238,6 +238,7 @@ static struct rtl_hal_ops rtl8192de_hal_ops = {
.led_control = rtl92de_led_control,
.set_desc = rtl92de_set_desc,
.get_desc = rtl92de_get_desc,
+ .is_tx_desc_closed = rtl92de_is_tx_desc_closed,
.tx_polling = rtl92de_tx_polling,
.enable_hw_sec = rtl92de_enable_hw_security_config,
.set_key = rtl92de_set_key,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c
index d7b023c..76f1224 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c
@@ -840,13 +840,15 @@ u64 rtl92de_get_desc(struct ieee80211_hw *hw,
break;
}
} else {
- struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
switch (desc_name) {
case HW_DESC_OWN:
- ret = GET_RX_DESC_OWN(pdesc);
+ ret = GET_RX_DESC_OWN(p_desc);
break;
case HW_DESC_RXPKT_LEN:
- ret = GET_RX_DESC_PKT_LEN(pdesc);
+ ret = GET_RX_DESC_PKT_LEN(p_desc);
+ break;
+ case HW_DESC_RXBUFF_ADDR:
+ ret = GET_RX_DESC_BUFF_ADDR(p_desc);
break;
default:
WARN_ONCE(true, "rtl8192de: ERR rxdesc :%d not processed\n",
@@ -857,6 +859,23 @@ u64 rtl92de_get_desc(struct ieee80211_hw *hw,
return ret;
}
+bool rtl92de_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+ u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+ u8 own = (u8)rtl92de_get_desc(hw, entry, true, HW_DESC_OWN);
+
+ /* a beacon packet will only use the first
+ * descriptor by defaut, and the own bit may not
+ * be cleared by the hardware
+ */
+ if (own)
+ return false;
+ return true;
+}
+
void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h
index f7f7765..3d026e5 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h
@@ -737,6 +737,8 @@ void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
u8 desc_name, u8 *val);
u64 rtl92de_get_desc(struct ieee80211_hw *hw,
u8 *p_desc, bool istx, u8 desc_name);
+bool rtl92de_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index);
void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
bool b_firstseg, bool b_lastseg,
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index 5adb939..1181b72 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -1050,8 +1050,10 @@ int rtl_usb_probe(struct usb_interface *intf,
rtlpriv->hw = hw;
rtlpriv->usb_data = kcalloc(RTL_USB_MAX_RX_COUNT, sizeof(u32),
GFP_KERNEL);
- if (!rtlpriv->usb_data)
+ if (!rtlpriv->usb_data) {
+ ieee80211_free_hw(hw);
return -ENOMEM;
+ }
/* this spin lock must be initialized early */
spin_lock_init(&rtlpriv->locks.usb_lock);
@@ -1112,6 +1114,7 @@ int rtl_usb_probe(struct usb_interface *intf,
_rtl_usb_io_handler_release(hw);
usb_put_dev(udev);
complete(&rtlpriv->firmware_loading_complete);
+ kfree(rtlpriv->usb_data);
return -ENODEV;
}
EXPORT_SYMBOL(rtl_usb_probe);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 1095df7..1a3a523 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1583,6 +1583,7 @@ static int rsi_send_beacon(struct rsi_common *common)
skb_pull(skb, (64 - dword_align_bytes));
if (rsi_prepare_beacon(common, skb)) {
rsi_dbg(ERR_ZONE, "Failed to prepare beacon\n");
+ dev_kfree_skb(skb);
return -EINVAL;
}
skb_queue_tail(&common->tx_queue[MGMT_BEACON_Q], skb);
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
index dbe78d8..7f34ec0 100644
--- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c
+++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
@@ -70,7 +70,7 @@ wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
out:
mutex_unlock(&wl->mutex);
- return 0;
+ return ret;
}
static int
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 27b6b14..4cafc31 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -173,7 +173,8 @@ static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb,
[skb_get_hash_raw(skb) % size];
}
-static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t
+xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct xenvif *vif = netdev_priv(dev);
struct xenvif_queue *queue = NULL;
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index a3bc8f0..159dc9e 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -277,7 +277,7 @@ static void fdp_nci_i2c_read_device_properties(struct device *dev,
*fw_vsc_cfg, len);
if (r) {
- devm_kfree(dev, fw_vsc_cfg);
+ devm_kfree(dev, *fw_vsc_cfg);
goto vsc_read_err;
}
} else {
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
index ba695e3..0df745c 100644
--- a/drivers/nfc/nxp-nci/i2c.c
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -236,8 +236,10 @@ static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
if (r == -EREMOTEIO) {
phy->hard_fault = r;
- skb = NULL;
- } else if (r < 0) {
+ if (info->mode == NXP_NCI_MODE_FW)
+ nxp_nci_fw_recv_frame(phy->ndev, NULL);
+ }
+ if (r < 0) {
nfc_err(&client->dev, "Read failed with error %d\n", r);
goto exit_irq_handled;
}
diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c
index 5d823e9..fcb57d6 100644
--- a/drivers/nfc/pn533/usb.c
+++ b/drivers/nfc/pn533/usb.c
@@ -559,18 +559,25 @@ static int pn533_usb_probe(struct usb_interface *interface,
rc = pn533_finalize_setup(priv);
if (rc)
- goto error;
+ goto err_deregister;
usb_set_intfdata(interface, phy);
return 0;
+err_deregister:
+ pn533_unregister_device(phy->priv);
error:
+ usb_kill_urb(phy->in_urb);
+ usb_kill_urb(phy->out_urb);
+ usb_kill_urb(phy->ack_urb);
+
usb_free_urb(phy->in_urb);
usb_free_urb(phy->out_urb);
usb_free_urb(phy->ack_urb);
usb_put_dev(phy->udev);
kfree(in_buf);
+ kfree(phy->ack_buffer);
return rc;
}
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index bb43ceb..60ae382 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -792,7 +792,7 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
if (rc)
- usb_unlink_urb(dev->out_urb);
+ usb_kill_urb(dev->out_urb);
exit:
mutex_unlock(&dev->out_urb_lock);
diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c
index e803fdf..f37069b 100644
--- a/drivers/nfc/st21nfca/core.c
+++ b/drivers/nfc/st21nfca/core.c
@@ -719,6 +719,7 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
NFC_PROTO_FELICA_MASK;
} else {
kfree_skb(nfcid_skb);
+ nfcid_skb = NULL;
/* P2P in type A */
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
ST21NFCA_RF_READER_F_NFCID1,
diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c
index 6aa5732..2ad263f 100644
--- a/drivers/ntb/hw/intel/ntb_hw_gen1.c
+++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c
@@ -265,7 +265,7 @@ static inline int ndev_db_clear_mask(struct intel_ntb_dev *ndev, u64 db_bits,
return 0;
}
-static inline int ndev_vec_mask(struct intel_ntb_dev *ndev, int db_vector)
+static inline u64 ndev_vec_mask(struct intel_ntb_dev *ndev, int db_vector)
{
u64 shift, mask;
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 0360c01..75ae2c5 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1260,11 +1260,11 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
ret = btt_data_read(arena, page, off, postmap, cur_len);
if (ret) {
- int rc;
-
/* Media error - set the e_flag */
- rc = btt_map_write(arena, premap, postmap, 0, 1,
- NVDIMM_IO_ATOMIC);
+ if (btt_map_write(arena, premap, postmap, 0, 1, NVDIMM_IO_ATOMIC))
+ dev_warn_ratelimited(to_dev(arena),
+ "Error persistently tracking bad blocks at %#x\n",
+ premap);
goto out_rtt;
}
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 5d0f99b..b7bd89b 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -551,9 +551,25 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
struct nvme_dsm_range *range;
struct bio *bio;
- range = kmalloc_array(segments, sizeof(*range), GFP_ATOMIC);
- if (!range)
- return BLK_STS_RESOURCE;
+ /*
+ * Some devices do not consider the DSM 'Number of Ranges' field when
+ * determining how much data to DMA. Always allocate memory for maximum
+ * number of segments to prevent device reading beyond end of buffer.
+ */
+ static const size_t alloc_size = sizeof(*range) * NVME_DSM_MAX_RANGES;
+
+ range = kzalloc(alloc_size, GFP_ATOMIC | __GFP_NOWARN);
+ if (!range) {
+ /*
+ * If we fail allocation our range, fallback to the controller
+ * discard page. If that's also busy, it's safe to return
+ * busy, as we know we can make progress once that's freed.
+ */
+ if (test_and_set_bit_lock(0, &ns->ctrl->discard_page_busy))
+ return BLK_STS_RESOURCE;
+
+ range = page_address(ns->ctrl->discard_page);
+ }
__rq_for_each_bio(bio, req) {
u64 slba = nvme_block_nr(ns, bio->bi_iter.bi_sector);
@@ -568,7 +584,10 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
}
if (WARN_ON_ONCE(n != segments)) {
- kfree(range);
+ if (virt_to_page(range) == ns->ctrl->discard_page)
+ clear_bit_unlock(0, &ns->ctrl->discard_page_busy);
+ else
+ kfree(range);
return BLK_STS_IOERR;
}
@@ -580,7 +599,7 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
req->special_vec.bv_page = virt_to_page(range);
req->special_vec.bv_offset = offset_in_page(range);
- req->special_vec.bv_len = sizeof(*range) * segments;
+ req->special_vec.bv_len = alloc_size;
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
return BLK_STS_OK;
@@ -653,8 +672,13 @@ void nvme_cleanup_cmd(struct request *req)
blk_rq_bytes(req) >> ns->lba_shift);
}
if (req->rq_flags & RQF_SPECIAL_PAYLOAD) {
- kfree(page_address(req->special_vec.bv_page) +
- req->special_vec.bv_offset);
+ struct nvme_ns *ns = req->rq_disk->private_data;
+ struct page *page = req->special_vec.bv_page;
+
+ if (page == ns->ctrl->discard_page)
+ clear_bit_unlock(0, &ns->ctrl->discard_page_busy);
+ else
+ kfree(page_address(page) + req->special_vec.bv_offset);
}
}
EXPORT_SYMBOL_GPL(nvme_cleanup_cmd);
@@ -3551,6 +3575,7 @@ static void nvme_free_ctrl(struct device *dev)
ida_simple_remove(&nvme_instance_ida, ctrl->instance);
kfree(ctrl->effects);
nvme_mpath_uninit(ctrl);
+ __free_page(ctrl->discard_page);
if (subsys) {
mutex_lock(&subsys->lock);
@@ -3592,6 +3617,14 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
memset(&ctrl->ka_cmd, 0, sizeof(ctrl->ka_cmd));
ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive;
+ BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) >
+ PAGE_SIZE);
+ ctrl->discard_page = alloc_page(GFP_KERNEL);
+ if (!ctrl->discard_page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
ret = ida_simple_get(&nvme_instance_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto out;
@@ -3625,10 +3658,12 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
return 0;
out_free_name:
- kfree_const(dev->kobj.name);
+ kfree_const(ctrl->device->kobj.name);
out_release_instance:
ida_simple_remove(&nvme_instance_ida, ctrl->instance);
out:
+ if (ctrl->discard_page)
+ __free_page(ctrl->discard_page);
return ret;
}
EXPORT_SYMBOL_GPL(nvme_init_ctrl);
@@ -3647,7 +3682,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
down_read(&ctrl->namespaces_rwsem);
/* Forcibly unquiesce queues to avoid blocking dispatch */
- if (ctrl->admin_q)
+ if (ctrl->admin_q && !blk_queue_dying(ctrl->admin_q))
blk_mq_unquiesce_queue(ctrl->admin_q);
list_for_each_entry(ns, &ctrl->namespaces, list)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 565bddc..1875f6b 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -342,7 +342,8 @@ nvme_fc_register_localport(struct nvme_fc_port_info *pinfo,
!template->ls_req || !template->fcp_io ||
!template->ls_abort || !template->fcp_abort ||
!template->max_hw_queues || !template->max_sgl_segments ||
- !template->max_dif_sgl_segments || !template->dma_boundary) {
+ !template->max_dif_sgl_segments || !template->dma_boundary ||
+ !template->module) {
ret = -EINVAL;
goto out_reghost_failed;
}
@@ -1986,6 +1987,7 @@ nvme_fc_ctrl_free(struct kref *ref)
{
struct nvme_fc_ctrl *ctrl =
container_of(ref, struct nvme_fc_ctrl, ref);
+ struct nvme_fc_lport *lport = ctrl->lport;
unsigned long flags;
if (ctrl->ctrl.tagset) {
@@ -2011,6 +2013,7 @@ nvme_fc_ctrl_free(struct kref *ref)
if (ctrl->ctrl.opts)
nvmf_free_options(ctrl->ctrl.opts);
kfree(ctrl);
+ module_put(lport->ops->module);
}
static void
@@ -2891,10 +2894,22 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
static void
__nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl)
{
- nvme_stop_keep_alive(&ctrl->ctrl);
+ /*
+ * if state is connecting - the error occurred as part of a
+ * reconnect attempt. The create_association error paths will
+ * clean up any outstanding io.
+ *
+ * if it's a different state - ensure all pending io is
+ * terminated. Given this can delay while waiting for the
+ * aborted io to return, we recheck adapter state below
+ * before changing state.
+ */
+ if (ctrl->ctrl.state != NVME_CTRL_CONNECTING) {
+ nvme_stop_keep_alive(&ctrl->ctrl);
- /* will block will waiting for io to terminate */
- nvme_fc_delete_association(ctrl);
+ /* will block will waiting for io to terminate */
+ nvme_fc_delete_association(ctrl);
+ }
if (ctrl->ctrl.state != NVME_CTRL_CONNECTING &&
!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING))
@@ -3040,10 +3055,15 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
goto out_fail;
}
+ if (!try_module_get(lport->ops->module)) {
+ ret = -EUNATCH;
+ goto out_free_ctrl;
+ }
+
idx = ida_simple_get(&nvme_fc_ctrl_cnt, 0, 0, GFP_KERNEL);
if (idx < 0) {
ret = -ENOSPC;
- goto out_free_ctrl;
+ goto out_mod_put;
}
ctrl->ctrl.opts = opts;
@@ -3185,6 +3205,8 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
out_free_ida:
put_device(ctrl->dev);
ida_simple_remove(&nvme_fc_ctrl_cnt, ctrl->cnum);
+out_mod_put:
+ module_put(lport->ops->module);
out_free_ctrl:
kfree(ctrl);
out_fail:
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 6fe5923..a69553e 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -968,6 +968,9 @@ void nvme_nvm_update_nvm_info(struct nvme_ns *ns)
struct nvm_dev *ndev = ns->ndev;
struct nvm_geo *geo = &ndev->geo;
+ if (geo->version == NVM_OCSSD_SPEC_12)
+ return;
+
geo->csecs = 1 << ns->lba_shift;
geo->sos = ns->ms;
}
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 892ef52..838ee58 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -575,7 +575,7 @@ int nvme_mpath_init(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
goto out;
}
- error = nvme_read_ana_log(ctrl, true);
+ error = nvme_read_ana_log(ctrl, false);
if (error)
goto out_free_ana_log_buf;
return 0;
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 2653e1f..cc4273f 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -238,6 +238,9 @@ struct nvme_ctrl {
u16 maxcmd;
int nr_reconnects;
struct nvmf_ctrl_options *opts;
+
+ struct page *discard_page;
+ unsigned long discard_page_busy;
};
struct nvme_subsystem {
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index a64a8bc..124f411 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1652,6 +1652,9 @@ static void nvme_map_cmb(struct nvme_dev *dev)
struct pci_dev *pdev = to_pci_dev(dev->dev);
int bar;
+ if (dev->cmb_size)
+ return;
+
dev->cmbsz = readl(dev->bar + NVME_REG_CMBSZ);
if (!dev->cmbsz)
return;
@@ -2136,7 +2139,6 @@ static void nvme_pci_disable(struct nvme_dev *dev)
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
- nvme_release_cmb(dev);
pci_free_irq_vectors(pdev);
if (pci_is_enabled(pdev)) {
@@ -2583,19 +2585,19 @@ static void nvme_remove(struct pci_dev *pdev)
struct nvme_dev *dev = pci_get_drvdata(pdev);
nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_DELETING);
-
- cancel_work_sync(&dev->ctrl.reset_work);
pci_set_drvdata(pdev, NULL);
if (!pci_device_is_present(pdev)) {
nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_DEAD);
nvme_dev_disable(dev, true);
+ nvme_dev_remove_admin(dev);
}
flush_work(&dev->ctrl.reset_work);
nvme_stop_ctrl(&dev->ctrl);
nvme_remove_namespaces(&dev->ctrl);
nvme_dev_disable(dev, true);
+ nvme_release_cmb(dev);
nvme_free_host_mem(dev);
nvme_dev_remove_admin(dev);
nvme_free_queues(dev, 0);
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 5251689..f0536d3 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -648,6 +648,7 @@ fcloop_fcp_op(struct nvmet_fc_target_port *tgtport,
break;
/* Fall-Thru to RSP handling */
+ /* FALLTHRU */
case NVMET_FCOP_RSP:
if (fcpreq) {
@@ -824,6 +825,7 @@ fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
#define FCLOOP_DMABOUND_4G 0xFFFFFFFF
static struct nvme_fc_port_template fctemplate = {
+ .module = THIS_MODULE,
.localport_delete = fcloop_localport_delete,
.remoteport_delete = fcloop_remoteport_delete,
.create_queue = fcloop_create_queue,
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 81a9dc5..39d972e 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -246,7 +246,8 @@ static void nvmet_file_execute_discard(struct nvmet_req *req)
break;
offset = le64_to_cpu(range.slba) << req->ns->blksize_shift;
- len = le32_to_cpu(range.nlb) << req->ns->blksize_shift;
+ len = le32_to_cpu(range.nlb);
+ len <<= req->ns->blksize_shift;
if (offset + len > req->ns->size) {
ret = NVME_SC_LBA_RANGE | NVME_SC_DNR;
break;
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 6313dba..67196c3 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -559,7 +559,7 @@ static struct nvmem_device *nvmem_find(const char *name)
d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
if (!d)
- return NULL;
+ return ERR_PTR(-ENOENT);
return to_nvmem_device(d);
}
@@ -899,7 +899,8 @@ static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
*p-- = 0;
/* clear msb bits if any leftover in the last byte */
- *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0);
+ if (cell->nbits%BITS_PER_BYTE)
+ *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0);
}
static int __nvmem_cell_read(struct nvmem_device *nvmem,
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index afb429a..926d9cc 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -466,6 +466,10 @@ static int imx_ocotp_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
+ clk_prepare_enable(priv->clk);
+ imx_ocotp_clr_err_if_set(priv->base);
+ clk_disable_unprepare(priv->clk);
+
priv->params = of_device_get_match_data(&pdev->dev);
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 3f21ea6..f0dbb7a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2066,7 +2066,7 @@ struct device_node *of_find_next_cache_node(const struct device_node *np)
/* OF on pmac has nodes instead of properties named "l2-cache"
* beneath CPU nodes.
*/
- if (!strcmp(np->type, "cpu"))
+ if (IS_ENABLED(CONFIG_PPC_PMAC) && !strcmp(np->type, "cpu"))
for_each_child_of_node(np, child)
if (!strcmp(child->type, "cache"))
return child;
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 2edb590..514528b 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -305,7 +305,6 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
{
struct property *new_prop = NULL, *prop;
int ret = 0;
- bool check_for_non_overlay_node = false;
if (target->in_livetree)
if (!of_prop_cmp(overlay_prop->name, "name") ||
@@ -318,6 +317,25 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
else
prop = NULL;
+ if (prop) {
+ if (!of_prop_cmp(prop->name, "#address-cells")) {
+ if (!of_prop_val_eq(prop, overlay_prop)) {
+ pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n",
+ target->np);
+ ret = -EINVAL;
+ }
+ return ret;
+
+ } else if (!of_prop_cmp(prop->name, "#size-cells")) {
+ if (!of_prop_val_eq(prop, overlay_prop)) {
+ pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n",
+ target->np);
+ ret = -EINVAL;
+ }
+ return ret;
+ }
+ }
+
if (is_symbols_prop) {
if (prop)
return -EINVAL;
@@ -330,33 +348,18 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
return -ENOMEM;
if (!prop) {
- check_for_non_overlay_node = true;
if (!target->in_livetree) {
new_prop->next = target->np->deadprops;
target->np->deadprops = new_prop;
}
ret = of_changeset_add_property(&ovcs->cset, target->np,
new_prop);
- } else if (!of_prop_cmp(prop->name, "#address-cells")) {
- if (!of_prop_val_eq(prop, new_prop)) {
- pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n",
- target->np);
- ret = -EINVAL;
- }
- } else if (!of_prop_cmp(prop->name, "#size-cells")) {
- if (!of_prop_val_eq(prop, new_prop)) {
- pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n",
- target->np);
- ret = -EINVAL;
- }
} else {
- check_for_non_overlay_node = true;
ret = of_changeset_update_property(&ovcs->cset, target->np,
new_prop);
}
- if (check_for_non_overlay_node &&
- !of_node_check_flag(target->np, OF_OVERLAY))
+ if (!of_node_check_flag(target->np, OF_OVERLAY))
pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n",
target->np, new_prop->name);
diff --git a/drivers/of/unittest-data/overlay_15.dts b/drivers/of/unittest-data/overlay_15.dts
index b98f251..5728490 100644
--- a/drivers/of/unittest-data/overlay_15.dts
+++ b/drivers/of/unittest-data/overlay_15.dts
@@ -20,8 +20,8 @@
#size-cells = <0>;
reg = <0>;
- test-mux-dev {
- reg = <32>;
+ test-mux-dev@20 {
+ reg = <0x20>;
compatible = "unittest-i2c-dev";
status = "okay";
};
diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi
index 25cf397..4ea024d 100644
--- a/drivers/of/unittest-data/tests-overlay.dtsi
+++ b/drivers/of/unittest-data/tests-overlay.dtsi
@@ -103,8 +103,8 @@
#size-cells = <0>;
reg = <0>;
- test-mux-dev {
- reg = <32>;
+ test-mux-dev@20 {
+ reg = <0x20>;
compatible = "unittest-i2c-dev";
status = "okay";
};
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 7f42314..808571f 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -375,6 +375,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
for (i = 0; i < 8; i++) {
bool passed = true;
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells", i, &args);
@@ -428,6 +429,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
}
/* Check for missing list property */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-missing",
"#phandle-cells", 0, &args);
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
@@ -436,6 +438,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
/* Check for missing cells property */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells-missing", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
@@ -444,6 +447,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for bad phandle in list */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle",
"#phandle-cells", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
@@ -452,6 +456,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for incorrectly formed argument list */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-bad-args",
"#phandle-cells", 1, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
@@ -502,6 +507,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
for (i = 0; i < 8; i++) {
bool passed = true;
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list",
"phandle", i, &args);
@@ -559,21 +565,25 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
}
/* Check for missing list property */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-missing",
"phandle", 0, &args);
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
/* Check for missing cells,map,mask property */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list",
"phandle-missing", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for bad phandle in list */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle",
"phandle", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for incorrectly formed argument list */
+ memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args",
"phandle", 1, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
@@ -783,7 +793,7 @@ static void __init of_unittest_parse_interrupts(void)
for (i = 0; i < 4; i++) {
bool passed = true;
- args.args_count = 0;
+ memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
passed &= !rc;
@@ -804,7 +814,7 @@ static void __init of_unittest_parse_interrupts(void)
for (i = 0; i < 4; i++) {
bool passed = true;
- args.args_count = 0;
+ memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
/* Test the values from tests-phandle.dtsi */
@@ -860,6 +870,7 @@ static void __init of_unittest_parse_interrupts_extended(void)
for (i = 0; i < 7; i++) {
bool passed = true;
+ memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
/* Test the values from tests-phandle.dtsi */
@@ -1067,20 +1078,44 @@ static void __init of_unittest_platform_populate(void)
* of np into dup node (present in live tree) and
* updates parent of children of np to dup.
*
- * @np: node already present in live tree
+ * @np: node whose properties are being added to the live tree
* @dup: node present in live tree to be updated
*/
static void update_node_properties(struct device_node *np,
struct device_node *dup)
{
struct property *prop;
+ struct property *save_next;
struct device_node *child;
-
- for_each_property_of_node(np, prop)
- of_add_property(dup, prop);
+ int ret;
for_each_child_of_node(np, child)
child->parent = dup;
+
+ /*
+ * "unittest internal error: unable to add testdata property"
+ *
+ * If this message reports a property in node '/__symbols__' then
+ * the respective unittest overlay contains a label that has the
+ * same name as a label in the live devicetree. The label will
+ * be in the live devicetree only if the devicetree source was
+ * compiled with the '-@' option. If you encounter this error,
+ * please consider renaming __all__ of the labels in the unittest
+ * overlay dts files with an odd prefix that is unlikely to be
+ * used in a real devicetree.
+ */
+
+ /*
+ * open code for_each_property_of_node() because of_add_property()
+ * sets prop->next to NULL
+ */
+ for (prop = np->properties; prop != NULL; prop = save_next) {
+ save_next = prop->next;
+ ret = of_add_property(dup, prop);
+ if (ret)
+ pr_err("unittest internal error: unable to add testdata property %pOF/%s",
+ np, prop->name);
+ }
}
/**
@@ -1089,18 +1124,25 @@ static void update_node_properties(struct device_node *np,
*
* @np: Node to attach to live tree
*/
-static int attach_node_and_children(struct device_node *np)
+static void attach_node_and_children(struct device_node *np)
{
struct device_node *next, *dup, *child;
unsigned long flags;
const char *full_name;
full_name = kasprintf(GFP_KERNEL, "%pOF", np);
+
+ if (!strcmp(full_name, "/__local_fixups__") ||
+ !strcmp(full_name, "/__fixups__")) {
+ kfree(full_name);
+ return;
+ }
+
dup = of_find_node_by_path(full_name);
kfree(full_name);
if (dup) {
update_node_properties(np, dup);
- return 0;
+ return;
}
child = np->child;
@@ -1121,8 +1163,6 @@ static int attach_node_and_children(struct device_node *np)
attach_node_and_children(child);
child = next;
}
-
- return 0;
}
/**
@@ -1159,6 +1199,7 @@ static int __init unittest_data_add(void)
of_fdt_unflatten_tree(unittest_data, NULL, &unittest_data_node);
if (!unittest_data_node) {
pr_warn("%s: No tree to attach; not running tests\n", __func__);
+ kfree(unittest_data);
return -ENODATA;
}
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 0595d66..31eb950 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -313,7 +313,7 @@ int dev_pm_opp_get_opp_count(struct device *dev)
count = PTR_ERR(opp_table);
dev_dbg(dev, "%s: OPP table not found (%d)\n",
__func__, count);
- return 0;
+ return count;
}
count = _get_opp_count(opp_table);
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 7b4ee33..15c81cf 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -230,6 +230,18 @@ static int port_check(struct device *dev, void *dev_drv)
return 0;
}
+/*
+ * Iterates through all the devices connected to the bus and return 1
+ * if the device is a parallel port.
+ */
+
+static int port_detect(struct device *dev, void *dev_drv)
+{
+ if (is_parport(dev))
+ return 1;
+ return 0;
+}
+
/**
* parport_register_driver - register a parallel port device driver
* @drv: structure describing the driver
@@ -282,6 +294,15 @@ int __parport_register_driver(struct parport_driver *drv, struct module *owner,
if (ret)
return ret;
+ /*
+ * check if bus has any parallel port registered, if
+ * none is found then load the lowlevel driver.
+ */
+ ret = bus_for_each_dev(&parport_bus_type, NULL, NULL,
+ port_detect);
+ if (!ret)
+ get_lowlevel_driver();
+
mutex_lock(®istration_lock);
if (drv->match_port)
bus_for_each_dev(&parport_bus_type, NULL, drv,
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index 5e199e7..765357b 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -36,6 +36,7 @@
#define PCIE_RC_K2HK 0xb008
#define PCIE_RC_K2E 0xb009
#define PCIE_RC_K2L 0xb00a
+#define PCIE_RC_K2G 0xb00b
#define to_keystone_pcie(x) dev_get_drvdata((x)->dev)
@@ -50,6 +51,8 @@ static void quirk_limit_mrrs(struct pci_dev *dev)
.class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L),
.class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
+ { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2G),
+ .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
{ 0, },
};
diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c
index c7e5f9d8..69c490d 100644
--- a/drivers/pci/controller/pci-msm.c
+++ b/drivers/pci/controller/pci-msm.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.*/
+/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.*/
#include <dt-bindings/regulator/qcom,rpmh-regulator-levels.h>
#include <linux/bitops.h>
@@ -330,6 +330,7 @@ enum msm_pcie_link_status {
MSM_PCIE_LINK_ENABLED,
MSM_PCIE_LINK_DISABLED,
MSM_PCIE_LINK_DRV,
+ MSM_PCIE_LINK_DOWN,
};
enum msm_pcie_boot_option {
@@ -626,6 +627,7 @@ struct msm_pcie_drv_info {
u16 seq;
u16 reply_seq;
u32 timeout_ms; /* IPC command timeout */
+ u32 l1ss_timeout_us;
struct completion completion;
};
@@ -4751,7 +4753,7 @@ static void msm_pcie_notify_client(struct msm_pcie_dev_t *dev,
notify->event = event;
notify->user = dev->event_reg->user;
- PCIE_DBG(dev, "PCIe: callback RC%d for event %d\n",
+ PCIE_DUMP(dev, "PCIe: callback RC%d for event %d\n",
dev->rc_idx, event);
dev->event_reg->callback(notify);
@@ -4887,7 +4889,7 @@ static irqreturn_t handle_aer_irq(int irq, void *data)
msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS, 0,
BIT(18)|BIT(17)|BIT(16));
- if (dev->link_status == MSM_PCIE_LINK_DISABLED) {
+ if (dev->link_status != MSM_PCIE_LINK_ENABLED) {
PCIE_DBG2(dev, "RC%d link is down\n", dev->rc_idx);
goto out;
}
@@ -5038,7 +5040,7 @@ static irqreturn_t handle_linkdown_irq(int irq, void *data)
"PCIe:the link of RC%d is suspending.\n",
dev->rc_idx);
} else {
- dev->link_status = MSM_PCIE_LINK_DISABLED;
+ dev->link_status = MSM_PCIE_LINK_DOWN;
dev->shadow_en = false;
if (dev->linkdown_panic)
@@ -5646,7 +5648,6 @@ static int msm_pcie_setup_drv(struct msm_pcie_dev_t *pcie_dev,
struct msm_pcie_drv_msg *msg;
struct msm_pcie_drv_tre *pkt;
struct msm_pcie_drv_header *hdr;
- u32 drv_l1ss_timeout_us = 0;
int ret;
drv_info = devm_kzalloc(&pcie_dev->pdev->dev, sizeof(*drv_info),
@@ -5655,12 +5656,12 @@ static int msm_pcie_setup_drv(struct msm_pcie_dev_t *pcie_dev,
return -ENOMEM;
ret = of_property_read_u32(of_node, "qcom,drv-l1ss-timeout-us",
- &drv_l1ss_timeout_us);
+ &drv_info->l1ss_timeout_us);
if (ret)
- drv_l1ss_timeout_us = L1SS_TIMEOUT_US;
+ drv_info->l1ss_timeout_us = L1SS_TIMEOUT_US;
PCIE_DBG(pcie_dev, "PCIe: RC%d: DRV L1ss timeout: %dus\n",
- pcie_dev->rc_idx, drv_l1ss_timeout_us);
+ pcie_dev->rc_idx, drv_info->l1ss_timeout_us);
drv_info->dev_id = pcie_dev->rc_idx;
@@ -5676,7 +5677,7 @@ static int msm_pcie_setup_drv(struct msm_pcie_dev_t *pcie_dev,
pkt->dword[0] = MSM_PCIE_DRV_CMD_ENABLE;
pkt->dword[1] = hdr->dev_id;
- pkt->dword[2] = drv_l1ss_timeout_us / 1000;
+ pkt->dword[2] = drv_info->l1ss_timeout_us / 1000;
msg = &drv_info->drv_disable;
pkt = &msg->pkt;
@@ -6225,9 +6226,19 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
if (target_link_speed == current_link_speed)
set_link_speed = false;
+ else
+ PCIE_DBG(pcie_dev,
+ "PCIe: RC%d: switching from Gen%d to Gen%d\n",
+ pcie_dev->rc_idx, current_link_speed,
+ target_link_speed);
if (target_link_width == current_link_width)
set_link_width = false;
+ else
+ PCIE_DBG(pcie_dev,
+ "PCIe: RC%d: switching from x%d to x%d\n",
+ pcie_dev->rc_idx, current_link_width,
+ target_link_width);
if (!set_link_speed && !set_link_width)
return 0;
@@ -6270,6 +6281,9 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
if (target_link_speed < current_link_speed)
msm_pcie_scale_link_bandwidth(pcie_dev, target_link_speed);
+
+ PCIE_DBG(pcie_dev, "PCIe: RC%d: successfully switched link bandwidth\n",
+ pcie_dev->rc_idx);
out:
msm_pcie_allow_l1(root_pci_dev);
@@ -6997,11 +7011,13 @@ static int msm_pcie_drv_resume(struct msm_pcie_dev_t *pcie_dev)
return 0;
}
-static int msm_pcie_drv_suspend(struct msm_pcie_dev_t *pcie_dev)
+static int msm_pcie_drv_suspend(struct msm_pcie_dev_t *pcie_dev,
+ u32 options)
{
struct rpmsg_device *rpdev = pcie_drv.rpdev;
struct msm_pcie_drv_info *drv_info = pcie_dev->drv_info;
struct msm_pcie_drv_msg *drv_enable = &drv_info->drv_enable;
+ struct msm_pcie_drv_tre *pkt = &drv_enable->pkt;
struct msm_pcie_clk_info_t *clk_info;
int ret, i;
@@ -7024,6 +7040,11 @@ static int msm_pcie_drv_suspend(struct msm_pcie_dev_t *pcie_dev)
/* disable global irq - no more linkdown/aer detection */
disable_irq(pcie_dev->irq[MSM_PCIE_INT_GLOBAL_INT].num);
+ if (options & MSM_PCIE_CONFIG_NO_L1SS_TO)
+ pkt->dword[2] = 0;
+ else
+ pkt->dword[2] = drv_info->l1ss_timeout_us / 1000;
+
drv_info->reply_seq = drv_info->seq++;
drv_enable->hdr.seq = drv_info->reply_seq;
@@ -7134,7 +7155,7 @@ int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
PCIE_DBG(pcie_dev,
"PCIe: RC%d: DRV: user requests for DRV suspend\n",
rc_idx);
- ret = msm_pcie_drv_suspend(pcie_dev);
+ ret = msm_pcie_drv_suspend(pcie_dev, options);
break;
case MSM_PCIE_SUSPEND:
PCIE_DBG(&msm_pcie_dev[rc_idx],
diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
index 976eaa9..58e4873 100644
--- a/drivers/pci/controller/pci-tegra.c
+++ b/drivers/pci/controller/pci-tegra.c
@@ -545,12 +545,15 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class);
-/* Tegra PCIE requires relaxed ordering */
+/* Tegra20 and Tegra30 PCIE requires relaxed ordering */
static void tegra_pcie_relax_enable(struct pci_dev *dev)
{
pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_relax_enable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_relax_enable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_relax_enable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_relax_enable);
static int tegra_pcie_request_resources(struct tegra_pcie *pcie)
{
diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c
index 6692654..c3a0889 100644
--- a/drivers/pci/controller/pcie-cadence-ep.c
+++ b/drivers/pci/controller/pcie-cadence-ep.c
@@ -355,7 +355,7 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
ep->irq_pci_addr = (pci_addr & ~pci_addr_mask);
ep->irq_pci_fn = fn;
}
- writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
+ writel(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
return 0;
}
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index c5ff6ca..1bfbceb9 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -394,75 +394,6 @@ static struct pci_ops mtk_pcie_ops_v2 = {
.write = mtk_pcie_config_write,
};
-static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
-{
- struct mtk_pcie *pcie = port->pcie;
- struct resource *mem = &pcie->mem;
- const struct mtk_pcie_soc *soc = port->pcie->soc;
- u32 val;
- size_t size;
- int err;
-
- /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
- if (pcie->base) {
- val = readl(pcie->base + PCIE_SYS_CFG_V2);
- val |= PCIE_CSR_LTSSM_EN(port->slot) |
- PCIE_CSR_ASPM_L1_EN(port->slot);
- writel(val, pcie->base + PCIE_SYS_CFG_V2);
- }
-
- /* Assert all reset signals */
- writel(0, port->base + PCIE_RST_CTRL);
-
- /*
- * Enable PCIe link down reset, if link status changed from link up to
- * link down, this will reset MAC control registers and configuration
- * space.
- */
- writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
-
- /* De-assert PHY, PE, PIPE, MAC and configuration reset */
- val = readl(port->base + PCIE_RST_CTRL);
- val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
- PCIE_MAC_SRSTB | PCIE_CRSTB;
- writel(val, port->base + PCIE_RST_CTRL);
-
- /* Set up vendor ID and class code */
- if (soc->need_fix_class_id) {
- val = PCI_VENDOR_ID_MEDIATEK;
- writew(val, port->base + PCIE_CONF_VEND_ID);
-
- val = PCI_CLASS_BRIDGE_HOST;
- writew(val, port->base + PCIE_CONF_CLASS_ID);
- }
-
- /* 100ms timeout value should be enough for Gen1/2 training */
- err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val,
- !!(val & PCIE_PORT_LINKUP_V2), 20,
- 100 * USEC_PER_MSEC);
- if (err)
- return -ETIMEDOUT;
-
- /* Set INTx mask */
- val = readl(port->base + PCIE_INT_MASK);
- val &= ~INTX_MASK;
- writel(val, port->base + PCIE_INT_MASK);
-
- /* Set AHB to PCIe translation windows */
- size = mem->end - mem->start;
- val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
- writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
-
- val = upper_32_bits(mem->start);
- writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
-
- /* Set PCIe to AXI translation memory space.*/
- val = fls(0xffffffff) | WIN_ENABLE;
- writel(val, port->base + PCIE_AXI_WINDOW0);
-
- return 0;
-}
-
static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
@@ -639,8 +570,6 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
ret = mtk_pcie_allocate_msi_domains(port);
if (ret)
return ret;
-
- mtk_pcie_enable_msi(port);
}
return 0;
@@ -707,6 +636,78 @@ static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
return 0;
}
+static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct resource *mem = &pcie->mem;
+ const struct mtk_pcie_soc *soc = port->pcie->soc;
+ u32 val;
+ size_t size;
+ int err;
+
+ /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
+ if (pcie->base) {
+ val = readl(pcie->base + PCIE_SYS_CFG_V2);
+ val |= PCIE_CSR_LTSSM_EN(port->slot) |
+ PCIE_CSR_ASPM_L1_EN(port->slot);
+ writel(val, pcie->base + PCIE_SYS_CFG_V2);
+ }
+
+ /* Assert all reset signals */
+ writel(0, port->base + PCIE_RST_CTRL);
+
+ /*
+ * Enable PCIe link down reset, if link status changed from link up to
+ * link down, this will reset MAC control registers and configuration
+ * space.
+ */
+ writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
+
+ /* De-assert PHY, PE, PIPE, MAC and configuration reset */
+ val = readl(port->base + PCIE_RST_CTRL);
+ val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
+ PCIE_MAC_SRSTB | PCIE_CRSTB;
+ writel(val, port->base + PCIE_RST_CTRL);
+
+ /* Set up vendor ID and class code */
+ if (soc->need_fix_class_id) {
+ val = PCI_VENDOR_ID_MEDIATEK;
+ writew(val, port->base + PCIE_CONF_VEND_ID);
+
+ val = PCI_CLASS_BRIDGE_PCI;
+ writew(val, port->base + PCIE_CONF_CLASS_ID);
+ }
+
+ /* 100ms timeout value should be enough for Gen1/2 training */
+ err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val,
+ !!(val & PCIE_PORT_LINKUP_V2), 20,
+ 100 * USEC_PER_MSEC);
+ if (err)
+ return -ETIMEDOUT;
+
+ /* Set INTx mask */
+ val = readl(port->base + PCIE_INT_MASK);
+ val &= ~INTX_MASK;
+ writel(val, port->base + PCIE_INT_MASK);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ mtk_pcie_enable_msi(port);
+
+ /* Set AHB to PCIe translation windows */
+ size = mem->end - mem->start;
+ val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
+
+ val = upper_32_bits(mem->start);
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
+
+ /* Set PCIe to AXI translation memory space.*/
+ val = fls(0xffffffff) | WIN_ENABLE;
+ writel(val, port->base + PCIE_AXI_WINDOW0);
+
+ return 0;
+}
+
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
@@ -1120,7 +1121,9 @@ static int mtk_pcie_request_resources(struct mtk_pcie *pcie)
if (err < 0)
return err;
- devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start);
+ err = devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start);
+ if (err)
+ return err;
return 0;
}
diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c
index 9b9c677..333ab60 100644
--- a/drivers/pci/controller/pcie-rcar.c
+++ b/drivers/pci/controller/pcie-rcar.c
@@ -93,8 +93,11 @@
#define LINK_SPEED_2_5GTS (1 << 16)
#define LINK_SPEED_5_0GTS (2 << 16)
#define MACCTLR 0x011058
+#define MACCTLR_NFTS_MASK GENMASK(23, 16) /* The name is from SH7786 */
#define SPEED_CHANGE BIT(24)
#define SCRAMBLE_DISABLE BIT(27)
+#define LTSMDIS BIT(31)
+#define MACCTLR_INIT_VAL (LTSMDIS | MACCTLR_NFTS_MASK)
#define PMSR 0x01105c
#define MACS2R 0x011078
#define MACCGSPSETR 0x011084
@@ -615,6 +618,8 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie)
if (IS_ENABLED(CONFIG_PCI_MSI))
rcar_pci_write_reg(pcie, 0x801f0000, PCIEMSITXR);
+ rcar_pci_write_reg(pcie, MACCTLR_INIT_VAL, MACCTLR);
+
/* Finish initialization - establish a PCI Express link */
rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR);
@@ -1237,6 +1242,7 @@ static int rcar_pcie_resume_noirq(struct device *dev)
return 0;
/* Re-establish the PCIe link */
+ rcar_pci_write_reg(pcie, MACCTLR_INIT_VAL, MACCTLR);
rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR);
return rcar_pcie_wait_for_dl(pcie);
}
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 65eaa6b..ab36e5c 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -818,12 +818,12 @@ static void vmd_remove(struct pci_dev *dev)
{
struct vmd_dev *vmd = pci_get_drvdata(dev);
- vmd_detach_resources(vmd);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_stop_root_bus(vmd->bus);
pci_remove_root_bus(vmd->bus);
vmd_cleanup_srcu(vmd);
vmd_teardown_dma_ops(vmd);
+ vmd_detach_resources(vmd);
irq_domain_remove(vmd->irq_domain);
}
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 12afa7f..c94c135 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -449,8 +449,15 @@ static void acpiphp_native_scan_bridge(struct pci_dev *bridge)
/* Scan non-hotplug bridges that need to be reconfigured */
for_each_pci_bridge(dev, bus) {
- if (!hotplug_is_native(dev))
- max = pci_scan_bridge(bus, dev, max, 1);
+ if (hotplug_is_native(dev))
+ continue;
+
+ max = pci_scan_bridge(bus, dev, max, 1);
+ if (dev->subordinate) {
+ pcibios_resource_survey_bus(dev->subordinate);
+ pci_bus_size_bridges(dev->subordinate);
+ pci_bus_assign_resources(dev->subordinate);
+ }
}
}
@@ -480,7 +487,6 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge)
if (PCI_SLOT(dev->devfn) == slot->device)
acpiphp_native_scan_bridge(dev);
}
- pci_assign_unassigned_bridge_resources(bus->self);
} else {
LIST_HEAD(add_list);
int max, pass;
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 811cf83..ef60718 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -106,6 +106,7 @@ struct slot {
* that has not yet been cleared by the user
* @pending_events: used by the IRQ handler to save events retrieved from the
* Slot Status register for later consumption by the IRQ thread
+ * @ist_running: flag to keep user request waiting while IRQ thread is running
* @request_result: result of last user request submitted to the IRQ thread
* @requester: wait queue to wake up on completion of user request,
* used for synchronous slot enable/disable request via sysfs
@@ -125,6 +126,7 @@ struct controller {
unsigned int notification_enabled:1;
unsigned int power_fault_detected;
atomic_t pending_events;
+ unsigned int ist_running;
int request_result;
wait_queue_head_t requester;
};
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index ec48c943..518c46f 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -348,7 +348,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
#endif /* PM */
};
-static int __init pcied_init(void)
+int __init pcie_hp_init(void)
{
int retval = 0;
@@ -359,4 +359,3 @@ static int __init pcied_init(void)
return retval;
}
-device_initcall(pcied_init);
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 9c397fa..c71964e 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -383,7 +383,8 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
ctrl->request_result = -ENODEV;
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
wait_event(ctrl->requester,
- !atomic_read(&ctrl->pending_events));
+ !atomic_read(&ctrl->pending_events) &&
+ !ctrl->ist_running);
return ctrl->request_result;
case POWERON_STATE:
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
@@ -416,7 +417,8 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
mutex_unlock(&p_slot->lock);
pciehp_request(ctrl, DISABLE_SLOT);
wait_event(ctrl->requester,
- !atomic_read(&ctrl->pending_events));
+ !atomic_read(&ctrl->pending_events) &&
+ !ctrl->ist_running);
return ctrl->request_result;
case POWEROFF_STATE:
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index a938abd..c3e3f53 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -620,6 +620,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
irqreturn_t ret;
u32 events;
+ ctrl->ist_running = true;
pci_config_pm_runtime_get(pdev);
/* rerun pciehp_isr() if the port was inaccessible on interrupt */
@@ -666,6 +667,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
up_read(&ctrl->reset_lock);
pci_config_pm_runtime_put(pdev);
+ ctrl->ist_running = false;
wake_up(&ctrl->requester);
return IRQ_HANDLED;
}
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
index cc860c5..a306cad 100644
--- a/drivers/pci/hotplug/rpaphp_core.c
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -154,11 +154,11 @@ static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
return speed;
}
-static int get_children_props(struct device_node *dn, const int **drc_indexes,
- const int **drc_names, const int **drc_types,
- const int **drc_power_domains)
+static int get_children_props(struct device_node *dn, const __be32 **drc_indexes,
+ const __be32 **drc_names, const __be32 **drc_types,
+ const __be32 **drc_power_domains)
{
- const int *indexes, *names, *types, *domains;
+ const __be32 *indexes, *names, *types, *domains;
indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
names = of_get_property(dn, "ibm,drc-names", NULL);
@@ -194,8 +194,8 @@ static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name,
char *drc_type, unsigned int my_index)
{
char *name_tmp, *type_tmp;
- const int *indexes, *names;
- const int *types, *domains;
+ const __be32 *indexes, *names;
+ const __be32 *types, *domains;
int i, rc;
rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
@@ -208,7 +208,7 @@ static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name,
/* Iterate through parent properties, looking for my-drc-index */
for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
- if ((unsigned int) indexes[i + 1] == my_index)
+ if (be32_to_cpu(indexes[i + 1]) == my_index)
break;
name_tmp += (strlen(name_tmp) + 1);
@@ -239,6 +239,8 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,
value = of_prop_next_u32(info, NULL, &entries);
if (!value)
return -EINVAL;
+ else
+ value++;
for (j = 0; j < entries; j++) {
of_read_drc_info_cell(&info, &value, &drc);
@@ -246,9 +248,10 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,
/* Should now know end of current entry */
/* Found it */
- if (my_index <= drc.last_drc_index) {
+ if (my_index >= drc.drc_index_start && my_index <= drc.last_drc_index) {
+ int index = my_index - drc.drc_index_start;
sprintf(cell_drc_name, "%s%d", drc.drc_name_prefix,
- my_index);
+ drc.drc_name_suffix_start + index);
break;
}
}
@@ -265,7 +268,7 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,
int rpaphp_check_drc_props(struct device_node *dn, char *drc_name,
char *drc_type)
{
- const unsigned int *my_index;
+ const __be32 *my_index;
my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
if (!my_index) {
@@ -273,12 +276,12 @@ int rpaphp_check_drc_props(struct device_node *dn, char *drc_name,
return -EINVAL;
}
- if (firmware_has_feature(FW_FEATURE_DRC_INFO))
+ if (of_find_property(dn->parent, "ibm,drc-info", NULL))
return rpaphp_check_drc_props_v2(dn, drc_name, drc_type,
- *my_index);
+ be32_to_cpu(*my_index));
else
return rpaphp_check_drc_props_v1(dn, drc_name, drc_type,
- *my_index);
+ be32_to_cpu(*my_index));
}
EXPORT_SYMBOL_GPL(rpaphp_check_drc_props);
@@ -309,10 +312,11 @@ static int is_php_type(char *drc_type)
* for built-in pci slots (even when the built-in slots are
* dlparable.)
*/
-static int is_php_dn(struct device_node *dn, const int **indexes,
- const int **names, const int **types, const int **power_domains)
+static int is_php_dn(struct device_node *dn, const __be32 **indexes,
+ const __be32 **names, const __be32 **types,
+ const __be32 **power_domains)
{
- const int *drc_types;
+ const __be32 *drc_types;
int rc;
rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
@@ -347,7 +351,7 @@ int rpaphp_add_slot(struct device_node *dn)
struct slot *slot;
int retval = 0;
int i;
- const int *indexes, *names, *types, *power_domains;
+ const __be32 *indexes, *names, *types, *power_domains;
char *name, *type;
if (!dn->name || strcmp(dn->name, "pci"))
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index af24ed5..23a363f 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -211,7 +211,7 @@ u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag)
return 0;
mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
- if (flag)
+ if (flag & PCI_MSIX_ENTRY_CTRL_MASKBIT)
mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
writel(mask_bits, pci_msix_desc_addr(desc) + PCI_MSIX_ENTRY_VECTOR_CTRL);
@@ -1155,7 +1155,8 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
const struct irq_affinity *affd)
{
static const struct irq_affinity msi_default_affd;
- int vecs = -ENOSPC;
+ int msix_vecs = -ENOSPC;
+ int msi_vecs = -ENOSPC;
if (flags & PCI_IRQ_AFFINITY) {
if (!affd)
@@ -1166,16 +1167,17 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
}
if (flags & PCI_IRQ_MSIX) {
- vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs,
- affd);
- if (vecs > 0)
- return vecs;
+ msix_vecs = __pci_enable_msix_range(dev, NULL, min_vecs,
+ max_vecs, affd);
+ if (msix_vecs > 0)
+ return msix_vecs;
}
if (flags & PCI_IRQ_MSI) {
- vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd);
- if (vecs > 0)
- return vecs;
+ msi_vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs,
+ affd);
+ if (msi_vecs > 0)
+ return msi_vecs;
}
/* use legacy irq if allowed */
@@ -1186,7 +1188,9 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
}
}
- return vecs;
+ if (msix_vecs == -ENOSPC)
+ return -ENOSPC;
+ return msi_vecs;
}
EXPORT_SYMBOL(pci_alloc_irq_vectors_affinity);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 54a8b23..de65154 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1051,17 +1051,22 @@ static int pci_pm_thaw_noirq(struct device *dev)
return error;
}
- if (pci_has_legacy_pm_support(pci_dev))
- return pci_legacy_resume_early(dev);
-
/*
- * pci_restore_state() requires the device to be in D0 (because of MSI
- * restoration among other things), so force it into D0 in case the
- * driver's "freeze" callbacks put it into a low-power state directly.
+ * Both the legacy ->resume_early() and the new pm->thaw_noirq()
+ * callbacks assume the device has been returned to D0 and its
+ * config state has been restored.
+ *
+ * In addition, pci_restore_state() restores MSI-X state in MMIO
+ * space, which requires the device to be in D0, so return it to D0
+ * in case the driver's "freeze" callbacks put it into a low-power
+ * state.
*/
pci_set_power_state(pci_dev, PCI_D0);
pci_restore_state(pci_dev);
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume_early(dev);
+
if (drv && drv->pm && drv->pm->thaw_noirq)
error = drv->pm->thaw_noirq(dev);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 2baf1f8..c9f51fc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -35,6 +35,8 @@
#include <linux/aer.h>
#include "pci.h"
+DEFINE_MUTEX(pci_slot_mutex);
+
const char *pci_power_names[] = {
"error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
};
@@ -5192,6 +5194,41 @@ static int pci_bus_reset(struct pci_bus *bus, int probe)
}
/**
+ * pci_bus_error_reset - reset the bridge's subordinate bus
+ * @bridge: The parent device that connects to the bus to reset
+ *
+ * This function will first try to reset the slots on this bus if the method is
+ * available. If slot reset fails or is not available, this will fall back to a
+ * secondary bus reset.
+ */
+int pci_bus_error_reset(struct pci_dev *bridge)
+{
+ struct pci_bus *bus = bridge->subordinate;
+ struct pci_slot *slot;
+
+ if (!bus)
+ return -ENOTTY;
+
+ mutex_lock(&pci_slot_mutex);
+ if (list_empty(&bus->slots))
+ goto bus_reset;
+
+ list_for_each_entry(slot, &bus->slots, list)
+ if (pci_probe_reset_slot(slot))
+ goto bus_reset;
+
+ list_for_each_entry(slot, &bus->slots, list)
+ if (pci_slot_reset(slot, 0))
+ goto bus_reset;
+
+ mutex_unlock(&pci_slot_mutex);
+ return 0;
+bus_reset:
+ mutex_unlock(&pci_slot_mutex);
+ return pci_bus_reset(bridge->subordinate, 0);
+}
+
+/**
* pci_probe_reset_bus - probe whether a PCI bus can be reset
* @bus: PCI bus to probe
*
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ab25752f..e9ede82 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -35,6 +35,7 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
int pci_probe_reset_function(struct pci_dev *dev);
int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
+int pci_bus_error_reset(struct pci_dev *dev);
/**
* struct pci_platform_pm_ops - Firmware PM callbacks
@@ -136,6 +137,7 @@ static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
+extern struct mutex pci_slot_mutex;
extern raw_spinlock_t pci_lock;
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 83180ed..1563e22 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -866,7 +866,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
{
if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
- e_info->dev[e_info->error_dev_num] = dev;
+ e_info->dev[e_info->error_dev_num] = pci_dev_get(dev);
e_info->error_dev_num++;
return 0;
}
@@ -1013,6 +1013,7 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
pcie_do_nonfatal_recovery(dev);
else if (info->severity == AER_FATAL)
pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER);
+ pci_dev_put(dev);
}
#ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -1115,8 +1116,9 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
&info->mask);
if (!(info->status & ~info->mask))
return 0;
- } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
- info->severity == AER_NONFATAL) {
+ } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ info->severity == AER_NONFATAL) {
/* Link is still healthy for IO reads */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
@@ -1526,7 +1528,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
- rc = pci_bridge_secondary_bus_reset(dev);
+ rc = pci_bus_error_reset(dev);
pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
/* Clear Root Error Status */
@@ -1569,10 +1571,9 @@ static struct pcie_port_service_driver aerdriver = {
*
* Invoked when AER root service driver is loaded.
*/
-static int __init aer_service_init(void)
+int __init pcie_aer_init(void)
{
if (!pci_aer_available() || aer_acpi_firmware_first())
return -ENXIO;
return pcie_port_service_register(&aerdriver);
}
-device_initcall(aer_service_init);
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index 1908dd2..118b5bc 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -307,8 +307,7 @@ static struct pcie_port_service_driver dpcdriver = {
.reset_link = dpc_reset_link,
};
-static int __init dpc_service_init(void)
+int __init pcie_dpc_init(void)
{
return pcie_port_service_register(&dpcdriver);
}
-device_initcall(dpc_service_init);
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 708fd3a..2c3b5bd 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -63,30 +63,12 @@ static int report_error_detected(struct pci_dev *dev, void *data)
if (!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->error_detected) {
- if (result_data->state == pci_channel_io_frozen &&
- dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
- /*
- * In case of fatal recovery, if one of down-
- * stream device has no driver. We might be
- * unable to recover because a later insmod
- * of a driver for this device is unaware of
- * its hw state.
- */
- pci_printk(KERN_DEBUG, dev, "device has %s\n",
- dev->driver ?
- "no AER-aware driver" : "no driver");
- }
-
/*
- * If there's any device in the subtree that does not
- * have an error_detected callback, returning
- * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
- * the subsequent mmio_enabled/slot_reset/resume
- * callbacks of "any" device in the subtree. All the
- * devices in the subtree are left in the error state
- * without recovery.
+ * If any device in the subtree does not have an error_detected
+ * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
+ * error callbacks of "any" device in the subtree, and will
+ * exit in the disconnected error state.
*/
-
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
vote = PCI_ERS_RESULT_NO_AER_DRIVER;
else
@@ -177,41 +159,30 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev)
{
int rc;
- rc = pci_bridge_secondary_bus_reset(dev);
+ rc = pci_bus_error_reset(dev);
pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
}
static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
{
- struct pci_dev *udev;
pci_ers_result_t status;
struct pcie_port_service_driver *driver = NULL;
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- /* Reset this port for all subordinates */
- udev = dev;
- } else {
- /* Reset the upstream component (likely downstream port) */
- udev = dev->bus->self;
- }
-
- /* Use the aer driver of the component firstly */
- driver = pcie_port_find_service(udev, service);
-
+ driver = pcie_port_find_service(dev, service);
if (driver && driver->reset_link) {
- status = driver->reset_link(udev);
- } else if (udev->has_secondary_link) {
- status = default_reset_link(udev);
+ status = driver->reset_link(dev);
+ } else if (dev->has_secondary_link) {
+ status = default_reset_link(dev);
} else {
pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
- pci_name(udev));
+ pci_name(dev));
return PCI_ERS_RESULT_DISCONNECT;
}
if (status != PCI_ERS_RESULT_RECOVERED) {
pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
- pci_name(udev));
+ pci_name(dev));
return PCI_ERS_RESULT_DISCONNECT;
}
@@ -243,31 +214,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
else
result_data.result = PCI_ERS_RESULT_RECOVERED;
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- /*
- * If the error is reported by a bridge, we think this error
- * is related to the downstream link of the bridge, so we
- * do error recovery on all subordinates of the bridge instead
- * of the bridge and clear the error status of the bridge.
- */
- if (cb == report_error_detected)
- dev->error_state = state;
- pci_walk_bus(dev->subordinate, cb, &result_data);
- if (cb == report_resume) {
- pci_aer_clear_device_status(dev);
- pci_cleanup_aer_uncorrect_error_status(dev);
- dev->error_state = pci_channel_io_normal;
- }
- } else {
- /*
- * If the error is reported by an end point, we think this
- * error is related to the upstream link of the end point.
- * The error is non fatal so the bus is ok; just invoke
- * the callback for the function that logged the error.
- */
- cb(dev, &result_data);
- }
-
+ pci_walk_bus(dev->subordinate, cb, &result_data);
return result_data.result;
}
@@ -347,6 +294,14 @@ void pcie_do_nonfatal_recovery(struct pci_dev *dev)
state = pci_channel_io_normal;
+ /*
+ * Error recovery runs on all subordinates of the first downstream port.
+ * If the downstream port detected the error, it is cleared at the end.
+ */
+ if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+ dev = dev->bus->self;
+
status = broadcast_error_message(dev,
state,
"error_detected",
@@ -378,6 +333,8 @@ void pcie_do_nonfatal_recovery(struct pci_dev *dev)
"resume",
report_resume);
+ pci_aer_clear_device_status(dev);
+ pci_cleanup_aer_uncorrect_error_status(dev);
pci_info(dev, "AER: Device recovery successful\n");
return;
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index e85c5a8..54d593d 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -437,6 +437,7 @@ static void pcie_pme_remove(struct pcie_device *srv)
pcie_pme_disable_interrupt(srv->port, data);
free_irq(srv->irq, srv);
+ cancel_work_sync(&data->work);
kfree(data);
}
@@ -454,8 +455,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
/**
* pcie_pme_service_init - Register the PCIe PME service driver.
*/
-static int __init pcie_pme_service_init(void)
+int __init pcie_pme_init(void)
{
return pcie_port_service_register(&pcie_pme_driver);
}
-device_initcall(pcie_pme_service_init);
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index d59afa4..2498b2d 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -23,6 +23,30 @@
#define PCIE_PORT_DEVICE_MAXSERVICES 4
+#ifdef CONFIG_PCIEAER
+int pcie_aer_init(void);
+#else
+static inline int pcie_aer_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_HOTPLUG_PCI_PCIE
+int pcie_hp_init(void);
+#else
+static inline int pcie_hp_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_PCIE_PME
+int pcie_pme_init(void);
+#else
+static inline int pcie_pme_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_PCIE_DPC
+int pcie_dpc_init(void);
+#else
+static inline int pcie_dpc_init(void) { return 0; }
+#endif
+
/* Port Type */
#define PCIE_ANY_PORT (~0)
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index eef22dc..23a5a0c 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -226,11 +226,20 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
{}
};
+static void __init pcie_init_services(void)
+{
+ pcie_aer_init();
+ pcie_pme_init();
+ pcie_dpc_init();
+ pcie_hp_init();
+}
+
static int __init pcie_portdrv_init(void)
{
if (pcie_ports_disabled)
return -EACCES;
+ pcie_init_services();
dmi_check_system(pcie_portdrv_dmi_table);
return pci_register_driver(&pcie_portdriver);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 06be529..20a57a4 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4219,15 +4219,21 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
static bool pci_quirk_cavium_acs_match(struct pci_dev *dev)
{
+ if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
+ return false;
+
+ switch (dev->device) {
/*
- * Effectively selects all downstream ports for whole ThunderX 1
- * family by 0xf800 mask (which represents 8 SoCs), while the lower
- * bits of device ID are used to indicate which subdevice is used
- * within the SoC.
+ * Effectively selects all downstream ports for whole ThunderX1
+ * (which represents 8 SoCs).
*/
- return (pci_is_pcie(dev) &&
- (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) &&
- ((dev->device & 0xf800) == 0xa000));
+ case 0xa000 ... 0xa7ff: /* ThunderX1 */
+ case 0xaf84: /* ThunderX2 */
+ case 0xb884: /* ThunderX3 */
+ return true;
+ default:
+ return false;
+ }
}
static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags)
@@ -4576,7 +4582,7 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
#define INTEL_BSPR_REG_BPPD (1 << 9)
/* Upstream Peer Decode Configuration Register */
-#define INTEL_UPDCR_REG 0x1114
+#define INTEL_UPDCR_REG 0x1014
/* 5:0 Peer Decode Enable bits */
#define INTEL_UPDCR_REG_MASK 0x3f
@@ -5083,8 +5089,8 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
pci_disable_device(pdev);
}
#define SWITCHTEC_QUIRK(vid) \
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, vid, \
- quirk_switchtec_ntb_dma_alias)
+ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_MICROSEMI, vid, \
+ PCI_CLASS_BRIDGE_OTHER, 8, quirk_switchtec_ntb_dma_alias)
SWITCHTEC_QUIRK(0x8531); /* PFX 24xG3 */
SWITCHTEC_QUIRK(0x8532); /* PFX 32xG3 */
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index e634229..a32897f 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -14,7 +14,6 @@
struct kset *pci_slots_kset;
EXPORT_SYMBOL_GPL(pci_slots_kset);
-static DEFINE_MUTEX(pci_slot_mutex);
static ssize_t pci_slot_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c
index 72db2e0..5aaa4ce 100644
--- a/drivers/pci/switch/switchtec.c
+++ b/drivers/pci/switch/switchtec.c
@@ -13,7 +13,7 @@
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
-
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/nospec.h>
MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
@@ -633,7 +633,7 @@ static int ioctl_event_summary(struct switchtec_dev *stdev,
u32 reg;
s.global = ioread32(&stdev->mmio_sw_event->global_summary);
- s.part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap);
+ s.part_bitmap = readq(&stdev->mmio_sw_event->part_event_bitmap);
s.local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary);
for (i = 0; i < stdev->partition_count; i++) {
diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig
index 8786a96..aa917a6 100644
--- a/drivers/phy/broadcom/Kconfig
+++ b/drivers/phy/broadcom/Kconfig
@@ -60,7 +60,8 @@
config PHY_BRCM_SATA
tristate "Broadcom SATA PHY driver"
- depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST
+ depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \
+ ARCH_BCM_63XX || COMPILE_TEST
depends on OF
select GENERIC_PHY
default ARCH_BCM_IPROC
diff --git a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
index 986224f..5a180f7 100644
--- a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
+++ b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
@@ -156,7 +156,6 @@ static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv,
{
struct device *dev = priv->dev;
const __be32 *offset;
- int ret;
priv->reg_bits = of_device_get_match_data(dev);
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
index abbbe75..5629d56 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
@@ -160,8 +160,8 @@ static int qcom_usb_hs_phy_power_on(struct phy *phy)
/* setup initial state */
qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state,
uphy->vbus_edev);
- ret = devm_extcon_register_notifier(&ulpi->dev, uphy->vbus_edev,
- EXTCON_USB, &uphy->vbus_notify);
+ ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB,
+ &uphy->vbus_notify);
if (ret)
goto err_ulpi;
}
@@ -182,6 +182,9 @@ static int qcom_usb_hs_phy_power_off(struct phy *phy)
{
struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ if (uphy->vbus_edev)
+ extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB,
+ &uphy->vbus_notify);
regulator_disable(uphy->v3p3);
regulator_disable(uphy->v1p8);
clk_disable_unprepare(uphy->sleep_clk);
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 6fb2b69..50cdf20 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/string.h>
#include <linux/usb/of.h>
#include <linux/workqueue.h>
@@ -199,7 +200,7 @@ static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch)
val = readl(usb2_base + USB2_OBINTEN);
writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
- rcar_gen3_enable_vbus_ctrl(ch, 0);
+ rcar_gen3_enable_vbus_ctrl(ch, 1);
rcar_gen3_init_for_host(ch);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
@@ -241,9 +242,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
if (!ch->has_otg_pins || !ch->phy->init_count)
return -EIO;
- if (!strncmp(buf, "host", strlen("host")))
+ if (sysfs_streq(buf, "host"))
new_mode = PHY_MODE_USB_HOST;
- else if (!strncmp(buf, "peripheral", strlen("peripheral")))
+ else if (sysfs_streq(buf, "peripheral"))
new_mode = PHY_MODE_USB_DEVICE;
else
return -EINVAL;
diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c
index a44680d..c267afb 100644
--- a/drivers/phy/ti/phy-twl4030-usb.c
+++ b/drivers/phy/ti/phy-twl4030-usb.c
@@ -144,6 +144,7 @@
#define PMBR1 0x0D
#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
+static irqreturn_t twl4030_usb_irq(int irq, void *_twl);
/*
* If VBUS is valid or ID is ground, then we know a
* cable is present and we need to be runtime-enabled
@@ -395,6 +396,33 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
}
+static int __maybe_unused twl4030_usb_suspend(struct device *dev)
+{
+ struct twl4030_usb *twl = dev_get_drvdata(dev);
+
+ /*
+ * we need enabled runtime on resume,
+ * so turn irq off here, so we do not get it early
+ * note: wakeup on usb plug works independently of this
+ */
+ dev_dbg(twl->dev, "%s\n", __func__);
+ disable_irq(twl->irq);
+
+ return 0;
+}
+
+static int __maybe_unused twl4030_usb_resume(struct device *dev)
+{
+ struct twl4030_usb *twl = dev_get_drvdata(dev);
+
+ dev_dbg(twl->dev, "%s\n", __func__);
+ enable_irq(twl->irq);
+ /* check whether cable status changed */
+ twl4030_usb_irq(0, twl);
+
+ return 0;
+}
+
static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
@@ -655,6 +683,7 @@ static const struct phy_ops ops = {
static const struct dev_pm_ops twl4030_usb_pm_ops = {
SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend,
twl4030_usb_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume)
};
static int twl4030_usb_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 08925d2..1bd3c10 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -72,10 +72,8 @@
#define GPIO_REG_OFFSET(p) ((p) / 32)
#define GPIO_REG_SHIFT(p) ((p) % 32)
-enum bcm2835_pinconf_param {
- /* argument: bcm2835_pinconf_pull */
- BCM2835_PINCONF_PARAM_PULL = (PIN_CONFIG_END + 1),
-};
+/* argument: bcm2835_pinconf_pull */
+#define BCM2835_PINCONF_PARAM_PULL (PIN_CONFIG_END + 1)
struct bcm2835_pinctrl {
struct device *dev;
diff --git a/drivers/pinctrl/bcm/pinctrl-ns2-mux.c b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c
index 4b5cf0e..951090f 100644
--- a/drivers/pinctrl/bcm/pinctrl-ns2-mux.c
+++ b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c
@@ -640,8 +640,8 @@ static int ns2_pinmux_enable(struct pinctrl_dev *pctrl_dev,
const struct ns2_pin_function *func;
const struct ns2_pin_group *grp;
- if (grp_select > pinctrl->num_groups ||
- func_select > pinctrl->num_functions)
+ if (grp_select >= pinctrl->num_groups ||
+ func_select >= pinctrl->num_functions)
return -EINVAL;
func = &pinctrl->functions[func_select];
diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c
index c4f4d90..618e044 100644
--- a/drivers/pinctrl/cirrus/pinctrl-madera-core.c
+++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c
@@ -608,7 +608,7 @@ static int madera_mux_set_mux(struct pinctrl_dev *pctldev,
unsigned int n_chip_groups = priv->chip->n_pin_groups;
const char *func_name = madera_mux_funcs[selector].name;
unsigned int reg;
- int i, ret;
+ int i, ret = 0;
dev_dbg(priv->dev, "%s selecting %u (%s) for group %u (%s)\n",
__func__, selector, func_name, group,
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index 2969ff3..177ee11 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -40,6 +40,13 @@ struct pinctrl_dt_map {
static void dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps)
{
+ int i;
+
+ for (i = 0; i < num_maps; ++i) {
+ kfree_const(map[i].dev_name);
+ map[i].dev_name = NULL;
+ }
+
if (pctldev) {
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
if (ops->dt_free_map)
@@ -74,7 +81,13 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
/* Initialize common mapping table entry fields */
for (i = 0; i < num_maps; i++) {
- map[i].dev_name = dev_name(p->dev);
+ const char *devname;
+
+ devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
+ if (!devname)
+ goto err_free_map;
+
+ map[i].dev_name = devname;
map[i].name = statename;
if (pctldev)
map[i].ctrl_dev_name = dev_name(pctldev->dev);
@@ -82,10 +95,8 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
/* Remember the converted mapping table entries */
dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
- if (!dt_map) {
- dt_free_map(pctldev, map, num_maps);
- return -ENOMEM;
- }
+ if (!dt_map)
+ goto err_free_map;
dt_map->pctldev = pctldev;
dt_map->map = map;
@@ -93,6 +104,10 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
list_add_tail(&dt_map->node, &p->dt_maps);
return pinctrl_register_map(map, num_maps, false);
+
+err_free_map:
+ dt_free_map(pctldev, map, num_maps);
+ return -ENOMEM;
}
struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c
index f38d596..021e28f 100644
--- a/drivers/pinctrl/intel/pinctrl-baytrail.c
+++ b/drivers/pinctrl/intel/pinctrl-baytrail.c
@@ -196,7 +196,6 @@ struct byt_gpio {
struct platform_device *pdev;
struct pinctrl_dev *pctl_dev;
struct pinctrl_desc pctl_desc;
- raw_spinlock_t lock;
const struct byt_pinctrl_soc_data *soc_data;
struct byt_community *communities_copy;
struct byt_gpio_pin_context *saved_context;
@@ -707,6 +706,8 @@ static const struct byt_pinctrl_soc_data *byt_soc_data[] = {
NULL,
};
+static DEFINE_RAW_SPINLOCK(byt_lock);
+
static struct byt_community *byt_get_community(struct byt_gpio *vg,
unsigned int pin)
{
@@ -848,7 +849,7 @@ static void byt_set_group_simple_mux(struct byt_gpio *vg,
unsigned long flags;
int i;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
for (i = 0; i < group.npins; i++) {
void __iomem *padcfg0;
@@ -868,7 +869,7 @@ static void byt_set_group_simple_mux(struct byt_gpio *vg,
writel(value, padcfg0);
}
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
}
static void byt_set_group_mixed_mux(struct byt_gpio *vg,
@@ -878,7 +879,7 @@ static void byt_set_group_mixed_mux(struct byt_gpio *vg,
unsigned long flags;
int i;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
for (i = 0; i < group.npins; i++) {
void __iomem *padcfg0;
@@ -898,7 +899,7 @@ static void byt_set_group_mixed_mux(struct byt_gpio *vg,
writel(value, padcfg0);
}
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
}
static int byt_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
@@ -947,11 +948,11 @@ static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned int offset)
unsigned long flags;
u32 value;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
value = readl(reg);
value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL);
writel(value, reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
}
static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev,
@@ -963,7 +964,7 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev,
u32 value, gpio_mux;
unsigned long flags;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
/*
* In most cases, func pin mux 000 means GPIO function.
@@ -985,7 +986,7 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev,
"pin %u forcibly re-configured as GPIO\n", offset);
}
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
pm_runtime_get(&vg->pdev->dev);
@@ -1013,7 +1014,7 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev,
unsigned long flags;
u32 value;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
value = readl(val_reg);
value &= ~BYT_DIR_MASK;
@@ -1030,7 +1031,7 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev,
"Potential Error: Setting GPIO with direct_irq_en to output");
writel(value, val_reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return 0;
}
@@ -1099,11 +1100,11 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset,
u32 conf, pull, val, debounce;
u16 arg = 0;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
conf = readl(conf_reg);
pull = conf & BYT_PULL_ASSIGN_MASK;
val = readl(val_reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
@@ -1130,9 +1131,9 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset,
if (!(conf & BYT_DEBOUNCE_EN))
return -EINVAL;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
debounce = readl(db_reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
switch (debounce & BYT_DEBOUNCE_PULSE_MASK) {
case BYT_DEBOUNCE_PULSE_375US:
@@ -1184,7 +1185,7 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev,
u32 conf, val, debounce;
int i, ret = 0;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
conf = readl(conf_reg);
val = readl(val_reg);
@@ -1292,7 +1293,7 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev,
if (!ret)
writel(conf, conf_reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return ret;
}
@@ -1317,9 +1318,9 @@ static int byt_gpio_get(struct gpio_chip *chip, unsigned offset)
unsigned long flags;
u32 val;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
val = readl(reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return !!(val & BYT_LEVEL);
}
@@ -1334,13 +1335,13 @@ static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
if (!reg)
return;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
old_val = readl(reg);
if (value)
writel(old_val | BYT_LEVEL, reg);
else
writel(old_val & ~BYT_LEVEL, reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
}
static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
@@ -1353,9 +1354,9 @@ static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
if (!reg)
return -EINVAL;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
value = readl(reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
if (!(value & BYT_OUTPUT_EN))
return GPIOF_DIR_OUT;
@@ -1398,14 +1399,14 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
const char *label;
unsigned int pin;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
pin = vg->soc_data->pins[i].number;
reg = byt_gpio_reg(vg, pin, BYT_CONF0_REG);
if (!reg) {
seq_printf(s,
"Could not retrieve pin %i conf0 reg\n",
pin);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
continue;
}
conf0 = readl(reg);
@@ -1414,11 +1415,11 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
if (!reg) {
seq_printf(s,
"Could not retrieve pin %i val reg\n", pin);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
continue;
}
val = readl(reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
comm = byt_get_community(vg, pin);
if (!comm) {
@@ -1502,9 +1503,9 @@ static void byt_irq_ack(struct irq_data *d)
if (!reg)
return;
- raw_spin_lock(&vg->lock);
+ raw_spin_lock(&byt_lock);
writel(BIT(offset % 32), reg);
- raw_spin_unlock(&vg->lock);
+ raw_spin_unlock(&byt_lock);
}
static void byt_irq_mask(struct irq_data *d)
@@ -1528,7 +1529,7 @@ static void byt_irq_unmask(struct irq_data *d)
if (!reg)
return;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
value = readl(reg);
switch (irqd_get_trigger_type(d)) {
@@ -1551,7 +1552,7 @@ static void byt_irq_unmask(struct irq_data *d)
writel(value, reg);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
}
static int byt_irq_type(struct irq_data *d, unsigned int type)
@@ -1565,7 +1566,7 @@ static int byt_irq_type(struct irq_data *d, unsigned int type)
if (!reg || offset >= vg->chip.ngpio)
return -EINVAL;
- raw_spin_lock_irqsave(&vg->lock, flags);
+ raw_spin_lock_irqsave(&byt_lock, flags);
value = readl(reg);
WARN(value & BYT_DIRECT_IRQ_EN,
@@ -1587,7 +1588,7 @@ static int byt_irq_type(struct irq_data *d, unsigned int type)
else if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
- raw_spin_unlock_irqrestore(&vg->lock, flags);
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return 0;
}
@@ -1623,9 +1624,9 @@ static void byt_gpio_irq_handler(struct irq_desc *desc)
continue;
}
- raw_spin_lock(&vg->lock);
+ raw_spin_lock(&byt_lock);
pending = readl(reg);
- raw_spin_unlock(&vg->lock);
+ raw_spin_unlock(&byt_lock);
for_each_set_bit(pin, &pending, 32) {
virq = irq_find_mapping(vg->chip.irq.domain, base + pin);
generic_handle_irq(virq);
@@ -1828,8 +1829,6 @@ static int byt_pinctrl_probe(struct platform_device *pdev)
return PTR_ERR(vg->pctl_dev);
}
- raw_spin_lock_init(&vg->lock);
-
ret = byt_gpio_probe(vg);
if (ret)
return ret;
@@ -1845,8 +1844,11 @@ static int byt_gpio_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct byt_gpio *vg = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&byt_lock, flags);
+
for (i = 0; i < vg->soc_data->npins; i++) {
void __iomem *reg;
u32 value;
@@ -1867,6 +1869,7 @@ static int byt_gpio_suspend(struct device *dev)
vg->saved_context[i].val = value;
}
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return 0;
}
@@ -1874,8 +1877,11 @@ static int byt_gpio_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct byt_gpio *vg = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&byt_lock, flags);
+
for (i = 0; i < vg->soc_data->npins; i++) {
void __iomem *reg;
u32 value;
@@ -1913,6 +1919,7 @@ static int byt_gpio_resume(struct device *dev)
}
}
+ raw_spin_unlock_irqrestore(&byt_lock, flags);
return 0;
}
#endif
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index 227646e..f16baf9 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -157,6 +157,7 @@ struct chv_pin_context {
* @pctldesc: Pin controller description
* @pctldev: Pointer to the pin controller device
* @chip: GPIO chip in this pin controller
+ * @irqchip: IRQ chip in this pin controller
* @regs: MMIO registers
* @intr_lines: Stores mapping between 16 HW interrupt wires and GPIO
* offset (in GPIO number space)
@@ -170,6 +171,7 @@ struct chv_pinctrl {
struct pinctrl_desc pctldesc;
struct pinctrl_dev *pctldev;
struct gpio_chip chip;
+ struct irq_chip irqchip;
void __iomem *regs;
unsigned intr_lines[16];
const struct chv_community *community;
@@ -1477,16 +1479,6 @@ static int chv_gpio_irq_type(struct irq_data *d, unsigned type)
return 0;
}
-static struct irq_chip chv_gpio_irqchip = {
- .name = "chv-gpio",
- .irq_startup = chv_gpio_irq_startup,
- .irq_ack = chv_gpio_irq_ack,
- .irq_mask = chv_gpio_irq_mask,
- .irq_unmask = chv_gpio_irq_unmask,
- .irq_set_type = chv_gpio_irq_type,
- .flags = IRQCHIP_SKIP_SET_WAKE,
-};
-
static void chv_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -1595,7 +1587,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq)
intsel >>= CHV_PADCTRL0_INTSEL_SHIFT;
if (need_valid_mask && intsel >= community->nirqs)
- clear_bit(i, chip->irq.valid_mask);
+ clear_bit(desc->number, chip->irq.valid_mask);
}
/*
@@ -1626,7 +1618,15 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq)
}
}
- ret = gpiochip_irqchip_add(chip, &chv_gpio_irqchip, 0,
+ pctrl->irqchip.name = "chv-gpio";
+ pctrl->irqchip.irq_startup = chv_gpio_irq_startup;
+ pctrl->irqchip.irq_ack = chv_gpio_irq_ack;
+ pctrl->irqchip.irq_mask = chv_gpio_irq_mask;
+ pctrl->irqchip.irq_unmask = chv_gpio_irq_unmask;
+ pctrl->irqchip.irq_set_type = chv_gpio_irq_type;
+ pctrl->irqchip.flags = IRQCHIP_SKIP_SET_WAKE;
+
+ ret = gpiochip_irqchip_add(chip, &pctrl->irqchip, 0,
handle_bad_irq, IRQ_TYPE_NONE);
if (ret) {
dev_err(pctrl->dev, "failed to add IRQ chip\n");
@@ -1643,7 +1643,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq)
}
}
- gpiochip_set_chained_irqchip(chip, &chv_gpio_irqchip, irq,
+ gpiochip_set_chained_irqchip(chip, &pctrl->irqchip, irq,
chv_gpio_irq_handler);
return 0;
}
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index 1ea3438..89ff279 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -49,6 +49,7 @@
#define PADCFG0_GPIROUTNMI BIT(17)
#define PADCFG0_PMODE_SHIFT 10
#define PADCFG0_PMODE_MASK (0xf << PADCFG0_PMODE_SHIFT)
+#define PADCFG0_PMODE_GPIO 0
#define PADCFG0_GPIORXDIS BIT(9)
#define PADCFG0_GPIOTXDIS BIT(8)
#define PADCFG0_GPIORXSTATE BIT(1)
@@ -301,7 +302,7 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
cfg1 = readl(intel_get_padcfg(pctrl, pin, PADCFG1));
mode = (cfg0 & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT;
- if (!mode)
+ if (mode == PADCFG0_PMODE_GPIO)
seq_puts(s, "GPIO ");
else
seq_printf(s, "mode %d ", mode);
@@ -422,6 +423,11 @@ static void __intel_gpio_set_direction(void __iomem *padcfg0, bool input)
writel(value, padcfg0);
}
+static int intel_gpio_get_gpio_mode(void __iomem *padcfg0)
+{
+ return (readl(padcfg0) & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT;
+}
+
static void intel_gpio_set_gpio_mode(void __iomem *padcfg0)
{
u32 value;
@@ -450,7 +456,20 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev,
}
padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
+
+ /*
+ * If pin is already configured in GPIO mode, we assume that
+ * firmware provides correct settings. In such case we avoid
+ * potential glitches on the pin. Otherwise, for the pin in
+ * alternative mode, consumer has to supply respective flags.
+ */
+ if (intel_gpio_get_gpio_mode(padcfg0) == PADCFG0_PMODE_GPIO) {
+ raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ return 0;
+ }
+
intel_gpio_set_gpio_mode(padcfg0);
+
/* Disable TX buffer and enable RX (this will be input) */
__intel_gpio_set_direction(padcfg0, true);
diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
index 3aac640..d76ac6b 100644
--- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
@@ -592,10 +592,10 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
regmap_read(info->regmap, in_reg, &in_val);
/* Set initial polarity based on current input level. */
- if (in_val & d->mask)
- val |= d->mask; /* falling */
+ if (in_val & BIT(d->hwirq % GPIO_PER_REG))
+ val |= BIT(d->hwirq % GPIO_PER_REG); /* falling */
else
- val &= ~d->mask; /* rising */
+ val &= ~(BIT(d->hwirq % GPIO_PER_REG)); /* rising */
break;
}
default:
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index cd7a5d9..b1ffdd3 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -544,7 +544,8 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id)
irqreturn_t ret = IRQ_NONE;
unsigned int i, irqnr;
unsigned long flags;
- u32 *regs, regval;
+ u32 __iomem *regs;
+ u32 regval;
u64 status, mask;
/* Read the wake status */
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index ef7ab20..9e2f373 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -493,7 +493,6 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
unsigned num_pins, num_configs, reserve;
unsigned long *configs;
struct property *pins;
- bool has_config;
u32 pinfunc;
int ret, i;
@@ -509,9 +508,6 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
return ret;
}
- if (num_configs)
- has_config = true;
-
num_pins = pins->length / sizeof(u32);
if (!num_pins) {
dev_err(pctldev->dev, "no pins found in node %pOF\n", np);
@@ -524,7 +520,7 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
* map for each pin.
*/
reserve = 1;
- if (has_config && num_pins >= 1)
+ if (num_configs)
reserve++;
reserve *= num_pins;
ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
@@ -547,7 +543,7 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps,
group, func);
- if (has_config) {
+ if (num_configs) {
ret = pinctrl_utils_add_map_configs(pctldev, map,
reserved_maps, num_maps, group,
configs, num_configs,
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 50f0ec4..fad0e13 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -1574,16 +1574,6 @@ void at91_pinctrl_gpio_resume(void)
#define gpio_irq_set_wake NULL
#endif /* CONFIG_PM */
-static struct irq_chip gpio_irqchip = {
- .name = "GPIO",
- .irq_ack = gpio_irq_ack,
- .irq_disable = gpio_irq_mask,
- .irq_mask = gpio_irq_mask,
- .irq_unmask = gpio_irq_unmask,
- /* .irq_set_type is set dynamically */
- .irq_set_wake = gpio_irq_set_wake,
-};
-
static void gpio_irq_handler(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
@@ -1624,12 +1614,22 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev,
struct gpio_chip *gpiochip_prev = NULL;
struct at91_gpio_chip *prev = NULL;
struct irq_data *d = irq_get_irq_data(at91_gpio->pioc_virq);
+ struct irq_chip *gpio_irqchip;
int ret, i;
+ gpio_irqchip = devm_kzalloc(&pdev->dev, sizeof(*gpio_irqchip), GFP_KERNEL);
+ if (!gpio_irqchip)
+ return -ENOMEM;
+
at91_gpio->pioc_hwirq = irqd_to_hwirq(d);
- /* Setup proper .irq_set_type function */
- gpio_irqchip.irq_set_type = at91_gpio->ops->irq_type;
+ gpio_irqchip->name = "GPIO";
+ gpio_irqchip->irq_ack = gpio_irq_ack;
+ 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_type = at91_gpio->ops->irq_type;
/* Disable irqs of this PIO controller */
writel_relaxed(~0, at91_gpio->regbase + PIO_IDR);
@@ -1640,7 +1640,7 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev,
* interrupt.
*/
ret = gpiochip_irqchip_add(&at91_gpio->chip,
- &gpio_irqchip,
+ gpio_irqchip,
0,
handle_edge_irq,
IRQ_TYPE_NONE);
@@ -1658,7 +1658,7 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev,
if (!gpiochip_prev) {
/* Then register the chain on the parent IRQ */
gpiochip_set_chained_irqchip(&at91_gpio->chip,
- &gpio_irqchip,
+ gpio_irqchip,
at91_gpio->pioc_virq,
gpio_irq_handler);
return 0;
diff --git a/drivers/pinctrl/pinctrl-gemini.c b/drivers/pinctrl/pinctrl-gemini.c
index fa7d998..3535f984 100644
--- a/drivers/pinctrl/pinctrl-gemini.c
+++ b/drivers/pinctrl/pinctrl-gemini.c
@@ -591,13 +591,16 @@ static const unsigned int tvc_3512_pins[] = {
319, /* TVC_DATA[1] */
301, /* TVC_DATA[2] */
283, /* TVC_DATA[3] */
- 265, /* TVC_CLK */
320, /* TVC_DATA[4] */
302, /* TVC_DATA[5] */
284, /* TVC_DATA[6] */
266, /* TVC_DATA[7] */
};
+static const unsigned int tvc_clk_3512_pins[] = {
+ 265, /* TVC_CLK */
+};
+
/* NAND flash pins */
static const unsigned int nflash_3512_pins[] = {
199, 200, 201, 202, 216, 217, 218, 219, 220, 234, 235, 236, 237, 252,
@@ -629,7 +632,7 @@ static const unsigned int pflash_3512_pins_extended[] = {
/* Serial flash pins CE0, CE1, DI, DO, CK */
static const unsigned int sflash_3512_pins[] = { 230, 231, 232, 233, 211 };
-/* The GPIO0A (0) pin overlap with TVC and extended parallel flash */
+/* The GPIO0A (0) pin overlap with TVC CLK and extended parallel flash */
static const unsigned int gpio0a_3512_pins[] = { 265 };
/* The GPIO0B (1-4) pins overlap with TVC and ICE */
@@ -823,7 +826,13 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
.num_pins = ARRAY_SIZE(tvc_3512_pins),
/* Conflict with character LCD and ICE */
.mask = LCD_PADS_ENABLE,
- .value = TVC_PADS_ENABLE | TVC_CLK_PAD_ENABLE,
+ .value = TVC_PADS_ENABLE,
+ },
+ {
+ .name = "tvcclkgrp",
+ .pins = tvc_clk_3512_pins,
+ .num_pins = ARRAY_SIZE(tvc_clk_3512_pins),
+ .value = TVC_CLK_PAD_ENABLE,
},
/*
* The construction is done such that it is possible to use a serial
@@ -860,8 +869,8 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
.name = "gpio0agrp",
.pins = gpio0a_3512_pins,
.num_pins = ARRAY_SIZE(gpio0a_3512_pins),
- /* Conflict with TVC */
- .mask = TVC_PADS_ENABLE,
+ /* Conflict with TVC CLK */
+ .mask = TVC_CLK_PAD_ENABLE,
},
{
.name = "gpio0bgrp",
@@ -1531,13 +1540,16 @@ static const unsigned int tvc_3516_pins[] = {
311, /* TVC_DATA[1] */
394, /* TVC_DATA[2] */
374, /* TVC_DATA[3] */
- 333, /* TVC_CLK */
354, /* TVC_DATA[4] */
395, /* TVC_DATA[5] */
312, /* TVC_DATA[6] */
334, /* TVC_DATA[7] */
};
+static const unsigned int tvc_clk_3516_pins[] = {
+ 333, /* TVC_CLK */
+};
+
/* NAND flash pins */
static const unsigned int nflash_3516_pins[] = {
243, 260, 261, 224, 280, 262, 281, 264, 300, 263, 282, 301, 320, 283,
@@ -1570,7 +1582,7 @@ static const unsigned int pflash_3516_pins_extended[] = {
static const unsigned int sflash_3516_pins[] = { 296, 338, 295, 359, 339 };
/* The GPIO0A (0-4) pins overlap with TVC and extended parallel flash */
-static const unsigned int gpio0a_3516_pins[] = { 333, 354, 395, 312, 334 };
+static const unsigned int gpio0a_3516_pins[] = { 354, 395, 312, 334 };
/* The GPIO0B (5-7) pins overlap with ICE */
static const unsigned int gpio0b_3516_pins[] = { 375, 396, 376 };
@@ -1602,6 +1614,9 @@ static const unsigned int gpio0j_3516_pins[] = { 359, 339 };
/* The GPIO0K (30,31) pins overlap with NAND flash */
static const unsigned int gpio0k_3516_pins[] = { 275, 298 };
+/* The GPIO0L (0) pins overlap with TVC_CLK */
+static const unsigned int gpio0l_3516_pins[] = { 333 };
+
/* The GPIO1A (0-4) pins that overlap with IDE and parallel flash */
static const unsigned int gpio1a_3516_pins[] = { 221, 200, 222, 201, 220 };
@@ -1761,7 +1776,13 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
.num_pins = ARRAY_SIZE(tvc_3516_pins),
/* Conflict with character LCD */
.mask = LCD_PADS_ENABLE,
- .value = TVC_PADS_ENABLE | TVC_CLK_PAD_ENABLE,
+ .value = TVC_PADS_ENABLE,
+ },
+ {
+ .name = "tvcclkgrp",
+ .pins = tvc_clk_3516_pins,
+ .num_pins = ARRAY_SIZE(tvc_clk_3516_pins),
+ .value = TVC_CLK_PAD_ENABLE,
},
/*
* The construction is done such that it is possible to use a serial
@@ -1873,6 +1894,13 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
.value = PFLASH_PADS_DISABLE | NAND_PADS_DISABLE,
},
{
+ .name = "gpio0lgrp",
+ .pins = gpio0l_3516_pins,
+ .num_pins = ARRAY_SIZE(gpio0l_3516_pins),
+ /* Conflict with TVE CLK */
+ .mask = TVC_CLK_PAD_ENABLE,
+ },
+ {
.name = "gpio1agrp",
.pins = gpio1a_3516_pins,
.num_pins = ARRAY_SIZE(gpio1a_3516_pins),
@@ -2184,7 +2212,8 @@ static int gemini_pmx_set_mux(struct pinctrl_dev *pctldev,
func->name, grp->name);
regmap_read(pmx->map, GLOBAL_MISC_CTRL, &before);
- regmap_update_bits(pmx->map, GLOBAL_MISC_CTRL, grp->mask,
+ regmap_update_bits(pmx->map, GLOBAL_MISC_CTRL,
+ grp->mask | grp->value,
grp->value);
regmap_read(pmx->map, GLOBAL_MISC_CTRL, &after);
diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c
index 628817c..a5accff 100644
--- a/drivers/pinctrl/pinctrl-ingenic.c
+++ b/drivers/pinctrl/pinctrl-ingenic.c
@@ -847,4 +847,4 @@ static int __init ingenic_pinctrl_drv_register(void)
{
return platform_driver_register(&ingenic_pinctrl_driver);
}
-postcore_initcall(ingenic_pinctrl_drv_register);
+subsys_initcall(ingenic_pinctrl_drv_register);
diff --git a/drivers/pinctrl/pinctrl-lpc18xx.c b/drivers/pinctrl/pinctrl-lpc18xx.c
index 190f17e..1d3b88e 100644
--- a/drivers/pinctrl/pinctrl-lpc18xx.c
+++ b/drivers/pinctrl/pinctrl-lpc18xx.c
@@ -630,14 +630,8 @@ static const struct pinctrl_pin_desc lpc18xx_pins[] = {
LPC18XX_PIN(i2c0_sda, PIN_I2C0_SDA),
};
-/**
- * enum lpc18xx_pin_config_param - possible pin configuration parameters
- * @PIN_CONFIG_GPIO_PIN_INT: route gpio to the gpio pin interrupt
- * controller.
- */
-enum lpc18xx_pin_config_param {
- PIN_CONFIG_GPIO_PIN_INT = PIN_CONFIG_END + 1,
-};
+/* PIN_CONFIG_GPIO_PIN_INT: route gpio to the gpio pin interrupt controller */
+#define PIN_CONFIG_GPIO_PIN_INT (PIN_CONFIG_END + 1)
static const struct pinconf_generic_params lpc18xx_params[] = {
{"nxp,gpio-pin-interrupt", PIN_CONFIG_GPIO_PIN_INT, 0},
diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
index 93f8bd0..ae74b26 100644
--- a/drivers/pinctrl/pinctrl-xway.c
+++ b/drivers/pinctrl/pinctrl-xway.c
@@ -1746,14 +1746,6 @@ static int pinmux_xway_probe(struct platform_device *pdev)
}
xway_pctrl_desc.pins = xway_info.pads;
- /* register the gpio chip */
- xway_chip.parent = &pdev->dev;
- ret = devm_gpiochip_add_data(&pdev->dev, &xway_chip, NULL);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register gpio chip\n");
- return ret;
- }
-
/* setup the data needed by pinctrl */
xway_pctrl_desc.name = dev_name(&pdev->dev);
xway_pctrl_desc.npins = xway_chip.ngpio;
@@ -1775,10 +1767,33 @@ static int pinmux_xway_probe(struct platform_device *pdev)
return ret;
}
- /* finish with registering the gpio range in pinctrl */
- xway_gpio_range.npins = xway_chip.ngpio;
- xway_gpio_range.base = xway_chip.base;
- pinctrl_add_gpio_range(xway_info.pctrl, &xway_gpio_range);
+ /* register the gpio chip */
+ xway_chip.parent = &pdev->dev;
+ xway_chip.owner = THIS_MODULE;
+ xway_chip.of_node = pdev->dev.of_node;
+ ret = devm_gpiochip_add_data(&pdev->dev, &xway_chip, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register gpio chip\n");
+ return ret;
+ }
+
+ /*
+ * For DeviceTree-supported systems, the gpio core checks the
+ * pinctrl's device node for the "gpio-ranges" property.
+ * If it is present, it takes care of adding the pin ranges
+ * for the driver. In this case the driver can skip ahead.
+ *
+ * In order to remain compatible with older, existing DeviceTree
+ * files which don't set the "gpio-ranges" property or systems that
+ * utilize ACPI the driver has to call gpiochip_add_pin_range().
+ */
+ if (!of_property_read_bool(pdev->dev.of_node, "gpio-ranges")) {
+ /* finish with registering the gpio range in pinctrl */
+ xway_gpio_range.npins = xway_chip.ngpio;
+ xway_gpio_range.base = xway_chip.base;
+ pinctrl_add_gpio_range(xway_info.pctrl, &xway_gpio_range);
+ }
+
dev_info(&pdev->dev, "Init done\n");
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-zynq.c b/drivers/pinctrl/pinctrl-zynq.c
index a0daf27..90fd37e 100644
--- a/drivers/pinctrl/pinctrl-zynq.c
+++ b/drivers/pinctrl/pinctrl-zynq.c
@@ -971,15 +971,12 @@ enum zynq_io_standards {
zynq_iostd_max
};
-/**
- * enum zynq_pin_config_param - possible pin configuration parameters
- * @PIN_CONFIG_IOSTANDARD: if the pin can select an IO standard, the argument to
+/*
+ * PIN_CONFIG_IOSTANDARD: if the pin can select an IO standard, the argument to
* this parameter (on a custom format) tells the driver which alternative
* IO standard to use.
*/
-enum zynq_pin_config_param {
- PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1,
-};
+#define PIN_CONFIG_IOSTANDARD (PIN_CONFIG_END + 1)
static const struct pinconf_generic_params zynq_dt_params[] = {
{"io-standard", PIN_CONFIG_IOSTANDARD, zynq_iostd_lvcmos18},
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 42e0d7d..cfe36b4 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1152,11 +1152,24 @@ static int pmic_gpio_probe(struct platform_device *pdev)
goto err_free;
}
- ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
- if (ret) {
- dev_err(dev, "failed to add pin range\n, ret=%d\n", ret);
- gpiochip_remove(&state->chip);
- goto err_free;
+ /*
+ * For DeviceTree-supported systems, the gpio core checks the
+ * pinctrl's device node for the "gpio-ranges" property.
+ * If it is present, it takes care of adding the pin ranges
+ * for the driver. In this case the driver can skip ahead.
+ *
+ * In order to remain compatible with older, existing DeviceTree
+ * files which don't set the "gpio-ranges" property or systems that
+ * utilize ACPI the driver has to call gpiochip_add_pin_range().
+ */
+ if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
+ ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
+ npins);
+ if (ret) {
+ dev_err(dev, "failed to add pin range\n");
+ gpiochip_remove(&state->chip);
+ goto err_free;
+ }
}
err_free:
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index 41ee33e..c5cab7c 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -754,12 +754,23 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = gpiochip_add_pin_range(&pctrl->chip,
- dev_name(pctrl->dev),
- 0, 0, pctrl->chip.ngpio);
- if (ret) {
- dev_err(pctrl->dev, "failed to add pin range\n");
- goto unregister_gpiochip;
+ /*
+ * For DeviceTree-supported systems, the gpio core checks the
+ * pinctrl's device node for the "gpio-ranges" property.
+ * If it is present, it takes care of adding the pin ranges
+ * for the driver. In this case the driver can skip ahead.
+ *
+ * In order to remain compatible with older, existing DeviceTree
+ * files which don't set the "gpio-ranges" property or systems that
+ * utilize ACPI the driver has to call gpiochip_add_pin_range().
+ */
+ if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) {
+ ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
+ 0, 0, pctrl->chip.ngpio);
+ if (ret) {
+ dev_err(pctrl->dev, "failed to add pin range\n");
+ goto unregister_gpiochip;
+ }
}
platform_set_drvdata(pdev, pctrl);
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index f49ea3d..e87ee43 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -494,8 +494,10 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
if (match) {
irq_chip = kmemdup(match->data,
sizeof(*irq_chip), GFP_KERNEL);
- if (!irq_chip)
+ if (!irq_chip) {
+ of_node_put(np);
return -ENOMEM;
+ }
wkup_np = np;
break;
}
@@ -512,6 +514,7 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
bank->nr_pins, &exynos_eint_irqd_ops, bank);
if (!bank->irq_domain) {
dev_err(dev, "wkup irq domain add failed\n");
+ of_node_put(wkup_np);
return -ENXIO;
}
@@ -526,8 +529,10 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
weint_data = devm_kcalloc(dev,
bank->nr_pins, sizeof(*weint_data),
GFP_KERNEL);
- if (!weint_data)
+ if (!weint_data) {
+ of_node_put(wkup_np);
return -ENOMEM;
+ }
for (idx = 0; idx < bank->nr_pins; ++idx) {
irq = irq_of_parse_and_map(bank->of_node, idx);
@@ -544,10 +549,13 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
}
}
- if (!muxed_banks)
+ if (!muxed_banks) {
+ of_node_put(wkup_np);
return 0;
+ }
irq = irq_of_parse_and_map(wkup_np, 0);
+ of_node_put(wkup_np);
if (!irq) {
dev_err(dev, "irq number for muxed EINTs not found\n");
return 0;
diff --git a/drivers/pinctrl/samsung/pinctrl-s3c24xx.c b/drivers/pinctrl/samsung/pinctrl-s3c24xx.c
index 7e824e4..9bd0a3d 100644
--- a/drivers/pinctrl/samsung/pinctrl-s3c24xx.c
+++ b/drivers/pinctrl/samsung/pinctrl-s3c24xx.c
@@ -490,8 +490,10 @@ static int s3c24xx_eint_init(struct samsung_pinctrl_drv_data *d)
return -ENODEV;
eint_data = devm_kzalloc(dev, sizeof(*eint_data), GFP_KERNEL);
- if (!eint_data)
+ if (!eint_data) {
+ of_node_put(eint_np);
return -ENOMEM;
+ }
eint_data->drvdata = d;
@@ -503,12 +505,14 @@ static int s3c24xx_eint_init(struct samsung_pinctrl_drv_data *d)
irq = irq_of_parse_and_map(eint_np, i);
if (!irq) {
dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i);
+ of_node_put(eint_np);
return -ENXIO;
}
eint_data->parents[i] = irq;
irq_set_chained_handler_and_data(irq, handlers[i], eint_data);
}
+ of_node_put(eint_np);
bank = d->pin_banks;
for (i = 0; i < d->nr_banks; ++i, ++bank) {
diff --git a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
index c399f09..f97f817 100644
--- a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
+++ b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
@@ -704,8 +704,10 @@ static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d)
return -ENODEV;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
+ if (!data) {
+ of_node_put(eint0_np);
return -ENOMEM;
+ }
data->drvdata = d;
for (i = 0; i < NUM_EINT0_IRQ; ++i) {
@@ -714,6 +716,7 @@ static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d)
irq = irq_of_parse_and_map(eint0_np, i);
if (!irq) {
dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i);
+ of_node_put(eint0_np);
return -ENXIO;
}
@@ -721,6 +724,7 @@ static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d)
s3c64xx_eint0_handlers[i],
data);
}
+ of_node_put(eint0_np);
bank = d->pin_banks;
for (i = 0; i < d->nr_banks; ++i, ++bank) {
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 698c7d8..c05217e 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -272,6 +272,7 @@ static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
&reserved_maps, num_maps);
if (ret < 0) {
samsung_dt_free_map(pctldev, *map, *num_maps);
+ of_node_put(np);
return ret;
}
}
@@ -785,8 +786,10 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions(
if (!of_get_child_count(cfg_np)) {
ret = samsung_pinctrl_create_function(dev, drvdata,
cfg_np, func);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(cfg_np);
return ERR_PTR(ret);
+ }
if (ret > 0) {
++func;
++func_cnt;
@@ -797,8 +800,11 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions(
for_each_child_of_node(cfg_np, func_np) {
ret = samsung_pinctrl_create_function(dev, drvdata,
func_np, func);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(func_np);
+ of_node_put(cfg_np);
return ERR_PTR(ret);
+ }
if (ret > 0) {
++func;
++func_cnt;
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
index b81c807..d2fcf7f 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
@@ -395,7 +395,7 @@ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM
#define MOD_SEL0_24 FM(SEL_HSCIF0_0) FM(SEL_HSCIF0_1)
#define MOD_SEL0_23 FM(SEL_HSCIF1_0) FM(SEL_HSCIF1_1)
#define MOD_SEL0_22 FM(SEL_HSCIF2_0) FM(SEL_HSCIF2_1)
-#define MOD_SEL0_21_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1) FM(SEL_I2C1_2) FM(SEL_I2C1_3) FM(SEL_I2C1_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL0_21_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1) FM(SEL_I2C1_2) FM(SEL_I2C1_3)
#define MOD_SEL0_19_18_17 FM(SEL_I2C2_0) FM(SEL_I2C2_1) FM(SEL_I2C2_2) FM(SEL_I2C2_3) FM(SEL_I2C2_4) F_(0, 0) F_(0, 0) F_(0, 0)
#define MOD_SEL0_16 FM(SEL_NDFC_0) FM(SEL_NDFC_1)
#define MOD_SEL0_15 FM(SEL_PWM0_0) FM(SEL_PWM0_1)
diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7264.c b/drivers/pinctrl/sh-pfc/pfc-sh7264.c
index 8070765..e1c34e1 100644
--- a/drivers/pinctrl/sh-pfc/pfc-sh7264.c
+++ b/drivers/pinctrl/sh-pfc/pfc-sh7264.c
@@ -1716,6 +1716,9 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
},
{ PINMUX_CFG_REG("PFCR3", 0xfffe38a8, 16, 4) {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
PF12MD_000, PF12MD_001, 0, PF12MD_011,
PF12MD_100, PF12MD_101, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 }
@@ -1759,8 +1762,10 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
0, 0, 0, 0, 0, 0, 0, 0,
PF1MD_000, PF1MD_001, PF1MD_010, PF1MD_011,
PF1MD_100, PF1MD_101, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0
- }
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ PF0MD_000, PF0MD_001, PF0MD_010, PF0MD_011,
+ PF0MD_100, PF0MD_101, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 }
},
{ PINMUX_CFG_REG("PFIOR0", 0xfffe38b2, 16, 1) {
diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7734.c b/drivers/pinctrl/sh-pfc/pfc-sh7734.c
index 6502e67..3eccc9b 100644
--- a/drivers/pinctrl/sh-pfc/pfc-sh7734.c
+++ b/drivers/pinctrl/sh-pfc/pfc-sh7734.c
@@ -1453,7 +1453,7 @@ static const struct pinmux_func pinmux_func_gpios[] = {
GPIO_FN(ET0_ETXD2_A),
GPIO_FN(EX_CS5), GPIO_FN(SD1_CMD_A), GPIO_FN(ATADIR), GPIO_FN(QSSL_B),
GPIO_FN(ET0_ETXD3_A),
- GPIO_FN(RD_WR), GPIO_FN(TCLK1_B),
+ GPIO_FN(RD_WR), GPIO_FN(TCLK0), GPIO_FN(CAN_CLK_B), GPIO_FN(ET0_ETXD4),
GPIO_FN(EX_WAIT0), GPIO_FN(TCLK1_B),
GPIO_FN(EX_WAIT1), GPIO_FN(SD1_DAT0_A), GPIO_FN(DREQ2),
GPIO_FN(CAN1_TX_C), GPIO_FN(ET0_LINK_C), GPIO_FN(ET0_ETXD5_A),
@@ -1949,7 +1949,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
/* IP3_20 [1] */
FN_EX_WAIT0, FN_TCLK1_B,
/* IP3_19_18 [2] */
- FN_RD_WR, FN_TCLK1_B, 0, 0,
+ FN_RD_WR, FN_TCLK0, FN_CAN_CLK_B, FN_ET0_ETXD4,
/* IP3_17_15 [3] */
FN_EX_CS5, FN_SD1_CMD_A, FN_ATADIR, FN_QSSL_B,
FN_ET0_ETXD3_A, 0, 0, 0,
@@ -2213,22 +2213,22 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
/* IP10_22 [1] */
FN_CAN_CLK_A, FN_RX4_D,
/* IP10_21_19 [3] */
- FN_AUDIO_CLKOUT, FN_TX1_E, FN_HRTS0_C, FN_FSE_B,
- FN_LCD_M_DISP_B, 0, 0, 0,
+ FN_AUDIO_CLKOUT, FN_TX1_E, 0, FN_HRTS0_C, FN_FSE_B,
+ FN_LCD_M_DISP_B, 0, 0,
/* IP10_18_16 [3] */
- FN_AUDIO_CLKC, FN_SCK1_E, FN_HCTS0_C, FN_FRB_B,
- FN_LCD_VEPWC_B, 0, 0, 0,
+ FN_AUDIO_CLKC, FN_SCK1_E, 0, FN_HCTS0_C, FN_FRB_B,
+ FN_LCD_VEPWC_B, 0, 0,
/* IP10_15 [1] */
FN_AUDIO_CLKB_A, FN_LCD_CLK_B,
/* IP10_14_12 [3] */
FN_AUDIO_CLKA_A, FN_VI1_CLK_B, FN_SCK1_D, FN_IECLK_B,
FN_LCD_FLM_B, 0, 0, 0,
/* IP10_11_9 [3] */
- FN_SSI_SDATA3, FN_VI1_7_B, FN_HTX0_C, FN_FWE_B,
- FN_LCD_CL2_B, 0, 0, 0,
+ FN_SSI_SDATA3, FN_VI1_7_B, 0, FN_HTX0_C, FN_FWE_B,
+ FN_LCD_CL2_B, 0, 0,
/* IP10_8_6 [3] */
- FN_SSI_SDATA2, FN_VI1_6_B, FN_HRX0_C, FN_FRE_B,
- FN_LCD_CL1_B, 0, 0, 0,
+ FN_SSI_SDATA2, FN_VI1_6_B, 0, FN_HRX0_C, FN_FRE_B,
+ FN_LCD_CL1_B, 0, 0,
/* IP10_5_3 [3] */
FN_SSI_WS23, FN_VI1_5_B, FN_TX1_D, FN_HSCK0_C, FN_FALE_B,
FN_LCD_DON_B, 0, 0, 0,
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index a9bec6e..14dfbbd 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -410,7 +410,7 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
unsigned int num_configs;
bool has_config = 0;
unsigned reserve = 0;
- int num_pins, num_funcs, maps_per_pin, i, err;
+ int num_pins, num_funcs, maps_per_pin, i, err = 0;
pctl = pinctrl_dev_get_drvdata(pctldev);
@@ -437,41 +437,45 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
if (has_config && num_pins >= 1)
maps_per_pin++;
- if (!num_pins || !maps_per_pin)
- return -EINVAL;
+ if (!num_pins || !maps_per_pin) {
+ err = -EINVAL;
+ goto exit;
+ }
reserve = num_pins * maps_per_pin;
err = pinctrl_utils_reserve_map(pctldev, map,
reserved_maps, num_maps, reserve);
if (err)
- return err;
+ goto exit;
for (i = 0; i < num_pins; i++) {
err = of_property_read_u32_index(node, "pinmux",
i, &pinfunc);
if (err)
- return err;
+ goto exit;
pin = STM32_GET_PIN_NO(pinfunc);
func = STM32_GET_PIN_FUNC(pinfunc);
if (!stm32_pctrl_is_function_valid(pctl, pin, func)) {
dev_err(pctl->dev, "invalid function.\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto exit;
}
grp = stm32_pctrl_find_group_by_pin(pctl, pin);
if (!grp) {
dev_err(pctl->dev, "unable to match pin %d to group\n",
pin);
- return -EINVAL;
+ err = -EINVAL;
+ goto exit;
}
err = stm32_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map,
reserved_maps, num_maps);
if (err)
- return err;
+ goto exit;
if (has_config) {
err = pinctrl_utils_add_map_configs(pctldev, map,
@@ -479,11 +483,13 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
configs, num_configs,
PIN_MAP_TYPE_CONFIGS_GROUP);
if (err)
- return err;
+ goto exit;
}
}
- return 0;
+exit:
+ kfree(configs);
+ return err;
}
static int stm32_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 26ebedc..61aaaf58 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -1042,6 +1042,7 @@ static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl,
static int sunxi_pinctrl_build_state(struct platform_device *pdev)
{
struct sunxi_pinctrl *pctl = platform_get_drvdata(pdev);
+ void *ptr;
int i;
/*
@@ -1108,13 +1109,15 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev)
}
/* And now allocated and fill the array for real */
- pctl->functions = krealloc(pctl->functions,
- pctl->nfunctions * sizeof(*pctl->functions),
- GFP_KERNEL);
- if (!pctl->functions) {
+ ptr = krealloc(pctl->functions,
+ pctl->nfunctions * sizeof(*pctl->functions),
+ GFP_KERNEL);
+ if (!ptr) {
kfree(pctl->functions);
+ pctl->functions = NULL;
return -ENOMEM;
}
+ pctl->functions = ptr;
for (i = 0; i < pctl->desc->npins; i++) {
const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index cc6bf43..501097e 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
@@ -20,6 +20,7 @@
#define GSI_CMD_POLL_CNT 5
#define GSI_STOP_CMD_TIMEOUT_MS 200
#define GSI_MAX_CH_LOW_WEIGHT 15
+#define GSI_IRQ_STORM_THR 5
#define GSI_STOP_CMD_POLL_CNT 4
#define GSI_STOP_IN_PROC_CMD_POLL_CNT 2
@@ -754,6 +755,8 @@ static void gsi_handle_irq(void)
unsigned long cnt = 0;
while (1) {
+ if (!gsi_ctx->per.clk_status_cb())
+ break;
type = gsi_readl(gsi_ctx->base +
GSI_EE_n_CNTXT_TYPE_IRQ_OFFS(ee));
@@ -806,8 +809,14 @@ static irqreturn_t gsi_isr(int irq, void *ctxt)
gsi_ctx->per.rel_clk_cb(gsi_ctx->per.user_data);
}
} else if (!gsi_ctx->per.clk_status_cb()) {
+ /* we only want to capture the gsi isr storm here */
+ if (atomic_read(&gsi_ctx->num_unclock_irq) ==
+ GSI_IRQ_STORM_THR)
+ gsi_ctx->per.enable_clk_bug_on();
+ atomic_inc(&gsi_ctx->num_unclock_irq);
return IRQ_HANDLED;
} else {
+ atomic_set(&gsi_ctx->num_unclock_irq, 0);
gsi_handle_irq();
}
return IRQ_HANDLED;
@@ -2775,6 +2784,15 @@ int gsi_query_channel_db_addr(unsigned long chan_hdl,
}
EXPORT_SYMBOL(gsi_query_channel_db_addr);
+int gsi_pending_irq_type(void)
+{
+ int ee = gsi_ctx->per.ee;
+
+ return gsi_readl(gsi_ctx->base +
+ GSI_EE_n_CNTXT_TYPE_IRQ_OFFS(ee));
+}
+EXPORT_SYMBOL(gsi_pending_irq_type);
+
int gsi_start_channel(unsigned long chan_hdl)
{
enum gsi_ch_cmd_opcode op = GSI_CH_START;
@@ -3745,17 +3763,19 @@ int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode)
return -GSI_STATUS_UNSUPPORTED_OP;
}
+ spin_lock_irqsave(&gsi_ctx->slock, flags);
+
if (atomic_read(&ctx->poll_mode))
curr = GSI_CHAN_MODE_POLL;
else
curr = GSI_CHAN_MODE_CALLBACK;
if (unlikely(mode == curr)) {
- GSIDBG("already in requested mode %u chan_hdl=%lu\n",
+ GSIERR("already in requested mode %u chan_hdl=%lu\n",
curr, chan_hdl);
+ spin_unlock_irqrestore(&gsi_ctx->slock, flags);
return -GSI_STATUS_UNSUPPORTED_OP;
}
- spin_lock_irqsave(&gsi_ctx->slock, flags);
if (curr == GSI_CHAN_MODE_CALLBACK &&
mode == GSI_CHAN_MODE_POLL) {
__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0);
@@ -4249,6 +4269,97 @@ int gsi_alloc_channel_ee(unsigned int chan_idx, unsigned int ee, int *code)
}
EXPORT_SYMBOL(gsi_alloc_channel_ee);
+int gsi_enable_flow_control_ee(unsigned int chan_idx, unsigned int ee,
+ int *code)
+{
+ enum gsi_generic_ee_cmd_opcode op = GSI_GEN_EE_CMD_ENABLE_FLOW_CHANNEL;
+ uint32_t val;
+ enum gsi_chan_state curr_state = GSI_CHAN_STATE_NOT_ALLOCATED;
+ int res;
+
+ if (!gsi_ctx) {
+ pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__);
+ return -GSI_STATUS_NODEV;
+ }
+
+ if (chan_idx >= gsi_ctx->max_ch || !code) {
+ GSIERR("bad params chan_idx=%d\n", chan_idx);
+ return -GSI_STATUS_INVALID_PARAMS;
+ }
+
+ mutex_lock(&gsi_ctx->mlock);
+ reinit_completion(&gsi_ctx->gen_ee_cmd_compl);
+
+ /* invalidate the response */
+ gsi_ctx->scratch.word0.val = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_CNTXT_SCRATCH_0_OFFS(gsi_ctx->per.ee));
+ gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code = 0;
+ gsi_writel(gsi_ctx->scratch.word0.val, gsi_ctx->base +
+ GSI_EE_n_CNTXT_SCRATCH_0_OFFS(gsi_ctx->per.ee));
+
+ gsi_ctx->gen_ee_cmd_dbg.flow_ctrl_channel++;
+ val = (((op << GSI_EE_n_GSI_EE_GENERIC_CMD_OPCODE_SHFT) &
+ GSI_EE_n_GSI_EE_GENERIC_CMD_OPCODE_BMSK) |
+ ((chan_idx << GSI_EE_n_GSI_EE_GENERIC_CMD_VIRT_CHAN_IDX_SHFT) &
+ GSI_EE_n_GSI_EE_GENERIC_CMD_VIRT_CHAN_IDX_BMSK) |
+ ((ee << GSI_EE_n_GSI_EE_GENERIC_CMD_EE_SHFT) &
+ GSI_EE_n_GSI_EE_GENERIC_CMD_EE_BMSK));
+ gsi_writel(val, gsi_ctx->base +
+ GSI_EE_n_GSI_EE_GENERIC_CMD_OFFS(gsi_ctx->per.ee));
+
+ res = wait_for_completion_timeout(&gsi_ctx->gen_ee_cmd_compl,
+ msecs_to_jiffies(GSI_CMD_TIMEOUT));
+ if (res == 0) {
+ GSIERR("chan_idx=%u ee=%u timed out\n", chan_idx, ee);
+ res = -GSI_STATUS_TIMED_OUT;
+ goto free_lock;
+ }
+
+ gsi_ctx->scratch.word0.val = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_CNTXT_SCRATCH_0_OFFS(gsi_ctx->per.ee));
+ if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+ GSI_GEN_EE_CMD_RETURN_CODE_CHANNEL_NOT_RUNNING) {
+ GSIDBG("chan_idx=%u ee=%u not in correct state\n",
+ chan_idx, ee);
+ *code = GSI_GEN_EE_CMD_RETURN_CODE_CHANNEL_NOT_RUNNING;
+ res = -GSI_STATUS_RES_ALLOC_FAILURE;
+ goto free_lock;
+ } else if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+ GSI_GEN_EE_CMD_RETURN_CODE_INCORRECT_CHANNEL_TYPE ||
+ gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+ GSI_GEN_EE_CMD_RETURN_CODE_INCORRECT_CHANNEL_INDEX){
+ GSIERR("chan_idx=%u ee=%u not in correct state\n",
+ chan_idx, ee);
+ GSI_ASSERT();
+ }
+ if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code == 0) {
+ GSIERR("No response received\n");
+ res = -GSI_STATUS_ERROR;
+ goto free_lock;
+ }
+
+ /*Reading current channel state*/
+ val = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_CNTXT_0_OFFS(chan_idx, ee));
+ curr_state = (val &
+ GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_BMSK) >>
+ GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_SHFT;
+ if (curr_state == GSI_CHAN_STATE_FLOW_CONTROL) {
+ GSIDBG("ch %u state updated to %u\n", chan_idx, curr_state);
+ res = GSI_STATUS_SUCCESS;
+ } else {
+ GSIERR("ch %u state updated to %u incorrect state\n",
+ chan_idx, curr_state);
+ res = -GSI_STATUS_ERROR;
+ }
+ *code = gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code;
+free_lock:
+ mutex_unlock(&gsi_ctx->mlock);
+
+ return res;
+}
+EXPORT_SYMBOL(gsi_enable_flow_control_ee);
+
int gsi_map_virtual_ch_to_per_ep(u32 ee, u32 chan_num, u32 per_ep_index)
{
if (!gsi_ctx) {
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 3b6c648..da43504 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef GSI_H
@@ -198,6 +198,7 @@ struct ch_debug_stats {
struct gsi_generic_ee_cmd_debug_stats {
unsigned long halt_channel;
+ unsigned long flow_ctrl_channel;
};
struct gsi_coal_chan_info {
@@ -236,6 +237,8 @@ struct gsi_ctx {
u32 intcntrlr_mem_size;
irq_handler_t intcntrlr_gsi_isr;
irq_handler_t intcntrlr_client_isr;
+
+ atomic_t num_unclock_irq;
};
enum gsi_re_type {
@@ -332,6 +335,8 @@ enum gsi_evt_ch_cmd_opcode {
enum gsi_generic_ee_cmd_opcode {
GSI_GEN_EE_CMD_HALT_CHANNEL = 0x1,
GSI_GEN_EE_CMD_ALLOC_CHANNEL = 0x2,
+ GSI_GEN_EE_CMD_ENABLE_FLOW_CHANNEL = 0x3,
+ GSI_GEN_EE_CMD_DISABLE_FLOW_CHANNEL = 0x4,
};
enum gsi_generic_ee_cmd_return_code {
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 7cbf21c..d9a860b 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/ipa.h>
@@ -3738,7 +3738,7 @@ int ipa_get_prot_id(enum ipa_client_type client)
EXPORT_SYMBOL(ipa_get_prot_id);
static const struct dev_pm_ops ipa_pm_ops = {
- .suspend_noirq = ipa_ap_suspend,
+ .suspend = ipa_ap_suspend,
.resume_noirq = ipa_ap_resume,
};
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
index 0c8bb24..16de3d9 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/ipa_wdi3.h>
@@ -39,6 +39,9 @@
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
+#define IPA_TX_MAX_INTF_PROP 2
+#define IPA_RX_MAX_INTF_PROP 2
+
struct ipa_wdi_intf_info {
char netdev_name[IPA_RESOURCE_NAME_MAX];
u8 hdr_len;
@@ -183,8 +186,8 @@ int ipa_wdi_reg_intf(struct ipa_wdi_reg_intf_in_params *in)
struct ipa_wdi_intf_info *entry;
struct ipa_tx_intf tx;
struct ipa_rx_intf rx;
- struct ipa_ioc_tx_intf_prop tx_prop[2];
- struct ipa_ioc_rx_intf_prop rx_prop[2];
+ struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
+ struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
u32 len;
int ret = 0;
@@ -243,10 +246,17 @@ int ipa_wdi_reg_intf(struct ipa_wdi_reg_intf_in_params *in)
hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl);
/* populate tx prop */
+ tx_prop = kmalloc(
+ sizeof(*tx_prop) * IPA_TX_MAX_INTF_PROP, GFP_KERNEL);
+ if (!tx_prop) {
+ IPAERR("failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto fail_commit_hdr;
+ }
tx.num_props = 2;
+ memset(tx_prop, 0, sizeof(*tx_prop));
tx.prop = tx_prop;
- memset(tx_prop, 0, sizeof(tx_prop));
tx_prop[0].ip = IPA_IP_v4;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS;
@@ -268,9 +278,16 @@ int ipa_wdi_reg_intf(struct ipa_wdi_reg_intf_in_params *in)
sizeof(tx_prop[1].hdr_name));
/* populate rx prop */
+ rx_prop = kmalloc(
+ sizeof(*rx_prop) * IPA_RX_MAX_INTF_PROP, GFP_KERNEL);
+ if (!rx_prop) {
+ IPAERR("failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto fail_commit_hdr;
+ }
rx.num_props = 2;
+ memset(rx_prop, 0, sizeof(*rx_prop));
rx.prop = rx_prop;
- memset(rx_prop, 0, sizeof(rx_prop));
rx_prop[0].ip = IPA_IP_v4;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD;
@@ -305,11 +322,16 @@ int ipa_wdi_reg_intf(struct ipa_wdi_reg_intf_in_params *in)
init_completion(&ipa_wdi_ctx->wdi_completion);
kfree(hdr);
+ kfree(tx_prop);
+ kfree(rx_prop);
+
mutex_unlock(&ipa_wdi_ctx->lock);
return 0;
fail_commit_hdr:
kfree(hdr);
+ kfree(tx_prop);
+ kfree(rx_prop);
fail_alloc_hdr:
kfree(new_intf);
mutex_unlock(&ipa_wdi_ctx->lock);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 11a82ef..91a582a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -124,6 +124,10 @@ static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work);
static DECLARE_DELAYED_WORK(ipa_dec_clients_disable_clks_on_wq_work,
ipa_dec_clients_disable_clks_on_wq);
+static void ipa_inc_clients_enable_clks_on_wq(struct work_struct *work);
+static DECLARE_WORK(ipa_inc_clients_enable_clks_on_wq_work,
+ ipa_inc_clients_enable_clks_on_wq);
+
static int ipa3_ioctl_add_rt_rule_v2(unsigned long arg);
static int ipa3_ioctl_add_rt_rule_ext_v2(unsigned long arg);
static int ipa3_ioctl_add_rt_rule_after_v2(unsigned long arg);
@@ -4827,6 +4831,8 @@ void _ipa_disable_clks_v3_0(void)
*/
void ipa3_disable_clks(void)
{
+ int type;
+
if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_NORMAL) {
IPAERR("not supported in this mode\n");
return;
@@ -4834,13 +4840,29 @@ void ipa3_disable_clks(void)
IPADBG("disabling IPA clocks and bus voting\n");
+ /*
+ * We see a NoC error on GSI on this flag sequence.
+ * Need to set this flag first before clock off.
+ */
+ atomic_set(&ipa3_ctx->ipa_clk_vote, 0);
+
+ /*
+ * If there is still pending gsi irq, this indicate
+ * issue on GSI FW side. We need to capture before
+ * turn off the ipa clock.
+ */
+ type = gsi_pending_irq_type();
+ if (type) {
+ IPAERR("unexpected gsi irq type: %d\n", type);
+ ipa_assert();
+ }
+
ipa3_ctx->ctrl->ipa3_disable_clks();
ipa_pm_set_clock_index(0);
if (msm_bus_scale_client_update_request(ipa3_ctx->ipa_bus_hdl, 0))
WARN(1, "bus scaling failed");
- atomic_set(&ipa3_ctx->ipa_clk_vote, 0);
}
/**
@@ -5013,6 +5035,12 @@ void ipa3_inc_client_enable_clks(struct ipa_active_client_logging_info *id)
mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
}
+void ipa3_handle_gsi_differ_irq(void)
+{
+ queue_work(ipa3_ctx->power_mgmt_wq,
+ &ipa_inc_clients_enable_clks_on_wq_work);
+}
+
/**
* ipa3_active_clks_status() - update the current msm bus clock vote
* status
@@ -5129,6 +5157,13 @@ static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work)
__ipa3_dec_client_disable_clks();
}
+static void ipa_inc_clients_enable_clks_on_wq(struct work_struct *work)
+{
+ ipa3_enable_clks();
+ IPAERR("unexpected clk access, clock on IPA to save reg");
+ ipa_assert();
+}
+
/**
* ipa3_dec_client_disable_clks_no_block() - Decrease active clients counter
* if possible without blocking. If this is the last client then the desrease
@@ -5924,6 +5959,7 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
gsi_props.req_clk_cb = NULL;
gsi_props.rel_clk_cb = NULL;
gsi_props.clk_status_cb = ipa3_active_clks_status;
+ gsi_props.enable_clk_bug_on = ipa3_handle_gsi_differ_irq;
if (ipa3_ctx->ipa_config_is_mhi) {
gsi_props.mhi_er_id_limits_valid = true;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index f773bfb..a1e7e4a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <asm/barrier.h>
@@ -583,6 +583,15 @@ int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params,
IPADBG("ep configuration successful\n");
} else {
IPADBG("Skipping endpoint configuration.\n");
+ if (IPA_CLIENT_IS_PROD(ipa3_ctx->ep[ipa_ep_idx].client) &&
+ ipa3_ctx->ep[ipa_ep_idx].client == IPA_CLIENT_USB_PROD
+ && !ipa3_is_mhip_offload_enabled()) {
+ if (ipa3_cfg_ep_seq(ipa_ep_idx,
+ ¶ms->ipa_ep_cfg.seq)) {
+ IPAERR("fail to configure USB pipe seq\n");
+ goto ipa_cfg_ep_fail;
+ }
+ }
}
out_params->clnt_hdl = ipa_ep_idx;
@@ -772,6 +781,7 @@ int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
int result = -EFAULT;
enum gsi_status gsi_res;
struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
+ int code = 0;
IPADBG("entry\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
@@ -814,6 +824,20 @@ int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
IPAERR("Error starting channel: %d\n", gsi_res);
goto write_chan_scratch_fail;
}
+
+ if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg &&
+ ipa3_ctx->ipa_endp_delay_wa &&
+ !ipa3_is_mhip_offload_enabled()) {
+ gsi_res = gsi_enable_flow_control_ee(ep->gsi_chan_hdl, 0,
+ &code);
+ if (gsi_res == GSI_STATUS_SUCCESS) {
+ IPADBG("flow control sussess gsi ch %d with code %d\n",
+ ep->gsi_chan_hdl, code);
+ } else {
+ IPADBG("failed to flow control gsi ch %d code %d\n",
+ ep->gsi_chan_hdl, code);
+ }
+ }
ipa3_start_gsi_debug_monitor(clnt_hdl);
if (!ep->keep_ipa_awake)
IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
@@ -1225,6 +1249,7 @@ int ipa3_start_stop_client_prod_gsi_chnl(enum ipa_client_type client,
int result = 0;
int pipe_idx;
struct ipa3_ep_context *ep;
+ int code = 0;
if (IPA_CLIENT_IS_CONS(client)) {
IPAERR("client (%d) not PROD\n", client);
@@ -1240,10 +1265,20 @@ int ipa3_start_stop_client_prod_gsi_chnl(enum ipa_client_type client,
client_lock_unlock_cb(client, true);
ep = &ipa3_ctx->ep[pipe_idx];
- if (ep->valid && ep->skip_ep_cfg && ipa3_get_teth_port_status(client)) {
- if (start_chnl)
+ if (ep->valid && ep->skip_ep_cfg && ipa3_get_teth_port_status(client)
+ && !ipa3_is_mhip_offload_enabled()) {
+ if (start_chnl) {
result = ipa3_start_gsi_channel(pipe_idx);
- else
+ result = gsi_enable_flow_control_ee(ep->gsi_chan_hdl,
+ 0, &code);
+ if (result == GSI_STATUS_SUCCESS) {
+ IPADBG("flow control sussess ch %d code %d\n",
+ ep->gsi_chan_hdl, code);
+ } else {
+ IPADBG("failed to flow control ch %d code %d\n",
+ ep->gsi_chan_hdl, code);
+ }
+ } else
result = ipa3_stop_gsi_channel(pipe_idx);
}
client_lock_unlock_cb(client, false);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 5c7ae8d..ccf0d26 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -721,7 +721,8 @@ static ssize_t ipa3_read_rt(struct file *file, char __user *ubuf, size_t count,
list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
i = 0;
list_for_each_entry(entry, &tbl->head_rt_rule_list, link) {
- if (entry->proc_ctx) {
+ if (entry->proc_ctx &&
+ (!ipa3_check_idr_if_freed(entry->proc_ctx))) {
ofst = entry->proc_ctx->offset_entry->offset;
ofst_words =
(ofst +
@@ -1145,6 +1146,7 @@ static ssize_t ipa3_read_stats(struct file *file, char __user *ubuf,
"lan_repl_rx_empty=%u\n"
"flow_enable=%u\n"
"flow_disable=%u\n",
+ "rx_page_drop_cnt=%u\n",
ipa3_ctx->stats.tx_sw_pkts,
ipa3_ctx->stats.tx_hw_pkts,
ipa3_ctx->stats.tx_non_linear,
@@ -1160,7 +1162,8 @@ static ssize_t ipa3_read_stats(struct file *file, char __user *ubuf,
ipa3_ctx->stats.lan_rx_empty,
ipa3_ctx->stats.lan_repl_rx_empty,
ipa3_ctx->stats.flow_enable,
- ipa3_ctx->stats.flow_disable);
+ ipa3_ctx->stats.flow_disable,
+ ipa3_ctx->stats.rx_page_drop_cnt);
cnt += nbytes;
for (i = 0; i < IPAHAL_PKT_STATUS_EXCEPTION_MAX; i++) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 82a0e54..e897ae2 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
@@ -1329,11 +1329,9 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl)
return result;
}
- if (ep->sys->napi_obj) {
- do {
- usleep_range(95, 105);
- } while (atomic_read(&ep->sys->curr_polling_state));
- }
+ do {
+ usleep_range(95, 105);
+ } while (atomic_read(&ep->sys->curr_polling_state));
if (IPA_CLIENT_IS_CONS(ep->client))
cancel_delayed_work_sync(&ep->sys->replenish_rx_work);
@@ -1783,6 +1781,7 @@ static void ipa3_wq_handle_rx(struct work_struct *work)
if (sys->napi_obj) {
ipa_pm_activate_sync(sys->pm_hdl);
napi_schedule(sys->napi_obj);
+ IPA_STATS_INC_CNT(sys->napi_sch_cnt);
} else
ipa3_handle_rx(sys);
}
@@ -3389,10 +3388,17 @@ static struct sk_buff *handle_page_completion(struct gsi_chan_xfer_notify
IPAERR("update_truesize not supported\n");
if (notify->veid >= GSI_VEID_MAX) {
- rx_pkt->sys->free_rx_wrapper(rx_pkt);
- if (!rx_page.is_tmp_alloc)
- init_page_count(rx_page.page);
IPAERR("notify->veid > GSI_VEID_MAX\n");
+ if (!rx_page.is_tmp_alloc) {
+ init_page_count(rx_page.page);
+ } else {
+ dma_unmap_page(ipa3_ctx->pdev, rx_page.dma_addr,
+ rx_pkt->len, DMA_FROM_DEVICE);
+ __free_pages(rx_pkt->page_data.page,
+ IPA_WAN_PAGE_ORDER);
+ }
+ rx_pkt->sys->free_rx_wrapper(rx_pkt);
+ IPA_STATS_INC_CNT(ipa3_ctx->stats.rx_page_drop_cnt);
return NULL;
}
@@ -3406,10 +3412,18 @@ static struct sk_buff *handle_page_completion(struct gsi_chan_xfer_notify
sys->ep->client == IPA_CLIENT_APPS_LAN_CONS) {
rx_skb = alloc_skb(0, GFP_ATOMIC);
if (unlikely(!rx_skb)) {
- rx_pkt->sys->free_rx_wrapper(rx_pkt);
- if (!rx_page.is_tmp_alloc)
- init_page_count(rx_page.page);
IPAERR("skb alloc failure\n");
+ list_del(&rx_pkt->link);
+ if (!rx_page.is_tmp_alloc) {
+ init_page_count(rx_page.page);
+ } else {
+ dma_unmap_page(ipa3_ctx->pdev, rx_page.dma_addr,
+ rx_pkt->len, DMA_FROM_DEVICE);
+ __free_pages(rx_pkt->page_data.page,
+ IPA_WAN_PAGE_ORDER);
+ }
+ rx_pkt->sys->free_rx_wrapper(rx_pkt);
+ IPA_STATS_INC_CNT(ipa3_ctx->stats.rx_page_drop_cnt);
return NULL;
}
/* go over the list backward to save computations on updating length */
@@ -4351,6 +4365,7 @@ void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys)
clk_off = ipa_pm_activate(sys->pm_hdl);
if (!clk_off && sys->napi_obj) {
napi_schedule(sys->napi_obj);
+ IPA_STATS_INC_CNT(sys->napi_sch_cnt);
return;
}
queue_work(sys->wq, &sys->work);
@@ -4993,6 +5008,7 @@ int ipa3_rx_poll(u32 clnt_hdl, int weight)
*/
if (cnt < weight && ep->sys->len > IPA_DEFAULT_SYS_YELLOW_WM) {
napi_complete(ep->sys->napi_obj);
+ IPA_STATS_INC_CNT(ep->sys->napi_comp_cnt);
ret = ipa3_rx_switch_to_intr_mode(ep->sys);
if (ret == -GSI_STATUS_PENDING_IRQ &&
napi_reschedule(ep->sys->napi_obj))
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 3865503..595d7c5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1032,6 +1032,8 @@ struct ipa3_sys_context {
struct workqueue_struct *repl_wq;
struct ipa3_status_stats *status_stat;
u32 pm_hdl;
+ unsigned int napi_sch_cnt;
+ unsigned int napi_comp_cnt;
/* ordering is important - other immutable fields go below */
};
@@ -1358,6 +1360,7 @@ struct ipa3_stats {
u32 flow_enable;
u32 flow_disable;
u32 tx_non_linear;
+ u32 rx_page_drop_cnt;
struct ipa3_page_recycle_stats page_recycle_stats[2];
};
@@ -2357,6 +2360,8 @@ int ipa3_clear_endpoint_delay(u32 clnt_hdl);
*/
int ipa3_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
+int ipa3_cfg_ep_seq(u32 clnt_hdl, const struct ipa_ep_cfg_seq *seq_cfg);
+
int ipa3_cfg_ep_nat(u32 clnt_hdl, const struct ipa_ep_cfg_nat *ipa_ep_cfg);
int ipa3_cfg_ep_conn_track(u32 clnt_hdl,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index a9816c6..1c47b93 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -182,7 +182,8 @@ static int ipa3_mhi_get_ch_poll_cfg(enum ipa_client_type client,
}
static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
- int ipa_ep_idx, struct start_gsi_channel *params)
+ int ipa_ep_idx, struct start_gsi_channel *params,
+ struct ipa_ep_cfg *ipa_ep_cfg)
{
int res = 0;
struct gsi_evt_ring_props ev_props;
@@ -193,6 +194,7 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
const struct ipa_gsi_ep_config *ep_cfg;
struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
bool burst_mode_enabled = false;
+ int code = 0;
IPA_MHI_FUNC_ENTRY();
@@ -342,6 +344,37 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
*params->mhi = ch_scratch.mhi;
+ res = ipa3_enable_data_path(ipa_ep_idx);
+ if (res) {
+ IPA_MHI_ERR("enable data path failed res=%d clnt=%d.\n", res,
+ ipa_ep_idx);
+ goto fail_ep_cfg;
+ }
+
+ if (!ep->skip_ep_cfg) {
+ if (ipa3_cfg_ep(ipa_ep_idx, ipa_ep_cfg)) {
+ IPAERR("fail to configure EP.\n");
+ goto fail_ep_cfg;
+ }
+ if (ipa3_cfg_ep_status(ipa_ep_idx, &ep->status)) {
+ IPAERR("fail to configure status of EP.\n");
+ goto fail_ep_cfg;
+ }
+ IPA_MHI_DBG("ep configuration successful\n");
+ } else {
+ IPA_MHI_DBG("skipping ep configuration\n");
+ if (IPA_CLIENT_IS_PROD(ipa3_ctx->ep[ipa_ep_idx].client) &&
+ ipa3_ctx->ep[ipa_ep_idx].client == IPA_CLIENT_MHI_PROD
+ && !ipa3_is_mhip_offload_enabled()) {
+ if (ipa3_cfg_ep_seq(ipa_ep_idx,
+ &ipa_ep_cfg->seq)) {
+ IPA_MHI_ERR("fail to configure USB pipe seq\n");
+ goto fail_ep_cfg;
+ }
+ }
+
+ }
+
if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg) {
memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
ep_cfg_ctrl.ipa_ep_delay = true;
@@ -356,6 +389,9 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
ep->ep_delay_set = false;
}
+ if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(client))
+ ipa3_install_dflt_flt_rules(ipa_ep_idx);
+
IPA_MHI_DBG("Starting channel\n");
res = gsi_start_channel(ep->gsi_chan_hdl);
if (res) {
@@ -363,9 +399,24 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
goto fail_ch_start;
}
+ if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg &&
+ ipa3_ctx->ipa_endp_delay_wa &&
+ !ipa3_is_mhip_offload_enabled()) {
+ res = gsi_enable_flow_control_ee(ep->gsi_chan_hdl, 0, &code);
+ if (res == GSI_STATUS_SUCCESS) {
+ IPA_MHI_DBG("flow ctrl sussess gsi ch %d code %d\n",
+ ep->gsi_chan_hdl, code);
+ } else {
+ IPA_MHI_DBG("failed to flow ctrll gsi ch %d code %d\n",
+ ep->gsi_chan_hdl, code);
+ }
+ }
+
IPA_MHI_FUNC_EXIT();
return 0;
+fail_ep_cfg:
+ ipa3_disable_data_path(ipa_ep_idx);
fail_ch_start:
fail_ch_scratch:
gsi_dealloc_channel(ep->gsi_chan_hdl);
@@ -473,49 +524,22 @@ int ipa3_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
ep->keep_ipa_awake = in->sys->keep_ipa_awake;
res = ipa_mhi_start_gsi_channel(client,
- ipa_ep_idx, &in->start.gsi);
+ ipa_ep_idx, &in->start.gsi,
+ &in->sys->ipa_ep_cfg);
if (res) {
IPA_MHI_ERR("ipa_mhi_start_gsi_channel failed %d\n",
res);
goto fail_start_channel;
}
- res = ipa3_enable_data_path(ipa_ep_idx);
- if (res) {
- IPA_MHI_ERR("enable data path failed res=%d clnt=%d.\n", res,
- ipa_ep_idx);
- goto fail_ep_cfg;
- }
-
- if (!ep->skip_ep_cfg) {
- if (ipa3_cfg_ep(ipa_ep_idx, &in->sys->ipa_ep_cfg)) {
- IPAERR("fail to configure EP.\n");
- goto fail_ep_cfg;
- }
- if (ipa3_cfg_ep_status(ipa_ep_idx, &ep->status)) {
- IPAERR("fail to configure status of EP.\n");
- goto fail_ep_cfg;
- }
- IPA_MHI_DBG("ep configuration successful\n");
- } else {
- IPA_MHI_DBG("skipping ep configuration\n");
- }
-
*clnt_hdl = ipa_ep_idx;
-
- if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(client))
- ipa3_install_dflt_flt_rules(ipa_ep_idx);
-
ipa3_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
- IPA_MHI_DBG("client %d (ep: %d) connected\n", client,
- ipa_ep_idx);
+ IPA_MHI_DBG("client %d (ep: %d) connected\n", client, ipa_ep_idx);
IPA_MHI_FUNC_EXIT();
return 0;
-fail_ep_cfg:
- ipa3_disable_data_path(ipa_ep_idx);
fail_start_channel:
memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
return -EPERM;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c
index 1ec5c85..d6c668a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c
@@ -1080,9 +1080,6 @@ static int ipa_mpm_connect_mhip_gsi_pipe(enum ipa_client_type mhip_client,
gsi_params.evt_scratch.mhip.rp_mod_timer_running = 0;
gsi_params.evt_scratch.mhip.fixed_buffer_sz = TRE_BUFF_SIZE;
- if (IPA_CLIENT_IS_PROD(mhip_client))
- gsi_params.evt_scratch.mhip.rp_mod_threshold = 4;
-
/* Channel Params */
gsi_params.chan_params.prot = GSI_CHAN_PROT_MHIP;
gsi_params.chan_params.dir = IPA_CLIENT_IS_PROD(mhip_client) ?
@@ -2193,7 +2190,7 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev,
ch->chan_props.ch_ctx.rlen = (IPA_MPM_RING_LEN) *
GSI_EVT_RING_RE_SIZE_16B;
/* Store Event properties */
- ch->evt_props.ev_ctx.update_rp_modc = 0;
+ ch->evt_props.ev_ctx.update_rp_modc = 1;
ch->evt_props.ev_ctx.update_rp_intmodt = 0;
ch->evt_props.ev_ctx.ertype = 1;
ch->evt_props.ev_ctx.rlen = (IPA_MPM_RING_LEN) *
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_odl.c b/drivers/platform/msm/ipa/ipa_v3/ipa_odl.c
index 69d35a3..84d760b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_odl.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_odl.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include "ipa_i.h"
@@ -418,6 +418,7 @@ static int ipa_adpl_open(struct inode *inode, struct file *filp)
int ret = 0;
IPADBG("Called the function :\n");
+ mutex_lock(&ipa3_odl_ctx->pipe_lock);
if (ipa3_odl_ctx->odl_state.odl_init &&
!ipa3_odl_ctx->odl_state.adpl_open) {
/* Activate ipa_pm*/
@@ -431,6 +432,7 @@ static int ipa_adpl_open(struct inode *inode, struct file *filp)
print_ipa_odl_state_bit_mask();
ret = -ENODEV;
}
+ mutex_unlock(&ipa3_odl_ctx->pipe_lock);
return ret;
}
@@ -439,6 +441,7 @@ static int ipa_adpl_release(struct inode *inode, struct file *filp)
{
int ret = 0;
/* Deactivate ipa_pm */
+ mutex_lock(&ipa3_odl_ctx->pipe_lock);
ret = ipa_pm_deactivate_sync(ipa3_odl_ctx->odl_pm_hdl);
if (ret)
IPAERR("failed to activate pm\n");
@@ -451,6 +454,7 @@ static int ipa_adpl_release(struct inode *inode, struct file *filp)
IPAERR("mpm failed to disable ADPL over ODL\n");
}
+ mutex_unlock(&ipa3_odl_ctx->pipe_lock);
return ret;
}
@@ -667,6 +671,7 @@ int ipa_odl_init(void)
odl_cdev = ipa3_odl_ctx->odl_cdev;
INIT_LIST_HEAD(&ipa3_odl_ctx->adpl_msg_list);
mutex_init(&ipa3_odl_ctx->adpl_msg_lock);
+ mutex_init(&ipa3_odl_ctx->pipe_lock);
odl_cdev[loop].class = class_create(THIS_MODULE, "ipa_adpl");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_odl.h b/drivers/platform/msm/ipa/ipa_v3/ipa_odl.h
index 5049001..72c07b0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_odl.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_odl.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _IPA3_ODL_H_
@@ -51,6 +51,7 @@ struct ipa_odl_context {
struct ipa3_odl_char_device_context odl_cdev[2];
struct list_head adpl_msg_list;
struct mutex adpl_msg_lock;
+ struct mutex pipe_lock;
struct ipa_sys_connect_params odl_sys_param;
u32 odl_client_hdl;
struct odl_state_bit_mask odl_state;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 7c6f123..5ee5ebd 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/bitops.h>
@@ -1741,7 +1741,8 @@ int __ipa3_del_rt_rule(u32 rule_hdl)
return -EINVAL;
}
- if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
+ if (!ipa3_check_idr_if_freed(entry) &&
+ !strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
IPADBG("Deleting rule from default rt table idx=%u\n",
entry->tbl->idx);
if (entry->tbl->rule_cnt == 1) {
@@ -1971,7 +1972,8 @@ int ipa3_reset_rt(enum ipa_ip_type ip, bool user_only)
}
}
tbl->rule_cnt--;
- if (rule->hdr)
+ if (rule->hdr &&
+ (!ipa3_check_idr_if_freed(rule->hdr)))
__ipa3_release_hdr(rule->hdr->id);
else if (rule->proc_ctx &&
(!ipa3_check_idr_if_freed(
@@ -2174,7 +2176,8 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy_i *rtrule)
goto error;
}
- if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
+ if (!ipa3_check_idr_if_freed(entry) &&
+ !strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
IPAERR_RL("Default tbl rule cannot be modified\n");
return -EINVAL;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index dbe30cf..1a7e477 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -65,7 +65,7 @@
#define IPA_FILT_ROUT_HASH_REG_VAL_v4_2 (0x00000000)
#define IPA_DMA_TASK_FOR_GSI_TIMEOUT_MSEC (15)
-#define IPA_COAL_CLOSE_FRAME_CMD_TIMEOUT_MSEC (500)
+#define IPA_COAL_CLOSE_FRAME_CMD_TIMEOUT_MSEC (20)
#define IPA_AGGR_BYTE_LIMIT (\
IPA_ENDP_INIT_AGGR_N_AGGR_BYTE_LIMIT_BMSK >> \
@@ -2068,6 +2068,12 @@ static const struct ipa_ep_configuration ipa3_ep_mapping
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 12, 4, 4, 4, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY} },
+ [IPA_4_2][IPA_CLIENT_ODL_DPL_CONS] = {
+ true, IPA_v4_2_GROUP_UL_DL,
+ false,
+ IPA_DPS_HPS_SEQ_TYPE_INVALID,
+ QMB_MASTER_SELECT_DDR,
+ { 13, 10, 6, 6, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY} },
[IPA_4_2][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v4_2_GROUP_UL_DL,
false,
@@ -2092,12 +2098,6 @@ static const struct ipa_ep_configuration ipa3_ep_mapping
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 10, 2, 6, 6, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY} },
- [IPA_4_2][IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS] = {
- true, IPA_v4_2_GROUP_UL_DL,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 13, 4, 6, 6, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY} },
[IPA_4_2][IPA_CLIENT_ETHERNET_CONS] = {
true, IPA_v4_2_GROUP_UL_DL,
false,
@@ -6205,7 +6205,7 @@ void ipa3_counter_remove_hdl(int hdl)
}
/* remove counters belong to this hdl, set used back to 0 */
offset = counter->hw_counter.start_id - 1;
- if (offset >= 0 && offset + counter->hw_counter.num_counters
+ if (offset >= 0 && (offset + counter->hw_counter.num_counters)
< IPA_FLT_RT_HW_COUNTER) {
memset(&ipa3_ctx->flt_rt_counters.used_hw + offset,
0, counter->hw_counter.num_counters * sizeof(bool));
@@ -6214,7 +6214,7 @@ void ipa3_counter_remove_hdl(int hdl)
goto err;
}
offset = counter->sw_counter.start_id - 1 - IPA_FLT_RT_HW_COUNTER;
- if (offset >= 0 && offset + counter->sw_counter.num_counters
+ if (offset >= 0 && (offset + counter->sw_counter.num_counters)
< IPA_FLT_RT_SW_COUNTER) {
memset(&ipa3_ctx->flt_rt_counters.used_sw + offset,
0, counter->sw_counter.num_counters * sizeof(bool));
@@ -7744,6 +7744,16 @@ static int _ipa_suspend_resume_pipe(enum ipa_client_type client, bool suspend)
ipa_assert();
}
} else {
+ if (IPA_CLIENT_IS_APPS_PROD(client) ||
+ (client == IPA_CLIENT_APPS_WAN_CONS &&
+ coal_ep_idx != IPA_EP_NOT_ALLOCATED))
+ goto chan_statrt;
+ if (!atomic_read(&ep->sys->curr_polling_state)) {
+ IPADBG("switch ch %ld to callback\n", ep->gsi_chan_hdl);
+ gsi_config_channel_mode(ep->gsi_chan_hdl,
+ GSI_CHAN_MODE_CALLBACK);
+ }
+chan_statrt:
res = gsi_start_channel(ep->gsi_chan_hdl);
if (res) {
IPAERR("failed to start LAN channel\n");
@@ -7770,12 +7780,7 @@ static int _ipa_suspend_resume_pipe(enum ipa_client_type client, bool suspend)
gsi_config_channel_mode(ep->gsi_chan_hdl, GSI_CHAN_MODE_POLL);
if (!ipa3_gsi_channel_is_quite(ep))
return -EAGAIN;
- } else if (!atomic_read(&ep->sys->curr_polling_state)) {
- IPADBG("switch ch %ld to callback\n", ep->gsi_chan_hdl);
- gsi_config_channel_mode(ep->gsi_chan_hdl,
- GSI_CHAN_MODE_CALLBACK);
}
-
return 0;
}
@@ -7791,7 +7796,8 @@ void ipa3_force_close_coal(void)
ipa3_init_imm_cmd_desc(&desc, ipa3_ctx->coal_cmd_pyld);
IPADBG("Sending 1 descriptor for coal force close\n");
- if (ipa3_send_cmd(1, &desc))
+ if (ipa3_send_cmd_timeout(1, &desc,
+ IPA_COAL_CLOSE_FRAME_CMD_TIMEOUT_MSEC))
IPADBG("ipa3_send_cmd timedout\n");
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 6c58fb2..06890ad 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
*/
/*
@@ -2699,7 +2699,7 @@ static const struct of_device_id rmnet_ipa_dt_match[] = {
MODULE_DEVICE_TABLE(of, rmnet_ipa_dt_match);
static const struct dev_pm_ops rmnet_ipa_pm_ops = {
- .suspend_noirq = rmnet_ipa_ap_suspend,
+ .suspend = rmnet_ipa_ap_suspend,
.resume_noirq = rmnet_ipa_ap_resume,
};
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index 1a31ef1..4537ad2 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -128,6 +128,7 @@ static int msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp,
if (node->data == data) {
list_del(pos);
pr_debug("Deleted the intf data\n");
+ kfree(node);
return 0;
}
}
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 3f3f233..bb1df7e 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -1261,6 +1261,35 @@ int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
EXPORT_SYMBOL(geni_se_tx_dma_prep);
/**
+ * geni_se_rx_dma_start() - Prepare the Serial Engine registers for RX DMA
+ transfers.
+ * @base: Base address of the SE register block.
+ * @rx_len: Length of the RX buffer.
+ * @rx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the Serial Engine registers for DMA RX.
+ *
+ * Return: None.
+ */
+void geni_se_rx_dma_start(void __iomem *base, int rx_len, dma_addr_t *rx_dma)
+{
+
+ if (!*rx_dma || !base || !rx_len)
+ return;
+
+ geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
+ geni_write_reg(GENI_SE_DMA_PTR_L(*rx_dma), base, SE_DMA_RX_PTR_L);
+ geni_write_reg(GENI_SE_DMA_PTR_H(*rx_dma), base, SE_DMA_RX_PTR_H);
+ /* RX does not have EOT bit */
+ geni_write_reg(0, base, SE_DMA_RX_ATTR);
+
+ /* Ensure that above register writes went through */
+ mb();
+ geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
+}
+EXPORT_SYMBOL(geni_se_rx_dma_start);
+
+/**
* geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
* @wrapper_dev: QUPv3 Wrapper Device to which the RX buffer is mapped.
* @base: Base address of the SE register block.
@@ -1285,12 +1314,8 @@ int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
if (ret)
return ret;
- geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
- geni_write_reg(GENI_SE_DMA_PTR_L(*rx_dma), base, SE_DMA_RX_PTR_L);
- geni_write_reg(GENI_SE_DMA_PTR_H(*rx_dma), base, SE_DMA_RX_PTR_H);
- /* RX does not have EOT bit */
- geni_write_reg(0, base, SE_DMA_RX_ATTR);
- geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
+ geni_se_rx_dma_start(base, rx_len, rx_dma);
+
return 0;
}
EXPORT_SYMBOL(geni_se_rx_dma_prep);
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index b253405..2d2980f 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -53,12 +53,15 @@ static const char *const pmic_names[] = {
[PM660L_SUBTYPE] = "PM660L",
[PM660_SUBTYPE] = "PM660",
[PMI632_SUBTYPE] = "PMI632",
- [PMI8937_SUBTYPE] = "PMI8937",
+ [PM2250_SUBTYPE] = "PM2250",
[PM8150_SUBTYPE] = "PM8150",
[PM8150B_SUBTYPE] = "PM8150B",
[PM8150L_SUBTYPE] = "PM8150L",
[PM6150_SUBTYPE] = "PM6150",
[PM7250B_SUBTYPE] = "PM7250B",
+ [PM6350_SUBTYPE] = "PM6350",
+ [PMK8350_SUBTYPE] = "PMK8350",
+ [PMR735B_SUBTYPE] = "PMR735B",
[PM6125_SUBTYPE] = "PM6125",
[PM8008_SUBTYPE] = "PM8008",
[SMB1355_SUBTYPE] = "SMB1355",
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index b422438..06a3c1e 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -78,7 +78,7 @@ struct bios_args {
u32 command;
u32 commandtype;
u32 datasize;
- u32 data;
+ u8 data[128];
};
enum hp_wmi_commandtype {
@@ -229,7 +229,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
.command = command,
.commandtype = query,
.datasize = insize,
- .data = 0,
+ .data = { 0 },
};
struct acpi_buffer input = { sizeof(struct bios_args), &args };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -241,7 +241,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
if (WARN_ON(insize > sizeof(args.data)))
return -EINVAL;
- memcpy(&args.data, buffer, insize);
+ memcpy(&args.data[0], buffer, insize);
wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
@@ -313,7 +313,7 @@ static int __init hp_wmi_bios_2008_later(void)
static int __init hp_wmi_bios_2009_later(void)
{
- int state = 0;
+ u8 state[128];
int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state,
sizeof(state), sizeof(state));
if (!ret)
@@ -393,7 +393,7 @@ static int hp_wmi_rfkill2_refresh(void)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
- 0, sizeof(state));
+ sizeof(state), sizeof(state));
if (err)
return err;
@@ -790,7 +790,7 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
- 0, sizeof(state));
+ sizeof(state), sizeof(state));
if (err)
return err < 0 ? err : -EINVAL;
diff --git a/drivers/platform/x86/intel_atomisp2_pm.c b/drivers/platform/x86/intel_atomisp2_pm.c
index 9371603..b0f421fe 100644
--- a/drivers/platform/x86/intel_atomisp2_pm.c
+++ b/drivers/platform/x86/intel_atomisp2_pm.c
@@ -33,46 +33,45 @@
#define ISPSSPM0_IUNIT_POWER_ON 0x0
#define ISPSSPM0_IUNIT_POWER_OFF 0x3
-static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int isp_set_power(struct pci_dev *dev, bool enable)
{
unsigned long timeout;
- u32 val;
+ u32 val = enable ? ISPSSPM0_IUNIT_POWER_ON :
+ ISPSSPM0_IUNIT_POWER_OFF;
- pci_write_config_dword(dev, PCI_INTERRUPT_CTRL, 0);
-
- /*
- * MRFLD IUNIT DPHY is located in an always-power-on island
- * MRFLD HW design need all CSI ports are disabled before
- * powering down the IUNIT.
- */
- pci_read_config_dword(dev, PCI_CSI_CONTROL, &val);
- val |= PCI_CSI_CONTROL_PORTS_OFF_MASK;
- pci_write_config_dword(dev, PCI_CSI_CONTROL, val);
-
- /* Write 0x3 to ISPSSPM0 bit[1:0] to power off the IUNIT */
+ /* Write to ISPSSPM0 bit[1:0] to power on/off the IUNIT */
iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0,
- ISPSSPM0_IUNIT_POWER_OFF, ISPSSPM0_ISPSSC_MASK);
+ val, ISPSSPM0_ISPSSC_MASK);
/*
* There should be no IUNIT access while power-down is
* in progress HW sighting: 4567865
* Wait up to 50 ms for the IUNIT to shut down.
+ * And we do the same for power on.
*/
timeout = jiffies + msecs_to_jiffies(50);
while (1) {
- /* Wait until ISPSSPM0 bit[25:24] shows 0x3 */
- iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &val);
- val = (val & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET;
- if (val == ISPSSPM0_IUNIT_POWER_OFF)
+ u32 tmp;
+
+ /* Wait until ISPSSPM0 bit[25:24] shows the right value */
+ iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &tmp);
+ tmp = (tmp & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET;
+ if (tmp == val)
break;
if (time_after(jiffies, timeout)) {
- dev_err(&dev->dev, "IUNIT power-off timeout.\n");
+ dev_err(&dev->dev, "IUNIT power-%s timeout.\n",
+ enable ? "on" : "off");
return -EBUSY;
}
usleep_range(1000, 2000);
}
+ return 0;
+}
+
+static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
pm_runtime_allow(&dev->dev);
pm_runtime_put_sync_suspend(&dev->dev);
@@ -87,11 +86,40 @@ static void isp_remove(struct pci_dev *dev)
static int isp_pci_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u32 val;
+
+ pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, 0);
+
+ /*
+ * MRFLD IUNIT DPHY is located in an always-power-on island
+ * MRFLD HW design need all CSI ports are disabled before
+ * powering down the IUNIT.
+ */
+ pci_read_config_dword(pdev, PCI_CSI_CONTROL, &val);
+ val |= PCI_CSI_CONTROL_PORTS_OFF_MASK;
+ pci_write_config_dword(pdev, PCI_CSI_CONTROL, val);
+
+ /*
+ * We lose config space access when punit power gates
+ * the ISP. Can't use pci_set_power_state() because
+ * pmcsr won't actually change when we write to it.
+ */
+ pci_save_state(pdev);
+ pdev->current_state = PCI_D3cold;
+ isp_set_power(pdev, false);
+
return 0;
}
static int isp_pci_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ isp_set_power(pdev, true);
+ pdev->current_state = PCI_D0;
+ pci_restore_state(pdev);
+
return 0;
}
@@ -99,6 +127,7 @@ static UNIVERSAL_DEV_PM_OPS(isp_pm_ops, isp_pci_suspend,
isp_pci_resume, NULL);
static const struct pci_device_id isp_id_table[] = {
+ { PCI_VDEVICE(INTEL, 0x0f38), },
{ PCI_VDEVICE(INTEL, 0x22b8), },
{ 0, }
};
diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c
index a26f410..f40b1c19 100644
--- a/drivers/platform/x86/intel_cht_int33fe.c
+++ b/drivers/platform/x86/intel_cht_int33fe.c
@@ -24,6 +24,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -88,9 +89,9 @@ static const struct property_entry fusb302_props[] = {
{ }
};
-static int cht_int33fe_probe(struct i2c_client *client)
+static int cht_int33fe_probe(struct platform_device *pdev)
{
- struct device *dev = &client->dev;
+ struct device *dev = &pdev->dev;
struct i2c_board_info board_info;
struct cht_int33fe_data *data;
struct i2c_client *max17047;
@@ -207,7 +208,7 @@ static int cht_int33fe_probe(struct i2c_client *client)
if (!data->pi3usb30532)
goto out_unregister_fusb302;
- i2c_set_clientdata(client, data);
+ platform_set_drvdata(pdev, data);
return 0;
@@ -223,9 +224,9 @@ static int cht_int33fe_probe(struct i2c_client *client)
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
}
-static int cht_int33fe_remove(struct i2c_client *i2c)
+static int cht_int33fe_remove(struct platform_device *pdev)
{
- struct cht_int33fe_data *data = i2c_get_clientdata(i2c);
+ struct cht_int33fe_data *data = platform_get_drvdata(pdev);
i2c_unregister_device(data->pi3usb30532);
i2c_unregister_device(data->fusb302);
@@ -237,29 +238,22 @@ static int cht_int33fe_remove(struct i2c_client *i2c)
return 0;
}
-static const struct i2c_device_id cht_int33fe_i2c_id[] = {
- { }
-};
-MODULE_DEVICE_TABLE(i2c, cht_int33fe_i2c_id);
-
static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
{ "INT33FE", },
{ }
};
MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
-static struct i2c_driver cht_int33fe_driver = {
+static struct platform_driver cht_int33fe_driver = {
.driver = {
.name = "Intel Cherry Trail ACPI INT33FE driver",
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
},
- .probe_new = cht_int33fe_probe,
+ .probe = cht_int33fe_probe,
.remove = cht_int33fe_remove,
- .id_table = cht_int33fe_i2c_id,
- .disable_i2c_core_irq_mapping = true,
};
-module_i2c_driver(cht_int33fe_driver);
+module_platform_driver(cht_int33fe_driver);
MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 742a0c2..69e28c1 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -575,7 +575,7 @@ static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = {
static
struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = {
- .items = mlxplat_mlxcpld_msn21xx_items,
+ .items = mlxplat_mlxcpld_msn201x_items,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items),
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
@@ -1421,7 +1421,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
- mlxplat_led = &mlxplat_default_ng_led_data;
+ mlxplat_led = &mlxplat_msn21xx_led_data;
mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data;
return 1;
@@ -1439,7 +1439,7 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
- mlxplat_led = &mlxplat_msn21xx_led_data;
+ mlxplat_led = &mlxplat_default_ng_led_data;
mlxplat_fan = &mlxplat_default_fan_data;
return 1;
diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c
index 6a61028..9c94ebb 100644
--- a/drivers/platform/x86/pmc_atom.c
+++ b/drivers/platform/x86/pmc_atom.c
@@ -445,6 +445,21 @@ static const struct dmi_system_id critclk_systems[] = {
DMI_MATCH(DMI_BOARD_NAME, "CB6363"),
},
},
+ {
+ .ident = "SIMATIC IPC227E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "6ES7647-8B"),
+ },
+ },
+ {
+ .ident = "CONNECT X300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "A5E45074588"),
+ },
+ },
+
{ /*sentinel*/ }
};
diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c
index 1360a7f..8760477 100644
--- a/drivers/power/avs/smartreflex.c
+++ b/drivers/power/avs/smartreflex.c
@@ -1010,8 +1010,7 @@ static int omap_sr_remove(struct platform_device *pdev)
if (sr_info->autocomp_active)
sr_stop_vddautocomp(sr_info);
- if (sr_info->dbg_dir)
- debugfs_remove_recursive(sr_info->dbg_dir);
+ debugfs_remove_recursive(sr_info->dbg_dir);
pm_runtime_disable(&pdev->dev);
list_del(&sr_info->node);
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c
index 0206cce..d9493e8 100644
--- a/drivers/power/reset/at91-sama5d2_shdwc.c
+++ b/drivers/power/reset/at91-sama5d2_shdwc.c
@@ -246,6 +246,9 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
if (!pdev->dev.of_node)
return -ENODEV;
+ if (at91_shdwc)
+ return -EBUSY;
+
at91_shdwc = devm_kzalloc(&pdev->dev, sizeof(*at91_shdwc), GFP_KERNEL);
if (!at91_shdwc)
return -ENOMEM;
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index 02356f9..8bb89c6 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -2433,17 +2433,14 @@ static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
size_t count)
{
unsigned long charge_full;
- ssize_t ret;
+ int ret;
ret = kstrtoul(buf, 10, &charge_full);
+ if (ret)
+ return ret;
- dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full);
-
- if (!ret) {
- di->bat_cap.max_mah = (int) charge_full;
- ret = count;
- }
- return ret;
+ di->bat_cap.max_mah = (int) charge_full;
+ return count;
}
static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
@@ -2455,20 +2452,16 @@ static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
size_t count)
{
unsigned long charge_now;
- ssize_t ret;
+ int ret;
ret = kstrtoul(buf, 10, &charge_now);
+ if (ret)
+ return ret;
- dev_dbg(di->dev, "Ret %zd charge_now %lu was %d",
- ret, charge_now, di->bat_cap.prev_mah);
-
- if (!ret) {
- di->bat_cap.user_mah = (int) charge_now;
- di->flags.user_cap = true;
- ret = count;
- queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
- }
- return ret;
+ di->bat_cap.user_mah = (int) charge_now;
+ di->flags.user_cap = true;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ return count;
}
static struct ab8500_fg_sysfs_entry charge_full_attr =
diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c
index 3bae023..e183a22 100644
--- a/drivers/power/supply/cpcap-battery.c
+++ b/drivers/power/supply/cpcap-battery.c
@@ -82,7 +82,7 @@ struct cpcap_battery_config {
};
struct cpcap_coulomb_counter_data {
- s32 sample; /* 24-bits */
+ s32 sample; /* 24 or 32 bits */
s32 accumulator;
s16 offset; /* 10-bits */
};
@@ -213,7 +213,7 @@ static int cpcap_battery_get_current(struct cpcap_battery_ddata *ddata)
* TI or ST coulomb counter in the PMIC.
*/
static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata,
- u32 sample, s32 accumulator,
+ s32 sample, s32 accumulator,
s16 offset, u32 divider)
{
s64 acc;
@@ -224,7 +224,6 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata,
if (!divider)
return 0;
- sample &= 0xffffff; /* 24-bits, unsigned */
offset &= 0x7ff; /* 10-bits, signed */
switch (ddata->vendor) {
@@ -259,7 +258,7 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata,
/* 3600000μAms = 1μAh */
static int cpcap_battery_cc_to_uah(struct cpcap_battery_ddata *ddata,
- u32 sample, s32 accumulator,
+ s32 sample, s32 accumulator,
s16 offset)
{
return cpcap_battery_cc_raw_div(ddata, sample,
@@ -268,7 +267,7 @@ static int cpcap_battery_cc_to_uah(struct cpcap_battery_ddata *ddata,
}
static int cpcap_battery_cc_to_ua(struct cpcap_battery_ddata *ddata,
- u32 sample, s32 accumulator,
+ s32 sample, s32 accumulator,
s16 offset)
{
return cpcap_battery_cc_raw_div(ddata, sample,
@@ -312,6 +311,8 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata,
/* Sample value CPCAP_REG_CCS1 & 2 */
ccd->sample = (buf[1] & 0x0fff) << 16;
ccd->sample |= buf[0];
+ if (ddata->vendor == CPCAP_VENDOR_TI)
+ ccd->sample = sign_extend32(24, ccd->sample);
/* Accumulator value CPCAP_REG_CCA1 & 2 */
ccd->accumulator = ((s16)buf[3]) << 16;
diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c
index d19307f..9e64728 100644
--- a/drivers/power/supply/max14656_charger_detector.c
+++ b/drivers/power/supply/max14656_charger_detector.c
@@ -240,6 +240,14 @@ static enum power_supply_property max14656_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
+static void stop_irq_work(void *data)
+{
+ struct max14656_chip *chip = data;
+
+ cancel_delayed_work_sync(&chip->irq_work);
+}
+
+
static int max14656_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -278,8 +286,6 @@ static int max14656_probe(struct i2c_client *client,
if (ret)
return -ENODEV;
- INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker);
-
chip->detect_psy = devm_power_supply_register(dev,
&chip->psy_desc, &psy_cfg);
if (IS_ERR(chip->detect_psy)) {
@@ -287,6 +293,13 @@ static int max14656_probe(struct i2c_client *client,
return -EINVAL;
}
+ INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker);
+ ret = devm_add_action(dev, stop_irq_work, chip);
+ if (ret) {
+ dev_err(dev, "devm_add_action %d failed\n", ret);
+ return ret;
+ }
+
ret = devm_request_irq(dev, chip->irq, max14656_irq,
IRQF_TRIGGER_FALLING,
MAX14656_NAME, chip);
diff --git a/drivers/power/supply/max8998_charger.c b/drivers/power/supply/max8998_charger.c
index cad7d1a..aa65e6c 100644
--- a/drivers/power/supply/max8998_charger.c
+++ b/drivers/power/supply/max8998_charger.c
@@ -86,7 +86,7 @@ static const struct power_supply_desc max8998_battery_desc = {
static int max8998_battery_probe(struct platform_device *pdev)
{
struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct max8998_platform_data *pdata = iodev->pdata;
struct power_supply_config psy_cfg = {};
struct max8998_battery_data *max8998;
struct i2c_client *i2c;
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index fe0d9a7..1a188a9 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -44,7 +44,7 @@ static const char * const power_supply_type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB",
"USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
"USB_PD", "USB_PD_DRP", "BrickID",
- "USB_HVDCP", "USB_HVDCP_3", "Wireless", "USB_FLOAT",
+ "USB_HVDCP", "USB_HVDCP_3", "USB_HVDCP_3P5", "Wireless", "USB_FLOAT",
"BMS", "Parallel", "Main", "USB_C_UFP", "USB_C_DFP",
"Charge_Pump",
};
@@ -466,6 +466,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(skin_health),
POWER_SUPPLY_ATTR(aicl_done),
POWER_SUPPLY_ATTR(voltage_step),
+ POWER_SUPPLY_ATTR(apsd_rerun),
+ POWER_SUPPLY_ATTR(apsd_timeout),
/* Charge pump properties */
POWER_SUPPLY_ATTR(cp_status1),
POWER_SUPPLY_ATTR(cp_status2),
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index eed473e..d2573b7 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -77,4 +77,17 @@
sizes. The HL6111R has voltage, current and temperature
protection mechanisms, an I2C interface, and a PSNS output.
+config SMB1398_CHARGER
+ tristate "SMB1398 power supply framework based driver"
+ depends on MFD_I2C_PMIC
+ help
+ Say Y to include the support of SMB1398 Charge driver based on power
+ supply framework.
+ SMB1398 is a combo charger chip which can work in different modes:
+ (1) DIV2 charge pump mode to work as a companion charger to be paired
+ with Qualcomm Technologies, Inc.’s family of standalone chargers;
+ (2) DIV2 and 3-level buck combo mode to regulate the output power from
+ wireless charger receiver and provide the input for downstream
+ chargers.
+
endmenu
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 4132b7b..0c8d8d3 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -7,3 +7,4 @@
obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o pmic-voter.o
obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o
obj-$(CONFIG_HL6111R) += hl6111r.o
+obj-$(CONFIG_SMB1398_CHARGER) += smb1398-charger.o pmic-voter.o
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 8111eec..a87725b 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__
@@ -194,7 +194,8 @@ static int get_hvdcp3_icl_limit(struct pl_data *chip)
{
int main_icl, target_icl = -EINVAL;
- if (chip->charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3)
+ if (chip->charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3 &&
+ chip->charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3P5)
return target_icl;
/*
diff --git a/drivers/power/supply/qcom/battery.h b/drivers/power/supply/qcom/battery.h
index b25afd6..acb4984 100644
--- a/drivers/power/supply/qcom/battery.h
+++ b/drivers/power/supply/qcom/battery.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019-2020 The Linux Foundation. All rights reserved.
*/
#ifndef __BATTERY_H
@@ -10,6 +10,7 @@ struct charger_param {
u32 fcc_step_delay_ms;
u32 fcc_step_size_ua;
u32 smb_version;
+ u32 hvdcp2_max_icl_ua;
u32 hvdcp3_max_icl_ua;
u32 forced_main_fcc;
};
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen4.c b/drivers/power/supply/qcom/qpnp-fg-gen4.c
index 38af5c9..7ae08c7 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen4.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen4.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "FG: %s: " fmt, __func__
@@ -2745,14 +2745,14 @@ static int fg_gen4_update_maint_soc(struct fg_dev *fg)
goto out;
}
- if (msoc > fg->maint_soc) {
+ if (msoc >= fg->maint_soc) {
/*
* When the monotonic SOC goes above maintenance SOC, we should
* stop showing the maintenance SOC.
*/
fg->delta_soc = 0;
fg->maint_soc = 0;
- } else if (fg->maint_soc && msoc <= fg->last_msoc) {
+ } else if (fg->maint_soc && msoc < fg->last_msoc) {
/* MSOC is decreasing. Decrease maintenance SOC as well */
fg->maint_soc -= 1;
if (!(msoc % 10)) {
@@ -4209,13 +4209,11 @@ static void status_change_work(struct work_struct *work)
cycle_count_update(chip->counter, (u32)batt_soc >> 24,
fg->charge_status, fg->charge_done, input_present);
- if (fg->charge_status != fg->prev_charge_status) {
- batt_soc_cp = div64_u64((u64)(u32)batt_soc * CENTI_FULL_SOC,
- BATT_SOC_32BIT);
- cap_learning_update(chip->cl, batt_temp, batt_soc_cp,
+ batt_soc_cp = div64_u64((u64)(u32)batt_soc * CENTI_FULL_SOC,
+ BATT_SOC_32BIT);
+ cap_learning_update(chip->cl, batt_temp, batt_soc_cp,
fg->charge_status, fg->charge_done, input_present,
qnovo_en);
- }
rc = fg_gen4_charge_full_update(fg);
if (rc < 0)
@@ -4251,7 +4249,6 @@ static void status_change_work(struct work_struct *work)
pr_err("Failed to validate SOC scale mode, rc=%d\n", rc);
ttf_update(chip->ttf, input_present);
- fg->prev_charge_status = fg->charge_status;
out:
fg_dbg(fg, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n",
fg->charge_status, fg->charge_type, fg->charge_done);
@@ -6209,7 +6206,6 @@ static int fg_gen4_probe(struct platform_device *pdev)
fg->debug_mask = &fg_gen4_debug_mask;
fg->irqs = fg_irqs;
fg->charge_status = -EINVAL;
- fg->prev_charge_status = -EINVAL;
fg->online_status = -EINVAL;
fg->batt_id_ohms = -EINVAL;
chip->ki_coeff_full_soc[0] = -EINVAL;
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index 7904595..7e8d651 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -4310,7 +4310,6 @@ static int process_suspend(struct qpnp_qg *chip)
return 0;
cancel_delayed_work_sync(&chip->ttf->ttf_work);
- cancel_delayed_work_sync(&chip->qg_sleep_exit_work);
chip->suspend_data = false;
@@ -4477,6 +4476,9 @@ static int qpnp_qg_suspend_noirq(struct device *dev)
int rc;
struct qpnp_qg *chip = dev_get_drvdata(dev);
+ /* cancel any pending sleep_exit work */
+ cancel_delayed_work_sync(&chip->qg_sleep_exit_work);
+
mutex_lock(&chip->data_lock);
rc = process_suspend(chip);
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 4b07fa1..9785605 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
@@ -19,6 +19,7 @@
#include <linux/regulator/machine.h>
#include <linux/iio/consumer.h>
#include <linux/pmic-voter.h>
+#include <linux/usb/typec.h>
#include "smb5-reg.h"
#include "smb5-lib.h"
#include "schgm-flash.h"
@@ -599,6 +600,11 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node)
if (chg->chg_param.hvdcp3_max_icl_ua <= 0)
chg->chg_param.hvdcp3_max_icl_ua = MICRO_3PA;
+ of_property_read_u32(node, "qcom,hvdcp2-max-icl-ua",
+ &chg->chg_param.hvdcp2_max_icl_ua);
+ if (chg->chg_param.hvdcp2_max_icl_ua <= 0)
+ chg->chg_param.hvdcp2_max_icl_ua = MICRO_3PA;
+
return 0;
}
@@ -820,6 +826,8 @@ static enum power_supply_property smb5_usb_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_VPH,
POWER_SUPPLY_PROP_THERM_ICL_LIMIT,
POWER_SUPPLY_PROP_SKIN_HEALTH,
+ POWER_SUPPLY_PROP_APSD_RERUN,
+ POWER_SUPPLY_PROP_APSD_TIMEOUT,
};
static int smb5_usb_get_prop(struct power_supply *psy,
@@ -963,6 +971,12 @@ static int smb5_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_SKIN_HEALTH:
val->intval = smblib_get_skin_temp_status(chg);
break;
+ case POWER_SUPPLY_PROP_APSD_RERUN:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_APSD_TIMEOUT:
+ val->intval = chg->apsd_ext_timeout;
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -1052,6 +1066,11 @@ static int smb5_usb_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_ADAPTER_CC_MODE:
chg->adapter_cc_mode = val->intval;
break;
+ case POWER_SUPPLY_PROP_APSD_RERUN:
+ del_timer_sync(&chg->apsd_timer);
+ chg->apsd_ext_timeout = false;
+ smblib_rerun_apsd(chg);
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -1070,6 +1089,7 @@ static int smb5_usb_prop_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_THERM_ICL_LIMIT:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT:
case POWER_SUPPLY_PROP_ADAPTER_CC_MODE:
+ case POWER_SUPPLY_PROP_APSD_RERUN:
return 1;
default:
break;
@@ -1287,7 +1307,7 @@ static int smb5_usb_main_get_prop(struct power_supply *psy,
break;
/* Use this property to report SMB health */
case POWER_SUPPLY_PROP_HEALTH:
- val->intval = smblib_get_prop_smb_health(chg);
+ rc = val->intval = smblib_get_prop_smb_health(chg);
break;
/* Use this property to report overheat status */
case POWER_SUPPLY_PROP_HOT_TEMP:
@@ -1298,12 +1318,10 @@ static int smb5_usb_main_get_prop(struct power_supply *psy,
rc = -EINVAL;
break;
}
- if (rc < 0) {
+ if (rc < 0)
pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
- return -ENODATA;
- }
- return 0;
+ return rc;
}
static int smb5_usb_main_set_prop(struct power_supply *psy,
@@ -1313,6 +1331,7 @@ static int smb5_usb_main_set_prop(struct power_supply *psy,
struct smb5 *chip = power_supply_get_drvdata(psy);
struct smb_charger *chg = &chip->chg;
union power_supply_propval pval = {0, };
+ enum power_supply_type real_chg_type = chg->real_charger_type;
int rc = 0, offset_ua = 0;
switch (psp) {
@@ -1388,7 +1407,8 @@ static int smb5_usb_main_set_prop(struct power_supply *psy,
vote_override(chg->usb_icl_votable, CC_MODE_VOTER,
(val->intval < 0) ? false : true, val->intval);
/* Main ICL updated re-calculate ILIM */
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
+ if (real_chg_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 ||
+ real_chg_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5)
rerun_election(chg->fcc_votable);
break;
case POWER_SUPPLY_PROP_COMP_CLAMP_LEVEL:
@@ -2099,14 +2119,18 @@ static int smb5_configure_typec(struct smb_charger *chg)
return rc;
}
+ val = EN_TRY_SNK_BIT;
+ /* PMI632 doesn't support try snk */
+ if (chg->chg_param.smb_version == PMI632_SUBTYPE)
+ val = 0;
+
/* enable try.snk and clear force sink for DRP mode */
rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
EN_TRY_SNK_BIT | EN_SNK_ONLY_BIT,
- EN_TRY_SNK_BIT);
+ val);
if (rc < 0) {
dev_err(chg->dev,
- "Couldn't configure TYPE_C_MODE_CFG_REG rc=%d\n",
- rc);
+ "Couldn't configure TYPE_C_MODE_CFG_REG rc=%d\n", rc);
return rc;
}
chg->typec_try_mode |= EN_TRY_SNK_BIT;
@@ -3383,6 +3407,37 @@ static int smb5_show_charger_status(struct smb5 *chip)
return rc;
}
+/*********************************
+ * TYPEC CLASS REGISTRATION *
+ **********************************/
+
+static int smb5_init_typec_class(struct smb5 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ /* Register typec class for only non-PD TypeC and uUSB designs */
+ if (!chg->pd_not_supported)
+ return rc;
+
+ mutex_init(&chg->typec_lock);
+ chg->typec_caps.type = TYPEC_PORT_DRP;
+ chg->typec_caps.data = TYPEC_PORT_DRD;
+ chg->typec_partner_desc.usb_pd = false;
+ chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_NONE;
+ chg->typec_caps.port_type_set = smblib_typec_port_type_set;
+ chg->typec_caps.revision = 0x0130;
+
+ chg->typec_port = typec_register_port(chg->dev, &chg->typec_caps);
+ if (IS_ERR(chg->typec_port)) {
+ rc = PTR_ERR(chg->typec_port);
+ pr_err("failed to register typec_port rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
static int smb5_probe(struct platform_device *pdev)
{
struct smb5 *chip;
@@ -3539,6 +3594,12 @@ static int smb5_probe(struct platform_device *pdev)
goto cleanup;
}
+ rc = smb5_init_typec_class(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize typec class rc=%d\n", rc);
+ goto cleanup;
+ }
+
rc = smb5_determine_initial_status(chip);
if (rc < 0) {
pr_err("Couldn't determine initial status rc=%d\n",
diff --git a/drivers/power/supply/qcom/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c
index 5bbaca7..e93cb9d 100644
--- a/drivers/power/supply/qcom/smb1355-charger.c
+++ b/drivers/power/supply/qcom/smb1355-charger.c
@@ -1197,6 +1197,16 @@ static int smb1355_init_hw(struct smb1355 *chip)
return rc;
}
+ /* disable charging when watchdog bites & set bite-timeout to 8secs */
+ val = BITE_WDOG_DISABLE_CHARGING_CFG_BIT | 0x3;
+ rc = smb1355_masked_write(chip, SNARL_BARK_BITE_WD_CFG_REG,
+ BITE_WDOG_DISABLE_CHARGING_CFG_BIT |
+ BITE_WDOG_TIMEOUT_MASK, val);
+ if (rc < 0) {
+ pr_err("Couldn't configure the watchdog bite rc=%d\n", rc);
+ return rc;
+ }
+
/* enable watchdog bark and bite interrupts, and disable the watchdog */
rc = smb1355_masked_write(chip, WD_CFG_REG, WDOG_TIMER_EN_BIT
| WDOG_TIMER_EN_ON_PLUGIN_BIT | BITE_WDOG_INT_EN_BIT
@@ -1207,15 +1217,6 @@ static int smb1355_init_hw(struct smb1355 *chip)
return rc;
}
- /* disable charging when watchdog bites */
- rc = smb1355_masked_write(chip, SNARL_BARK_BITE_WD_CFG_REG,
- BITE_WDOG_DISABLE_CHARGING_CFG_BIT,
- BITE_WDOG_DISABLE_CHARGING_CFG_BIT);
- if (rc < 0) {
- pr_err("Couldn't configure the watchdog bite rc=%d\n", rc);
- return rc;
- }
-
/*
* Disable command based SMB1355 enablement and disable parallel
* charging path by switching to command based mode.
diff --git a/drivers/power/supply/qcom/smb1390-charger-psy.c b/drivers/power/supply/qcom/smb1390-charger-psy.c
index 7a0dee0..833c8b6 100644
--- a/drivers/power/supply/qcom/smb1390-charger-psy.c
+++ b/drivers/power/supply/qcom/smb1390-charger-psy.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "SMB1390: %s: " fmt, __func__
@@ -197,6 +197,12 @@ struct smb1390 {
int irq_status;
int taper_entry_fv;
bool switcher_enabled;
+ int cp_status1;
+ int cp_status2;
+ int cp_enable;
+ int cp_isns_master;
+ int cp_isns_slave;
+ int cp_ilim;
int die_temp;
bool suspended;
bool disabled;
@@ -1332,6 +1338,45 @@ static enum power_supply_property smb1390_charge_pump_props[] = {
POWER_SUPPLY_PROP_PARALLEL_MODE,
};
+static int smb1390_get_prop_suspended(struct smb1390 *chip,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_STATUS1:
+ val->intval = chip->cp_status1;
+ break;
+ case POWER_SUPPLY_PROP_CP_STATUS2:
+ val->intval = chip->cp_status2;
+ break;
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ val->intval = chip->cp_enable;
+ break;
+ case POWER_SUPPLY_PROP_CP_SWITCHER_EN:
+ val->intval = chip->switcher_enabled;
+ break;
+ case POWER_SUPPLY_PROP_CP_DIE_TEMP:
+ val->intval = chip->die_temp;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS:
+ val->intval = chip->cp_isns_master;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS_SLAVE:
+ val->intval = chip->cp_isns_slave;
+ break;
+ case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
+ val->intval = chip->irq_status;
+ break;
+ case POWER_SUPPLY_PROP_CP_ILIM:
+ val->intval = chip->cp_ilim;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int smb1390_get_prop(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
@@ -1340,65 +1385,70 @@ static int smb1390_get_prop(struct power_supply *psy,
int rc = 0, status;
bool enable;
+ /*
+ * Return the cached values when the system is in suspend state
+ * instead of reading the registers to avoid read failures.
+ */
+ if (chip->suspended) {
+ rc = smb1390_get_prop_suspended(chip, prop, val);
+ if (!rc)
+ return rc;
+ rc = 0;
+ }
+
switch (prop) {
case POWER_SUPPLY_PROP_CP_STATUS1:
rc = smb1390_read(chip, CORE_STATUS1_REG, &status);
if (!rc)
- val->intval = status;
+ chip->cp_status1 = val->intval = status;
break;
case POWER_SUPPLY_PROP_CP_STATUS2:
rc = smb1390_read(chip, CORE_STATUS2_REG, &status);
if (!rc)
- val->intval = status;
+ chip->cp_status2 = val->intval = status;
break;
case POWER_SUPPLY_PROP_CP_ENABLE:
- rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN, &enable);
+ rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN,
+ &enable);
if (!rc)
- val->intval = enable &&
- !get_effective_result(chip->disable_votable);
+ chip->cp_enable = val->intval = enable &&
+ !get_effective_result(chip->disable_votable);
break;
case POWER_SUPPLY_PROP_CP_SWITCHER_EN:
- if (chip->suspended) {
- val->intval = chip->switcher_enabled;
- } else {
- rc = smb1390_get_cp_en_status(chip, SWITCHER_EN,
- &enable);
- if (!rc)
- val->intval = enable;
- }
+ rc = smb1390_get_cp_en_status(chip, SWITCHER_EN,
+ &enable);
+ if (!rc)
+ val->intval = enable;
break;
case POWER_SUPPLY_PROP_CP_DIE_TEMP:
- if (chip->suspended) {
- if (chip->die_temp != -ENODATA)
- val->intval = chip->die_temp;
- else
- rc = -ENODATA;
- } else {
- /*
- * Add a filter to the die temp value read:
- * If temp > THERMAL_SUSPEND_DECIDEGC then
- * - treat it as an error and report last valid
- * cached temperature.
- * - return -ENODATA if the cached value is
- * invalid.
- */
+ /*
+ * Add a filter to the die temp value read:
+ * If temp > THERMAL_SUSPEND_DECIDEGC then
+ * - treat it as an error and report last valid
+ * cached temperature.
+ * - return -ENODATA if the cached value is
+ * invalid.
+ */
- rc = smb1390_get_die_temp(chip, val);
- if (rc >= 0) {
- if (val->intval <= THERMAL_SUSPEND_DECIDEGC)
- chip->die_temp = val->intval;
- else if (chip->die_temp == -ENODATA)
- rc = -ENODATA;
- else
- val->intval = chip->die_temp;
- }
+ rc = smb1390_get_die_temp(chip, val);
+ if (rc >= 0) {
+ if (val->intval <= THERMAL_SUSPEND_DECIDEGC)
+ chip->die_temp = val->intval;
+ else if (chip->die_temp == -ENODATA)
+ rc = -ENODATA;
+ else
+ val->intval = chip->die_temp;
}
break;
case POWER_SUPPLY_PROP_CP_ISNS:
rc = smb1390_get_isns_master(chip, val);
+ if (!rc)
+ chip->cp_isns_master = val->intval;
break;
case POWER_SUPPLY_PROP_CP_ISNS_SLAVE:
rc = smb1390_get_isns_slave(chip, val);
+ if (!rc)
+ chip->cp_isns_slave = val->intval;
break;
case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
val->intval = 0;
@@ -1415,6 +1465,8 @@ static int smb1390_get_prop(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CP_ILIM:
rc = smb1390_get_cp_ilim(chip, val);
+ if (!rc)
+ chip->cp_ilim = val->intval;
break;
case POWER_SUPPLY_PROP_CHIP_VERSION:
val->intval = chip->pmic_rev_id->rev4;
@@ -1435,7 +1487,7 @@ static int smb1390_get_prop(struct power_supply *psy,
default:
smb1390_dbg(chip, PR_MISC, "charge pump power supply get prop %d not supported\n",
prop);
- return -EINVAL;
+ rc = -EINVAL;
}
return rc;
diff --git a/drivers/power/supply/qcom/smb1398-charger.c b/drivers/power/supply/qcom/smb1398-charger.c
new file mode 100644
index 0000000..210fd42
--- /dev/null
+++ b/drivers/power/supply/qcom/smb1398-charger.c
@@ -0,0 +1,2543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "SMB1398: %s: " fmt, __func__
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pmic-voter.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/iio/consumer.h>
+
+/* Status register definition */
+#define INPUT_STATUS_REG 0x2609
+#define INPUT_USB_IN BIT(1)
+#define INPUT_WLS_IN BIT(0)
+
+#define PERPH0_INT_RT_STS_REG 0x2610
+#define USB_IN_OVLO_STS BIT(7)
+#define WLS_IN_OVLO_STS BIT(6)
+#define USB_IN_UVLO_STS BIT(5)
+#define WLS_IN_UVLO_STS BIT(4)
+#define DIV2_IREV_LATCH_STS BIT(3)
+#define VOL_UV_LATCH_STS BIT(2)
+#define TEMP_SHUTDOWN_STS BIT(1)
+#define CFLY_HARD_FAULT_LATCH_STS BIT(0)
+
+#define MODE_STATUS_REG 0x2641
+#define SMB_EN BIT(7)
+#define PRE_EN_DCDC BIT(6)
+#define DIV2_EN_SLAVE BIT(5)
+#define LCM_EN BIT(4)
+#define DIV2_EN BIT(3)
+#define BUCK_EN BIT(2)
+#define CFLY_SS_DONE BIT(1)
+#define DCDC_EN BIT(0)
+
+#define SWITCHER_OFF_WIN_STATUS_REG 0x2642
+#define DIV2_WIN_OV BIT(1)
+#define DIV2_WIN_UV BIT(0)
+
+#define SWITCHER_OFF_VIN_STATUS_REG 0x2643
+#define USB_IN_OVLO BIT(3)
+#define WLS_IN_OVLO BIT(2)
+#define USB_IN_UVLO BIT(1)
+#define WLS_IN_UVLO BIT(0)
+
+#define SWITCHER_OFF_FAULT_REG 0x2644
+#define VOUT_OV_3LVL_BUCK BIT(5)
+#define VOUT_UV_LATCH BIT(4)
+#define ITERM_3LVL_LATCH BIT(3)
+#define DIV2_IREV_LATCH BIT(2)
+#define TEMP_SHDWN BIT(1)
+#define CFLY_HARD_FAULT_LATCH BIT(0)
+
+#define BUCK_CC_CV_STATE_REG 0x2645
+#define BUCK_IN_CC_REGULATION BIT(1)
+#define BUCK_IN_CV_REGULATION BIT(0)
+
+#define INPUT_CURRENT_REGULATION_REG 0x2646
+#define BUCK_IN_ICL BIT(1)
+#define DIV2_IN_ILIM BIT(0)
+
+/* Config register definition */
+#define PERPH0_MISC_CFG2_REG 0x2636
+#define CFG_TEMP_PIN_ITEMP BIT(1)
+
+#define MISC_USB_WLS_SUSPEND_REG 0x2630
+#define WLS_SUSPEND BIT(1)
+#define USB_SUSPEND BIT(0)
+
+#define MISC_SL_SWITCH_EN_REG 0x2631
+#define EN_SLAVE BIT(1)
+#define EN_SWITCHER BIT(0)
+
+#define MISC_DIV2_3LVL_CTRL_REG 0x2632
+#define MISC_DIV2_3LVL_CTRL_MASK GENMASK(7, 0)
+#define EN_DIV2_CP BIT(2)
+#define EN_3LVL_BULK BIT(1)
+#define EN_CHG_2X BIT(0)
+
+#define MISC_CFG0_REG 0x2634
+#define DIS_SYNC_DRV_BIT BIT(5)
+#define SW_EN_SWITCHER_BIT BIT(3)
+
+#define MISC_CFG1_REG 0x2635
+#define MISC_CFG1_MASK GENMASK(7, 0)
+#define CFG_OP_MODE_MASK GENMASK(2, 0)
+#define OP_MODE_DISABLED 0
+#define OP_MODE_3LVL_BULK 1
+#define OP_MODE_COMBO 2
+#define OP_MODE_DIV2_CP 3
+#define OP_MODE_PRE_REG_3S 4
+#define OP_MODE_ITLGS_1P 5
+#define OP_MODE_ITLGS_2X 6
+#define OP_MODE_PRE_REGULATOR 7
+
+#define MISC_CFG2_REG 0x2636
+
+#define NOLOCK_SPARE_REG 0x2637
+#define DIV2_WIN_UV_SEL_BIT BIT(4)
+#define DIV2_WIN_UV_25MV 0
+#define COMBO_WIN_LO_EXIT_SEL_MASK GENMASK(3, 2)
+#define EXIT_DIV2_VOUT_HI_12P5MV 0
+#define EXIT_DIV2_VOUT_HI_25MV 1
+#define EXIT_DIV2_VOUT_HI_50MV 2
+#define EXIT_DIV2_VOUT_HI_75MV 3
+#define COMBO_WIN_HI_EXIT_SEL_MASK GENMASK(1, 0)
+#define EXIT_DIV2_VOUT_LO_75MV 0
+#define EXIT_DIV2_VOUT_LO_100MV 1
+#define EXIT_DIV2_VOUT_LO_200MV 2
+#define EXIT_DIV2_VOUT_LO_250MV 3
+
+#define SMB_EN_TRIGGER_CFG_REG 0x2639
+#define SMB_EN_NEG_TRIGGER BIT(1)
+#define SMB_EN_POS_TRIGGER BIT(0)
+
+#define DIV2_LCM_CFG_REG 0x2653
+#define DIV2_LCM_REFRESH_TIMER_SEL_MASK GENMASK(5, 4)
+#define DIV2_WIN_BURST_HIGH_REF_MASK GENMASK(3, 2)
+#define DIV2_WIN_BURST_LOW_REF_MASK GENMASK(1, 0)
+
+#define DIV2_CURRENT_REG 0x2655
+#define DIV2_EN_ILIM_DET BIT(2)
+#define DIV2_EN_IREV_DET BIT(1)
+#define DIV2_EN_OCP_DET BIT(0)
+
+#define DIV2_PROTECTION_REG 0x2656
+#define DIV2_WIN_OV_SEL_MASK GENMASK(1, 0)
+#define WIN_OV_200_MV 0
+#define WIN_OV_300_MV 1
+#define WIN_OV_400_MV 2
+#define WIN_OV_500_MV 3
+
+#define DIV2_MODE_CFG_REG 0x265C
+
+#define LCM_EXIT_CTRL_REG 0x265D
+
+#define ICHG_SS_DAC_TARGET_REG 0x2660
+#define ICHG_SS_DAC_VALUE_MASK GENMASK(5, 0)
+#define ICHG_STEP_MA 100
+
+#define VOUT_DAC_TARGET_REG 0x2663
+#define VOUT_DAC_VALUE_MASK GENMASK(7, 0)
+#define VOUT_1P_MIN_MV 3300
+#define VOUT_1S_MIN_MV 6600
+#define VOUT_1P_STEP_MV 10
+#define VOUT_1S_STEP_MV 20
+
+#define VOUT_SS_DAC_TARGET_REG 0x2666
+#define VOUT_SS_DAC_VALUE_MASK GENMASK(5, 0)
+#define VOUT_SS_1P_STEP_MV 90
+#define VOUT_SS_1S_STEP_MV 180
+
+#define IIN_SS_DAC_TARGET_REG 0x2669
+#define IIN_SS_DAC_VALUE_MASK GENMASK(6, 0)
+#define IIN_STEP_MA 50
+
+#define PERPH0_DIV2_REF_CFG 0x2671
+#define CFG_IREV_REF_BIT BIT(2)
+
+#define PERPH0_CFG_SDCDC_REG 0x267A
+#define EN_WIN_UV_BIT BIT(7)
+
+#define SSUPLY_TEMP_CTRL_REG 0x2683
+#define SEL_OUT_TEMP_MAX_MASK GENMASK(7, 5)
+#define SEL_OUT_TEMP_MAX_SHFT 5
+#define SEL_OUT_HIGHZ (0 << SEL_OUT_TEMP_MAX_SHFT)
+#define SEL_OUT_VTEMP (1 << SEL_OUT_TEMP_MAX_SHFT)
+#define SEL_OUT_ICHG (2 << SEL_OUT_TEMP_MAX_SHFT)
+#define SEL_OUT_IIN_FB (4 << SEL_OUT_TEMP_MAX_SHFT)
+
+#define PERPH1_INT_RT_STS_REG 0x2710
+#define DIV2_WIN_OV_STS BIT(7)
+#define DIV2_WIN_UV_STS BIT(6)
+#define DIV2_ILIM_STS BIT(5)
+#define DIV2_CFLY_SS_DONE_STS BIT(1)
+
+/* available voters */
+#define ILIM_VOTER "ILIM_VOTER"
+#define TAPER_VOTER "TAPER_VOTER"
+#define STATUS_CHANGE_VOTER "STATUS_CHANGE_VOTER"
+#define SHUTDOWN_VOTER "SHUTDOWN_VOTER"
+#define CUTOFF_SOC_VOTER "CUTOFF_SOC_VOTER"
+#define SRC_VOTER "SRC_VOTER"
+#define ICL_VOTER "ICL_VOTER"
+#define WIRELESS_VOTER "WIRELESS_VOTER"
+#define SWITCHER_TOGGLE_VOTER "SWITCHER_TOGGLE_VOTER"
+#define USER_VOTER "USER_VOTER"
+#define FCC_VOTER "FCC_VOTER"
+#define CP_VOTER "CP_VOTER"
+#define CC_MODE_VOTER "CC_MODE_VOTER"
+#define MAIN_DISABLE_VOTER "MAIN_DISABLE_VOTER"
+#define TAPER_MAIN_ICL_LIMIT_VOTER "TAPER_MAIN_ICL_LIMIT_VOTER"
+
+/* Constant definitions */
+/* Need to define max ILIM for smb1398 */
+#define DIV2_MAX_ILIM_UA 3200000
+#define DIV2_MAX_ILIM_DUAL_CP_UA 6400000
+#define DIV2_ILIM_CFG_PCT 105
+
+#define TAPER_STEPPER_UA_DEFAULT 100000
+#define TAPER_STEPPER_UA_IN_CC_MODE 200000
+#define CC_MODE_TAPER_MAIN_ICL_UA 500000
+
+#define MAX_IOUT_UA 6300000
+#define MAX_1S_VOUT_UV 11700000
+
+#define THERMAL_SUSPEND_DECIDEGC 1400
+
+#define DIV2_CP_MASTER 0
+#define DIV2_CP_SLAVE 1
+#define COMBO_PRE_REGULATOR 2
+
+enum isns_mode {
+ ISNS_MODE_OFF = 0,
+ ISNS_MODE_ACTIVE,
+ ISNS_MODE_STANDBY,
+};
+
+enum {
+ /* Perph0 IRQs */
+ CFLY_HARD_FAULT_LATCH_IRQ,
+ TEMP_SHDWN_IRQ,
+ VOUT_UV_LATH_IRQ,
+ DIV2_IREV_LATCH_IRQ,
+ WLS_IN_UVLO_IRQ,
+ USB_IN_UVLO_IRQ,
+ WLS_IN_OVLO_IRQ,
+ USB_IN_OVLO_IRQ,
+ /* Perph1 IRQs */
+ BK_IIN_REG_IRQ,
+ CFLY_SS_DONE_IRQ,
+ EN_DCDC_IRQ,
+ ITERM_3LVL_LATCH_IRQ,
+ VOUT_OV_3LB_IRQ,
+ DIV2_ILIM_IRQ,
+ DIV2_WIN_UV_IRQ,
+ DIV2_WIN_OV_IRQ,
+ /* Perph2 IRQs */
+ IN_3LVL_MODE_IRQ,
+ DIV2_MODE_IRQ,
+ BK_CV_REG_IRQ,
+ BK_CC_REG_IRQ,
+ SS_DAC_INT_IRQ,
+ SMB_EN_RISE_IRQ,
+ SMB_EN_FALL_IRQ,
+ /* End */
+ NUM_IRQS,
+};
+
+struct smb_irq {
+ const char *name;
+ const irq_handler_t handler;
+ const bool wake;
+ int shift;
+};
+
+static const struct smb_irq smb_irqs[];
+
+struct smb1398_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct pmic_revid_data *pmic_rev_id;
+
+ struct wakeup_source *ws;
+ struct iio_channel *die_temp_chan;
+
+ struct power_supply *div2_cp_master_psy;
+ struct power_supply *div2_cp_slave_psy;
+ struct power_supply *pre_regulator_psy;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
+ struct notifier_block nb;
+
+ struct votable *awake_votable;
+ struct votable *div2_cp_disable_votable;
+ struct votable *div2_cp_slave_disable_votable;
+ struct votable *div2_cp_ilim_votable;
+ struct votable *pre_regulator_iout_votable;
+ struct votable *pre_regulator_vout_votable;
+ struct votable *fcc_votable;
+ struct votable *fv_votable;
+ struct votable *fcc_main_votable;
+ struct votable *usb_icl_votable;
+
+ struct work_struct status_change_work;
+ struct work_struct taper_work;
+
+ struct mutex die_chan_lock;
+ spinlock_t status_change_lock;
+
+ int irqs[NUM_IRQS];
+ int die_temp;
+ int div2_cp_min_ilim_ua;
+ int ilim_ua_disable_div2_cp_slave;
+ int max_cutoff_soc;
+ int taper_entry_fv;
+ int div2_irq_status;
+ u32 div2_cp_role;
+ u32 pl_output_mode;
+ u32 pl_input_mode;
+ enum isns_mode current_capability;
+ int cc_mode_taper_main_icl_ua;
+ int cp_status1;
+ int cp_status2;
+ int cp_enable;
+ int cp_isns_master;
+ int cp_isns_slave;
+ int cp_ilim;
+
+ bool status_change_running;
+ bool taper_work_running;
+ bool cutoff_soc_checked;
+ bool smb_en;
+ bool switcher_en;
+ bool slave_en;
+ bool in_suspend;
+ bool disabled;
+};
+
+static int smb1398_read(struct smb1398_chip *chip, u16 reg, u8 *val)
+{
+ int rc = 0, value = 0;
+
+ rc = regmap_read(chip->regmap, reg, &value);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't read register 0x%x, rc=%d\n",
+ reg, rc);
+ else
+ *val = (u8)value;
+
+ return rc;
+}
+
+static int smb1398_masked_write(struct smb1398_chip *chip,
+ u16 reg, u8 mask, u8 val)
+{
+ int rc = 0;
+
+ rc = regmap_update_bits(chip->regmap, reg, mask, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't update register 0x%x to 0x%x with mask 0x%x, rc=%d\n",
+ reg, val, mask, rc);
+
+ return rc;
+}
+
+static int smb1398_get_enable_status(struct smb1398_chip *chip)
+{
+ int rc = 0;
+ u8 val;
+ bool switcher_en = false;
+
+ rc = smb1398_read(chip, MODE_STATUS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ chip->smb_en = !!(val & SMB_EN);
+ chip->switcher_en = !!(val & PRE_EN_DCDC);
+ chip->slave_en = !!(val & DIV2_EN_SLAVE);
+
+ rc = smb1398_read(chip, MISC_SL_SWITCH_EN_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ switcher_en = !!(val & EN_SWITCHER);
+ chip->switcher_en = switcher_en && chip->switcher_en;
+
+ dev_dbg(chip->dev, "smb_en = %d, switcher_en = %d, slave_en = %d\n",
+ chip->smb_en, chip->switcher_en, chip->slave_en);
+ return rc;
+}
+
+static int smb1398_get_iin_ma(struct smb1398_chip *chip, int *iin_ma)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = smb1398_read(chip, IIN_SS_DAC_TARGET_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ *iin_ma = (val & IIN_SS_DAC_VALUE_MASK) * IIN_STEP_MA;
+
+ dev_dbg(chip->dev, "get iin_ma = %dmA\n", *iin_ma);
+ return rc;
+}
+
+static int smb1398_set_iin_ma(struct smb1398_chip *chip, int iin_ma)
+{
+ int rc = 0;
+ u8 val;
+
+ val = iin_ma / IIN_STEP_MA;
+ rc = smb1398_masked_write(chip, IIN_SS_DAC_TARGET_REG,
+ IIN_SS_DAC_VALUE_MASK, val);
+ if (rc < 0)
+ return rc;
+
+ dev_dbg(chip->dev, "set iin_ma = %dmA\n", iin_ma);
+ return rc;
+}
+
+static int smb1398_set_ichg_ma(struct smb1398_chip *chip, int ichg_ma)
+{
+ int rc = 0;
+ u8 val;
+
+ if (ichg_ma < 0 || ichg_ma > ICHG_SS_DAC_VALUE_MASK * ICHG_STEP_MA)
+ return rc;
+
+ val = ichg_ma / ICHG_STEP_MA;
+ rc = smb1398_masked_write(chip, ICHG_SS_DAC_TARGET_REG,
+ ICHG_SS_DAC_VALUE_MASK, val);
+
+ dev_dbg(chip->dev, "set ichg %dmA\n", ichg_ma);
+ return rc;
+}
+
+static int smb1398_get_ichg_ma(struct smb1398_chip *chip, int *ichg_ma)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = smb1398_read(chip, ICHG_SS_DAC_TARGET_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ *ichg_ma = (val & ICHG_SS_DAC_VALUE_MASK) * ICHG_STEP_MA;
+
+ dev_dbg(chip->dev, "get ichg %dmA\n", *ichg_ma);
+ return 0;
+}
+
+static int smb1398_set_1s_vout_mv(struct smb1398_chip *chip, int vout_mv)
+{
+ int rc = 0;
+ u8 val;
+
+ if (vout_mv < VOUT_1S_MIN_MV)
+ return -EINVAL;
+
+ val = (vout_mv - VOUT_1S_MIN_MV) / VOUT_1S_STEP_MV;
+
+ rc = smb1398_masked_write(chip, VOUT_DAC_TARGET_REG,
+ VOUT_DAC_VALUE_MASK, val);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int smb1398_get_1s_vout_mv(struct smb1398_chip *chip, int *vout_mv)
+{
+ int rc;
+ u8 val;
+
+ rc = smb1398_read(chip, VOUT_DAC_TARGET_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ *vout_mv = (val & VOUT_DAC_VALUE_MASK) * VOUT_1S_STEP_MV +
+ VOUT_1S_MIN_MV;
+
+ return 0;
+}
+
+static int smb1398_get_die_temp(struct smb1398_chip *chip, int *temp)
+{
+ int die_temp_deciC = 0, rc = 0;
+
+ rc = smb1398_get_enable_status(chip);
+ if (rc < 0)
+ return rc;
+
+ if (!chip->smb_en)
+ return -ENODATA;
+
+ mutex_lock(&chip->die_chan_lock);
+ rc = iio_read_channel_processed(chip->die_temp_chan, &die_temp_deciC);
+ mutex_unlock(&chip->die_chan_lock);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read die_temp_chan, rc=%d\n", rc);
+ } else {
+ *temp = die_temp_deciC / 100;
+ dev_dbg(chip->dev, "die temp %d\n", *temp);
+ }
+
+ return rc;
+}
+
+static int smb1398_div2_cp_get_status1(
+ struct smb1398_chip *chip, u8 *status)
+{
+ int rc = 0;
+ u8 val;
+ bool ilim, win_uv, win_ov;
+
+ rc = smb1398_read(chip, PERPH1_INT_RT_STS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ win_uv = !!(val & DIV2_WIN_UV_STS);
+ win_ov = !!(val & DIV2_WIN_OV_STS);
+ ilim = !!(val & DIV2_ILIM_STS);
+ *status = ilim << 5 | win_uv << 1 | win_ov;
+
+ dev_dbg(chip->dev, "status1 = 0x%x\n", *status);
+ return rc;
+}
+
+static int smb1398_div2_cp_get_status2(
+ struct smb1398_chip *chip, u8 *status)
+{
+ int rc = 0;
+ u8 val;
+ bool smb_en, vin_ov, vin_uv, irev, tsd, switcher_off;
+
+ rc = smb1398_read(chip, MODE_STATUS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ smb_en = !!(val & SMB_EN);
+ switcher_off = !(val & PRE_EN_DCDC);
+
+ rc = smb1398_read(chip, PERPH1_INT_RT_STS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ switcher_off = !(val & DIV2_CFLY_SS_DONE_STS) && switcher_off;
+
+ rc = smb1398_read(chip, SWITCHER_OFF_VIN_STATUS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ vin_ov = !!(val & USB_IN_OVLO);
+ vin_uv = !!(val & USB_IN_UVLO);
+
+ rc = smb1398_read(chip, SWITCHER_OFF_FAULT_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ irev = !!(val & DIV2_IREV_LATCH);
+ tsd = !!(val & TEMP_SHDWN);
+
+ *status = smb_en << 7 | vin_ov << 6 | vin_uv << 5
+ | irev << 3 | tsd << 2 | switcher_off;
+
+ dev_dbg(chip->dev, "status2 = 0x%x\n", *status);
+ return rc;
+}
+
+static int smb1398_div2_cp_get_irq_status(
+ struct smb1398_chip *chip, u8 *status)
+{
+ int rc = 0;
+ u8 val;
+ bool ilim, irev, tsd, off_vin, off_win;
+
+ rc = smb1398_read(chip, PERPH1_INT_RT_STS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ ilim = !!(val & DIV2_ILIM_STS);
+ off_win = !!(val & (DIV2_WIN_OV_STS | DIV2_WIN_UV_STS));
+
+ rc = smb1398_read(chip, PERPH0_INT_RT_STS_REG, &val);
+ if (rc < 0)
+ return rc;
+
+ irev = !!(val & DIV2_IREV_LATCH_STS);
+ tsd = !!(val & TEMP_SHUTDOWN_STS);
+ off_vin = !!(val & (USB_IN_OVLO_STS | USB_IN_UVLO_STS));
+
+ *status = ilim << 6 | irev << 3 | tsd << 2 | off_vin << 1 | off_win;
+
+ dev_dbg(chip->dev, "irq_status = 0x%x\n", *status);
+ return rc;
+}
+
+static int smb1398_div2_cp_switcher_en(struct smb1398_chip *chip, bool en)
+{
+ int rc;
+
+ rc = smb1398_masked_write(chip, MISC_SL_SWITCH_EN_REG,
+ EN_SWITCHER, en ? EN_SWITCHER : 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't write SWITCH_EN_REG, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->switcher_en = en;
+
+ dev_dbg(chip->dev, "%s switcher\n", en ? "enable" : "disable");
+ return rc;
+}
+
+static int smb1398_div2_cp_isns_mode_control(
+ struct smb1398_chip *chip, enum isns_mode mode)
+{
+ int rc = 0;
+ u8 mux_sel;
+
+ switch (mode) {
+ case ISNS_MODE_STANDBY:
+ /* VTEMP */
+ mux_sel = SEL_OUT_VTEMP;
+ break;
+ case ISNS_MODE_OFF:
+ /* High-Z */
+ mux_sel = SEL_OUT_HIGHZ;
+ break;
+ case ISNS_MODE_ACTIVE:
+ /* IIN_FB */
+ mux_sel = SEL_OUT_IIN_FB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = smb1398_masked_write(chip, SSUPLY_TEMP_CTRL_REG,
+ SEL_OUT_TEMP_MAX_MASK, mux_sel);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set SSUPLY_TEMP_CTRL_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1398_masked_write(chip, PERPH0_MISC_CFG2_REG,
+ CFG_TEMP_PIN_ITEMP, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set PERPH0_MISC_CFG2_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static inline int calculate_div2_cp_isns_ua(int temp)
+{
+ /* ISNS = (2850 + (0.0034 * thermal_reading) / 0.32) * 1000 uA */
+ return (2850 * 1000 + div_s64((s64)temp * 340, 32));
+}
+
+static bool is_cps_available(struct smb1398_chip *chip)
+{
+ if (chip->div2_cp_slave_psy)
+ return true;
+
+ chip->div2_cp_slave_psy = power_supply_get_by_name("cp_slave");
+ if (chip->div2_cp_slave_psy)
+ return true;
+
+ return false;
+}
+
+static int smb1398_div2_cp_get_master_isns(
+ struct smb1398_chip *chip, int *isns_ua)
+{
+ union power_supply_propval pval = {0};
+ int rc = 0, temp;
+
+ rc = smb1398_get_enable_status(chip);
+ if (rc < 0)
+ return rc;
+
+ if (!chip->smb_en)
+ return -ENODATA;
+
+ /*
+ * Follow this procedure to read master CP ISNS:
+ * set slave CP TEMP_MUX to HighZ;
+ * set master CP TEMP_MUX to IIN_FB;
+ * read corresponding ADC channel in Kekaha;
+ * set master CP TEMP_MUX to VTEMP;
+ */
+ mutex_lock(&chip->die_chan_lock);
+ if (is_cps_available(chip)) {
+ pval.intval = ISNS_MODE_OFF;
+ rc = power_supply_set_property(chip->div2_cp_slave_psy,
+ POWER_SUPPLY_PROP_CURRENT_CAPABILITY, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set slave ISNS_MODE_OFF, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+ }
+
+ rc = smb1398_div2_cp_isns_mode_control(chip, ISNS_MODE_ACTIVE);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set master ISNS_MODE_ACTIVE, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+
+ rc = iio_read_channel_processed(chip->die_temp_chan, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read die_temp_chan, rc=%d\n", rc);
+ goto unlock;
+ }
+
+ rc = smb1398_div2_cp_isns_mode_control(chip, ISNS_MODE_STANDBY);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set master ISNS_MODE_STANDBY, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&chip->die_chan_lock);
+ if (rc >= 0) {
+ *isns_ua = calculate_div2_cp_isns_ua(temp);
+ dev_dbg(chip->dev, "master isns = %duA\n", *isns_ua);
+ }
+
+ return rc;
+}
+
+static int smb1398_div2_cp_get_slave_isns(
+ struct smb1398_chip *chip, int *isns_ua)
+{
+ union power_supply_propval pval = {0};
+ int temp = 0, rc;
+
+ if (!is_cps_available(chip)) {
+ *isns_ua = 0;
+ return 0;
+ }
+
+ rc = smb1398_get_enable_status(chip);
+ if (rc < 0)
+ return rc;
+
+ if (!chip->smb_en || !chip->slave_en)
+ return -ENODATA;
+
+ /*
+ * Follow this procedure to read slave CP ISNS:
+ * set master CP TEMP_MUX to HighZ;
+ * set slave CP TEMP_MUX to IIN_FB;
+ * read corresponding ADC channel in Kekaha;
+ * set master CP TEMP_MUX to VTEMP;
+ */
+ mutex_lock(&chip->die_chan_lock);
+ rc = smb1398_div2_cp_isns_mode_control(chip, ISNS_MODE_OFF);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set master ISNS_MODE_OFF, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+
+ pval.intval = ISNS_MODE_ACTIVE;
+ rc = power_supply_set_property(chip->div2_cp_slave_psy,
+ POWER_SUPPLY_PROP_CURRENT_CAPABILITY, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set slave ISNS_MODE_ACTIVE, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+
+ rc = iio_read_channel_processed(chip->die_temp_chan, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get die_temp_chan, rc=%d\n", rc);
+ goto unlock;
+ }
+
+ rc = smb1398_div2_cp_isns_mode_control(chip, ISNS_MODE_STANDBY);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set master ISNS_MODE_STANDBY, rc=%d\n",
+ rc);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&chip->die_chan_lock);
+
+ if (rc >= 0) {
+ *isns_ua = calculate_div2_cp_isns_ua(temp);
+ dev_dbg(chip->dev, "slave isns = %duA\n", *isns_ua);
+ }
+
+ return rc;
+}
+
+static void smb1398_toggle_switcher(struct smb1398_chip *chip)
+{
+ int rc = 0;
+
+ /*
+ * Disable DIV2_ILIM detection before toggling the switcher
+ * to prevent any ILIM interrupt storm while the toggling
+ */
+ rc = smb1398_masked_write(chip, DIV2_CURRENT_REG, DIV2_EN_ILIM_DET, 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't disable EN_ILIM_DET, rc=%d\n", rc);
+
+ vote(chip->div2_cp_disable_votable, SWITCHER_TOGGLE_VOTER, true, 0);
+
+ /* Delay for toggling switcher */
+ usleep_range(20, 30);
+ vote(chip->div2_cp_disable_votable, SWITCHER_TOGGLE_VOTER, false, 0);
+
+ rc = smb1398_masked_write(chip, DIV2_CURRENT_REG,
+ DIV2_EN_ILIM_DET, DIV2_EN_ILIM_DET);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't disable EN_ILIM_DET, rc=%d\n", rc);
+}
+
+static enum power_supply_property div2_cp_master_props[] = {
+ POWER_SUPPLY_PROP_CP_STATUS1,
+ POWER_SUPPLY_PROP_CP_STATUS2,
+ POWER_SUPPLY_PROP_CP_ENABLE,
+ POWER_SUPPLY_PROP_CP_SWITCHER_EN,
+ POWER_SUPPLY_PROP_CP_DIE_TEMP,
+ POWER_SUPPLY_PROP_CP_ISNS,
+ POWER_SUPPLY_PROP_CP_ISNS_SLAVE,
+ POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER,
+ POWER_SUPPLY_PROP_CP_IRQ_STATUS,
+ POWER_SUPPLY_PROP_CP_ILIM,
+ POWER_SUPPLY_PROP_CHIP_VERSION,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_PARALLEL_MODE,
+ POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE,
+ POWER_SUPPLY_PROP_MIN_ICL,
+};
+
+static int div2_cp_master_get_prop_suspended(struct smb1398_chip *chip,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_STATUS1:
+ val->intval = chip->cp_status1;
+ break;
+ case POWER_SUPPLY_PROP_CP_STATUS2:
+ val->intval = chip->cp_status2;
+ break;
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ val->intval = chip->cp_enable;
+ break;
+ case POWER_SUPPLY_PROP_CP_SWITCHER_EN:
+ val->intval = chip->switcher_en;
+ break;
+ case POWER_SUPPLY_PROP_CP_DIE_TEMP:
+ val->intval = chip->die_temp;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS:
+ val->intval = chip->cp_isns_master;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS_SLAVE:
+ val->intval = chip->cp_isns_slave;
+ break;
+ case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
+ val->intval = chip->div2_irq_status;
+ break;
+ case POWER_SUPPLY_PROP_CP_ILIM:
+ val->intval = chip->cp_ilim;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int div2_cp_master_get_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0, ilim_ma, temp, isns_ua;
+ u8 status;
+
+ /*
+ * Return the cached values when the system is in suspend state
+ * instead of reading the registers to avoid read failures.
+ */
+ if (chip->in_suspend) {
+ rc = div2_cp_master_get_prop_suspended(chip, prop, val);
+ if (!rc)
+ return rc;
+ rc = 0;
+ }
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_STATUS1:
+ rc = smb1398_div2_cp_get_status1(chip, &status);
+ if (!rc)
+ chip->cp_status1 = val->intval = status;
+ break;
+ case POWER_SUPPLY_PROP_CP_STATUS2:
+ rc = smb1398_div2_cp_get_status2(chip, &status);
+ if (!rc)
+ chip->cp_status2 = val->intval = status;
+ break;
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ rc = smb1398_get_enable_status(chip);
+ if (!rc)
+ chip->cp_enable = val->intval = chip->smb_en &&
+ !get_effective_result(
+ chip->div2_cp_disable_votable);
+ break;
+ case POWER_SUPPLY_PROP_CP_SWITCHER_EN:
+ rc = smb1398_get_enable_status(chip);
+ if (!rc)
+ val->intval = chip->switcher_en;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS:
+ rc = smb1398_div2_cp_get_master_isns(chip, &isns_ua);
+ if (rc >= 0)
+ chip->cp_isns_master = val->intval = isns_ua;
+ break;
+ case POWER_SUPPLY_PROP_CP_ISNS_SLAVE:
+ rc = smb1398_div2_cp_get_slave_isns(chip, &isns_ua);
+ if (rc >= 0)
+ chip->cp_isns_slave = val->intval = isns_ua;
+ break;
+ case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CP_DIE_TEMP:
+ rc = smb1398_get_die_temp(chip, &temp);
+ if (rc >= 0) {
+ val->intval = temp;
+ if (temp <= THERMAL_SUSPEND_DECIDEGC)
+ chip->die_temp = temp;
+ else if (chip->die_temp == -ENODATA)
+ rc = -ENODATA;
+ else
+ val->intval = chip->die_temp;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
+ val->intval = chip->div2_irq_status;
+ rc = smb1398_div2_cp_get_irq_status(chip, &status);
+ if (!rc)
+ val->intval |= status;
+ break;
+ case POWER_SUPPLY_PROP_CP_ILIM:
+ if (is_cps_available(chip)) {
+ if (chip->div2_cp_ilim_votable)
+ val->intval = get_effective_result(
+ chip->div2_cp_ilim_votable);
+ } else {
+ rc = smb1398_get_iin_ma(chip, &ilim_ma);
+ if (!rc)
+ val->intval = (ilim_ma * 1000 * 100)
+ / DIV2_ILIM_CFG_PCT;
+ }
+ chip->cp_ilim = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CHIP_VERSION:
+ val->intval = chip->pmic_rev_id->rev4;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = (chip->pmic_rev_id->rev4 > 1) ? "SMB1398_V2" :
+ "SMB1398_V1";
+ break;
+ case POWER_SUPPLY_PROP_PARALLEL_MODE:
+ val->intval = chip->pl_input_mode;
+ break;
+ case POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE:
+ val->intval = chip->pl_output_mode;
+ break;
+ case POWER_SUPPLY_PROP_MIN_ICL:
+ val->intval = chip->div2_cp_min_ilim_ua;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int div2_cp_master_set_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ vote(chip->div2_cp_disable_votable,
+ USER_VOTER, !val->intval, 0);
+ break;
+ case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
+ if (!!val->intval)
+ smb1398_toggle_switcher(chip);
+ break;
+ case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
+ chip->div2_irq_status = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CP_ILIM:
+ if (chip->div2_cp_ilim_votable)
+ vote_override(chip->div2_cp_ilim_votable, CC_MODE_VOTER,
+ (val->intval > 0), val->intval);
+ break;
+ default:
+ dev_err(chip->dev, "setprop %d is not supported\n", prop);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int div2_cp_master_prop_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
+ case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
+ case POWER_SUPPLY_PROP_CP_ILIM:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct power_supply_desc div2_cp_master_desc = {
+ .name = "charge_pump_master",
+ .type = POWER_SUPPLY_TYPE_CHARGE_PUMP,
+ .properties = div2_cp_master_props,
+ .num_properties = ARRAY_SIZE(div2_cp_master_props),
+ .get_property = div2_cp_master_get_prop,
+ .set_property = div2_cp_master_set_prop,
+ .property_is_writeable = div2_cp_master_prop_is_writeable,
+};
+
+static int smb1398_init_div2_cp_master_psy(struct smb1398_chip *chip)
+{
+ struct power_supply_config div2_cp_master_psy_cfg = {};
+ int rc = 0;
+
+ div2_cp_master_psy_cfg.drv_data = chip;
+ div2_cp_master_psy_cfg.of_node = chip->dev->of_node;
+
+ chip->div2_cp_master_psy = devm_power_supply_register(chip->dev,
+ &div2_cp_master_desc, &div2_cp_master_psy_cfg);
+ if (IS_ERR(chip->div2_cp_master_psy)) {
+ rc = PTR_ERR(chip->div2_cp_master_psy);
+ dev_err(chip->dev, "Register div2_cp_master power supply failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static bool is_psy_voter_available(struct smb1398_chip *chip)
+{
+ if (!chip->batt_psy) {
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (!chip->batt_psy) {
+ dev_dbg(chip->dev, "Couldn't find battery psy\n");
+ return false;
+ }
+ }
+
+ if (!chip->usb_psy) {
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy) {
+ dev_dbg(chip->dev, "Couldn't find USB psy\n");
+ return false;
+ }
+ }
+
+ if (!chip->dc_psy) {
+ chip->dc_psy = power_supply_get_by_name("dc");
+ if (!chip->dc_psy) {
+ dev_dbg(chip->dev, "Couldn't find DC psy\n");
+ return false;
+ }
+ }
+
+ if (!chip->fcc_votable) {
+ chip->fcc_votable = find_votable("FCC");
+ if (!chip->fcc_votable) {
+ dev_dbg(chip->dev, "Couldn't find FCC voltable\n");
+ return false;
+ }
+ }
+
+ if (!chip->fv_votable) {
+ chip->fv_votable = find_votable("FV");
+ if (!chip->fv_votable) {
+ dev_dbg(chip->dev, "Couldn't find FV voltable\n");
+ return false;
+ }
+ }
+
+ if (!chip->usb_icl_votable) {
+ chip->usb_icl_votable = find_votable("USB_ICL");
+ if (!chip->usb_icl_votable) {
+ dev_dbg(chip->dev, "Couldn't find USB_ICL voltable\n");
+ return false;
+ }
+ }
+
+ if (!chip->fcc_main_votable) {
+ chip->fcc_main_votable = find_votable("FCC_MAIN");
+ if (!chip->fcc_main_votable) {
+ dev_dbg(chip->dev, "Couldn't find FCC_MAIN voltable\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool is_cutoff_soc_reached(struct smb1398_chip *chip)
+{
+ int rc;
+ union power_supply_propval pval = {0};
+
+ if (!chip->batt_psy)
+ goto err;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CAPACITY, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get battery soc, rc=%d\n", rc);
+ goto err;
+ }
+
+ if (pval.intval >= chip->max_cutoff_soc)
+ return true;
+err:
+ return false;
+}
+
+static bool is_adapter_in_cc_mode(struct smb1398_chip *chip)
+{
+ int rc;
+ union power_supply_propval pval = {0};
+
+ if (!chip->usb_psy) {
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy)
+ return false;
+ }
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_ADAPTER_CC_MODE,
+ &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get ADAPTER_CC_MODE, rc=%d\n");
+ return rc;
+ }
+
+ return !!pval.intval;
+}
+
+static int smb1398_awake_vote_cb(struct votable *votable,
+ void *data, int awake, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ return 0;
+}
+
+static int smb1398_div2_cp_disable_vote_cb(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+ int rc = 0;
+
+ if (!is_psy_voter_available(chip) || chip->in_suspend)
+ return -EAGAIN;
+
+ rc = smb1398_div2_cp_switcher_en(chip, !disable);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s switcher failed, rc=%d\n",
+ !!disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ if (is_cps_available(chip))
+ vote(chip->div2_cp_slave_disable_votable, MAIN_DISABLE_VOTER,
+ !!disable ? true : false, 0);
+
+ if (chip->div2_cp_master_psy && (disable != chip->disabled))
+ power_supply_changed(chip->div2_cp_master_psy);
+
+ chip->disabled = disable;
+ return 0;
+}
+
+static int smb1398_div2_cp_slave_disable_vote_cb(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+ union power_supply_propval pval = {0};
+ u16 reg;
+ u8 val;
+ int rc, ilim_ua;
+
+ if (!is_cps_available(chip))
+ return -ENODEV;
+
+ /* Enable/disable SYNC driver before enabling/disabling slave */
+ reg = MISC_CFG0_REG;
+ val = !!disable ? DIS_SYNC_DRV_BIT : 0;
+ rc = smb1398_masked_write(chip, reg, DIS_SYNC_DRV_BIT, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s slave SYNC_DRV failed, rc=%d\n",
+ !!disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ reg = MISC_SL_SWITCH_EN_REG;
+ val = !!disable ? 0 : EN_SLAVE;
+ rc = smb1398_masked_write(chip, reg, EN_SLAVE, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't write slave_en, rc=%d\n", rc);
+ return rc;
+ }
+
+ pval.intval = !disable;
+ rc = power_supply_set_property(chip->div2_cp_slave_psy,
+ POWER_SUPPLY_PROP_CP_ENABLE, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s slave switcher failed, rc=%d\n",
+ !!disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ /* Re-distribute ILIM to Master CP when Slave is disabled */
+ if (disable && (chip->div2_cp_ilim_votable)) {
+ ilim_ua = get_effective_result_locked(
+ chip->div2_cp_ilim_votable);
+
+ ilim_ua = (ilim_ua * DIV2_ILIM_CFG_PCT) / 100;
+
+ if (ilim_ua > DIV2_MAX_ILIM_UA)
+ ilim_ua = DIV2_MAX_ILIM_UA;
+
+ rc = smb1398_set_iin_ma(chip, ilim_ua / 1000);
+ if (rc < 0) {
+ dev_err(chip->dev, "Could't set CP master ilim, rc=%d\n",
+ rc);
+ return rc;
+ }
+ dev_dbg(chip->dev, "slave disabled, restore master CP ilim to %duA\n",
+ ilim_ua);
+ }
+
+ return rc;
+}
+
+static int smb1398_div2_cp_ilim_vote_cb(struct votable *votable,
+ void *data, int ilim_ua, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+ union power_supply_propval pval = {0};
+ int rc = 0, max_ilim_ua;
+ bool slave_dis, split_ilim = false;
+
+ if (!is_psy_voter_available(chip) || chip->in_suspend)
+ return -EAGAIN;
+
+ if (!client)
+ return -EINVAL;
+
+ ilim_ua = (ilim_ua * DIV2_ILIM_CFG_PCT) / 100;
+
+ max_ilim_ua = is_cps_available(chip) ?
+ DIV2_MAX_ILIM_DUAL_CP_UA : DIV2_MAX_ILIM_UA;
+ ilim_ua = min(ilim_ua, max_ilim_ua);
+ if (ilim_ua < chip->div2_cp_min_ilim_ua) {
+ dev_dbg(chip->dev, "ilim %duA is too low to config CP charging\n",
+ ilim_ua);
+ vote(chip->div2_cp_disable_votable, ILIM_VOTER, true, 0);
+ } else {
+ if (is_cps_available(chip)) {
+ split_ilim = true;
+ slave_dis = ilim_ua < (2 * chip->div2_cp_min_ilim_ua);
+ vote(chip->div2_cp_slave_disable_votable, ILIM_VOTER,
+ slave_dis, 0);
+ slave_dis = !!get_effective_result(
+ chip->div2_cp_slave_disable_votable);
+ if (slave_dis)
+ split_ilim = false;
+ }
+
+ if (split_ilim) {
+ ilim_ua /= 2;
+ pval.intval = ilim_ua;
+ rc = power_supply_set_property(chip->div2_cp_slave_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, &pval);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set CP slave ilim, rc=%d\n",
+ rc);
+ dev_dbg(chip->dev, "set CP slave ilim to %duA\n",
+ ilim_ua);
+ }
+
+ rc = smb1398_set_iin_ma(chip, ilim_ua / 1000);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CP master ilim, rc=%d\n",
+ rc);
+ return rc;
+ }
+ dev_dbg(chip->dev, "set CP master ilim to %duA\n", ilim_ua);
+ vote(chip->div2_cp_disable_votable, ILIM_VOTER, false, 0);
+ }
+
+ return 0;
+}
+
+static void smb1398_destroy_votables(struct smb1398_chip *chip)
+{
+ destroy_votable(chip->awake_votable);
+ destroy_votable(chip->div2_cp_disable_votable);
+ destroy_votable(chip->div2_cp_ilim_votable);
+ destroy_votable(chip->div2_cp_slave_disable_votable);
+}
+
+static int smb1398_div2_cp_create_votables(struct smb1398_chip *chip)
+{
+ int rc;
+
+ chip->awake_votable = create_votable("SMB1398_AWAKE",
+ VOTE_SET_ANY, smb1398_awake_vote_cb, chip);
+ if (IS_ERR_OR_NULL(chip->awake_votable))
+ return PTR_ERR_OR_ZERO(chip->awake_votable);
+
+ chip->div2_cp_disable_votable = create_votable("CP_DISABLE",
+ VOTE_SET_ANY, smb1398_div2_cp_disable_vote_cb, chip);
+ if (IS_ERR_OR_NULL(chip->div2_cp_disable_votable)) {
+ rc = PTR_ERR_OR_ZERO(chip->div2_cp_disable_votable);
+ goto destroy;
+ }
+
+ chip->div2_cp_slave_disable_votable = create_votable("CP_SLAVE_DISABLE",
+ VOTE_SET_ANY, smb1398_div2_cp_slave_disable_vote_cb,
+ chip);
+ if (IS_ERR_OR_NULL(chip->div2_cp_slave_disable_votable)) {
+ rc = PTR_ERR_OR_ZERO(chip->div2_cp_slave_disable_votable);
+ goto destroy;
+ }
+
+ chip->div2_cp_ilim_votable = create_votable("CP_ILIM",
+ VOTE_MIN, smb1398_div2_cp_ilim_vote_cb, chip);
+ if (IS_ERR_OR_NULL(chip->div2_cp_ilim_votable)) {
+ rc = PTR_ERR_OR_ZERO(chip->div2_cp_ilim_votable);
+ goto destroy;
+ }
+
+ vote(chip->div2_cp_disable_votable, USER_VOTER, true, 0);
+ vote(chip->div2_cp_disable_votable, CUTOFF_SOC_VOTER,
+ is_cutoff_soc_reached(chip), 0);
+
+ /*
+ * In case SMB1398 probe happens after FCC value has been configured,
+ * update ilim vote to reflect FCC / 2 value, this is only applicable
+ * when SMB1398 is directly connected to VBAT.
+ */
+ if (is_psy_voter_available(chip) &&
+ (chip->pl_output_mode != POWER_SUPPLY_PL_OUTPUT_VPH))
+ vote(chip->div2_cp_ilim_votable, FCC_VOTER, true,
+ get_effective_result(chip->fcc_votable) / 2);
+ return 0;
+destroy:
+ smb1398_destroy_votables(chip);
+
+ return 0;
+}
+
+static irqreturn_t default_irq_handler(int irq, void *data)
+{
+ struct smb1398_chip *chip = data;
+ int rc, i;
+ bool switcher_en = chip->switcher_en;
+
+ for (i = 0; i < NUM_IRQS; i++) {
+ if (irq == chip->irqs[i]) {
+ dev_dbg(chip->dev, "IRQ %s triggered\n",
+ smb_irqs[i].name);
+ chip->div2_irq_status |= 1 << smb_irqs[i].shift;
+ }
+ }
+
+ rc = smb1398_get_enable_status(chip);
+ if (rc < 0)
+ goto out;
+
+ if (chip->switcher_en != switcher_en)
+ if (chip->fcc_votable)
+ rerun_election(chip->fcc_votable);
+out:
+ if (chip->div2_cp_master_psy)
+ power_supply_changed(chip->div2_cp_master_psy);
+ return IRQ_HANDLED;
+}
+
+static const struct smb_irq smb_irqs[] = {
+ /* useful IRQs from perph0 */
+ [TEMP_SHDWN_IRQ] = {
+ .name = "temp-shdwn",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 2,
+ },
+ [DIV2_IREV_LATCH_IRQ] = {
+ .name = "div2-irev",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 3,
+ },
+ [USB_IN_UVLO_IRQ] = {
+ .name = "usbin-uv",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 1,
+ },
+ [USB_IN_OVLO_IRQ] = {
+ .name = "usbin-ov",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 1,
+ },
+ /* useful IRQs from perph1 */
+ [DIV2_ILIM_IRQ] = {
+ .name = "div2-ilim",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 6,
+ },
+ [DIV2_WIN_UV_IRQ] = {
+ .name = "div2-win-uv",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 0,
+ },
+ [DIV2_WIN_OV_IRQ] = {
+ .name = "div2-win-ov",
+ .handler = default_irq_handler,
+ .wake = true,
+ .shift = 0,
+ },
+};
+
+static int smb1398_get_irq_index_byname(const char *irq_name)
+{
+ int i;
+
+ for (i = 0; i < NUM_IRQS; i++) {
+ if (smb_irqs[i].name != NULL)
+ if (strcmp(smb_irqs[i].name, irq_name) == 0)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+static int smb1398_request_interrupt(struct smb1398_chip *chip,
+ struct device_node *node, const char *irq_name)
+{
+ int rc = 0, irq, irq_index;
+
+ irq = of_irq_get_byname(node, irq_name);
+ if (irq < 0) {
+ dev_err(chip->dev, "Couldn't get irq %s failed\n", irq_name);
+ return irq;
+ }
+
+ irq_index = smb1398_get_irq_index_byname(irq_name);
+ if (irq_index < 0) {
+ dev_err(chip->dev, "%s IRQ is not defined\n", irq_name);
+ return irq_index;
+ }
+
+ if (!smb_irqs[irq_index].handler)
+ return 0;
+
+ /*
+ * Do not register temp-shdwn interrupt as it may misfire on toggling
+ * the SMB_EN input.
+ */
+ if (irq_index == TEMP_SHDWN_IRQ)
+ return 0;
+
+ rc = devm_request_threaded_irq(chip->dev, irq, NULL,
+ smb_irqs[irq_index].handler,
+ IRQF_ONESHOT, irq_name, chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Request interrupt for %s failed, rc=%d\n",
+ irq_name, rc);
+ return rc;
+ }
+
+ chip->irqs[irq_index] = irq;
+ if (smb_irqs[irq_index].wake)
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+static int smb1398_request_interrupts(struct smb1398_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ int rc = 0;
+ const char *name;
+ struct property *prop;
+
+ of_property_for_each_string(node, "interrupt-names", prop, name) {
+ rc = smb1398_request_interrupt(chip, node, name);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+#define ILIM_NR 10
+#define ILIM_DR 8
+#define ILIM_FACTOR(ilim) ((ilim * ILIM_NR) / ILIM_DR)
+
+static void smb1398_configure_ilim(struct smb1398_chip *chip, int mode)
+{
+ int rc;
+ union power_supply_propval pval = {0, };
+
+ /* PPS adapter reply on the current advertised by the adapter */
+ if ((chip->pl_output_mode == POWER_SUPPLY_PL_OUTPUT_VPH)
+ && (mode == POWER_SUPPLY_CP_PPS)) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &pval);
+ if (rc < 0)
+ pr_err("Couldn't get PD CURRENT MAX rc=%d\n", rc);
+ else
+ vote(chip->div2_cp_ilim_votable, ICL_VOTER,
+ true, ILIM_FACTOR(pval.intval));
+ }
+
+ /* QC3.0/Wireless adapter rely on the settled AICL for USBMID_USBMID */
+ if ((chip->pl_input_mode == POWER_SUPPLY_PL_USBMID_USBMID)
+ && (mode == POWER_SUPPLY_CP_HVDCP3)) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval);
+ if (rc < 0)
+ pr_err("Couldn't get usb aicl rc=%d\n", rc);
+ else
+ vote(chip->div2_cp_ilim_votable, ICL_VOTER,
+ true, pval.intval);
+ }
+}
+
+static void smb1398_status_change_work(struct work_struct *work)
+{
+ struct smb1398_chip *chip = container_of(work,
+ struct smb1398_chip, status_change_work);
+ union power_supply_propval pval = {0};
+ int rc;
+
+ if (!is_psy_voter_available(chip))
+ goto out;
+ /*
+ * If batt soc is not valid upon bootup, but becomes
+ * valid due to the battery discharging later, remove
+ * vote from CUTOFF_SOC_VOTER.
+ */
+ if (is_cutoff_soc_reached(chip))
+ vote(chip->div2_cp_disable_votable, CUTOFF_SOC_VOTER, false, 0);
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_SMB_EN_MODE, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get SMB_EN_MODE, rc=%d\n", rc);
+ goto out;
+ }
+
+ /* If no CP charging started */
+ if (pval.intval != POWER_SUPPLY_CHARGER_SEC_CP) {
+ chip->cutoff_soc_checked = false;
+ vote(chip->div2_cp_slave_disable_votable, SRC_VOTER, true, 0);
+ vote(chip->div2_cp_slave_disable_votable,
+ TAPER_VOTER, false, 0);
+ vote(chip->div2_cp_disable_votable, TAPER_VOTER, false, 0);
+ vote(chip->div2_cp_disable_votable, SRC_VOTER, true, 0);
+ vote(chip->div2_cp_disable_votable, CUTOFF_SOC_VOTER, true, 0);
+ vote(chip->fcc_votable, CP_VOTER, false, 0);
+ vote(chip->div2_cp_ilim_votable, CC_MODE_VOTER, false, 0);
+ vote_override(chip->usb_icl_votable,
+ TAPER_MAIN_ICL_LIMIT_VOTER, false, 0);
+ goto out;
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_SMB_EN_REASON, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get SMB_EN_REASON failed, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ /*
+ * Slave SMB1398 is not required for the power-rating of QC3
+ */
+ if (pval.intval != POWER_SUPPLY_CP_HVDCP3)
+ vote(chip->div2_cp_slave_disable_votable, SRC_VOTER, false, 0);
+
+ if (pval.intval == POWER_SUPPLY_CP_NONE) {
+ vote(chip->div2_cp_disable_votable, SRC_VOTER, true, 0);
+ goto out;
+ }
+
+ vote(chip->div2_cp_disable_votable, SRC_VOTER, false, 0);
+ if (!chip->cutoff_soc_checked) {
+ vote(chip->div2_cp_disable_votable, CUTOFF_SOC_VOTER,
+ is_cutoff_soc_reached(chip), 0);
+ chip->cutoff_soc_checked = true;
+ }
+
+ if (pval.intval == POWER_SUPPLY_CP_WIRELESS) {
+ /*
+ * Get the max output current from the wireless PSY
+ * and set the DIV2 CP ilim accordingly
+ */
+ vote(chip->div2_cp_ilim_votable, ICL_VOTER, false, 0);
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't get DC CURRENT_MAX, rc=%d\n",
+ rc);
+ else
+ vote(chip->div2_cp_ilim_votable, WIRELESS_VOTER,
+ true, pval.intval);
+ } else {
+ vote(chip->div2_cp_ilim_votable, WIRELESS_VOTER, false, 0);
+ smb1398_configure_ilim(chip, pval.intval);
+ }
+
+ /*
+ * Remove CP Taper condition disable vote if float voltage
+ * increased in comparison to voltage at which it entered taper.
+ */
+ if (chip->taper_entry_fv < get_effective_result(chip->fv_votable)) {
+ vote(chip->div2_cp_slave_disable_votable,
+ TAPER_VOTER, false, 0);
+ vote(chip->div2_cp_disable_votable, TAPER_VOTER, false, 0);
+ }
+
+ /*
+ * all votes that would result in disabling the charge pump have
+ * been cast; ensure the charge pump is still enabled before
+ * continuing.
+ */
+ if (get_effective_result(chip->div2_cp_disable_votable))
+ goto out;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get CHARGE_TYPE, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
+ if (!chip->taper_work_running) {
+ chip->taper_work_running = true;
+ vote(chip->awake_votable, TAPER_VOTER, true, 0);
+ queue_work(system_long_wq, &chip->taper_work);
+ }
+ }
+out:
+ pm_relax(chip->dev);
+ chip->status_change_running = false;
+}
+
+static int smb1398_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct smb1398_chip *chip = container_of(nb, struct smb1398_chip, nb);
+ struct power_supply *psy = (struct power_supply *)data;
+ unsigned long flags;
+
+ if (event != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if (strcmp(psy->desc->name, "battery") == 0 ||
+ strcmp(psy->desc->name, "usb") == 0 ||
+ strcmp(psy->desc->name, "main") == 0 ||
+ strcmp(psy->desc->name, "cp_slave") == 0) {
+ spin_lock_irqsave(&chip->status_change_lock, flags);
+ if (!chip->status_change_running) {
+ chip->status_change_running = true;
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->status_change_work);
+ }
+ spin_unlock_irqrestore(&chip->status_change_lock, flags);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void smb1398_taper_work(struct work_struct *work)
+{
+ struct smb1398_chip *chip = container_of(work,
+ struct smb1398_chip, taper_work);
+ union power_supply_propval pval = {0};
+ int rc, fcc_ua, fv_uv, stepper_ua, main_fcc_ua;
+ bool slave_en;
+
+ if (!is_psy_voter_available(chip))
+ goto out;
+
+ if (!chip->fcc_main_votable)
+ chip->fcc_main_votable = find_votable("FCC_MAIN");
+
+ if (chip->fcc_main_votable)
+ main_fcc_ua = get_effective_result(chip->fcc_main_votable);
+
+ chip->taper_entry_fv = get_effective_result(chip->fv_votable);
+ while (true) {
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't get CHARGE_TYPE, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ fv_uv = get_effective_result(chip->fv_votable);
+ if (fv_uv > chip->taper_entry_fv) {
+ dev_dbg(chip->dev, "Float voltage increased (%d-->%d)uV, exit!\n",
+ chip->taper_entry_fv, fv_uv);
+ vote(chip->div2_cp_disable_votable, TAPER_VOTER,
+ false, 0);
+ goto out;
+ } else {
+ chip->taper_entry_fv = fv_uv;
+ }
+
+ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
+ stepper_ua = is_adapter_in_cc_mode(chip) ?
+ TAPER_STEPPER_UA_IN_CC_MODE :
+ TAPER_STEPPER_UA_DEFAULT;
+ fcc_ua = get_effective_result(chip->fcc_votable)
+ - stepper_ua;
+ dev_dbg(chip->dev, "Taper stepper reduce FCC to %d\n",
+ fcc_ua);
+ vote(chip->fcc_votable, CP_VOTER, true, fcc_ua);
+ fcc_ua -= main_fcc_ua;
+ /*
+ * If total FCC is less than the minimum ILIM to
+ * keep CP master and slave online, disable CP.
+ */
+ if (fcc_ua < (chip->div2_cp_min_ilim_ua * 2)) {
+ vote(chip->div2_cp_disable_votable,
+ TAPER_VOTER, true, 0);
+ /*
+ * When master CP is disabled, reset all votes
+ * on ICL to enable Main charger to pump
+ * charging current.
+ */
+ if (chip->usb_icl_votable)
+ vote_override(chip->usb_icl_votable,
+ TAPER_MAIN_ICL_LIMIT_VOTER,
+ false, 0);
+ goto out;
+ }
+ /*
+ * If total FCC is less than the minimum ILIM to keep
+ * slave CP online, disable slave, and set master CP
+ * ILIM to maximum to avoid ILIM IRQ storm.
+ */
+ slave_en = !get_effective_result(
+ chip->div2_cp_slave_disable_votable);
+ if ((fcc_ua < chip->ilim_ua_disable_div2_cp_slave) &&
+ slave_en && is_cps_available(chip)) {
+ dev_dbg(chip->dev, "Disable slave CP in taper\n");
+ vote(chip->div2_cp_slave_disable_votable,
+ TAPER_VOTER, true, 0);
+ vote_override(chip->div2_cp_ilim_votable,
+ CC_MODE_VOTER,
+ is_adapter_in_cc_mode(chip),
+ DIV2_MAX_ILIM_DUAL_CP_UA);
+
+ if (chip->usb_icl_votable)
+ vote_override(chip->usb_icl_votable,
+ TAPER_MAIN_ICL_LIMIT_VOTER,
+ is_adapter_in_cc_mode(chip),
+ chip->cc_mode_taper_main_icl_ua);
+ }
+ } else {
+ dev_dbg(chip->dev, "Not in taper, exit!\n");
+ }
+ msleep(500);
+ }
+out:
+ dev_dbg(chip->dev, "exit taper work\n");
+ vote(chip->fcc_votable, CP_VOTER, false, 0);
+ vote(chip->awake_votable, TAPER_VOTER, false, 0);
+ chip->taper_work_running = false;
+}
+
+static int smb1398_div2_cp_hw_init(struct smb1398_chip *chip)
+{
+ int rc = 0;
+
+ /* Configure window (Vin/2 - Vout) OV level to 500mV */
+ rc = smb1398_masked_write(chip, DIV2_PROTECTION_REG,
+ DIV2_WIN_OV_SEL_MASK, WIN_OV_500_MV);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set WIN_OV_500_MV rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Configure master TEMP pin to output Vtemp signal by default */
+ rc = smb1398_masked_write(chip, SSUPLY_TEMP_CTRL_REG,
+ SEL_OUT_TEMP_MAX_MASK, SEL_OUT_VTEMP);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set SSUPLY_TEMP_CTRL_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Configure to use Vtemp signal */
+ rc = smb1398_masked_write(chip, PERPH0_MISC_CFG2_REG,
+ CFG_TEMP_PIN_ITEMP, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set PERPH0_MISC_CFG2_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Configure IREV threshold to 200mA */
+ rc = smb1398_masked_write(chip, PERPH0_DIV2_REF_CFG,
+ CFG_IREV_REF_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't configure IREV threshold rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Initial configuration needed before enabling DIV2_CP operations */
+ rc = smb1398_masked_write(chip, MISC_DIV2_3LVL_CTRL_REG,
+ MISC_DIV2_3LVL_CTRL_MASK, 0x04);
+ if (rc < 0) {
+ dev_err(chip->dev, "set EN_DIV2_CP failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1398_masked_write(chip, MISC_CFG1_REG, MISC_CFG1_MASK, 0x02);
+ if (rc < 0) {
+ dev_err(chip->dev, "set OP_MODE_COMBO failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* switcher enable controlled by register */
+ rc = smb1398_masked_write(chip, MISC_CFG0_REG,
+ SW_EN_SWITCHER_BIT, SW_EN_SWITCHER_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_EN_SOURCE, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1398_div2_cp_parse_dt(struct smb1398_chip *chip)
+{
+ int rc = 0;
+
+ rc = of_property_match_string(chip->dev->of_node,
+ "io-channel-names", "die_temp");
+ if (rc < 0) {
+ dev_err(chip->dev, "die_temp IIO channel not found\n");
+ return rc;
+ }
+
+ chip->die_temp_chan = devm_iio_channel_get(chip->dev,
+ "die_temp");
+ if (IS_ERR(chip->die_temp_chan)) {
+ rc = PTR_ERR(chip->die_temp_chan);
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Couldn't get die_temp_chan, rc=%d\n",
+ rc);
+ chip->die_temp_chan = NULL;
+ return rc;
+ }
+
+ chip->div2_cp_min_ilim_ua = 1000000;
+ of_property_read_u32(chip->dev->of_node, "qcom,div2-cp-min-ilim-ua",
+ &chip->div2_cp_min_ilim_ua);
+
+ chip->max_cutoff_soc = 85;
+ of_property_read_u32(chip->dev->of_node, "qcom,max-cutoff-soc",
+ &chip->max_cutoff_soc);
+
+ chip->ilim_ua_disable_div2_cp_slave = chip->div2_cp_min_ilim_ua * 3;
+
+ of_property_read_u32(chip->dev->of_node, "qcom,ilim-ua-disable-slave",
+ &chip->ilim_ua_disable_div2_cp_slave);
+
+ chip->cc_mode_taper_main_icl_ua = CC_MODE_TAPER_MAIN_ICL_UA;
+ of_property_read_u32(chip->dev->of_node,
+ "qcom,cc-mode-taper-main-icl-ua",
+ &chip->cc_mode_taper_main_icl_ua);
+
+ /* Default parallel output configuration is VPH connection */
+ chip->pl_output_mode = POWER_SUPPLY_PL_OUTPUT_VPH;
+ of_property_read_u32(chip->dev->of_node, "qcom,parallel-output-mode",
+ &chip->pl_output_mode);
+
+ /* Default parallel input configuration is USBMID connection */
+ chip->pl_input_mode = POWER_SUPPLY_PL_USBMID_USBMID;
+ of_property_read_u32(chip->dev->of_node, "qcom,parallel-input-mode",
+ &chip->pl_input_mode);
+
+ return 0;
+}
+
+static int smb1398_div2_cp_master_probe(struct smb1398_chip *chip)
+{
+ int rc;
+ struct device_node *revid_dev_node;
+ struct pmic_revid_data *pmic_rev_id;
+
+ revid_dev_node = of_parse_phandle(chip->dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Couldn't get revid node\n");
+ return -EINVAL;
+ }
+
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ of_node_put(revid_dev_node);
+
+ if (IS_ERR_OR_NULL(pmic_rev_id)) {
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ chip->pmic_rev_id = pmic_rev_id;
+ spin_lock_init(&chip->status_change_lock);
+ mutex_init(&chip->die_chan_lock);
+
+ rc = smb1398_div2_cp_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't parse devicetree, rc=%d\n", rc);
+ return rc;
+ }
+
+ INIT_WORK(&chip->status_change_work, &smb1398_status_change_work);
+ INIT_WORK(&chip->taper_work, &smb1398_taper_work);
+
+ rc = smb1398_div2_cp_hw_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "div2_cp_hw_init failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1398_div2_cp_create_votables(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "smb1398_div2_cp_create_votables failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1398_init_div2_cp_master_psy(chip);
+ if (rc > 0) {
+ dev_err(chip->dev, "smb1398_init_div2_cp_master_psy failed, rc=%d\n",
+ rc);
+ goto destroy_votable;
+ }
+
+ chip->nb.notifier_call = smb1398_notifier_cb;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ dev_err(chip->dev, "register notifier_cb failed, rc=%d\n", rc);
+ goto destroy_votable;
+ }
+
+ rc = smb1398_request_interrupts(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "smb1398_request_interrupts failed, rc=%d\n",
+ rc);
+ goto destroy_votable;
+ }
+
+ rc = device_init_wakeup(chip->dev, true);
+ if (rc < 0) {
+ dev_err(chip->dev, "init wakeup failed for div2_cp_master device, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(chip->dev, "smb1398 DIV2_CP master is probed successfully\n");
+
+ return 0;
+destroy_votable:
+ mutex_destroy(&chip->die_chan_lock);
+ smb1398_destroy_votables(chip);
+
+ return rc;
+}
+
+static enum power_supply_property div2_cp_slave_props[] = {
+ POWER_SUPPLY_PROP_CP_ENABLE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
+};
+
+static int div2_cp_slave_get_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *pval)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ pval->intval = chip->switcher_en;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ pval->intval = 0;
+ if (!chip->div2_cp_ilim_votable)
+ chip->div2_cp_ilim_votable = find_votable("CP_ILIM");
+ if (chip->div2_cp_ilim_votable)
+ pval->intval = get_effective_result_locked(
+ chip->div2_cp_ilim_votable);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
+ pval->intval = (int)chip->current_capability;
+ break;
+ default:
+ dev_err(chip->dev, "read div2_cp_slave property %d is not supported\n",
+ prop);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int div2_cp_slave_set_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *pval)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+ int ilim_ma, rc = 0;
+ enum isns_mode mode;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ rc = smb1398_div2_cp_switcher_en(chip, !!pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ ilim_ma = pval->intval / 1000;
+ rc = smb1398_set_iin_ma(chip, ilim_ma);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
+ mode = (enum isns_mode)pval->intval;
+ rc = smb1398_div2_cp_isns_mode_control(chip, mode);
+ if (rc < 0)
+ return rc;
+ chip->current_capability = mode;
+ break;
+ default:
+ dev_err(chip->dev, "write div2_cp_slave property %d is not supported\n",
+ prop);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int div2_cp_slave_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CP_ENABLE:
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct power_supply_desc div2_cps_psy_desc = {
+ .name = "cp_slave",
+ .type = POWER_SUPPLY_TYPE_PARALLEL,
+ .properties = div2_cp_slave_props,
+ .num_properties = ARRAY_SIZE(div2_cp_slave_props),
+ .get_property = div2_cp_slave_get_prop,
+ .set_property = div2_cp_slave_set_prop,
+ .property_is_writeable = div2_cp_slave_is_writeable,
+};
+
+static int smb1398_init_div2_cp_slave_psy(struct smb1398_chip *chip)
+{
+ int rc = 0;
+ struct power_supply_config cps_cfg = {};
+
+ cps_cfg.drv_data = chip;
+ cps_cfg.of_node = chip->dev->of_node;
+
+ chip->div2_cp_slave_psy = devm_power_supply_register(chip->dev,
+ &div2_cps_psy_desc, &cps_cfg);
+ if (IS_ERR(chip->div2_cp_slave_psy)) {
+ rc = PTR_ERR(chip->div2_cp_slave_psy);
+ dev_err(chip->dev, "register div2_cp_slave_psy failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int smb1398_div2_cp_slave_probe(struct smb1398_chip *chip)
+{
+ int rc;
+ u8 status;
+
+ rc = smb1398_read(chip, MODE_STATUS_REG, &status);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read slave MODE_STATUS_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /*
+ * Disable slave WIN_UV detection, otherwise slave might not be
+ * enabled due to WIN_UV until master drawing very high current.
+ */
+ rc = smb1398_masked_write(chip, PERPH0_CFG_SDCDC_REG, EN_WIN_UV_BIT, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable DIV2_CP WIN_UV, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Configure slave TEMP pin to HIGH-Z by default */
+ rc = smb1398_masked_write(chip, SSUPLY_TEMP_CTRL_REG,
+ SEL_OUT_TEMP_MAX_MASK, SEL_OUT_HIGHZ);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set SSUPLY_TEMP_CTRL_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Configure to use Vtemp */
+ rc = smb1398_masked_write(chip, PERPH0_MISC_CFG2_REG,
+ CFG_TEMP_PIN_ITEMP, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set PERPH0_MISC_CFG2_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* switcher enable controlled by register */
+ rc = smb1398_masked_write(chip, MISC_CFG0_REG,
+ SW_EN_SWITCHER_BIT, SW_EN_SWITCHER_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set MISC_CFG0_REG, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1398_init_div2_cp_slave_psy(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Initial div2_cp_slave_psy failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(chip->dev, "smb1398 DIV2_CP slave probe successfully\n");
+
+ return 0;
+}
+
+static int smb1398_pre_regulator_iout_vote_cb(struct votable *votable,
+ void *data, int iout_ua, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+ int rc = 0;
+
+ if (chip->in_suspend)
+ return -EAGAIN;
+
+ if (!client)
+ return -EINVAL;
+
+ iout_ua = min(iout_ua, MAX_IOUT_UA);
+ rc = smb1398_set_ichg_ma(chip, do_div(iout_ua, 1000));
+ if (rc < 0)
+ return rc;
+
+ dev_dbg(chip->dev, "set iout %duA\n", iout_ua);
+ return 0;
+}
+
+static int smb1398_pre_regulator_vout_vote_cb(struct votable *votable,
+ void *data, int vout_uv, const char *client)
+{
+ struct smb1398_chip *chip = (struct smb1398_chip *)data;
+ int rc = 0;
+
+ if (chip->in_suspend)
+ return -EAGAIN;
+
+ if (!client)
+ return -EINVAL;
+
+ vout_uv = min(vout_uv, MAX_1S_VOUT_UV);
+ rc = smb1398_set_1s_vout_mv(chip, vout_uv / 1000);
+ if (rc < 0)
+ return rc;
+
+ dev_dbg(chip->dev, "set vout %duV\n", vout_uv);
+ return 0;
+}
+
+static int smb1398_create_pre_regulator_votables(struct smb1398_chip *chip)
+{
+ chip->pre_regulator_iout_votable = create_votable("PRE_REGULATOR_IOUT",
+ VOTE_MIN, smb1398_pre_regulator_iout_vote_cb, chip);
+ if (IS_ERR_OR_NULL(chip->pre_regulator_iout_votable))
+ return PTR_ERR_OR_ZERO(chip->pre_regulator_iout_votable);
+
+ chip->pre_regulator_vout_votable = create_votable("PRE_REGULATOR_VOUT",
+ VOTE_MIN, smb1398_pre_regulator_vout_vote_cb, chip);
+
+ if (IS_ERR_OR_NULL(chip->pre_regulator_vout_votable)) {
+ destroy_votable(chip->pre_regulator_iout_votable);
+ return PTR_ERR_OR_ZERO(chip->pre_regulator_vout_votable);
+ }
+
+ return 0;
+}
+
+static enum power_supply_property pre_regulator_props[] = {
+ POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+};
+
+static int pre_regulator_get_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *pval)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+ int rc, iin_ma, iout_ma, vout_mv;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ rc = smb1398_get_iin_ma(chip, &iin_ma);
+ if (rc < 0)
+ return rc;
+ pval->intval = iin_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ if (chip->pre_regulator_iout_votable) {
+ pval->intval = get_effective_result(
+ chip->pre_regulator_iout_votable);
+ } else {
+ rc = smb1398_get_ichg_ma(chip, &iout_ma);
+ if (rc < 0)
+ return rc;
+ pval->intval = iout_ma * 1000;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ if (chip->pre_regulator_vout_votable) {
+ pval->intval = get_effective_result(
+ chip->pre_regulator_vout_votable);
+ } else {
+ rc = smb1398_get_1s_vout_mv(chip, &vout_mv);
+ if (rc < 0)
+ return rc;
+ pval->intval = vout_mv * 1000;
+ }
+ break;
+ default:
+ dev_err(chip->dev, "read pre_regulator property %d is not supported\n",
+ prop);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pre_regulator_set_prop(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *pval)
+{
+ struct smb1398_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ rc = smb1398_set_iin_ma(chip, pval->intval / 1000);
+ if (rc < 0)
+ return rc;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ vote(chip->pre_regulator_iout_votable, CP_VOTER,
+ true, pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ vote(chip->pre_regulator_vout_votable, CP_VOTER,
+ true, pval->intval);
+ break;
+ default:
+ dev_err(chip->dev, "write pre_regulator property %d is not supported\n",
+ prop);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int pre_regulator_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct power_supply_desc pre_regulator_psy_desc = {
+ .name = "pre_regulator",
+ .type = POWER_SUPPLY_TYPE_WIRELESS,
+ .properties = pre_regulator_props,
+ .num_properties = ARRAY_SIZE(pre_regulator_props),
+ .get_property = pre_regulator_get_prop,
+ .set_property = pre_regulator_set_prop,
+ .property_is_writeable = pre_regulator_is_writeable,
+};
+
+static int smb1398_create_pre_regulator_psy(struct smb1398_chip *chip)
+{
+ struct power_supply_config pre_regulator_psy_cfg = {};
+ int rc = 0;
+
+ pre_regulator_psy_cfg.drv_data = chip;
+ pre_regulator_psy_cfg.of_node = chip->dev->of_node;
+
+ chip->pre_regulator_psy = devm_power_supply_register(chip->dev,
+ &pre_regulator_psy_desc,
+ &pre_regulator_psy_cfg);
+ if (IS_ERR(chip->pre_regulator_psy)) {
+ rc = PTR_ERR(chip->pre_regulator_psy);
+ dev_err(chip->dev, "register pre_regulator psy failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int smb1398_pre_regulator_probe(struct smb1398_chip *chip)
+{
+ int rc = 0;
+
+ rc = smb1398_create_pre_regulator_votables(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Create votable for pre_regulator failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1398_create_pre_regulator_psy(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Create pre-regulator failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+
+}
+
+static int smb1398_probe(struct platform_device *pdev)
+{
+ struct smb1398_chip *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->die_temp = -ENODATA;
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Get regmap failed\n");
+ return -EINVAL;
+ }
+ chip->disabled = true;
+ platform_set_drvdata(pdev, chip);
+
+ chip->div2_cp_role = (int)of_device_get_match_data(chip->dev);
+ switch (chip->div2_cp_role) {
+ case DIV2_CP_MASTER:
+ rc = smb1398_div2_cp_master_probe(chip);
+ break;
+ case DIV2_CP_SLAVE:
+ rc = smb1398_div2_cp_slave_probe(chip);
+ break;
+ case COMBO_PRE_REGULATOR:
+ rc = smb1398_pre_regulator_probe(chip);
+ break;
+ default:
+ dev_err(chip->dev, "Couldn't find a match role for %d\n",
+ chip->div2_cp_role);
+ goto cleanup;
+ }
+
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Couldn't probe SMB1398 %s rc= %d\n",
+ !!chip->div2_cp_role ? "slave" : "master", rc);
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ platform_set_drvdata(pdev, NULL);
+ return rc;
+}
+
+static int smb1398_remove(struct platform_device *pdev)
+{
+ struct smb1398_chip *chip = platform_get_drvdata(pdev);
+
+ if (chip->div2_cp_role == DIV2_CP_MASTER) {
+ vote(chip->awake_votable, SHUTDOWN_VOTER, false, 0);
+ vote(chip->div2_cp_disable_votable, SHUTDOWN_VOTER, true, 0);
+ vote(chip->div2_cp_ilim_votable, SHUTDOWN_VOTER, true, 0);
+ cancel_work_sync(&chip->taper_work);
+ cancel_work_sync(&chip->status_change_work);
+ mutex_destroy(&chip->die_chan_lock);
+ smb1398_destroy_votables(chip);
+ }
+
+ return 0;
+}
+
+static int smb1398_suspend(struct device *dev)
+{
+ struct smb1398_chip *chip = dev_get_drvdata(dev);
+
+ chip->in_suspend = true;
+ return 0;
+}
+
+static int smb1398_resume(struct device *dev)
+{
+ struct smb1398_chip *chip = dev_get_drvdata(dev);
+
+ chip->in_suspend = false;
+
+ if (chip->div2_cp_role == DIV2_CP_MASTER) {
+ rerun_election(chip->div2_cp_ilim_votable);
+ rerun_election(chip->div2_cp_disable_votable);
+ }
+
+ return 0;
+}
+
+static void smb1398_shutdown(struct platform_device *pdev)
+{
+ struct smb1398_chip *chip = platform_get_drvdata(pdev);
+ int rc;
+
+ power_supply_unreg_notifier(&chip->nb);
+
+ /* Disable SMB1398 */
+ rc = smb1398_div2_cp_switcher_en(chip, 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't disable chip rc= %d\n", rc);
+}
+
+static const struct dev_pm_ops smb1398_pm_ops = {
+ .suspend = smb1398_suspend,
+ .resume = smb1398_resume,
+};
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "qcom,smb1396-div2-cp-master",
+ .data = (void *)DIV2_CP_MASTER,
+ },
+ { .compatible = "qcom,smb1396-div2-cp-slave",
+ .data = (void *)DIV2_CP_SLAVE,
+ },
+ { .compatible = "qcom,smb1398-pre-regulator",
+ .data = (void *)COMBO_PRE_REGULATOR,
+ },
+ {
+ },
+};
+
+static struct platform_driver smb1398_driver = {
+ .driver = {
+ .name = "qcom,smb1398-charger",
+ .pm = &smb1398_pm_ops,
+ .of_match_table = match_table,
+ },
+ .probe = smb1398_probe,
+ .remove = smb1398_remove,
+ .shutdown = smb1398_shutdown,
+};
+module_platform_driver(smb1398_driver);
+
+MODULE_DESCRIPTION("SMB1398 charger driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 0080717..a6329ca 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -915,7 +915,8 @@ int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua)
* - Output connection topology is VBAT
*/
if (!is_cp_topo_vbatt(chg) || chg->hvdcp3_standalone_config
- || (chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3))
+ || ((chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3)
+ && chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3P5))
return -EINVAL;
rc = power_supply_get_property(chg->cp_psy, POWER_SUPPLY_PROP_CP_ENABLE,
@@ -1068,7 +1069,7 @@ static int smblib_request_dpdm(struct smb_charger *chg, bool enable)
return rc;
}
-static void smblib_rerun_apsd(struct smb_charger *chg)
+void smblib_rerun_apsd(struct smb_charger *chg)
{
int rc;
@@ -1087,6 +1088,8 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
/* if PD is active, APSD is disabled so won't have a valid result */
if (chg->pd_active) {
chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD;
+ } else if (chg->qc3p5_detected) {
+ chg->real_charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5;
} else {
/*
* Update real charger type only if its not FLOAT
@@ -1097,8 +1100,8 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
chg->real_charger_type = apsd_result->pst;
}
- smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
- apsd_result->name, chg->pd_active);
+ smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d QC3P5=%d\n",
+ apsd_result->name, chg->pd_active, chg->qc3p5_detected);
return apsd_result;
}
@@ -1243,6 +1246,9 @@ static void smblib_uusb_removal(struct smb_charger *chg)
chg->uusb_apsd_rerun_done = false;
chg->chg_param.forced_main_fcc = 0;
+ del_timer_sync(&chg->apsd_timer);
+ chg->apsd_ext_timeout = false;
+
/* write back the default FLOAT charger configuration */
rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
(u8)FLOAT_OPTIONS_MASK, chg->float_cfg);
@@ -1282,6 +1288,9 @@ static void smblib_uusb_removal(struct smb_charger *chg)
chg->qc2_unsupported_voltage = QC2_COMPLIANT;
}
+
+ chg->qc3p5_detected = false;
+ smblib_update_usb_type(chg);
}
void smblib_suspend_on_debug_battery(struct smb_charger *chg)
@@ -2533,7 +2542,8 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg)
vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
}
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3
+ || chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) {
rc = smblib_hvdcp3_set_fsw(chg);
if (rc < 0)
smblib_err(chg, "Couldn't set QC3.0 Fsw rc=%d\n", rc);
@@ -2666,6 +2676,10 @@ int smblib_dp_dm(struct smb_charger *chg, int val)
if (rc < 0)
pr_err("Failed to force 12V\n");
break;
+ case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5:
+ chg->qc3p5_detected = true;
+ smblib_update_usb_type(chg);
+ break;
case POWER_SUPPLY_DP_DM_ICL_UP:
default:
break;
@@ -3278,6 +3292,7 @@ int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg,
break;
}
/* else, fallthrough */
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
case POWER_SUPPLY_TYPE_USB_PD:
if (chg->chg_param.smb_version == PMI632_SUBTYPE)
@@ -3307,6 +3322,7 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
break;
}
/* else, fallthrough */
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
if (chg->chg_param.smb_version == PMI632_SUBTYPE)
val->intval = MICRO_9V;
@@ -3325,15 +3341,20 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
}
#define HVDCP3_STEP_UV 200000
+#define HVDCP3P5_STEP_UV 20000
static int smblib_estimate_adaptor_voltage(struct smb_charger *chg,
union power_supply_propval *val)
{
+ int step_uv = HVDCP3_STEP_UV;
+
switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP:
val->intval = MICRO_12V;
break;
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
+ step_uv = HVDCP3P5_STEP_UV;
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
- val->intval = MICRO_5V + (HVDCP3_STEP_UV * chg->pulse_cnt);
+ val->intval = MICRO_5V + (step_uv * chg->pulse_cnt);
break;
case POWER_SUPPLY_TYPE_USB_PD:
/* Take the average of min and max values */
@@ -3890,8 +3911,11 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
union power_supply_propval *val)
{
int rc, pulses;
+ int step_uv = HVDCP3_STEP_UV;
switch (chg->real_charger_type) {
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
+ step_uv = HVDCP3P5_STEP_UV;
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
@@ -3899,7 +3923,7 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
"Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
return 0;
}
- val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses;
+ val->intval = MICRO_5V + step_uv * pulses;
break;
case POWER_SUPPLY_TYPE_USB_PD:
val->intval = chg->voltage_min_uv;
@@ -3929,7 +3953,6 @@ int smblib_get_pe_start(struct smb_charger *chg,
int smblib_get_prop_smb_health(struct smb_charger *chg)
{
int rc;
- u8 stat;
int input_present;
union power_supply_propval prop = {0, };
@@ -3937,50 +3960,21 @@ int smblib_get_prop_smb_health(struct smb_charger *chg)
if (rc < 0)
return rc;
- if (input_present == INPUT_NOT_PRESENT)
+ if ((input_present == INPUT_NOT_PRESENT) || (!is_cp_available(chg)))
return POWER_SUPPLY_HEALTH_UNKNOWN;
- /*
- * SMB health is used only for CP, report UNKNOWN if
- * switcher is not enabled.
- */
- if (is_cp_available(chg)) {
- rc = power_supply_get_property(chg->cp_psy,
- POWER_SUPPLY_PROP_CP_SWITCHER_EN, &prop);
- if (!rc && !prop.intval)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
+ rc = power_supply_get_property(chg->cp_psy,
+ POWER_SUPPLY_PROP_CP_DIE_TEMP, &prop);
+ if (rc < 0)
+ return rc;
- if (chg->wa_flags & SW_THERM_REGULATION_WA) {
- if (chg->smb_temp == -ENODATA)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
-
- if (chg->smb_temp > SMB_TEMP_RST_THRESH)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
-
- if (chg->smb_temp > SMB_TEMP_REG_H_THRESH)
- return POWER_SUPPLY_HEALTH_HOT;
-
- if (chg->smb_temp > SMB_TEMP_REG_L_THRESH)
- return POWER_SUPPLY_HEALTH_WARM;
-
- return POWER_SUPPLY_HEALTH_COOL;
- }
-
- rc = smblib_read(chg, SMB_TEMP_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SMB_TEMP_STATUS_REG, rc=%d\n",
- rc);
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
-
- if (stat & SMB_TEMP_RST_BIT)
+ if (prop.intval > SMB_TEMP_RST_THRESH)
return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (stat & SMB_TEMP_UB_BIT)
+ if (prop.intval > SMB_TEMP_REG_H_THRESH)
return POWER_SUPPLY_HEALTH_HOT;
- if (stat & SMB_TEMP_LB_BIT)
+ if (prop.intval > SMB_TEMP_REG_L_THRESH)
return POWER_SUPPLY_HEALTH_WARM;
return POWER_SUPPLY_HEALTH_COOL;
@@ -5032,6 +5026,7 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status)
if ((batt_status == TERMINATE_CHARGE) && (pval.intval == 100)) {
chg->cc_soc_ref = 0;
chg->last_cc_soc = 0;
+ chg->term_vbat_uv = 0;
alarm_start_relative(&chg->chg_termination_alarm,
ms_to_ktime(CHG_TERM_WA_ENTRY_DELAY_MS));
} else if (pval.intval < 100) {
@@ -5041,6 +5036,7 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status)
*/
chg->cc_soc_ref = 0;
chg->last_cc_soc = 0;
+ chg->term_vbat_uv = 0;
}
}
@@ -5479,6 +5475,7 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+#define APSD_EXTENDED_TIMEOUT_MS 400
/* triggers when HVDCP 3.0 authentication has finished */
static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
bool rising)
@@ -5495,13 +5492,29 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
/* the APSD done handler will set the USB supply type */
apsd_result = smblib_get_apsd_result(chg);
- /* for QC3, switch to CP if present */
- if ((apsd_result->bit & QC_3P0_BIT) && chg->sec_cp_present) {
- rc = smblib_select_sec_charger(chg, POWER_SUPPLY_CHARGER_SEC_CP,
- POWER_SUPPLY_CP_HVDCP3, false);
- if (rc < 0)
- dev_err(chg->dev,
- "Couldn't enable secondary chargers rc=%d\n", rc);
+ if (apsd_result->bit & QC_3P0_BIT) {
+ /* for QC3, switch to CP if present */
+ if (chg->sec_cp_present) {
+ rc = smblib_select_sec_charger(chg,
+ POWER_SUPPLY_CHARGER_SEC_CP,
+ POWER_SUPPLY_CP_HVDCP3, false);
+ if (rc < 0)
+ dev_err(chg->dev,
+ "Couldn't enable secondary chargers rc=%d\n",
+ rc);
+ }
+
+ /* QC3.5 detection timeout */
+ if (!chg->apsd_ext_timeout &&
+ !timer_pending(&chg->apsd_timer)) {
+ smblib_dbg(chg, PR_MISC,
+ "APSD Extented timer started at %lld\n",
+ jiffies_to_msecs(jiffies));
+
+ mod_timer(&chg->apsd_timer,
+ msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS)
+ + jiffies);
+ }
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
@@ -5511,16 +5524,23 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
bool rising, bool qc_charger)
{
+ u32 hvdcp_ua = 0;
+
if (rising) {
if (qc_charger) {
+ hvdcp_ua = (chg->real_charger_type ==
+ POWER_SUPPLY_TYPE_USB_HVDCP) ?
+ chg->chg_param.hvdcp2_max_icl_ua :
+ HVDCP_CURRENT_UA;
+
/* enable HDC and ICL irq for QC2/3 charger */
vote(chg->limited_irq_disable_votable,
CHARGER_TYPE_VOTER, false, 0);
vote(chg->hdc_irq_disable_votable,
CHARGER_TYPE_VOTER, false, 0);
vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- HVDCP_CURRENT_UA);
+ hvdcp_ua);
} else {
/* A plain DCP, enforce DCP ICL if specified */
vote(chg->usb_icl_votable, DCP_VOTER,
@@ -5853,6 +5873,228 @@ static void typec_ra_ra_insertion(struct smb_charger *chg)
smblib_hvdcp_detect_enable(chg, true);
}
+static int typec_partner_register(struct smb_charger *chg)
+{
+ int typec_mode, rc = 0;
+
+ mutex_lock(&chg->typec_lock);
+
+ if (!chg->typec_port || chg->pr_swap_in_progress)
+ goto unlock;
+
+ if (!chg->typec_partner) {
+ if (chg->sink_src_mode == AUDIO_ACCESS_MODE)
+ chg->typec_partner_desc.accessory =
+ TYPEC_ACCESSORY_AUDIO;
+ else
+ chg->typec_partner_desc.accessory =
+ TYPEC_ACCESSORY_NONE;
+
+ chg->typec_partner = typec_register_partner(chg->typec_port,
+ &chg->typec_partner_desc);
+ if (IS_ERR(chg->typec_partner)) {
+ rc = PTR_ERR(chg->typec_partner);
+ pr_err("failed to register typec_partner rc=%d\n", rc);
+ goto unlock;
+ }
+ }
+
+ typec_mode = smblib_get_prop_typec_mode(chg);
+
+ if (typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
+ || typec_mode == POWER_SUPPLY_TYPEC_NONE) {
+ typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
+ typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
+ } else {
+ typec_set_data_role(chg->typec_port, TYPEC_HOST);
+ typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
+ }
+
+unlock:
+ mutex_unlock(&chg->typec_lock);
+ return rc;
+}
+
+static void typec_partner_unregister(struct smb_charger *chg)
+{
+ mutex_lock(&chg->typec_lock);
+
+ if (chg->typec_partner && !chg->pr_swap_in_progress) {
+ smblib_dbg(chg, PR_MISC, "Un-registering typeC partner\n");
+ typec_unregister_partner(chg->typec_partner);
+ chg->typec_partner = NULL;
+ }
+
+ mutex_unlock(&chg->typec_lock);
+}
+
+static const char * const dr_mode_text[] = {
+ "ufp", "dfp", "none"
+};
+
+static int smblib_force_dr_mode(struct smb_charger *chg, int mode)
+{
+ int rc = 0;
+
+ switch (mode) {
+ case TYPEC_PORT_SNK:
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case TYPEC_PORT_SRC:
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, EN_SRC_ONLY_BIT);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't enable src, rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case TYPEC_PORT_DRP:
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't enable DRP, rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ default:
+ smblib_err(chg, "Power role %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ chg->dr_mode = mode;
+
+ return rc;
+}
+
+int smblib_typec_port_type_set(const struct typec_capability *cap,
+ enum typec_port_type type)
+{
+ struct smb_charger *chg = container_of(cap,
+ struct smb_charger, typec_caps);
+ int rc = 0;
+
+ mutex_lock(&chg->typec_lock);
+
+ if ((chg->pr_swap_in_progress) || (type == TYPEC_PORT_DRP)) {
+ smblib_dbg(chg, PR_MISC, "Ignoring port type request type = %d swap_in_progress = %d\n",
+ type, chg->pr_swap_in_progress);
+ goto unlock;
+ }
+
+ chg->pr_swap_in_progress = true;
+
+ rc = smblib_force_dr_mode(chg, type);
+ if (rc < 0) {
+ chg->pr_swap_in_progress = false;
+ smblib_err(chg, "Failed to force mode, rc=%d\n", rc);
+ goto unlock;
+ }
+
+ smblib_dbg(chg, PR_MISC, "Requested role %s\n",
+ type ? "SINK" : "SOURCE");
+
+ /*
+ * As per the hardware requirements,
+ * schedule the work with required delay.
+ */
+ if (!(delayed_work_pending(&chg->role_reversal_check))) {
+ cancel_delayed_work_sync(&chg->role_reversal_check);
+ schedule_delayed_work(&chg->role_reversal_check,
+ msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS));
+ vote(chg->awake_votable, TYPEC_SWAP_VOTER, true, 0);
+ }
+
+unlock:
+ mutex_unlock(&chg->typec_lock);
+ return rc;
+}
+
+static int smblib_role_switch_failure(struct smb_charger *chg, int mode)
+{
+ int rc = 0;
+ union power_supply_propval pval = {0, };
+
+ if (!chg->use_extcon)
+ return 0;
+
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get usb presence status rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /*
+ * When role switch fails notify the
+ * current charger state to usb driver.
+ */
+ if (pval.intval && mode == TYPEC_PORT_SRC) {
+ smblib_dbg(chg, PR_MISC, " Role reversal failed, notifying device mode to usb driver.\n");
+ smblib_notify_device_mode(chg, true);
+ }
+
+ return rc;
+}
+
+static void smblib_typec_role_check_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ role_reversal_check.work);
+ int rc = 0;
+
+ mutex_lock(&chg->typec_lock);
+
+ switch (chg->dr_mode) {
+ case TYPEC_PORT_SNK:
+ if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
+ smblib_dbg(chg, PR_MISC, "Role reversal not latched to UFP in %d msecs. Resetting to DRP mode\n",
+ ROLE_REVERSAL_DELAY_MS);
+ rc = smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
+ if (rc < 0)
+ smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
+ rc);
+ } else {
+ chg->power_role = POWER_SUPPLY_TYPEC_PR_SINK;
+ typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
+ typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
+ smblib_dbg(chg, PR_MISC, "Role changed successfully to SINK");
+ }
+ break;
+ case TYPEC_PORT_SRC:
+ if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
+ || chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
+ smblib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n",
+ ROLE_REVERSAL_DELAY_MS);
+ rc = smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
+ if (rc < 0)
+ smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
+ rc);
+ rc = smblib_role_switch_failure(chg, TYPEC_PORT_SRC);
+ if (rc < 0)
+ smblib_err(chg, "Failed to role switch rc=%d\n",
+ rc);
+ } else {
+ chg->power_role = POWER_SUPPLY_TYPEC_PR_SOURCE;
+ typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
+ typec_set_data_role(chg->typec_port, TYPEC_HOST);
+ smblib_dbg(chg, PR_MISC, "Role changed successfully to SOURCE");
+ }
+ break;
+ default:
+ pr_debug("Already in DRP mode\n");
+ break;
+ }
+
+ chg->pr_swap_in_progress = false;
+ vote(chg->awake_votable, TYPEC_SWAP_VOTER, false, 0);
+ mutex_unlock(&chg->typec_lock);
+}
+
static void typec_sink_removal(struct smb_charger *chg)
{
int rc;
@@ -5868,6 +6110,8 @@ static void typec_sink_removal(struct smb_charger *chg)
smblib_notify_usb_host(chg, false);
chg->otg_present = false;
}
+
+ typec_partner_unregister(chg);
}
static void typec_src_removal(struct smb_charger *chg)
@@ -5886,6 +6130,7 @@ static void typec_src_removal(struct smb_charger *chg)
dev_err(chg->dev,
"Couldn't disable secondary charger rc=%d\n", rc);
+ chg->qc3p5_detected = false;
typec_src_fault_condition_cfg(chg, false);
smblib_hvdcp_detect_try_enable(chg, false);
smblib_update_usb_type(chg);
@@ -6002,7 +6247,11 @@ static void typec_src_removal(struct smb_charger *chg)
if (chg->use_extcon)
smblib_notify_device_mode(chg, false);
+ typec_partner_unregister(chg);
chg->typec_legacy = false;
+
+ del_timer_sync(&chg->apsd_timer);
+ chg->apsd_ext_timeout = false;
}
static void typec_mode_unattached(struct smb_charger *chg)
@@ -6184,6 +6433,10 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
typec_src_insertion(chg);
}
+ rc = typec_partner_register(chg);
+ if (rc < 0)
+ smblib_err(chg, "failed to register partner rc =%d\n",
+ rc);
} else {
switch (chg->sink_src_mode) {
case SRC_MODE:
@@ -6206,6 +6459,18 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
smblib_apsd_enable(chg, true);
}
+ /*
+ * Restore DRP mode on type-C cable disconnect if role
+ * swap is not in progress, to ensure forced sink or src
+ * mode configuration is reset properly.
+ */
+ mutex_lock(&chg->typec_lock);
+
+ if (chg->typec_port && !chg->pr_swap_in_progress)
+ smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
+
+ mutex_unlock(&chg->typec_lock);
+
if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
schedule_delayed_work(&chg->lpd_detach_work,
msecs_to_jiffies(1000));
@@ -7073,6 +7338,7 @@ static void smblib_chg_termination_work(struct work_struct *work)
struct smb_charger *chg = container_of(work, struct smb_charger,
chg_termination_work);
int rc, input_present, delay = CHG_TERM_WA_ENTRY_DELAY_MS;
+ int vbat_now_uv, max_fv_uv;
/*
* Hold awake votable to prevent pm_relax being called prior to
@@ -7092,6 +7358,14 @@ static void smblib_chg_termination_work(struct work_struct *work)
goto out;
}
+ /* Get the battery float voltage */
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ &pval);
+ if (rc < 0)
+ goto out;
+
+ max_fv_uv = pval.intval;
+
rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CHARGE_FULL,
&pval);
if (rc < 0)
@@ -7105,6 +7379,20 @@ static void smblib_chg_termination_work(struct work_struct *work)
*/
if (pval.intval != chg->charge_full_cc || !chg->cc_soc_ref) {
chg->charge_full_cc = pval.intval;
+
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval);
+ if (rc < 0)
+ goto out;
+
+ /*
+ * Store the Vbat at the charge termination to compare with
+ * the current voltage to see if the Vbat is increasing after
+ * charge termination in BSM.
+ */
+ chg->term_vbat_uv = pval.intval;
+ vbat_now_uv = pval.intval;
+
rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CC_SOC,
&pval);
if (rc < 0)
@@ -7112,6 +7400,13 @@ static void smblib_chg_termination_work(struct work_struct *work)
chg->cc_soc_ref = pval.intval;
} else {
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval);
+ if (rc < 0)
+ goto out;
+
+ vbat_now_uv = pval.intval;
+
rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CC_SOC,
&pval);
if (rc < 0)
@@ -7132,15 +7427,18 @@ static void smblib_chg_termination_work(struct work_struct *work)
/*
* Suspend/Unsuspend USB input to keep cc_soc within the 0.5% to 0.75%
- * overshoot range of the cc_soc value at termination, to prevent
- * overcharging.
+ * overshoot range of the cc_soc value at termination and make sure that
+ * vbat is indeed rising above vfloat.
*/
if (pval.intval < DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10050, 10000)) {
vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0);
delay = CHG_TERM_WA_ENTRY_DELAY_MS;
- } else if (pval.intval > DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10075,
- 10000)) {
+ } else if ((pval.intval > DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10075,
+ 10000))
+ && ((vbat_now_uv > chg->term_vbat_uv) &&
+ (vbat_now_uv > max_fv_uv))) {
+
if (input_present & INPUT_PRESENT_USB)
vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER,
true, 0);
@@ -7150,8 +7448,9 @@ static void smblib_chg_termination_work(struct work_struct *work)
delay = CHG_TERM_WA_EXIT_DELAY_MS;
}
- smblib_dbg(chg, PR_MISC, "Chg Term WA readings: cc_soc: %d, cc_soc_ref: %d, delay: %d\n",
- pval.intval, chg->cc_soc_ref, delay);
+ smblib_dbg(chg, PR_MISC, "Chg Term WA readings: cc_soc: %d, cc_soc_ref: %d, delay: %d vbat_now %d term_vbat %d\n",
+ pval.intval, chg->cc_soc_ref, delay, vbat_now_uv,
+ chg->term_vbat_uv);
alarm_start_relative(&chg->chg_termination_alarm, ms_to_ktime(delay));
out:
vote(chg->awake_votable, CHG_TERMINATION_VOTER, false, 0);
@@ -7173,6 +7472,18 @@ static enum alarmtimer_restart chg_termination_alarm_cb(struct alarm *alarm,
return ALARMTIMER_NORESTART;
}
+static void apsd_timer_cb(struct timer_list *tm)
+{
+ struct smb_charger *chg = container_of(tm, struct smb_charger,
+ apsd_timer);
+
+ smblib_dbg(chg, PR_MISC, "APSD Extented timer timeout at %lld\n",
+ jiffies_to_msecs(jiffies));
+
+ chg->apsd_ext_timeout = true;
+}
+
+#define SOFT_JEITA_HYSTERESIS_OFFSET 0x200
static void jeita_update_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -7182,6 +7493,8 @@ static void jeita_update_work(struct work_struct *work)
union power_supply_propval val;
int rc, tmp[2], max_fcc_ma, max_fv_uv;
u32 jeita_hard_thresholds[2];
+ u16 addr;
+ u8 buff[2];
batt_node = of_find_node_by_name(node, "qcom,battery-data");
if (!batt_node) {
@@ -7244,6 +7557,34 @@ static void jeita_update_work(struct work_struct *work)
rc);
goto out;
}
+ } else {
+ /* Populate the jeita-soft-thresholds */
+ addr = CHGR_JEITA_THRESHOLD_BASE_REG(JEITA_SOFT);
+ rc = smblib_batch_read(chg, addr, buff, 2);
+ if (rc < 0) {
+ pr_err("failed to read 0x%4X, rc=%d\n", addr, rc);
+ goto out;
+ }
+
+ chg->jeita_soft_thlds[1] = buff[1] | buff[0] << 8;
+
+ rc = smblib_batch_read(chg, addr + 2, buff, 2);
+ if (rc < 0) {
+ pr_err("failed to read 0x%4X, rc=%d\n", addr + 2, rc);
+ goto out;
+ }
+
+ chg->jeita_soft_thlds[0] = buff[1] | buff[0] << 8;
+
+ /*
+ * Update the soft jeita hysteresis 2 DegC less for warm and
+ * 2 DegC more for cool than the soft jeita thresholds to avoid
+ * overwriting the registers with invalid values.
+ */
+ chg->jeita_soft_hys_thlds[0] =
+ chg->jeita_soft_thlds[0] - SOFT_JEITA_HYSTERESIS_OFFSET;
+ chg->jeita_soft_hys_thlds[1] =
+ chg->jeita_soft_thlds[1] + SOFT_JEITA_HYSTERESIS_OFFSET;
}
chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL;
@@ -7624,6 +7965,10 @@ int smblib_init(struct smb_charger *chg)
smblib_pr_swap_detach_work);
INIT_DELAYED_WORK(&chg->pr_lock_clear_work,
smblib_pr_lock_clear_work);
+ timer_setup(&chg->apsd_timer, apsd_timer_cb, 0);
+
+ INIT_DELAYED_WORK(&chg->role_reversal_check,
+ smblib_typec_role_check_work);
if (chg->wa_flags & CHG_TERMINATION_WA) {
INIT_WORK(&chg->chg_termination_work,
@@ -7669,6 +8014,7 @@ int smblib_init(struct smb_charger *chg)
chg->thermal_status = TEMP_BELOW_RANGE;
chg->typec_irq_en = true;
chg->cp_topo = -EINVAL;
+ chg->dr_mode = TYPEC_PORT_DRP;
switch (chg->mode) {
case PARALLEL_MASTER:
@@ -7761,6 +8107,7 @@ int smblib_deinit(struct smb_charger *chg)
alarm_cancel(&chg->chg_termination_alarm);
cancel_work_sync(&chg->chg_termination_work);
}
+ del_timer_sync(&chg->apsd_timer);
cancel_work_sync(&chg->bms_update_work);
cancel_work_sync(&chg->jeita_update_work);
cancel_work_sync(&chg->pl_update_work);
@@ -7775,6 +8122,7 @@ int smblib_deinit(struct smb_charger *chg)
cancel_delayed_work_sync(&chg->lpd_detach_work);
cancel_delayed_work_sync(&chg->thermal_regulation_work);
cancel_delayed_work_sync(&chg->usbov_dbc_work);
+ cancel_delayed_work_sync(&chg->role_reversal_check);
cancel_delayed_work_sync(&chg->pr_swap_detach_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index a13235d..a6d14b7 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*/
#ifndef __SMB5_CHARGER_H
@@ -8,11 +8,13 @@
#include <linux/alarmtimer.h>
#include <linux/ktime.h>
#include <linux/types.h>
+#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/extcon-provider.h>
+#include <linux/usb/typec.h>
#include "storm-watch.h"
#include "battery.h"
@@ -78,6 +80,7 @@ enum print_reason {
#define WLS_PL_CHARGING_VOTER "WLS_PL_CHARGING_VOTER"
#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER"
#define OVERHEAT_LIMIT_VOTER "OVERHEAT_LIMIT_VOTER"
+#define TYPEC_SWAP_VOTER "TYPEC_SWAP_VOTER"
#define BOOST_BACK_STORM_COUNT 3
#define WEAK_CHG_STORM_COUNT 8
@@ -99,6 +102,7 @@ enum print_reason {
#define DCIN_ICL_MIN_UA 100000
#define DCIN_ICL_MAX_UA 1500000
#define DCIN_ICL_STEP_UA 100000
+#define ROLE_REVERSAL_DELAY_MS 500
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -391,6 +395,7 @@ struct smb_charger {
spinlock_t typec_pr_lock;
struct mutex adc_lock;
struct mutex dpdm_lock;
+ struct mutex typec_lock;
/* power supplies */
struct power_supply *batt_psy;
@@ -418,6 +423,12 @@ struct smb_charger {
struct smb_regulator *vconn_vreg;
struct regulator *dpdm_reg;
+ /* typec */
+ struct typec_port *typec_port;
+ struct typec_capability typec_caps;
+ struct typec_partner *typec_partner;
+ struct typec_partner_desc typec_partner_desc;
+
/* votables */
struct votable *dc_suspend_votable;
struct votable *fcc_votable;
@@ -457,12 +468,15 @@ struct smb_charger {
struct delayed_work usbov_dbc_work;
struct delayed_work pr_swap_detach_work;
struct delayed_work pr_lock_clear_work;
+ struct delayed_work role_reversal_check;
struct alarm lpd_recheck_timer;
struct alarm moisture_protection_alarm;
struct alarm chg_termination_alarm;
struct alarm dcin_aicl_alarm;
+ struct timer_list apsd_timer;
+
struct charger_param chg_param;
/* secondary charger config */
bool sec_pl_present;
@@ -507,6 +521,7 @@ struct smb_charger {
bool typec_present;
int fake_input_current_limited;
int typec_mode;
+ int dr_mode;
int usb_icl_change_irq_enabled;
u32 jeita_status;
u8 float_cfg;
@@ -553,6 +568,7 @@ struct smb_charger {
int charge_full_cc;
int cc_soc_ref;
int last_cc_soc;
+ int term_vbat_uv;
int usbin_forced_max_uv;
int init_thermal_ua;
u32 comp_clamp_level;
@@ -561,6 +577,8 @@ struct smb_charger {
bool hvdcp3_standalone_config;
bool dcin_icl_user_set;
bool dpdm_enabled;
+ bool apsd_ext_timeout;
+ bool qc3p5_detected;
/* workaround flag */
u32 wa_flags;
@@ -777,6 +795,7 @@ int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg,
const union power_supply_propval *val);
void smblib_suspend_on_debug_battery(struct smb_charger *chg);
int smblib_rerun_apsd_if_required(struct smb_charger *chg);
+void smblib_rerun_apsd(struct smb_charger *chg);
int smblib_get_prop_fcc_delta(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_thermal_threshold(struct smb_charger *chg, u16 addr, int *val);
@@ -790,6 +809,8 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_typec_port_type_set(const struct typec_capability *cap,
+ enum typec_port_type type);
int smblib_get_prop_from_bms(struct smb_charger *chg,
enum power_supply_property psp,
union power_supply_propval *val);
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index b6a7d9f..0e202d4 100644
--- a/drivers/power/supply/twl4030_charger.c
+++ b/drivers/power/supply/twl4030_charger.c
@@ -420,7 +420,8 @@ static void twl4030_current_worker(struct work_struct *data)
if (v < USB_MIN_VOLT) {
/* Back up and stop adjusting. */
- bci->usb_cur -= USB_CUR_STEP;
+ if (bci->usb_cur >= USB_CUR_STEP)
+ bci->usb_cur -= USB_CUR_STEP;
bci->usb_cur_target = bci->usb_cur;
} else if (bci->usb_cur >= bci->usb_cur_target ||
bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) {
@@ -439,6 +440,7 @@ static void twl4030_current_worker(struct work_struct *data)
static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
{
int ret;
+ u32 reg;
if (bci->usb_mode == CHARGE_OFF)
enable = false;
@@ -452,14 +454,38 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
bci->usb_enabled = 1;
}
- if (bci->usb_mode == CHARGE_AUTO)
+ if (bci->usb_mode == CHARGE_AUTO) {
+ /* Enable interrupts now. */
+ reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC |
+ TWL4030_TBATOR2 | TWL4030_TBATOR1 |
+ TWL4030_BATSTS);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+ TWL4030_INTERRUPTS_BCIIMR1A);
+ if (ret < 0) {
+ dev_err(bci->dev,
+ "failed to unmask interrupts: %d\n",
+ ret);
+ return ret;
+ }
/* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
+ }
/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
if (bci->usb_mode == CHARGE_LINEAR) {
+ /* Enable interrupts now. */
+ reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_TBATOR2 |
+ TWL4030_TBATOR1 | TWL4030_BATSTS);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+ TWL4030_INTERRUPTS_BCIIMR1A);
+ if (ret < 0) {
+ dev_err(bci->dev,
+ "failed to unmask interrupts: %d\n",
+ ret);
+ return ret;
+ }
twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0);
/* Watch dog key: WOVF acknowledge */
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33,
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index c64903a..b818f65 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -175,9 +175,9 @@ static struct posix_clock_operations ptp_clock_ops = {
.read = ptp_read,
};
-static void delete_ptp_clock(struct posix_clock *pc)
+static void ptp_clock_release(struct device *dev)
{
- struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev);
mutex_destroy(&ptp->tsevq_mux);
mutex_destroy(&ptp->pincfg_mux);
@@ -222,7 +222,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
}
ptp->clock.ops = ptp_clock_ops;
- ptp->clock.release = delete_ptp_clock;
ptp->info = info;
ptp->devid = MKDEV(major, index);
ptp->index = index;
@@ -249,15 +248,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
if (err)
goto no_pin_groups;
- /* Create a new device in our class. */
- ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid,
- ptp, ptp->pin_attr_groups,
- "ptp%d", ptp->index);
- if (IS_ERR(ptp->dev)) {
- err = PTR_ERR(ptp->dev);
- goto no_device;
- }
-
/* Register a new PPS source. */
if (info->pps) {
struct pps_source_info pps;
@@ -273,8 +263,18 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
}
}
- /* Create a posix clock. */
- err = posix_clock_register(&ptp->clock, ptp->devid);
+ /* Initialize a new device of our class in our clock structure. */
+ device_initialize(&ptp->dev);
+ ptp->dev.devt = ptp->devid;
+ ptp->dev.class = ptp_class;
+ ptp->dev.parent = parent;
+ ptp->dev.groups = ptp->pin_attr_groups;
+ ptp->dev.release = ptp_clock_release;
+ dev_set_drvdata(&ptp->dev, ptp);
+ dev_set_name(&ptp->dev, "ptp%d", ptp->index);
+
+ /* Create a posix clock and link it to the device. */
+ err = posix_clock_register(&ptp->clock, &ptp->dev);
if (err) {
pr_err("failed to create posix clock\n");
goto no_clock;
@@ -286,8 +286,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
if (ptp->pps_source)
pps_unregister_source(ptp->pps_source);
no_pps:
- device_destroy(ptp_class, ptp->devid);
-no_device:
ptp_cleanup_pin_groups(ptp);
no_pin_groups:
if (ptp->kworker)
@@ -317,7 +315,6 @@ int ptp_clock_unregister(struct ptp_clock *ptp)
if (ptp->pps_source)
pps_unregister_source(ptp->pps_source);
- device_destroy(ptp_class, ptp->devid);
ptp_cleanup_pin_groups(ptp);
posix_clock_unregister(&ptp->clock);
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index c7c62b7..05f6b6a 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -41,7 +41,7 @@ struct timestamp_event_queue {
struct ptp_clock {
struct posix_clock clock;
- struct device *dev;
+ struct device dev;
struct ptp_clock_info *info;
dev_t devid;
int index; /* index into clocks.map */
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index e91dd2b..4b6c062 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -910,6 +910,7 @@ void pwm_put(struct pwm_device *pwm)
if (pwm->chip->ops->free)
pwm->chip->ops->free(pwm->chip, pwm);
+ pwm_set_chip_data(pwm, NULL);
pwm->label = NULL;
module_put(pwm->chip->ops->owner);
diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c
index d961a82..31b0103 100644
--- a/drivers/pwm/pwm-bcm-iproc.c
+++ b/drivers/pwm/pwm-bcm-iproc.c
@@ -187,6 +187,7 @@ static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
static const struct pwm_ops iproc_pwm_ops = {
.apply = iproc_pwmc_apply,
.get_state = iproc_pwmc_get_state,
+ .owner = THIS_MODULE,
};
static int iproc_pwmc_probe(struct platform_device *pdev)
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
index 7c8d6a1..b91c477 100644
--- a/drivers/pwm/pwm-berlin.c
+++ b/drivers/pwm/pwm-berlin.c
@@ -84,7 +84,6 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm);
- pwm_set_chip_data(pwm, NULL);
kfree(channel);
}
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
index 26ec24e..7e16b7d 100644
--- a/drivers/pwm/pwm-clps711x.c
+++ b/drivers/pwm/pwm-clps711x.c
@@ -48,7 +48,7 @@ static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
{
/* Duty cycle 0..15 max */
- return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm));
+ return DIV_ROUND_CLOSEST(v * 0xf, pwm->args.period);
}
static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
@@ -71,7 +71,7 @@ static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
struct clps711x_chip *priv = to_clps711x_chip(chip);
unsigned int duty;
- if (period_ns != pwm_get_period(pwm))
+ if (period_ns != pwm->args.period)
return -EINVAL;
duty = clps711x_get_duty(pwm, duty_ns);
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
index 4721a26..1e69c1c 100644
--- a/drivers/pwm/pwm-lpss.c
+++ b/drivers/pwm/pwm-lpss.c
@@ -97,7 +97,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
unsigned long long on_time_div;
unsigned long c = lpwm->info->clk_rate, base_unit_range;
unsigned long long base_unit, freq = NSEC_PER_SEC;
- u32 ctrl;
+ u32 orig_ctrl, ctrl;
do_div(freq, period_ns);
@@ -114,13 +114,17 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
do_div(on_time_div, period_ns);
on_time_div = 255ULL - on_time_div;
- ctrl = pwm_lpss_read(pwm);
+ orig_ctrl = ctrl = pwm_lpss_read(pwm);
ctrl &= ~PWM_ON_TIME_DIV_MASK;
ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
base_unit &= base_unit_range;
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div;
- pwm_lpss_write(pwm, ctrl);
+
+ if (orig_ctrl != ctrl) {
+ pwm_lpss_write(pwm, ctrl);
+ pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE);
+ }
}
static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
@@ -144,7 +148,6 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
- pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false);
ret = pwm_lpss_wait_for_update(pwm);
if (ret) {
@@ -157,7 +160,6 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (ret)
return ret;
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
- pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
return pwm_lpss_wait_for_update(pwm);
}
} else if (pwm_is_enabled(pwm)) {
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index a7eaf962..567f5e2 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -176,7 +176,6 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
pm_runtime_put(pca->chip.dev);
mutex_lock(&pca->lock);
pwm = &pca->chip.pwms[offset];
- pwm_set_chip_data(pwm, NULL);
mutex_unlock(&pca->lock);
}
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index 062f2cf..3762432 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -238,7 +238,6 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
devm_kfree(chip->dev, pwm_get_chip_data(pwm));
- pwm_set_chip_data(pwm, NULL);
}
static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800-regulator.c
similarity index 100%
rename from drivers/regulator/88pm800.c
rename to drivers/regulator/88pm800-regulator.c
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 62f3005..9d6e77d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -12,7 +12,7 @@
obj-$(CONFIG_REGULATOR_PROXY_CONSUMER) += proxy-consumer.o
obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
-obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o
+obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 83dba3f..6057882 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -956,23 +956,6 @@ static struct ab8500_regulator_info
.update_val_idle = 0x82,
.update_val_normal = 0x02,
},
- [AB8505_LDO_USB] = {
- .desc = {
- .name = "LDO-USB",
- .ops = &ab8500_regulator_mode_ops,
- .type = REGULATOR_VOLTAGE,
- .id = AB8505_LDO_USB,
- .owner = THIS_MODULE,
- .n_voltages = 1,
- .volt_table = fixed_3300000_voltage,
- },
- .update_bank = 0x03,
- .update_reg = 0x82,
- .update_mask = 0x03,
- .update_val = 0x01,
- .update_val_idle = 0x03,
- .update_val_normal = 0x01,
- },
[AB8505_LDO_AUDIO] = {
.desc = {
.name = "LDO-AUDIO",
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 316861b..f2360fa 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1742,8 +1742,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
regulator = create_regulator(rdev, dev, id);
if (regulator == NULL) {
regulator = ERR_PTR(-ENOMEM);
- put_device(&rdev->dev);
module_put(rdev->owner);
+ put_device(&rdev->dev);
return regulator;
}
@@ -1869,13 +1869,13 @@ static void _regulator_put(struct regulator *regulator)
rdev->open_count--;
rdev->exclusive = 0;
- put_device(&rdev->dev);
regulator_unlock(rdev);
kfree_const(regulator->supply_name);
kfree(regulator);
module_put(rdev->owner);
+ put_device(&rdev->dev);
}
/**
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
index 860400d..a8f2f07 100644
--- a/drivers/regulator/max8907-regulator.c
+++ b/drivers/regulator/max8907-regulator.c
@@ -299,7 +299,10 @@ static int max8907_regulator_probe(struct platform_device *pdev)
memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc));
/* Backwards compatibility with MAX8907B; SD1 uses different voltages */
- regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+ ret = regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+ if (ret)
+ return ret;
+
if ((val & MAX8907_II2RR_VERSION_MASK) ==
MAX8907_II2RR_VERSION_REV_B) {
pmic->desc[MAX8907_SD1].min_uV = 637500;
@@ -336,14 +339,20 @@ static int max8907_regulator_probe(struct platform_device *pdev)
}
if (pmic->desc[i].ops == &max8907_ldo_ops) {
- regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ ret = regmap_read(config.regmap, pmic->desc[i].enable_reg,
&val);
+ if (ret)
+ return ret;
+
if ((val & MAX8907_MASK_LDO_SEQ) !=
MAX8907_MASK_LDO_SEQ)
pmic->desc[i].ops = &max8907_ldo_hwctl_ops;
} else if (pmic->desc[i].ops == &max8907_out5v_ops) {
- regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ ret = regmap_read(config.regmap, pmic->desc[i].enable_reg,
&val);
+ if (ret)
+ return ret;
+
if ((val & (MAX8907_MASK_OUT5V_VINEN |
MAX8907_MASK_OUT5V_ENSRC)) !=
MAX8907_MASK_OUT5V_ENSRC)
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 210fc20..b255590 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -214,12 +214,12 @@ static void of_get_regulation_constraints(struct device_node *np,
"regulator-off-in-suspend"))
suspend_state->enabled = DISABLE_IN_SUSPEND;
- if (!of_property_read_u32(np, "regulator-suspend-min-microvolt",
- &pval))
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-min-microvolt", &pval))
suspend_state->min_uV = pval;
- if (!of_property_read_u32(np, "regulator-suspend-max-microvolt",
- &pval))
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-max-microvolt", &pval))
suspend_state->max_uV = pval;
if (!of_property_read_u32(suspend_np,
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index bb5ab7d7..c2cc392 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -443,13 +443,16 @@ static int palmas_ldo_write(struct palmas *palmas, unsigned int reg,
static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode)
{
int id = rdev_get_id(dev);
+ int ret;
struct palmas_pmic *pmic = rdev_get_drvdata(dev);
struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata;
struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
unsigned int reg;
bool rail_enable = true;
- palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®);
+ ret = palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®);
+ if (ret)
+ return ret;
reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
index 31c3a23..69a377ab 100644
--- a/drivers/regulator/pfuze100-regulator.c
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -710,7 +710,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
/* SW2~SW4 high bit check and modify the voltage value table */
if (i >= sw_check_start && i <= sw_check_end) {
- regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
+ ret = regmap_read(pfuze_chip->regmap,
+ desc->vsel_reg, &val);
+ if (ret) {
+ dev_err(&client->dev, "Fails to read from the register.\n");
+ return ret;
+ }
+
if (val & sw_hi) {
if (pfuze_chip->chip_id == PFUZE3000 ||
pfuze_chip->chip_id == PFUZE3001) {
diff --git a/drivers/regulator/qcom_pm8008-regulator.c b/drivers/regulator/qcom_pm8008-regulator.c
index ae7271f..15e0829 100644
--- a/drivers/regulator/qcom_pm8008-regulator.c
+++ b/drivers/regulator/qcom_pm8008-regulator.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#define pr_fmt(fmt) "PM8008: %s: " fmt, __func__
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>
@@ -24,6 +25,7 @@
#define STARTUP_DELAY_USEC 20
#define VSET_STEP_SIZE_MV 1
#define VSET_STEP_MV 8
+#define VSET_STEP_UV (VSET_STEP_MV * 1000)
#define MISC_BASE 0x900
@@ -187,9 +189,16 @@ static int pm8008_regulator_is_enabled(struct regulator_dev *rdev)
static int pm8008_regulator_enable(struct regulator_dev *rdev)
{
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
- int rc, init_mv, delay_us, delay_ms, retry_count = 10;
+ int rc, rc2, current_uv, delay_us, delay_ms, retry_count = 10;
u8 reg;
+ current_uv = pm8008_regulator_get_voltage(rdev);
+ if (current_uv < 0) {
+ pm8008_err(pm8008_reg, "failed to get current voltage rc=%d\n",
+ current_uv);
+ return current_uv;
+ }
+
rc = regulator_enable(pm8008_reg->en_supply);
if (rc < 0) {
pm8008_err(pm8008_reg,
@@ -198,12 +207,22 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev)
}
if (pm8008_reg->parent_supply) {
+ rc = regulator_set_voltage(pm8008_reg->parent_supply,
+ current_uv + pm8008_reg->min_dropout_uv,
+ INT_MAX);
+ if (rc < 0) {
+ pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
+ rc);
+ goto remove_en;
+ }
+
rc = regulator_enable(pm8008_reg->parent_supply);
if (rc < 0) {
pm8008_err(pm8008_reg,
"failed to enable parent rc=%d\n", rc);
- regulator_disable(pm8008_reg->en_supply);
- return rc;
+ regulator_set_voltage(pm8008_reg->parent_supply, 0,
+ INT_MAX);
+ goto remove_en;
}
}
@@ -217,20 +236,14 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev)
}
/*
- * wait for VREG_OK
- * Read voltage and calculate the delay.
+ * Wait for the VREG_READY status bit to be set using a timeout delay
+ * calculated from the current commanded voltage.
*/
- init_mv = pm8008_regulator_get_voltage(rdev) / 1000;
- if (init_mv < 0) {
- pm8008_err(pm8008_reg,
- "failed to get regulator voltage rc=%d\n", rc);
- goto out;
- }
delay_us = STARTUP_DELAY_USEC
- + DIV_ROUND_UP(init_mv * 1000, pm8008_reg->step_rate);
+ + DIV_ROUND_UP(current_uv, pm8008_reg->step_rate);
delay_ms = DIV_ROUND_UP(delay_us, 1000);
- /* Retry 10 times for VREG_OK before bailing out */
+ /* Retry 10 times for VREG_READY before bailing out */
while (retry_count--) {
if (delay_ms > 20)
msleep(delay_ms);
@@ -242,7 +255,7 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev)
if (rc < 0) {
pm8008_err(pm8008_reg,
"failed to read regulator status rc=%d\n", rc);
- goto out;
+ goto disable_ldo;
}
if (reg & VREG_READY_BIT) {
pm8008_debug(pm8008_reg, "regulator enabled\n");
@@ -250,20 +263,33 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev)
}
}
- pm8008_err(pm8008_reg,
- "failed to enable regulator VREG_READY not set\n");
-out:
+ pm8008_err(pm8008_reg, "failed to enable regulator, VREG_READY not set\n");
+ rc = -ETIME;
+
+disable_ldo:
pm8008_masked_write(pm8008_reg->regmap,
LDO_ENABLE_REG(pm8008_reg->base), ENABLE_BIT, 0);
-remove_vote:
- rc = regulator_disable(pm8008_reg->en_supply);
- if (pm8008_reg->parent_supply)
- rc |= regulator_disable(pm8008_reg->parent_supply);
- if (rc < 0)
- pm8008_err(pm8008_reg,
- "failed to disable parent regulator rc=%d\n", rc);
- return -ETIME;
+remove_vote:
+ if (pm8008_reg->parent_supply) {
+ rc2 = regulator_disable(pm8008_reg->parent_supply);
+ if (rc2 < 0)
+ pm8008_err(pm8008_reg, "failed to disable parent supply rc=%d\n",
+ rc2);
+ rc2 = regulator_set_voltage(pm8008_reg->parent_supply, 0,
+ INT_MAX);
+ if (rc2 < 0)
+ pm8008_err(pm8008_reg, "failed to remove voltage vote for parent supply rc=%d\n",
+ rc2);
+ }
+
+remove_en:
+ rc2 = regulator_disable(pm8008_reg->en_supply);
+ if (rc2 < 0)
+ pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n",
+ rc2);
+
+ return rc;
}
static int pm8008_regulator_disable(struct regulator_dev *rdev)
@@ -280,28 +306,29 @@ static int pm8008_regulator_disable(struct regulator_dev *rdev)
return rc;
}
- /* remove vote from chip enable regulator */
- rc = regulator_disable(pm8008_reg->en_supply);
- if (rc < 0) {
- pm8008_err(pm8008_reg,
- "failed to disable en_supply rc=%d\n", rc);
- }
-
/* remove voltage vote from parent regulator */
if (pm8008_reg->parent_supply) {
+ rc = regulator_disable(pm8008_reg->parent_supply);
+ if (rc < 0) {
+ pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n",
+ rc);
+ return rc;
+ }
rc = regulator_set_voltage(pm8008_reg->parent_supply,
0, INT_MAX);
if (rc < 0) {
- pm8008_err(pm8008_reg,
- "failed to remove parent voltage rc=%d\n", rc);
+ pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n",
+ rc);
return rc;
}
- rc = regulator_disable(pm8008_reg->parent_supply);
- if (rc < 0) {
- pm8008_err(pm8008_reg,
- "failed to disable parent rc=%d\n", rc);
- return rc;
- }
+ }
+
+ /* remove vote from chip enable regulator */
+ rc = regulator_disable(pm8008_reg->en_supply);
+ if (rc < 0) {
+ pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n",
+ rc);
+ return rc;
}
pm8008_debug(pm8008_reg, "regulator disabled\n");
@@ -341,31 +368,76 @@ static int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, int min_uv,
return 0;
}
+static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uv)
+{
+ struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
+
+ return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate);
+}
+
static int pm8008_regulator_set_voltage(struct regulator_dev *rdev,
int min_uv, int max_uv, unsigned int *selector)
{
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
- int rc = 0;
+ int rc = 0, current_uv = 0, rounded_uv = 0, enabled = 0;
if (pm8008_reg->parent_supply) {
- /* request on parent regulator with headroom */
+ enabled = pm8008_regulator_is_enabled(rdev);
+ if (enabled < 0) {
+ return enabled;
+ } else if (enabled) {
+ current_uv = pm8008_regulator_get_voltage(rdev);
+ if (current_uv < 0)
+ return current_uv;
+ rounded_uv = roundup(min_uv, VSET_STEP_UV);
+ }
+ }
+
+ /*
+ * Set the parent_supply voltage before changing the LDO voltage when
+ * the LDO voltage is being increased.
+ */
+ if (pm8008_reg->parent_supply && enabled && rounded_uv >= current_uv) {
+ /* Request parent voltage with headroom */
rc = regulator_set_voltage(pm8008_reg->parent_supply,
- pm8008_reg->min_dropout_uv + min_uv,
+ rounded_uv + pm8008_reg->min_dropout_uv,
INT_MAX);
if (rc < 0) {
- pm8008_err(pm8008_reg,
- "failed to request parent supply voltage rc=%d\n",
+ pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
rc);
return rc;
}
}
rc = pm8008_write_voltage(pm8008_reg, min_uv, max_uv);
- if (rc < 0) {
- /* remove parent's voltage vote */
- if (pm8008_reg->parent_supply)
- regulator_set_voltage(pm8008_reg->parent_supply,
- 0, INT_MAX);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Set the parent_supply voltage after changing the LDO voltage when
+ * the LDO voltage is being reduced.
+ */
+ if (pm8008_reg->parent_supply && enabled && rounded_uv < current_uv) {
+ /*
+ * Ensure sufficient time for the LDO voltage to slew down
+ * before reducing the parent supply voltage. The regulator
+ * framework will add the same delay after this function returns
+ * in all cases (i.e. enabled/disabled and increasing/decreasing
+ * voltage).
+ */
+ udelay(pm8008_regulator_set_voltage_time(rdev, rounded_uv,
+ current_uv));
+
+ /* Request parent voltage with headroom */
+ rc = regulator_set_voltage(pm8008_reg->parent_supply,
+ rounded_uv + pm8008_reg->min_dropout_uv,
+ INT_MAX);
+ if (rc < 0) {
+ pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n",
+ rc);
+ return rc;
+ }
}
pm8008_debug(pm8008_reg, "voltage set to %d\n", min_uv);
@@ -423,14 +495,6 @@ static int pm8008_regulator_set_load(struct regulator_dev *rdev, int load_uA)
return pm8008_regulator_set_mode(rdev, mode);
}
-static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev,
- int old_uV, int new_uv)
-{
- struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
-
- return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate);
-}
-
static struct regulator_ops pm8008_regulator_ops = {
.enable = pm8008_regulator_enable,
.disable = pm8008_regulator_disable,
@@ -506,6 +570,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
struct device_node *reg_node = pm8008_reg->of_node;
char buff[MAX_REG_NAME];
int rc, i, init_voltage;
+ u32 base = 0;
u8 reg;
/* get regulator data */
@@ -518,11 +583,12 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
return -EINVAL;
}
- rc = of_property_read_u16(reg_node, "reg", &pm8008_reg->base);
+ rc = of_property_read_u32(reg_node, "reg", &base);
if (rc < 0) {
pr_err("%s: failed to get regulator base rc=%d\n", name, rc);
return rc;
}
+ pm8008_reg->base = base;
pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv;
of_property_read_u32(reg_node, "qcom,min-dropout-voltage",
@@ -709,7 +775,7 @@ static int pm8008_regulator_probe(struct platform_device *pdev)
/* PM8008 chip enable regulator callbacks */
static int pm8008_enable_regulator_enable(struct regulator_dev *rdev)
{
- struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
+ struct pm8008_chip *chip = rdev_get_drvdata(rdev);
int rc;
rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG,
@@ -725,7 +791,7 @@ static int pm8008_enable_regulator_enable(struct regulator_dev *rdev)
static int pm8008_enable_regulator_disable(struct regulator_dev *rdev)
{
- struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
+ struct pm8008_chip *chip = rdev_get_drvdata(rdev);
int rc;
rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG,
@@ -741,7 +807,7 @@ static int pm8008_enable_regulator_disable(struct regulator_dev *rdev)
static int pm8008_enable_regulator_is_enabled(struct regulator_dev *rdev)
{
- struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
+ struct pm8008_chip *chip = rdev_get_drvdata(rdev);
int rc;
u8 reg;
diff --git a/drivers/regulator/rn5t618-regulator.c b/drivers/regulator/rn5t618-regulator.c
index 790a4a7..40b7464 100644
--- a/drivers/regulator/rn5t618-regulator.c
+++ b/drivers/regulator/rn5t618-regulator.c
@@ -154,6 +154,7 @@ static struct platform_driver rn5t618_regulator_driver = {
module_platform_driver(rn5t618_regulator_driver);
+MODULE_ALIAS("platform:rn5t618-regulator");
MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
MODULE_DESCRIPTION("RN5T618 regulator driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c
index cced1ff..89b9314 100644
--- a/drivers/regulator/ti-abb-regulator.c
+++ b/drivers/regulator/ti-abb-regulator.c
@@ -173,19 +173,14 @@ static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb)
while (timeout++ <= abb->settling_time) {
status = ti_abb_check_txdone(abb);
if (status)
- break;
+ return 0;
udelay(1);
}
- if (timeout > abb->settling_time) {
- dev_warn_ratelimited(dev,
- "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
- __func__, timeout, readl(abb->int_base));
- return -ETIMEDOUT;
- }
-
- return 0;
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
}
/**
@@ -205,19 +200,14 @@ static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb)
status = ti_abb_check_txdone(abb);
if (!status)
- break;
+ return 0;
udelay(1);
}
- if (timeout > abb->settling_time) {
- dev_warn_ratelimited(dev,
- "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
- __func__, timeout, readl(abb->int_base));
- return -ETIMEDOUT;
- }
-
- return 0;
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
}
/**
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 02ccdaa..5ebb6ee 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -1102,8 +1102,10 @@ static int tps65910_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pmic);
/* Give control of all register to control port */
- tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
+ err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
DEVCTRL_SR_CTL_I2C_SEL_MASK);
+ if (err < 0)
+ return err;
switch (tps65910_chip_id(tps65910)) {
case TPS65910:
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
index e230bef..d200334 100644
--- a/drivers/remoteproc/da8xx_remoteproc.c
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -226,7 +226,7 @@ static int da8xx_rproc_get_internal_memories(struct platform_device *pdev,
res->start & DA8XX_RPROC_LOCAL_ADDRESS_MASK;
drproc->mem[i].size = resource_size(res);
- dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %p da 0x%x\n",
+ dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n",
mem_names[i], &drproc->mem[i].bus_addr,
drproc->mem[i].size, drproc->mem[i].cpu_addr,
drproc->mem[i].dev_addr);
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
index 602af83..0d33e30 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -84,6 +84,7 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
else
dev_err(q6v5->dev, "fatal error without message\n");
+ q6v5->running = false;
rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR);
return IRQ_HANDLED;
@@ -150,8 +151,6 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5)
{
int ret;
- q6v5->running = false;
-
qcom_smem_state_update_bits(q6v5->state,
BIT(q6v5->stop_bit), BIT(q6v5->stop_bit));
diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c
index 47be411..3a4c3d7 100644
--- a/drivers/remoteproc/remoteproc_sysfs.c
+++ b/drivers/remoteproc/remoteproc_sysfs.c
@@ -48,6 +48,11 @@ static ssize_t firmware_store(struct device *dev,
}
len = strcspn(buf, "\n");
+ if (!len) {
+ dev_err(dev, "can't provide a NULL firmware\n");
+ err = -EINVAL;
+ goto out;
+ }
p = kstrndup(buf, len, GFP_KERNEL);
if (!p) {
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 225e34c..f7bf204 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
break;
}
}
- of_node_put(args.np);
if (!rcdev) {
- mutex_unlock(&reset_list_mutex);
- return ERR_PTR(-EPROBE_DEFER);
+ rstc = ERR_PTR(-EPROBE_DEFER);
+ goto out;
}
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
- mutex_unlock(&reset_list_mutex);
- return ERR_PTR(-EINVAL);
+ rstc = ERR_PTR(-EINVAL);
+ goto out;
}
rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) {
- mutex_unlock(&reset_list_mutex);
- return ERR_PTR(rstc_id);
+ rstc = ERR_PTR(rstc_id);
+ goto out;
}
/* reset_list_mutex also protects the rcdev's reset_control list */
rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
+out:
mutex_unlock(&reset_list_mutex);
+ of_node_put(args.np);
return rstc;
}
@@ -606,6 +607,7 @@ static void reset_control_array_put(struct reset_control_array *resets)
for (i = 0; i < resets->num_rstcs; i++)
__reset_control_put_internal(resets->rstc[i]);
mutex_unlock(&reset_list_mutex);
+ kfree(resets);
}
/**
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index c996be2..514c542 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -285,6 +285,7 @@ static void qcom_glink_channel_release(struct kref *ref)
{
struct glink_channel *channel = container_of(ref, struct glink_channel,
refcount);
+ struct glink_core_rx_intent *intent;
struct glink_core_rx_intent *tmp;
unsigned long flags;
int iid;
@@ -292,7 +293,18 @@ static void qcom_glink_channel_release(struct kref *ref)
CH_INFO(channel, "\n");
wake_up(&channel->intent_req_event);
+ /* cancel pending rx_done work */
+ kthread_cancel_work_sync(&channel->intent_work);
+
spin_lock_irqsave(&channel->intent_lock, flags);
+ /* Free all non-reuse intents pending rx_done work */
+ list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
+ if (!intent->reuse) {
+ kfree(intent->data);
+ kfree(intent);
+ }
+ }
+
idr_for_each_entry(&channel->liids, tmp, iid) {
kfree(tmp->data);
kfree(tmp);
@@ -1899,6 +1911,18 @@ static void qcom_glink_notif_reset(void *data)
spin_unlock_irqrestore(&glink->idr_lock, flags);
}
+static void qcom_glink_cancel_rx_work(struct qcom_glink *glink)
+{
+ struct glink_defer_cmd *dcmd;
+ struct glink_defer_cmd *tmp;
+
+ /* cancel any pending deferred rx_work */
+ cancel_work_sync(&glink->rx_work);
+
+ list_for_each_entry_safe(dcmd, tmp, &glink->rx_queue, node)
+ kfree(dcmd);
+}
+
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
unsigned long features,
struct qcom_glink_pipe *rx,
@@ -2019,28 +2043,16 @@ void qcom_glink_native_remove(struct qcom_glink *glink)
struct glink_channel *channel;
int cid;
int ret;
- unsigned long flags;
subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF);
qcom_glink_notif_reset(glink);
disable_irq(glink->irq);
- cancel_work_sync(&glink->rx_work);
+ qcom_glink_cancel_rx_work(glink);
ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
if (ret)
dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
- spin_lock_irqsave(&glink->idr_lock, flags);
- idr_for_each_entry(&glink->lcids, channel, cid) {
- spin_unlock_irqrestore(&glink->idr_lock, flags);
- /* cancel pending rx_done work for each channel*/
- kthread_cancel_work_sync(&channel->intent_work);
- spin_lock_irqsave(&glink->idr_lock, flags);
- }
- spin_unlock_irqrestore(&glink->idr_lock, flags);
-
- spin_lock_irqsave(&glink->idr_lock, flags);
-
/* Release any defunct local channels, waiting for close-ack */
idr_for_each_entry(&glink->lcids, channel, cid) {
kref_put(&channel->refcount, qcom_glink_channel_release);
@@ -2053,9 +2065,12 @@ void qcom_glink_native_remove(struct qcom_glink *glink)
idr_remove(&glink->rcids, cid);
}
+ /* Release any defunct local channels, waiting for close-req */
+ idr_for_each_entry(&glink->rcids, channel, cid)
+ kref_put(&channel->refcount, qcom_glink_channel_release);
+
idr_destroy(&glink->lcids);
idr_destroy(&glink->rcids);
- spin_unlock_irqrestore(&glink->idr_lock, flags);
kthread_flush_worker(&glink->kworker);
kthread_stop(glink->task);
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 4938a2d..949d113 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -105,13 +105,11 @@ static void glink_smem_rx_peak(struct qcom_glink_pipe *np,
tail -= pipe->native.length;
len = min_t(size_t, count, pipe->native.length - tail);
- if (len) {
+ if (len)
memcpy_fromio(data, pipe->fifo + tail, len);
- }
- if (len != count) {
+ if (len != count)
memcpy_fromio(data + len, pipe->fifo, (count - len));
- }
}
static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 3d577e2..ce051f9 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -127,7 +127,7 @@ EXPORT_SYMBOL_GPL(rtc_read_time);
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
- int err;
+ int err, uie;
err = rtc_valid_tm(tm);
if (err != 0)
@@ -139,6 +139,17 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
rtc_subtract_offset(rtc, tm);
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ uie = rtc->uie_rtctimer.enabled || rtc->uie_irq_active;
+#else
+ uie = rtc->uie_rtctimer.enabled;
+#endif
+ if (uie) {
+ err = rtc_update_irq_enable(rtc, 0);
+ if (err)
+ return err;
+ }
+
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -162,6 +173,12 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
/* A timer might have just expired */
schedule_work(&rtc->irqwork);
+ if (uie) {
+ err = rtc_update_irq_enable(rtc, 1);
+ if (err)
+ return err;
+ }
+
trace_rtc_set_time(rtc_tm_to_time64(tm), err);
return err;
}
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index bde53c8c..b74338d6 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -514,7 +514,6 @@ MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
static __init int armada38x_rtc_probe(struct platform_device *pdev)
{
- const struct rtc_class_ops *ops;
struct resource *res;
struct armada38x_rtc *rtc;
const struct of_device_id *match;
@@ -551,6 +550,11 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "no irq\n");
return rtc->irq;
}
+
+ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
0, pdev->name, rtc) < 0) {
dev_warn(&pdev->dev, "Interrupt not available.\n");
@@ -560,28 +564,24 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
if (rtc->irq != -1) {
device_init_wakeup(&pdev->dev, 1);
- ops = &armada38x_rtc_ops;
+ rtc->rtc_dev->ops = &armada38x_rtc_ops;
} else {
/*
* If there is no interrupt available then we can't
* use the alarm
*/
- ops = &armada38x_rtc_ops_noirq;
+ rtc->rtc_dev->ops = &armada38x_rtc_ops_noirq;
}
rtc->data = (struct armada38x_rtc_data *)match->data;
-
/* Update RTC-MBUS bridge timing parameters */
rtc->data->update_mbus_timing(rtc);
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
- ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- ret = PTR_ERR(rtc->rtc_dev);
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret)
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
- return ret;
- }
- return 0;
+
+ return ret;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index ea18a8f..033f65a 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -518,7 +518,7 @@ static ssize_t timestamp0_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
int sr;
sr = isl1208_i2c_get_sr(client);
@@ -540,7 +540,7 @@ static ssize_t timestamp0_store(struct device *dev,
static ssize_t timestamp0_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, };
struct rtc_time tm;
int sr;
@@ -650,7 +650,7 @@ static ssize_t
isl1208_sysfs_show_atrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int atr = isl1208_i2c_get_atr(to_i2c_client(dev));
+ int atr = isl1208_i2c_get_atr(to_i2c_client(dev->parent));
if (atr < 0)
return atr;
@@ -663,7 +663,7 @@ static ssize_t
isl1208_sysfs_show_dtrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));
+ int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev->parent));
if (dtr < 0)
return dtr;
@@ -676,7 +676,7 @@ static ssize_t
isl1208_sysfs_show_usr(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int usr = isl1208_i2c_get_usr(to_i2c_client(dev));
+ int usr = isl1208_i2c_get_usr(to_i2c_client(dev->parent));
if (usr < 0)
return usr;
@@ -701,7 +701,10 @@ isl1208_sysfs_store_usr(struct device *dev,
if (usr < 0 || usr > 0xffff)
return -EINVAL;
- return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;
+ if (isl1208_i2c_set_usr(to_i2c_client(dev->parent), usr))
+ return -EIO;
+
+ return count;
}
static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
@@ -765,7 +768,6 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
rtc->ops = &isl1208_rtc_ops;
i2c_set_clientdata(client, rtc);
- dev_set_drvdata(&rtc->dev, client);
rc = isl1208_i2c_get_sr(client);
if (rc < 0) {
@@ -804,7 +806,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
evdet_irq = of_irq_get_byname(np, "evdet");
}
- rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
+ rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files);
if (rc)
return rc;
@@ -821,14 +823,6 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
return rtc_register_device(rtc);
}
-static int
-isl1208_remove(struct i2c_client *client)
-{
- sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
-
- return 0;
-}
-
static const struct i2c_device_id isl1208_id[] = {
{ "isl1208", TYPE_ISL1208 },
{ "isl1218", TYPE_ISL1218 },
@@ -851,7 +845,6 @@ static struct i2c_driver isl1208_driver = {
.of_match_table = of_match_ptr(isl1208_of_match),
},
.probe = isl1208_probe,
- .remove = isl1208_remove,
.id_table = isl1208_id,
};
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 8a60900d..4aff349a 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -360,7 +360,7 @@ static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
out:
mutex_unlock(&info->lock);
- return 0;
+ return ret;
}
static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index 08c661a..20e50d9 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -215,7 +215,7 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
out:
mutex_unlock(&info->lock);
- return 0;
+ return ret;
}
static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
index 385f830..e9a25ec 100644
--- a/drivers/rtc/rtc-mt6397.c
+++ b/drivers/rtc/rtc-mt6397.c
@@ -332,6 +332,10 @@ static int mtk_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_allocate_device(rtc->dev);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
ret = request_threaded_irq(rtc->irq, NULL,
mtk_rtc_irq_handler_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
@@ -344,11 +348,11 @@ static int mtk_rtc_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
- rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
- &mtk_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
+ rtc->rtc_dev->ops = &mtk_rtc_ops;
+
+ ret = rtc_register_device(rtc->rtc_dev);
+ if (ret) {
dev_err(&pdev->dev, "register rtc device failed\n");
- ret = PTR_ERR(rtc->rtc_dev);
goto out_free_irq;
}
@@ -365,7 +369,6 @@ static int mtk_rtc_remove(struct platform_device *pdev)
{
struct mt6397_rtc *rtc = platform_get_drvdata(pdev);
- rtc_device_unregister(rtc->rtc_dev);
free_irq(rtc->irq, rtc->rtc_dev);
irq_dispose_mapping(rtc->irq);
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
index 3fcd2cb..2e03021 100644
--- a/drivers/rtc/rtc-pcf8523.c
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client)
return !!(value & REG_CONTROL3_BLF);
}
-static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
+static int pcf8523_load_capacitance(struct i2c_client *client)
{
+ u32 load;
u8 value;
int err;
@@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
if (err < 0)
return err;
- if (!high)
- value &= ~REG_CONTROL1_CAP_SEL;
- else
+ load = 12500;
+ of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads",
+ &load);
+
+ switch (load) {
+ default:
+ dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500",
+ load);
+ /* fall through */
+ case 12500:
value |= REG_CONTROL1_CAP_SEL;
+ break;
+ case 7000:
+ value &= ~REG_CONTROL1_CAP_SEL;
+ break;
+ }
err = pcf8523_write(client, REG_CONTROL1, value);
- if (err < 0)
- return err;
return err;
}
@@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client,
if (!pcf)
return -ENOMEM;
- err = pcf8523_select_capacitance(client, true);
+ err = pcf8523_load_capacitance(client);
if (err < 0)
- return err;
+ dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+ err);
err = pcf8523_set_pm(client, 0);
if (err < 0)
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index f85a1a9..343bb6e 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -112,6 +112,13 @@ static int pl030_probe(struct amba_device *dev, const struct amba_id *id)
goto err_rtc;
}
+ rtc->rtc = devm_rtc_allocate_device(&dev->dev);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err_rtc;
+ }
+
+ rtc->rtc->ops = &pl030_ops;
rtc->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!rtc->base) {
ret = -ENOMEM;
@@ -128,12 +135,9 @@ static int pl030_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
goto err_irq;
- rtc->rtc = rtc_device_register("pl030", &dev->dev, &pl030_ops,
- THIS_MODULE);
- if (IS_ERR(rtc->rtc)) {
- ret = PTR_ERR(rtc->rtc);
+ ret = rtc_register_device(rtc->rtc);
+ if (ret)
goto err_reg;
- }
return 0;
@@ -154,7 +158,6 @@ static int pl030_remove(struct amba_device *dev)
writel(0, rtc->base + RTC_CR);
free_irq(dev->irq[0], rtc);
- rtc_device_unregister(rtc->rtc);
iounmap(rtc->base);
amba_release_regions(dev);
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index bd38010..9f8cbbd 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -1,14 +1,8 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2010-2011, 2020, The Linux Foundation. All rights reserved.
*/
+
#include <linux/of.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -455,6 +449,16 @@ static const struct pm8xxx_rtc_regs pm8941_regs = {
.alarm_en = BIT(7),
};
+static const struct pm8xxx_rtc_regs pmk8350_regs = {
+ .ctrl = 0x6146,
+ .write = 0x6140,
+ .read = 0x6148,
+ .alarm_rw = 0x6240,
+ .alarm_ctrl = 0x6246,
+ .alarm_ctrl2 = 0x6248,
+ .alarm_en = BIT(7),
+};
+
/*
* Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
*/
@@ -463,6 +467,7 @@ static const struct of_device_id pm8xxx_id_table[] = {
{ .compatible = "qcom,pm8018-rtc", .data = &pm8921_regs },
{ .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
{ .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
+ { .compatible = "qcom,pmk8350-rtc", .data = &pmk8350_regs },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c
index 29fc3d2..17ccef5d 100644
--- a/drivers/rtc/rtc-rv8803.c
+++ b/drivers/rtc/rtc-rv8803.c
@@ -623,7 +623,7 @@ MODULE_DEVICE_TABLE(i2c, rv8803_id);
static const struct of_device_id rv8803_of_match[] = {
{
.compatible = "microcrystal,rv8803",
- .data = (void *)rx_8900
+ .data = (void *)rv_8803
},
{
.compatible = "epson,rx8900",
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index 77feb60..3c64dbb 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -108,7 +108,7 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
static int s35390a_init(struct s35390a *s35390a)
{
- char buf;
+ u8 buf;
int ret;
unsigned initcount = 0;
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 75c8c503..58e03ac 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -327,7 +327,6 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
struct rtc_time *tm = &alrm->time;
unsigned int alrm_en;
int ret;
- int year = tm->tm_year - 100;
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alrm->enabled,
@@ -356,11 +355,6 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
}
- if (year < 100 && year >= 0) {
- alrm_en |= S3C2410_RTCALM_YEAREN;
- writeb(bin2bcd(year), info->base + S3C2410_ALMYEAR);
- }
-
if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
alrm_en |= S3C2410_RTCALM_MONEN;
writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON);
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index f1ff30a..9746c32 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -338,8 +338,8 @@ int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps)
new_cnt = old_cnt + add_cnt + 1;
groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
- if (IS_ERR_OR_NULL(groups))
- return PTR_ERR(groups);
+ if (!groups)
+ return -ENOMEM;
memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups));
memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups));
groups[old_cnt + add_cnt] = NULL;
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 08dbefc..61c110b 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -253,9 +253,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
struct resource *res;
int irq, ret;
struct nvmem_config nvmem_cfg = {
- .name = "rv8803_nvram",
- .word_size = 4,
- .stride = 4,
+ .name = "tx4939_nvram",
.size = TX4939_RTC_REG_RAMSIZE,
.reg_read = tx4939_nvram_read,
.reg_write = tx4939_nvram_write,
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index f89f9d0..a2e34c8 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1135,7 +1135,8 @@ static u32 get_fcx_max_data(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
int fcx_in_css, fcx_in_gneq, fcx_in_features;
- int tpm, mdc;
+ unsigned int mdc;
+ int tpm;
if (dasd_nofcx)
return 0;
@@ -1149,7 +1150,7 @@ static u32 get_fcx_max_data(struct dasd_device *device)
return 0;
mdc = ccw_device_get_mdc(device->cdev, 0);
- if (mdc < 0) {
+ if (mdc == 0) {
dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n");
return 0;
} else {
@@ -1160,12 +1161,12 @@ static u32 get_fcx_max_data(struct dasd_device *device)
static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
{
struct dasd_eckd_private *private = device->private;
- int mdc;
+ unsigned int mdc;
u32 fcx_max_data;
if (private->fcx_max_data) {
mdc = ccw_device_get_mdc(device->cdev, lpm);
- if ((mdc < 0)) {
+ if (mdc == 0) {
dev_warn(&device->cdev->dev,
"Detecting the maximum data size for zHPF "
"requests failed (rc=%d) for a new path %x\n",
@@ -1769,7 +1770,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
dasd_free_block(device->block);
device->block = NULL;
out_err1:
- kfree(private->conf_data);
+ dasd_eckd_clear_conf_data(device);
kfree(device->private);
device->private = NULL;
return rc;
@@ -1778,7 +1779,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
static void dasd_eckd_uncheck_device(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
- int i;
if (!private)
return;
@@ -1788,21 +1788,7 @@ static void dasd_eckd_uncheck_device(struct dasd_device *device)
private->sneq = NULL;
private->vdsneq = NULL;
private->gneq = NULL;
- private->conf_len = 0;
- for (i = 0; i < 8; i++) {
- kfree(device->path[i].conf_data);
- if ((__u8 *)device->path[i].conf_data ==
- private->conf_data) {
- private->conf_data = NULL;
- private->conf_len = 0;
- }
- device->path[i].conf_data = NULL;
- device->path[i].cssid = 0;
- device->path[i].ssid = 0;
- device->path[i].chpid = 0;
- }
- kfree(private->conf_data);
- private->conf_data = NULL;
+ dasd_eckd_clear_conf_data(device);
}
static struct dasd_ccw_req *
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index c6ab34f..3072b89 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -11,6 +11,7 @@
GCOV_PROFILE_sclp_early_core.o := n
KCOV_INSTRUMENT_sclp_early_core.o := n
UBSAN_SANITIZE_sclp_early_core.o := n
+KASAN_SANITIZE_sclp_early_core.o := n
CFLAGS_sclp_early_core.o += -D__NO_FORTIFY
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 4435ae0..f0cae19 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -624,7 +624,7 @@ EXPORT_SYMBOL(ccw_device_tm_start_timeout);
* @mask: mask of paths to use
*
* Return the number of 64K-bytes blocks all paths at least support
- * for a transport command. Return values <= 0 indicate failures.
+ * for a transport command. Return value 0 indicates failure.
*/
int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask)
{
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 3be5465..027a53e 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -1223,11 +1223,10 @@ static struct bus_attribute *const ap_bus_attrs[] = {
};
/**
- * ap_select_domain(): Select an AP domain.
- *
- * Pick one of the 16 AP domains.
+ * ap_select_domain(): Select an AP domain if possible and we haven't
+ * already done so before.
*/
-static int ap_select_domain(void)
+static void ap_select_domain(void)
{
int count, max_count, best_domain;
struct ap_queue_status status;
@@ -1242,7 +1241,7 @@ static int ap_select_domain(void)
if (ap_domain_index >= 0) {
/* Domain has already been selected. */
spin_unlock_bh(&ap_domain_lock);
- return 0;
+ return;
}
best_domain = -1;
max_count = 0;
@@ -1269,11 +1268,8 @@ static int ap_select_domain(void)
if (best_domain >= 0) {
ap_domain_index = best_domain;
AP_DBF(DBF_DEBUG, "new ap_domain_index=%d\n", ap_domain_index);
- spin_unlock_bh(&ap_domain_lock);
- return 0;
}
spin_unlock_bh(&ap_domain_lock);
- return -ENODEV;
}
/*
@@ -1351,8 +1347,7 @@ static void ap_scan_bus(struct work_struct *unused)
AP_DBF(DBF_DEBUG, "%s running\n", __func__);
ap_query_configuration(ap_configuration);
- if (ap_select_domain() != 0)
- goto out;
+ ap_select_domain();
for (id = 0; id < AP_DEVICES; id++) {
/* check if device is registered */
@@ -1468,12 +1463,11 @@ static void ap_scan_bus(struct work_struct *unused)
}
} /* end device loop */
- if (defdomdevs < 1)
+ if (ap_domain_index >= 0 && defdomdevs < 1)
AP_DBF(DBF_INFO,
"no queue device with default domain %d available\n",
ap_domain_index);
-out:
mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
}
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index 0aa4b3c..576ac08 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -14,6 +14,9 @@
#include <asm/facility.h>
#include "ap_bus.h"
+#include "ap_debug.h"
+
+static void __ap_flush_queue(struct ap_queue *aq);
/**
* ap_queue_enable_interruption(): Enable interruption on an AP queue.
@@ -541,7 +544,25 @@ static ssize_t reset_show(struct device *dev,
return rc;
}
-static DEVICE_ATTR_RO(reset);
+static ssize_t reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+
+ spin_lock_bh(&aq->lock);
+ __ap_flush_queue(aq);
+ aq->state = AP_STATE_RESET_START;
+ ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+ spin_unlock_bh(&aq->lock);
+
+ AP_DBF(DBF_INFO, "reset queue=%02x.%04x triggered by user\n",
+ AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(reset);
static ssize_t interrupt_show(struct device *dev,
struct device_attribute *attr, char *buf)
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 2e1a27b..2126f4c 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -62,6 +62,7 @@ struct error_hdr {
#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
#define REP82_ERROR_RESERVED_FIELD 0x88
#define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A
+#define REP82_ERROR_FILTERED_BY_HYPERVISOR 0x8B
#define REP82_ERROR_TRANSPORT_FAIL 0x90
#define REP82_ERROR_PACKET_TRUNCATED 0xA0
#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
@@ -92,6 +93,7 @@ static inline int convert_error(struct zcrypt_queue *zq,
case REP82_ERROR_INVALID_DOMAIN_PRECHECK:
case REP82_ERROR_INVALID_DOMAIN_PENDING:
case REP82_ERROR_INVALID_SPECIAL_CMD:
+ case REP82_ERROR_FILTERED_BY_HYPERVISOR:
// REP88_ERROR_INVALID_KEY // '82' CEX2A
// REP88_ERROR_OPERAND // '84' CEX2A
// REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 461afc2..81e2c59 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -901,44 +901,6 @@ static void qeth_send_control_data_cb(struct qeth_channel *channel,
qeth_release_buffer(channel, iob);
}
-static int qeth_setup_channel(struct qeth_channel *channel, bool alloc_buffers)
-{
- int cnt;
-
- QETH_DBF_TEXT(SETUP, 2, "setupch");
-
- channel->ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
- if (!channel->ccw)
- return -ENOMEM;
- channel->state = CH_STATE_DOWN;
- atomic_set(&channel->irq_pending, 0);
- init_waitqueue_head(&channel->wait_q);
-
- if (!alloc_buffers)
- return 0;
-
- for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) {
- channel->iob[cnt].data =
- kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
- if (channel->iob[cnt].data == NULL)
- break;
- channel->iob[cnt].state = BUF_STATE_FREE;
- channel->iob[cnt].channel = channel;
- channel->iob[cnt].callback = qeth_send_control_data_cb;
- channel->iob[cnt].rc = 0;
- }
- if (cnt < QETH_CMD_BUFFER_NO) {
- kfree(channel->ccw);
- while (cnt-- > 0)
- kfree(channel->iob[cnt].data);
- return -ENOMEM;
- }
- channel->io_buf_no = 0;
- spin_lock_init(&channel->iob_lock);
-
- return 0;
-}
-
static int qeth_set_thread_start_bit(struct qeth_card *card,
unsigned long thread)
{
@@ -1339,14 +1301,61 @@ static void qeth_free_buffer_pool(struct qeth_card *card)
static void qeth_clean_channel(struct qeth_channel *channel)
{
+ struct ccw_device *cdev = channel->ccwdev;
int cnt;
QETH_DBF_TEXT(SETUP, 2, "freech");
+
+ spin_lock_irq(get_ccwdev_lock(cdev));
+ cdev->handler = NULL;
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+
for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++)
kfree(channel->iob[cnt].data);
kfree(channel->ccw);
}
+static int qeth_setup_channel(struct qeth_channel *channel, bool alloc_buffers)
+{
+ struct ccw_device *cdev = channel->ccwdev;
+ int cnt;
+
+ QETH_DBF_TEXT(SETUP, 2, "setupch");
+
+ channel->ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+ if (!channel->ccw)
+ return -ENOMEM;
+ channel->state = CH_STATE_DOWN;
+ atomic_set(&channel->irq_pending, 0);
+ init_waitqueue_head(&channel->wait_q);
+
+ spin_lock_irq(get_ccwdev_lock(cdev));
+ cdev->handler = qeth_irq;
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+
+ if (!alloc_buffers)
+ return 0;
+
+ for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) {
+ channel->iob[cnt].data =
+ kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
+ if (channel->iob[cnt].data == NULL)
+ break;
+ channel->iob[cnt].state = BUF_STATE_FREE;
+ channel->iob[cnt].channel = channel;
+ channel->iob[cnt].callback = qeth_send_control_data_cb;
+ channel->iob[cnt].rc = 0;
+ }
+ if (cnt < QETH_CMD_BUFFER_NO) {
+ qeth_clean_channel(channel);
+ return -ENOMEM;
+ }
+ channel->io_buf_no = 0;
+ spin_lock_init(&channel->iob_lock);
+
+ return 0;
+}
+
static void qeth_set_single_write_queues(struct qeth_card *card)
{
if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) &&
@@ -1498,7 +1507,7 @@ static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr)
CARD_BUS_ID(card), card->info.mcl_level);
}
-static struct qeth_card *qeth_alloc_card(void)
+static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev)
{
struct qeth_card *card;
@@ -1507,6 +1516,11 @@ static struct qeth_card *qeth_alloc_card(void)
if (!card)
goto out;
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
+
+ card->gdev = gdev;
+ CARD_RDEV(card) = gdev->cdev[0];
+ CARD_WDEV(card) = gdev->cdev[1];
+ CARD_DDEV(card) = gdev->cdev[2];
if (qeth_setup_channel(&card->read, true))
goto out_ip;
if (qeth_setup_channel(&card->write, true))
@@ -5745,7 +5759,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev));
- card = qeth_alloc_card();
+ card = qeth_alloc_card(gdev);
if (!card) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", -ENOMEM);
rc = -ENOMEM;
@@ -5761,15 +5775,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
goto err_card;
}
- card->read.ccwdev = gdev->cdev[0];
- card->write.ccwdev = gdev->cdev[1];
- card->data.ccwdev = gdev->cdev[2];
dev_set_drvdata(&gdev->dev, card);
- card->gdev = gdev;
- gdev->cdev[0]->handler = qeth_irq;
- gdev->cdev[1]->handler = qeth_irq;
- gdev->cdev[2]->handler = qeth_irq;
-
qeth_setup_card(card);
rc = qeth_update_from_chp_desc(card);
if (rc)
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index c1c35ecc..95669d4 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -789,7 +789,10 @@ static int __qeth_l2_open(struct net_device *dev)
if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
napi_enable(&card->napi);
+ local_bh_disable();
napi_schedule(&card->napi);
+ /* kick-start the NAPI softirq: */
+ local_bh_enable();
} else
rc = -EIO;
return rc;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 9c5e801..52e0ae4 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2414,7 +2414,10 @@ static int __qeth_l3_open(struct net_device *dev)
if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
napi_enable(&card->napi);
+ local_bh_disable();
napi_schedule(&card->napi);
+ /* kick-start the NAPI softirq: */
+ local_bh_enable();
} else
rc = -EIO;
return rc;
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 3b368fc..946380f 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -94,11 +94,9 @@ void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req)
memcpy(rec->u.res.fsf_status_qual, &q_head->fsf_status_qual,
FSF_STATUS_QUALIFIER_SIZE);
- if (req->fsf_command != FSF_QTCB_FCP_CMND) {
- rec->pl_len = q_head->log_length;
- zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start,
- rec->pl_len, "fsf_res", req->req_id);
- }
+ rec->pl_len = q_head->log_length;
+ zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start,
+ rec->pl_len, "fsf_res", req->req_id);
debug_event(dbf->hba, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->hba_lock, flags);
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 332701d..f602b42 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -172,9 +172,6 @@ static int zfcp_erp_handle_failed(int want, struct zfcp_adapter *adapter,
adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
}
break;
- default:
- need = 0;
- break;
}
return need;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index df888506..91aa4bfc 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -2104,11 +2104,8 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req,
break;
case FSF_CMND_LENGTH_NOT_VALID:
dev_err(&req->adapter->ccw_device->dev,
- "Incorrect CDB length %d, LUN 0x%016Lx on "
- "port 0x%016Lx closed\n",
- req->qtcb->bottom.io.fcp_cmnd_length,
- (unsigned long long)zfcp_scsi_dev_lun(sdev),
- (unsigned long long)zfcp_sdev->port->wwpn);
+ "Incorrect FCP_CMND length %d, FCP device closed\n",
+ req->qtcb->bottom.io.fcp_cmnd_length);
zfcp_erp_adapter_shutdown(req->adapter, 0, "fssfch4");
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 7c09700..a8ac480 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -862,7 +862,7 @@
config 53C700_LE_ON_BE
bool
- depends on SCSI_LASI700
+ depends on SCSI_LASI700 || SCSI_SNI_53C710
default y
config SCSI_STEX
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index 5160d62..95a3e3b 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -129,8 +129,12 @@
#define NCR5380_release_dma_irq(x)
#endif
+static unsigned int disconnect_mask = ~0;
+module_param(disconnect_mask, int, 0444);
+
static int do_abort(struct Scsi_Host *);
static void do_reset(struct Scsi_Host *);
+static void bus_reset_cleanup(struct Scsi_Host *);
/**
* initialize_SCp - init the scsi pointer field
@@ -513,16 +517,15 @@ static void complete_cmd(struct Scsi_Host *instance,
if (hostdata->sensing == cmd) {
/* Autosense processing ends here */
- if ((cmd->result & 0xff) != SAM_STAT_GOOD) {
+ if (status_byte(cmd->result) != GOOD) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
- set_host_byte(cmd, DID_ERROR);
- } else
+ } else {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ set_driver_byte(cmd, DRIVER_SENSE);
+ }
hostdata->sensing = NULL;
}
- hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
-
cmd->scsi_done(cmd);
}
@@ -886,7 +889,14 @@ static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id)
/* Probably Bus Reset */
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- dsprintk(NDEBUG_INTR, instance, "unknown interrupt\n");
+ if (sr & SR_RST) {
+ /* Certainly Bus Reset */
+ shost_printk(KERN_WARNING, instance,
+ "bus reset interrupt\n");
+ bus_reset_cleanup(instance);
+ } else {
+ dsprintk(NDEBUG_INTR, instance, "unknown interrupt\n");
+ }
#ifdef SUN3_SCSI_VME
dregs->csr |= CSR_DMA_ENABLE;
#endif
@@ -904,20 +914,16 @@ static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-/*
- * Function : int NCR5380_select(struct Scsi_Host *instance,
- * struct scsi_cmnd *cmd)
+/**
+ * NCR5380_select - attempt arbitration and selection for a given command
+ * @instance: the Scsi_Host instance
+ * @cmd: the scsi_cmnd to execute
*
- * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- * including ARBITRATION, SELECTION, and initial message out for
- * IDENTIFY and queue messages.
+ * This routine establishes an I_T_L nexus for a SCSI command. This involves
+ * ARBITRATION, SELECTION and MESSAGE OUT phases and an IDENTIFY message.
*
- * Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute.
- *
- * Returns cmd if selection failed but should be retried,
- * NULL if selection failed and should not be retried, or
- * NULL if selection succeeded (hostdata->connected == cmd).
+ * Returns true if the operation should be retried.
+ * Returns false if it should not be retried.
*
* Side effects :
* If bus busy, arbitration failed, etc, NCR5380_select() will exit
@@ -925,16 +931,15 @@ static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id)
* SELECT_ENABLE will be set appropriately, the NCR5380
* will cease to drive any SCSI bus signals.
*
- * If successful : I_T_L or I_T_L_Q nexus will be established,
- * instance->connected will be set to cmd.
+ * If successful : the I_T_L nexus will be established, and
+ * hostdata->connected will be set to cmd.
* SELECT interrupt will be disabled.
*
* If failed (no target) : cmd->scsi_done() will be called, and the
* cmd->result host byte set to DID_BAD_TARGET.
*/
-static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
- struct scsi_cmnd *cmd)
+static bool NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
__releases(&hostdata->lock) __acquires(&hostdata->lock)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
@@ -942,6 +947,10 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
unsigned char *data;
int len;
int err;
+ bool ret = true;
+ bool can_disconnect = instance->irq != NO_IRQ &&
+ cmd->cmnd[0] != REQUEST_SENSE &&
+ (disconnect_mask & BIT(scmd_id(cmd)));
NCR5380_dprint(NDEBUG_ARBITRATION, instance);
dsprintk(NDEBUG_ARBITRATION, instance, "starting arbitration, id = %d\n",
@@ -950,7 +959,7 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
/*
* Arbitration and selection phases are slow and involve dropping the
* lock, so we have to watch out for EH. An exception handler may
- * change 'selecting' to NULL. This function will then return NULL
+ * change 'selecting' to NULL. This function will then return false
* so that the caller will forget about 'cmd'. (During information
* transfer phases, EH may change 'connected' to NULL.)
*/
@@ -986,7 +995,7 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
if (!hostdata->selecting) {
/* Command was aborted */
NCR5380_write(MODE_REG, MR_BASE);
- return NULL;
+ return false;
}
if (err < 0) {
NCR5380_write(MODE_REG, MR_BASE);
@@ -1035,7 +1044,7 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
if (!hostdata->selecting) {
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- return NULL;
+ return false;
}
dsprintk(NDEBUG_ARBITRATION, instance, "won arbitration\n");
@@ -1118,13 +1127,13 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
/* Can't touch cmd if it has been reclaimed by the scsi ML */
if (!hostdata->selecting)
- return NULL;
+ return false;
cmd->result = DID_BAD_TARGET << 16;
complete_cmd(instance, cmd);
dsprintk(NDEBUG_SELECTION, instance,
"target did not respond within 250ms\n");
- cmd = NULL;
+ ret = false;
goto out;
}
@@ -1156,12 +1165,12 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
}
if (!hostdata->selecting) {
do_abort(instance);
- return NULL;
+ return false;
}
dsprintk(NDEBUG_SELECTION, instance, "target %d selected, going into MESSAGE OUT phase.\n",
scmd_id(cmd));
- tmp[0] = IDENTIFY(((instance->irq == NO_IRQ) ? 0 : 1), cmd->device->lun);
+ tmp[0] = IDENTIFY(can_disconnect, cmd->device->lun);
len = 1;
data = tmp;
@@ -1172,7 +1181,7 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
cmd->result = DID_ERROR << 16;
complete_cmd(instance, cmd);
dsprintk(NDEBUG_SELECTION, instance, "IDENTIFY message transfer failed\n");
- cmd = NULL;
+ ret = false;
goto out;
}
@@ -1187,13 +1196,13 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
initialize_SCp(cmd);
- cmd = NULL;
+ ret = false;
out:
if (!hostdata->selecting)
return NULL;
hostdata->selecting = NULL;
- return cmd;
+ return ret;
}
/*
@@ -1712,6 +1721,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
cmd->result = DID_ERROR << 16;
complete_cmd(instance, cmd);
hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
return;
#endif
case PHASE_DATAIN:
@@ -1794,6 +1804,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
cmd, scmd_id(cmd), cmd->device->lun);
hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
cmd->result &= ~0xffff;
cmd->result |= cmd->SCp.Status;
@@ -1947,6 +1958,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
NCR5380_transfer_pio(instance, &phase, &len, &data);
if (msgout == ABORT) {
hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
cmd->result = DID_ERROR << 16;
complete_cmd(instance, cmd);
maybe_release_dma_irq(instance);
@@ -2009,8 +2021,11 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
NCR5380_write(MODE_REG, MR_BASE);
target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
-
- dsprintk(NDEBUG_RESELECTION, instance, "reselect\n");
+ if (!target_mask || target_mask & (target_mask - 1)) {
+ shost_printk(KERN_WARNING, instance,
+ "reselect: bad target_mask 0x%02x\n", target_mask);
+ return;
+ }
/*
* At this point, we have detected that our SCSI ID is on the bus,
@@ -2024,6 +2039,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
if (NCR5380_poll_politely(hostdata,
STATUS_REG, SR_SEL, 0, 2 * HZ) < 0) {
+ shost_printk(KERN_ERR, instance, "reselect: !SEL timeout\n");
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
return;
}
@@ -2035,6 +2051,10 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
if (NCR5380_poll_politely(hostdata,
STATUS_REG, SR_REQ, SR_REQ, 2 * HZ) < 0) {
+ if ((NCR5380_read(STATUS_REG) & (SR_BSY | SR_SEL)) == 0)
+ /* BUS FREE phase */
+ return;
+ shost_printk(KERN_ERR, instance, "reselect: REQ timeout\n");
do_abort(instance);
return;
}
@@ -2096,13 +2116,16 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
dsprintk(NDEBUG_RESELECTION | NDEBUG_QUEUES, instance,
"reselect: removed %p from disconnected queue\n", tmp);
} else {
+ int target = ffs(target_mask) - 1;
+
shost_printk(KERN_ERR, instance, "target bitmask 0x%02x lun %d not in disconnected queue.\n",
target_mask, lun);
/*
* Since we have an established nexus that we can't do anything
* with, we must abort it.
*/
- do_abort(instance);
+ if (do_abort(instance) == 0)
+ hostdata->busy[target] &= ~(1 << lun);
return;
}
@@ -2267,15 +2290,16 @@ static int NCR5380_abort(struct scsi_cmnd *cmd)
if (list_del_cmd(&hostdata->autosense, cmd)) {
dsprintk(NDEBUG_ABORT, instance,
"abort: removed %p from sense queue\n", cmd);
- set_host_byte(cmd, DID_ERROR);
complete_cmd(instance, cmd);
}
out:
if (result == FAILED)
dsprintk(NDEBUG_ABORT, instance, "abort: failed to abort %p\n", cmd);
- else
+ else {
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
dsprintk(NDEBUG_ABORT, instance, "abort: successfully aborted %p\n", cmd);
+ }
queue_work(hostdata->work_q, &hostdata->main_task);
maybe_release_dma_irq(instance);
@@ -2285,31 +2309,12 @@ static int NCR5380_abort(struct scsi_cmnd *cmd)
}
-/**
- * NCR5380_host_reset - reset the SCSI host
- * @cmd: SCSI command undergoing EH
- *
- * Returns SUCCESS
- */
-
-static int NCR5380_host_reset(struct scsi_cmnd *cmd)
+static void bus_reset_cleanup(struct Scsi_Host *instance)
{
- struct Scsi_Host *instance = cmd->device->host;
struct NCR5380_hostdata *hostdata = shost_priv(instance);
int i;
- unsigned long flags;
struct NCR5380_cmd *ncmd;
- spin_lock_irqsave(&hostdata->lock, flags);
-
-#if (NDEBUG & NDEBUG_ANY)
- scmd_printk(KERN_INFO, cmd, __func__);
-#endif
- NCR5380_dprint(NDEBUG_ANY, instance);
- NCR5380_dprint_phase(NDEBUG_ANY, instance);
-
- do_reset(instance);
-
/* reset NCR registers */
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(TARGET_COMMAND_REG, 0);
@@ -2321,11 +2326,6 @@ static int NCR5380_host_reset(struct scsi_cmnd *cmd)
* commands!
*/
- if (list_del_cmd(&hostdata->unissued, cmd)) {
- cmd->result = DID_RESET << 16;
- cmd->scsi_done(cmd);
- }
-
if (hostdata->selecting) {
hostdata->selecting->result = DID_RESET << 16;
complete_cmd(instance, hostdata->selecting);
@@ -2343,7 +2343,6 @@ static int NCR5380_host_reset(struct scsi_cmnd *cmd)
list_for_each_entry(ncmd, &hostdata->autosense, list) {
struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd);
- set_host_byte(cmd, DID_RESET);
cmd->scsi_done(cmd);
}
INIT_LIST_HEAD(&hostdata->autosense);
@@ -2360,6 +2359,41 @@ static int NCR5380_host_reset(struct scsi_cmnd *cmd)
queue_work(hostdata->work_q, &hostdata->main_task);
maybe_release_dma_irq(instance);
+}
+
+/**
+ * NCR5380_host_reset - reset the SCSI host
+ * @cmd: SCSI command undergoing EH
+ *
+ * Returns SUCCESS
+ */
+
+static int NCR5380_host_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long flags;
+ struct NCR5380_cmd *ncmd;
+
+ spin_lock_irqsave(&hostdata->lock, flags);
+
+#if (NDEBUG & NDEBUG_ANY)
+ shost_printk(KERN_INFO, instance, __func__);
+#endif
+ NCR5380_dprint(NDEBUG_ANY, instance);
+ NCR5380_dprint_phase(NDEBUG_ANY, instance);
+
+ list_for_each_entry(ncmd, &hostdata->unissued, list) {
+ struct scsi_cmnd *scmd = NCR5380_to_scmd(ncmd);
+
+ scmd->result = DID_RESET << 16;
+ scmd->scsi_done(scmd);
+ }
+ INIT_LIST_HEAD(&hostdata->unissued);
+
+ do_reset(instance);
+ bus_reset_cleanup(instance);
+
spin_unlock_irqrestore(&hostdata->lock, flags);
return SUCCESS;
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
index 8a6d002..5935fd6 100644
--- a/drivers/scsi/NCR5380.h
+++ b/drivers/scsi/NCR5380.h
@@ -275,7 +275,7 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id);
static void NCR5380_main(struct work_struct *work);
static const char *NCR5380_info(struct Scsi_Host *instance);
static void NCR5380_reselect(struct Scsi_Host *instance);
-static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *);
+static bool NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *);
static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
static int NCR5380_poll_politely2(struct NCR5380_hostdata *,
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 12316ef..c75d469 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -4135,9 +4135,9 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
pci_read_config_byte(acb->pdev, i, &value[i]);
}
/* hardware reset signal */
- if ((acb->dev_id == 0x1680)) {
+ if (acb->dev_id == 0x1680) {
writel(ARCMSR_ARC1680_BUS_RESET, &pmuA->reserved1[0]);
- } else if ((acb->dev_id == 0x1880)) {
+ } else if (acb->dev_id == 0x1880) {
do {
count++;
writel(0xF, &pmuC->write_sequence);
@@ -4161,7 +4161,7 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
} while (((readl(&pmuE->host_diagnostic_3xxx) &
ARCMSR_ARC1884_DiagWrite_ENABLE) == 0) && (count < 5));
writel(ARCMSR_ARC188X_RESET_ADAPTER, &pmuE->host_diagnostic_3xxx);
- } else if ((acb->dev_id == 0x1214)) {
+ } else if (acb->dev_id == 0x1214) {
writel(0x20, pmuD->reset_request);
} else {
pci_write_config_byte(acb->pdev, 0x84, 0x20);
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index 89f5154..764c46d7 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -742,7 +742,7 @@ static int __init atari_scsi_probe(struct platform_device *pdev)
atari_scsi_template.sg_tablesize = SG_ALL;
} else {
atari_scsi_template.can_queue = 1;
- atari_scsi_template.sg_tablesize = SG_NONE;
+ atari_scsi_template.sg_tablesize = 1;
}
if (setup_can_queue > 0)
@@ -751,8 +751,8 @@ static int __init atari_scsi_probe(struct platform_device *pdev)
if (setup_cmd_per_lun > 0)
atari_scsi_template.cmd_per_lun = setup_cmd_per_lun;
- /* Leave sg_tablesize at 0 on a Falcon! */
- if (ATARIHW_PRESENT(TT_SCSI) && setup_sg_tablesize >= 0)
+ /* Don't increase sg_tablesize on Falcon! */
+ if (ATARIHW_PRESENT(TT_SCSI) && setup_sg_tablesize > 0)
atari_scsi_template.sg_tablesize = setup_sg_tablesize;
if (setup_hostid >= 0) {
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index 3d0c96a..c19c26e 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -1453,7 +1453,7 @@ union bfa_aen_data_u {
struct bfa_aen_entry_s {
struct list_head qe;
enum bfa_aen_category aen_category;
- u32 aen_type;
+ int aen_type;
union bfa_aen_data_u aen_data;
u64 aen_tv_sec;
u64 aen_tv_usec;
diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h
index e61ed8d..bd4ac18 100644
--- a/drivers/scsi/bfa/bfad_im.h
+++ b/drivers/scsi/bfa/bfad_im.h
@@ -143,7 +143,7 @@ struct bfad_im_s {
static inline void bfad_im_post_vendor_event(struct bfa_aen_entry_s *entry,
struct bfad_s *drv, int cnt,
enum bfa_aen_category cat,
- enum bfa_ioc_aen_event evt)
+ int evt)
{
struct timespec64 ts;
diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c
index ed2dae6..1793981 100644
--- a/drivers/scsi/csiostor/csio_init.c
+++ b/drivers/scsi/csiostor/csio_init.c
@@ -649,7 +649,7 @@ csio_shost_init(struct csio_hw *hw, struct device *dev,
if (csio_lnode_init(ln, hw, pln))
goto err_shost_put;
- if (scsi_add_host(shost, dev))
+ if (scsi_add_host_with_dma(shost, dev, &hw->pdev->dev))
goto err_lnode_exit;
return ln;
diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c
index cc5611e..a8e29e3 100644
--- a/drivers/scsi/csiostor/csio_lnode.c
+++ b/drivers/scsi/csiostor/csio_lnode.c
@@ -301,6 +301,7 @@ csio_ln_fdmi_rhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
struct fc_fdmi_port_name *port_name;
uint8_t buf[64];
uint8_t *fc4_type;
+ unsigned long flags;
if (fdmi_req->wr_status != FW_SUCCESS) {
csio_ln_dbg(ln, "WR error:%x in processing fdmi rhba cmd\n",
@@ -385,13 +386,13 @@ csio_ln_fdmi_rhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
len = (uint32_t)(pld - (uint8_t *)cmd);
/* Submit FDMI RPA request */
- spin_lock_irq(&hw->lock);
+ spin_lock_irqsave(&hw->lock, flags);
if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_done,
FCOE_CT, &fdmi_req->dma_buf, len)) {
CSIO_INC_STATS(ln, n_fdmi_err);
csio_ln_dbg(ln, "Failed to issue fdmi rpa req\n");
}
- spin_unlock_irq(&hw->lock);
+ spin_unlock_irqrestore(&hw->lock, flags);
}
/*
@@ -412,6 +413,7 @@ csio_ln_fdmi_dprt_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
struct fc_fdmi_rpl *reg_pl;
struct fs_fdmi_attrs *attrib_blk;
uint8_t buf[64];
+ unsigned long flags;
if (fdmi_req->wr_status != FW_SUCCESS) {
csio_ln_dbg(ln, "WR error:%x in processing fdmi dprt cmd\n",
@@ -491,13 +493,13 @@ csio_ln_fdmi_dprt_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
attrib_blk->numattrs = htonl(numattrs);
/* Submit FDMI RHBA request */
- spin_lock_irq(&hw->lock);
+ spin_lock_irqsave(&hw->lock, flags);
if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_rhba_cbfn,
FCOE_CT, &fdmi_req->dma_buf, len)) {
CSIO_INC_STATS(ln, n_fdmi_err);
csio_ln_dbg(ln, "Failed to issue fdmi rhba req\n");
}
- spin_unlock_irq(&hw->lock);
+ spin_unlock_irqrestore(&hw->lock, flags);
}
/*
@@ -512,6 +514,7 @@ csio_ln_fdmi_dhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
void *cmd;
struct fc_fdmi_port_name *port_name;
uint32_t len;
+ unsigned long flags;
if (fdmi_req->wr_status != FW_SUCCESS) {
csio_ln_dbg(ln, "WR error:%x in processing fdmi dhba cmd\n",
@@ -542,13 +545,13 @@ csio_ln_fdmi_dhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
len += sizeof(*port_name);
/* Submit FDMI request */
- spin_lock_irq(&hw->lock);
+ spin_lock_irqsave(&hw->lock, flags);
if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_dprt_cbfn,
FCOE_CT, &fdmi_req->dma_buf, len)) {
CSIO_INC_STATS(ln, n_fdmi_err);
csio_ln_dbg(ln, "Failed to issue fdmi dprt req\n");
}
- spin_unlock_irq(&hw->lock);
+ spin_unlock_irqrestore(&hw->lock, flags);
}
/**
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index 1ed2cd8..3943347 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -1969,6 +1969,11 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
xferred -= psge->length;
} else {
/* Partial SG entry done */
+ pci_dma_sync_single_for_cpu(srb->dcb->
+ acb->dev,
+ srb->sg_bus_addr,
+ SEGMENTX_LEN,
+ PCI_DMA_TODEVICE);
psge->length -= xferred;
psge->address += xferred;
srb->sg_index = idx;
@@ -3447,14 +3452,12 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
}
}
- if (dir != PCI_DMA_NONE && scsi_sg_count(cmd))
- pci_dma_sync_sg_for_cpu(acb->dev, scsi_sglist(cmd),
- scsi_sg_count(cmd), dir);
-
ckc_only = 0;
/* Check Error Conditions */
ckc_e:
+ pci_unmap_srb(acb, srb);
+
if (cmd->cmnd[0] == INQUIRY) {
unsigned char *base = NULL;
struct ScsiInqData *ptr;
@@ -3507,7 +3510,6 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
cmd, cmd->result);
srb_free_insert(acb, srb);
}
- pci_unmap_srb(acb, srb);
cmd->scsi_done(cmd);
waiting_process_next(acb);
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 9c21938..c95c782 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -526,6 +526,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
unsigned int tpg_desc_tbl_off;
unsigned char orig_transition_tmo;
unsigned long flags;
+ bool transitioning_sense = false;
if (!pg->expiry) {
unsigned long transition_tmo = ALUA_FAILOVER_TIMEOUT * HZ;
@@ -586,13 +587,19 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
goto retry;
}
/*
- * Retry on ALUA state transition or if any
- * UNIT ATTENTION occurred.
+ * If the array returns with 'ALUA state transition'
+ * sense code here it cannot return RTPG data during
+ * transition. So set the state to 'transitioning' directly.
*/
if (sense_hdr.sense_key == NOT_READY &&
- sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
- err = SCSI_DH_RETRY;
- else if (sense_hdr.sense_key == UNIT_ATTENTION)
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) {
+ transitioning_sense = true;
+ goto skip_rtpg;
+ }
+ /*
+ * Retry on any other UNIT ATTENTION occurred.
+ */
+ if (sense_hdr.sense_key == UNIT_ATTENTION)
err = SCSI_DH_RETRY;
if (err == SCSI_DH_RETRY &&
pg->expiry != 0 && time_before(jiffies, pg->expiry)) {
@@ -680,7 +687,11 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
off = 8 + (desc[7] * 4);
}
+ skip_rtpg:
spin_lock_irqsave(&pg->lock, flags);
+ if (transitioning_sense)
+ pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
+
sdev_printk(KERN_INFO, sdev,
"%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n",
ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state),
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 6c7d2e2..d499c44 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -220,7 +220,7 @@ struct hisi_sas_hw {
int (*slot_index_alloc)(struct hisi_hba *hisi_hba, int *slot_idx,
struct domain_device *device);
struct hisi_sas_device *(*alloc_dev)(struct domain_device *device);
- void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*sl_notify_ssp)(struct hisi_hba *hisi_hba, int phy_no);
int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq);
void (*start_delivery)(struct hisi_sas_dq *dq);
void (*prep_ssp)(struct hisi_hba *hisi_hba,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index fd9d82c..3319167 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -485,7 +485,13 @@ static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags,
struct hisi_sas_dq *dq = NULL;
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) {
- if (in_softirq())
+ /*
+ * For IOs from upper layer, it may already disable preempt
+ * in the IO path, if disable preempt again in down(),
+ * function schedule() will report schedule_bug(), so check
+ * preemptible() before goto down().
+ */
+ if (!preemptible())
return -EINVAL;
down(&hisi_hba->sem);
@@ -716,7 +722,8 @@ static void hisi_sas_phyup_work(struct work_struct *work)
struct asd_sas_phy *sas_phy = &phy->sas_phy;
int phy_no = sas_phy->id;
- hisi_hba->hw->sl_notify(hisi_hba, phy_no); /* This requires a sleep */
+ if (phy->identify.target_port_protocols == SAS_PROTOCOL_SSP)
+ hisi_hba->hw->sl_notify_ssp(hisi_hba, phy_no);
hisi_sas_bytes_dmaed(hisi_hba, phy_no);
}
@@ -885,7 +892,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
return hisi_sas_task_exec(task, gfp_flags, 0, NULL);
}
-static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
+static int hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
struct sas_phy_linkrates *r)
{
struct sas_phy_linkrates _r;
@@ -894,6 +901,9 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
struct asd_sas_phy *sas_phy = &phy->sas_phy;
enum sas_linkrate min, max;
+ if (r->minimum_linkrate > SAS_LINK_RATE_1_5_GBPS)
+ return -EINVAL;
+
if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) {
max = sas_phy->phy->maximum_linkrate;
min = r->minimum_linkrate;
@@ -901,15 +911,20 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
max = r->maximum_linkrate;
min = sas_phy->phy->minimum_linkrate;
} else
- return;
+ return -EINVAL;
_r.maximum_linkrate = max;
_r.minimum_linkrate = min;
+ sas_phy->phy->maximum_linkrate = max;
+ sas_phy->phy->minimum_linkrate = min;
+
hisi_hba->hw->phy_disable(hisi_hba, phy_no);
msleep(100);
hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r);
hisi_hba->hw->phy_start(hisi_hba, phy_no);
+
+ return 0;
}
static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
@@ -935,8 +950,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
break;
case PHY_FUNC_SET_LINK_RATE:
- hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata);
- break;
+ return hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata);
case PHY_FUNC_GET_EVENTS:
if (hisi_hba->hw->get_events) {
hisi_hba->hw->get_events(hisi_hba, phy_no);
@@ -952,8 +966,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
static void hisi_sas_task_done(struct sas_task *task)
{
- if (!del_timer(&task->slow_task->timer))
- return;
+ del_timer(&task->slow_task->timer);
complete(&task->slow_task->completion);
}
@@ -962,13 +975,17 @@ static void hisi_sas_tmf_timedout(struct timer_list *t)
struct sas_task_slow *slow = from_timer(slow, t, timer);
struct sas_task *task = slow->task;
unsigned long flags;
+ bool is_completed = true;
spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ is_completed = false;
+ }
spin_unlock_irqrestore(&task->task_state_lock, flags);
- complete(&task->slow_task->completion);
+ if (!is_completed)
+ complete(&task->slow_task->completion);
}
#define TASK_TIMEOUT 20
@@ -1021,8 +1038,16 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
struct hisi_sas_slot *slot = task->lldd_task;
dev_err(dev, "abort tmf: TMF task timeout and not done\n");
- if (slot)
+ if (slot) {
+ struct hisi_sas_cq *cq =
+ &hisi_hba->cq[slot->dlvry_queue];
+ /*
+ * flush tasklet to avoid free'ing task
+ * before using task in IO completion
+ */
+ tasklet_kill(&cq->tasklet);
slot->task = NULL;
+ }
goto ex_err;
} else
@@ -1398,6 +1423,17 @@ static int hisi_sas_abort_task(struct sas_task *task)
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ struct hisi_sas_slot *slot = task->lldd_task;
+ struct hisi_sas_cq *cq;
+
+ if (slot) {
+ /*
+ * flush tasklet to avoid free'ing task
+ * before using task in IO completion
+ */
+ cq = &hisi_hba->cq[slot->dlvry_queue];
+ tasklet_kill(&cq->tasklet);
+ }
spin_unlock_irqrestore(&task->task_state_lock, flags);
rc = TMF_RESP_FUNC_COMPLETE;
goto out;
@@ -1453,12 +1489,19 @@ static int hisi_sas_abort_task(struct sas_task *task)
/* SMP */
struct hisi_sas_slot *slot = task->lldd_task;
u32 tag = slot->idx;
+ struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
rc = hisi_sas_internal_task_abort(hisi_hba, device,
HISI_SAS_INT_ABT_CMD, tag);
if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
- task->lldd_task)
- hisi_sas_do_release_task(hisi_hba, task, slot);
+ task->lldd_task) {
+ /*
+ * flush tasklet to avoid free'ing task
+ * before using task in IO completion
+ */
+ tasklet_kill(&cq->tasklet);
+ slot->task = NULL;
+ }
}
out:
@@ -1825,8 +1868,16 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
struct hisi_sas_slot *slot = task->lldd_task;
- if (slot)
+ if (slot) {
+ struct hisi_sas_cq *cq =
+ &hisi_hba->cq[slot->dlvry_queue];
+ /*
+ * flush tasklet to avoid free'ing task
+ * before using task in IO completion
+ */
+ tasklet_kill(&cq->tasklet);
slot->task = NULL;
+ }
dev_err(dev, "internal task abort: timeout and not done.\n");
res = -EIO;
goto exit;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
index 410eccf..8aa3222 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -834,7 +834,7 @@ static void phys_init_v1_hw(struct hisi_hba *hisi_hba)
mod_timer(timer, jiffies + HZ);
}
-static void sl_notify_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -1822,7 +1822,7 @@ static struct scsi_host_template sht_v1_hw = {
static const struct hisi_sas_hw hisi_sas_v1_hw = {
.hw_init = hisi_sas_v1_init,
.setup_itct = setup_itct_v1_hw,
- .sl_notify = sl_notify_v1_hw,
+ .sl_notify_ssp = sl_notify_ssp_v1_hw,
.clear_itct = clear_itct_v1_hw,
.prep_smp = prep_smp_v1_hw,
.prep_ssp = prep_ssp_v1_hw,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 1c4ea58..ebc984f 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -1584,7 +1584,7 @@ static void phys_init_v2_hw(struct hisi_hba *hisi_hba)
}
}
-static void sl_notify_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -2481,7 +2481,6 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
}
out:
- hisi_sas_slot_task_free(hisi_hba, task, slot);
sts = ts->stat;
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
@@ -2491,6 +2490,7 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
}
task->task_state_flags |= SAS_TASK_STATE_DONE;
spin_unlock_irqrestore(&task->task_state_lock, flags);
+ hisi_sas_slot_task_free(hisi_hba, task, slot);
if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
spin_lock_irqsave(&device->done_lock, flags);
@@ -3575,7 +3575,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
.setup_itct = setup_itct_v2_hw,
.slot_index_alloc = slot_index_alloc_quirk_v2_hw,
.alloc_dev = alloc_dev_quirk_v2_hw,
- .sl_notify = sl_notify_v2_hw,
+ .sl_notify_ssp = sl_notify_ssp_v2_hw,
.get_wideport_bitmap = get_wideport_bitmap_v2_hw,
.clear_itct = clear_itct_v2_hw,
.free_device = free_device_v2_hw,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 3922b17..ce2f232 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -827,7 +827,7 @@ static void phys_init_v3_hw(struct hisi_hba *hisi_hba)
}
}
-static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -1520,6 +1520,7 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
u32 irq_value, irq_msk;
struct hisi_hba *hisi_hba = p;
struct device *dev = hisi_hba->dev;
+ struct pci_dev *pdev = hisi_hba->pci_dev;
int i;
irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
@@ -1551,6 +1552,17 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
error->msg, irq_value);
queue_work(hisi_hba->wq, &hisi_hba->rst_work);
}
+
+ if (pdev->revision < 0x21) {
+ u32 reg_val;
+
+ reg_val = hisi_sas_read32(hisi_hba,
+ AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, reg_val);
+ }
}
if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) {
@@ -1749,7 +1761,6 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
}
out:
- hisi_sas_slot_task_free(hisi_hba, task, slot);
sts = ts->stat;
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
@@ -1759,6 +1770,7 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
}
task->task_state_flags |= SAS_TASK_STATE_DONE;
spin_unlock_irqrestore(&task->task_state_lock, flags);
+ hisi_sas_slot_task_free(hisi_hba, task, slot);
if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
spin_lock_irqsave(&device->done_lock, flags);
@@ -2115,7 +2127,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = {
.get_wideport_bitmap = get_wideport_bitmap_v3_hw,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
.clear_itct = clear_itct_v3_hw,
- .sl_notify = sl_notify_v3_hw,
+ .sl_notify_ssp = sl_notify_ssp_v3_hw,
.prep_ssp = prep_ssp_v3_hw,
.prep_smp = prep_smp_v3_hw,
.prep_stp = prep_ata_v3_hw,
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index bd6ac6b..fe587ef 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -3485,6 +3485,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
case START_STOP:
scb->scsi_cmd->result = DID_OK << 16;
+ break;
case TEST_UNIT_READY:
case INQUIRY:
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 1ee3868..7b5deae 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -2717,9 +2717,9 @@ enum sci_status sci_controller_continue_io(struct isci_request *ireq)
* the task management request.
* @task_request: the handle to the task request object to start.
*/
-enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
- struct isci_remote_device *idev,
- struct isci_request *ireq)
+enum sci_status sci_controller_start_task(struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq)
{
enum sci_status status;
@@ -2728,7 +2728,7 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
"%s: SCIC Controller starting task from invalid "
"state\n",
__func__);
- return SCI_TASK_FAILURE_INVALID_STATE;
+ return SCI_FAILURE_INVALID_STATE;
}
status = sci_remote_device_start_task(ihost, idev, ireq);
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index b353992..6bc3f02 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -489,7 +489,7 @@ enum sci_status sci_controller_start_io(
struct isci_remote_device *idev,
struct isci_request *ireq);
-enum sci_task_status sci_controller_start_task(
+enum sci_status sci_controller_start_task(
struct isci_host *ihost,
struct isci_remote_device *idev,
struct isci_request *ireq);
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index ed197bc8..2f15170 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -1626,9 +1626,9 @@ static enum sci_status atapi_d2h_reg_frame_handler(struct isci_request *ireq,
if (status == SCI_SUCCESS) {
if (ireq->stp.rsp.status & ATA_ERR)
- status = SCI_IO_FAILURE_RESPONSE_VALID;
+ status = SCI_FAILURE_IO_RESPONSE_VALID;
} else {
- status = SCI_IO_FAILURE_RESPONSE_VALID;
+ status = SCI_FAILURE_IO_RESPONSE_VALID;
}
if (status != SCI_SUCCESS) {
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 6dcaed0..fb6eba3 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -258,7 +258,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
struct isci_tmf *tmf, unsigned long timeout_ms)
{
DECLARE_COMPLETION_ONSTACK(completion);
- enum sci_task_status status = SCI_TASK_FAILURE;
+ enum sci_status status = SCI_FAILURE;
struct isci_request *ireq;
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
@@ -301,7 +301,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
/* start the TMF io. */
status = sci_controller_start_task(ihost, idev, ireq);
- if (status != SCI_TASK_SUCCESS) {
+ if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
"%s: start_io failed - status = 0x%x, request = %p\n",
__func__,
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index b025a0b..55181d2 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -374,8 +374,16 @@ static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
unsigned int noreclaim_flag;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
int rc = 0;
+ if (!tcp_sw_conn->sock) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "Transport not bound to socket!\n");
+ return -EINVAL;
+ }
+
noreclaim_flag = memalloc_noreclaim_save();
while (iscsi_sw_tcp_xmit_qlen(conn)) {
@@ -800,7 +808,8 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
- &addr, param, buf);
+ &addr,
+ (enum iscsi_param)param, buf);
default:
return iscsi_host_get_param(shost, param, buf);
}
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 4ad61cf..7a05c72 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1983,7 +1983,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc);
- spin_lock(&session->frwd_lock);
+ spin_lock_bh(&session->frwd_lock);
task = (struct iscsi_task *)sc->SCp.ptr;
if (!task) {
/*
@@ -2110,7 +2110,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
done:
if (task)
task->last_timeout = jiffies;
- spin_unlock(&session->frwd_lock);
+ spin_unlock_bh(&session->frwd_lock);
ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ?
"timer reset" : "shutdown or nh");
return rc;
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 0148ae62..e320534 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -97,12 +97,21 @@ static int sas_get_port_device(struct asd_sas_port *port)
else
dev->dev_type = SAS_SATA_DEV;
dev->tproto = SAS_PROTOCOL_SATA;
- } else {
+ } else if (port->oob_mode == SAS_OOB_MODE) {
struct sas_identify_frame *id =
(struct sas_identify_frame *) dev->frame_rcvd;
dev->dev_type = id->dev_type;
dev->iproto = id->initiator_bits;
dev->tproto = id->target_bits;
+ } else {
+ /* If the oob mode is OOB_NOT_CONNECTED, the port is
+ * disconnected due to race with PHY down. We cannot
+ * continue to discover this port
+ */
+ sas_put_device(dev);
+ pr_warn("Port %016llx is disconnected when discovering\n",
+ SAS_ADDR(port->attached_sas_addr));
+ return -ENODEV;
}
sas_init_dev(dev);
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index b141d10..3e74fe9 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -614,7 +614,14 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
}
res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
-
+ if (res) {
+ pr_err("ex %016llx phy%02d PHY control failed: %d\n",
+ SAS_ADDR(dev->sas_addr), phy_id, res);
+ } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) {
+ pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n",
+ SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]);
+ res = pc_resp[2];
+ }
kfree(pc_resp);
kfree(pc_req);
return res;
@@ -817,6 +824,26 @@ static struct domain_device *sas_ex_discover_end_dev(
#ifdef CONFIG_SCSI_SAS_ATA
if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) {
+ if (child->linkrate > parent->min_linkrate) {
+ struct sas_phy_linkrates rates = {
+ .maximum_linkrate = parent->min_linkrate,
+ .minimum_linkrate = parent->min_linkrate,
+ };
+ int ret;
+
+ pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ ret = sas_smp_phy_control(parent, phy_id,
+ PHY_FUNC_LINK_RESET, &rates);
+ if (ret) {
+ pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n",
+ SAS_ADDR(child->sas_addr), phy_id, ret);
+ goto out_free;
+ }
+ pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ child->linkrate = child->min_linkrate;
+ }
res = sas_get_ata_info(child, phy);
if (res)
goto out_free;
@@ -2062,14 +2089,11 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
return res;
}
- /* delete the old link */
- if (SAS_ADDR(phy->attached_sas_addr) &&
- SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr)) {
- SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n",
- SAS_ADDR(dev->sas_addr), phy_id,
- SAS_ADDR(phy->attached_sas_addr));
- sas_unregister_devs_sas_addr(dev, phy_id, last);
- }
+ /* we always have to delete the old device when we went here */
+ SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n",
+ SAS_ADDR(dev->sas_addr), phy_id,
+ SAS_ADDR(phy->attached_sas_addr));
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
return sas_discover_new(dev, phy_id);
}
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 43732e8..706aca3 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -490,6 +490,7 @@ struct lpfc_vport {
struct nvme_fc_local_port *localport;
uint8_t nvmei_support; /* driver supports NVME Initiator */
uint32_t last_fcp_wqidx;
+ uint32_t rcv_flogi_cnt; /* How many unsol FLOGIs ACK'd. */
};
struct hbq_s {
@@ -965,7 +966,8 @@ struct lpfc_hba {
struct list_head port_list;
struct lpfc_vport *pport; /* physical lpfc_vport pointer */
uint16_t max_vpi; /* Maximum virtual nports */
-#define LPFC_MAX_VPI 0xFFFF /* Max number of VPI supported */
+#define LPFC_MAX_VPI 0xFF /* Max number VPI supported 0 - 0xff */
+#define LPFC_MAX_VPORTS 0x100 /* Max vports per port, with pport */
uint16_t max_vports; /*
* For IOV HBAs max_vpi can change
* after a reset. max_vports is max
@@ -1235,6 +1237,12 @@ lpfc_sli_read_hs(struct lpfc_hba *phba)
static inline struct lpfc_sli_ring *
lpfc_phba_elsring(struct lpfc_hba *phba)
{
+ /* Return NULL if sli_rev has become invalid due to bad fw */
+ if (phba->sli_rev != LPFC_SLI_REV4 &&
+ phba->sli_rev != LPFC_SLI_REV3 &&
+ phba->sli_rev != LPFC_SLI_REV2)
+ return NULL;
+
if (phba->sli_rev == LPFC_SLI_REV4) {
if (phba->sli4_hba.els_wq)
return phba->sli4_hba.els_wq->pring;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 55cd96e..fe084d4 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1332,7 +1332,7 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
return -EACCES;
if ((phba->sli_rev < LPFC_SLI_REV4) ||
- (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2))
return -EPERM;
@@ -1632,6 +1632,9 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
max_vpi = (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) > 0) ?
(bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - 1) : 0;
+ /* Limit the max we support */
+ if (max_vpi > LPFC_MAX_VPI)
+ max_vpi = LPFC_MAX_VPI;
if (mvpi)
*mvpi = max_vpi;
if (avpi)
@@ -1647,8 +1650,13 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
*axri = pmb->un.varRdConfig.avail_xri;
if (mvpi)
*mvpi = pmb->un.varRdConfig.max_vpi;
- if (avpi)
- *avpi = pmb->un.varRdConfig.avail_vpi;
+ if (avpi) {
+ /* avail_vpi is only valid if link is up and ready */
+ if (phba->link_state == LPFC_HBA_READY)
+ *avpi = pmb->un.varRdConfig.avail_vpi;
+ else
+ *avpi = pmb->un.varRdConfig.max_vpi;
+ }
}
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -3841,8 +3849,9 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
val);
return -EINVAL;
}
- if (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC &&
- val == 4) {
+ if ((phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC ||
+ phba->pcidev->device == PCI_DEVICE_ID_LANCER_G7_FC) &&
+ val == 4) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
"3114 Loop mode not supported\n");
return -EINVAL;
@@ -4264,7 +4273,7 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
uint32_t prev_val, if_type;
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2 &&
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2 &&
phba->hba_flag & HBA_FORCED_LINK_SPEED)
return -EPERM;
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 90745fe..21f104c 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -2221,7 +2221,7 @@ lpfc_bsg_diag_loopback_mode(struct bsg_job *job)
if (phba->sli_rev < LPFC_SLI_REV4)
rc = lpfc_sli3_bsg_diag_loopback_mode(phba, job);
- else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2)
rc = lpfc_sli4_bsg_diag_loopback_mode(phba, job);
else
@@ -2261,7 +2261,7 @@ lpfc_sli4_bsg_diag_mode_end(struct bsg_job *job)
if (phba->sli_rev < LPFC_SLI_REV4)
return -ENODEV;
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
return -ENODEV;
@@ -2353,7 +2353,7 @@ lpfc_sli4_bsg_link_diag_test(struct bsg_job *job)
rc = -ENODEV;
goto job_error;
}
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2) {
rc = -ENODEV;
goto job_error;
@@ -4419,12 +4419,6 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job,
phba->mbox_ext_buf_ctx.seqNum++;
nemb_tp = phba->mbox_ext_buf_ctx.nembType;
- dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
- if (!dd_data) {
- rc = -ENOMEM;
- goto job_error;
- }
-
pbuf = (uint8_t *)dmabuf->virt;
size = job->request_payload.payload_len;
sg_copy_to_buffer(job->request_payload.sg_list,
@@ -4461,6 +4455,13 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job,
"2968 SLI_CONFIG ext-buffer wr all %d "
"ebuffers received\n",
phba->mbox_ext_buf_ctx.numBuf);
+
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ rc = -ENOMEM;
+ goto job_error;
+ }
+
/* mailbox command structure for base driver */
pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmboxq) {
@@ -4509,6 +4510,8 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job,
return SLI_CONFIG_HANDLED;
job_error:
+ if (pmboxq)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_dma_page_free(phba, dmabuf);
kfree(dd_data);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index d909d90..384f5cd 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -471,11 +471,6 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
"Parse GID_FTrsp: did:x%x flg:x%x x%x",
Did, ndlp->nlp_flag, vport->fc_flag);
- /* Don't assume the rport is always the previous
- * FC4 type.
- */
- ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
-
/* By default, the driver expects to support FCP FC4 */
if (fc4_type == FC_TYPE_FCP)
ndlp->nlp_fc4_type |= NLP_FC4_FCP;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index f3c6801..7398350 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1057,9 +1057,9 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
goto flogifail;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
- "0150 FLOGI failure Status:x%x/x%x TMO:x%x\n",
+ "0150 FLOGI failure Status:x%x/x%x xri x%x TMO:x%x\n",
irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout);
+ cmdiocb->sli4_xritag, irsp->ulpTimeout);
/* FLOGI failed, so there is no fabric */
spin_lock_irq(shost->host_lock);
@@ -1113,7 +1113,8 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* FLOGI completes successfully */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0101 FLOGI completes successfully, I/O tag:x%x, "
- "Data: x%x x%x x%x x%x x%x x%x\n", cmdiocb->iotag,
+ "xri x%x Data: x%x x%x x%x x%x x%x %x\n",
+ cmdiocb->iotag, cmdiocb->sli4_xritag,
irsp->un.ulpWord[4], sp->cmn.e_d_tov,
sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution,
vport->port_state, vport->fc_flag);
@@ -1157,6 +1158,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
phba->hba_flag &= ~(FCF_RR_INPROG | HBA_DEVLOSS_TMO);
spin_unlock_irq(&phba->hbalock);
+ phba->fcf.fcf_redisc_attempted = 0; /* reset */
goto out;
}
if (!rc) {
@@ -1171,6 +1173,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
phba->hba_flag &= ~(FCF_RR_INPROG | HBA_DEVLOSS_TMO);
spin_unlock_irq(&phba->hbalock);
+ phba->fcf.fcf_redisc_attempted = 0; /* reset */
goto out;
}
}
@@ -1340,6 +1343,8 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
Fabric_DID);
pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return -EIO;
/*
* Check the txcmplq for an iocb that matches the nport the driver is
@@ -1553,8 +1558,10 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
*/
new_ndlp = lpfc_findnode_wwpn(vport, &sp->portName);
+ /* return immediately if the WWPN matches ndlp */
if (new_ndlp == ndlp && NLP_CHK_NODE_ACT(new_ndlp))
return ndlp;
+
if (phba->sli_rev == LPFC_SLI_REV4) {
active_rrqs_xri_bitmap = mempool_alloc(phba->active_rrq_pool,
GFP_KERNEL);
@@ -1563,9 +1570,13 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
phba->cfg_rrq_xri_bitmap_sz);
}
- lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "3178 PLOGI confirm: ndlp %p x%x: new_ndlp %p\n",
- ndlp, ndlp->nlp_DID, new_ndlp);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE,
+ "3178 PLOGI confirm: ndlp x%x x%x x%x: "
+ "new_ndlp x%x x%x x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_fc4_type,
+ (new_ndlp ? new_ndlp->nlp_DID : 0),
+ (new_ndlp ? new_ndlp->nlp_flag : 0),
+ (new_ndlp ? new_ndlp->nlp_fc4_type : 0));
if (!new_ndlp) {
rc = memcmp(&ndlp->nlp_portname, name,
@@ -1614,6 +1625,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
phba->cfg_rrq_xri_bitmap_sz);
}
+ /* At this point in this routine, we know new_ndlp will be
+ * returned. however, any previous GID_FTs that were done
+ * would have updated nlp_fc4_type in ndlp, so we must ensure
+ * new_ndlp has the right value.
+ */
+ if (vport->fc_flag & FC_FABRIC)
+ new_ndlp->nlp_fc4_type = ndlp->nlp_fc4_type;
+
lpfc_unreg_rpi(vport, new_ndlp);
new_ndlp->nlp_DID = ndlp->nlp_DID;
new_ndlp->nlp_prev_state = ndlp->nlp_prev_state;
@@ -1663,7 +1682,6 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
if (ndlp->nrport) {
ndlp->nrport = NULL;
lpfc_nlp_put(ndlp);
- new_ndlp->nlp_fc4_type = ndlp->nlp_fc4_type;
}
/* We shall actually free the ndlp with both nlp_DID and
@@ -1737,6 +1755,12 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
active_rrqs_xri_bitmap)
mempool_free(active_rrqs_xri_bitmap,
phba->active_rrq_pool);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE,
+ "3173 PLOGI confirm exit: new_ndlp x%x x%x x%x\n",
+ new_ndlp->nlp_DID, new_ndlp->nlp_flag,
+ new_ndlp->nlp_fc4_type);
+
return new_ndlp;
}
@@ -4086,7 +4110,7 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
mempool_free(mbox, phba->mbox_mem_pool);
}
out:
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp) && shost) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_ACC_REGLOGIN | NLP_RM_DFLT_RPI);
spin_unlock_irq(shost->host_lock);
@@ -4264,14 +4288,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
default:
return 1;
}
- /* Xmit ELS ACC response tag <ulpIoTag> */
- lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0128 Xmit ELS ACC response tag x%x, XRI: x%x, "
- "DID: x%x, nlp_flag: x%x nlp_state: x%x RPI: x%x "
- "fc_flag x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
- ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
- ndlp->nlp_rpi, vport->fc_flag);
if (ndlp->nlp_flag & NLP_LOGO_ACC) {
spin_lock_irq(shost->host_lock);
if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED ||
@@ -4440,6 +4456,15 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
lpfc_els_free_iocb(phba, elsiocb);
return 1;
}
+
+ /* Xmit ELS ACC response tag <ulpIoTag> */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0128 Xmit ELS ACC response Status: x%x, IoTag: x%x, "
+ "XRI: x%x, DID: x%x, nlp_flag: x%x nlp_state: x%x "
+ "RPI: x%x, fc_flag x%x\n",
+ rc, elsiocb->iotag, elsiocb->sli4_xritag,
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_rpi, vport->fc_flag);
return 0;
}
@@ -5534,7 +5559,7 @@ lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct ls_rjt stat;
if (phba->sli_rev < LPFC_SLI_REV4 ||
- bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2) {
rjt_err = LSRJT_UNABLE_TPC;
rjt_expl = LSEXP_REQ_UNSUPPORTED;
@@ -6450,6 +6475,11 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
port_state = vport->port_state;
vport->fc_flag |= FC_PT2PT;
vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+
+ /* Acking an unsol FLOGI. Count 1 for link bounce
+ * work-around.
+ */
+ vport->rcv_flogi_cnt++;
spin_unlock_irq(shost->host_lock);
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"3311 Rcv Flogi PS x%x new PS x%x "
@@ -7847,8 +7877,9 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct ls_rjt stat;
uint32_t *payload;
uint32_t cmd, did, newnode;
- uint8_t rjt_exp, rjt_err = 0;
+ uint8_t rjt_exp, rjt_err = 0, init_link = 0;
IOCB_t *icmd = &elsiocb->iocb;
+ LPFC_MBOXQ_t *mbox;
if (!vport || !(elsiocb->context2))
goto dropit;
@@ -7997,6 +8028,19 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvFLOGI++;
+
+ /* If the driver believes fabric discovery is done and is ready,
+ * bounce the link. There is some descrepancy.
+ */
+ if (vport->port_state >= LPFC_LOCAL_CFG_LINK &&
+ vport->fc_flag & FC_PT2PT &&
+ vport->rcv_flogi_cnt >= 1) {
+ rjt_err = LSRJT_LOGICAL_BSY;
+ rjt_exp = LSEXP_NOTHING_MORE;
+ init_link++;
+ goto lsrjt;
+ }
+
lpfc_els_rcv_flogi(vport, elsiocb, ndlp);
if (newnode)
lpfc_nlp_put(ndlp);
@@ -8225,6 +8269,27 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
lpfc_nlp_put(elsiocb->context1);
elsiocb->context1 = NULL;
+
+ /* Special case. Driver received an unsolicited command that
+ * unsupportable given the driver's current state. Reset the
+ * link and start over.
+ */
+ if (init_link) {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return;
+ lpfc_linkdown(phba);
+ lpfc_init_link(phba, mbox,
+ phba->cfg_topology,
+ phba->cfg_link_speed);
+ mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
+ MBX_NOT_FINISHED)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ }
+
return;
dropit:
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index ccdd82b..5d65717 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -952,6 +952,7 @@ lpfc_linkdown(struct lpfc_hba *phba)
}
spin_lock_irq(shost->host_lock);
phba->pport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI);
+ phba->pport->rcv_flogi_cnt = 0;
spin_unlock_irq(shost->host_lock);
}
return 0;
@@ -1023,6 +1024,7 @@ lpfc_linkup(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
int i;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
phba->link_state = LPFC_LINK_UP;
@@ -1036,6 +1038,13 @@ lpfc_linkup(struct lpfc_hba *phba)
lpfc_linkup_port(vports[i]);
lpfc_destroy_vport_work_array(phba, vports);
+ /* Clear the pport flogi counter in case the link down was
+ * absorbed without an ACQE. No lock here - in worker thread
+ * and discovery is synchronized.
+ */
+ spin_lock_irq(shost->host_lock);
+ phba->pport->rcv_flogi_cnt = 0;
+ spin_unlock_irq(shost->host_lock);
return 0;
}
@@ -1997,6 +2006,26 @@ int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *vport, uint16_t fcf_index)
"failover and change port state:x%x/x%x\n",
phba->pport->port_state, LPFC_VPORT_UNKNOWN);
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
+
+ if (!phba->fcf.fcf_redisc_attempted) {
+ lpfc_unregister_fcf(phba);
+
+ rc = lpfc_sli4_redisc_fcf_table(phba);
+ if (!rc) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "3195 Rediscover FCF table\n");
+ phba->fcf.fcf_redisc_attempted = 1;
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
+ "3196 Rediscover FCF table "
+ "failed. Status:x%x\n", rc);
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
+ "3197 Already rediscover FCF table "
+ "attempted. No more retry\n");
+ }
goto stop_flogi_current_fcf;
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_ELS,
@@ -4198,7 +4227,7 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (new_state == NLP_STE_MAPPED_NODE ||
new_state == NLP_STE_UNMAPPED_NODE) {
- if (ndlp->nlp_fc4_type & NLP_FC4_FCP ||
+ if (ndlp->nlp_fc4_type ||
ndlp->nlp_DID == Fabric_DID ||
ndlp->nlp_DID == NameServer_DID ||
ndlp->nlp_DID == FDMI_DID) {
@@ -4751,7 +4780,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (phba->sli_rev == LPFC_SLI_REV4 &&
(!(vport->load_flag & FC_UNLOADING)) &&
(bf_get(lpfc_sli_intf_if_type,
- &phba->sli4_hba.sli_intf) ==
+ &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2) &&
(kref_read(&ndlp->kref) > 0)) {
mbox->context1 = lpfc_nlp_get(ndlp);
@@ -5202,9 +5231,14 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
/* If we've already received a PLOGI from this NPort
* we don't need to try to discover it again.
*/
- if (ndlp->nlp_flag & NLP_RCV_PLOGI)
+ if (ndlp->nlp_flag & NLP_RCV_PLOGI &&
+ !(ndlp->nlp_type &
+ (NLP_FCP_TARGET | NLP_NVME_TARGET)))
return NULL;
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 9acb5b4..57510a8 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1801,7 +1801,12 @@ lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action,
lpfc_offline(phba);
/* release interrupt for possible resource change */
lpfc_sli4_disable_intr(phba);
- lpfc_sli_brdrestart(phba);
+ rc = lpfc_sli_brdrestart(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "6309 Failed to restart board\n");
+ return rc;
+ }
/* request and enable interrupt */
intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
@@ -5044,7 +5049,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
break;
}
/* If fast FCF failover rescan event is pending, do nothing */
- if (phba->fcf.fcf_flag & FCF_REDISC_EVT) {
+ if (phba->fcf.fcf_flag & (FCF_REDISC_EVT | FCF_REDISC_PEND)) {
spin_unlock_irq(&phba->hbalock);
break;
}
@@ -7761,6 +7766,9 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
bf_get(lpfc_mbx_rd_conf_xri_base, rd_config);
phba->sli4_hba.max_cfg_param.max_vpi =
bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+ /* Limit the max we support */
+ if (phba->sli4_hba.max_cfg_param.max_vpi > LPFC_MAX_VPORTS)
+ phba->sli4_hba.max_cfg_param.max_vpi = LPFC_MAX_VPORTS;
phba->sli4_hba.max_cfg_param.vpi_base =
bf_get(lpfc_mbx_rd_conf_vpi_base, rd_config);
phba->sli4_hba.max_cfg_param.max_rpi =
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index deb094f..e6bf5e8 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -513,9 +513,9 @@ lpfc_init_link(struct lpfc_hba * phba,
break;
}
- if (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC &&
- mb->un.varInitLnk.link_flags & FLAGS_TOPOLOGY_MODE_LOOP) {
- /* Failover is not tried for Lancer G6 */
+ if ((phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC ||
+ phba->pcidev->device == PCI_DEVICE_ID_LANCER_G7_FC) &&
+ mb->un.varInitLnk.link_flags & FLAGS_TOPOLOGY_MODE_LOOP) {
mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
phba->cfg_topology = FLAGS_TOPOLOGY_MODE_PT_PT;
}
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index a6619fd..3dfed19 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -483,8 +483,10 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* single discovery thread, this will cause a huge delay in
* discovery. Also this will cause multiple state machines
* running in parallel for this node.
+ * This only applies to a fabric environment.
*/
- if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {
+ if ((ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) &&
+ (vport->fc_flag & FC_FABRIC)) {
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp);
}
@@ -844,9 +846,9 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (!(vport->fc_flag & FC_PT2PT)) {
/* Check config parameter use-adisc or FCP-2 */
- if ((vport->cfg_use_adisc && (vport->fc_flag & FC_RSCN_MODE)) ||
+ if (vport->cfg_use_adisc && ((vport->fc_flag & FC_RSCN_MODE) ||
((ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) &&
- (ndlp->nlp_type & NLP_FCP_TARGET))) {
+ (ndlp->nlp_type & NLP_FCP_TARGET)))) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_ADISC;
spin_unlock_irq(shost->host_lock);
@@ -2323,6 +2325,7 @@ lpfc_device_recov_unmap_node(struct lpfc_vport *vport,
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
spin_unlock_irq(shost->host_lock);
lpfc_disc_set_adisc(vport, ndlp);
@@ -2400,6 +2403,7 @@ lpfc_device_recov_mapped_node(struct lpfc_vport *vport,
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
spin_unlock_irq(shost->host_lock);
lpfc_disc_set_adisc(vport, ndlp);
return ndlp->nlp_state;
@@ -2657,6 +2661,7 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_cancel_retry_delay_tmo(vport, ndlp);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
spin_unlock_irq(shost->host_lock);
return ndlp->nlp_state;
}
@@ -2865,8 +2870,9 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* DSM in event <evt> on NPort <nlp_DID> in state <cur_state> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0211 DSM in event x%x on NPort x%x in "
- "state %d Data: x%x\n",
- evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag);
+ "state %d Data: x%x x%x\n",
+ evt, ndlp->nlp_DID, cur_state,
+ ndlp->nlp_flag, ndlp->nlp_fc4_type);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM,
"DSM in: evt:%d ste:%d did:x%x",
diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c
index 645ffb5..6c355d8 100644
--- a/drivers/scsi/lpfc/lpfc_nvme.c
+++ b/drivers/scsi/lpfc/lpfc_nvme.c
@@ -282,7 +282,7 @@ lpfc_nvme_delete_queue(struct nvme_fc_local_port *pnvme_lport,
vport = lport->vport;
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
- "6001 ENTER. lpfc_pnvme %p, qidx x%xi qhandle %p\n",
+ "6001 ENTER. lpfc_pnvme %p, qidx x%x qhandle %p\n",
lport, qidx, handle);
kfree(handle);
}
@@ -1856,7 +1856,6 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
bf_set(abort_cmd_criteria, &abts_wqe->abort_cmd, T_XRI_TAG);
/* word 7 */
- bf_set(wqe_ct, &abts_wqe->abort_cmd.wqe_com, 0);
bf_set(wqe_cmnd, &abts_wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX);
bf_set(wqe_class, &abts_wqe->abort_cmd.wqe_com,
nvmereq_wqe->iocb.ulpClass);
@@ -1871,7 +1870,6 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
abts_buf->iotag);
/* word 10 */
- bf_set(wqe_wqid, &abts_wqe->abort_cmd.wqe_com, nvmereq_wqe->hba_wqidx);
bf_set(wqe_qosd, &abts_wqe->abort_cmd.wqe_com, 1);
bf_set(wqe_lenloc, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE);
@@ -1905,6 +1903,8 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
/* Declare and initialization an instance of the FC NVME template. */
static struct nvme_fc_port_template lpfc_nvme_template = {
+ .module = THIS_MODULE,
+
/* initiator-based functions */
.localport_delete = lpfc_nvme_localport_delete,
.remoteport_delete = lpfc_nvme_remoteport_delete,
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c
index e2575c8..768eba8 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.c
+++ b/drivers/scsi/lpfc/lpfc_nvmet.c
@@ -1340,15 +1340,14 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
idx = 0;
}
- infop = phba->sli4_hba.nvmet_ctx_info;
- for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
+ infop = lpfc_get_ctx_list(phba, i, j);
lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT,
"6408 TOTAL NVMET ctx for CPU %d "
"MRQ %d: cnt %d nextcpu %p\n",
i, j, infop->nvmet_ctx_list_cnt,
infop->nvmet_ctx_next_cpu);
- infop++;
}
}
return 0;
@@ -1713,7 +1712,11 @@ lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
}
tgtp->tport_unreg_cmp = &tport_unreg_cmp;
nvmet_fc_unregister_targetport(phba->targetport);
- wait_for_completion_timeout(&tport_unreg_cmp, 5);
+ if (!wait_for_completion_timeout(tgtp->tport_unreg_cmp,
+ msecs_to_jiffies(LPFC_NVMET_WAIT_TMO)))
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME,
+ "6179 Unreg targetport %p timeout "
+ "reached.\n", phba->targetport);
lpfc_nvmet_cleanup_io_context(phba);
}
phba->targetport = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h
index 0ec1082..3b17028 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.h
+++ b/drivers/scsi/lpfc/lpfc_nvmet.h
@@ -31,6 +31,8 @@
#define LPFC_NVMET_MRQ_AUTO 0
#define LPFC_NVMET_MRQ_MAX 16
+#define LPFC_NVMET_WAIT_TMO (5 * MSEC_PER_SEC)
+
/* Used for NVME Target */
struct lpfc_nvmet_tgtport {
struct lpfc_hba *phba;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 200b5bca..425b836 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -2732,6 +2732,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
int prot_group_type = 0;
int fcpdl;
+ struct lpfc_vport *vport = phba->pport;
/*
* Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
@@ -2837,6 +2838,14 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
*/
iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+ /*
+ * For First burst, we may need to adjust the initial transfer
+ * length for DIF
+ */
+ if (iocb_cmd->un.fcpi.fcpi_XRdy &&
+ (fcpdl < vport->cfg_first_burst_size))
+ iocb_cmd->un.fcpi.fcpi_XRdy = fcpdl;
+
return 0;
err:
if (lpfc_cmd->seg_cnt)
@@ -3401,6 +3410,7 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
int prot_group_type = 0;
int fcpdl;
+ struct lpfc_vport *vport = phba->pport;
/*
* Start the lpfc command prep by bumping the sgl beyond fcp_cmnd
@@ -3517,6 +3527,14 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
/*
+ * For First burst, we may need to adjust the initial transfer
+ * length for DIF
+ */
+ if (iocb_cmd->un.fcpi.fcpi_XRdy &&
+ (fcpdl < vport->cfg_first_burst_size))
+ iocb_cmd->un.fcpi.fcpi_XRdy = fcpdl;
+
+ /*
* If the OAS driver feature is enabled and the lun is enabled for
* OAS, set the oas iocb related flags.
*/
@@ -4161,7 +4179,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
/* If pCmd was set to NULL from abort path, do not call scsi_done */
if (xchg(&lpfc_cmd->pCmd, NULL) == NULL) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0711 FCP cmd already NULL, sid: 0x%06x, "
+ "5688 FCP cmd already NULL, sid: 0x%06x, "
"did: 0x%06x, oxid: 0x%04x\n",
vport->fc_myDID,
(pnode) ? pnode->nlp_DID : 0,
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index a490e63..a801917 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -392,11 +392,7 @@ lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q)
struct lpfc_register doorbell;
doorbell.word0 = 0;
- bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
- bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
- bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
- (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
- bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
+ bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
}
@@ -4644,6 +4640,8 @@ lpfc_sli_brdrestart_s4(struct lpfc_hba *phba)
hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
rc = lpfc_sli4_brdreset(phba);
+ if (rc)
+ return rc;
spin_lock_irq(&phba->hbalock);
phba->pport->stopped = 0;
@@ -10991,19 +10989,12 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* Complete prepping the abort wqe and issue to the FW. */
abts_wqe = &abtsiocbp->wqe;
- bf_set(abort_cmd_ia, &abts_wqe->abort_cmd, 0);
+
+ /* Clear any stale WQE contents */
+ memset(abts_wqe, 0, sizeof(union lpfc_wqe));
bf_set(abort_cmd_criteria, &abts_wqe->abort_cmd, T_XRI_TAG);
- /* Explicitly set reserved fields to zero.*/
- abts_wqe->abort_cmd.rsrvd4 = 0;
- abts_wqe->abort_cmd.rsrvd5 = 0;
-
- /* WQE Common - word 6. Context is XRI tag. Set 0. */
- bf_set(wqe_xri_tag, &abts_wqe->abort_cmd.wqe_com, 0);
- bf_set(wqe_ctxt_tag, &abts_wqe->abort_cmd.wqe_com, 0);
-
/* word 7 */
- bf_set(wqe_ct, &abts_wqe->abort_cmd.wqe_com, 0);
bf_set(wqe_cmnd, &abts_wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX);
bf_set(wqe_class, &abts_wqe->abort_cmd.wqe_com,
cmdiocb->iocb.ulpClass);
@@ -11018,7 +11009,6 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
abtsiocbp->iotag);
/* word 10 */
- bf_set(wqe_wqid, &abts_wqe->abort_cmd.wqe_com, cmdiocb->hba_wqidx);
bf_set(wqe_qosd, &abts_wqe->abort_cmd.wqe_com, 1);
bf_set(wqe_lenloc, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE);
@@ -12938,13 +12928,19 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
/* Setting active mailbox pointer need to be in sync to flag clear */
phba->sli.mbox_active = NULL;
+ if (bf_get(lpfc_trailer_consumed, mcqe))
+ lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
spin_unlock_irqrestore(&phba->hbalock, iflags);
/* Wake up worker thread to post the next pending mailbox command */
lpfc_worker_wake_up(phba);
+ return workposted;
+
out_no_mqe_complete:
+ spin_lock_irqsave(&phba->hbalock, iflags);
if (bf_get(lpfc_trailer_consumed, mcqe))
lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
- return workposted;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return false;
}
/**
@@ -17871,6 +17867,13 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
static void
__lpfc_sli4_free_rpi(struct lpfc_hba *phba, int rpi)
{
+ /*
+ * if the rpi value indicates a prior unreg has already
+ * been done, skip the unreg.
+ */
+ if (rpi == LPFC_RPI_ALLOC_ERROR)
+ return;
+
if (test_and_clear_bit(rpi, phba->sli4_hba.rpi_bmask)) {
phba->sli4_hba.rpi_count--;
phba->sli4_hba.max_cfg_param.rpi_used--;
@@ -18435,15 +18438,8 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba)
goto initial_priority;
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
"2844 No roundrobin failover FCF available\n");
- if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX)
- return LPFC_FCOE_FCF_NEXT_NONE;
- else {
- lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
- "3063 Only FCF available idx %d, flag %x\n",
- next_fcf_index,
- phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag);
- return next_fcf_index;
- }
+
+ return LPFC_FCOE_FCF_NEXT_NONE;
}
if (next_fcf_index < LPFC_SLI4_FCF_TBL_INDX_MAX &&
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 399c001..3dcc661 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -279,6 +279,7 @@ struct lpfc_fcf {
#define FCF_REDISC_EVT 0x100 /* FCF rediscovery event to worker thread */
#define FCF_REDISC_FOV 0x200 /* Post FCF rediscovery fast failover */
#define FCF_REDISC_PROG (FCF_REDISC_PEND | FCF_REDISC_EVT)
+ uint16_t fcf_redisc_attempted;
uint32_t addr_mode;
uint32_t eligible_fcf_cnt;
struct lpfc_fcf_rec current_rec;
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
index 643321f..b5050c2 100644
--- a/drivers/scsi/mac_scsi.c
+++ b/drivers/scsi/mac_scsi.c
@@ -429,7 +429,7 @@ static int __init mac_scsi_probe(struct platform_device *pdev)
mac_scsi_template.can_queue = setup_can_queue;
if (setup_cmd_per_lun > 0)
mac_scsi_template.cmd_per_lun = setup_cmd_per_lun;
- if (setup_sg_tablesize >= 0)
+ if (setup_sg_tablesize > 0)
mac_scsi_template.sg_tablesize = setup_sg_tablesize;
if (setup_hostid >= 0)
mac_scsi_template.this_id = setup_hostid & 7;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index bc37666f..2f31d26 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -3894,12 +3894,12 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
/*
* The cur_state should not last for more than max_wait secs
*/
- for (i = 0; i < (max_wait * 1000); i++) {
+ for (i = 0; i < max_wait; i++) {
curr_abs_state = instance->instancet->
read_fw_status_reg(instance->reg_set);
if (abs_state == curr_abs_state) {
- msleep(1);
+ msleep(1000);
} else
break;
}
@@ -5410,7 +5410,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
if (!instance->msix_vectors) {
i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY);
if (i < 0)
- goto fail_setup_irqs;
+ goto fail_init_adapter;
}
megasas_setup_reply_map(instance);
@@ -5619,9 +5619,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
fail_get_ld_pd_list:
instance->instancet->disable_intr(instance);
-fail_init_adapter:
megasas_destroy_irqs(instance);
-fail_setup_irqs:
+fail_init_adapter:
if (instance->msix_vectors)
pci_free_irq_vectors(instance->pdev);
instance->msix_vectors = 0;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index d2ab520..2c556c7 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -4117,7 +4117,7 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc)
* flag unset in NVDATA.
*/
mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11);
- if (ioc->manu_pg11.EEDPTagMode == 0) {
+ if (!ioc->is_gen35_ioc && ioc->manu_pg11.EEDPTagMode == 0) {
pr_err("%s: overriding NVDATA EEDPTagMode setting\n",
ioc->name);
ioc->manu_pg11.EEDPTagMode &= ~0x3;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
index d29a2dc..9b01c5a 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_config.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -692,10 +692,6 @@ mpt3sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
r = _config_request(ioc, &mpi_request, mpi_reply,
MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
sizeof(*config_page));
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
out:
return r;
}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
index 5e8c059..0734501 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -1597,7 +1597,8 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc,
" for diag buffers, requested size(%d)\n",
ioc->name, __func__, request_data_sz);
mpt3sas_base_free_smid(ioc, smid);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
ioc->diag_buffer[buffer_type] = request_data;
ioc->diag_buffer_sz[buffer_type] = request_data_sz;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 73d661a..d3c944d 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -3791,6 +3791,40 @@ _scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
return _scsih_check_for_pending_tm(ioc, smid);
}
+/** _scsih_allow_scmd_to_device - check whether scmd needs to
+ * issue to IOC or not.
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ *
+ * Returns true if scmd can be issued to IOC otherwise returns false.
+ */
+inline bool _scsih_allow_scmd_to_device(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd)
+{
+
+ if (ioc->pci_error_recovery)
+ return false;
+
+ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) {
+ if (ioc->remove_host)
+ return false;
+
+ return true;
+ }
+
+ if (ioc->remove_host) {
+
+ switch (scmd->cmnd[0]) {
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
/**
* _scsih_sas_control_complete - completion routine
@@ -4623,7 +4657,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
return 0;
}
- if (ioc->pci_error_recovery || ioc->remove_host) {
+ if (!(_scsih_allow_scmd_to_device(ioc, scmd))) {
scmd->result = DID_NO_CONNECT << 16;
scmd->scsi_done(scmd);
return 0;
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 4dd6cad..3e814c0 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -1479,6 +1479,12 @@ u32 pm8001_mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
} else {
u32 producer_index;
void *pi_virt = circularQ->pi_virt;
+ /* spurious interrupt during setup if
+ * kexec-ing and driver doing a doorbell access
+ * with the pre-kexec oq interrupt setup
+ */
+ if (!pi_virt)
+ break;
/* Update the producer index from SPC */
producer_index = pm8001_read_32(pi_virt);
circularQ->producer_index = cpu_to_le32(producer_index);
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
index 947d601..59feda2 100644
--- a/drivers/scsi/pm8001/pm8001_sas.c
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -374,6 +374,13 @@ static int pm8001_task_exec(struct sas_task *task,
return 0;
}
pm8001_ha = pm8001_find_ha_by_dev(task->dev);
+ if (pm8001_ha->controller_fatal_error) {
+ struct task_status_struct *ts = &t->task_status;
+
+ ts->resp = SAS_TASK_UNDELIVERED;
+ t->task_done(t);
+ return 0;
+ }
PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n "));
spin_lock_irqsave(&pm8001_ha->lock, flags);
do {
@@ -466,7 +473,7 @@ static int pm8001_task_exec(struct sas_task *task,
dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc);
if (!sas_protocol_ata(t->task_proto))
if (n_elem)
- dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem,
+ dma_unmap_sg(pm8001_ha->dev, t->scatter, t->num_scatter,
t->data_dir);
out_done:
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 80b4dd6..1816e35 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -538,6 +538,7 @@ struct pm8001_hba_info {
u32 logging_level;
u32 fw_status;
u32 smp_exp_mode;
+ bool controller_fatal_error;
const struct firmware *fw_image;
struct isr_param irq_vector[PM8001_MAX_MSIX_VEC];
u32 reset_in_progress;
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
index 42f0405..8627feb 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.c
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -577,6 +577,9 @@ static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_size);
pm8001_mw32(address, MAIN_PCS_EVENT_LOG_OPTION,
pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity);
+ /* Update Fatal error interrupt vector */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt |=
+ ((pm8001_ha->number_of_intr - 1) << 8);
pm8001_mw32(address, MAIN_FATAL_ERROR_INTERRUPT,
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt);
pm8001_mw32(address, MAIN_EVENT_CRC_CHECK,
@@ -1110,6 +1113,9 @@ static int pm80xx_chip_init(struct pm8001_hba_info *pm8001_ha)
return -EBUSY;
}
+ /* Initialize the controller fatal error flag */
+ pm8001_ha->controller_fatal_error = false;
+
/* Initialize pci space address eg: mpi offset */
init_pci_device_addresses(pm8001_ha);
init_default_table_values(pm8001_ha);
@@ -1218,13 +1224,17 @@ pm80xx_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
u32 bootloader_state;
u32 ibutton0, ibutton1;
- /* Check if MPI is in ready state to reset */
- if (mpi_uninit_check(pm8001_ha) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("MPI state is not ready\n"));
- return -1;
+ /* Process MPI table uninitialization only if FW is ready */
+ if (!pm8001_ha->controller_fatal_error) {
+ /* Check if MPI is in ready state to reset */
+ if (mpi_uninit_check(pm8001_ha) != 0) {
+ regval = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk(
+ "MPI state is not ready scratch1 :0x%x\n",
+ regval));
+ return -1;
+ }
}
-
/* checked for reset register normal state; 0x0 */
regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
PM8001_INIT_DBG(pm8001_ha,
@@ -2372,6 +2382,8 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
pm8001_printk("task 0x%p done with io_status 0x%x"
" resp 0x%x stat 0x%x but aborted by upper layer!\n",
t, status, ts->resp, ts->stat));
+ if (t->slow_task)
+ complete(&t->slow_task->completion);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
} else {
spin_unlock_irqrestore(&t->task_state_lock, flags);
@@ -3752,6 +3764,46 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
}
}
+static void print_scratchpad_registers(struct pm8001_hba_info *pm8001_ha)
+{
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_SCRATCH_PAD_1:0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_SCRATCH_PAD_2: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_SCRATCH_PAD_3: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_1: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_1)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_2: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_2)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_3: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_3)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_4: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_4)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_HOST_SCRATCH_PAD_5: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_5)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_RSVD_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_6)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MSGU_RSVD_SCRATCH_PAD_1: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_7)));
+}
+
static int process_oq(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
struct outbound_queue_table *circularQ;
@@ -3759,10 +3811,28 @@ static int process_oq(struct pm8001_hba_info *pm8001_ha, u8 vec)
u8 uninitialized_var(bc);
u32 ret = MPI_IO_STATUS_FAIL;
unsigned long flags;
+ u32 regval;
+ if (vec == (pm8001_ha->number_of_intr - 1)) {
+ regval = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ if ((regval & SCRATCH_PAD_MIPSALL_READY) !=
+ SCRATCH_PAD_MIPSALL_READY) {
+ pm8001_ha->controller_fatal_error = true;
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk(
+ "Firmware Fatal error! Regval:0x%x\n", regval));
+ print_scratchpad_registers(pm8001_ha);
+ return ret;
+ }
+ }
spin_lock_irqsave(&pm8001_ha->lock, flags);
circularQ = &pm8001_ha->outbnd_q_tbl[vec];
do {
+ /* spurious interrupt during setup if kexec-ing and
+ * driver doing a doorbell access w/ the pre-kexec oq
+ * interrupt setup.
+ */
+ if (!circularQ->pi_virt)
+ break;
ret = pm8001_mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
if (MPI_IO_STATUS_SUCCESS == ret) {
/* process the outbound message */
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h
index 889e69c..7dd2699 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.h
+++ b/drivers/scsi/pm8001/pm80xx_hwi.h
@@ -1384,6 +1384,9 @@ typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t;
#define SCRATCH_PAD_BOOT_LOAD_SUCCESS 0x0
#define SCRATCH_PAD_IOP0_READY 0xC00
#define SCRATCH_PAD_IOP1_READY 0x3000
+#define SCRATCH_PAD_MIPSALL_READY (SCRATCH_PAD_IOP1_READY | \
+ SCRATCH_PAD_IOP0_READY | \
+ SCRATCH_PAD_RAAE_READY)
/* boot loader state */
#define SCRATCH_PAD1_BOOTSTATE_MASK 0x70 /* Bit 4-6 */
diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c
index 04f0c4d..5178cd0 100644
--- a/drivers/scsi/qedf/qedf_els.c
+++ b/drivers/scsi/qedf/qedf_els.c
@@ -23,8 +23,6 @@ static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op,
int rc = 0;
uint32_t did, sid;
uint16_t xid;
- uint32_t start_time = jiffies / HZ;
- uint32_t current_time;
struct fcoe_wqe *sqe;
unsigned long flags;
u16 sqe_idx;
@@ -59,18 +57,12 @@ static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op,
goto els_err;
}
-retry_els:
els_req = qedf_alloc_cmd(fcport, QEDF_ELS);
if (!els_req) {
- current_time = jiffies / HZ;
- if ((current_time - start_time) > 10) {
- QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
- "els: Failed els 0x%x\n", op);
- rc = -ENOMEM;
- goto els_err;
- }
- mdelay(20 * USEC_PER_MSEC);
- goto retry_els;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS,
+ "Failed to alloc ELS request 0x%x\n", op);
+ rc = -ENOMEM;
+ goto els_err;
}
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "initiate_els els_req = "
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 15d493f3..b008d58 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -655,7 +655,8 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
break;
} else {
/* Make sure FC side is not in reset */
- qla2x00_wait_for_hba_online(vha);
+ WARN_ON_ONCE(qla2x00_wait_for_hba_online(vha) !=
+ QLA_SUCCESS);
/* Issue MPI reset */
scsi_block_requests(vha->host);
@@ -2161,6 +2162,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags))
msleep(1000);
+ qla_nvme_delete(vha);
+
qla24xx_disable_vp(vha);
qla2x00_wait_for_sess_deletion(vha);
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 4a9fd8d..47f062e 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -258,7 +258,7 @@ qla2x00_process_els(struct bsg_job *bsg_job)
srb_t *sp;
const char *type;
int req_sg_cnt, rsp_sg_cnt;
- int rval = (DRIVER_ERROR << 16);
+ int rval = (DID_ERROR << 16);
uint16_t nextlid = 0;
if (bsg_request->msgcode == FC_BSG_RPT_ELS) {
@@ -342,6 +342,8 @@ qla2x00_process_els(struct bsg_job *bsg_job)
dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
if (!req_sg_cnt) {
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
rval = -ENOMEM;
goto done_free_fcport;
}
@@ -349,6 +351,8 @@ qla2x00_process_els(struct bsg_job *bsg_job)
rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
if (!rsp_sg_cnt) {
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
rval = -ENOMEM;
goto done_free_fcport;
}
@@ -433,7 +437,7 @@ qla2x00_process_ct(struct bsg_job *bsg_job)
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
- int rval = (DRIVER_ERROR << 16);
+ int rval = (DID_ERROR << 16);
int req_sg_cnt, rsp_sg_cnt;
uint16_t loop_id;
struct fc_port *fcport;
@@ -1775,8 +1779,8 @@ qla24xx_process_bidir_cmd(struct bsg_job *bsg_job)
uint16_t nextlid = 0;
uint32_t tot_dsds;
srb_t *sp = NULL;
- uint32_t req_data_len = 0;
- uint32_t rsp_data_len = 0;
+ uint32_t req_data_len;
+ uint32_t rsp_data_len;
/* Check the type of the adapter */
if (!IS_BIDI_CAPABLE(ha)) {
@@ -1881,6 +1885,9 @@ qla24xx_process_bidir_cmd(struct bsg_job *bsg_job)
goto done_unmap_sg;
}
+ req_data_len = bsg_job->request_payload.payload_len;
+ rsp_data_len = bsg_job->reply_payload.payload_len;
+
if (req_data_len != rsp_data_len) {
rval = EXT_STATUS_BUSY;
ql_log(ql_log_warn, vha, 0x70aa,
@@ -1888,10 +1895,6 @@ qla24xx_process_bidir_cmd(struct bsg_job *bsg_job)
goto done_unmap_sg;
}
- req_data_len = bsg_job->request_payload.payload_len;
- rsp_data_len = bsg_job->reply_payload.payload_len;
-
-
/* Alloc SRB structure */
sp = qla2x00_get_sp(vha, &(vha->bidir_fcport), GFP_KERNEL);
if (!sp) {
@@ -1948,7 +1951,7 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
- int rval = (DRIVER_ERROR << 16);
+ int rval = (DID_ERROR << 16);
struct qla_mt_iocb_rqst_fx00 *piocb_rqst;
srb_t *sp;
int req_sg_cnt = 0, rsp_sg_cnt = 0;
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 34ff4bbc..f621cb5 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -3264,7 +3264,7 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res)
fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
if (res == QLA_FUNCTION_TIMEOUT)
- return;
+ goto done;
if (res == (DID_ERROR << 16)) {
/* entry status error */
@@ -3277,7 +3277,7 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res)
ql_dbg(ql_dbg_disc, vha, 0x2019,
"GPSC command unsupported, disabling query.\n");
ha->flags.gpsc_supported = 0;
- res = QLA_SUCCESS;
+ goto done;
}
} else {
switch (be16_to_cpu(ct_rsp->rsp.gpsc.speed)) {
@@ -3310,7 +3310,6 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res)
be16_to_cpu(ct_rsp->rsp.gpsc.speeds),
be16_to_cpu(ct_rsp->rsp.gpsc.speed));
}
-done:
memset(&ea, 0, sizeof(ea));
ea.event = FCME_GPSC_DONE;
ea.rc = res;
@@ -3318,6 +3317,7 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res)
ea.sp = sp;
qla2x00_fcport_event_handler(vha, &ea);
+done:
sp->free(sp);
}
@@ -3902,9 +3902,10 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
fc_port_t *fcport;
u32 i, rc;
bool found;
- struct fab_scan_rp *rp;
+ struct fab_scan_rp *rp, *trp;
unsigned long flags;
u8 recheck = 0;
+ u16 dup = 0, dup_cnt = 0;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s enter\n", __func__);
@@ -3935,6 +3936,7 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
for (i = 0; i < vha->hw->max_fibre_devices; i++) {
u64 wwn;
+ int k;
rp = &vha->scan.l[i];
found = false;
@@ -3943,6 +3945,20 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
if (wwn == 0)
continue;
+ /* Remove duplicate NPORT ID entries from switch data base */
+ for (k = i + 1; k < vha->hw->max_fibre_devices; k++) {
+ trp = &vha->scan.l[k];
+ if (rp->id.b24 == trp->id.b24) {
+ dup = 1;
+ dup_cnt++;
+ ql_dbg(ql_dbg_disc + ql_dbg_verbose,
+ vha, 0xffff,
+ "Detected duplicate NPORT ID from switch data base: ID %06x WWN %8phN WWN %8phN\n",
+ rp->id.b24, rp->port_name, trp->port_name);
+ memset(trp, 0, sizeof(*trp));
+ }
+ }
+
if (!memcmp(rp->port_name, vha->port_name, WWN_SIZE))
continue;
@@ -3982,6 +3998,12 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
}
}
+ if (dup) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Detected %d duplicate NPORT ID(s) from switch data base\n",
+ dup_cnt);
+ }
+
/*
* Logout all previous fabric dev marked lost, except FCP2 devices.
*/
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index bee9cfb..851f75b 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -242,15 +242,13 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->done = qla2x00_async_login_sp_done;
- if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport)) {
+ if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport))
lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY;
- } else {
+ else
lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
- if (fcport->fc4f_nvme)
- lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI;
-
- }
+ if (fcport->fc4f_nvme)
+ lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI;
ql_dbg(ql_dbg_disc, vha, 0x2072,
"Async-login - %8phC hdl=%x, loopid=%x portid=%02x%02x%02x "
@@ -293,9 +291,6 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
struct srb_iocb *lio;
int rval = QLA_FUNCTION_FAILED;
- if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
- return rval;
-
fcport->flags |= FCF_ASYNC_SENT;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
@@ -438,6 +433,7 @@ int qla_post_els_plogi_work(struct scsi_qla_host *vha, fc_port_t *fcport)
e->u.fcport.fcport = fcport;
fcport->flags |= FCF_ASYNC_ACTIVE;
+ fcport->disc_state = DSC_LOGIN_PEND;
return qla2x00_post_work(vha, e);
}
@@ -970,6 +966,7 @@ int qla24xx_post_gnl_work(struct scsi_qla_host *vha, fc_port_t *fcport)
e->u.fcport.fcport = fcport;
fcport->flags |= FCF_ASYNC_ACTIVE;
+ fcport->disc_state = DSC_LOGIN_PEND;
return qla2x00_post_work(vha, e);
}
@@ -987,13 +984,11 @@ void qla24xx_async_gpdb_sp_done(void *s, int res)
"Async done-%s res %x, WWPN %8phC mb[1]=%x mb[2]=%x \n",
sp->name, res, fcport->port_name, mb[1], mb[2]);
- if (res == QLA_FUNCTION_TIMEOUT) {
- dma_pool_free(sp->vha->hw->s_dma_pool, sp->u.iocb_cmd.u.mbx.in,
- sp->u.iocb_cmd.u.mbx.in_dma);
- return;
- }
-
fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
+
+ if (res == QLA_FUNCTION_TIMEOUT)
+ goto done;
+
memset(&ea, 0, sizeof(ea));
ea.event = FCME_GPDB_DONE;
ea.fcport = fcport;
@@ -1001,6 +996,7 @@ void qla24xx_async_gpdb_sp_done(void *s, int res)
qla2x00_fcport_event_handler(vha, &ea);
+done:
dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in,
sp->u.iocb_cmd.u.mbx.in_dma);
@@ -1772,38 +1768,34 @@ int
qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait)
{
scsi_qla_host_t *vha = cmd_sp->vha;
- fc_port_t *fcport = cmd_sp->fcport;
struct srb_iocb *abt_iocb;
srb_t *sp;
int rval = QLA_FUNCTION_FAILED;
- sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ sp = qla2xxx_get_qpair_sp(cmd_sp->qpair, cmd_sp->fcport, GFP_KERNEL);
if (!sp)
goto done;
abt_iocb = &sp->u.iocb_cmd;
sp->type = SRB_ABT_CMD;
sp->name = "abort";
+ sp->qpair = cmd_sp->qpair;
if (wait)
sp->flags = SRB_WAKEUP_ON_COMP;
abt_iocb->timeout = qla24xx_abort_iocb_timeout;
init_completion(&abt_iocb->u.abt.comp);
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha));
+ /* FW can send 2 x ABTS's timeout/20s */
+ qla2x00_init_timer(sp, 42);
abt_iocb->u.abt.cmd_hndl = cmd_sp->handle;
-
- if (vha->flags.qpairs_available && cmd_sp->qpair)
- abt_iocb->u.abt.req_que_no =
- cpu_to_le16(cmd_sp->qpair->req->id);
- else
- abt_iocb->u.abt.req_que_no = cpu_to_le16(vha->req->id);
+ abt_iocb->u.abt.req_que_no = cpu_to_le16(cmd_sp->qpair->req->id);
sp->done = qla24xx_abort_sp_done;
ql_dbg(ql_dbg_async, vha, 0x507c,
- "Abort command issued - hdl=%x, target_id=%x\n",
- cmd_sp->handle, fcport->tgt_id);
+ "Abort command issued - hdl=%x, type=%x\n",
+ cmd_sp->handle, cmd_sp->type);
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -4823,14 +4815,8 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
set_bit(RSCN_UPDATE, &flags);
clear_bit(LOCAL_LOOP_UPDATE, &flags);
- } else if (ha->current_topology == ISP_CFG_N) {
- clear_bit(RSCN_UPDATE, &flags);
- if (qla_tgt_mode_enabled(vha)) {
- /* allow the other side to start the login */
- clear_bit(LOCAL_LOOP_UPDATE, &flags);
- set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
- }
- } else if (ha->current_topology == ISP_CFG_NL) {
+ } else if (ha->current_topology == ISP_CFG_NL ||
+ ha->current_topology == ISP_CFG_N) {
clear_bit(RSCN_UPDATE, &flags);
set_bit(LOCAL_LOOP_UPDATE, &flags);
} else if (!vha->flags.online ||
@@ -4874,19 +4860,10 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
*/
if (qla_tgt_mode_enabled(vha) ||
qla_dual_mode_enabled(vha)) {
- if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) {
- spin_lock_irqsave(&ha->tgt.atio_lock,
- flags);
- qlt_24xx_process_atio_queue(vha, 0);
- spin_unlock_irqrestore(
- &ha->tgt.atio_lock, flags);
- } else {
- spin_lock_irqsave(&ha->hardware_lock,
- flags);
- qlt_24xx_process_atio_queue(vha, 1);
- spin_unlock_irqrestore(
- &ha->hardware_lock, flags);
- }
+ spin_lock_irqsave(&ha->tgt.atio_lock, flags);
+ qlt_24xx_process_atio_queue(vha, 0);
+ spin_unlock_irqrestore(&ha->tgt.atio_lock,
+ flags);
}
}
}
@@ -6515,6 +6492,7 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
if (!(IS_P3P_TYPE(ha)))
ha->isp_ops->reset_chip(vha);
+ ha->link_data_rate = PORT_SPEED_UNKNOWN;
SAVE_TOPO(ha);
ha->flags.rida_fmt2 = 0;
ha->flags.n2n_ae = 0;
@@ -8739,8 +8717,6 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
struct qla_hw_data *ha = qpair->hw;
qpair->delete_in_progress = 1;
- while (atomic_read(&qpair->ref_count))
- msleep(500);
ret = qla25xx_delete_req_que(vha, qpair->req);
if (ret != QLA_SUCCESS)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 1199272..7e47321 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2537,7 +2537,8 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073,
"PLOGI ELS IOCB:\n");
ql_dump_buffer(ql_log_info, vha, 0x0109,
- (uint8_t *)els_iocb, 0x70);
+ (uint8_t *)els_iocb,
+ sizeof(*els_iocb));
} else {
els_iocb->tx_byte_count = sizeof(struct els_logo_payload);
els_iocb->tx_address[0] =
@@ -2703,7 +2704,8 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x3073, "PLOGI buffer:\n");
ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x0109,
- (uint8_t *)elsio->u.els_plogi.els_plogi_pyld, 0x70);
+ (uint8_t *)elsio->u.els_plogi.els_plogi_pyld,
+ sizeof(*elsio->u.els_plogi.els_plogi_pyld));
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
@@ -3297,19 +3299,21 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
{
struct srb_iocb *aio = &sp->u.iocb_cmd;
scsi_qla_host_t *vha = sp->vha;
- struct req_que *req = vha->req;
+ struct req_que *req = sp->qpair->req;
memset(abt_iocb, 0, sizeof(struct abort_entry_24xx));
abt_iocb->entry_type = ABORT_IOCB_TYPE;
abt_iocb->entry_count = 1;
abt_iocb->handle = cpu_to_le32(MAKE_HANDLE(req->id, sp->handle));
- abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ if (sp->fcport) {
+ abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
+ abt_iocb->port_id[1] = sp->fcport->d_id.b.area;
+ abt_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+ }
abt_iocb->handle_to_abort =
cpu_to_le32(MAKE_HANDLE(aio->u.abt.req_que_no,
aio->u.abt.cmd_hndl));
- abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
- abt_iocb->port_id[1] = sp->fcport->d_id.b.area;
- abt_iocb->port_id[2] = sp->fcport->d_id.b.domain;
abt_iocb->vp_index = vha->vp_idx;
abt_iocb->req_que_no = cpu_to_le16(aio->u.abt.req_que_no);
/* Send the command to the firmware */
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 88d8acf..e6d1629 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1049,8 +1049,6 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
ql_dbg(ql_dbg_async, vha, 0x5011,
"Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
mb[1], mb[2], mb[3]);
-
- qlt_async_event(mb[0], vha, mb);
break;
}
@@ -1067,8 +1065,6 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
set_bit(VP_CONFIG_OK, &vha->vp_flags);
-
- qlt_async_event(mb[0], vha, mb);
break;
case MBA_RSCN_UPDATE: /* State Change Registration */
@@ -2837,6 +2833,7 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
case ELS_IOCB_TYPE:
case ABORT_IOCB_TYPE:
case MBX_IOCB_TYPE:
+ default:
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (sp) {
sp->done(sp, res);
@@ -2847,7 +2844,6 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
case ABTS_RESP_24XX:
case CTIO_TYPE7:
case CTIO_CRC2:
- default:
return 1;
}
fatal:
@@ -3121,6 +3117,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
uint16_t mb[8];
struct rsp_que *rsp;
unsigned long flags;
+ bool process_atio = false;
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
@@ -3181,22 +3178,13 @@ qla24xx_intr_handler(int irq, void *dev_id)
qla24xx_process_response_queue(vha, rsp);
break;
case INTR_ATIO_QUE_UPDATE_27XX:
- case INTR_ATIO_QUE_UPDATE:{
- unsigned long flags2;
- spin_lock_irqsave(&ha->tgt.atio_lock, flags2);
- qlt_24xx_process_atio_queue(vha, 1);
- spin_unlock_irqrestore(&ha->tgt.atio_lock, flags2);
+ case INTR_ATIO_QUE_UPDATE:
+ process_atio = true;
break;
- }
- case INTR_ATIO_RSP_QUE_UPDATE: {
- unsigned long flags2;
- spin_lock_irqsave(&ha->tgt.atio_lock, flags2);
- qlt_24xx_process_atio_queue(vha, 1);
- spin_unlock_irqrestore(&ha->tgt.atio_lock, flags2);
-
+ case INTR_ATIO_RSP_QUE_UPDATE:
+ process_atio = true;
qla24xx_process_response_queue(vha, rsp);
break;
- }
default:
ql_dbg(ql_dbg_async, vha, 0x504f,
"Unrecognized interrupt type (%d).\n", stat * 0xff);
@@ -3210,6 +3198,12 @@ qla24xx_intr_handler(int irq, void *dev_id)
qla2x00_handle_mbx_completion(ha, status);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (process_atio) {
+ spin_lock_irqsave(&ha->tgt.atio_lock, flags);
+ qlt_24xx_process_atio_queue(vha, 0);
+ spin_unlock_irqrestore(&ha->tgt.atio_lock, flags);
+ }
+
return IRQ_HANDLED;
}
@@ -3256,6 +3250,7 @@ qla24xx_msix_default(int irq, void *dev_id)
uint32_t hccr;
uint16_t mb[8];
unsigned long flags;
+ bool process_atio = false;
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
@@ -3312,22 +3307,13 @@ qla24xx_msix_default(int irq, void *dev_id)
qla24xx_process_response_queue(vha, rsp);
break;
case INTR_ATIO_QUE_UPDATE_27XX:
- case INTR_ATIO_QUE_UPDATE:{
- unsigned long flags2;
- spin_lock_irqsave(&ha->tgt.atio_lock, flags2);
- qlt_24xx_process_atio_queue(vha, 1);
- spin_unlock_irqrestore(&ha->tgt.atio_lock, flags2);
+ case INTR_ATIO_QUE_UPDATE:
+ process_atio = true;
break;
- }
- case INTR_ATIO_RSP_QUE_UPDATE: {
- unsigned long flags2;
- spin_lock_irqsave(&ha->tgt.atio_lock, flags2);
- qlt_24xx_process_atio_queue(vha, 1);
- spin_unlock_irqrestore(&ha->tgt.atio_lock, flags2);
-
+ case INTR_ATIO_RSP_QUE_UPDATE:
+ process_atio = true;
qla24xx_process_response_queue(vha, rsp);
break;
- }
default:
ql_dbg(ql_dbg_async, vha, 0x5051,
"Unrecognized interrupt type (%d).\n", stat & 0xff);
@@ -3338,6 +3324,12 @@ qla24xx_msix_default(int irq, void *dev_id)
qla2x00_handle_mbx_completion(ha, status);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (process_atio) {
+ spin_lock_irqsave(&ha->tgt.atio_lock, flags);
+ qlt_24xx_process_atio_queue(vha, 0);
+ spin_unlock_irqrestore(&ha->tgt.atio_lock, flags);
+ }
+
return IRQ_HANDLED;
}
@@ -3422,10 +3414,8 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
ha->msix_count, ret);
goto msix_out;
} else if (ret < ha->msix_count) {
- ql_log(ql_log_warn, vha, 0x00c6,
- "MSI-X: Failed to enable support "
- "with %d vectors, using %d vectors.\n",
- ha->msix_count, ret);
+ ql_log(ql_log_info, vha, 0x00c6,
+ "MSI-X: Using %d vectors\n", ret);
ha->msix_count = ret;
/* Recalculate queue values */
if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 84f57f0..abef3b2 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -684,6 +684,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
mcp->mb[2] = LSW(risc_addr);
mcp->mb[3] = 0;
mcp->mb[4] = 0;
+ mcp->mb[11] = 0;
ha->flags.using_lr_setting = 0;
if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
IS_QLA27XX(ha)) {
@@ -727,7 +728,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
if (ha->flags.exchoffld_enabled)
mcp->mb[4] |= ENABLE_EXCHANGE_OFFLD;
- mcp->out_mb |= MBX_4|MBX_3|MBX_2|MBX_1;
+ mcp->out_mb |= MBX_4 | MBX_3 | MBX_2 | MBX_1 | MBX_11;
mcp->in_mb |= MBX_3 | MBX_2 | MBX_1;
} else {
mcp->mb[1] = LSW(risc_addr);
@@ -3870,6 +3871,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
vha->d_id.b24 = 0;
vha->d_id.b.al_pa = 1;
ha->flags.n2n_bigger = 1;
+ ha->flags.n2n_ae = 0;
id.b.al_pa = 2;
ql_dbg(ql_dbg_async, vha, 0x5075,
@@ -3880,6 +3882,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
"Format 1: Remote login - Waiting for WWPN %8phC.\n",
rptid_entry->u.f1.port_name);
ha->flags.n2n_bigger = 0;
+ ha->flags.n2n_ae = 1;
}
qla24xx_post_newsess_work(vha, &id,
rptid_entry->u.f1.port_name,
@@ -3891,7 +3894,6 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
/* if our portname is higher then initiate N2N login */
set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
- ha->flags.n2n_ae = 1;
return;
break;
case TOPO_FL:
@@ -6130,17 +6132,13 @@ int qla24xx_send_mb_cmd(struct scsi_qla_host *vha, mbx_cmd_t *mcp)
case QLA_SUCCESS:
ql_dbg(ql_dbg_mbx, vha, 0x119d, "%s: %s done.\n",
__func__, sp->name);
- sp->free(sp);
break;
default:
ql_dbg(ql_dbg_mbx, vha, 0x119e, "%s: %s Failed. %x.\n",
__func__, sp->name, rval);
- sp->free(sp);
break;
}
- return rval;
-
done_free_sp:
sp->free(sp);
done:
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index d620f4b..516fccd 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -931,7 +931,7 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL);
if (!sp)
- goto done;
+ return rval;
sp->type = SRB_CTRL_VP;
sp->name = "ctrl_vp";
@@ -946,7 +946,7 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
ql_dbg(ql_dbg_async, vha, 0xffff,
"%s: %s Failed submission. %x.\n",
__func__, sp->name, rval);
- goto done_free_sp;
+ goto done;
}
ql_dbg(ql_dbg_vport, vha, 0x113f, "%s hndl %x submitted\n",
@@ -962,16 +962,13 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
case QLA_SUCCESS:
ql_dbg(ql_dbg_vport, vha, 0xffff, "%s: %s done.\n",
__func__, sp->name);
- goto done_free_sp;
+ break;
default:
ql_dbg(ql_dbg_vport, vha, 0xffff, "%s: %s Failed. %x.\n",
__func__, sp->name, rval);
- goto done_free_sp;
+ break;
}
done:
- return rval;
-
-done_free_sp:
sp->free(sp);
return rval;
}
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index e6545cb..db367e4 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -474,21 +474,10 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
int rval = -ENODEV;
srb_t *sp;
struct qla_qpair *qpair = hw_queue_handle;
- struct nvme_private *priv;
+ struct nvme_private *priv = fd->private;
struct qla_nvme_rport *qla_rport = rport->private;
- if (!fd || !qpair) {
- ql_log(ql_log_warn, NULL, 0x2134,
- "NO NVMe request or Queue Handle\n");
- return rval;
- }
-
- priv = fd->private;
fcport = qla_rport->fcport;
- if (!fcport) {
- ql_log(ql_log_warn, NULL, 0x210e, "No fcport ptr\n");
- return rval;
- }
vha = fcport->vha;
@@ -517,6 +506,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
sp->name = "nvme_cmd";
sp->done = qla_nvme_sp_done;
sp->qpair = qpair;
+ sp->vha = vha;
nvme = &sp->u.iocb_cmd;
nvme->u.nvme.desc = fd;
@@ -564,12 +554,13 @@ static void qla_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
schedule_work(&fcport->free_work);
}
- fcport->nvme_flag &= ~(NVME_FLAG_REGISTERED | NVME_FLAG_DELETING);
+ fcport->nvme_flag &= ~NVME_FLAG_DELETING;
ql_log(ql_log_info, fcport->vha, 0x2110,
"remoteport_delete of %p completed.\n", fcport);
}
static struct nvme_fc_port_template qla_nvme_fc_transport = {
+ .module = THIS_MODULE,
.localport_delete = qla_nvme_localport_delete,
.remoteport_delete = qla_nvme_remoteport_delete,
.create_queue = qla_nvme_alloc_queue,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 60b6019..bb20a4a 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1029,7 +1029,7 @@ qla2xxx_mqueuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd,
ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3078,
"Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
if (rval == QLA_INTERFACE_ERROR)
- goto qc24_fail_command;
+ goto qc24_free_sp_fail_command;
goto qc24_host_busy_free_sp;
}
@@ -1044,6 +1044,11 @@ qla2xxx_mqueuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd,
qc24_target_busy:
return SCSI_MLQUEUE_TARGET_BUSY;
+qc24_free_sp_fail_command:
+ sp->free(sp);
+ CMD_SP(cmd) = NULL;
+ qla2xxx_rel_qpair_sp(sp->qpair, sp);
+
qc24_fail_command:
cmd->scsi_done(cmd);
@@ -1744,6 +1749,7 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
!ha->flags.eeh_busy &&
(!test_bit(ABORT_ISP_ACTIVE,
&vha->dpc_flags)) &&
+ !qla2x00_isp_reg_stat(ha) &&
(sp->type == SRB_SCSI_CMD)) {
/*
* Don't abort commands in
@@ -3186,6 +3192,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);
ha->wq = alloc_workqueue("qla2xxx_wq", 0, 0);
+ if (unlikely(!ha->wq)) {
+ ret = -ENOMEM;
+ goto probe_failed;
+ }
if (ha->isp_ops->initialize_adapter(base_vha)) {
ql_log(ql_log_fatal, base_vha, 0x00d6,
@@ -3492,6 +3502,10 @@ qla2x00_shutdown(struct pci_dev *pdev)
qla2x00_try_to_stop_firmware(vha);
}
+ /* Disable timer */
+ if (vha->timer_active)
+ qla2x00_stop_timer(vha);
+
/* Turn adapter off line */
vha->flags.online = 0;
@@ -3529,6 +3543,8 @@ qla2x00_delete_all_vps(struct qla_hw_data *ha, scsi_qla_host_t *base_vha)
spin_unlock_irqrestore(&ha->vport_slock, flags);
mutex_unlock(&ha->vport_lock);
+ qla_nvme_delete(vha);
+
fc_vport_terminate(vha->fc_vport);
scsi_host_put(vha->host);
@@ -6051,12 +6067,27 @@ qla2x00_do_dpc(void *data)
if (test_and_clear_bit
(ISP_ABORT_NEEDED, &base_vha->dpc_flags) &&
!test_bit(UNLOADING, &base_vha->dpc_flags)) {
+ bool do_reset = true;
- ql_dbg(ql_dbg_dpc, base_vha, 0x4007,
- "ISP abort scheduled.\n");
- if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
+ switch (ql2x_ini_mode) {
+ case QLA2XXX_INI_MODE_ENABLED:
+ break;
+ case QLA2XXX_INI_MODE_DISABLED:
+ if (!qla_tgt_mode_enabled(base_vha))
+ do_reset = false;
+ break;
+ case QLA2XXX_INI_MODE_DUAL:
+ if (!qla_dual_mode_enabled(base_vha))
+ do_reset = false;
+ break;
+ default:
+ break;
+ }
+
+ if (do_reset && !(test_and_set_bit(ABORT_ISP_ACTIVE,
&base_vha->dpc_flags))) {
-
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4007,
+ "ISP abort scheduled.\n");
if (ha->isp_ops->abort_isp(base_vha)) {
/* failed. retry later */
set_bit(ISP_ABORT_NEEDED,
@@ -6064,10 +6095,9 @@ qla2x00_do_dpc(void *data)
}
clear_bit(ABORT_ISP_ACTIVE,
&base_vha->dpc_flags);
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4008,
+ "ISP abort end.\n");
}
-
- ql_dbg(ql_dbg_dpc, base_vha, 0x4008,
- "ISP abort end.\n");
}
if (test_and_clear_bit(FCPORT_UPDATE_NEEDED,
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index d6dc320..e954541 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -1261,7 +1261,6 @@ void qlt_schedule_sess_for_deletion(struct fc_port *sess)
"Scheduling sess %p for deletion %8phC\n",
sess, sess->port_name);
- INIT_WORK(&sess->del_work, qla24xx_delete_sess_fn);
WARN_ON(!queue_work(sess->vha->hw->wq, &sess->del_work));
}
@@ -4703,6 +4702,12 @@ static int qlt_handle_login(struct scsi_qla_host *vha,
sess = qlt_find_sess_invalidate_other(vha, wwn,
port_id, loop_id, &conflict_sess);
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "%s %d Term INOT due to WWN=0 lid=%d, NportID %06X ",
+ __func__, __LINE__, loop_id, port_id.b24);
+ qlt_send_term_imm_notif(vha, iocb, 1);
+ goto out;
}
if (IS_SW_RESV_ADDR(port_id)) {
@@ -4774,6 +4779,7 @@ static int qlt_handle_login(struct scsi_qla_host *vha,
switch (sess->disc_state) {
case DSC_DELETED:
+ case DSC_LOGIN_PEND:
qlt_plogi_ack_unref(vha, pla);
break;
@@ -6056,7 +6062,6 @@ static void qlt_abort_work(struct qla_tgt *tgt,
struct qla_hw_data *ha = vha->hw;
struct fc_port *sess = NULL;
unsigned long flags = 0, flags2 = 0;
- uint32_t be_s_id;
uint8_t s_id[3];
int rc;
@@ -6069,8 +6074,7 @@ static void qlt_abort_work(struct qla_tgt *tgt,
s_id[1] = prm->abts.fcp_hdr_le.s_id[1];
s_id[2] = prm->abts.fcp_hdr_le.s_id[0];
- sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
- (unsigned char *)&be_s_id);
+ sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
if (!sess) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2);
@@ -6539,7 +6543,8 @@ qlt_enable_vha(struct scsi_qla_host *vha)
} else {
set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
qla2xxx_wake_dpc(base_vha);
- qla2x00_wait_for_hba_online(base_vha);
+ WARN_ON_ONCE(qla2x00_wait_for_hba_online(base_vha) !=
+ QLA_SUCCESS);
}
}
EXPORT_SYMBOL(qlt_enable_vha);
@@ -6569,7 +6574,9 @@ static void qlt_disable_vha(struct scsi_qla_host *vha)
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
- qla2x00_wait_for_hba_online(vha);
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
+ ql_dbg(ql_dbg_tgt, vha, 0xe081,
+ "qla2x00_wait_for_hba_online() failed\n");
}
/*
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index b8c1a73..654e1af 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -926,38 +926,14 @@ static ssize_t tcm_qla2xxx_tpg_enable_show(struct config_item *item,
atomic_read(&tpg->lport_tpg_enabled));
}
-static void tcm_qla2xxx_depend_tpg(struct work_struct *work)
-{
- struct tcm_qla2xxx_tpg *base_tpg = container_of(work,
- struct tcm_qla2xxx_tpg, tpg_base_work);
- struct se_portal_group *se_tpg = &base_tpg->se_tpg;
- struct scsi_qla_host *base_vha = base_tpg->lport->qla_vha;
-
- if (!target_depend_item(&se_tpg->tpg_group.cg_item)) {
- atomic_set(&base_tpg->lport_tpg_enabled, 1);
- qlt_enable_vha(base_vha);
- }
- complete(&base_tpg->tpg_base_comp);
-}
-
-static void tcm_qla2xxx_undepend_tpg(struct work_struct *work)
-{
- struct tcm_qla2xxx_tpg *base_tpg = container_of(work,
- struct tcm_qla2xxx_tpg, tpg_base_work);
- struct se_portal_group *se_tpg = &base_tpg->se_tpg;
- struct scsi_qla_host *base_vha = base_tpg->lport->qla_vha;
-
- if (!qlt_stop_phase1(base_vha->vha_tgt.qla_tgt)) {
- atomic_set(&base_tpg->lport_tpg_enabled, 0);
- target_undepend_item(&se_tpg->tpg_group.cg_item);
- }
- complete(&base_tpg->tpg_base_comp);
-}
-
static ssize_t tcm_qla2xxx_tpg_enable_store(struct config_item *item,
const char *page, size_t count)
{
struct se_portal_group *se_tpg = to_tpg(item);
+ struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+ struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+ struct tcm_qla2xxx_lport, lport_wwn);
+ struct scsi_qla_host *vha = lport->qla_vha;
struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
struct tcm_qla2xxx_tpg, se_tpg);
unsigned long op;
@@ -976,24 +952,16 @@ static ssize_t tcm_qla2xxx_tpg_enable_store(struct config_item *item,
if (atomic_read(&tpg->lport_tpg_enabled))
return -EEXIST;
- INIT_WORK(&tpg->tpg_base_work, tcm_qla2xxx_depend_tpg);
+ atomic_set(&tpg->lport_tpg_enabled, 1);
+ qlt_enable_vha(vha);
} else {
if (!atomic_read(&tpg->lport_tpg_enabled))
return count;
- INIT_WORK(&tpg->tpg_base_work, tcm_qla2xxx_undepend_tpg);
+ atomic_set(&tpg->lport_tpg_enabled, 0);
+ qlt_stop_phase1(vha->vha_tgt.qla_tgt);
}
- init_completion(&tpg->tpg_base_comp);
- schedule_work(&tpg->tpg_base_work);
- wait_for_completion(&tpg->tpg_base_comp);
- if (op) {
- if (!atomic_read(&tpg->lport_tpg_enabled))
- return -ENODEV;
- } else {
- if (atomic_read(&tpg->lport_tpg_enabled))
- return -EPERM;
- }
return count;
}
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h
index 7550ba2..147cf6c 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.h
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.h
@@ -48,9 +48,6 @@ struct tcm_qla2xxx_tpg {
struct tcm_qla2xxx_tpg_attrib tpg_attrib;
/* Returned by tcm_qla2xxx_make_tpg() */
struct se_portal_group se_tpg;
- /* Items for dealing with configfs_depend_item */
- struct completion tpg_base_comp;
- struct work_struct tpg_base_work;
};
struct tcm_qla2xxx_fc_loopid {
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 25c8ce5..f8acf10 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -4280,7 +4280,6 @@ static int qla4xxx_mem_alloc(struct scsi_qla_host *ha)
return QLA_SUCCESS;
mem_alloc_error_exit:
- qla4xxx_mem_free(ha);
return QLA_ERROR;
}
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 65305b3..a1dbae8 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5351,6 +5351,11 @@ static int __init scsi_debug_init(void)
return -EINVAL;
}
+ if (sdebug_num_tgts < 0) {
+ pr_err("num_tgts must be >= 0\n");
+ return -EINVAL;
+ }
+
if (sdebug_guard > 1) {
pr_err("guard must be 0 or 1\n");
return -EINVAL;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index af349d1..f86a6be 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2377,7 +2377,8 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
{
unsigned int cmd_size, sgl_size;
- sgl_size = scsi_mq_sgl_size(shost);
+ sgl_size = max_t(unsigned int, sizeof(struct scatterlist),
+ scsi_mq_sgl_size(shost));
cmd_size = sizeof(struct scsi_cmnd) + shost->hostt->cmd_size + sgl_size;
if (scsi_host_get_prot(shost))
cmd_size += sizeof(struct scsi_data_buffer) + sgl_size;
diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c
index 0ff083b..617a607 100644
--- a/drivers/scsi/scsi_trace.c
+++ b/drivers/scsi/scsi_trace.c
@@ -30,15 +30,18 @@ static const char *
scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len)
{
const char *ret = trace_seq_buffer_ptr(p);
- sector_t lba = 0, txlen = 0;
+ u32 lba = 0, txlen;
lba |= ((cdb[1] & 0x1F) << 16);
lba |= (cdb[2] << 8);
lba |= cdb[3];
- txlen = cdb[4];
+ /*
+ * From SBC-2: a TRANSFER LENGTH field set to zero specifies that 256
+ * logical blocks shall be read (READ(6)) or written (WRITE(6)).
+ */
+ txlen = cdb[4] ? cdb[4] : 256;
- trace_seq_printf(p, "lba=%llu txlen=%llu",
- (unsigned long long)lba, (unsigned long long)txlen);
+ trace_seq_printf(p, "lba=%u txlen=%u", lba, txlen);
trace_seq_putc(p, 0);
return ret;
diff --git a/drivers/scsi/sni_53c710.c b/drivers/scsi/sni_53c710.c
index 1f9a087..3102a75 100644
--- a/drivers/scsi/sni_53c710.c
+++ b/drivers/scsi/sni_53c710.c
@@ -78,10 +78,8 @@ static int snirm710_probe(struct platform_device *dev)
base = res->start;
hostdata = kzalloc(sizeof(*hostdata), GFP_KERNEL);
- if (!hostdata) {
- dev_printk(KERN_ERR, dev, "Failed to allocate host data\n");
+ if (!hostdata)
return -ENOMEM;
- }
hostdata->dev = &dev->dev;
dma_set_mask(&dev->dev, DMA_BIT_MASK(32));
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index 9492638..af8a7ef 100644
--- a/drivers/scsi/sun3_scsi.c
+++ b/drivers/scsi/sun3_scsi.c
@@ -498,7 +498,7 @@ static struct scsi_host_template sun3_scsi_template = {
.eh_host_reset_handler = sun3scsi_host_reset,
.can_queue = 16,
.this_id = 7,
- .sg_tablesize = SG_NONE,
+ .sg_tablesize = 1,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING,
.cmd_size = NCR5380_CMD_SIZE,
@@ -520,7 +520,7 @@ static int __init sun3_scsi_probe(struct platform_device *pdev)
sun3_scsi_template.can_queue = setup_can_queue;
if (setup_cmd_per_lun > 0)
sun3_scsi_template.cmd_per_lun = setup_cmd_per_lun;
- if (setup_sg_tablesize >= 0)
+ if (setup_sg_tablesize > 0)
sun3_scsi_template.sg_tablesize = setup_sg_tablesize;
if (setup_hostid >= 0)
sun3_scsi_template.this_id = setup_hostid & 7;
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index bd3f6e2..0a2a545 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -4370,6 +4370,13 @@ static void sym_nego_rejected(struct sym_hcb *np, struct sym_tcb *tp, struct sym
OUTB(np, HS_PRT, HS_BUSY);
}
+#define sym_printk(lvl, tp, cp, fmt, v...) do { \
+ if (cp) \
+ scmd_printk(lvl, cp->cmd, fmt, ##v); \
+ else \
+ starget_printk(lvl, tp->starget, fmt, ##v); \
+} while (0)
+
/*
* chip exception handler for programmed interrupts.
*/
@@ -4415,7 +4422,7 @@ static void sym_int_sir(struct sym_hcb *np)
* been selected with ATN. We do not want to handle that.
*/
case SIR_SEL_ATN_NO_MSG_OUT:
- scmd_printk(KERN_WARNING, cp->cmd,
+ sym_printk(KERN_WARNING, tp, cp,
"No MSG OUT phase after selection with ATN\n");
goto out_stuck;
/*
@@ -4423,7 +4430,7 @@ static void sym_int_sir(struct sym_hcb *np)
* having reselected the initiator.
*/
case SIR_RESEL_NO_MSG_IN:
- scmd_printk(KERN_WARNING, cp->cmd,
+ sym_printk(KERN_WARNING, tp, cp,
"No MSG IN phase after reselection\n");
goto out_stuck;
/*
@@ -4431,7 +4438,7 @@ static void sym_int_sir(struct sym_hcb *np)
* an IDENTIFY.
*/
case SIR_RESEL_NO_IDENTIFY:
- scmd_printk(KERN_WARNING, cp->cmd,
+ sym_printk(KERN_WARNING, tp, cp,
"No IDENTIFY after reselection\n");
goto out_stuck;
/*
@@ -4460,7 +4467,7 @@ static void sym_int_sir(struct sym_hcb *np)
case SIR_RESEL_ABORTED:
np->lastmsg = np->msgout[0];
np->msgout[0] = M_NOOP;
- scmd_printk(KERN_WARNING, cp->cmd,
+ sym_printk(KERN_WARNING, tp, cp,
"message %x sent on bad reselection\n", np->lastmsg);
goto out;
/*
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6640c27..445cbec 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3,7 +3,7 @@
*
* This code is based on drivers/scsi/ufs/ufshcd.c
* Copyright (C) 2011-2013 Samsung India Software Operations
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
@@ -2508,6 +2508,34 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler(
return HRTIMER_NORESTART;
}
+static void ufshcd_init_clk_scaling(struct ufs_hba *hba)
+{
+ char wq_name[sizeof("ufs_clkscaling_00")];
+
+ if (!ufshcd_is_clkscaling_supported(hba))
+ return;
+
+ INIT_WORK(&hba->clk_scaling.suspend_work,
+ ufshcd_clk_scaling_suspend_work);
+ INIT_WORK(&hba->clk_scaling.resume_work,
+ ufshcd_clk_scaling_resume_work);
+
+ snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d",
+ hba->host->host_no);
+ hba->clk_scaling.workq = create_singlethread_workqueue(wq_name);
+
+ ufshcd_clkscaling_init_sysfs(hba);
+}
+
+static void ufshcd_exit_clk_scaling(struct ufs_hba *hba)
+{
+ if (!ufshcd_is_clkscaling_supported(hba))
+ return;
+
+ destroy_workqueue(hba->clk_scaling.workq);
+ ufshcd_devfreq_remove(hba);
+}
+
static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
struct ufs_clk_gating *gating = &hba->clk_gating;
@@ -4355,10 +4383,10 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
goto out_unlock;
}
- hba->dev_cmd.query.descriptor = NULL;
*buf_len = be16_to_cpu(response->upiu_res.length);
out_unlock:
+ hba->dev_cmd.query.descriptor = NULL;
mutex_unlock(&hba->dev_cmd.lock);
out:
ufshcd_release_all(hba);
@@ -9105,6 +9133,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
ufshcd_wb_config(hba);
/*
+ * Enable auto hibern8 if supported, after full host and
+ * device initialization.
+ */
+ ufshcd_set_auto_hibern8_timer(hba);
+
+ /*
* If we are in error handling context or in power management callbacks
* context, no need to scan the host
*/
@@ -9142,12 +9176,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
pm_runtime_put_sync(hba->dev);
}
- /*
- * Enable auto hibern8 if supported, after full host and
- * device initialization.
- */
- ufshcd_set_auto_hibern8_timer(hba);
-
out:
if (ret) {
ufshcd_set_ufs_dev_poweroff(hba);
@@ -9158,8 +9186,11 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
* If we failed to initialize the device or the device is not
* present, turn off the power/clocks etc.
*/
- if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress)
+ if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) {
pm_runtime_put_sync(hba->dev);
+ ufshcd_exit_clk_scaling(hba);
+ ufshcd_hba_exit(hba);
+ }
trace_ufshcd_init(dev_name(hba->dev), ret,
ktime_to_us(ktime_sub(ktime_get(), start)),
@@ -9993,13 +10024,9 @@ static void ufshcd_hba_exit(struct ufs_hba *hba)
if (hba->is_powered) {
ufshcd_variant_hba_exit(hba);
ufshcd_setup_vreg(hba, false);
- if (ufshcd_is_clkscaling_supported(hba)) {
+ if (ufshcd_is_clkscaling_supported(hba))
if (hba->devfreq)
ufshcd_suspend_clkscaling(hba);
- if (hba->clk_scaling.workq)
- destroy_workqueue(hba->clk_scaling.workq);
- ufshcd_devfreq_remove(hba);
- }
if (hba->recovery_wq)
destroy_workqueue(hba->recovery_wq);
@@ -10817,6 +10844,7 @@ void ufshcd_remove(struct ufs_hba *hba)
ufshcd_disable_intr(hba, hba->intr_mask);
ufshcd_hba_stop(hba, true);
+ ufshcd_exit_clk_scaling(hba);
ufshcd_exit_clk_gating(hba);
ufshcd_exit_hibern8_on_idle(hba);
if (ufshcd_is_clkscaling_supported(hba)) {
@@ -11012,6 +11040,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_init_clk_gating(hba);
ufshcd_init_hibern8(hba);
+ ufshcd_init_clk_scaling(hba);
+
/*
* In order to avoid any spurious interrupt immediately after
* registering UFS controller interrupt handler, clear any pending UFS
@@ -11063,21 +11093,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
goto out_remove_scsi_host;
}
- if (ufshcd_is_clkscaling_supported(hba)) {
- char wq_name[sizeof("ufs_clkscaling_00")];
-
- INIT_WORK(&hba->clk_scaling.suspend_work,
- ufshcd_clk_scaling_suspend_work);
- INIT_WORK(&hba->clk_scaling.resume_work,
- ufshcd_clk_scaling_resume_work);
-
- snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d",
- host->host_no);
- hba->clk_scaling.workq = create_singlethread_workqueue(wq_name);
-
- ufshcd_clkscaling_init_sysfs(hba);
- }
-
/*
* If rpm_lvl and and spm_lvl are not already set to valid levels,
* set the default power management level for UFS runtime and system
@@ -11118,6 +11133,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
out_remove_scsi_host:
scsi_remove_host(hba->host);
exit_gating:
+ ufshcd_exit_clk_scaling(hba);
ufshcd_exit_clk_gating(hba);
out_disable:
hba->is_irq_enabled = false;
diff --git a/drivers/scsi/zorro_esp.c b/drivers/scsi/zorro_esp.c
index bb70882e..6a5b547 100644
--- a/drivers/scsi/zorro_esp.c
+++ b/drivers/scsi/zorro_esp.c
@@ -245,7 +245,14 @@ static int fastlane_esp_irq_pending(struct esp *esp)
static u32 zorro_esp_dma_length_limit(struct esp *esp, u32 dma_addr,
u32 dma_len)
{
- return dma_len > 0xFFFFFF ? 0xFFFFFF : dma_len;
+ return dma_len > (1U << 16) ? (1U << 16) : dma_len;
+}
+
+static u32 fastlane_esp_dma_length_limit(struct esp *esp, u32 dma_addr,
+ u32 dma_len)
+{
+ /* The old driver used 0xfffc as limit, so do that here too */
+ return dma_len > 0xfffc ? 0xfffc : dma_len;
}
static void zorro_esp_reset_dma(struct esp *esp)
@@ -484,7 +491,6 @@ static void zorro_esp_send_blz1230_dma_cmd(struct esp *esp, u32 addr,
scsi_esp_cmd(esp, ESP_CMD_DMA);
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
scsi_esp_cmd(esp, cmd);
}
@@ -529,7 +535,6 @@ static void zorro_esp_send_blz1230II_dma_cmd(struct esp *esp, u32 addr,
scsi_esp_cmd(esp, ESP_CMD_DMA);
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
scsi_esp_cmd(esp, cmd);
}
@@ -574,7 +579,6 @@ static void zorro_esp_send_blz2060_dma_cmd(struct esp *esp, u32 addr,
scsi_esp_cmd(esp, ESP_CMD_DMA);
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
scsi_esp_cmd(esp, cmd);
}
@@ -599,7 +603,6 @@ static void zorro_esp_send_cyber_dma_cmd(struct esp *esp, u32 addr,
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
if (write) {
/* DMA receive */
@@ -649,7 +652,6 @@ static void zorro_esp_send_cyberII_dma_cmd(struct esp *esp, u32 addr,
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
if (write) {
/* DMA receive */
@@ -691,7 +693,6 @@ static void zorro_esp_send_fastlane_dma_cmd(struct esp *esp, u32 addr,
zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
- zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
if (write) {
/* DMA receive */
@@ -824,7 +825,7 @@ static const struct esp_driver_ops fastlane_esp_ops = {
.unmap_single = zorro_esp_unmap_single,
.unmap_sg = zorro_esp_unmap_sg,
.irq_pending = fastlane_esp_irq_pending,
- .dma_length_limit = zorro_esp_dma_length_limit,
+ .dma_length_limit = fastlane_esp_dma_length_limit,
.reset_dma = zorro_esp_reset_dma,
.dma_drain = zorro_esp_dma_drain,
.dma_invalidate = fastlane_esp_dma_invalidate,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index cef8ac0..9b1bfc0 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <asm/dma-iommu.h>
@@ -167,17 +167,10 @@ static int ngd_slim_qmi_new_server(struct qmi_handle *hdl,
container_of(qmi, struct msm_slim_ctrl, qmi);
SLIM_INFO(dev, "Slimbus QMI new server event received\n");
- /* Hold wake lock until notify slaves thread is done */
- pm_stay_awake(dev->dev);
qmi->svc_info.sq_family = AF_QIPCRTR;
qmi->svc_info.sq_node = service->node;
qmi->svc_info.sq_port = service->port;
- if (dev->lpass_mem_usage) {
- dev->lpass_mem->start = dev->lpass_phy_base;
- dev->lpass.base = dev->lpass_virt_base;
- }
- atomic_set(&dev->ssr_in_progress, 0);
- schedule_work(&dev->dsp.dom_up);
+ complete(&dev->qmi_up);
return 0;
}
@@ -187,7 +180,10 @@ static void ngd_slim_qmi_del_server(struct qmi_handle *hdl,
{
struct msm_slim_qmi *qmi =
container_of(hdl, struct msm_slim_qmi, svc_event_hdl);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ reinit_completion(&dev->qmi_up);
qmi->svc_info.sq_node = 0;
qmi->svc_info.sq_port = 0;
}
@@ -275,6 +271,19 @@ static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
ngd_dom_down(dev);
mutex_unlock(&dev->tx_lock);
break;
+ case SUBSYS_AFTER_POWERUP:
+ case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+ SLIM_INFO(dev, "SLIM DSP SSR notify cb:0x%x\n", code);
+ /* Hold wake lock until notify slaves thread is done */
+ pm_stay_awake(dev->dev);
+ atomic_set(&dev->init_in_progress, 1);
+ if (dev->lpass_mem_usage) {
+ dev->lpass_mem->start = dev->lpass_phy_base;
+ dev->lpass.base = dev->lpass_virt_base;
+ }
+ atomic_set(&dev->ssr_in_progress, 0);
+ schedule_work(&dev->dsp.dom_up);
+ break;
case LOCATOR_UP:
reg = _cmd;
if (!reg || reg->total_domains != 1) {
@@ -294,9 +303,21 @@ static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
&cur);
SLIM_INFO(dev, "reg-PD client:%s with service:%s\n",
reg->client_name, reg->service_name);
- SLIM_INFO(dev, "reg-PD dom:%s instance:%d, cur:%d\n",
+ SLIM_INFO(dev, "reg-PD dom:%s instance:%d, cur:0x%x\n",
reg->domain_list->name,
reg->domain_list->instance_id, cur);
+
+ if (cur == SERVREG_NOTIF_SERVICE_STATE_UP_V01) {
+ pm_stay_awake(dev->dev);
+ atomic_set(&dev->init_in_progress, 1);
+ if (dev->lpass_mem_usage) {
+ dev->lpass_mem->start = dev->lpass_phy_base;
+ dev->lpass.base = dev->lpass_virt_base;
+ }
+ atomic_set(&dev->ssr_in_progress, 0);
+ schedule_work(&dev->dsp.dom_up);
+ }
+
if (IS_ERR_OR_NULL(dev->dsp.domr))
ngd_reg_ssr(dev);
else
@@ -1562,6 +1583,7 @@ static int ngd_slim_rx_msgq_thread(void *data)
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
struct completion *notify = &dev->rx_msgq_notify;
int ret = 0;
+ bool release_wake_lock = false;
while (!kthread_should_stop()) {
struct slim_msg_txn txn;
@@ -1599,17 +1621,36 @@ static int ngd_slim_rx_msgq_thread(void *data)
/* ADSP SSR, send device_up notifications */
if (prev_state == MSM_CTRL_DOWN)
complete(&dev->qmi.slave_notify);
+ else
+ release_wake_lock = true;
} else if (ret == -EIO) {
SLIM_WARN(dev, "capability message NACKed, retrying\n");
if (retries < INIT_MX_RETRIES) {
msleep(DEF_RETRY_MS);
retries++;
goto capability_retry;
+ } else {
+ release_wake_lock = true;
}
} else {
SLIM_WARN(dev, "SLIM: capability TX failed:%d\n", ret);
+ release_wake_lock = true;
+ }
+
+ if (release_wake_lock) {
+ /*
+ * As we are not going to reset the
+ * init_in_progress flag and release wake
+ * lock from notify slave thread, we are
+ * doing it here.
+ */
+ atomic_set(&dev->init_in_progress, 0);
+ pm_relax(dev->dev);
+ release_wake_lock = false;
}
}
+ atomic_set(&dev->init_in_progress, 0);
+ pm_relax(dev->dev);
return 0;
}
@@ -1627,6 +1668,7 @@ static int ngd_notify_slaves(void *data)
pm_relax(dev->dev);
return ret;
}
+ ngd_dom_init(dev);
while (!kthread_should_stop()) {
wait_for_completion_interruptible(&dev->qmi.slave_notify);
@@ -1642,7 +1684,6 @@ static int ngd_notify_slaves(void *data)
* controller is up
*/
slim_ctrl_add_boarddevs(&dev->ctrl);
- ngd_dom_init(dev);
} else {
slim_framer_booted(ctrl);
}
@@ -1664,7 +1705,10 @@ static int ngd_notify_slaves(void *data)
mutex_lock(&ctrl->m_ctrl);
}
mutex_unlock(&ctrl->m_ctrl);
+ atomic_set(&dev->init_in_progress, 0);
+ pm_relax(dev->dev);
}
+ atomic_set(&dev->init_in_progress, 0);
pm_relax(dev->dev);
return 0;
}
@@ -1689,8 +1733,15 @@ static void ngd_dom_up(struct work_struct *work)
container_of(work, struct msm_slim_ss, dom_up);
struct msm_slim_ctrl *dev =
container_of(dsp, struct msm_slim_ctrl, dsp);
+
+ /* Make sure qmi service is up before continuing */
+ wait_for_completion_interruptible(&dev->qmi_up);
+
mutex_lock(&dev->ssr_lock);
- ngd_slim_enable(dev, true);
+ if (ngd_slim_enable(dev, true)) {
+ atomic_set(&dev->init_in_progress, 0);
+ pm_relax(dev->dev);
+ }
mutex_unlock(&dev->ssr_lock);
}
@@ -1960,6 +2011,7 @@ static int ngd_slim_probe(struct platform_device *pdev)
init_completion(&dev->reconf);
init_completion(&dev->ctrl_up);
+ init_completion(&dev->qmi_up);
mutex_init(&dev->tx_lock);
mutex_init(&dev->ssr_lock);
spin_lock_init(&dev->tx_buf_lock);
@@ -2122,6 +2174,17 @@ static int ngd_slim_runtime_resume(struct device *device)
int ret = 0;
mutex_lock(&dev->tx_lock);
+
+ if (dev->qmi.deferred_resp) {
+ SLIM_WARN(dev, "%s: RT resume called ahead of sys resume\n",
+ __func__);
+ ret = msm_slim_qmi_deferred_status_req(dev);
+ if (ret)
+ SLIM_WARN(dev, "%s: deferred resp failure\n",
+ __func__);
+ dev->qmi.deferred_resp = false;
+ }
+
if ((dev->state >= MSM_CTRL_ASLEEP) && (dev->qmi.handle != NULL))
ret = ngd_slim_power_up(dev, false);
if (ret || dev->qmi.handle == NULL) {
@@ -2181,6 +2244,12 @@ static int ngd_slim_suspend(struct device *dev)
cdev = platform_get_drvdata(pdev);
+ if (atomic_read(&cdev->init_in_progress)) {
+ ret = -EBUSY;
+ SLIM_INFO(cdev, "system suspend due to ssr: %d\n", ret);
+ return ret;
+ }
+
if (cdev->state == MSM_CTRL_AWAKE) {
ret = -EBUSY;
SLIM_INFO(cdev, "system suspend: %d\n", ret);
@@ -2224,6 +2293,7 @@ static int ngd_slim_resume(struct device *dev)
* mark runtime-pm status as active to be consistent
* with HW status
*/
+ mutex_lock(&cdev->tx_lock);
if (cdev->qmi.deferred_resp) {
ret = msm_slim_qmi_deferred_status_req(cdev);
if (ret) {
@@ -2239,6 +2309,7 @@ static int ngd_slim_resume(struct device *dev)
* clock/power on
*/
SLIM_INFO(cdev, "system resume state: %d\n", cdev->state);
+ mutex_unlock(&cdev->tx_lock);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 5fed0aa..eabcecfe 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _SLIM_MSM_H
@@ -324,6 +324,8 @@ struct msm_slim_ctrl {
u32 current_rx_buf[10];
int current_count;
atomic_t ssr_in_progress;
+ atomic_t init_in_progress;
+ struct completion qmi_up;
};
struct msm_sat_chan {
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 113e884..f0d46b1 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -13,7 +13,7 @@
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_SOC_XWAY) += lantiq/
obj-y += mediatek/
-obj-$(CONFIG_ARCH_MESON) += amlogic/
+obj-y += amlogic/
obj-y += qcom/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
diff --git a/drivers/soc/bcm/brcmstb/pm/pm-arm.c b/drivers/soc/bcm/brcmstb/pm/pm-arm.c
index a5577dd..8ee0634 100644
--- a/drivers/soc/bcm/brcmstb/pm/pm-arm.c
+++ b/drivers/soc/bcm/brcmstb/pm/pm-arm.c
@@ -404,7 +404,7 @@ noinline int brcmstb_pm_s3_finish(void)
{
struct brcmstb_s3_params *params = ctrl.s3_params;
dma_addr_t params_pa = ctrl.s3_params_pa;
- phys_addr_t reentry = virt_to_phys(&cpu_resume);
+ phys_addr_t reentry = virt_to_phys(&cpu_resume_arm);
enum bsp_initiate_command cmd;
u32 flags;
diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c
index 2f71f7d..f9edd28 100644
--- a/drivers/soc/fsl/qbman/bman_portal.c
+++ b/drivers/soc/fsl/qbman/bman_portal.c
@@ -91,7 +91,15 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg;
struct resource *addr_phys[2];
- int irq, cpu;
+ int irq, cpu, err;
+
+ err = bman_is_probed();
+ if (!err)
+ return -EPROBE_DEFER;
+ if (err < 0) {
+ dev_err(&pdev->dev, "failing probe due to bman probe error\n");
+ return -ENODEV;
+ }
pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
if (!pcfg)
diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c
index b3da635..d160fc2 100644
--- a/drivers/soc/imx/gpc.c
+++ b/drivers/soc/imx/gpc.c
@@ -69,7 +69,7 @@ static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
u32 val;
/* Read ISO and ISO2SW power down delays */
- regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val);
+ regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
iso = val & 0x3f;
iso2sw = (val >> 8) & 0x3f;
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e5ddad5..ab38c20 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -133,6 +133,15 @@
data required to configure LLCC so that clients can start using the
LLCC slices.
+config QCOM_LAGOON_LLCC
+ tristate "Qualcomm Technologies, Inc. LAGOON LLCC driver"
+ depends on QCOM_LLCC
+ help
+ Qualcomm Technologies, Inc. platform specific LLCC driver for LAGOON.
+ Say yes here to enable the LLCC driver for LAGOON. This provides
+ data required to configure LLCC so that clients can start using the
+ LLCC slices.
+
config QCOM_SDM845_LLCC
tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
depends on QCOM_LLCC
@@ -846,6 +855,8 @@
An offline CPU is considered as a reserved CPU since this OS can't use
it.
+source "drivers/soc/qcom/icnss2/Kconfig"
+
config ICNSS
tristate "Platform driver for Q6 integrated connectivity"
select CNSS_UTILS
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 7d2ae35..530043d 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -24,6 +24,7 @@
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
obj-$(CONFIG_QCOM_KONA_LLCC) += llcc-kona.o
obj-$(CONFIG_QCOM_LITO_LLCC) += llcc-lito.o
+obj-$(CONFIG_QCOM_LAGOON_LLCC) += llcc-lagoon.o
obj-$(CONFIG_QCOM_LLCC_PERFMON) += llcc_perfmon.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_SECURE_BUFFER) += secure_buffer.o
@@ -98,3 +99,4 @@
obj-$(CONFIG_RMNET_CTL) += rmnet_ctl/
obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o
obj-$(CONFIG_QTI_L2_REUSE) += l2_reuse.o
+obj-$(CONFIG_ICNSS2) += icnss2/
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index 57af8a5..ee9197f 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -219,9 +219,9 @@ static int apr_add_device(struct device *dev, struct device_node *np,
adev->domain_id = id->domain_id;
adev->version = id->svc_version;
if (np)
- strncpy(adev->name, np->name, APR_NAME_SIZE);
+ strscpy(adev->name, np->name, APR_NAME_SIZE);
else
- strncpy(adev->name, id->name, APR_NAME_SIZE);
+ strscpy(adev->name, id->name, APR_NAME_SIZE);
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
id->domain_id, id->svc_id);
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index 53376fb..1057e55 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -570,8 +570,8 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
overstep:
ret = -EINVAL;
dcc_sram_memset(drvdata->dev, drvdata->ram_base, 0, drvdata->ram_size);
- dev_err(drvdata->dev, "DCC SRAM oversteps, 0x%x (0x%x)\n",
- sram_offset, drvdata->ram_size);
+ dev_err(drvdata->dev, "list: %d, DCC SRAM oversteps, 0x%x (0x%x)\n",
+ curr_list, sram_offset, drvdata->ram_size);
err:
return ret;
}
@@ -636,6 +636,58 @@ static bool is_dcc_enabled(struct dcc_drvdata *drvdata)
return dcc_enable;
}
+static void __dcc_config_reset(struct dcc_drvdata *drvdata)
+{
+ struct dcc_config_entry *entry, *temp;
+ int curr_list;
+
+ for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
+
+ list_for_each_entry_safe(entry, temp,
+ &drvdata->cfg_head[curr_list], list) {
+ list_del(&entry->list);
+ devm_kfree(drvdata->dev, entry);
+ drvdata->nr_config[curr_list]--;
+ }
+ }
+ drvdata->ram_start = 0;
+ drvdata->ram_cfg = 0;
+}
+
+static void dcc_config_reset(struct dcc_drvdata *drvdata)
+{
+ mutex_lock(&drvdata->mutex);
+ __dcc_config_reset(drvdata);
+ mutex_unlock(&drvdata->mutex);
+}
+static void __dcc_disable(struct dcc_drvdata *drvdata)
+{
+ int curr_list;
+
+ if (!dcc_ready(drvdata))
+ dev_err(drvdata->dev, "DCC is not ready Disabling DCC...\n");
+
+ for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
+ if (!drvdata->enable[curr_list])
+ continue;
+ dcc_writel(drvdata, 0, DCC_LL_CFG(curr_list));
+ dcc_writel(drvdata, 0, DCC_LL_BASE(curr_list));
+ dcc_writel(drvdata, 0, DCC_FD_BASE(curr_list));
+ dcc_writel(drvdata, 0, DCC_LL_LOCK(curr_list));
+ drvdata->enable[curr_list] = false;
+ }
+ dcc_sram_memset(drvdata->dev, drvdata->ram_base, 0, drvdata->ram_size);
+ drvdata->ram_cfg = 0;
+ drvdata->ram_start = 0;
+}
+
+static void dcc_disable(struct dcc_drvdata *drvdata)
+{
+ mutex_lock(&drvdata->mutex);
+ __dcc_disable(drvdata);
+ mutex_unlock(&drvdata->mutex);
+}
+
static int dcc_enable(struct dcc_drvdata *drvdata)
{
int ret = 0;
@@ -662,7 +714,10 @@ static int dcc_enable(struct dcc_drvdata *drvdata)
ret = __dcc_ll_cfg(drvdata, list);
if (ret) {
dcc_writel(drvdata, 0, DCC_LL_LOCK(list));
- dev_info(drvdata->dev, "DCC ram programming failed\n");
+ dev_err(drvdata->dev, "DCC ram programming failed\n"
+ "Disable all links and reset all config\n");
+ __dcc_disable(drvdata);
+ __dcc_config_reset(drvdata);
goto err;
}
@@ -704,30 +759,6 @@ static int dcc_enable(struct dcc_drvdata *drvdata)
return ret;
}
-static void dcc_disable(struct dcc_drvdata *drvdata)
-{
- int curr_list;
-
- mutex_lock(&drvdata->mutex);
-
- if (!dcc_ready(drvdata))
- dev_err(drvdata->dev, "DCC is not ready Disabling DCC...\n");
-
- for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
- if (!drvdata->enable[curr_list])
- continue;
- dcc_writel(drvdata, 0, DCC_LL_CFG(curr_list));
- dcc_writel(drvdata, 0, DCC_LL_BASE(curr_list));
- dcc_writel(drvdata, 0, DCC_FD_BASE(curr_list));
- dcc_writel(drvdata, 0, DCC_LL_LOCK(curr_list));
- drvdata->enable[curr_list] = false;
- }
- dcc_sram_memset(drvdata->dev, drvdata->ram_base, 0, drvdata->ram_size);
- drvdata->ram_cfg = 0;
- drvdata->ram_start = 0;
- mutex_unlock(&drvdata->mutex);
-}
-
static ssize_t curr_list_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1151,27 +1182,6 @@ static ssize_t config_store(struct device *dev,
}
static DEVICE_ATTR_RW(config);
-static void dcc_config_reset(struct dcc_drvdata *drvdata)
-{
- struct dcc_config_entry *entry, *temp;
- int curr_list;
-
- mutex_lock(&drvdata->mutex);
-
- for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
-
- list_for_each_entry_safe(entry, temp,
- &drvdata->cfg_head[curr_list], list) {
- list_del(&entry->list);
- devm_kfree(drvdata->dev, entry);
- drvdata->nr_config[curr_list]--;
- }
- }
- drvdata->ram_start = 0;
- drvdata->ram_cfg = 0;
- mutex_unlock(&drvdata->mutex);
-}
-
static ssize_t config_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c
index 1204ebc..0ee43a8 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -621,7 +621,6 @@ static int msm_eud_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"TCSR scm_io_write failed with rc:%d\n", ret);
- goto error;
}
} else {
dev_err(chip->dev,
diff --git a/drivers/soc/qcom/fsa4480-i2c.c b/drivers/soc/qcom/fsa4480-i2c.c
index 707d249..2fc871c 100644
--- a/drivers/soc/qcom/fsa4480-i2c.c
+++ b/drivers/soc/qcom/fsa4480-i2c.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -228,8 +228,11 @@ EXPORT_SYMBOL(fsa4480_reg_notifier);
int fsa4480_unreg_notifier(struct notifier_block *nb,
struct device_node *node)
{
+ int rc = 0;
struct i2c_client *client = of_find_i2c_device_by_node(node);
struct fsa4480_priv *fsa_priv;
+ struct device *dev;
+ union power_supply_propval mode;
if (!client)
return -EINVAL;
@@ -237,10 +240,27 @@ int fsa4480_unreg_notifier(struct notifier_block *nb,
fsa_priv = (struct fsa4480_priv *)i2c_get_clientdata(client);
if (!fsa_priv)
return -EINVAL;
+ dev = fsa_priv->dev;
+ if (!dev)
+ return -EINVAL;
- fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98);
- return blocking_notifier_chain_unregister
+ mutex_lock(&fsa_priv->notification_lock);
+ /* get latest mode within locked context */
+ rc = power_supply_get_property(fsa_priv->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_MODE, &mode);
+ if (rc) {
+ dev_dbg(dev, "%s: Unable to read USB TYPEC_MODE: %d\n",
+ __func__, rc);
+ goto done;
+ }
+ /* Do not reset switch settings for usb digital hs */
+ if (mode.intval != POWER_SUPPLY_TYPEC_SINK)
+ fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98);
+ rc = blocking_notifier_chain_unregister
(&fsa_priv->fsa4480_notifier, nb);
+done:
+ mutex_unlock(&fsa_priv->notification_lock);
+ return rc;
}
EXPORT_SYMBOL(fsa4480_unreg_notifier);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index a889bb2..b9205f4 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "icnss: " fmt
@@ -1398,7 +1398,8 @@ static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv,
if (atomic_read(&priv->is_shutdown)) {
atomic_set(&priv->is_shutdown, false);
if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
- !test_bit(ICNSS_SHUTDOWN_DONE, &priv->state)) {
+ !test_bit(ICNSS_SHUTDOWN_DONE, &priv->state) &&
+ !test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
icnss_call_driver_remove(priv);
}
}
diff --git a/drivers/soc/qcom/icnss2/Kconfig b/drivers/soc/qcom/icnss2/Kconfig
new file mode 100644
index 0000000..34ff85b
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ICNSS2
+ tristate "Platform driver for Wi-Fi Module module"
+ select CNSS_UTILS
+ help
+ This module adds support for Q6 integrated WLAN connectivity
+ subsystem with iWCN architecture. This module is responsible for
+ communicating WLAN on/off control messages to FW over QMI channel.
+ It is also responsible for handling WLAN PD restart notifications.
+
+config ICNSS2_DEBUG
+ bool "ICNSS2 Platform Driver Debug Support"
+ depends on ICNSS2
+ help
+ Say 'Y' here to enable ICNSS driver debug support. Debug support
+ primarily consists of logs consisting of information related to
+ hardware register access and enabling BUG_ON for certain cases to aid
+ the debugging.
+
+config ICNSS2_QMI
+ bool "ICNSS2 Platform Driver QMI support"
+ select CNSS_QMI_SVC
+ depends on ICNSS2
+ help
+ Say 'Y' here to enable ICNSS QMI support. ICNSS driver will use
+ QMI framework to communicate with WLAN FW. It will send coldboot
+ handshake messages to WLAN FW, which includes hardware capabilities
+ and configurations. It also send WLAN on/off control message to FW
+ over QMI channel.
+
+config CNSS_QCA6750
+ bool "Enable ICNSS QCA6750 chipset specific changes"
+ depends on ICNSS2
+ help
+ This enables the changes from WLAN host driver that are specific to
+ CNSS QCA6750 chipset.
+ These changes are needed to support the new hardware architecture
+ for CNSS QCA6750 chipset.
diff --git a/drivers/soc/qcom/icnss2/Makefile b/drivers/soc/qcom/icnss2/Makefile
new file mode 100644
index 0000000..433483e
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+ccflags-y += -I$(srctree)/drivers/net/wireless/cnss_utils/
+obj-$(CONFIG_ICNSS2) += icnss2.o
+
+icnss2-y := main.o
+icnss2-y += debug.o
+icnss2-y += power.o
+icnss2-$(CONFIG_ICNSS2_QMI) += qmi.o
diff --git a/drivers/soc/qcom/icnss2/debug.c b/drivers/soc/qcom/icnss2/debug.c
new file mode 100644
index 0000000..8731a7b
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/debug.c
@@ -0,0 +1,780 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include "main.h"
+#include "debug.h"
+#include "qmi.h"
+#include "power.h"
+
+void *icnss_ipc_log_context;
+void *icnss_ipc_log_long_context;
+
+static ssize_t icnss_regwrite_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ uint32_t reg_offset, mem_type, reg_val;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state))
+ return -EINVAL;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, ®_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, ®_val))
+ return -EINVAL;
+
+ ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
+ sizeof(uint32_t),
+ (uint8_t *)®_val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int icnss_regwrite_show(struct seq_file *s, void *data)
+{
+ struct icnss_priv *priv = s->private;
+
+ seq_puts(s, "Usage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state))
+ seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+ return 0;
+}
+
+static int icnss_regwrite_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_regwrite_show, inode->i_private);
+}
+
+static const struct file_operations icnss_regwrite_fops = {
+ .read = seq_read,
+ .write = icnss_regwrite_write,
+ .open = icnss_regwrite_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int icnss_regread_show(struct seq_file *s, void *data)
+{
+ struct icnss_priv *priv = s->private;
+
+ mutex_lock(&priv->dev_lock);
+ if (!priv->diag_reg_read_buf) {
+ seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state))
+ seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+ mutex_unlock(&priv->dev_lock);
+ return 0;
+ }
+
+ seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
+ priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
+ priv->diag_reg_read_len);
+
+ seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
+ priv->diag_reg_read_len, false);
+
+ priv->diag_reg_read_len = 0;
+ kfree(priv->diag_reg_read_buf);
+ priv->diag_reg_read_buf = NULL;
+ mutex_unlock(&priv->dev_lock);
+
+ return 0;
+}
+
+static int icnss_regread_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_regread_show, inode->i_private);
+}
+
+static ssize_t icnss_reg_parse(const char __user *user_buf, size_t count,
+ struct icnss_reg_info *reg_info_ptr)
+{
+ char buf[64] = {0};
+ char *sptr = NULL, *token = NULL;
+ const char *delim = " ";
+ unsigned int len = 0;
+
+ if (user_buf == NULL)
+ return -EFAULT;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, ®_info_ptr->mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, ®_info_ptr->reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, ®_info_ptr->data_len))
+ return -EINVAL;
+
+ if (reg_info_ptr->data_len == 0 ||
+ reg_info_ptr->data_len > WLFW_MAX_DATA_SIZE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ uint8_t *reg_buf = NULL;
+ int ret = 0;
+ struct icnss_reg_info reg_info;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state))
+ return -EINVAL;
+
+ ret = icnss_reg_parse(user_buf, count, ®_info);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->dev_lock);
+ kfree(priv->diag_reg_read_buf);
+ priv->diag_reg_read_buf = NULL;
+
+ reg_buf = kzalloc(reg_info.data_len, GFP_KERNEL);
+ if (!reg_buf) {
+ mutex_unlock(&priv->dev_lock);
+ return -ENOMEM;
+ }
+
+ ret = wlfw_athdiag_read_send_sync_msg(priv, reg_info.reg_offset,
+ reg_info.mem_type,
+ reg_info.data_len,
+ reg_buf);
+ if (ret) {
+ kfree(reg_buf);
+ mutex_unlock(&priv->dev_lock);
+ return ret;
+ }
+
+ priv->diag_reg_read_addr = reg_info.reg_offset;
+ priv->diag_reg_read_mem_type = reg_info.mem_type;
+ priv->diag_reg_read_len = reg_info.data_len;
+ priv->diag_reg_read_buf = reg_buf;
+ mutex_unlock(&priv->dev_lock);
+
+ return count;
+}
+
+static const struct file_operations icnss_regread_fops = {
+ .read = seq_read,
+ .write = icnss_regread_write,
+ .open = icnss_regread_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ int ret;
+ u32 val;
+
+ ret = kstrtou32_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ if (ret == 0)
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
+ return count;
+}
+
+static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
+ struct icnss_priv *priv)
+{
+ if (priv->stats.rejuvenate_ind) {
+ seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
+ seq_printf(s, "Number of Rejuvenations: %u\n",
+ priv->stats.rejuvenate_ind);
+ seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
+ priv->cause_for_rejuvenation);
+ seq_printf(s, "Requesting Sub-System: 0x%x\n",
+ priv->requesting_sub_system);
+ seq_printf(s, "Line Number: %u\n",
+ priv->line_number);
+ seq_printf(s, "Function Name: %s\n",
+ priv->function_name);
+ }
+
+ return 0;
+}
+
+static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
+{
+ int i;
+
+ seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
+ seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
+ "Free", "Enable", "Disable");
+ for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
+ seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
+ priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
+ priv->stats.ce_irqs[i].free,
+ priv->stats.ce_irqs[i].enable,
+ priv->stats.ce_irqs[i].disable);
+
+ return 0;
+}
+
+static int icnss_stats_show_capability(struct seq_file *s,
+ struct icnss_priv *priv)
+{
+ if (test_bit(ICNSS_FW_READY, &priv->state)) {
+ seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
+ seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
+ seq_printf(s, "Chip family: 0x%x\n",
+ priv->chip_info.chip_family);
+ seq_printf(s, "Board ID: 0x%x\n", priv->board_id);
+ seq_printf(s, "SOC Info: 0x%x\n", priv->soc_id);
+ seq_printf(s, "Firmware Version: 0x%x\n",
+ priv->fw_version_info.fw_version);
+ seq_printf(s, "Firmware Build Timestamp: %s\n",
+ priv->fw_version_info.fw_build_timestamp);
+ seq_printf(s, "Firmware Build ID: %s\n",
+ priv->fw_build_id);
+ }
+
+ return 0;
+}
+
+static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
+{
+ int i;
+
+ seq_puts(s, "\n<----------------- Events stats ------------------->\n");
+ seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
+ for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
+ seq_printf(s, "%24s %16u %16u\n",
+ icnss_driver_event_to_str(i),
+ priv->stats.events[i].posted,
+ priv->stats.events[i].processed);
+
+ return 0;
+}
+
+static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
+{
+ enum icnss_driver_state i;
+ int skip = 0;
+ unsigned long state;
+
+ seq_printf(s, "\nState: 0x%lx(", priv->state);
+ for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
+
+ if (!(state & 0x1))
+ continue;
+
+ if (skip++)
+ seq_puts(s, " | ");
+
+ switch (i) {
+ case ICNSS_WLFW_CONNECTED:
+ seq_puts(s, "FW CONN");
+ continue;
+ case ICNSS_POWER_ON:
+ seq_puts(s, "POWER ON");
+ continue;
+ case ICNSS_FW_READY:
+ seq_puts(s, "FW READY");
+ continue;
+ case ICNSS_DRIVER_PROBED:
+ seq_puts(s, "DRIVER PROBED");
+ continue;
+ case ICNSS_FW_TEST_MODE:
+ seq_puts(s, "FW TEST MODE");
+ continue;
+ case ICNSS_PM_SUSPEND:
+ seq_puts(s, "PM SUSPEND");
+ continue;
+ case ICNSS_PM_SUSPEND_NOIRQ:
+ seq_puts(s, "PM SUSPEND NOIRQ");
+ continue;
+ case ICNSS_SSR_REGISTERED:
+ seq_puts(s, "SSR REGISTERED");
+ continue;
+ case ICNSS_PDR_REGISTERED:
+ seq_puts(s, "PDR REGISTERED");
+ continue;
+ case ICNSS_PD_RESTART:
+ seq_puts(s, "PD RESTART");
+ continue;
+ case ICNSS_WLFW_EXISTS:
+ seq_puts(s, "WLAN FW EXISTS");
+ continue;
+ case ICNSS_SHUTDOWN_DONE:
+ seq_puts(s, "SHUTDOWN DONE");
+ continue;
+ case ICNSS_HOST_TRIGGERED_PDR:
+ seq_puts(s, "HOST TRIGGERED PDR");
+ continue;
+ case ICNSS_FW_DOWN:
+ seq_puts(s, "FW DOWN");
+ continue;
+ case ICNSS_DRIVER_UNLOADING:
+ seq_puts(s, "DRIVER UNLOADING");
+ continue;
+ case ICNSS_REJUVENATE:
+ seq_puts(s, "FW REJUVENATE");
+ continue;
+ case ICNSS_MODE_ON:
+ seq_puts(s, "MODE ON DONE");
+ continue;
+ case ICNSS_BLOCK_SHUTDOWN:
+ seq_puts(s, "BLOCK SHUTDOWN");
+ continue;
+ case ICNSS_PDR:
+ seq_puts(s, "PDR TRIGGERED");
+ }
+
+ seq_printf(s, "UNKNOWN-%d", i);
+ }
+ seq_puts(s, ")\n");
+
+ return 0;
+}
+
+#define ICNSS_STATS_DUMP(_s, _priv, _x) \
+ seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
+
+static int icnss_stats_show(struct seq_file *s, void *data)
+{
+
+ struct icnss_priv *priv = s->private;
+
+ ICNSS_STATS_DUMP(s, priv, ind_register_req);
+ ICNSS_STATS_DUMP(s, priv, ind_register_resp);
+ ICNSS_STATS_DUMP(s, priv, ind_register_err);
+ ICNSS_STATS_DUMP(s, priv, cap_req);
+ ICNSS_STATS_DUMP(s, priv, cap_resp);
+ ICNSS_STATS_DUMP(s, priv, cap_err);
+ ICNSS_STATS_DUMP(s, priv, pin_connect_result);
+ ICNSS_STATS_DUMP(s, priv, cfg_req);
+ ICNSS_STATS_DUMP(s, priv, cfg_resp);
+ ICNSS_STATS_DUMP(s, priv, cfg_req_err);
+ ICNSS_STATS_DUMP(s, priv, mode_req);
+ ICNSS_STATS_DUMP(s, priv, mode_resp);
+ ICNSS_STATS_DUMP(s, priv, mode_req_err);
+ ICNSS_STATS_DUMP(s, priv, ini_req);
+ ICNSS_STATS_DUMP(s, priv, ini_resp);
+ ICNSS_STATS_DUMP(s, priv, ini_req_err);
+ ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
+ ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
+ ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
+ ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
+
+ seq_puts(s, "\n<------------------ PM stats ------------------->\n");
+ ICNSS_STATS_DUMP(s, priv, pm_suspend);
+ ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
+ ICNSS_STATS_DUMP(s, priv, pm_resume);
+ ICNSS_STATS_DUMP(s, priv, pm_resume_err);
+ ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
+ ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
+ ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
+ ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
+ ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
+ ICNSS_STATS_DUMP(s, priv, pm_relax);
+
+ if (priv->device_id != WCN6750_DEVICE_ID) {
+ seq_puts(s, "\n<------------------ MSA stats ------------------->\n");
+ ICNSS_STATS_DUMP(s, priv, msa_info_req);
+ ICNSS_STATS_DUMP(s, priv, msa_info_resp);
+ ICNSS_STATS_DUMP(s, priv, msa_info_err);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_req);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_err);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
+
+ seq_puts(s, "\n<------------------ Rejuvenate stats ------------------->\n");
+ ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
+ ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
+ ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
+ ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
+ icnss_stats_show_rejuvenate_info(s, priv);
+
+ }
+
+ icnss_stats_show_irqs(s, priv);
+
+ icnss_stats_show_capability(s, priv);
+
+ icnss_stats_show_events(s, priv);
+
+ icnss_stats_show_state(s, priv);
+
+ return 0;
+}
+
+static int icnss_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_stats_show, inode->i_private);
+}
+
+static const struct file_operations icnss_stats_fops = {
+ .read = seq_read,
+ .write = icnss_stats_write,
+ .release = single_release,
+ .open = icnss_stats_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int icnss_fw_debug_show(struct seq_file *s, void *data)
+{
+ struct icnss_priv *priv = s->private;
+
+ seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
+
+ seq_puts(s, "\nCMD: test_mode\n");
+ seq_puts(s, " VAL: 0 (Test mode disable)\n");
+ seq_puts(s, " VAL: 1 (WLAN FW test)\n");
+ seq_puts(s, " VAL: 2 (CCPM test)\n");
+ seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
+ seq_puts(s, " VAL: 4 (allow recursive recovery)\n");
+ seq_puts(s, " VAL: 3 (Disallow recursive recovery)\n");
+
+ seq_puts(s, "\nCMD: dynamic_feature_mask\n");
+ seq_puts(s, " VAL: (64 bit feature mask)\n");
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+ seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
+ goto out;
+ }
+
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+ seq_puts(s, "Machine mode is running, can't run test_mode!\n");
+ goto out;
+ }
+
+ if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+ seq_puts(s, "test_mode is running, can't run test_mode!\n");
+ goto out;
+ }
+
+out:
+ seq_puts(s, "\n");
+ return 0;
+}
+
+static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
+{
+ int ret;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+ icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
+ priv->state);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+ icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+ icnss_pr_err("Test mode not started, state: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
+
+ ret = icnss_hw_power_off(priv);
+
+ clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+out:
+ return ret;
+}
+
+static int icnss_test_mode_fw_test(struct icnss_priv *priv,
+ enum icnss_driver_mode mode)
+{
+ int ret;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+ icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
+ priv->state);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+ icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+ icnss_pr_err("Test mode already started, state: 0x%lx\n",
+ priv->state);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = icnss_hw_power_on(priv);
+ if (ret)
+ goto out;
+
+ set_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+ ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
+ if (ret)
+ goto power_off;
+
+ return 0;
+
+power_off:
+ icnss_hw_power_off(priv);
+ clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+out:
+ return ret;
+}
+
+
+static ssize_t icnss_fw_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ char *cmd;
+ uint64_t val;
+ const char *delim = " ";
+ int ret = 0;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EINVAL;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+ if (!sptr)
+ return -EINVAL;
+ cmd = token;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+ if (kstrtou64(token, 0, &val))
+ return -EINVAL;
+
+ if (strcmp(cmd, "test_mode") == 0) {
+ switch (val) {
+ case 0:
+ ret = icnss_test_mode_fw_test_off(priv);
+ break;
+ case 1:
+ ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
+ break;
+ case 2:
+ ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
+ break;
+ case 3:
+ ret = icnss_trigger_recovery(&priv->pdev->dev);
+ break;
+ case 4:
+ icnss_allow_recursive_recovery(&priv->pdev->dev);
+ break;
+ case 5:
+ icnss_disallow_recursive_recovery(&priv->pdev->dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
+ ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
+ } else {
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int icnss_fw_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_fw_debug_show, inode->i_private);
+}
+
+static const struct file_operations icnss_fw_debug_fops = {
+ .read = seq_read,
+ .write = icnss_fw_debug_write,
+ .release = single_release,
+ .open = icnss_fw_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+#ifdef CONFIG_ICNSS2_DEBUG
+int icnss_debugfs_create(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct dentry *root_dentry;
+
+ root_dentry = debugfs_create_dir("icnss", NULL);
+
+ if (IS_ERR(root_dentry)) {
+ ret = PTR_ERR(root_dentry);
+ icnss_pr_err("Unable to create debugfs %d\n", ret);
+ goto out;
+ }
+
+ priv->root_dentry = root_dentry;
+
+ debugfs_create_file("fw_debug", 0600, root_dentry, priv,
+ &icnss_fw_debug_fops);
+ debugfs_create_file("stats", 0600, root_dentry, priv,
+ &icnss_stats_fops);
+ debugfs_create_file("reg_read", 0600, root_dentry, priv,
+ &icnss_regread_fops);
+ debugfs_create_file("reg_write", 0600, root_dentry, priv,
+ &icnss_regwrite_fops);
+out:
+ return ret;
+}
+#else
+int icnss_debugfs_create(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct dentry *root_dentry;
+
+ root_dentry = debugfs_create_dir("icnss", NULL);
+
+ if (IS_ERR(root_dentry)) {
+ ret = PTR_ERR(root_dentry);
+ icnss_pr_err("Unable to create debugfs %d\n", ret);
+ return ret;
+ }
+
+ priv->root_dentry = root_dentry;
+
+ debugfs_create_file("stats", 0600, root_dentry, priv,
+ &icnss_stats_fops);
+ return 0;
+}
+#endif
+
+void icnss_debugfs_destroy(struct icnss_priv *priv)
+{
+ debugfs_remove_recursive(priv->root_dentry);
+}
+
+void icnss_debug_init(void)
+{
+ icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
+ "icnss", 0);
+ if (!icnss_ipc_log_context)
+ icnss_pr_err("Unable to create log context\n");
+
+ icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
+ "icnss_long", 0);
+ if (!icnss_ipc_log_long_context)
+ icnss_pr_err("Unable to create log long context\n");
+}
+
+void icnss_debug_deinit(void)
+{
+ if (icnss_ipc_log_context) {
+ ipc_log_context_destroy(icnss_ipc_log_context);
+ icnss_ipc_log_context = NULL;
+ }
+
+ if (icnss_ipc_log_long_context) {
+ ipc_log_context_destroy(icnss_ipc_log_long_context);
+ icnss_ipc_log_long_context = NULL;
+ }
+}
diff --git a/drivers/soc/qcom/icnss2/debug.h b/drivers/soc/qcom/icnss2/debug.h
new file mode 100644
index 0000000..ada100e
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/debug.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ICNSS_DEBUG_H
+#define _ICNSS_DEBUG_H
+
+#include <linux/ipc_logging.h>
+#include <linux/printk.h>
+
+#define NUM_LOG_PAGES 10
+#define NUM_LOG_LONG_PAGES 4
+
+extern void *icnss_ipc_log_context;
+extern void *icnss_ipc_log_long_context;
+
+#define icnss_ipc_log_string(_x...) \
+ ipc_log_string(icnss_ipc_log_context, _x)
+
+#define icnss_ipc_log_long_string(_x...) \
+ ipc_log_string(icnss_ipc_log_long_context, _x)
+
+#define icnss_pr_err(_fmt, ...) do { \
+ printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
+ icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define icnss_pr_warn(_fmt, ...) do { \
+ printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
+ icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define icnss_pr_info(_fmt, ...) do { \
+ printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
+ icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define icnss_pr_dbg(_fmt, ...) do { \
+ pr_debug(_fmt, ##__VA_ARGS__); \
+ icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define icnss_pr_vdbg(_fmt, ...) do { \
+ pr_debug(_fmt, ##__VA_ARGS__); \
+ icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
+ } while (0)
+#elif defined(DEBUG)
+#define icnss_pr_dbg(_fmt, ...) do { \
+ printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
+ icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define icnss_pr_vdbg(_fmt, ...) do { \
+ printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
+ icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+#else
+#define icnss_pr_dbg(_fmt, ...) do { \
+ no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
+ icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define icnss_pr_vdbg(_fmt, ...) do { \
+ no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
+ icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
+ ##__VA_ARGS__); \
+ } while (0)
+#endif
+
+#ifdef CONFIG_ICNSS2_DEBUG
+#define ICNSS_ASSERT(_condition) do { \
+ if (!(_condition)) { \
+ icnss_pr_err("ASSERT at line %d\n", __LINE__); \
+ BUG(); \
+ } \
+ } while (0)
+#else
+#define ICNSS_ASSERT(_condition) do { } while (0)
+#endif
+
+#define icnss_fatal_err(_fmt, ...) \
+ icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__)
+
+enum icnss_debug_quirks {
+ HW_ALWAYS_ON,
+ HW_DEBUG_ENABLE,
+ SKIP_QMI,
+ RECOVERY_DISABLE,
+ SSR_ONLY,
+ PDR_ONLY,
+ ENABLE_DAEMON_SUPPORT,
+ FW_REJUVENATE_ENABLE,
+};
+
+void icnss_debug_init(void);
+void icnss_debug_deinit(void);
+int icnss_debugfs_create(struct icnss_priv *priv);
+void icnss_debugfs_destroy(struct icnss_priv *priv);
+#endif /* _ICNSS_DEBUG_H */
diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c
new file mode 100644
index 0000000..96b8301
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/main.c
@@ -0,0 +1,2677 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "icnss2: " fmt
+
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/iommu.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/thread_info.h>
+#include <linux/uaccess.h>
+#include <linux/adc-tm-clients.h>
+#include <linux/iio/consumer.h>
+#include <linux/etherdevice.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/sysfs.h>
+#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/secure_buffer.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/socinfo.h>
+#include <soc/qcom/ramdump.h>
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+#include "power.h"
+
+#define MAX_PROP_SIZE 32
+#define NUM_LOG_PAGES 10
+#define NUM_LOG_LONG_PAGES 4
+#define ICNSS_MAGIC 0x5abc5abc
+
+#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
+#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
+#define ICNSS_DEFAULT_FEATURE_MASK 0x01
+
+#define ICNSS_QUIRKS_DEFAULT BIT(FW_REJUVENATE_ENABLE)
+#define ICNSS_MAX_PROBE_CNT 2
+
+#define ICNSS_BDF_TYPE_DEFAULT ICNSS_BDF_ELF
+
+#define PROBE_TIMEOUT 15000
+#define WLFW_TIMEOUT msecs_to_jiffies(3000)
+
+static struct icnss_priv *penv;
+
+uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK;
+
+#define ICNSS_EVENT_PENDING 2989
+
+#define ICNSS_EVENT_SYNC BIT(0)
+#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
+#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
+ ICNSS_EVENT_SYNC)
+
+
+enum icnss_pdr_cause_index {
+ ICNSS_FW_CRASH,
+ ICNSS_ROOT_PD_CRASH,
+ ICNSS_ROOT_PD_SHUTDOWN,
+ ICNSS_HOST_ERROR,
+};
+
+static const char * const icnss_pdr_cause[] = {
+ [ICNSS_FW_CRASH] = "FW crash",
+ [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
+ [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
+ [ICNSS_HOST_ERROR] = "Host error",
+};
+
+static void icnss_set_plat_priv(struct icnss_priv *priv)
+{
+ penv = priv;
+}
+
+static struct icnss_priv *icnss_get_plat_priv()
+{
+ return penv;
+}
+
+static ssize_t icnss_sysfs_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct icnss_priv *priv = icnss_get_plat_priv();
+
+ atomic_set(&priv->is_shutdown, true);
+ icnss_pr_dbg("Received shutdown indication");
+ return count;
+}
+
+static struct kobj_attribute icnss_sysfs_attribute =
+__ATTR(shutdown, 0660, NULL, icnss_sysfs_store);
+
+static void icnss_pm_stay_awake(struct icnss_priv *priv)
+{
+ if (atomic_inc_return(&priv->pm_count) != 1)
+ return;
+
+ icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
+ atomic_read(&priv->pm_count));
+
+ pm_stay_awake(&priv->pdev->dev);
+
+ priv->stats.pm_stay_awake++;
+}
+
+static void icnss_pm_relax(struct icnss_priv *priv)
+{
+ int r = atomic_dec_return(&priv->pm_count);
+
+ WARN_ON(r < 0);
+
+ if (r != 0)
+ return;
+
+ icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
+ atomic_read(&priv->pm_count));
+
+ pm_relax(&priv->pdev->dev);
+ priv->stats.pm_relax++;
+}
+
+char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
+{
+ switch (type) {
+ case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
+ return "SERVER_ARRIVE";
+ case ICNSS_DRIVER_EVENT_SERVER_EXIT:
+ return "SERVER_EXIT";
+ case ICNSS_DRIVER_EVENT_FW_READY_IND:
+ return "FW_READY";
+ case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
+ return "REGISTER_DRIVER";
+ case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
+ return "UNREGISTER_DRIVER";
+ case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
+ return "PD_SERVICE_DOWN";
+ case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+ return "FW_EARLY_CRASH_IND";
+ case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+ return "IDLE_SHUTDOWN";
+ case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+ return "IDLE_RESTART";
+ case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
+ return "FW_INIT_DONE";
+ case ICNSS_DRIVER_EVENT_MAX:
+ return "EVENT_MAX";
+ }
+
+ return "UNKNOWN";
+};
+
+int icnss_driver_event_post(struct icnss_priv *priv,
+ enum icnss_driver_event_type type,
+ u32 flags, void *data)
+{
+ struct icnss_driver_event *event;
+ unsigned long irq_flags;
+ int gfp = GFP_KERNEL;
+ int ret = 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
+ icnss_driver_event_to_str(type), type, current->comm,
+ flags, priv->state);
+
+ if (type >= ICNSS_DRIVER_EVENT_MAX) {
+ icnss_pr_err("Invalid Event type: %d, can't post", type);
+ return -EINVAL;
+ }
+
+ if (in_interrupt() || irqs_disabled())
+ gfp = GFP_ATOMIC;
+
+ event = kzalloc(sizeof(*event), gfp);
+ if (event == NULL)
+ return -ENOMEM;
+
+ icnss_pm_stay_awake(priv);
+
+ event->type = type;
+ event->data = data;
+ init_completion(&event->complete);
+ event->ret = ICNSS_EVENT_PENDING;
+ event->sync = !!(flags & ICNSS_EVENT_SYNC);
+
+ spin_lock_irqsave(&priv->event_lock, irq_flags);
+ list_add_tail(&event->list, &priv->event_list);
+ spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+
+ priv->stats.events[type].posted++;
+ queue_work(priv->event_wq, &priv->event_work);
+
+ if (!(flags & ICNSS_EVENT_SYNC))
+ goto out;
+
+ if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
+ wait_for_completion(&event->complete);
+ else
+ ret = wait_for_completion_interruptible(&event->complete);
+
+ icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
+ icnss_driver_event_to_str(type), type, priv->state, ret,
+ event->ret);
+
+ spin_lock_irqsave(&priv->event_lock, irq_flags);
+ if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
+ event->sync = false;
+ spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+ ret = -EINTR;
+ goto out;
+ }
+ spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+
+ ret = event->ret;
+ kfree(event);
+
+out:
+ icnss_pm_relax(priv);
+ return ret;
+}
+
+bool icnss_is_fw_ready(void)
+{
+ if (!penv)
+ return false;
+ else
+ return test_bit(ICNSS_FW_READY, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_fw_ready);
+
+void icnss_block_shutdown(bool status)
+{
+ if (!penv)
+ return;
+
+ if (status) {
+ set_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state);
+ reinit_completion(&penv->unblock_shutdown);
+ } else {
+ clear_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state);
+ complete(&penv->unblock_shutdown);
+ }
+}
+EXPORT_SYMBOL(icnss_block_shutdown);
+
+bool icnss_is_fw_down(void)
+{
+
+ struct icnss_priv *priv = icnss_get_plat_priv();
+
+ if (!priv)
+ return false;
+
+ return test_bit(ICNSS_FW_DOWN, &priv->state) ||
+ test_bit(ICNSS_PD_RESTART, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &priv->state);
+}
+EXPORT_SYMBOL(icnss_is_fw_down);
+
+bool icnss_is_rejuvenate(void)
+{
+ if (!penv)
+ return false;
+ else
+ return test_bit(ICNSS_REJUVENATE, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_rejuvenate);
+
+bool icnss_is_pdr(void)
+{
+ if (!penv)
+ return false;
+ else
+ return test_bit(ICNSS_PDR, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_pdr);
+
+static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
+{
+ struct icnss_priv *priv = ctx;
+
+ if (priv)
+ priv->force_err_fatal = true;
+
+ icnss_pr_err("Received force error fatal request from FW\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
+{
+ struct icnss_priv *priv = ctx;
+ struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+ icnss_pr_err("Received early crash indication from FW\n");
+
+ if (priv) {
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_fw_timeout(true);
+
+ if (test_bit(ICNSS_FW_READY, &priv->state)) {
+ fw_down_data.crashed = true;
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
+ }
+ }
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+ 0, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static void register_fw_error_notifications(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct device_node *dev_node;
+ int irq = 0, ret = 0;
+
+ if (!priv)
+ return;
+
+ dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in");
+ if (!dev_node) {
+ icnss_pr_err("Failed to get smp2p node for force-fatal-error\n");
+ return;
+ }
+
+ icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name);
+
+ if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) {
+ ret = irq = of_irq_get_byname(dev_node,
+ "qcom,smp2p-force-fatal-error");
+ if (ret < 0) {
+ icnss_pr_err("Unable to get force-fatal-error irq %d\n",
+ irq);
+ return;
+ }
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, fw_error_fatal_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "wlanfw-err", priv);
+ if (ret < 0) {
+ icnss_pr_err("Unable to register for error fatal IRQ handler %d ret = %d",
+ irq, ret);
+ return;
+ }
+ icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq);
+ priv->fw_error_fatal_irq = irq;
+}
+
+static void register_early_crash_notifications(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct device_node *dev_node;
+ int irq = 0, ret = 0;
+
+ if (!priv)
+ return;
+
+ dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in");
+ if (!dev_node) {
+ icnss_pr_err("Failed to get smp2p node for early-crash-ind\n");
+ return;
+ }
+
+ icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name);
+
+ if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) {
+ ret = irq = of_irq_get_byname(dev_node,
+ "qcom,smp2p-early-crash-ind");
+ if (ret < 0) {
+ icnss_pr_err("Unable to get early-crash-ind irq %d\n",
+ irq);
+ return;
+ }
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ fw_crash_indication_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "wlanfw-early-crash-ind", priv);
+ if (ret < 0) {
+ icnss_pr_err("Unable to register for early crash indication IRQ handler %d ret = %d",
+ irq, ret);
+ return;
+ }
+ icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq);
+ priv->fw_early_crash_irq = irq;
+}
+
+int icnss_call_driver_uevent(struct icnss_priv *priv,
+ enum icnss_uevent uevent, void *data)
+{
+ struct icnss_uevent_data uevent_data;
+
+ if (!priv->ops || !priv->ops->uevent)
+ return 0;
+
+ icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
+ priv->state, uevent);
+
+ uevent_data.uevent = uevent;
+ uevent_data.data = data;
+
+ return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
+}
+
+static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+ bool ignore_assert = false;
+
+ if (!priv)
+ return -ENODEV;
+
+ set_bit(ICNSS_WLFW_EXISTS, &priv->state);
+ clear_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_fw_timeout(false);
+
+ if (test_bit(ICNSS_WLFW_CONNECTED, &penv->state)) {
+ icnss_pr_err("QMI Server already in Connected State\n");
+ ICNSS_ASSERT(0);
+ }
+
+ ret = icnss_connect_to_fw_server(priv, data);
+ if (ret)
+ goto fail;
+
+ set_bit(ICNSS_WLFW_CONNECTED, &priv->state);
+
+ ret = icnss_hw_power_on(priv);
+ if (ret)
+ goto clear_server;
+
+ ret = wlfw_ind_register_send_sync_msg(priv);
+ if (ret < 0) {
+ if (ret == -EALREADY) {
+ ret = 0;
+ goto qmi_registered;
+ }
+ ignore_assert = true;
+ goto err_power_on;
+ }
+
+ if (priv->device_id == WCN6750_DEVICE_ID) {
+ ret = wlfw_host_cap_send_sync(priv);
+ if (ret < 0)
+ goto err_power_on;
+ }
+
+ if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ if (!priv->msa_va) {
+ icnss_pr_err("Invalid MSA address\n");
+ ret = -EINVAL;
+ goto err_power_on;
+ }
+
+ ret = wlfw_msa_mem_info_send_sync_msg(priv);
+ if (ret < 0) {
+ ignore_assert = true;
+ goto err_power_on;
+ }
+
+ ret = wlfw_msa_ready_send_sync_msg(priv);
+ if (ret < 0) {
+ ignore_assert = true;
+ goto err_power_on;
+ }
+ }
+
+ ret = wlfw_cap_send_sync_msg(priv);
+ if (ret < 0) {
+ ignore_assert = true;
+ goto err_power_on;
+ }
+
+ if (priv->device_id == WCN6750_DEVICE_ID) {
+ ret = wlfw_device_info_send_msg(priv);
+ if (ret < 0) {
+ ignore_assert = true;
+ goto err_power_on;
+ }
+
+ icnss_wlfw_bdf_dnld_send_sync(priv, ICNSS_BDF_REGDB);
+
+ ret = icnss_wlfw_bdf_dnld_send_sync(priv,
+ priv->ctrl_params.bdf_type);
+
+ }
+
+ if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ wlfw_dynamic_feature_mask_send_sync_msg(priv,
+ dynamic_feature_mask);
+ }
+
+ if (!priv->fw_error_fatal_irq)
+ register_fw_error_notifications(&priv->pdev->dev);
+
+ if (!priv->fw_early_crash_irq)
+ register_early_crash_notifications(&priv->pdev->dev);
+
+ if (priv->vbatt_supported)
+ icnss_init_vph_monitor(priv);
+
+ return ret;
+
+err_power_on:
+ icnss_hw_power_off(priv);
+clear_server:
+ icnss_clear_server(priv);
+fail:
+ ICNSS_ASSERT(ignore_assert);
+qmi_registered:
+ return ret;
+}
+
+static int icnss_driver_event_server_exit(struct icnss_priv *priv)
+{
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_info("WLAN FW Service Disconnected: 0x%lx\n", priv->state);
+
+ icnss_clear_server(priv);
+
+ if (priv->adc_tm_dev && priv->vbatt_supported)
+ adc_tm5_disable_chan_meas(priv->adc_tm_dev,
+ &priv->vph_monitor_params);
+
+ return 0;
+}
+
+static int icnss_call_driver_probe(struct icnss_priv *priv)
+{
+ int ret = 0;
+ int probe_cnt = 0;
+
+ if (!priv->ops || !priv->ops->probe)
+ return 0;
+
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ return -EINVAL;
+
+ icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
+
+ icnss_hw_power_on(priv);
+
+ icnss_block_shutdown(true);
+ while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+ ret = priv->ops->probe(&priv->pdev->dev);
+ probe_cnt++;
+ if (ret != -EPROBE_DEFER)
+ break;
+ }
+ if (ret < 0) {
+ icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+ ret, priv->state, probe_cnt);
+ icnss_block_shutdown(false);
+ goto out;
+ }
+
+ icnss_block_shutdown(false);
+ set_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+ return 0;
+
+out:
+ icnss_hw_power_off(priv);
+ return ret;
+}
+
+static int icnss_call_driver_shutdown(struct icnss_priv *priv)
+{
+ if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ if (!priv->ops || !priv->ops->shutdown)
+ goto out;
+
+ if (test_bit(ICNSS_SHUTDOWN_DONE, &priv->state))
+ goto out;
+
+ icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
+
+ priv->ops->shutdown(&priv->pdev->dev);
+ set_bit(ICNSS_SHUTDOWN_DONE, &priv->state);
+
+out:
+ return 0;
+}
+
+static int icnss_pd_restart_complete(struct icnss_priv *priv)
+{
+ int ret;
+
+ icnss_pm_relax(priv);
+
+ icnss_call_driver_shutdown(priv);
+
+ clear_bit(ICNSS_PDR, &priv->state);
+ clear_bit(ICNSS_REJUVENATE, &priv->state);
+ clear_bit(ICNSS_PD_RESTART, &priv->state);
+ priv->early_crash_ind = false;
+ priv->is_ssr = false;
+
+ if (!priv->ops || !priv->ops->reinit)
+ goto out;
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+ icnss_pr_err("FW is in bad state, state: 0x%lx\n",
+ priv->state);
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto call_probe;
+
+ icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
+
+ icnss_hw_power_on(priv);
+
+ icnss_block_shutdown(true);
+
+ ret = priv->ops->reinit(&priv->pdev->dev);
+ if (ret < 0) {
+ icnss_fatal_err("Driver reinit failed: %d, state: 0x%lx\n",
+ ret, priv->state);
+ if (!priv->allow_recursive_recovery)
+ ICNSS_ASSERT(false);
+ icnss_block_shutdown(false);
+ goto out_power_off;
+ }
+
+out:
+ icnss_block_shutdown(false);
+ clear_bit(ICNSS_SHUTDOWN_DONE, &priv->state);
+ return 0;
+
+call_probe:
+ return icnss_call_driver_probe(priv);
+
+out_power_off:
+ icnss_hw_power_off(priv);
+
+ return ret;
+}
+
+
+static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data)
+{
+ int ret = 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ set_bit(ICNSS_FW_READY, &priv->state);
+ clear_bit(ICNSS_MODE_ON, &priv->state);
+
+ icnss_pr_info("WLAN FW is ready: 0x%lx\n", priv->state);
+
+ icnss_hw_power_off(priv);
+
+ if (!priv->pdev) {
+ icnss_pr_err("Device is not ready\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (test_bit(ICNSS_PD_RESTART, &priv->state))
+ ret = icnss_pd_restart_complete(priv);
+ else
+ ret = icnss_call_driver_probe(priv);
+
+out:
+ return ret;
+}
+
+static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data)
+{
+ int ret = 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_info("WLAN FW Initialization done: 0x%lx\n", priv->state);
+
+ ret = wlfw_wlan_mode_send_sync_msg(priv,
+ (enum wlfw_driver_mode_enum_v01)ICNSS_CALIBRATION);
+
+ return ret;
+}
+
+static int icnss_driver_event_register_driver(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+ int probe_cnt = 0;
+
+ if (priv->ops)
+ return -EEXIST;
+
+ priv->ops = data;
+
+ if (test_bit(SKIP_QMI, &priv->ctrl_params.quirks))
+ set_bit(ICNSS_FW_READY, &priv->state);
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+ icnss_pr_err("FW is in bad state, state: 0x%lx\n",
+ priv->state);
+ return -ENODEV;
+ }
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+ icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
+ priv->state);
+ goto out;
+ }
+
+ ret = icnss_hw_power_on(priv);
+ if (ret)
+ goto out;
+
+ icnss_block_shutdown(true);
+ while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+ ret = priv->ops->probe(&priv->pdev->dev);
+ probe_cnt++;
+ if (ret != -EPROBE_DEFER)
+ break;
+ }
+ if (ret) {
+ icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+ ret, priv->state, probe_cnt);
+ icnss_block_shutdown(false);
+ goto power_off;
+ }
+
+ icnss_block_shutdown(false);
+ set_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+ return 0;
+
+power_off:
+ icnss_hw_power_off(priv);
+out:
+ return ret;
+}
+
+static int icnss_driver_event_unregister_driver(struct icnss_priv *priv,
+ void *data)
+{
+ if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+ priv->ops = NULL;
+ goto out;
+ }
+
+ set_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+
+ icnss_block_shutdown(true);
+
+ if (priv->ops)
+ priv->ops->remove(&priv->pdev->dev);
+
+ icnss_block_shutdown(false);
+
+ clear_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+ clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+ priv->ops = NULL;
+
+ icnss_hw_power_off(priv);
+
+out:
+ return 0;
+}
+
+static int icnss_call_driver_remove(struct icnss_priv *priv)
+{
+ icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
+
+ clear_bit(ICNSS_FW_READY, &priv->state);
+
+ if (test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
+ return 0;
+
+ if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ return 0;
+
+ if (!priv->ops || !priv->ops->remove)
+ return 0;
+
+ set_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+ priv->ops->remove(&priv->pdev->dev);
+
+ clear_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+ clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+ icnss_hw_power_off(priv);
+
+ return 0;
+}
+
+static int icnss_fw_crashed(struct icnss_priv *priv,
+ struct icnss_event_pd_service_down_data *event_data)
+{
+ icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
+
+ set_bit(ICNSS_PD_RESTART, &priv->state);
+ clear_bit(ICNSS_FW_READY, &priv->state);
+
+ icnss_pm_stay_awake(priv);
+
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
+
+ if (event_data && event_data->fw_rejuvenate)
+ wlfw_rejuvenate_ack_send_sync_msg(priv);
+
+ return 0;
+}
+
+static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
+ void *data)
+{
+ struct icnss_event_pd_service_down_data *event_data = data;
+
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+ icnss_ignore_fw_timeout(false);
+ goto out;
+ }
+
+ if (priv->force_err_fatal)
+ ICNSS_ASSERT(0);
+
+ if (priv->early_crash_ind) {
+ icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
+ event_data->crashed, priv->state);
+ goto out;
+ }
+
+ if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
+ icnss_fatal_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
+ event_data->crashed, priv->state);
+ if (!priv->allow_recursive_recovery)
+ ICNSS_ASSERT(0);
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_PD_RESTART, &priv->state))
+ icnss_fw_crashed(priv, event_data);
+
+out:
+ kfree(data);
+
+ return 0;
+}
+
+static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
+ void *data)
+{
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+ icnss_ignore_fw_timeout(false);
+ goto out;
+ }
+
+ priv->early_crash_ind = true;
+ icnss_fw_crashed(priv, NULL);
+
+out:
+ kfree(data);
+
+ return 0;
+}
+
+static int icnss_driver_event_idle_shutdown(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+
+ if (!priv->ops || !priv->ops->idle_shutdown)
+ return 0;
+
+ if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &priv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle shutdown callback\n");
+ ret = -EBUSY;
+ } else {
+ icnss_pr_dbg("Calling driver idle shutdown, state: 0x%lx\n",
+ priv->state);
+ icnss_block_shutdown(true);
+ ret = priv->ops->idle_shutdown(&priv->pdev->dev);
+ icnss_block_shutdown(false);
+ }
+
+ return ret;
+}
+
+static int icnss_driver_event_idle_restart(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+
+ if (!priv->ops || !priv->ops->idle_restart)
+ return 0;
+
+ if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &priv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle restart callback\n");
+ ret = -EBUSY;
+ } else {
+ icnss_pr_dbg("Calling driver idle restart, state: 0x%lx\n",
+ priv->state);
+ icnss_block_shutdown(true);
+ ret = priv->ops->idle_restart(&priv->pdev->dev);
+ icnss_block_shutdown(false);
+ }
+
+ return ret;
+}
+
+static void icnss_driver_event_work(struct work_struct *work)
+{
+ struct icnss_priv *priv =
+ container_of(work, struct icnss_priv, event_work);
+ struct icnss_driver_event *event;
+ unsigned long flags;
+ int ret;
+
+ icnss_pm_stay_awake(priv);
+
+ spin_lock_irqsave(&priv->event_lock, flags);
+
+ while (!list_empty(&priv->event_list)) {
+ event = list_first_entry(&priv->event_list,
+ struct icnss_driver_event, list);
+ list_del(&event->list);
+ spin_unlock_irqrestore(&priv->event_lock, flags);
+
+ icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
+ icnss_driver_event_to_str(event->type),
+ event->sync ? "-sync" : "", event->type,
+ priv->state);
+
+ switch (event->type) {
+ case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
+ ret = icnss_driver_event_server_arrive(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_SERVER_EXIT:
+ ret = icnss_driver_event_server_exit(priv);
+ break;
+ case ICNSS_DRIVER_EVENT_FW_READY_IND:
+ ret = icnss_driver_event_fw_ready_ind(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
+ ret = icnss_driver_event_register_driver(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
+ ret = icnss_driver_event_unregister_driver(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
+ ret = icnss_driver_event_pd_service_down(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+ ret = icnss_driver_event_early_crash_ind(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+ ret = icnss_driver_event_idle_shutdown(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+ ret = icnss_driver_event_idle_restart(priv,
+ event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
+ ret = icnss_driver_event_fw_init_done(priv,
+ event->data);
+ break;
+ default:
+ icnss_pr_err("Invalid Event type: %d", event->type);
+ kfree(event);
+ continue;
+ }
+
+ priv->stats.events[event->type].processed++;
+
+ icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
+ icnss_driver_event_to_str(event->type),
+ event->sync ? "-sync" : "", event->type, ret,
+ priv->state);
+
+ spin_lock_irqsave(&priv->event_lock, flags);
+ if (event->sync) {
+ event->ret = ret;
+ complete(&event->complete);
+ continue;
+ }
+ spin_unlock_irqrestore(&priv->event_lock, flags);
+
+ kfree(event);
+
+ spin_lock_irqsave(&priv->event_lock, flags);
+ }
+ spin_unlock_irqrestore(&priv->event_lock, flags);
+
+ icnss_pm_relax(priv);
+}
+
+static int icnss_msa0_ramdump(struct icnss_priv *priv)
+{
+ struct ramdump_segment segment;
+
+ memset(&segment, 0, sizeof(segment));
+ segment.v_address = priv->msa_va;
+ segment.size = priv->msa_mem_size;
+ return do_ramdump(priv->msa0_dump_dev, &segment, 1);
+}
+
+static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv,
+ void *data)
+{
+ struct notif_data *notif = data;
+ int ret = 0;
+
+ if (!notif->crashed) {
+ if (atomic_read(&priv->is_shutdown)) {
+ atomic_set(&priv->is_shutdown, false);
+ if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
+ !test_bit(ICNSS_SHUTDOWN_DONE, &priv->state)) {
+ icnss_call_driver_remove(priv);
+ }
+ }
+
+ if (test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
+ if (!wait_for_completion_timeout(
+ &priv->unblock_shutdown,
+ msecs_to_jiffies(PROBE_TIMEOUT)))
+ icnss_pr_err("modem block shutdown timeout\n");
+ }
+
+ ret = wlfw_send_modem_shutdown_msg(priv);
+ if (ret < 0)
+ icnss_pr_err("Fail to send modem shutdown Indication %d\n",
+ ret);
+ }
+}
+
+static int icnss_modem_notifier_nb(struct notifier_block *nb,
+ unsigned long code,
+ void *data)
+{
+ struct icnss_event_pd_service_down_data *event_data;
+ struct notif_data *notif = data;
+ struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+ modem_ssr_nb);
+ struct icnss_uevent_fw_down_data fw_down_data;
+
+ icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
+
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ icnss_pr_info("Collecting msa0 segment dump\n");
+ icnss_msa0_ramdump(priv);
+ return NOTIFY_OK;
+ }
+
+ if (code != SUBSYS_BEFORE_SHUTDOWN)
+ return NOTIFY_OK;
+
+ priv->is_ssr = true;
+
+ icnss_update_state_send_modem_shutdown(priv, data);
+
+ if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_fw_timeout(true);
+
+ fw_down_data.crashed = !!notif->crashed;
+ if (test_bit(ICNSS_FW_READY, &priv->state))
+ icnss_call_driver_uevent(priv,
+ ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
+ return NOTIFY_OK;
+ }
+
+ icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
+ priv->state, notif->crashed);
+
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+
+ if (notif->crashed)
+ priv->stats.recovery.root_pd_crash++;
+ else
+ priv->stats.recovery.root_pd_shutdown++;
+
+ icnss_ignore_fw_timeout(true);
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+
+ if (event_data == NULL)
+ return notifier_from_errno(-ENOMEM);
+
+ event_data->crashed = notif->crashed;
+
+ fw_down_data.crashed = !!notif->crashed;
+ if (test_bit(ICNSS_FW_READY, &priv->state))
+ icnss_call_driver_uevent(priv,
+ ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ ICNSS_EVENT_SYNC, event_data);
+
+ return NOTIFY_OK;
+}
+
+static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
+
+ priv->modem_notify_handler =
+ subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
+
+ if (IS_ERR(priv->modem_notify_handler)) {
+ ret = PTR_ERR(priv->modem_notify_handler);
+ icnss_pr_err("Modem register notifier failed: %d\n", ret);
+ }
+
+ set_bit(ICNSS_SSR_REGISTERED, &priv->state);
+
+ return ret;
+}
+
+static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
+{
+ if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
+ return 0;
+
+ subsys_notif_unregister_notifier(priv->modem_notify_handler,
+ &priv->modem_ssr_nb);
+ priv->modem_notify_handler = NULL;
+
+ return 0;
+}
+
+static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
+{
+ int i;
+
+ if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
+ return 0;
+
+ for (i = 0; i < priv->total_domains; i++)
+ service_notif_unregister_notifier(
+ priv->service_notifier[i].handle,
+ &priv->service_notifier_nb);
+
+ kfree(priv->service_notifier);
+
+ priv->service_notifier = NULL;
+
+ return 0;
+}
+
+static int icnss_service_notifier_notify(struct notifier_block *nb,
+ unsigned long notification, void *data)
+{
+ struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+ service_notifier_nb);
+ enum pd_subsys_state *state = data;
+ struct icnss_event_pd_service_down_data *event_data;
+ struct icnss_uevent_fw_down_data fw_down_data;
+ enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
+
+ icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
+ notification, priv->state);
+
+ if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
+ goto done;
+
+ if (!priv->is_ssr)
+ set_bit(ICNSS_PDR, &priv->state);
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+
+ if (event_data == NULL)
+ return notifier_from_errno(-ENOMEM);
+
+ event_data->crashed = true;
+
+ if (state == NULL) {
+ priv->stats.recovery.root_pd_crash++;
+ goto event_post;
+ }
+
+ switch (*state) {
+ case ROOT_PD_WDOG_BITE:
+ priv->stats.recovery.root_pd_crash++;
+ break;
+ case ROOT_PD_SHUTDOWN:
+ cause = ICNSS_ROOT_PD_SHUTDOWN;
+ priv->stats.recovery.root_pd_shutdown++;
+ event_data->crashed = false;
+ break;
+ case USER_PD_STATE_CHANGE:
+ if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
+ cause = ICNSS_HOST_ERROR;
+ priv->stats.recovery.pdr_host_error++;
+ } else {
+ cause = ICNSS_FW_CRASH;
+ priv->stats.recovery.pdr_fw_crash++;
+ }
+ break;
+ default:
+ priv->stats.recovery.root_pd_crash++;
+ break;
+ }
+ icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
+ *state, priv->state, icnss_pdr_cause[cause]);
+event_post:
+ if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_fw_timeout(true);
+
+ fw_down_data.crashed = event_data->crashed;
+ if (test_bit(ICNSS_FW_READY, &priv->state))
+ icnss_call_driver_uevent(priv,
+ ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
+ }
+
+ clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ ICNSS_EVENT_SYNC, event_data);
+done:
+ if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
+ clear_bit(ICNSS_FW_DOWN, &priv->state);
+ return NOTIFY_OK;
+}
+
+static int icnss_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode, void *data)
+{
+ struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+ get_service_nb);
+ struct pd_qmi_client_data *pd = data;
+ int curr_state;
+ int ret;
+ int i;
+ int j;
+ bool duplicate;
+ struct service_notifier_context *notifier;
+
+ icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
+ priv->state);
+
+ if (opcode != LOCATOR_UP)
+ return NOTIFY_DONE;
+
+ if (pd->total_domains == 0) {
+ icnss_pr_err("Did not find any domains\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ notifier = kcalloc(pd->total_domains,
+ sizeof(struct service_notifier_context),
+ GFP_KERNEL);
+ if (!notifier) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
+
+ for (i = 0; i < pd->total_domains; i++) {
+ duplicate = false;
+ for (j = i + 1; j < pd->total_domains; j++) {
+ if (!strcmp(pd->domain_list[i].name,
+ pd->domain_list[j].name))
+ duplicate = true;
+ }
+
+ if (duplicate)
+ continue;
+
+ icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id);
+
+ notifier[i].handle =
+ service_notif_register_notifier(pd->domain_list[i].name,
+ pd->domain_list[i].instance_id,
+ &priv->service_notifier_nb, &curr_state);
+ notifier[i].instance_id = pd->domain_list[i].instance_id;
+ strlcpy(notifier[i].name, pd->domain_list[i].name,
+ QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
+
+ if (IS_ERR(notifier[i].handle)) {
+ icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
+ i, pd->domain_list->name,
+ pd->domain_list->instance_id);
+ ret = PTR_ERR(notifier[i].handle);
+ goto free_handle;
+ }
+ }
+
+ priv->service_notifier = notifier;
+ priv->total_domains = pd->total_domains;
+
+ set_bit(ICNSS_PDR_REGISTERED, &priv->state);
+
+ icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
+ priv->state);
+
+ return NOTIFY_OK;
+
+free_handle:
+ for (i = 0; i < pd->total_domains; i++) {
+ if (notifier[i].handle)
+ service_notif_unregister_notifier(notifier[i].handle,
+ &priv->service_notifier_nb);
+ }
+ kfree(notifier);
+
+out:
+ icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
+ priv->state);
+
+ return NOTIFY_OK;
+}
+
+
+static int icnss_pd_restart_enable(struct icnss_priv *priv)
+{
+ int ret;
+
+ if (test_bit(SSR_ONLY, &priv->ctrl_params.quirks)) {
+ icnss_pr_dbg("PDR disabled through module parameter\n");
+ return 0;
+ }
+
+ icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
+
+ priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
+ ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
+ ICNSS_WLAN_SERVICE_NAME,
+ &priv->get_service_nb);
+ if (ret) {
+ icnss_pr_err("Get service location failed: %d\n", ret);
+ goto out;
+ }
+
+ return 0;
+out:
+ icnss_pr_err("Failed to enable PD restart: %d\n", ret);
+ return ret;
+
+}
+
+
+static int icnss_enable_recovery(struct icnss_priv *priv)
+{
+ int ret;
+
+ if (test_bit(RECOVERY_DISABLE, &priv->ctrl_params.quirks)) {
+ icnss_pr_dbg("Recovery disabled through module parameter\n");
+ return 0;
+ }
+
+ if (test_bit(PDR_ONLY, &priv->ctrl_params.quirks)) {
+ icnss_pr_dbg("SSR disabled through module parameter\n");
+ goto enable_pdr;
+ }
+
+ priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
+ &priv->pdev->dev);
+ if (!priv->msa0_dump_dev)
+ return -ENOMEM;
+
+ icnss_modem_ssr_register_notifier(priv);
+ if (test_bit(SSR_ONLY, &priv->ctrl_params.quirks)) {
+ icnss_pr_dbg("PDR disabled through module parameter\n");
+ return 0;
+ }
+
+enable_pdr:
+ ret = icnss_pd_restart_enable(priv);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int __icnss_register_driver(struct icnss_driver_ops *ops,
+ struct module *owner, const char *mod_name)
+{
+ int ret = 0;
+ struct icnss_priv *priv = icnss_get_plat_priv();
+
+ if (!priv || !priv->pdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_dbg("Registering driver, state: 0x%lx\n", priv->state);
+
+ if (priv->ops) {
+ icnss_pr_err("Driver already registered\n");
+ ret = -EEXIST;
+ goto out;
+ }
+
+ if (!ops->probe || !ops->remove) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
+ 0, ops);
+
+ if (ret == -EINTR)
+ ret = 0;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(__icnss_register_driver);
+
+int icnss_unregister_driver(struct icnss_driver_ops *ops)
+{
+ int ret;
+ struct icnss_priv *priv = icnss_get_plat_priv();
+
+ if (!priv || !priv->pdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops) {
+ icnss_pr_err("Driver not registered\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = icnss_driver_event_post(priv,
+ ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_unregister_driver);
+
+static struct icnss_msi_config msi_config = {
+ .total_vectors = 28,
+ .total_users = 2,
+ .users = (struct icnss_msi_user[]) {
+ { .name = "CE", .num_vectors = 10, .base_vector = 0 },
+ { .name = "DP", .num_vectors = 18, .base_vector = 10 },
+ },
+};
+
+static int icnss_get_msi_assignment(struct icnss_priv *priv)
+{
+ priv->msi_config = &msi_config;
+
+ return 0;
+}
+
+int icnss_get_user_msi_assignment(struct device *dev, char *user_name,
+ int *num_vectors, u32 *user_base_data,
+ u32 *base_vector)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct icnss_msi_config *msi_config;
+ int idx;
+
+ if (!priv)
+ return -ENODEV;
+
+ msi_config = priv->msi_config;
+ if (!msi_config) {
+ icnss_pr_err("MSI is not supported.\n");
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < msi_config->total_users; idx++) {
+ if (strcmp(user_name, msi_config->users[idx].name) == 0) {
+ *num_vectors = msi_config->users[idx].num_vectors;
+ *user_base_data = msi_config->users[idx].base_vector
+ + priv->msi_base_data;
+ *base_vector = msi_config->users[idx].base_vector;
+
+ icnss_pr_dbg("Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
+ user_name, *num_vectors, *user_base_data,
+ *base_vector);
+
+ return 0;
+ }
+ }
+
+ icnss_pr_err("Failed to find MSI assignment for %s!\n", user_name);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(icnss_get_user_msi_assignment);
+
+int icnss_get_msi_irq(struct device *dev, unsigned int vector)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int irq_num;
+
+ irq_num = priv->srng_irqs[vector];
+ icnss_pr_dbg("Get IRQ number %d for vector index %d\n",
+ irq_num, vector);
+
+ return irq_num;
+}
+EXPORT_SYMBOL(icnss_get_msi_irq);
+
+void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low,
+ u32 *msi_addr_high)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ *msi_addr_low = lower_32_bits(priv->msi_addr_iova);
+ *msi_addr_high = upper_32_bits(priv->msi_addr_iova);
+
+}
+EXPORT_SYMBOL(icnss_get_msi_address);
+
+int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
+ irqreturn_t (*handler)(int, void *),
+ unsigned long flags, const char *name, void *ctx)
+{
+ int ret = 0;
+ unsigned int irq;
+ struct ce_irq_list *irq_entry;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv || !priv->pdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, priv->state);
+
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ irq = priv->ce_irqs[ce_id];
+ irq_entry = &priv->ce_irq_list[ce_id];
+
+ if (irq_entry->handler || irq_entry->irq) {
+ icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
+ irq, ce_id);
+ ret = -EEXIST;
+ goto out;
+ }
+
+ ret = request_irq(irq, handler, flags, name, ctx);
+ if (ret) {
+ icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
+ irq, ce_id, ret);
+ goto out;
+ }
+ irq_entry->irq = irq;
+ irq_entry->handler = handler;
+
+ icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
+
+ penv->stats.ce_irqs[ce_id].request++;
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_ce_request_irq);
+
+int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
+{
+ int ret = 0;
+ unsigned int irq;
+ struct ce_irq_list *irq_entry;
+
+ if (!penv || !penv->pdev || !dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
+
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ irq = penv->ce_irqs[ce_id];
+ irq_entry = &penv->ce_irq_list[ce_id];
+ if (!irq_entry->handler || !irq_entry->irq) {
+ icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
+ ret = -EEXIST;
+ goto out;
+ }
+ free_irq(irq, ctx);
+ irq_entry->irq = 0;
+ irq_entry->handler = NULL;
+
+ penv->stats.ce_irqs[ce_id].free++;
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_ce_free_irq);
+
+void icnss_enable_irq(struct device *dev, unsigned int ce_id)
+{
+ unsigned int irq;
+
+ if (!penv || !penv->pdev || !dev) {
+ icnss_pr_err("Platform driver not initialized\n");
+ return;
+ }
+
+ icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+ penv->state);
+
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
+ return;
+ }
+
+ penv->stats.ce_irqs[ce_id].enable++;
+
+ irq = penv->ce_irqs[ce_id];
+ enable_irq(irq);
+}
+EXPORT_SYMBOL(icnss_enable_irq);
+
+void icnss_disable_irq(struct device *dev, unsigned int ce_id)
+{
+ unsigned int irq;
+
+ if (!penv || !penv->pdev || !dev) {
+ icnss_pr_err("Platform driver not initialized\n");
+ return;
+ }
+
+ icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+ penv->state);
+
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
+ ce_id);
+ return;
+ }
+
+ irq = penv->ce_irqs[ce_id];
+ disable_irq(irq);
+
+ penv->stats.ce_irqs[ce_id].disable++;
+}
+EXPORT_SYMBOL(icnss_disable_irq);
+
+int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
+{
+ char *fw_build_timestamp = NULL;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Platform driver not initialized\n");
+ return -EINVAL;
+ }
+
+ info->v_addr = priv->mem_base_va;
+ info->p_addr = priv->mem_base_pa;
+ info->chip_id = priv->chip_info.chip_id;
+ info->chip_family = priv->chip_info.chip_family;
+ info->board_id = priv->board_id;
+ info->soc_id = priv->soc_id;
+ info->fw_version = priv->fw_version_info.fw_version;
+ fw_build_timestamp = priv->fw_version_info.fw_build_timestamp;
+ fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN] = '\0';
+ strlcpy(info->fw_build_timestamp,
+ priv->fw_version_info.fw_build_timestamp,
+ WLFW_MAX_TIMESTAMP_LEN + 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(icnss_get_soc_info);
+
+int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
+{
+ int ret;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!dev)
+ return -ENODEV;
+
+ if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+ !test_bit(ICNSS_FW_READY, &penv->state)) {
+ icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
+ priv->state);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
+
+ ret = wlfw_ini_send_sync_msg(priv, fw_log_mode);
+ if (ret)
+ icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
+ ret, fw_log_mode);
+ return ret;
+}
+EXPORT_SYMBOL(icnss_set_fw_log_mode);
+
+int icnss_athdiag_read(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *output)
+{
+ int ret = 0;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for diag read: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ if (!output || data_len == 0
+ || data_len > WLFW_MAX_DATA_SIZE) {
+ icnss_pr_err("Invalid parameters for diag read: output %pK, data_len %u\n",
+ output, data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state)) {
+ icnss_pr_err("Invalid state for diag read: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
+ data_len, output);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_read);
+
+int icnss_athdiag_write(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *input)
+{
+ int ret = 0;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for diag write: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ if (!input || data_len == 0
+ || data_len > WLFW_MAX_DATA_SIZE) {
+ icnss_pr_err("Invalid parameters for diag write: input %pK, data_len %u\n",
+ input, data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state)) {
+ icnss_pr_err("Invalid state for diag write: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
+ data_len, input);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_write);
+
+int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
+ enum icnss_driver_mode mode,
+ const char *host_version)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state) ||
+ !test_bit(ICNSS_FW_READY, &priv->state)) {
+ icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
+ priv->state);
+ return -EINVAL;
+ }
+
+ if (test_bit(ICNSS_MODE_ON, &priv->state)) {
+ icnss_pr_err("Already Mode on, ignoring wlan_enable state: 0x%lx\n",
+ priv->state);
+ return -EINVAL;
+ }
+
+ return icnss_send_wlan_enable_to_fw(priv, config, mode, host_version);
+}
+EXPORT_SYMBOL(icnss_wlan_enable);
+
+int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+ icnss_pr_dbg("FW down, ignoring wlan_disable state: 0x%lx\n",
+ priv->state);
+ return 0;
+ }
+
+ return icnss_send_wlan_disable_to_fw(priv);
+}
+EXPORT_SYMBOL(icnss_wlan_disable);
+
+bool icnss_is_qmi_disable(struct device *dev)
+{
+ return test_bit(SKIP_QMI, &penv->ctrl_params.quirks) ? true : false;
+}
+EXPORT_SYMBOL(icnss_is_qmi_disable);
+
+int icnss_get_ce_id(struct device *dev, int irq)
+{
+ int i;
+
+ if (!penv || !penv->pdev || !dev)
+ return -ENODEV;
+
+ for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
+ if (penv->ce_irqs[i] == irq)
+ return i;
+ }
+
+ icnss_pr_err("No matching CE id for irq %d\n", irq);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(icnss_get_ce_id);
+
+int icnss_get_irq(struct device *dev, int ce_id)
+{
+ int irq;
+
+ if (!penv || !penv->pdev || !dev)
+ return -ENODEV;
+
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
+ return -EINVAL;
+
+ irq = penv->ce_irqs[ce_id];
+
+ return irq;
+}
+EXPORT_SYMBOL(icnss_get_irq);
+
+struct iommu_domain *icnss_smmu_get_domain(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK\n", dev);
+ return NULL;
+ }
+ return priv->iommu_domain;
+}
+EXPORT_SYMBOL(icnss_smmu_get_domain);
+
+int icnss_smmu_map(struct device *dev,
+ phys_addr_t paddr, uint32_t *iova_addr, size_t size)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ unsigned long iova;
+ size_t len;
+ int ret = 0;
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+ dev, priv);
+ return -EINVAL;
+ }
+
+ if (!iova_addr) {
+ icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
+ &paddr, size);
+ return -EINVAL;
+ }
+
+ len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
+ iova = roundup(priv->smmu_iova_ipa_start, PAGE_SIZE);
+
+ if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
+ icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
+ iova,
+ &priv->smmu_iova_ipa_start,
+ priv->smmu_iova_ipa_len);
+ return -ENOMEM;
+ }
+
+ ret = iommu_map(priv->iommu_domain, iova,
+ rounddown(paddr, PAGE_SIZE), len,
+ IOMMU_READ | IOMMU_WRITE);
+ if (ret) {
+ icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
+ return ret;
+ }
+
+ priv->smmu_iova_ipa_start = iova + len;
+ *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
+
+ return 0;
+}
+EXPORT_SYMBOL(icnss_smmu_map);
+
+unsigned int icnss_socinfo_get_serial_number(struct device *dev)
+{
+ return socinfo_get_serial_number();
+}
+EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
+
+int icnss_trigger_recovery(struct device *dev)
+{
+ int ret = 0;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+ icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
+ priv->state);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
+ icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
+ priv->state);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!priv->service_notifier || !priv->service_notifier[0].handle) {
+ icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
+ priv->state);
+
+ /*
+ * Initiate PDR, required only for the first instance
+ */
+ ret = service_notif_pd_restart(priv->service_notifier[0].name,
+ priv->service_notifier[0].instance_id);
+
+ if (!ret)
+ set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_trigger_recovery);
+
+int icnss_idle_shutdown(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK", dev);
+ return -EINVAL;
+ }
+
+ if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &priv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle shutdown\n");
+ return -EBUSY;
+ }
+
+ return icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_shutdown);
+
+int icnss_idle_restart(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK", dev);
+ return -EINVAL;
+ }
+
+ if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &priv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle restart\n");
+ return -EBUSY;
+ }
+
+ return icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_RESTART,
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_restart);
+
+void icnss_allow_recursive_recovery(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ priv->allow_recursive_recovery = true;
+
+ icnss_pr_info("Recursive recovery allowed for WLAN\n");
+}
+
+void icnss_disallow_recursive_recovery(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ priv->allow_recursive_recovery = false;
+
+ icnss_pr_info("Recursive recovery disallowed for WLAN\n");
+}
+
+static void icnss_sysfs_create(struct icnss_priv *priv)
+{
+ struct kobject *icnss_kobject;
+ int error = 0;
+
+ atomic_set(&priv->is_shutdown, false);
+
+ icnss_kobject = kobject_create_and_add("shutdown_wlan", kernel_kobj);
+ if (!icnss_kobject) {
+ icnss_pr_err("Unable to create kernel object");
+ return;
+ }
+
+ priv->icnss_kobject = icnss_kobject;
+
+ error = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr);
+ if (error)
+ icnss_pr_err("Unable to create icnss sysfs file");
+}
+
+static void icnss_sysfs_destroy(struct icnss_priv *priv)
+{
+ struct kobject *icnss_kobject;
+
+ icnss_kobject = priv->icnss_kobject;
+ if (icnss_kobject)
+ kobject_put(icnss_kobject);
+}
+
+static int icnss_get_vbatt_info(struct icnss_priv *priv)
+{
+ struct adc_tm_chip *adc_tm_dev = NULL;
+ struct iio_channel *channel = NULL;
+ int ret = 0;
+
+ adc_tm_dev = get_adc_tm(&priv->pdev->dev, "icnss");
+ if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
+ icnss_pr_err("adc_tm_dev probe defer\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (IS_ERR(adc_tm_dev)) {
+ ret = PTR_ERR(adc_tm_dev);
+ icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
+ ret);
+ return ret;
+ }
+
+ channel = iio_channel_get(&priv->pdev->dev, "icnss");
+ if (PTR_ERR(channel) == -EPROBE_DEFER) {
+ icnss_pr_err("channel probe defer\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (IS_ERR(channel)) {
+ ret = PTR_ERR(channel);
+ icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
+ ret);
+ return ret;
+ }
+
+ priv->adc_tm_dev = adc_tm_dev;
+ priv->channel = channel;
+
+ return 0;
+}
+
+static int icnss_resource_parse(struct icnss_priv *priv)
+{
+ int ret = 0, i = 0;
+ struct platform_device *pdev = priv->pdev;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ u32 int_prop;
+
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,icnss-adc_tm")) {
+ ret = icnss_get_vbatt_info(priv);
+ if (ret == -EPROBE_DEFER)
+ goto out;
+ priv->vbatt_supported = true;
+ }
+
+ ret = icnss_get_vreg(priv);
+ if (ret) {
+ icnss_pr_err("Failed to get vreg, err = %d\n", ret);
+ goto out;
+ }
+
+ ret = icnss_get_clk(priv);
+ if (ret) {
+ icnss_pr_err("Failed to get clocks, err = %d\n", ret);
+ goto put_vreg;
+ }
+
+ if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "membase");
+ if (!res) {
+ icnss_pr_err("Memory base not found in DT\n");
+ ret = -EINVAL;
+ goto put_clk;
+ }
+
+ priv->mem_base_pa = res->start;
+ priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
+ resource_size(res));
+ if (!priv->mem_base_va) {
+ icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
+ &priv->mem_base_pa);
+ ret = -EINVAL;
+ goto put_clk;
+ }
+ icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%pK\n",
+ &priv->mem_base_pa,
+ priv->mem_base_va);
+
+ for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
+ res = platform_get_resource(priv->pdev,
+ IORESOURCE_IRQ, i);
+ if (!res) {
+ icnss_pr_err("Fail to get IRQ-%d\n", i);
+ ret = -ENODEV;
+ goto put_clk;
+ } else {
+ priv->ce_irqs[i] = res->start;
+ }
+ }
+ } else if (priv->device_id == WCN6750_DEVICE_ID) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "msi_addr");
+ if (!res) {
+ icnss_pr_err("MSI address not found in DT\n");
+ ret = -EINVAL;
+ goto put_clk;
+ }
+
+ priv->msi_addr_pa = res->start;
+ priv->msi_addr_iova = dma_map_resource(dev, priv->msi_addr_pa,
+ PAGE_SIZE,
+ DMA_FROM_DEVICE, 0);
+ if (dma_mapping_error(dev, priv->msi_addr_iova)) {
+ icnss_pr_err("MSI: failed to map msi address\n");
+ priv->msi_addr_iova = 0;
+ ret = -ENOMEM;
+ goto put_clk;
+ }
+ icnss_pr_dbg("MSI Addr pa: %pa, iova: 0x%pK\n",
+ &priv->msi_addr_pa,
+ priv->msi_addr_iova);
+
+ ret = of_property_read_u32_index(dev->of_node,
+ "interrupts",
+ 1,
+ &int_prop);
+ if (ret) {
+ icnss_pr_dbg("Read interrupt prop failed");
+ goto put_clk;
+ }
+
+ priv->msi_base_data = int_prop + 32;
+ icnss_pr_dbg(" MSI Base Data: %d, IRQ Index: %d\n",
+ priv->msi_base_data, int_prop);
+
+ icnss_get_msi_assignment(priv);
+ for (i = 0; i < msi_config.total_vectors; i++) {
+ res = platform_get_resource(priv->pdev,
+ IORESOURCE_IRQ, i);
+ if (!res) {
+ icnss_pr_err("Fail to get IRQ-%d\n", i);
+ ret = -ENODEV;
+ goto put_clk;
+ } else {
+ priv->srng_irqs[i] = res->start;
+ }
+ }
+ }
+
+ return 0;
+
+put_clk:
+ icnss_put_clk(priv);
+put_vreg:
+ icnss_put_vreg(priv);
+out:
+ return ret;
+}
+
+static int icnss_msa_dt_parse(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct platform_device *pdev = priv->pdev;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = NULL;
+ u64 prop_size = 0;
+ const __be32 *addrp = NULL;
+
+ np = of_parse_phandle(dev->of_node,
+ "qcom,wlan-msa-fixed-region", 0);
+ if (np) {
+ addrp = of_get_address(np, 0, &prop_size, NULL);
+ if (!addrp) {
+ icnss_pr_err("Failed to get assigned-addresses or property\n");
+ ret = -EINVAL;
+ of_node_put(np);
+ goto out;
+ }
+
+ priv->msa_pa = of_translate_address(np, addrp);
+ if (priv->msa_pa == OF_BAD_ADDR) {
+ icnss_pr_err("Failed to translate MSA PA from device-tree\n");
+ ret = -EINVAL;
+ of_node_put(np);
+ goto out;
+ }
+
+ of_node_put(np);
+
+ priv->msa_va = memremap(priv->msa_pa,
+ (unsigned long)prop_size, MEMREMAP_WT);
+ if (!priv->msa_va) {
+ icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
+ &priv->msa_pa);
+ ret = -EINVAL;
+ goto out;
+ }
+ priv->msa_mem_size = prop_size;
+ } else {
+ ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
+ &priv->msa_mem_size);
+ if (ret || priv->msa_mem_size == 0) {
+ icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
+ priv->msa_mem_size, ret);
+ goto out;
+ }
+
+ priv->msa_va = dmam_alloc_coherent(&pdev->dev,
+ priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
+
+ if (!priv->msa_va) {
+ icnss_pr_err("DMA alloc failed for MSA\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%pK MSA Memory Size: 0x%x\n",
+ &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int icnss_smmu_dt_parse(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct platform_device *pdev = priv->pdev;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ u32 addr_win[2];
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,iommu-dma-addr-pool",
+ addr_win,
+ ARRAY_SIZE(addr_win));
+
+ if (ret) {
+ icnss_pr_err("SMMU IOVA base not found\n");
+ } else {
+ priv->iommu_domain =
+ iommu_get_domain_for_dev(&pdev->dev);
+
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM,
+ "smmu_iova_ipa");
+ if (!res) {
+ icnss_pr_err("SMMU IOVA IPA not found\n");
+ } else {
+ priv->smmu_iova_ipa_start = res->start;
+ priv->smmu_iova_ipa_len = resource_size(res);
+ icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zx\n",
+ &priv->smmu_iova_ipa_start,
+ priv->smmu_iova_ipa_len);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id icnss_platform_id_table[] = {
+ { .name = "wcn6750", .driver_data = WCN6750_DEVICE_ID, },
+ { .name = "adrastea", .driver_data = ADRASTEA_DEVICE_ID, },
+ { },
+};
+
+static const struct of_device_id icnss_dt_match[] = {
+ {
+ .compatible = "qcom,wcn6750",
+ .data = (void *)&icnss_platform_id_table[0]},
+ {
+ .compatible = "qcom,icnss",
+ .data = (void *)&icnss_platform_id_table[1]},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, icnss_dt_match);
+
+static void icnss_init_control_params(struct icnss_priv *priv)
+{
+ priv->ctrl_params.qmi_timeout = WLFW_TIMEOUT;
+ priv->ctrl_params.quirks = ICNSS_QUIRKS_DEFAULT;
+ priv->ctrl_params.bdf_type = ICNSS_BDF_TYPE_DEFAULT;
+
+ if (of_property_read_bool(priv->pdev->dev.of_node,
+ "cnss-daemon-support")) {
+ priv->ctrl_params.quirks |= BIT(ENABLE_DAEMON_SUPPORT);
+ }
+}
+
+static int icnss_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+ struct icnss_priv *priv;
+ const struct of_device_id *of_id;
+ const struct platform_device_id *device_id;
+
+ if (dev_get_drvdata(dev)) {
+ icnss_pr_err("Driver is already initialized\n");
+ return -EEXIST;
+ }
+
+ of_id = of_match_device(icnss_dt_match, &pdev->dev);
+ if (!of_id || !of_id->data) {
+ icnss_pr_err("Failed to find of match device!\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ device_id = of_id->data;
+
+ icnss_pr_dbg("Platform driver probe\n");
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->magic = ICNSS_MAGIC;
+ dev_set_drvdata(dev, priv);
+
+ priv->pdev = pdev;
+ priv->device_id = device_id->driver_data;
+ INIT_LIST_HEAD(&priv->vreg_list);
+ INIT_LIST_HEAD(&priv->clk_list);
+ icnss_allow_recursive_recovery(dev);
+
+ icnss_init_control_params(priv);
+
+ ret = icnss_resource_parse(priv);
+ if (ret)
+ goto out;
+
+ ret = icnss_msa_dt_parse(priv);
+ if (ret)
+ goto out;
+
+ ret = icnss_smmu_dt_parse(priv);
+ if (ret)
+ goto out;
+
+ spin_lock_init(&priv->event_lock);
+ spin_lock_init(&priv->on_off_lock);
+ mutex_init(&priv->dev_lock);
+
+ priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
+ if (!priv->event_wq) {
+ icnss_pr_err("Workqueue creation failed\n");
+ ret = -EFAULT;
+ goto smmu_cleanup;
+ }
+
+ INIT_WORK(&priv->event_work, icnss_driver_event_work);
+ INIT_LIST_HEAD(&priv->event_list);
+
+ ret = icnss_register_fw_service(priv);
+ if (ret < 0) {
+ icnss_pr_err("fw service registration failed: %d\n", ret);
+ goto out_destroy_wq;
+ }
+
+ icnss_enable_recovery(priv);
+
+ icnss_debugfs_create(priv);
+
+ icnss_sysfs_create(priv);
+
+ ret = device_init_wakeup(&priv->pdev->dev, true);
+ if (ret)
+ icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
+ ret);
+
+ icnss_set_plat_priv(priv);
+
+ init_completion(&priv->unblock_shutdown);
+
+ icnss_pr_info("Platform driver probed successfully\n");
+
+ return 0;
+
+out_destroy_wq:
+ destroy_workqueue(priv->event_wq);
+smmu_cleanup:
+ priv->iommu_domain = NULL;
+out:
+ dev_set_drvdata(dev, NULL);
+
+ return ret;
+}
+
+static int icnss_remove(struct platform_device *pdev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state);
+
+ device_init_wakeup(&priv->pdev->dev, false);
+
+ icnss_debugfs_destroy(priv);
+
+ icnss_sysfs_destroy(priv);
+
+ complete_all(&priv->unblock_shutdown);
+
+ icnss_modem_ssr_unregister_notifier(priv);
+
+ destroy_ramdump_device(priv->msa0_dump_dev);
+
+ icnss_pdr_unregister_notifier(priv);
+
+ icnss_unregister_fw_service(priv);
+ if (priv->event_wq)
+ destroy_workqueue(priv->event_wq);
+
+ priv->iommu_domain = NULL;
+
+ icnss_hw_power_off(priv);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icnss_pm_suspend(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm suspend: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->pm_suspend ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->pm_suspend(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_suspend++;
+ set_bit(ICNSS_PM_SUSPEND, &priv->state);
+ } else {
+ priv->stats.pm_suspend_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_resume(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm resume: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->pm_resume ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->pm_resume(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_resume++;
+ clear_bit(ICNSS_PM_SUSPEND, &priv->state);
+ } else {
+ priv->stats.pm_resume_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_suspend_noirq(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->suspend_noirq ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->suspend_noirq(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_suspend_noirq++;
+ set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+ } else {
+ priv->stats.pm_suspend_noirq_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_resume_noirq(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %pK, data %pK, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->resume_noirq ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->resume_noirq(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_resume_noirq++;
+ clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+ } else {
+ priv->stats.pm_resume_noirq_err++;
+ }
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops icnss_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
+ icnss_pm_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
+ icnss_pm_resume_noirq)
+};
+
+
+static struct platform_driver icnss_driver = {
+ .probe = icnss_probe,
+ .remove = icnss_remove,
+ .driver = {
+ .name = "icnss2",
+ .pm = &icnss_pm_ops,
+ .of_match_table = icnss_dt_match,
+ },
+};
+
+static int __init icnss_initialize(void)
+{
+ icnss_debug_init();
+ return platform_driver_register(&icnss_driver);
+}
+
+static void __exit icnss_exit(void)
+{
+ platform_driver_unregister(&icnss_driver);
+ icnss_debug_deinit();
+}
+
+
+module_init(icnss_initialize);
+module_exit(icnss_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DEVICE "iWCN CORE platform driver");
diff --git a/drivers/soc/qcom/icnss2/main.h b/drivers/soc/qcom/icnss2/main.h
new file mode 100644
index 0000000..26741b0
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/main.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include <linux/adc-tm-clients.h>
+#include <linux/iio/consumer.h>
+#include <linux/irqreturn.h>
+#include <asm/dma-iommu.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/ipc_logging.h>
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include <soc/qcom/icnss2.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+#include "wlan_firmware_service_v01.h"
+
+#define WCN6750_DEVICE_ID 0x6750
+#define ADRASTEA_DEVICE_ID 0xabcd
+
+extern uint64_t dynamic_feature_mask;
+
+enum icnss_bdf_type {
+ ICNSS_BDF_BIN,
+ ICNSS_BDF_ELF,
+ ICNSS_BDF_REGDB = 4,
+ ICNSS_BDF_DUMMY = 255,
+};
+
+struct icnss_control_params {
+ unsigned long quirks;
+ unsigned int qmi_timeout;
+ unsigned int bdf_type;
+};
+
+enum icnss_driver_event_type {
+ ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
+ ICNSS_DRIVER_EVENT_SERVER_EXIT,
+ ICNSS_DRIVER_EVENT_FW_READY_IND,
+ ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
+ ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+ ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+ ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+ ICNSS_DRIVER_EVENT_IDLE_RESTART,
+ ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
+ ICNSS_DRIVER_EVENT_MAX,
+};
+
+struct icnss_event_server_arrive_data {
+ unsigned int node;
+ unsigned int port;
+};
+
+struct icnss_event_pd_service_down_data {
+ bool crashed;
+ bool fw_rejuvenate;
+};
+
+struct icnss_driver_event {
+ struct list_head list;
+ enum icnss_driver_event_type type;
+ bool sync;
+ struct completion complete;
+ int ret;
+ void *data;
+};
+
+enum icnss_driver_state {
+ ICNSS_WLFW_CONNECTED,
+ ICNSS_POWER_ON,
+ ICNSS_FW_READY,
+ ICNSS_DRIVER_PROBED,
+ ICNSS_FW_TEST_MODE,
+ ICNSS_PM_SUSPEND,
+ ICNSS_PM_SUSPEND_NOIRQ,
+ ICNSS_SSR_REGISTERED,
+ ICNSS_PDR_REGISTERED,
+ ICNSS_PD_RESTART,
+ ICNSS_WLFW_EXISTS,
+ ICNSS_SHUTDOWN_DONE,
+ ICNSS_HOST_TRIGGERED_PDR,
+ ICNSS_FW_DOWN,
+ ICNSS_DRIVER_UNLOADING,
+ ICNSS_REJUVENATE,
+ ICNSS_MODE_ON,
+ ICNSS_BLOCK_SHUTDOWN,
+ ICNSS_PDR,
+};
+
+struct ce_irq_list {
+ int irq;
+ irqreturn_t (*handler)(int irq, void *priv);
+};
+
+struct icnss_vreg_cfg {
+ const char *name;
+ u32 min_uv;
+ u32 max_uv;
+ u32 load_ua;
+ u32 delay_us;
+ u32 need_unvote;
+ bool required;
+};
+
+struct icnss_vreg_info {
+ struct list_head list;
+ struct regulator *reg;
+ struct icnss_vreg_cfg cfg;
+ u32 enabled;
+};
+
+enum icnss_vreg_type {
+ ICNSS_VREG_PRIM,
+};
+struct icnss_clk_cfg {
+ const char *name;
+ u32 freq;
+ u32 required;
+};
+
+struct icnss_clk_info {
+ struct list_head list;
+ struct clk *clk;
+ struct icnss_clk_cfg cfg;
+ u32 enabled;
+};
+
+struct icnss_stats {
+ struct {
+ uint32_t posted;
+ uint32_t processed;
+ } events[ICNSS_DRIVER_EVENT_MAX];
+
+ struct {
+ uint32_t request;
+ uint32_t free;
+ uint32_t enable;
+ uint32_t disable;
+ } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+
+ struct {
+ uint32_t pdr_fw_crash;
+ uint32_t pdr_host_error;
+ uint32_t root_pd_crash;
+ uint32_t root_pd_shutdown;
+ } recovery;
+
+ uint32_t pm_suspend;
+ uint32_t pm_suspend_err;
+ uint32_t pm_resume;
+ uint32_t pm_resume_err;
+ uint32_t pm_suspend_noirq;
+ uint32_t pm_suspend_noirq_err;
+ uint32_t pm_resume_noirq;
+ uint32_t pm_resume_noirq_err;
+ uint32_t pm_stay_awake;
+ uint32_t pm_relax;
+
+ uint32_t ind_register_req;
+ uint32_t ind_register_resp;
+ uint32_t ind_register_err;
+ uint32_t msa_info_req;
+ uint32_t msa_info_resp;
+ uint32_t msa_info_err;
+ uint32_t msa_ready_req;
+ uint32_t msa_ready_resp;
+ uint32_t msa_ready_err;
+ uint32_t msa_ready_ind;
+ uint32_t cap_req;
+ uint32_t cap_resp;
+ uint32_t cap_err;
+ uint32_t pin_connect_result;
+ uint32_t cfg_req;
+ uint32_t cfg_resp;
+ uint32_t cfg_req_err;
+ uint32_t mode_req;
+ uint32_t mode_resp;
+ uint32_t mode_req_err;
+ uint32_t ini_req;
+ uint32_t ini_resp;
+ uint32_t ini_req_err;
+ u32 rejuvenate_ind;
+ uint32_t rejuvenate_ack_req;
+ uint32_t rejuvenate_ack_resp;
+ uint32_t rejuvenate_ack_err;
+ uint32_t vbatt_req;
+ uint32_t vbatt_resp;
+ uint32_t vbatt_req_err;
+ uint32_t device_info_req;
+ uint32_t device_info_resp;
+ uint32_t device_info_err;
+};
+
+#define WLFW_MAX_TIMESTAMP_LEN 32
+#define WLFW_MAX_BUILD_ID_LEN 128
+#define WLFW_MAX_NUM_MEMORY_REGIONS 2
+#define WLFW_FUNCTION_NAME_LEN 129
+#define WLFW_MAX_DATA_SIZE 6144
+#define WLFW_MAX_STR_LEN 16
+#define WLFW_MAX_NUM_CE 12
+#define WLFW_MAX_NUM_SVC 24
+#define WLFW_MAX_NUM_SHADOW_REG 24
+
+struct service_notifier_context {
+ void *handle;
+ uint32_t instance_id;
+ char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
+};
+
+struct wlfw_rf_chip_info {
+ uint32_t chip_id;
+ uint32_t chip_family;
+};
+
+struct wlfw_rf_board_info {
+ uint32_t board_id;
+};
+
+struct wlfw_fw_version_info {
+ uint32_t fw_version;
+ char fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN + 1];
+};
+
+struct icnss_mem_region_info {
+ uint64_t reg_addr;
+ uint32_t size;
+ uint8_t secure_flag;
+};
+
+struct icnss_msi_user {
+ char *name;
+ int num_vectors;
+ u32 base_vector;
+};
+
+struct icnss_msi_config {
+ int total_vectors;
+ int total_users;
+ struct icnss_msi_user *users;
+};
+
+struct icnss_priv {
+ uint32_t magic;
+ struct platform_device *pdev;
+ struct icnss_driver_ops *ops;
+ struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
+ struct list_head vreg_list;
+ struct list_head clk_list;
+ unsigned long device_id;
+ struct icnss_msi_config *msi_config;
+ u32 msi_base_data;
+ struct icnss_control_params ctrl_params;
+ u8 cal_done;
+ u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+ u32 srng_irqs[IWCN_MAX_IRQ_REGISTRATIONS];
+ phys_addr_t mem_base_pa;
+ void __iomem *mem_base_va;
+ u32 mem_base_size;
+ struct iommu_domain *iommu_domain;
+ dma_addr_t smmu_iova_ipa_start;
+ size_t smmu_iova_ipa_len;
+ struct qmi_handle qmi;
+ struct list_head event_list;
+ spinlock_t event_lock;
+ struct work_struct event_work;
+ struct work_struct fw_recv_msg_work;
+ struct workqueue_struct *event_wq;
+ phys_addr_t msa_pa;
+ phys_addr_t msi_addr_pa;
+ dma_addr_t msi_addr_iova;
+ uint32_t msa_mem_size;
+ void *msa_va;
+ unsigned long state;
+ struct wlfw_rf_chip_info chip_info;
+ uint32_t board_id;
+ uint32_t soc_id;
+ struct wlfw_fw_version_info fw_version_info;
+ char fw_build_id[WLFW_MAX_BUILD_ID_LEN + 1];
+ u32 pwr_pin_result;
+ u32 phy_io_pin_result;
+ u32 rf_pin_result;
+ uint32_t nr_mem_region;
+ struct icnss_mem_region_info
+ mem_region[WLFW_MAX_NUM_MEMORY_REGIONS];
+ struct dentry *root_dentry;
+ spinlock_t on_off_lock;
+ struct icnss_stats stats;
+ struct work_struct service_notifier_work;
+ struct service_notifier_context *service_notifier;
+ struct notifier_block service_notifier_nb;
+ int total_domains;
+ struct notifier_block get_service_nb;
+ void *modem_notify_handler;
+ struct notifier_block modem_ssr_nb;
+ uint32_t diag_reg_read_addr;
+ uint32_t diag_reg_read_mem_type;
+ uint32_t diag_reg_read_len;
+ uint8_t *diag_reg_read_buf;
+ atomic_t pm_count;
+ struct ramdump_device *msa0_dump_dev;
+ bool force_err_fatal;
+ bool allow_recursive_recovery;
+ bool early_crash_ind;
+ u8 cause_for_rejuvenation;
+ u8 requesting_sub_system;
+ u16 line_number;
+ struct mutex dev_lock;
+ uint32_t fw_error_fatal_irq;
+ uint32_t fw_early_crash_irq;
+ struct completion unblock_shutdown;
+ struct adc_tm_param vph_monitor_params;
+ struct adc_tm_chip *adc_tm_dev;
+ struct iio_channel *channel;
+ uint64_t vph_pwr;
+ bool vbatt_supported;
+ char function_name[WLFW_FUNCTION_NAME_LEN + 1];
+ bool is_ssr;
+ struct kobject *icnss_kobject;
+ atomic_t is_shutdown;
+};
+
+struct icnss_reg_info {
+ uint32_t mem_type;
+ uint32_t reg_offset;
+ uint32_t data_len;
+};
+
+char *icnss_driver_event_to_str(enum icnss_driver_event_type type);
+int icnss_call_driver_uevent(struct icnss_priv *priv,
+ enum icnss_uevent uevent, void *data);
+int icnss_driver_event_post(struct icnss_priv *priv,
+ enum icnss_driver_event_type type,
+ u32 flags, void *data);
+void icnss_allow_recursive_recovery(struct device *dev);
+void icnss_disallow_recursive_recovery(struct device *dev);
+#endif
+
diff --git a/drivers/soc/qcom/icnss2/power.c b/drivers/soc/qcom/icnss2/power.c
new file mode 100644
index 0000000..8ed9f98
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/power.c
@@ -0,0 +1,850 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <soc/qcom/cmd-db.h>
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+
+static struct icnss_vreg_cfg icnss_vreg_list[] = {
+ {"vdd-wlan-core", 1300000, 1300000, 0, 0, 0, false},
+ {"vdd-wlan-io", 1800000, 1800000, 0, 0, 0, false},
+ {"vdd-wlan-xtal-aon", 0, 0, 0, 0, 0, false},
+ {"vdd-wlan-xtal", 1800000, 1800000, 0, 2, 0, false},
+ {"vdd-wlan", 0, 0, 0, 0, 0, false},
+ {"vdd-wlan-ctrl1", 0, 0, 0, 0, 0, false},
+ {"vdd-wlan-ctrl2", 0, 0, 0, 0, 0, false},
+ {"vdd-wlan-sp2t", 2700000, 2700000, 0, 0, 0, false},
+ {"wlan-ant-switch", 1800000, 1800000, 0, 0, 0, false},
+ {"wlan-soc-swreg", 1200000, 1200000, 0, 0, 0, false},
+ {"vdd-wlan-aon", 950000, 950000, 0, 0, 0, false},
+ {"vdd-wlan-dig", 950000, 952000, 0, 0, 0, false},
+ {"vdd-wlan-rfa1", 1900000, 1900000, 0, 0, 0, false},
+ {"vdd-wlan-rfa2", 1350000, 1350000, 0, 0, 0, false},
+ {"vdd-wlan-en", 0, 0, 0, 10, 0, false},
+};
+
+static struct icnss_vreg_cfg icnss_adrestea_vreg_list[] = {
+ {"vdd-cx-mx", 752000, 752000, 0, 0, 0, false},
+ {"vdd-1.8-xo", 1800000, 1800000, 0, 0, 0, false},
+ {"vdd-1.3-rfa", 1304000, 1304000, 0, 0, 0, false},
+ {"vdd-3.3-ch1", 3312000, 3312000, 0, 0, 0, false},
+ {"vdd-3.3-ch0", 3312000, 3312000, 0, 0, 0, false},
+};
+
+static struct icnss_clk_cfg icnss_clk_list[] = {
+ {"rf_clk", 0, 0},
+};
+
+static struct icnss_clk_cfg icnss_adrestea_clk_list[] = {
+ {"cxo_ref_clk_pin", 0, 0},
+};
+
+#define ICNSS_VREG_LIST_SIZE ARRAY_SIZE(icnss_vreg_list)
+#define ICNSS_VREG_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_vreg_list)
+#define ICNSS_CLK_LIST_SIZE ARRAY_SIZE(icnss_clk_list)
+#define ICNSS_CLK_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_clk_list)
+
+#define MAX_PROP_SIZE 32
+#define ICNSS_THRESHOLD_HIGH 3600000
+#define ICNSS_THRESHOLD_LOW 3450000
+#define ICNSS_THRESHOLD_GUARD 20000
+
+static int icnss_get_vreg_single(struct icnss_priv *priv,
+ struct icnss_vreg_info *vreg)
+{
+ int ret = 0;
+ struct device *dev = NULL;
+ struct regulator *reg = NULL;
+ const __be32 *prop = NULL;
+ char prop_name[MAX_PROP_SIZE] = {0};
+ int len = 0;
+ int i;
+
+ dev = &priv->pdev->dev;
+
+ reg = devm_regulator_get_optional(dev, vreg->cfg.name);
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ if (ret == -ENODEV) {
+ return ret;
+ } else if (ret == -EPROBE_DEFER) {
+ icnss_pr_info("EPROBE_DEFER for regulator: %s\n",
+ vreg->cfg.name);
+ goto out;
+ } else if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ if (vreg->cfg.required) {
+ icnss_pr_err("Regulator %s doesn't exist: %d\n",
+ vreg->cfg.name, ret);
+ goto out;
+ } else {
+ icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
+ vreg->cfg.name, ret);
+ goto done;
+ }
+ } else {
+ icnss_pr_err("Failed to get regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ goto out;
+ }
+ }
+
+ vreg->reg = reg;
+
+ snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-config",
+ vreg->cfg.name);
+
+ prop = of_get_property(dev->of_node, prop_name, &len);
+
+ icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
+ prop_name, len);
+
+ if (!prop || len < (2 * sizeof(__be32))) {
+ icnss_pr_dbg("Property %s %s, use default\n", prop_name,
+ prop ? "invalid format" : "doesn't exist");
+ goto done;
+ }
+
+ for (i = 0; (i * sizeof(__be32)) < len; i++) {
+ switch (i) {
+ case 0:
+ vreg->cfg.min_uv = be32_to_cpup(&prop[0]);
+ break;
+ case 1:
+ vreg->cfg.max_uv = be32_to_cpup(&prop[1]);
+ break;
+ case 2:
+ vreg->cfg.load_ua = be32_to_cpup(&prop[2]);
+ break;
+ case 3:
+ vreg->cfg.delay_us = be32_to_cpup(&prop[3]);
+ break;
+ case 4:
+ if (priv->device_id == WCN6750_DEVICE_ID)
+ vreg->cfg.need_unvote = be32_to_cpup(&prop[4]);
+ else
+ vreg->cfg.need_unvote = 0;
+ break;
+ default:
+ icnss_pr_dbg("Property %s, ignoring value at %d\n",
+ prop_name, i);
+ break;
+ }
+ }
+
+done:
+ icnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u, need_unvote: %u\n",
+ vreg->cfg.name, vreg->cfg.min_uv,
+ vreg->cfg.max_uv, vreg->cfg.load_ua,
+ vreg->cfg.delay_us, vreg->cfg.need_unvote);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void icnss_put_vreg_single(struct icnss_priv *priv,
+ struct icnss_vreg_info *vreg)
+{
+ struct device *dev = &priv->pdev->dev;
+
+ icnss_pr_dbg("Put regulator: %s\n", vreg->cfg.name);
+ devm_regulator_put(vreg->reg);
+ devm_kfree(dev, vreg);
+}
+
+static int icnss_vreg_on_single(struct icnss_vreg_info *vreg)
+{
+ int ret = 0;
+
+ if (vreg->enabled) {
+ icnss_pr_dbg("Regulator %s is already enabled\n",
+ vreg->cfg.name);
+ return 0;
+ }
+
+ icnss_pr_dbg("Regulator %s is being enabled\n", vreg->cfg.name);
+
+ if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+ ret = regulator_set_voltage(vreg->reg,
+ vreg->cfg.min_uv,
+ vreg->cfg.max_uv);
+
+ if (ret) {
+ icnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n",
+ vreg->cfg.name, vreg->cfg.min_uv,
+ vreg->cfg.max_uv, ret);
+ goto out;
+ }
+ }
+
+ if (vreg->cfg.load_ua) {
+ ret = regulator_set_load(vreg->reg,
+ vreg->cfg.load_ua);
+
+ if (ret < 0) {
+ icnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n",
+ vreg->cfg.name, vreg->cfg.load_ua,
+ ret);
+ goto out;
+ }
+ }
+
+ if (vreg->cfg.delay_us)
+ udelay(vreg->cfg.delay_us);
+
+ ret = regulator_enable(vreg->reg);
+ if (ret) {
+ icnss_pr_err("Failed to enable regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ goto out;
+ }
+
+ vreg->enabled = true;
+
+out:
+ return ret;
+}
+
+static int icnss_vreg_unvote_single(struct icnss_vreg_info *vreg)
+{
+ int ret = 0;
+
+ if (!vreg->enabled) {
+ icnss_pr_dbg("Regulator %s is already disabled\n",
+ vreg->cfg.name);
+ return 0;
+ }
+
+ icnss_pr_dbg("Removing vote for Regulator %s\n", vreg->cfg.name);
+
+ if (vreg->cfg.load_ua) {
+ ret = regulator_set_load(vreg->reg, 0);
+ if (ret < 0)
+ icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ }
+
+ if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+ ret = regulator_set_voltage(vreg->reg, 0,
+ vreg->cfg.max_uv);
+ if (ret)
+ icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ }
+
+ return ret;
+}
+
+static int icnss_vreg_off_single(struct icnss_vreg_info *vreg)
+{
+ int ret = 0;
+
+ if (!vreg->enabled) {
+ icnss_pr_dbg("Regulator %s is already disabled\n",
+ vreg->cfg.name);
+ return 0;
+ }
+
+ icnss_pr_dbg("Regulator %s is being disabled\n",
+ vreg->cfg.name);
+
+ ret = regulator_disable(vreg->reg);
+ if (ret)
+ icnss_pr_err("Failed to disable regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+
+ if (vreg->cfg.load_ua) {
+ ret = regulator_set_load(vreg->reg, 0);
+ if (ret < 0)
+ icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ }
+
+ if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+ ret = regulator_set_voltage(vreg->reg, 0,
+ vreg->cfg.max_uv);
+ if (ret)
+ icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
+ vreg->cfg.name, ret);
+ }
+ vreg->enabled = false;
+
+ return ret;
+}
+
+static struct icnss_vreg_cfg *get_vreg_list(u32 *vreg_list_size,
+ unsigned long device_id)
+{
+ switch (device_id) {
+ case WCN6750_DEVICE_ID:
+ *vreg_list_size = ICNSS_VREG_LIST_SIZE;
+ return icnss_vreg_list;
+
+ case ADRASTEA_DEVICE_ID:
+ *vreg_list_size = ICNSS_VREG_ADRESTEA_LIST_SIZE;
+ return icnss_adrestea_vreg_list;
+
+ default:
+ icnss_pr_err("Unsupported device_id 0x%x\n", device_id);
+ *vreg_list_size = 0;
+ return NULL;
+ }
+}
+
+int icnss_get_vreg(struct icnss_priv *priv)
+{
+ int ret = 0;
+ int i;
+ struct icnss_vreg_info *vreg;
+ struct icnss_vreg_cfg *vreg_cfg = NULL;
+ struct list_head *vreg_list = &priv->vreg_list;
+ struct device *dev = &priv->pdev->dev;
+ u32 vreg_list_size = 0;
+
+ vreg_cfg = get_vreg_list(&vreg_list_size, priv->device_id);
+ if (!vreg_cfg)
+ return -EINVAL;
+
+ for (i = 0; i < vreg_list_size; i++) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ memcpy(&vreg->cfg, &vreg_cfg[i], sizeof(vreg->cfg));
+ ret = icnss_get_vreg_single(priv, vreg);
+ if (ret != 0) {
+ if (ret == -ENODEV) {
+ devm_kfree(dev, vreg);
+ continue;
+ } else {
+ devm_kfree(dev, vreg);
+ return ret;
+ }
+ }
+ list_add_tail(&vreg->list, vreg_list);
+ }
+
+ return 0;
+}
+
+void icnss_put_vreg(struct icnss_priv *priv)
+{
+ struct list_head *vreg_list = &priv->vreg_list;
+ struct icnss_vreg_info *vreg = NULL;
+
+ while (!list_empty(vreg_list)) {
+ vreg = list_first_entry(vreg_list,
+ struct icnss_vreg_info, list);
+ list_del(&vreg->list);
+ if (IS_ERR_OR_NULL(vreg->reg))
+ continue;
+ icnss_put_vreg_single(priv, vreg);
+ }
+}
+
+static int icnss_vreg_on(struct icnss_priv *priv)
+{
+ struct list_head *vreg_list = &priv->vreg_list;
+ struct icnss_vreg_info *vreg = NULL;
+ int ret = 0;
+
+ list_for_each_entry(vreg, vreg_list, list) {
+ if (IS_ERR_OR_NULL(vreg->reg))
+ continue;
+ ret = icnss_vreg_on_single(vreg);
+ if (ret)
+ break;
+ }
+
+ if (!ret)
+ return 0;
+
+ list_for_each_entry_continue_reverse(vreg, vreg_list, list) {
+ if (IS_ERR_OR_NULL(vreg->reg) || !vreg->enabled)
+ continue;
+
+ icnss_vreg_off_single(vreg);
+ }
+
+ return ret;
+}
+
+static int icnss_vreg_off(struct icnss_priv *priv)
+{
+ struct list_head *vreg_list = &priv->vreg_list;
+ struct icnss_vreg_info *vreg = NULL;
+
+ list_for_each_entry_reverse(vreg, vreg_list, list) {
+ if (IS_ERR_OR_NULL(vreg->reg))
+ continue;
+
+ icnss_vreg_off_single(vreg);
+ }
+
+ return 0;
+}
+
+int icnss_vreg_unvote(struct icnss_priv *priv)
+{
+ struct list_head *vreg_list = &priv->vreg_list;
+ struct icnss_vreg_info *vreg = NULL;
+
+ list_for_each_entry_reverse(vreg, vreg_list, list) {
+ if (IS_ERR_OR_NULL(vreg->reg))
+ continue;
+
+ if (vreg->cfg.need_unvote)
+ icnss_vreg_unvote_single(vreg);
+ }
+
+ return 0;
+}
+
+int icnss_get_clk_single(struct icnss_priv *priv,
+ struct icnss_clk_info *clk_info)
+{
+ struct device *dev = &priv->pdev->dev;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get(dev, clk_info->cfg.name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ if (clk_info->cfg.required)
+ icnss_pr_err("Failed to get clock %s, err = %d\n",
+ clk_info->cfg.name, ret);
+ else
+ icnss_pr_dbg("Failed to get optional clock %s, err = %d\n",
+ clk_info->cfg.name, ret);
+ return ret;
+ }
+
+ clk_info->clk = clk;
+ icnss_pr_dbg("Got clock: %s, freq: %u\n",
+ clk_info->cfg.name, clk_info->cfg.freq);
+
+ return 0;
+}
+
+static void icnss_put_clk_single(struct icnss_priv *priv,
+ struct icnss_clk_info *clk_info)
+{
+ struct device *dev = &priv->pdev->dev;
+
+ icnss_pr_dbg("Put clock: %s\n", clk_info->cfg.name);
+ devm_clk_put(dev, clk_info->clk);
+}
+
+static int icnss_clk_on_single(struct icnss_clk_info *clk_info)
+{
+ int ret;
+
+ if (clk_info->enabled) {
+ icnss_pr_dbg("Clock %s is already enabled\n",
+ clk_info->cfg.name);
+ return 0;
+ }
+
+ icnss_pr_dbg("Clock %s is being enabled\n", clk_info->cfg.name);
+
+ if (clk_info->cfg.freq) {
+ ret = clk_set_rate(clk_info->clk, clk_info->cfg.freq);
+ if (ret) {
+ icnss_pr_err("Failed to set frequency %u for clock %s, err = %d\n",
+ clk_info->cfg.freq, clk_info->cfg.name,
+ ret);
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(clk_info->clk);
+ if (ret) {
+ icnss_pr_err("Failed to enable clock %s, err = %d\n",
+ clk_info->cfg.name, ret);
+ return ret;
+ }
+
+ clk_info->enabled = true;
+
+ return 0;
+}
+
+static int icnss_clk_off_single(struct icnss_clk_info *clk_info)
+{
+ if (!clk_info->enabled) {
+ icnss_pr_dbg("Clock %s is already disabled\n",
+ clk_info->cfg.name);
+ return 0;
+ }
+
+ icnss_pr_dbg("Clock %s is being disabled\n", clk_info->cfg.name);
+
+ clk_disable_unprepare(clk_info->clk);
+ clk_info->enabled = false;
+
+ return 0;
+}
+
+int icnss_get_clk(struct icnss_priv *priv)
+{
+ struct device *dev;
+ struct list_head *clk_list;
+ struct icnss_clk_info *clk_info;
+ struct icnss_clk_cfg *clk_cfg;
+ int ret, i;
+ u32 clk_list_size = 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ dev = &priv->pdev->dev;
+ clk_list = &priv->clk_list;
+
+ if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ clk_cfg = icnss_adrestea_clk_list;
+ clk_list_size = ICNSS_CLK_ADRESTEA_LIST_SIZE;
+ } else if (priv->device_id == WCN6750_DEVICE_ID) {
+ clk_cfg = icnss_clk_list;
+ clk_list_size = ICNSS_CLK_LIST_SIZE;
+ }
+
+ if (!list_empty(clk_list)) {
+ icnss_pr_dbg("Clocks have already been updated\n");
+ return 0;
+ }
+
+ for (i = 0; i < clk_list_size; i++) {
+ clk_info = devm_kzalloc(dev, sizeof(*clk_info), GFP_KERNEL);
+ if (!clk_info) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ memcpy(&clk_info->cfg, &clk_cfg[i],
+ sizeof(clk_info->cfg));
+ ret = icnss_get_clk_single(priv, clk_info);
+ if (ret != 0) {
+ if (clk_info->cfg.required) {
+ devm_kfree(dev, clk_info);
+ goto cleanup;
+ } else {
+ devm_kfree(dev, clk_info);
+ continue;
+ }
+ }
+ list_add_tail(&clk_info->list, clk_list);
+ }
+
+ return 0;
+
+cleanup:
+ while (!list_empty(clk_list)) {
+ clk_info = list_first_entry(clk_list, struct icnss_clk_info,
+ list);
+ list_del(&clk_info->list);
+ if (IS_ERR_OR_NULL(clk_info->clk))
+ continue;
+ icnss_put_clk_single(priv, clk_info);
+ devm_kfree(dev, clk_info);
+ }
+
+ return ret;
+}
+
+void icnss_put_clk(struct icnss_priv *priv)
+{
+ struct device *dev;
+ struct list_head *clk_list;
+ struct icnss_clk_info *clk_info;
+
+ if (!priv)
+ return;
+
+ dev = &priv->pdev->dev;
+ clk_list = &priv->clk_list;
+
+ while (!list_empty(clk_list)) {
+ clk_info = list_first_entry(clk_list, struct icnss_clk_info,
+ list);
+ list_del(&clk_info->list);
+ if (IS_ERR_OR_NULL(clk_info->clk))
+ continue;
+ icnss_put_clk_single(priv, clk_info);
+ devm_kfree(dev, clk_info);
+ }
+}
+
+static int icnss_clk_on(struct list_head *clk_list)
+{
+ struct icnss_clk_info *clk_info;
+ int ret = 0;
+
+ list_for_each_entry(clk_info, clk_list, list) {
+ if (IS_ERR_OR_NULL(clk_info->clk))
+ continue;
+ ret = icnss_clk_on_single(clk_info);
+ if (ret)
+ break;
+ }
+
+ if (!ret)
+ return 0;
+
+ list_for_each_entry_continue_reverse(clk_info, clk_list, list) {
+ if (IS_ERR_OR_NULL(clk_info->clk))
+ continue;
+
+ icnss_clk_off_single(clk_info);
+ }
+
+ return ret;
+}
+
+static int icnss_clk_off(struct list_head *clk_list)
+{
+ struct icnss_clk_info *clk_info;
+
+ list_for_each_entry_reverse(clk_info, clk_list, list) {
+ if (IS_ERR_OR_NULL(clk_info->clk))
+ continue;
+
+ icnss_clk_off_single(clk_info);
+ }
+
+ return 0;
+}
+
+int icnss_hw_power_on(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
+
+ spin_lock(&priv->on_off_lock);
+ if (test_bit(ICNSS_POWER_ON, &priv->state)) {
+ spin_unlock(&priv->on_off_lock);
+ return ret;
+ }
+ set_bit(ICNSS_POWER_ON, &priv->state);
+ spin_unlock(&priv->on_off_lock);
+
+ ret = icnss_vreg_on(priv);
+ if (ret) {
+ icnss_pr_err("Failed to turn on vreg, err = %d\n", ret);
+ goto out;
+ }
+
+ ret = icnss_clk_on(&priv->clk_list);
+ if (ret)
+ goto vreg_off;
+
+ return ret;
+
+vreg_off:
+ icnss_vreg_off(priv);
+out:
+ clear_bit(ICNSS_POWER_ON, &priv->state);
+ return ret;
+}
+
+int icnss_hw_power_off(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ if (test_bit(HW_ALWAYS_ON, &priv->ctrl_params.quirks))
+ return 0;
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state))
+ return 0;
+
+ icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
+
+ spin_lock(&priv->on_off_lock);
+ if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
+ spin_unlock(&priv->on_off_lock);
+ return ret;
+ }
+ clear_bit(ICNSS_POWER_ON, &priv->state);
+ spin_unlock(&priv->on_off_lock);
+
+ icnss_clk_off(&priv->clk_list);
+
+ ret = icnss_vreg_off(priv);
+
+ return ret;
+}
+
+int icnss_power_on(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+ dev, priv);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
+
+ return icnss_hw_power_on(priv);
+}
+EXPORT_SYMBOL(icnss_power_on);
+
+int icnss_power_off(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+ dev, priv);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
+
+ return icnss_hw_power_off(priv);
+}
+EXPORT_SYMBOL(icnss_power_off);
+
+void icnss_put_resources(struct icnss_priv *priv)
+{
+ icnss_put_clk(priv);
+ icnss_put_vreg(priv);
+}
+
+static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
+{
+ int ret = 0;
+ int result;
+
+ if (!priv->channel) {
+ icnss_pr_err("Channel doesn't exists\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = iio_read_channel_processed(priv->channel, &result);
+ if (ret < 0) {
+ icnss_pr_err("Error reading channel, ret = %d\n", ret);
+ goto out;
+ }
+
+ *result_uv = (uint64_t)result;
+out:
+ return ret;
+}
+
+static void icnss_vph_notify(enum adc_tm_state state, void *ctx)
+{
+ struct icnss_priv *priv = ctx;
+ u64 vph_pwr = 0;
+ u64 vph_pwr_prev;
+ int ret = 0;
+ bool update = true;
+
+ if (!priv) {
+ icnss_pr_err("Priv pointer is NULL\n");
+ return;
+ }
+
+ vph_pwr_prev = priv->vph_pwr;
+
+ ret = icnss_get_phone_power(priv, &vph_pwr);
+ if (ret < 0)
+ return;
+
+ if (vph_pwr < ICNSS_THRESHOLD_LOW) {
+ if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
+ ICNSS_THRESHOLD_GUARD;
+ priv->vph_monitor_params.low_thr = 0;
+ } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
+ if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
+ ICNSS_THRESHOLD_GUARD;
+ priv->vph_monitor_params.high_thr = 0;
+ } else {
+ if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
+ vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+ }
+
+ priv->vph_pwr = vph_pwr;
+
+ if (update) {
+ icnss_send_vbatt_update(priv, vph_pwr);
+ icnss_pr_dbg("set low threshold to %d, high threshold to %d Phone power=%llu\n",
+ priv->vph_monitor_params.low_thr,
+ priv->vph_monitor_params.high_thr, vph_pwr);
+ }
+
+ ret = adc_tm5_channel_measure(priv->adc_tm_dev,
+ &priv->vph_monitor_params);
+ if (ret)
+ icnss_pr_err("TM channel setup failed %d\n", ret);
+}
+
+static int icnss_setup_vph_monitor(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ if (!priv->adc_tm_dev) {
+ icnss_pr_err("ADC TM handler is NULL\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+ priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ priv->vph_monitor_params.channel = ADC_VBAT_SNS;
+ priv->vph_monitor_params.btm_ctx = priv;
+ priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
+ icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
+ priv->vph_monitor_params.low_thr,
+ priv->vph_monitor_params.high_thr);
+
+ ret = adc_tm5_channel_measure(priv->adc_tm_dev,
+ &priv->vph_monitor_params);
+ if (ret)
+ icnss_pr_err("TM channel setup failed %d\n", ret);
+out:
+ return ret;
+}
+
+int icnss_init_vph_monitor(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ ret = icnss_get_phone_power(priv, &priv->vph_pwr);
+ if (ret < 0)
+ goto out;
+
+ icnss_pr_dbg("Phone power=%llu\n", priv->vph_pwr);
+
+ icnss_send_vbatt_update(priv, priv->vph_pwr);
+
+ ret = icnss_setup_vph_monitor(priv);
+ if (ret)
+ goto out;
+out:
+ return ret;
+}
diff --git a/drivers/soc/qcom/icnss2/power.h b/drivers/soc/qcom/icnss2/power.h
new file mode 100644
index 0000000..0036941
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/power.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __ICNSS_POWER_H__
+#define __ICNSS_POWER_H__
+
+int icnss_hw_power_on(struct icnss_priv *priv);
+int icnss_hw_power_off(struct icnss_priv *priv);
+int icnss_get_clk(struct icnss_priv *priv);
+int icnss_get_vreg(struct icnss_priv *priv);
+int icnss_init_vph_monitor(struct icnss_priv *priv);
+void icnss_put_resources(struct icnss_priv *priv);
+void icnss_put_vreg(struct icnss_priv *priv);
+void icnss_put_clk(struct icnss_priv *priv);
+int icnss_vreg_unvote(struct icnss_priv *priv);
+
+#endif
diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c
new file mode 100644
index 0000000..cbe9c11
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/qmi.c
@@ -0,0 +1,1837 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "icnss2_qmi: " fmt
+
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ipc_logging.h>
+#include <linux/thread_info.h>
+#include <linux/firmware.h>
+#include <linux/soc/qcom/qmi.h>
+#include <soc/qcom/icnss2.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+#include "wlan_firmware_service_v01.h"
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+
+#define WLFW_SERVICE_WCN_INS_ID_V01 3
+#define WLFW_SERVICE_INS_ID_V01 0
+#define WLFW_CLIENT_ID 0x4b4e454c
+#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77
+
+#define MAX_BDF_FILE_NAME 13
+#define BDF_FILE_NAME_PREFIX "bdwlan"
+#define ELF_BDF_FILE_NAME "bdwlan.elf"
+#define BIN_BDF_FILE_NAME "bdwlan.bin"
+#define REGDB_FILE_NAME "regdb.bin"
+#define DUMMY_BDF_FILE_NAME "bdwlan.dmy"
+
+#ifdef CONFIG_ICNSS2_DEBUG
+bool ignore_fw_timeout;
+#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_fw_timeout)
+#else
+#define ICNSS_QMI_ASSERT() do { } while (0)
+#endif
+
+#ifdef CONFIG_ICNSS2_DEBUG
+void icnss_ignore_fw_timeout(bool ignore)
+{
+ ignore_fw_timeout = ignore;
+}
+#else
+void icnss_ignore_fw_timeout(bool ignore) { }
+#endif
+
+#define icnss_qmi_fatal_err(_fmt, ...) do { \
+ icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__); \
+ ICNSS_QMI_ASSERT(); \
+ } while (0)
+
+int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv)
+{
+ int ret;
+ int i;
+ struct wlfw_msa_info_req_msg_v01 *req;
+ struct wlfw_msa_info_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+ uint64_t max_mapped_addr;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->msa_addr = priv->msa_pa;
+ req->size = priv->msa_mem_size;
+
+ priv->stats.msa_info_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_msa_info_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Fail to init txn for MSA Mem info resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_MSA_INFO_REQ_V01,
+ WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_msa_info_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send MSA Mem info req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("MSA Mem info resp wait failed ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI MSA Mem info request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ icnss_pr_dbg("Receive mem_region_info_len: %d\n",
+ resp->mem_region_info_len);
+
+ if (resp->mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
+ icnss_qmi_fatal_err(
+ "Invalid memory region length received: %d\n",
+ resp->mem_region_info_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ max_mapped_addr = priv->msa_pa + priv->msa_mem_size;
+ priv->stats.msa_info_resp++;
+ priv->nr_mem_region = resp->mem_region_info_len;
+ for (i = 0; i < resp->mem_region_info_len; i++) {
+
+ if (resp->mem_region_info[i].size > priv->msa_mem_size ||
+ resp->mem_region_info[i].region_addr >= max_mapped_addr ||
+ resp->mem_region_info[i].region_addr < priv->msa_pa ||
+ resp->mem_region_info[i].size +
+ resp->mem_region_info[i].region_addr > max_mapped_addr) {
+ icnss_pr_dbg("Received out of range Addr: 0x%llx Size: 0x%x\n",
+ resp->mem_region_info[i].region_addr,
+ resp->mem_region_info[i].size);
+ ret = -EINVAL;
+ goto fail_unwind;
+ }
+
+ priv->mem_region[i].reg_addr =
+ resp->mem_region_info[i].region_addr;
+ priv->mem_region[i].size =
+ resp->mem_region_info[i].size;
+ priv->mem_region[i].secure_flag =
+ resp->mem_region_info[i].secure_flag;
+ icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
+ i, priv->mem_region[i].reg_addr,
+ priv->mem_region[i].size,
+ priv->mem_region[i].secure_flag);
+ }
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+fail_unwind:
+ memset(&priv->mem_region[0], 0, sizeof(priv->mem_region[0]) * i);
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.msa_info_err++;
+ return ret;
+}
+
+int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_msa_ready_req_msg_v01 *req;
+ struct wlfw_msa_ready_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ priv->stats.msa_ready_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_msa_ready_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Fail to init txn for MSA Mem Ready resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_MSA_READY_REQ_V01,
+ WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_msa_ready_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send MSA Mem Ready req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "MSA Mem Ready resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI MSA Mem Ready request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.msa_ready_resp++;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.msa_ready_err++;
+ return ret;
+}
+
+int wlfw_device_info_send_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_device_info_req_msg_v01 *req;
+ struct wlfw_device_info_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending Device Info request message, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ priv->stats.device_info_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_device_info_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Fail to init txn for Device Info resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_DEVICE_INFO_REQ_V01,
+ WLFW_DEVICE_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_device_info_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send device info req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Device Info resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Device info request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.device_info_resp++;
+
+ if (resp->bar_addr_valid)
+ priv->mem_base_pa = resp->bar_addr;
+
+ if (resp->bar_size_valid)
+ priv->mem_base_size = resp->bar_size;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.device_info_err++;
+ return ret;
+}
+
+int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_ind_register_req_msg_v01 *req;
+ struct wlfw_ind_register_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->client_id_valid = 1;
+ req->client_id = WLFW_CLIENT_ID;
+ req->fw_ready_enable_valid = 1;
+ req->fw_ready_enable = 1;
+ req->pin_connect_result_enable_valid = 1;
+ req->pin_connect_result_enable = 1;
+
+ if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ req->msa_ready_enable_valid = 1;
+ req->msa_ready_enable = 1;
+ if (test_bit(FW_REJUVENATE_ENABLE,
+ &priv->ctrl_params.quirks)) {
+ req->rejuvenate_enable_valid = 1;
+ req->rejuvenate_enable = 1;
+ }
+ } else if (priv->device_id == WCN6750_DEVICE_ID) {
+ req->fw_init_done_enable_valid = 1;
+ req->fw_init_done_enable = 1;
+ req->cal_done_enable_valid = 1;
+ req->cal_done_enable = 1;
+ }
+
+ priv->stats.ind_register_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_ind_register_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Fail to init txn for Ind Register resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_IND_REGISTER_REQ_V01,
+ WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_ind_register_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send Ind Register req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Ind Register resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Ind Register request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.ind_register_resp++;
+
+ if (resp->fw_status_valid &&
+ (resp->fw_status & QMI_WLFW_ALREADY_REGISTERED_V01)) {
+ ret = -EALREADY;
+ icnss_pr_dbg("WLFW already registered\n");
+ goto qmi_registered;
+ }
+
+ kfree(resp);
+ kfree(req);
+
+ return 0;
+
+out:
+ priv->stats.ind_register_err++;
+qmi_registered:
+ kfree(resp);
+ kfree(req);
+ return ret;
+}
+
+int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_cap_req_msg_v01 *req;
+ struct wlfw_cap_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending capability message, state: 0x%lx\n", priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ priv->stats.cap_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn, wlfw_cap_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Fail to init txn for Capability resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_CAP_REQ_V01,
+ WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_cap_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send Capability req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Capability resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Capability request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ if (resp->resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
+ icnss_qmi_fatal_err("RF card not present\n");
+ goto out;
+ }
+
+ priv->stats.cap_resp++;
+
+ if (resp->chip_info_valid) {
+ priv->chip_info.chip_id = resp->chip_info.chip_id;
+ priv->chip_info.chip_family = resp->chip_info.chip_family;
+ }
+ if (resp->board_info_valid)
+ priv->board_id = resp->board_info.board_id;
+ else
+ priv->board_id = 0xFF;
+ if (resp->soc_info_valid)
+ priv->soc_id = resp->soc_info.soc_id;
+ if (resp->fw_version_info_valid) {
+ priv->fw_version_info.fw_version =
+ resp->fw_version_info.fw_version;
+ strlcpy(priv->fw_version_info.fw_build_timestamp,
+ resp->fw_version_info.fw_build_timestamp,
+ WLFW_MAX_TIMESTAMP_LEN + 1);
+ }
+ if (resp->fw_build_id_valid)
+ strlcpy(priv->fw_build_id, resp->fw_build_id,
+ QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
+
+ icnss_pr_dbg("Capability, chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s, fw_build_id: %s",
+ priv->chip_info.chip_id, priv->chip_info.chip_family,
+ priv->board_id, priv->soc_id,
+ priv->fw_version_info.fw_version,
+ priv->fw_version_info.fw_build_timestamp,
+ priv->fw_build_id);
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.cap_err++;
+ return ret;
+}
+
+static int icnss_get_bdf_file_name(struct icnss_priv *priv,
+ u32 bdf_type, char *filename,
+ u32 filename_len)
+{
+ int ret = 0;
+
+ switch (bdf_type) {
+ case ICNSS_BDF_ELF:
+ if (priv->board_id == 0xFF)
+ snprintf(filename, filename_len, ELF_BDF_FILE_NAME);
+ else if (priv->board_id < 0xFF)
+ snprintf(filename, filename_len,
+ BDF_FILE_NAME_PREFIX "e%02x",
+ priv->board_id);
+ else
+ snprintf(filename, filename_len,
+ BDF_FILE_NAME_PREFIX "%03x",
+ priv->board_id);
+ break;
+ case ICNSS_BDF_BIN:
+ if (priv->board_id == 0xFF)
+ snprintf(filename, filename_len, BIN_BDF_FILE_NAME);
+ else if (priv->board_id < 0xFF)
+ snprintf(filename, filename_len,
+ BDF_FILE_NAME_PREFIX "b%02x",
+ priv->board_id);
+ else
+ snprintf(filename, filename_len,
+ BDF_FILE_NAME_PREFIX "%03x",
+ priv->board_id);
+ break;
+ case ICNSS_BDF_REGDB:
+ snprintf(filename, filename_len, REGDB_FILE_NAME);
+ break;
+ case ICNSS_BDF_DUMMY:
+ icnss_pr_dbg("CNSS_BDF_DUMMY is set, sending dummy BDF\n");
+ snprintf(filename, filename_len, DUMMY_BDF_FILE_NAME);
+ ret = MAX_BDF_FILE_NAME;
+ break;
+ default:
+ icnss_pr_err("Invalid BDF type: %d\n",
+ priv->ctrl_params.bdf_type);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type)
+{
+ struct wlfw_bdf_download_req_msg_v01 *req;
+ struct wlfw_bdf_download_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+ char filename[MAX_BDF_FILE_NAME];
+ const struct firmware *fw_entry = NULL;
+ const u8 *temp;
+ unsigned int remaining;
+ int ret = 0;
+
+ icnss_pr_dbg("Sending BDF download message, state: 0x%lx, type: %d\n",
+ priv->state, bdf_type);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ ret = icnss_get_bdf_file_name(priv, bdf_type,
+ filename, sizeof(filename));
+ if (ret > 0) {
+ temp = DUMMY_BDF_FILE_NAME;
+ remaining = MAX_BDF_FILE_NAME;
+ goto bypass_bdf;
+ } else if (ret < 0) {
+ goto err_req_fw;
+ }
+
+ ret = request_firmware(&fw_entry, filename, &priv->pdev->dev);
+ if (ret) {
+ icnss_pr_err("Failed to load BDF: %s\n", filename);
+ goto err_req_fw;
+ }
+
+ temp = fw_entry->data;
+ remaining = fw_entry->size;
+
+bypass_bdf:
+ icnss_pr_dbg("Downloading BDF: %s, size: %u\n", filename, remaining);
+
+ while (remaining) {
+ req->valid = 1;
+ req->file_id_valid = 1;
+ req->file_id = priv->board_id;
+ req->total_size_valid = 1;
+ req->total_size = remaining;
+ req->seg_id_valid = 1;
+ req->data_valid = 1;
+ req->end_valid = 1;
+ req->bdf_type_valid = 1;
+ req->bdf_type = bdf_type;
+
+ if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
+ req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
+ } else {
+ req->data_len = remaining;
+ req->end = 1;
+ }
+
+ memcpy(req->data, temp, req->data_len);
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_bdf_download_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Failed to initialize txn for BDF download request, err: %d\n",
+ ret);
+ goto err_send;
+ }
+
+ ret = qmi_send_request
+ (&priv->qmi, NULL, &txn,
+ QMI_WLFW_BDF_DOWNLOAD_REQ_V01,
+ WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_bdf_download_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Failed to send respond BDF download request, err: %d\n",
+ ret);
+ goto err_send;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Failed to wait for response of BDF download request, err: %d\n",
+ ret);
+ goto err_send;
+ }
+
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("BDF download request failed, result: %d, err: %d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto err_send;
+ }
+
+ remaining -= req->data_len;
+ temp += req->data_len;
+ req->seg_id++;
+ }
+
+ if (bdf_type != ICNSS_BDF_DUMMY)
+ release_firmware(fw_entry);
+
+ kfree(req);
+ kfree(resp);
+ return 0;
+
+err_send:
+ if (bdf_type != ICNSS_BDF_DUMMY)
+ release_firmware(fw_entry);
+err_req_fw:
+ if (bdf_type != ICNSS_BDF_REGDB)
+ ICNSS_ASSERT(0);
+ kfree(req);
+ kfree(resp);
+ return ret;
+}
+
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+ enum wlfw_driver_mode_enum_v01 mode)
+{
+ int ret;
+ struct wlfw_wlan_mode_req_msg_v01 *req;
+ struct wlfw_wlan_mode_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ /* During recovery do not send mode request for WLAN OFF as
+ * FW not able to process it.
+ */
+ if (test_bit(ICNSS_PD_RESTART, &priv->state) &&
+ mode == QMI_WLFW_OFF_V01)
+ return 0;
+
+ icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
+ priv->state, mode);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->mode = mode;
+ req->hw_debug_valid = 1;
+ req->hw_debug = !!test_bit(HW_DEBUG_ENABLE, &priv->ctrl_params.quirks);
+
+ priv->stats.mode_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_wlan_mode_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Fail to init txn for Mode resp %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_WLAN_MODE_REQ_V01,
+ WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_wlan_mode_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send Mode req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Mode resp wait failed with ret %d\n", ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Mode request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.mode_resp++;
+
+ if (mode == QMI_WLFW_OFF_V01) {
+ icnss_pr_dbg("Clear mode on 0x%lx, mode: %d\n",
+ priv->state, mode);
+ clear_bit(ICNSS_MODE_ON, &priv->state);
+ } else {
+ icnss_pr_dbg("Set mode on 0x%lx, mode: %d\n",
+ priv->state, mode);
+ set_bit(ICNSS_MODE_ON, &priv->state);
+ }
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.mode_req_err++;
+ return ret;
+}
+
+int wlfw_wlan_cfg_send_sync_msg(struct icnss_priv *priv,
+ struct wlfw_wlan_cfg_req_msg_v01 *data)
+{
+ int ret;
+ struct wlfw_wlan_cfg_req_msg_v01 *req;
+ struct wlfw_wlan_cfg_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending config request, state: 0x%lx\n", priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ memcpy(req, data, sizeof(*req));
+
+ priv->stats.cfg_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_wlan_cfg_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Fail to init txn for Config resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_WLAN_CFG_REQ_V01,
+ WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_wlan_cfg_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send Config req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Config resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Config request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.cfg_resp++;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.cfg_req_err++;
+ return ret;
+}
+
+int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_shutdown_req_msg_v01 *req;
+ struct wlfw_shutdown_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending modem shutdown request, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->shutdown_valid = 1;
+ req->shutdown = 1;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_shutdown_resp_msg_v01_ei, resp);
+
+ if (ret < 0) {
+ icnss_pr_err("Fail to init txn for shutdown resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_SHUTDOWN_REQ_V01,
+ WLFW_SHUTDOWN_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_shutdown_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Fail to send Shutdown req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Shutdown resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI modem shutdown request rejected result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+out:
+ kfree(resp);
+ kfree(req);
+ return ret;
+}
+
+int wlfw_ini_send_sync_msg(struct icnss_priv *priv, uint8_t fw_log_mode)
+{
+ int ret;
+ struct wlfw_ini_req_msg_v01 *req;
+ struct wlfw_ini_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
+ priv->state, fw_log_mode);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->enablefwlog_valid = 1;
+ req->enablefwlog = fw_log_mode;
+
+ priv->stats.ini_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn, wlfw_ini_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("Fail to init txn for INI resp %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_INI_REQ_V01,
+ WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_ini_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send INI req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err("INI resp wait failed with ret %d\n", ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI INI request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.ini_resp++;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.ini_req_err++;
+ return ret;
+}
+
+int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ int ret;
+ struct wlfw_athdiag_read_req_msg_v01 *req;
+ struct wlfw_athdiag_read_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
+ priv->state, offset, mem_type, data_len);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->offset = offset;
+ req->mem_type = mem_type;
+ req->data_len = data_len;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_athdiag_read_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Fail to init txn for Athdiag Read resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_ATHDIAG_READ_REQ_V01,
+ WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_athdiag_read_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Fail to send Athdiag Read req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Athdaig Read resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI Athdiag Read request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ } else {
+ ret = 0;
+ }
+
+ if (!resp->data_valid || resp->data_len < data_len) {
+ icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
+ resp->data_valid, resp->data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(data, resp->data, resp->data_len);
+
+out:
+ kfree(resp);
+ kfree(req);
+ return ret;
+}
+
+int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ int ret;
+ struct wlfw_athdiag_write_req_msg_v01 *req;
+ struct wlfw_athdiag_write_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %pK\n",
+ priv->state, offset, mem_type, data_len, data);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->offset = offset;
+ req->mem_type = mem_type;
+ req->data_len = data_len;
+ memcpy(req->data, data, data_len);
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_athdiag_write_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Fail to init txn for Athdiag Write resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_ATHDIAG_WRITE_REQ_V01,
+ WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_athdiag_write_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Fail to send Athdiag Write req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Athdiag Write resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI Athdiag Write request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+out:
+ kfree(resp);
+ kfree(req);
+ return ret;
+}
+
+int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
+{
+ int ret;
+ struct wlfw_rejuvenate_ack_req_msg_v01 *req;
+ struct wlfw_rejuvenate_ack_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ priv->stats.rejuvenate_ack_req++;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_rejuvenate_ack_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Fail to init txn for Rejuvenate Ack resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_REJUVENATE_ACK_REQ_V01,
+ WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_rejuvenate_ack_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_qmi_fatal_err("Fail to send Rejuvenate Ack req %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_qmi_fatal_err(
+ "Rejuvenate Ack resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_qmi_fatal_err(
+ "QMI Rejuvenate Ack request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.rejuvenate_ack_resp++;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.rejuvenate_ack_err++;
+ return ret;
+}
+
+int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
+ uint64_t dynamic_feature_mask)
+{
+ int ret;
+ struct wlfw_dynamic_feature_mask_req_msg_v01 *req;
+ struct wlfw_dynamic_feature_mask_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ if (!test_bit(ICNSS_WLFW_CONNECTED, &priv->state)) {
+ icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
+ priv->state);
+ return -EINVAL;
+ }
+
+ if (!test_bit(FW_REJUVENATE_ENABLE, &priv->ctrl_params.quirks)) {
+ icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
+ return 0;
+ }
+
+ icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
+ dynamic_feature_mask, priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->mask_valid = 1;
+ req->mask = dynamic_feature_mask;
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_dynamic_feature_mask_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Fail to init txn for Dynamic Feature Mask resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01,
+ WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_dynamic_feature_mask_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Fail to send Dynamic Feature Mask req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Dynamic Feature Mask resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI Dynamic Feature Mask request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
+ resp->prev_mask_valid, resp->prev_mask,
+ resp->curr_mask_valid, resp->curr_mask);
+
+out:
+ kfree(resp);
+ kfree(req);
+ return ret;
+}
+
+void icnss_handle_rejuvenate(struct icnss_priv *priv)
+{
+ struct icnss_event_pd_service_down_data *event_data;
+ struct icnss_uevent_fw_down_data fw_down_data;
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+ if (event_data == NULL)
+ return;
+
+ event_data->crashed = true;
+ event_data->fw_rejuvenate = true;
+ fw_down_data.crashed = true;
+ set_bit(ICNSS_REJUVENATE, &priv->state);
+
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ 0, event_data);
+}
+
+static void fw_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct icnss_priv *priv =
+ container_of(qmi, struct icnss_priv, qmi);
+
+ icnss_pr_dbg("Received FW Ready Indication\n");
+
+ if (!txn) {
+ pr_err("spurious indication\n");
+ return;
+ }
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_READY_IND,
+ 0, NULL);
+}
+
+static void msa_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+
+ icnss_pr_dbg("Received MSA Ready Indication\n");
+
+ if (!txn) {
+ pr_err("spurious indication\n");
+ return;
+ }
+
+ priv->stats.msa_ready_ind++;
+}
+
+static void pin_connect_result_ind_cb(struct qmi_handle *qmi,
+ struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+ const struct wlfw_pin_connect_result_ind_msg_v01 *ind_msg = data;
+
+ icnss_pr_dbg("Received Pin Connect Result Indication\n");
+
+ if (!txn) {
+ pr_err("spurious indication\n");
+ return;
+ }
+
+ if (ind_msg->pwr_pin_result_valid)
+ priv->pwr_pin_result = ind_msg->pwr_pin_result;
+ if (ind_msg->phy_io_pin_result_valid)
+ priv->phy_io_pin_result = ind_msg->phy_io_pin_result;
+ if (ind_msg->rf_pin_result_valid)
+ priv->rf_pin_result = ind_msg->rf_pin_result;
+
+ icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
+ ind_msg->pwr_pin_result, ind_msg->phy_io_pin_result,
+ ind_msg->rf_pin_result);
+ priv->stats.pin_connect_result++;
+}
+
+static void rejuvenate_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+ const struct wlfw_rejuvenate_ind_msg_v01 *ind_msg = data;
+
+ icnss_pr_dbg("Received Rejuvenate Indication\n");
+
+ if (!txn) {
+ pr_err("spurious indication\n");
+ return;
+ }
+
+ icnss_ignore_fw_timeout(true);
+
+ if (ind_msg->cause_for_rejuvenation_valid)
+ priv->cause_for_rejuvenation = ind_msg->cause_for_rejuvenation;
+ else
+ priv->cause_for_rejuvenation = 0;
+ if (ind_msg->requesting_sub_system_valid)
+ priv->requesting_sub_system = ind_msg->requesting_sub_system;
+ else
+ priv->requesting_sub_system = 0;
+ if (ind_msg->line_number_valid)
+ priv->line_number = ind_msg->line_number;
+ else
+ priv->line_number = 0;
+ if (ind_msg->function_name_valid)
+ memcpy(priv->function_name, ind_msg->function_name,
+ QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
+ else
+ memset(priv->function_name, 0,
+ QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
+
+ icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
+ priv->cause_for_rejuvenation,
+ priv->requesting_sub_system,
+ priv->line_number,
+ priv->function_name);
+
+ priv->stats.rejuvenate_ind++;
+
+ icnss_handle_rejuvenate(priv);
+}
+
+static void cal_done_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ icnss_pr_dbg("Received QMI WLFW calibration done indication\n");
+
+ if (!txn) {
+ icnss_pr_err("Spurious indication\n");
+ return;
+ }
+}
+
+static void fw_init_done_ind_cb(struct qmi_handle *qmi,
+ struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+
+ icnss_pr_dbg("Received QMI WLFW FW initialization done indication\n");
+
+ if (!txn) {
+ icnss_pr_err("Spurious indication\n");
+ return;
+ }
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
+ 0, NULL);
+}
+
+static struct qmi_msg_handler wlfw_msg_handlers[] = {
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_FW_READY_IND_V01,
+ .ei = wlfw_fw_ready_ind_msg_v01_ei,
+ .decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01),
+ .fn = fw_ready_ind_cb
+ },
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_MSA_READY_IND_V01,
+ .ei = wlfw_msa_ready_ind_msg_v01_ei,
+ .decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01),
+ .fn = msa_ready_ind_cb
+ },
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01,
+ .ei = wlfw_pin_connect_result_ind_msg_v01_ei,
+ .decoded_size =
+ sizeof(struct wlfw_pin_connect_result_ind_msg_v01),
+ .fn = pin_connect_result_ind_cb
+ },
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_REJUVENATE_IND_V01,
+ .ei = wlfw_rejuvenate_ind_msg_v01_ei,
+ .decoded_size = sizeof(struct wlfw_rejuvenate_ind_msg_v01),
+ .fn = rejuvenate_ind_cb
+ },
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_CAL_DONE_IND_V01,
+ .ei = wlfw_cal_done_ind_msg_v01_ei,
+ .decoded_size = sizeof(struct wlfw_cal_done_ind_msg_v01),
+ .fn = cal_done_ind_cb
+ },
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_FW_INIT_DONE_IND_V01,
+ .ei = wlfw_fw_init_done_ind_msg_v01_ei,
+ .decoded_size = sizeof(struct wlfw_fw_init_done_ind_msg_v01),
+ .fn = fw_init_done_ind_cb
+ },
+ {}
+};
+
+int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data)
+{
+ struct icnss_event_server_arrive_data *event_data = data;
+ struct qmi_handle *qmi = &priv->qmi;
+ struct sockaddr_qrtr sq = { 0 };
+ int ret = 0;
+
+ if (!priv) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ sq.sq_family = AF_QIPCRTR;
+ sq.sq_node = event_data->node;
+ sq.sq_port = event_data->port;
+ ret = kernel_connect(qmi->sock, (struct sockaddr *)&sq, sizeof(sq), 0);
+ if (ret < 0) {
+ icnss_pr_err("Fail to connect to remote service port\n");
+ goto out;
+ }
+
+ icnss_pr_info("QMI Server Connected: state: 0x%lx\n", priv->state);
+
+ kfree(data);
+ return 0;
+
+out:
+ kfree(data);
+ ICNSS_ASSERT(0);
+ return ret;
+}
+
+int icnss_clear_server(struct icnss_priv *priv)
+{
+ if (!priv)
+ return -ENODEV;
+
+ icnss_pr_info("QMI Service Disconnected: 0x%lx\n", priv->state);
+ clear_bit(ICNSS_WLFW_CONNECTED, &priv->state);
+
+ return 0;
+}
+
+static int wlfw_new_server(struct qmi_handle *qmi,
+ struct qmi_service *service)
+{
+ struct icnss_priv *priv =
+ container_of(qmi, struct icnss_priv, qmi);
+ struct icnss_event_server_arrive_data *event_data;
+
+ icnss_pr_dbg("WLFW server arrive: node %u port %u\n",
+ service->node, service->port);
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+ if (event_data == NULL)
+ return -ENOMEM;
+
+ event_data->node = service->node;
+ event_data->port = service->port;
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
+ 0, event_data);
+
+ return 0;
+}
+
+static void wlfw_del_server(struct qmi_handle *qmi,
+ struct qmi_service *service)
+{
+ struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+
+ icnss_pr_dbg("WLFW server delete\n");
+
+ if (priv) {
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_fw_timeout(true);
+ }
+
+ icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_SERVER_EXIT,
+ 0, NULL);
+}
+
+static struct qmi_ops wlfw_qmi_ops = {
+ .new_server = wlfw_new_server,
+ .del_server = wlfw_del_server,
+};
+
+int icnss_register_fw_service(struct icnss_priv *priv)
+{
+ int ret;
+
+ ret = qmi_handle_init(&priv->qmi,
+ WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
+ &wlfw_qmi_ops, wlfw_msg_handlers);
+ if (ret < 0)
+ return ret;
+
+ if (priv->device_id == WCN6750_DEVICE_ID)
+ ret = qmi_add_lookup(&priv->qmi, WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_WCN_INS_ID_V01);
+ else
+ ret = qmi_add_lookup(&priv->qmi, WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01);
+ return ret;
+}
+
+void icnss_unregister_fw_service(struct icnss_priv *priv)
+{
+ qmi_handle_release(&priv->qmi);
+}
+
+int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+ struct icnss_wlan_enable_cfg *config,
+ enum icnss_driver_mode mode,
+ const char *host_version)
+{
+ struct wlfw_wlan_cfg_req_msg_v01 req;
+ u32 i;
+ int ret;
+
+ icnss_pr_dbg("Mode: %d, config: %pK, host_version: %s\n",
+ mode, config, host_version);
+
+ memset(&req, 0, sizeof(req));
+
+ if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
+ goto skip;
+
+ if (!config || !host_version) {
+ icnss_pr_err("Invalid cfg pointer, config: %pK, host_version: %pK\n",
+ config, host_version);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ req.host_version_valid = 1;
+ strlcpy(req.host_version, host_version,
+ WLFW_MAX_STR_LEN + 1);
+
+ req.tgt_cfg_valid = 1;
+ if (config->num_ce_tgt_cfg > WLFW_MAX_NUM_CE)
+ req.tgt_cfg_len = WLFW_MAX_NUM_CE;
+ else
+ req.tgt_cfg_len = config->num_ce_tgt_cfg;
+ for (i = 0; i < req.tgt_cfg_len; i++) {
+ req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
+ req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
+ req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
+ req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
+ req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
+ }
+
+ req.svc_cfg_valid = 1;
+ if (config->num_ce_svc_pipe_cfg > WLFW_MAX_NUM_SVC)
+ req.svc_cfg_len = WLFW_MAX_NUM_SVC;
+ else
+ req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
+ for (i = 0; i < req.svc_cfg_len; i++) {
+ req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
+ req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
+ req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
+ }
+
+ if (priv->device_id == WCN6750_DEVICE_ID) {
+ req.shadow_reg_v2_valid = 1;
+ if (config->num_shadow_reg_cfg >
+ QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01)
+ req.shadow_reg_v2_len =
+ QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01;
+ else
+ req.shadow_reg_v2_len = config->num_shadow_reg_cfg;
+
+ memcpy(req.shadow_reg_v2, config->shadow_reg_cfg,
+ sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) *
+ req.shadow_reg_v2_len);
+ } else if (priv->device_id == ADRASTEA_DEVICE_ID) {
+ req.shadow_reg_valid = 1;
+ if (config->num_shadow_reg_cfg >
+ QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
+ req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
+ else
+ req.shadow_reg_len = config->num_shadow_reg_cfg;
+
+ memcpy(req.shadow_reg, config->shadow_reg_cfg,
+ sizeof(struct wlfw_msi_cfg_s_v01) * req.shadow_reg_len);
+ }
+
+ ret = wlfw_wlan_cfg_send_sync_msg(priv, &req);
+ if (ret)
+ goto out;
+skip:
+ ret = wlfw_wlan_mode_send_sync_msg(priv,
+ (enum wlfw_driver_mode_enum_v01)mode);
+out:
+ if (test_bit(SKIP_QMI, &priv->ctrl_params.quirks))
+ ret = 0;
+
+ return ret;
+}
+
+int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
+{
+ enum wlfw_driver_mode_enum_v01 mode = QMI_WLFW_OFF_V01;
+
+ return wlfw_wlan_mode_send_sync_msg(priv, mode);
+}
+
+int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv)
+{
+ int ret;
+ struct wlfw_vbatt_req_msg_v01 *req;
+ struct wlfw_vbatt_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+
+ if (!priv)
+ return -ENODEV;
+
+ if (test_bit(ICNSS_FW_DOWN, &priv->state))
+ return -EINVAL;
+
+ icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n", priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ priv->stats.vbatt_req++;
+
+ req->voltage_uv = voltage_uv;
+
+ ret = qmi_txn_init(&priv->qmi, &txn, wlfw_vbatt_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Fail to init txn for Vbatt message resp %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_VBATT_REQ_V01,
+ WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_vbatt_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Fail to send Vbatt message req %d\n", ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("VBATT message resp wait failed with ret %d\n",
+ ret);
+ goto out;
+ } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI Vbatt message request rejected, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ priv->stats.vbatt_resp++;
+
+ kfree(resp);
+ kfree(req);
+ return 0;
+
+out:
+ kfree(resp);
+ kfree(req);
+ priv->stats.vbatt_req_err++;
+ return ret;
+}
+
+int wlfw_host_cap_send_sync(struct icnss_priv *priv)
+{
+ struct wlfw_host_cap_req_msg_v01 *req;
+ struct wlfw_host_cap_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+ int ret = 0;
+
+ icnss_pr_dbg("Sending host capability message, state: 0x%lx\n",
+ priv->state);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->num_clients_valid = 1;
+ if (test_bit(ENABLE_DAEMON_SUPPORT,
+ &priv->ctrl_params.quirks))
+ req->num_clients = 2;
+ else
+ req->num_clients = 1;
+ icnss_pr_dbg("Number of clients is %d\n", req->num_clients);
+
+ req->bdf_support_valid = 1;
+ req->bdf_support = 1;
+
+ req->cal_done_valid = 1;
+ req->cal_done = priv->cal_done;
+ icnss_pr_dbg("Calibration done is %d\n", priv->cal_done);
+
+ ret = qmi_txn_init(&priv->qmi, &txn,
+ wlfw_host_cap_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ icnss_pr_err("Failed to initialize txn for host capability request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&priv->qmi, NULL, &txn,
+ QMI_WLFW_HOST_CAP_REQ_V01,
+ WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_host_cap_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ icnss_pr_err("Failed to send host capability request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+ if (ret < 0) {
+ icnss_pr_err("Failed to wait for response of host capability request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("Host capability request failed, result: %d, err: %d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ kfree(req);
+ kfree(resp);
+ return 0;
+
+out:
+ ICNSS_ASSERT(0);
+ kfree(req);
+ kfree(resp);
+ return ret;
+}
diff --git a/drivers/soc/qcom/icnss2/qmi.h b/drivers/soc/qcom/icnss2/qmi.h
new file mode 100644
index 0000000..964579b
--- /dev/null
+++ b/drivers/soc/qcom/icnss2/qmi.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __ICNSS_QMI_H__
+#define __ICNSS_QMI_H__
+
+#ifndef CONFIG_ICNSS2_QMI
+
+static inline int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int icnss_connect_to_fw_server(struct icnss_priv *priv,
+ void *data)
+{
+ return 0;
+}
+static inline int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int wlfw_dynamic_feature_mask_send_sync_msg(
+ struct icnss_priv *priv, uint64_t dynamic_feature_mask)
+{
+ return 0;
+}
+static inline int icnss_clear_server(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline void icnss_ignore_fw_timeout(bool ignore) {}
+static int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int wlfw_ini_send_sync_msg(struct icnss_priv *priv,
+ uint8_t fw_log_mode)
+{
+ return 0;
+}
+static inline int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ return 0;
+}
+static inline int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ return 0;
+}
+static inline int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+ enum icnss_driver_mode mode)
+{
+ return 0;
+}
+static int wlfw_host_cap_send_sync(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+ struct icnss_wlan_enable_cfg *config,
+ enum icnss_driver_mode mode,
+ const char *host_version)
+{
+ return 0;
+}
+static inline int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline int icnss_register_fw_service(struct icnss_priv *priv)
+{
+ return 0;
+}
+static inline void icnss_unregister_fw_service(struct icnss_priv *priv) {}
+static inline int icnss_send_vbatt_update(struct icnss_priv *priv,
+ uint64_t voltage_uv)
+{
+ return 0;
+}
+
+static inline int wlfw_device_info_send_msg(struct icnss_priv *priv)
+{
+ return 0;
+}
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+ enum wlfw_driver_mode_enum_v01 mode)
+{
+ return 0;
+}
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type)
+{
+ return 0;
+}
+#else
+int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv);
+int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data);
+int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv);
+int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv);
+int wlfw_cap_send_sync_msg(struct icnss_priv *priv);
+int icnss_qmi_pin_connect_result_ind(struct icnss_priv *priv,
+ void *msg, unsigned int msg_len);
+int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
+ uint64_t dynamic_feature_mask);
+int icnss_clear_server(struct icnss_priv *priv);
+int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv);
+void icnss_ignore_fw_timeout(bool ignore);
+int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv);
+int wlfw_ini_send_sync_msg(struct icnss_priv *priv, uint8_t fw_log_mode);
+int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data);
+int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data);
+int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+ struct icnss_wlan_enable_cfg *config,
+ enum icnss_driver_mode mode,
+ const char *host_version);
+int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv);
+int icnss_register_fw_service(struct icnss_priv *priv);
+void icnss_unregister_fw_service(struct icnss_priv *priv);
+int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv);
+int wlfw_host_cap_send_sync(struct icnss_priv *priv);
+int wlfw_device_info_send_msg(struct icnss_priv *priv);
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+ enum wlfw_driver_mode_enum_v01 mode);
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type);
+#endif
+
+#endif /* __ICNSS_QMI_H__*/
diff --git a/drivers/soc/qcom/icnss_qmi.c b/drivers/soc/qcom/icnss_qmi.c
index 55e0eea..8a59ff5 100644
--- a/drivers/soc/qcom/icnss_qmi.c
+++ b/drivers/soc/qcom/icnss_qmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "icnss_qmi: " fmt
@@ -482,9 +482,14 @@ int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
icnss_qmi_fatal_err("Mode resp wait failed with ret %d\n", ret);
goto out;
} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ ret = -resp->resp.result;
+ if (resp->resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED) {
+ icnss_pr_err("QMI Mode req rejected as CCPM init failed, result:%d error:%d\n",
+ resp->resp.result, resp->resp.error);
+ goto out;
+ }
icnss_qmi_fatal_err("QMI Mode request rejected, result:%d error:%d\n",
resp->resp.result, resp->resp.error);
- ret = -resp->resp.result;
goto out;
}
diff --git a/drivers/soc/qcom/l2_reuse.c b/drivers/soc/qcom/l2_reuse.c
index 622e914..e20b49a 100644
--- a/drivers/soc/qcom/l2_reuse.c
+++ b/drivers/soc/qcom/l2_reuse.c
@@ -11,7 +11,7 @@
#include <linux/sysfs.h>
#include <linux/kobject.h>
-#define L2_REUSE_SMC_ID 0x02001F01
+#define L2_REUSE_SMC_ID 0x00200090C
static bool l2_reuse_enable = true;
static struct kobject *l2_reuse_kobj;
diff --git a/drivers/soc/qcom/llcc-lagoon.c b/drivers/soc/qcom/llcc-lagoon.c
new file mode 100644
index 0000000..21c0e7c
--- /dev/null
+++ b/drivers/soc/qcom/llcc-lagoon.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+/*
+ * SCT entry contains of the following parameters
+ * uid: Unique id for the client's use case
+ * slice_id: llcc slice id for each client
+ * max_cap: The maximum capacity of the cache slice provided in KB
+ * priority: Priority of the client used to select victim line for replacement
+ * fixed_size: Determine of the slice has a fixed capacity
+ * bonus_ways: Bonus ways to be used by any slice, bonus way is used only if
+ * it't not a reserved way.
+ * res_ways: Reserved ways for the cache slice, the reserved ways cannot be used
+ * by any other client than the one its assigned to.
+ * cache_mode: Each slice operates as a cache, this controls the mode of the
+ * slice normal or TCM
+ * probe_target_ways: Determines what ways to probe for access hit. When
+ * configured to 1 only bonus and reseved ways are probed.
+ * when configured to 0 all ways in llcc are probed.
+ * dis_cap_alloc: Disable capacity based allocation for a client
+ * write_scid_en: Bit enables write cache support for a given scid.
+ * retain_on_pc: If this bit is set and client has maitained active vote
+ * then the ways assigned to this client are not flushed on power
+ * collapse.
+ * activate_on_init: Activate the slice immidiately after the SCT is programmed
+ */
+#define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, wse, rp, a) \
+ { \
+ .usecase_id = uid, \
+ .slice_id = sid, \
+ .max_cap = mc, \
+ .priority = p, \
+ .fixed_size = fs, \
+ .bonus_ways = bway, \
+ .res_ways = rway, \
+ .cache_mode = cmod, \
+ .probe_target_ways = ptw, \
+ .dis_cap_alloc = dca, \
+ .write_scid_en = wse, \
+ .retain_on_pc = rp, \
+ .activate_on_init = a, \
+ }
+
+static struct llcc_slice_config lagoon_data[] = {
+ SCT_ENTRY(LLCC_CPUSS, 1, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1),
+ SCT_ENTRY(LLCC_MDM, 8, 256, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0),
+ SCT_ENTRY(LLCC_GPUHTW, 11, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0),
+ SCT_ENTRY(LLCC_GPU, 12, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0),
+ SCT_ENTRY(LLCC_MDMPNG, 21, 768, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0),
+ SCT_ENTRY(LLCC_NPU, 23, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0),
+};
+
+static int lagoon_qcom_llcc_probe(struct platform_device *pdev)
+{
+ return qcom_llcc_probe(pdev, lagoon_data,
+ ARRAY_SIZE(lagoon_data));
+}
+
+static const struct of_device_id lagoon_qcom_llcc_of_match[] = {
+ { .compatible = "lagoon-llcc-v1", },
+ { },
+};
+
+static struct platform_driver lagoon_qcom_llcc_driver = {
+ .driver = {
+ .name = "lagoon-llcc",
+ .of_match_table = lagoon_qcom_llcc_of_match,
+ },
+ .probe = lagoon_qcom_llcc_probe,
+};
+module_platform_driver(lagoon_qcom_llcc_driver);
+
+MODULE_DESCRIPTION("QCOM lagoon LLCC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index 1f5cc0b..59d164b 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, 2019-2020, The Linux Foundation. All rights reserved.
*/
#include <asm/cacheflush.h>
@@ -28,6 +28,7 @@
#define INPUT_DATA_BY_HLOS 0x00C0FFEE
#define FORMAT_VERSION_1 0x1
+#define FORMAT_VERSION_2 0x2
#define CORE_REG_NUM_DEFAULT 0x1
#define MAGIC_INDEX 0
@@ -37,6 +38,20 @@
#define PERCORE_INDEX 4
#define SYSTEM_REGS_INPUT_INDEX 5
+#define CMD_REPEAT_READ (0x2 << 24)
+#define CMD_DELAY (0x1 << 24)
+#define CMD_READ 0x0
+#define CMD_READ_WORD 0x1
+#define CMD_WRITE 0x2
+#define CMD_EXTRA 0x3
+
+#define CMD_MASK 0x3
+#define OFFSET_MASK GENMASK(31, 2)
+#define EXTRA_CMD_MASK GENMASK(31, 24)
+#define EXTRA_VALUE_MASK GENMASK(23, 0)
+#define MAX_EXTRA_VALUE 0xffffff
+
+
struct cpuss_dump_data {
void *dump_vaddr;
u32 size;
@@ -119,7 +134,7 @@ static int update_reg_dump_table(struct device *dev, u32 core_reg_num)
memset(cpudata->dump_vaddr, 0xDE, cpudata->size);
p = (struct reg_dump_data *)cpudata->dump_vaddr;
p->magic = INPUT_DATA_BY_HLOS;
- p->version = FORMAT_VERSION_1;
+ p->version = FORMAT_VERSION_2;
p->system_regs_input_index = system_regs_input_index;
p->regdump_output_byte_offset = regdump_output_byte_offset;
memset((uint32_t *)cpudata->dump_vaddr + PERCORE_INDEX, 0x0,
@@ -197,9 +212,8 @@ static ssize_t register_config_show(struct device *dev,
char local_buf[64];
int len = 0, count = 0;
int index, system_index_start, index_end;
- uint32_t register_offset, length_in_bytes;
- uint32_t length_in_words;
- uint32_t *p;
+ uint32_t register_offset, val;
+ uint32_t *p, cmd;
struct cpuss_dump_data *cpudata = dev_get_drvdata(dev);
buf[0] = '\0';
@@ -212,7 +226,7 @@ static ssize_t register_config_show(struct device *dev,
p = (uint32_t *)cpudata->dump_vaddr;
/* print per-core & system registers */
- len = snprintf(local_buf, 64, "per-core registers:\n");
+ len = scnprintf(local_buf, 64, "per-core registers:\n");
strlcat(buf, local_buf, PAGE_SIZE);
count += len;
@@ -221,7 +235,7 @@ static ssize_t register_config_show(struct device *dev,
cpudata->sys_reg_size / sizeof(uint32_t) + 1;
for (index = PERCORE_INDEX; index < index_end;) {
if (index == system_index_start) {
- len = snprintf(local_buf, 64, "system registers:\n");
+ len = scnprintf(local_buf, 64, "system registers:\n");
if ((count + len) > PAGE_SIZE) {
dev_err(dev, "Couldn't write complete config\n");
break;
@@ -237,19 +251,44 @@ static ssize_t register_config_show(struct device *dev,
continue;
}
- if (register_offset & 0x3) {
- length_in_words = register_offset & 0x3;
- length_in_bytes = length_in_words << 2;
- len = snprintf(local_buf, 64,
- "Index: 0x%x, addr: 0x%x\n",
- index, register_offset);
- index++;
- } else {
- length_in_bytes = *(p + index + 1);
- len = snprintf(local_buf, 64,
- "Index: 0x%x, addr: 0x%x, length: 0x%x\n",
- index, register_offset, length_in_bytes);
+ cmd = register_offset & CMD_MASK;
+ register_offset &= OFFSET_MASK;
+
+ switch (cmd) {
+ case CMD_READ:
+ val = *(p + index + 1);
+ len = scnprintf(local_buf, 64,
+ "0x%x, 0x%x, r\n",
+ register_offset, val);
index += 2;
+ break;
+ case CMD_READ_WORD:
+ len = scnprintf(local_buf, 64,
+ "0x%x, 0x%x, r\n",
+ register_offset, 0x4);
+ index++;
+ break;
+ case CMD_WRITE:
+ val = *(p + index + 1);
+ len = scnprintf(local_buf, 64,
+ "0x%x, 0x%x, w\n",
+ register_offset, val);
+ index += 2;
+ break;
+ case CMD_EXTRA:
+ val = *(p + index + 1);
+ cmd = val & EXTRA_CMD_MASK;
+ val &= EXTRA_VALUE_MASK;
+ if (cmd == CMD_DELAY)
+ len = scnprintf(local_buf, 64,
+ "0x%x, 0x%x, d\n",
+ register_offset, val);
+ else
+ len = scnprintf(local_buf, 64,
+ "0x%x, 0x%x, R\n",
+ register_offset, val);
+ index += 2;
+ break;
}
if ((count + len) > PAGE_SIZE) {
@@ -265,6 +304,49 @@ static ssize_t register_config_show(struct device *dev,
return count;
}
+static int config_cpuss_register(struct device *dev,
+ uint32_t *p, uint32_t index, char cmd,
+ uint32_t register_offset, uint32_t val)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case 'r':
+ if (val > 4) {
+ *(p + index) = register_offset;
+ *(p + index + 1) = val;
+ } else {
+ *(p + index) = register_offset | CMD_READ_WORD;
+ }
+ break;
+ case 'R':
+ if (val > MAX_EXTRA_VALUE) {
+ dev_err(dev, "repeat read time exceeded the limit\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ *(p + index) = register_offset | CMD_EXTRA;
+ *(p + index + 1) = val | CMD_REPEAT_READ;
+ break;
+ case 'd':
+ if (val > MAX_EXTRA_VALUE) {
+ dev_err(dev, "sleep time exceeded the limit\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ *(p + index) = CMD_EXTRA;
+ *(p + index + 1) = val | CMD_DELAY;
+ break;
+ case 'w':
+ *(p + index) = register_offset | CMD_WRITE;
+ *(p + index + 1) = val;
+ break;
+ default:
+ dev_err(dev, "Don't support this command\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
/**
* This function sets configs of per-core or system registers.
*/
@@ -273,9 +355,9 @@ static ssize_t register_config_store(struct device *dev,
const char *buf, size_t size)
{
int ret;
- uint32_t register_offset, length_in_bytes, per_core = 0;
- uint32_t length_in_words;
+ uint32_t register_offset, val, reserve_size = 4, per_core = 0;
int nval;
+ char cmd;
uint32_t num_cores;
u32 extra_memory;
u32 used_memory;
@@ -283,29 +365,37 @@ static ssize_t register_config_store(struct device *dev,
uint32_t *p;
struct cpuss_dump_data *cpudata = dev_get_drvdata(dev);
- nval = sscanf(buf, "%x %x %u", ®ister_offset,
- &length_in_bytes, &per_core);
- if (nval != 2 && nval != 3)
+ nval = sscanf(buf, "%x %x %c %u", ®ister_offset,
+ &val, &cmd, &per_core);
+ if (nval < 2)
return -EINVAL;
+ if (nval == 2)
+ cmd = 'r';
if (per_core > 1)
return -EINVAL;
if (register_offset & 0x3) {
dev_err(dev, "Invalid address, must be 4 byte aligned\n");
return -EINVAL;
}
- if (length_in_bytes & 0x3) {
- dev_err(dev, "Invalid length, must be 4 byte aligned\n");
- return -EINVAL;
- }
- if (length_in_bytes == 0) {
- dev_err(dev, "Invalid length of 0\n");
- return -EINVAL;
+
+ if (cmd == 'r' || cmd == 'R') {
+ if (val == 0) {
+ dev_err(dev, "Invalid length of 0\n");
+ return -EINVAL;
+ }
+ if (cmd == 'r' && val & 0x3) {
+ dev_err(dev, "Invalid length, must be 4 byte aligned\n");
+ return -EINVAL;
+ }
+ if (cmd == 'R')
+ reserve_size = val * 4;
+ else
+ reserve_size = val;
}
mutex_lock(&cpudata->mutex);
p = (uint32_t *)cpudata->dump_vaddr;
- length_in_words = length_in_bytes >> 2;
if (per_core) { /* per-core register */
if (cpudata->core_reg_used_num == cpudata->core_reg_num) {
dev_err(dev, "Couldn't add per-core config, out of range\n");
@@ -314,26 +404,25 @@ static ssize_t register_config_store(struct device *dev,
}
num_cores = num_possible_cpus();
- extra_memory = length_in_bytes * num_cores;
+ extra_memory = reserve_size * num_cores;
used_memory = cpudata->used_memory + extra_memory;
- if (extra_memory / num_cores < length_in_bytes ||
- used_memory > cpudata->size ||
- used_memory < cpudata->used_memory) {
+ if (extra_memory / num_cores < reserve_size ||
+ used_memory > cpudata->size ||
+ used_memory < cpudata->used_memory) {
dev_err(dev, "Couldn't add per-core reg config, no enough memory\n");
ret = -ENOMEM;
goto err;
}
- if (length_in_words > 3) {
- *(p + cpudata->core_reg_end_index) = register_offset;
- *(p + cpudata->core_reg_end_index + 1) =
- length_in_bytes;
- cpudata->core_reg_end_index += 2;
- } else {
- *(p + cpudata->core_reg_end_index) = register_offset |
- length_in_words;
+ ret = config_cpuss_register(dev, p, cpudata->core_reg_end_index,
+ cmd, register_offset, val);
+ if (ret)
+ goto err;
+
+ if (cmd == 'r' && val == 4)
cpudata->core_reg_end_index++;
- }
+ else
+ cpudata->core_reg_end_index += 2;
cpudata->core_reg_used_num++;
cpudata->used_memory = used_memory;
@@ -341,40 +430,34 @@ static ssize_t register_config_store(struct device *dev,
system_reg_end_index = *(p + SYS_REG_INPUT_INDEX) +
cpudata->sys_reg_size / sizeof(uint32_t);
- if (length_in_words > 3) {
- extra_memory = sizeof(uint32_t) * 2 + length_in_bytes;
- used_memory = cpudata->used_memory + extra_memory;
- if (extra_memory < length_in_bytes ||
- used_memory > cpudata->size ||
- used_memory < cpudata->used_memory) {
- dev_err(dev, "Couldn't add system reg config, no enough memory\n");
- ret = -ENOMEM;
- goto err;
- }
+ if (cmd == 'r' && reserve_size == 4)
+ extra_memory = sizeof(uint32_t) + reserve_size;
+ else
+ extra_memory = sizeof(uint32_t) * 2 + reserve_size;
- *(p + system_reg_end_index) = register_offset;
- *(p + system_reg_end_index + 1) = length_in_bytes;
- system_reg_end_index += 2;
- cpudata->sys_reg_size += sizeof(uint32_t) * 2;
- } else {
- extra_memory = sizeof(uint32_t) + length_in_bytes;
- used_memory = cpudata->used_memory + extra_memory;
- if (extra_memory < length_in_bytes ||
- used_memory > cpudata->size ||
- used_memory < cpudata->used_memory) {
- dev_err(dev, "Couldn't add system reg config, no enough memory\n");
- ret = -ENOMEM;
- goto err;
- }
+ used_memory = cpudata->used_memory + extra_memory;
+ if (extra_memory < reserve_size ||
+ used_memory > cpudata->size ||
+ used_memory < cpudata->used_memory) {
+ dev_err(dev, "Couldn't add system reg config, no enough memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
- *(p + system_reg_end_index) = register_offset |
- length_in_words;
+ ret = config_cpuss_register(dev, p, system_reg_end_index,
+ cmd, register_offset, val);
+ if (ret)
+ goto err;
+
+ if (cmd == 'r' && val == 4) {
system_reg_end_index++;
cpudata->sys_reg_size += sizeof(uint32_t);
+ } else {
+ system_reg_end_index += 2;
+ cpudata->sys_reg_size += sizeof(uint32_t) * 2;
}
cpudata->used_memory = used_memory;
-
*(p + system_reg_end_index) = 0x0;
*(p + OUTPUT_DUMP_INDEX) = (system_reg_end_index + 1)
* sizeof(uint32_t);
@@ -388,6 +471,24 @@ static ssize_t register_config_store(struct device *dev,
}
static DEVICE_ATTR_RW(register_config);
+static ssize_t format_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct reg_dump_data *p;
+ struct cpuss_dump_data *cpudata = dev_get_drvdata(dev);
+
+ if (!cpudata)
+ return -EFAULT;
+
+ mutex_lock(&cpudata->mutex);
+ p = (struct reg_dump_data *)cpudata->dump_vaddr;
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", p->version);
+
+ mutex_unlock(&cpudata->mutex);
+ return ret;
+}
+static DEVICE_ATTR_RO(format_version);
/**
* This function resets the register dump table.
*/
@@ -412,6 +513,7 @@ static const struct device_attribute *register_dump_attrs[] = {
&dev_attr_core_reg_num,
&dev_attr_register_config,
&dev_attr_register_reset,
+ &dev_attr_format_version,
NULL,
};
diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c
index a3e2070..87b72ed 100644
--- a/drivers/soc/qcom/minidump_log.c
+++ b/drivers/soc/qcom/minidump_log.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -32,6 +32,7 @@ static void __init register_log_buf(void)
md_entry.virt_addr = (uintptr_t) (*log_bufp);
md_entry.phys_addr = virt_to_phys(*log_bufp);
md_entry.size = *log_buf_lenp;
+ md_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&md_entry))
pr_err("Failed to add logbuf in Minidump\n");
}
@@ -42,6 +43,7 @@ static void register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size,
struct page *sp_page;
struct vm_struct *stack_vm_area = task_stack_vm_area(current);
+ ksp_entry->id = MINIDUMP_DEFAULT_ID;
ksp_entry->virt_addr = sp;
ksp_entry->size = size;
if (stack_vm_area) {
@@ -67,6 +69,7 @@ static void __init register_kernel_sections(void)
ksec_entry.virt_addr = (uintptr_t)_sdata;
ksec_entry.phys_addr = virt_to_phys(_sdata);
ksec_entry.size = roundup((__bss_stop - _sdata), 4);
+ ksec_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&ksec_entry))
pr_err("Failed to add data section in Minidump\n");
@@ -80,6 +83,7 @@ static void __init register_kernel_sections(void)
ksec_entry.virt_addr = (uintptr_t)start;
ksec_entry.phys_addr = per_cpu_ptr_to_phys(start);
ksec_entry.size = static_size;
+ ksec_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&ksec_entry))
pr_err("Failed to add percpu sections in Minidump\n");
}
@@ -152,6 +156,7 @@ void dump_stack_minidump(u64 sp)
ktsk_entry.virt_addr = (u64)current;
ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current);
ktsk_entry.size = sizeof(struct task_struct);
+ ktsk_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&ktsk_entry))
pr_err("Failed to add current task %d in Minidump\n", cpu);
}
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index feed3db2..ee89ffb 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -513,7 +513,7 @@ EXPORT_SYMBOL(geni_se_resources_on);
*/
int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
{
- unsigned long freq = 0;
+ long freq = 0;
int i;
if (se->clk_perf_tbl) {
@@ -529,7 +529,7 @@ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
freq = clk_round_rate(se->clk, freq + 1);
- if (!freq || freq == se->clk_perf_tbl[i - 1])
+ if (freq <= 0 || freq == se->clk_perf_tbl[i - 1])
break;
se->clk_perf_tbl[i] = freq;
}
@@ -544,16 +544,17 @@ EXPORT_SYMBOL(geni_se_clk_tbl_get);
* @se: Pointer to the concerned serial engine.
* @req_freq: Requested clock frequency.
* @index: Index of the resultant frequency in the table.
- * @res_freq: Resultant frequency which matches or is closer to the
- * requested frequency.
+ * @res_freq: Resultant frequency of the source clock.
* @exact: Flag to indicate exact multiple requirement of the requested
* frequency.
*
- * This function is called by the protocol drivers to determine the matching
- * or exact multiple of the requested frequency, as provided by the serial
- * engine clock in order to meet the performance requirements. If there is
- * no matching or exact multiple of the requested frequency found, then it
- * selects the closest floor frequency, if exact flag is not set.
+ * This function is called by the protocol drivers to determine the best match
+ * of the requested frequency as provided by the serial engine clock in order
+ * to meet the performance requirements.
+ *
+ * If we return success:
+ * - if @exact is true then @res_freq / <an_integer> == @req_freq
+ * - if @exact is false then @res_freq / <an_integer> <= @req_freq
*
* Return: 0 on success, standard Linux error codes on failure.
*/
@@ -564,6 +565,9 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
unsigned long *tbl;
int num_clk_levels;
int i;
+ unsigned long best_delta;
+ unsigned long new_delta;
+ unsigned int divider;
num_clk_levels = geni_se_clk_tbl_get(se, &tbl);
if (num_clk_levels < 0)
@@ -572,18 +576,21 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
if (num_clk_levels == 0)
return -EINVAL;
- *res_freq = 0;
+ best_delta = ULONG_MAX;
for (i = 0; i < num_clk_levels; i++) {
- if (!(tbl[i] % req_freq)) {
+ divider = DIV_ROUND_UP(tbl[i], req_freq);
+ new_delta = req_freq - tbl[i] / divider;
+ if (new_delta < best_delta) {
+ /* We have a new best! */
*index = i;
*res_freq = tbl[i];
- return 0;
- }
- if (!(*res_freq) || ((tbl[i] > *res_freq) &&
- (tbl[i] < req_freq))) {
- *index = i;
- *res_freq = tbl[i];
+ /* If the new best is exact then we're done */
+ if (new_delta == 0)
+ return 0;
+
+ /* Record how close we got */
+ best_delta = new_delta;
}
}
diff --git a/drivers/soc/qcom/qmi_rmnet.c b/drivers/soc/qcom/qmi_rmnet.c
index f12512e..4e20ba8 100644
--- a/drivers/soc/qcom/qmi_rmnet.c
+++ b/drivers/soc/qcom/qmi_rmnet.c
@@ -715,17 +715,19 @@ void qmi_rmnet_enable_all_flows(struct net_device *dev)
spin_lock_bh(&qos->qos_lock);
list_for_each_entry(bearer, &qos->bearer_head, list) {
- if (bearer->tx_off)
- continue;
- do_wake = !bearer->grant_size;
- bearer->grant_size = DEFAULT_GRANT;
- bearer->grant_thresh = qmi_rmnet_grant_per(DEFAULT_GRANT);
bearer->seq = 0;
bearer->ack_req = 0;
bearer->bytes_in_flight = 0;
bearer->tcp_bidir = false;
bearer->rat_switch = false;
+ if (bearer->tx_off)
+ continue;
+
+ do_wake = !bearer->grant_size;
+ bearer->grant_size = DEFAULT_GRANT;
+ bearer->grant_thresh = qmi_rmnet_grant_per(DEFAULT_GRANT);
+
if (do_wake)
dfc_bearer_flow_ctl(dev, bearer, qos);
}
@@ -806,11 +808,11 @@ static int qmi_rmnet_get_queue_sa(struct qos_info *qos, struct sk_buff *skb)
int ip_type;
int txq = DEFAULT_MQ_NUM;
- /* Put RS/NS in default mq */
+ /* Put NDP in default mq */
if (skb->protocol == htons(ETH_P_IPV6) &&
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6 &&
- (icmp6_hdr(skb)->icmp6_type == 133 ||
- icmp6_hdr(skb)->icmp6_type == 135)) {
+ icmp6_hdr(skb)->icmp6_type >= 133 &&
+ icmp6_hdr(skb)->icmp6_type <= 137) {
return DEFAULT_MQ_NUM;
}
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h
index 4b477cb..6d63e7d 100644
--- a/drivers/soc/qcom/rpmh-internal.h
+++ b/drivers/soc/qcom/rpmh-internal.h
@@ -1,9 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
-
#ifndef __RPM_INTERNAL_H__
#define __RPM_INTERNAL_H__
@@ -124,5 +123,5 @@ void rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable);
void rpmh_tx_done(const struct tcs_request *msg, int r);
-void rpmh_rsc_debug(struct rsc_drv *drv);
+void rpmh_rsc_debug(struct rsc_drv *drv, struct completion *compl);
#endif /* __RPM_INTERNAL_H__ */
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index 7537d18..ceedbff 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME
@@ -692,7 +692,7 @@ static void print_tcs_info(struct rsc_drv *drv, int tcs_id, unsigned long *accl)
}
}
-void rpmh_rsc_debug(struct rsc_drv *drv)
+void rpmh_rsc_debug(struct rsc_drv *drv, struct completion *compl)
{
struct irq_data *rsc_irq_data = irq_get_irq_data(drv->irq);
bool irq_sts;
@@ -718,6 +718,8 @@ void rpmh_rsc_debug(struct rsc_drv *drv)
irq_get_irqchip_state(drv->irq, IRQCHIP_STATE_PENDING, &irq_sts);
pr_warn("HW IRQ %lu is %s at GIC\n", rsc_irq_data->hwirq,
irq_sts ? "PENDING" : "NOT PENDING");
+ pr_warn("Completion is %s to finish\n",
+ completion_done(compl) ? "PENDING" : "NOT PENDING");
for_each_set_bit(i, &accl, ARRAY_SIZE(accl_str)) {
strlcat(str, accl_str[i], sizeof(str));
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index 82691f7..bda109f 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/atomic.h>
@@ -336,7 +336,7 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
if (!ret) {
- rpmh_rsc_debug(ctrlr_to_drv(ctrlr));
+ rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compl);
return -ETIMEDOUT;
}
@@ -477,7 +477,7 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
* the completion that we're going to free once
* we've returned from this function.
*/
- rpmh_rsc_debug(ctrlr_to_drv(ctrlr));
+ rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compls[i]);
ret = -ETIMEDOUT;
goto exit;
}
diff --git a/drivers/soc/qcom/rq_stats.c b/drivers/soc/qcom/rq_stats.c
index 4906d97..72e1bf1 100644
--- a/drivers/soc/qcom/rq_stats.c
+++ b/drivers/soc/qcom/rq_stats.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2010-2015, 2017, 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2015, 2017, 2019-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
@@ -21,7 +21,7 @@ static void def_work_fn(struct work_struct *work)
static ssize_t show_def_timer_ms(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- int64_t diff;
+ uint64_t diff;
unsigned int udiff;
diff = ktime_to_ns(ktime_get()) - rq_info.def_start_time;
diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c
index 8201946..ca3e229 100644
--- a/drivers/soc/qcom/smcinvoke.c
+++ b/drivers/soc/qcom/smcinvoke.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -17,6 +17,7 @@
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
#include <linux/kref.h>
+#include <linux/signal.h>
#include <soc/qcom/scm.h>
#include <asm/cacheflush.h>
@@ -142,7 +143,7 @@ static size_t g_max_cb_buf_size = SMCINVOKE_TZ_MIN_BUF_SIZE;
static long smcinvoke_ioctl(struct file *, unsigned int, unsigned long);
static int smcinvoke_open(struct inode *, struct file *);
static int smcinvoke_release(struct inode *, struct file *);
-static int destroy_cb_server(uint16_t);
+static int release_cb_server(uint16_t);
static const struct file_operations g_smcinvoke_fops = {
.owner = THIS_MODULE,
@@ -211,6 +212,7 @@ struct smcinvoke_server_info {
uint16_t server_id;
uint16_t state;
uint32_t txn_id;
+ struct kref ref_cnt;
wait_queue_head_t req_wait_q;
wait_queue_head_t rsp_wait_q;
size_t cb_buf_size;
@@ -246,6 +248,23 @@ struct smcinvoke_mem_obj {
struct list_head list;
};
+static void destroy_cb_server(struct kref *kref)
+{
+ struct smcinvoke_server_info *server = container_of(kref,
+ struct smcinvoke_server_info, ref_cnt);
+ if (server) {
+ hash_del(&server->hash);
+ kfree(server);
+ }
+}
+
+/*
+ * A separate find func is reqd mainly for couple of cases:
+ * next_cb_server_id_locked which checks if server id had been utilized or not.
+ * - It would be overhead if we do ref_cnt for this case
+ * smcinvoke_release: which is called when server is closed from userspace.
+ * - During server creation we init ref count, now put it back
+ */
static struct smcinvoke_server_info *find_cb_server_locked(uint16_t server_id)
{
struct smcinvoke_server_info *data = NULL;
@@ -257,6 +276,16 @@ static struct smcinvoke_server_info *find_cb_server_locked(uint16_t server_id)
return NULL;
}
+struct smcinvoke_server_info *get_cb_server_locked(uint16_t server_id)
+{
+ struct smcinvoke_server_info *server = find_cb_server_locked(server_id);
+
+ if (server)
+ kref_get(&server->ref_cnt);
+
+ return server;
+}
+
static uint16_t next_cb_server_id_locked(void)
{
if (g_last_cb_server_id == CBOBJ_SERVER_ID_END)
@@ -389,19 +418,18 @@ static void free_pending_cbobj_locked(struct kref *kref)
list_del(&obj->list);
server = obj->server;
kfree(obj);
- if ((server->state == SMCINVOKE_SERVER_STATE_DEFUNCT) &&
- list_empty(&server->pending_cbobjs)) {
- hash_del(&server->hash);
- kfree(server);
- }
+ if (server)
+ kref_put(&server->ref_cnt, destroy_cb_server);
}
static int get_pending_cbobj_locked(uint16_t srvr_id, int16_t obj_id)
{
+ int ret = 0;
+ bool release_server = true;
struct list_head *head = NULL;
struct smcinvoke_cbobj *cbobj = NULL;
struct smcinvoke_cbobj *obj = NULL;
- struct smcinvoke_server_info *server = find_cb_server_locked(srvr_id);
+ struct smcinvoke_server_info *server = get_cb_server_locked(srvr_id);
if (!server)
return OBJECT_ERROR_BADOBJ;
@@ -410,38 +438,50 @@ static int get_pending_cbobj_locked(uint16_t srvr_id, int16_t obj_id)
list_for_each_entry(cbobj, head, list)
if (cbobj->cbobj_id == obj_id) {
kref_get(&cbobj->ref_cnt);
- return 0;
+ goto out;
}
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
- if (!obj)
- return OBJECT_ERROR_KMEM;
+ if (!obj) {
+ ret = OBJECT_ERROR_KMEM;
+ goto out;
+ }
obj->cbobj_id = obj_id;
kref_init(&obj->ref_cnt);
obj->server = server;
+ /*
+ * we are holding server ref in cbobj; we will
+ * release server ref when cbobj is destroyed
+ */
+ release_server = false;
list_add_tail(&obj->list, head);
-
- return 0;
+out:
+ if (release_server)
+ kref_put(&server->ref_cnt, destroy_cb_server);
+ return ret;
}
static int put_pending_cbobj_locked(uint16_t srvr_id, int16_t obj_id)
{
+ int ret = -EINVAL;
struct smcinvoke_server_info *srvr_info =
- find_cb_server_locked(srvr_id);
+ get_cb_server_locked(srvr_id);
struct list_head *head = NULL;
struct smcinvoke_cbobj *cbobj = NULL;
if (!srvr_info)
- return -EINVAL;
+ return ret;
head = &srvr_info->pending_cbobjs;
list_for_each_entry(cbobj, head, list)
if (cbobj->cbobj_id == obj_id) {
kref_put(&cbobj->ref_cnt, free_pending_cbobj_locked);
- return 0;
+ ret = 0;
+ break;
}
- return -EINVAL;
+ kref_put(&srvr_info->ref_cnt, destroy_cb_server);
+ return ret;
}
static int release_tzhandle_locked(int32_t tzhandle)
@@ -826,9 +866,17 @@ static void process_kernel_obj(void *buf, size_t buf_len)
{
struct smcinvoke_tzcb_req *cb_req = buf;
- cb_req->result = (cb_req->hdr.op == OBJECT_OP_MAP_REGION) ?
- smcinvoke_map_mem_region(buf, buf_len) :
- OBJECT_ERROR_INVALID;
+ switch (cb_req->hdr.op) {
+ case OBJECT_OP_MAP_REGION:
+ cb_req->result = smcinvoke_map_mem_region(buf, buf_len);
+ break;
+ case OBJECT_OP_YIELD:
+ cb_req->result = OBJECT_OK;
+ break;
+ default:
+ cb_req->result = OBJECT_ERROR_INVALID;
+ break;
+ }
}
static void process_mem_obj(void *buf, size_t buf_len)
@@ -896,7 +944,7 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp)
kref_init(&cb_txn->ref_cnt);
mutex_lock(&g_smcinvoke_lock);
- srvr_info = find_cb_server_locked(
+ srvr_info = get_cb_server_locked(
TZHANDLE_GET_SERVER(cb_req->hdr.tzhandle));
if (!srvr_info || srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT) {
/* ret equals Object_ERROR_DEFUNCT, at this point go to out */
@@ -937,6 +985,8 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp)
}
memcpy(buf, cb_req, buf_len);
kref_put(&cb_txn->ref_cnt, delete_cb_txn);
+ if (srvr_info)
+ kref_put(&srvr_info->ref_cnt, destroy_cb_server);
mutex_unlock(&g_smcinvoke_lock);
}
@@ -1417,6 +1467,7 @@ static long process_server_req(struct file *filp, unsigned int cmd,
if (!server_info)
return -ENOMEM;
+ kref_init(&server_info->ref_cnt);
init_waitqueue_head(&server_info->req_wait_q);
init_waitqueue_head(&server_info->rsp_wait_q);
server_info->cb_buf_size = server_req.cb_buf_size;
@@ -1437,7 +1488,7 @@ static long process_server_req(struct file *filp, unsigned int cmd,
server_info->server_id, &server_fd);
if (ret)
- destroy_cb_server(server_info->server_id);
+ release_cb_server(server_info->server_id);
return server_fd;
}
@@ -1446,6 +1497,7 @@ static long process_accept_req(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = -1;
+ sigset_t pending_sig;
struct smcinvoke_file_data *server_obj = filp->private_data;
struct smcinvoke_accept user_args = {0};
struct smcinvoke_cb_txn *cb_txn = NULL;
@@ -1466,7 +1518,7 @@ static long process_accept_req(struct file *filp, unsigned int cmd,
return -EPERM;
mutex_lock(&g_smcinvoke_lock);
- server_info = find_cb_server_locked(server_obj->server_id);
+ server_info = get_cb_server_locked(server_obj->server_id);
mutex_unlock(&g_smcinvoke_lock);
if (!server_info)
return -EINVAL;
@@ -1517,6 +1569,21 @@ static long process_accept_req(struct file *filp, unsigned int cmd,
if (ret) {
pr_debug("%s wait_event interrupted: ret = %d\n",
__func__, ret);
+ /*
+ * Ideally, we should destroy server if accept threads
+ * are returning due to client being killed or device
+ * going down (Shutdown/Reboot) but that would make
+ * server_info invalid. Other accept/invoke threads are
+ * using server_info and would crash. So dont do that.
+ */
+ pending_sig = (¤t->pending)->signal;
+ if (sigismember(&pending_sig, SIGKILL)) {
+ mutex_lock(&g_smcinvoke_lock);
+ server_info->state =
+ SMCINVOKE_SERVER_STATE_DEFUNCT;
+ wake_up_interruptible(&server_info->rsp_wait_q);
+ mutex_unlock(&g_smcinvoke_lock);
+ }
goto out;
}
@@ -1546,6 +1613,8 @@ static long process_accept_req(struct file *filp, unsigned int cmd,
}
} while (!cb_txn);
out:
+ if (server_info)
+ kref_put(&server_info->ref_cnt, destroy_cb_server);
return ret;
}
@@ -1707,26 +1776,14 @@ static int smcinvoke_open(struct inode *nodp, struct file *filp)
return 0;
}
-static int destroy_cb_server(uint16_t server_id)
+static int release_cb_server(uint16_t server_id)
{
struct smcinvoke_server_info *server = NULL;
mutex_lock(&g_smcinvoke_lock);
server = find_cb_server_locked(server_id);
- if (server) {
- if (!list_empty(&server->pending_cbobjs)) {
- server->state = SMCINVOKE_SERVER_STATE_DEFUNCT;
- wake_up_interruptible(&server->rsp_wait_q);
- /*
- * we dont worry about threads waiting on req_wait_q
- * because server can't be closed as long as there is
- * atleast one accept thread active
- */
- } else {
- hash_del(&server->hash);
- kfree(server);
- }
- }
+ if (server)
+ kref_put(&server->ref_cnt, destroy_cb_server);
mutex_unlock(&g_smcinvoke_lock);
return 0;
}
@@ -1744,7 +1801,7 @@ static int smcinvoke_release(struct inode *nodp, struct file *filp)
struct qtee_shm in_shm = {0}, out_shm = {0};
if (file_data->context_type == SMCINVOKE_OBJ_TYPE_SERVER) {
- ret = destroy_cb_server(file_data->server_id);
+ ret = release_cb_server(file_data->server_id);
goto out;
}
diff --git a/drivers/soc/qcom/smcinvoke_object.h b/drivers/soc/qcom/smcinvoke_object.h
index 175a794..09972e4 100644
--- a/drivers/soc/qcom/smcinvoke_object.h
+++ b/drivers/soc/qcom/smcinvoke_object.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __SMCINVOKE_OBJECT_H
#define __SMCINVOKE_OBJECT_H
@@ -16,6 +16,7 @@
#define OBJECT_OP_RELEASE (OBJECT_OP_METHOD_MASK - 0)
#define OBJECT_OP_RETAIN (OBJECT_OP_METHOD_MASK - 1)
#define OBJECT_OP_MAP_REGION 0
+#define OBJECT_OP_YIELD 1
#define OBJECT_COUNTS_MAX_BI 0xF
#define OBJECT_COUNTS_MAX_BO 0xF
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index d24eb7c..8a9d08f 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -73,23 +73,23 @@
} while (0)
#define spcom_pr_err(_fmt, ...) do { \
- pr_err(_fmt); \
+ pr_err(_fmt, ##__VA_ARGS__); \
spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \
} while (0)
#define spcom_pr_warn(_fmt, ...) do { \
- pr_warn(_fmt); \
+ pr_warn(_fmt, ##__VA_ARGS__); \
spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \
} while (0)
#define spcom_pr_info(_fmt, ...) do { \
- pr_info(_fmt); \
+ pr_info(_fmt, ##__VA_ARGS__); \
spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \
} while (0)
#if defined(DEBUG)
#define spcom_pr_dbg(_fmt, ...) do { \
- pr_debug(_fmt); \
+ pr_debug(_fmt, ##__VA_ARGS__); \
spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \
} while (0)
#else
diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c
index 5eabb2b..e3ab1c4 100644
--- a/drivers/soc/qcom/spss_utils.c
+++ b/drivers/soc/qcom/spss_utils.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
/*
@@ -33,6 +33,7 @@
#include <linux/notifier.h>
#include <linux/sizes.h> /* SZ_4K */
#include <linux/uaccess.h> /* copy_from_user() */
+#include <linux/completion.h> /* wait_for_completion_timeout() */
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
@@ -91,6 +92,13 @@ static phys_addr_t cmac_mem_addr;
#define SPU_EMULATUION (BIT(0) | BIT(1))
#define SPU_PRESENT_IN_EMULATION BIT(2)
+/* Events notification */
+static struct completion spss_events[SPSS_NUM_EVENTS];
+static bool spss_events_signaled[SPSS_NUM_EVENTS];
+
+/* Protect from ioctl signal func called by multiple-proc at the same time */
+static struct mutex event_lock;
+
/**
* struct device state
*/
@@ -376,15 +384,119 @@ static int spss_create_sysfs(struct device *dev)
/*==========================================================================*/
/* IOCTL */
/*==========================================================================*/
+static int spss_wait_for_event(struct spss_ioc_wait_for_event *req)
+{
+ int ret;
+ uint32_t event_id;
+ uint32_t timeout_sec;
+ long timeleft = 1;
+
+ event_id = req->event_id;
+ timeout_sec = req->timeout_sec;
+
+ if (event_id >= SPSS_NUM_EVENTS) {
+ pr_err("event_id [%d] invalid\n", event_id);
+ return -EINVAL;
+ }
+
+ pr_debug("wait for event [%d], timeout_sec [%d]\n",
+ event_id, timeout_sec);
+
+ if (timeout_sec) {
+ unsigned long jiffies = 0;
+
+ jiffies = msecs_to_jiffies(timeout_sec*1000);
+ timeleft = wait_for_completion_interruptible_timeout(
+ &spss_events[event_id], jiffies);
+ ret = timeleft;
+ } else {
+ ret = wait_for_completion_interruptible(
+ &spss_events[event_id]);
+ }
+
+ if (timeleft == 0) {
+ pr_err("wait for event [%d] timeout [%d] sec expired\n",
+ event_id, timeout_sec);
+ req->status = EVENT_STATUS_TIMEOUT;
+ } else if (ret < 0) {
+ pr_err("wait for event [%d] interrupted. ret [%d]\n",
+ event_id, ret);
+ req->status = EVENT_STATUS_ABORTED;
+ if (ret == -ERESTARTSYS) /* handle LPM event */
+ return ret;
+ } else {
+ pr_debug("wait for event [%d] completed.\n", event_id);
+ req->status = EVENT_STATUS_SIGNALED;
+ }
+
+ return 0;
+}
+
+static int spss_signal_event(struct spss_ioc_signal_event *req)
+{
+ uint32_t event_id;
+
+ mutex_lock(&event_lock);
+
+ event_id = req->event_id;
+
+ if (event_id >= SPSS_NUM_EVENTS) {
+ pr_err("event_id [%d] invalid\n", event_id);
+ mutex_unlock(&event_lock);
+ return -EINVAL;
+ }
+
+ if (spss_events_signaled[event_id]) {
+ pr_err("event_id [%d] already signaled\n", event_id);
+ mutex_unlock(&event_lock);
+ return -EINVAL;
+ }
+
+ pr_debug("signal event [%d]\n", event_id);
+ complete_all(&spss_events[event_id]);
+ req->status = EVENT_STATUS_SIGNALED;
+ spss_events_signaled[event_id] = true;
+
+ mutex_unlock(&event_lock);
+
+ return 0;
+}
+
+static int spss_is_event_signaled(struct spss_ioc_is_signaled *req)
+{
+ uint32_t event_id;
+
+ mutex_lock(&event_lock);
+
+ event_id = req->event_id;
+
+ if (event_id >= SPSS_NUM_EVENTS) {
+ pr_err("event_id [%d] invalid\n", event_id);
+ mutex_unlock(&event_lock);
+ return -EINVAL;
+ }
+
+ if (spss_events_signaled[event_id])
+ req->status = EVENT_STATUS_SIGNALED;
+ else
+ req->status = EVENT_STATUS_NOT_SIGNALED;
+
+ mutex_unlock(&event_lock);
+
+ return 0;
+}
+
static long spss_utils_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
+ int ret;
void *buf = (void *) arg;
unsigned char data[64] = {0};
size_t size = 0;
u32 i = 0;
/* Saved cmacs of spu firmware and UEFI loaded spu apps */
u32 fw_and_apps_cmacs[FW_AND_APPS_CMAC_SIZE];
+ void *req = (void *) data;
if (buf == NULL) {
pr_err("invalid ioctl arg\n");
@@ -429,7 +541,7 @@ static long spss_utils_ioctl(struct file *file,
/*
* SPSS is loaded now by UEFI,
- * so IAR callback is not being called on powerup by PIL.
+ * so IAR callback is not being called on power-up by PIL.
* therefore read the spu pbl fw cmac and apps cmac from ioctl.
* The callback shall be called on spss SSR.
*/
@@ -439,6 +551,42 @@ static long spss_utils_ioctl(struct file *file,
spss_get_saved_uefi_apps_cmac();
break;
+ case SPSS_IOC_WAIT_FOR_EVENT:
+ /* check input params */
+ if (size != sizeof(struct spss_ioc_wait_for_event)) {
+ pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
+ return -EINVAL;
+ }
+ ret = spss_wait_for_event(req);
+ copy_to_user((void __user *)arg, data, size);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case SPSS_IOC_SIGNAL_EVENT:
+ /* check input params */
+ if (size != sizeof(struct spss_ioc_signal_event)) {
+ pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
+ return -EINVAL;
+ }
+ ret = spss_signal_event(req);
+ copy_to_user((void __user *)arg, data, size);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case SPSS_IOC_IS_EVENT_SIGNALED:
+ /* check input params */
+ if (size != sizeof(struct spss_ioc_is_signaled)) {
+ pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
+ return -EINVAL;
+ }
+ ret = spss_is_event_signaled(req);
+ copy_to_user((void __user *)arg, data, size);
+ if (ret < 0)
+ return ret;
+ break;
+
default:
pr_err("invalid ioctl cmd [0x%x]\n", cmd);
return -EINVAL;
@@ -772,7 +920,7 @@ static int spss_parse_dt(struct device_node *node)
iar_state = val1;
- pr_info("iar_state [%d]\n", iar_state);
+ pr_debug("iar_state [%d]\n", iar_state);
return 0;
}
@@ -909,28 +1057,65 @@ static int spss_set_saved_uefi_apps_cmac(void)
return 0;
}
-static int spss_utils_iar_callback(struct notifier_block *nb,
+static int spss_utils_pil_callback(struct notifier_block *nb,
unsigned long code,
void *data)
{
- /* do nothing if IAR is not active */
- if (!is_iar_active)
- return NOTIFY_OK;
+ int i, event_id;
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
pr_debug("[SUBSYS_BEFORE_SHUTDOWN] event.\n");
+ mutex_lock(&event_lock);
+ /* Reset NVM-ready and SPU-ready events */
+ for (i = SPSS_EVENT_ID_NVM_READY;
+ i <= SPSS_EVENT_ID_SPU_READY; i++) {
+ reinit_completion(&spss_events[i]);
+ spss_events_signaled[i] = false;
+ }
+ mutex_unlock(&event_lock);
+ pr_debug("reset spss events.\n");
break;
case SUBSYS_AFTER_SHUTDOWN:
pr_debug("[SUBSYS_AFTER_SHUTDOWN] event.\n");
+ mutex_lock(&event_lock);
+ event_id = SPSS_EVENT_ID_SPU_POWER_DOWN;
+ complete_all(&spss_events[event_id]);
+ spss_events_signaled[event_id] = true;
+
+ event_id = SPSS_EVENT_ID_SPU_POWER_UP;
+ reinit_completion(&spss_events[event_id]);
+ spss_events_signaled[event_id] = false;
+ mutex_unlock(&event_lock);
break;
case SUBSYS_BEFORE_POWERUP:
pr_debug("[SUBSYS_BEFORE_POWERUP] event.\n");
break;
case SUBSYS_AFTER_POWERUP:
pr_debug("[SUBSYS_AFTER_POWERUP] event.\n");
+ mutex_lock(&event_lock);
+ event_id = SPSS_EVENT_ID_SPU_POWER_UP;
+ complete_all(&spss_events[event_id]);
+ spss_events_signaled[event_id] = true;
+
+ event_id = SPSS_EVENT_ID_SPU_POWER_DOWN;
+ reinit_completion(&spss_events[event_id]);
+ spss_events_signaled[event_id] = false;
+ mutex_unlock(&event_lock);
+ break;
+ case SUBSYS_RAMDUMP_NOTIFICATION:
+ pr_debug("[SUBSYS_RAMDUMP_NOTIFICATION] event.\n");
+ break;
+ case SUBSYS_PROXY_VOTE:
+ pr_debug("[SUBSYS_PROXY_VOTE] event.\n");
+ break;
+ case SUBSYS_PROXY_UNVOTE:
+ pr_debug("[SUBSYS_PROXY_UNVOTE] event.\n");
break;
case SUBSYS_BEFORE_AUTH_AND_RESET:
+ /* do nothing if IAR is not active */
+ if (!is_iar_active)
+ return NOTIFY_OK;
pr_debug("[SUBSYS_BEFORE_AUTH_AND_RESET] event.\n");
/* Called on SSR as spss firmware is loaded by UEFI */
spss_set_fw_cmac(cmac_buf, sizeof(cmac_buf));
@@ -951,6 +1136,7 @@ static int spss_utils_iar_callback(struct notifier_block *nb,
static int spss_probe(struct platform_device *pdev)
{
int ret = 0;
+ int i;
struct device_node *np = NULL;
struct device *dev = NULL;
@@ -1023,7 +1209,7 @@ static int spss_probe(struct platform_device *pdev)
if (!iar_nb)
return -ENOMEM;
- iar_nb->notifier_call = spss_utils_iar_callback;
+ iar_nb->notifier_call = spss_utils_pil_callback;
iar_notif_handle = subsys_notif_register_notifier("spss", iar_nb);
if (IS_ERR_OR_NULL(iar_notif_handle)) {
@@ -1031,6 +1217,12 @@ static int spss_probe(struct platform_device *pdev)
kfree(iar_nb);
}
+ for (i = 0 ; i < SPSS_NUM_EVENTS; i++) {
+ init_completion(&spss_events[i]);
+ spss_events_signaled[i] = false;
+ }
+ mutex_init(&event_lock);
+
return 0;
}
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 8343023..7b8485e 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -760,6 +760,7 @@ static int msm_watchdog_probe(struct platform_device *pdev)
md_entry.virt_addr = (uintptr_t)wdog_dd;
md_entry.phys_addr = virt_to_phys(wdog_dd);
md_entry.size = sizeof(*wdog_dd);
+ md_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&md_entry))
pr_info("Failed to add Watchdog data in Minidump\n");
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index df3ccb3..373400d 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -281,7 +281,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp
struct rpmsg_channel_info chinfo;
struct wcnss_ctrl *_wcnss = wcnss;
- strncpy(chinfo.name, name, sizeof(chinfo.name));
+ strscpy(chinfo.name, name, sizeof(chinfo.name));
chinfo.src = RPMSG_ADDR_ANY;
chinfo.dst = RPMSG_ADDR_ANY;
diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c
index caf894f..77422ba 100644
--- a/drivers/soc/renesas/r8a77970-sysc.c
+++ b/drivers/soc/renesas/r8a77970-sysc.c
@@ -27,8 +27,8 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = {
{ "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON },
{ "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR },
{ "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR },
- { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR },
- { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR },
+ { "a2dp", 0x400, 2, R8A77970_PD_A2DP, R8A77970_PD_A3IR },
+ { "a2cn", 0x400, 3, R8A77970_PD_A2CN, R8A77970_PD_A3IR },
{ "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR },
{ "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR },
};
diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c
index 9265fb5..a8dbe55 100644
--- a/drivers/soc/renesas/r8a77980-sysc.c
+++ b/drivers/soc/renesas/r8a77980-sysc.c
@@ -38,12 +38,12 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = {
{ "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR },
{ "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR },
{ "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR },
- { "a2pd0", 0x400, 11, R8A77980_PD_A2PD0, R8A77980_PD_A3IR },
- { "a2pd1", 0x400, 12, R8A77980_PD_A2PD1, R8A77980_PD_A3IR },
+ { "a2dp0", 0x400, 11, R8A77980_PD_A2DP0, R8A77980_PD_A3IR },
+ { "a2dp1", 0x400, 12, R8A77980_PD_A2DP1, R8A77980_PD_A3IR },
{ "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR },
- { "a3vip", 0x2c0, 0, R8A77980_PD_A3VIP, R8A77980_PD_ALWAYS_ON },
- { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_A3VIP },
- { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_A3VIP },
+ { "a3vip0", 0x2c0, 0, R8A77980_PD_A3VIP0, R8A77980_PD_ALWAYS_ON },
+ { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_ALWAYS_ON },
+ { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_ALWAYS_ON },
};
const struct rcar_sysc_info r8a77980_sysc_info __initconst = {
diff --git a/drivers/soc/renesas/r8a77990-sysc.c b/drivers/soc/renesas/r8a77990-sysc.c
index 15579eb..664b244 100644
--- a/drivers/soc/renesas/r8a77990-sysc.c
+++ b/drivers/soc/renesas/r8a77990-sysc.c
@@ -28,19 +28,6 @@ static struct rcar_sysc_area r8a77990_areas[] __initdata = {
{ "3dg-b", 0x100, 1, R8A77990_PD_3DG_B, R8A77990_PD_3DG_A },
};
-static void __init rcar_sysc_fix_parent(struct rcar_sysc_area *areas,
- unsigned int num_areas, u8 id,
- int new_parent)
-{
- unsigned int i;
-
- for (i = 0; i < num_areas; i++)
- if (areas[i].isr_bit == id) {
- areas[i].parent = new_parent;
- return;
- }
-}
-
/* Fixups for R-Car E3 ES1.0 revision */
static const struct soc_device_attribute r8a77990[] __initconst = {
{ .soc_id = "r8a77990", .revision = "ES1.0" },
@@ -50,12 +37,10 @@ static const struct soc_device_attribute r8a77990[] __initconst = {
static int __init r8a77990_sysc_init(void)
{
if (soc_device_match(r8a77990)) {
- rcar_sysc_fix_parent(r8a77990_areas,
- ARRAY_SIZE(r8a77990_areas),
- R8A77990_PD_3DG_A, R8A77990_PD_3DG_B);
- rcar_sysc_fix_parent(r8a77990_areas,
- ARRAY_SIZE(r8a77990_areas),
- R8A77990_PD_3DG_B, R8A77990_PD_ALWAYS_ON);
+ /* Fix incorrect 3DG hierarchy */
+ swap(r8a77990_areas[7], r8a77990_areas[8]);
+ r8a77990_areas[7].parent = R8A77990_PD_ALWAYS_ON;
+ r8a77990_areas[8].parent = R8A77990_PD_3DG_B;
}
return 0;
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 4b452f3..f17a678 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -65,6 +65,8 @@
#define PWRGATE_STATUS 0x38
+#define PMC_IMPL_E_33V_PWR 0x40
+
#define PMC_PWR_DET 0x48
#define PMC_SCRATCH0_MODE_RECOVERY BIT(31)
@@ -154,6 +156,7 @@ struct tegra_pmc_soc {
bool has_tsense_reset;
bool has_gpu_clamps;
bool needs_mbist_war;
+ bool has_impl_33v_pwr;
const struct tegra_io_pad_soc *io_pads;
unsigned int num_io_pads;
@@ -1067,20 +1070,31 @@ int tegra_io_pad_set_voltage(enum tegra_io_pad id,
mutex_lock(&pmc->powergates_lock);
- /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */
- value = tegra_pmc_readl(PMC_PWR_DET);
- value |= BIT(pad->voltage);
- tegra_pmc_writel(value, PMC_PWR_DET);
+ if (pmc->soc->has_impl_33v_pwr) {
+ value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR);
- /* update I/O voltage */
- value = tegra_pmc_readl(PMC_PWR_DET_VALUE);
+ if (voltage == TEGRA_IO_PAD_1800000UV)
+ value &= ~BIT(pad->voltage);
+ else
+ value |= BIT(pad->voltage);
- if (voltage == TEGRA_IO_PAD_1800000UV)
- value &= ~BIT(pad->voltage);
- else
+ tegra_pmc_writel(value, PMC_IMPL_E_33V_PWR);
+ } else {
+ /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */
+ value = tegra_pmc_readl(PMC_PWR_DET);
value |= BIT(pad->voltage);
+ tegra_pmc_writel(value, PMC_PWR_DET);
- tegra_pmc_writel(value, PMC_PWR_DET_VALUE);
+ /* update I/O voltage */
+ value = tegra_pmc_readl(PMC_PWR_DET_VALUE);
+
+ if (voltage == TEGRA_IO_PAD_1800000UV)
+ value &= ~BIT(pad->voltage);
+ else
+ value |= BIT(pad->voltage);
+
+ tegra_pmc_writel(value, PMC_PWR_DET_VALUE);
+ }
mutex_unlock(&pmc->powergates_lock);
@@ -1102,7 +1116,10 @@ int tegra_io_pad_get_voltage(enum tegra_io_pad id)
if (pad->voltage == UINT_MAX)
return -ENOTSUPP;
- value = tegra_pmc_readl(PMC_PWR_DET_VALUE);
+ if (pmc->soc->has_impl_33v_pwr)
+ value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR);
+ else
+ value = tegra_pmc_readl(PMC_PWR_DET_VALUE);
if ((value & BIT(pad->voltage)) == 0)
return TEGRA_IO_PAD_1800000UV;
@@ -1561,6 +1578,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
.cpu_powergates = tegra30_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = false,
+ .has_impl_33v_pwr = false,
.num_io_pads = 0,
.io_pads = NULL,
.regs = &tegra20_pmc_regs,
@@ -1603,6 +1621,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
.cpu_powergates = tegra114_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = false,
+ .has_impl_33v_pwr = false,
.num_io_pads = 0,
.io_pads = NULL,
.regs = &tegra20_pmc_regs,
@@ -1683,6 +1702,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
.cpu_powergates = tegra124_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = true,
+ .has_impl_33v_pwr = false,
.num_io_pads = ARRAY_SIZE(tegra124_io_pads),
.io_pads = tegra124_io_pads,
.regs = &tegra20_pmc_regs,
@@ -1772,6 +1792,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.cpu_powergates = tegra210_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = true,
+ .has_impl_33v_pwr = false,
.needs_mbist_war = true,
.num_io_pads = ARRAY_SIZE(tegra210_io_pads),
.io_pads = tegra210_io_pads,
@@ -1800,7 +1821,7 @@ static const struct tegra_io_pad_soc tegra186_io_pads[] = {
{ .id = TEGRA_IO_PAD_HDMI_DP0, .dpd = 28, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_HDMI_DP1, .dpd = 29, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = 32, .voltage = UINT_MAX },
- { .id = TEGRA_IO_PAD_SDMMC2_HV, .dpd = 34, .voltage = UINT_MAX },
+ { .id = TEGRA_IO_PAD_SDMMC2_HV, .dpd = 34, .voltage = 5 },
{ .id = TEGRA_IO_PAD_SDMMC4, .dpd = 36, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_CAM, .dpd = 38, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_DSIB, .dpd = 40, .voltage = UINT_MAX },
@@ -1812,12 +1833,13 @@ static const struct tegra_io_pad_soc tegra186_io_pads[] = {
{ .id = TEGRA_IO_PAD_CSIF, .dpd = 46, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_SPI, .dpd = 47, .voltage = UINT_MAX },
{ .id = TEGRA_IO_PAD_UFS, .dpd = 49, .voltage = UINT_MAX },
- { .id = TEGRA_IO_PAD_DMIC_HV, .dpd = 52, .voltage = UINT_MAX },
+ { .id = TEGRA_IO_PAD_DMIC_HV, .dpd = 52, .voltage = 2 },
{ .id = TEGRA_IO_PAD_EDP, .dpd = 53, .voltage = UINT_MAX },
- { .id = TEGRA_IO_PAD_SDMMC1_HV, .dpd = 55, .voltage = UINT_MAX },
- { .id = TEGRA_IO_PAD_SDMMC3_HV, .dpd = 56, .voltage = UINT_MAX },
+ { .id = TEGRA_IO_PAD_SDMMC1_HV, .dpd = 55, .voltage = 4 },
+ { .id = TEGRA_IO_PAD_SDMMC3_HV, .dpd = 56, .voltage = 6 },
{ .id = TEGRA_IO_PAD_CONN, .dpd = 60, .voltage = UINT_MAX },
- { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = UINT_MAX },
+ { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = 1 },
+ { .id = TEGRA_IO_PAD_AO_HV, .dpd = UINT_MAX, .voltage = 0 },
};
static const struct tegra_pmc_regs tegra186_pmc_regs = {
@@ -1870,6 +1892,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.cpu_powergates = NULL,
.has_tsense_reset = false,
.has_gpu_clamps = false,
+ .has_impl_33v_pwr = true,
.num_io_pads = ARRAY_SIZE(tegra186_io_pads),
.io_pads = tegra186_io_pads,
.regs = &tegra186_pmc_regs,
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
index 1ba1556..c7708fe 100644
--- a/drivers/soundwire/Kconfig
+++ b/drivers/soundwire/Kconfig
@@ -4,6 +4,7 @@
menuconfig SOUNDWIRE
tristate "SoundWire support"
+ depends on ACPI
help
SoundWire is a 2-Pin interface with data and clock line ratified
by the MIPI Alliance. SoundWire is used for transporting data
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index dcc0ff9..df172bf 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -175,6 +175,7 @@ static inline int do_transfer_defer(struct sdw_bus *bus,
defer->msg = msg;
defer->length = msg->len;
+ init_completion(&defer->complete);
for (i = 0; i <= retry; i++) {
resp = bus->ops->xfer_msg_defer(bus, msg, defer);
@@ -805,7 +806,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
static int sdw_handle_slave_alerts(struct sdw_slave *slave)
{
struct sdw_slave_intr_status slave_intr;
- u8 clear = 0, bit, port_status[15];
+ u8 clear = 0, bit, port_status[15] = {0};
int port_num, stat, ret, count = 0;
unsigned long port;
bool slave_notify = false;
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 29bc99c..e49d3c8 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -352,7 +352,10 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
unsigned int link_id = sdw->instance;
int pdi_conf = 0;
- pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
+ /* the Bulk and PCM streams are not contiguous */
+ pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
+ if (pdi->num >= 2)
+ pdi->intel_alh_id += 2;
/*
* Program stream parameters to stream SHIM register
@@ -381,7 +384,10 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
unsigned int link_id = sdw->instance;
unsigned int conf;
- pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
+ /* the Bulk and PCM streams are not contiguous */
+ pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
+ if (pdi->num >= 2)
+ pdi->intel_alh_id += 2;
/* Program Stream config ALH register */
conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
index d1ea6b4..5c8a20d 100644
--- a/drivers/soundwire/intel_init.c
+++ b/drivers/soundwire/intel_init.c
@@ -151,7 +151,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
struct acpi_device *adev;
if (acpi_bus_get_device(handle, &adev)) {
- dev_err(&adev->dev, "Couldn't find ACPI handle\n");
+ pr_err("%s: Couldn't find ACPI handle\n", __func__);
return AE_NOT_FOUND;
}
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 3f890d1..2fb43c5 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1193,10 +1193,8 @@ static int atmel_spi_setup(struct spi_device *spi)
as = spi_master_get_devdata(spi->master);
/* see notes above re chipselect */
- if (!atmel_spi_is_v2(as)
- && spi->chip_select == 0
- && (spi->mode & SPI_CS_HIGH)) {
- dev_dbg(&spi->dev, "setup: can't be active-high\n");
+ if (!as->use_cs_gpios && (spi->mode & SPI_CS_HIGH)) {
+ dev_warn(&spi->dev, "setup: non GPIO CS can't be active-high\n");
return -EINVAL;
}
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index c23849f..9a06ffd 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -101,6 +101,7 @@ struct bcm63xx_hsspi {
struct platform_device *pdev;
struct clk *clk;
+ struct clk *pll_clk;
void __iomem *regs;
u8 __iomem *fifo;
@@ -332,7 +333,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
struct resource *res_mem;
void __iomem *regs;
struct device *dev = &pdev->dev;
- struct clk *clk;
+ struct clk *clk, *pll_clk = NULL;
int irq, ret;
u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS;
@@ -358,7 +359,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
rate = clk_get_rate(clk);
if (!rate) {
- struct clk *pll_clk = devm_clk_get(dev, "pll");
+ pll_clk = devm_clk_get(dev, "pll");
if (IS_ERR(pll_clk)) {
ret = PTR_ERR(pll_clk);
@@ -373,19 +374,20 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
clk_disable_unprepare(pll_clk);
if (!rate) {
ret = -EINVAL;
- goto out_disable_clk;
+ goto out_disable_pll_clk;
}
}
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) {
ret = -ENOMEM;
- goto out_disable_clk;
+ goto out_disable_pll_clk;
}
bs = spi_master_get_devdata(master);
bs->pdev = pdev;
bs->clk = clk;
+ bs->pll_clk = pll_clk;
bs->regs = regs;
bs->speed_hz = rate;
bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
@@ -440,6 +442,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
out_put_master:
spi_master_put(master);
+out_disable_pll_clk:
+ clk_disable_unprepare(pll_clk);
out_disable_clk:
clk_disable_unprepare(clk);
return ret;
@@ -453,6 +457,7 @@ static int bcm63xx_hsspi_remove(struct platform_device *pdev)
/* reset the hardware and block queue progress */
__raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
+ clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
@@ -465,6 +470,7 @@ static int bcm63xx_hsspi_suspend(struct device *dev)
struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
spi_master_suspend(master);
+ clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
@@ -480,6 +486,12 @@ static int bcm63xx_hsspi_resume(struct device *dev)
if (ret)
return ret;
+ if (bs->pll_clk) {
+ ret = clk_prepare_enable(bs->pll_clk);
+ if (ret)
+ return ret;
+ }
+
spi_master_resume(master);
return 0;
diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c
index 8779377..828fbbe 100644
--- a/drivers/spi/spi-cavium-thunderx.c
+++ b/drivers/spi/spi-cavium-thunderx.c
@@ -81,6 +81,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev,
error:
clk_disable_unprepare(p->clk);
+ pci_release_regions(pdev);
spi_master_put(master);
return ret;
}
@@ -95,6 +96,7 @@ static void thunderx_spi_remove(struct pci_dev *pdev)
return;
clk_disable_unprepare(p->clk);
+ pci_release_regions(pdev);
/* Put everything in a known state. */
writeq(0, p->register_base + OCTEON_SPI_CFG(p));
}
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index e6d5cc6..5167097 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -276,7 +276,7 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
fsl_lpspi_set_watermark(fsl_lpspi);
- temp = CFGR1_PCSCFG | CFGR1_MASTER | CFGR1_NOSTALL;
+ temp = CFGR1_PCSCFG | CFGR1_MASTER;
if (fsl_lpspi->config.mode & SPI_CS_HIGH)
temp |= CFGR1_PCSPOL;
writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 8f2e978..8b79e36 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -832,9 +832,9 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
if (ret)
goto err;
- irq = irq_of_parse_and_map(np, 0);
- if (!irq) {
- ret = -EINVAL;
+ irq = platform_get_irq(ofdev, 0);
+ if (irq < 0) {
+ ret = irq;
goto err;
}
@@ -847,7 +847,6 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
return 0;
err:
- irq_dispose_mapping(irq);
if (type == TYPE_FSL)
of_fsl_spi_free_chipselects(dev);
return ret;
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index e6eb979..e4b31d6 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -676,6 +676,8 @@ static int img_spfi_probe(struct platform_device *pdev)
dma_release_channel(spfi->tx_ch);
if (spfi->rx_ch)
dma_release_channel(spfi->rx_ch);
+ spfi->tx_ch = NULL;
+ spfi->rx_ch = NULL;
dev_warn(spfi->dev, "Failed to get DMA channels, falling back to PIO mode\n");
} else {
master->dma_tx = spfi->tx_ch;
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 86bf456..0c2867d 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -98,6 +98,7 @@ struct mtk_spi {
struct clk *parent_clk, *sel_clk, *spi_clk;
struct spi_transfer *cur_transfer;
u32 xfer_len;
+ u32 num_xfered;
struct scatterlist *tx_sgl, *rx_sgl;
u32 tx_sgl_len, rx_sgl_len;
const struct mtk_spi_compatible *dev_comp;
@@ -385,6 +386,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
mdata->cur_transfer = xfer;
mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
+ mdata->num_xfered = 0;
mtk_spi_prepare_transfer(master, xfer);
mtk_spi_setup_packet(master);
@@ -415,6 +417,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master,
mdata->tx_sgl_len = 0;
mdata->rx_sgl_len = 0;
mdata->cur_transfer = xfer;
+ mdata->num_xfered = 0;
mtk_spi_prepare_transfer(master, xfer);
@@ -482,7 +485,7 @@ static int mtk_spi_setup(struct spi_device *spi)
static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
{
- u32 cmd, reg_val, cnt, remainder;
+ u32 cmd, reg_val, cnt, remainder, len;
struct spi_master *master = dev_id;
struct mtk_spi *mdata = spi_master_get_devdata(master);
struct spi_transfer *trans = mdata->cur_transfer;
@@ -497,36 +500,38 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
if (trans->rx_buf) {
cnt = mdata->xfer_len / 4;
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
- trans->rx_buf, cnt);
+ trans->rx_buf + mdata->num_xfered, cnt);
remainder = mdata->xfer_len % 4;
if (remainder > 0) {
reg_val = readl(mdata->base + SPI_RX_DATA_REG);
- memcpy(trans->rx_buf + (cnt * 4),
- ®_val, remainder);
+ memcpy(trans->rx_buf +
+ mdata->num_xfered +
+ (cnt * 4),
+ ®_val,
+ remainder);
}
}
- trans->len -= mdata->xfer_len;
- if (!trans->len) {
+ mdata->num_xfered += mdata->xfer_len;
+ if (mdata->num_xfered == trans->len) {
spi_finalize_current_transfer(master);
return IRQ_HANDLED;
}
- if (trans->tx_buf)
- trans->tx_buf += mdata->xfer_len;
- if (trans->rx_buf)
- trans->rx_buf += mdata->xfer_len;
-
- mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, trans->len);
+ len = trans->len - mdata->num_xfered;
+ mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
mtk_spi_setup_packet(master);
- cnt = trans->len / 4;
- iowrite32_rep(mdata->base + SPI_TX_DATA_REG, trans->tx_buf, cnt);
+ cnt = mdata->xfer_len / 4;
+ iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
+ trans->tx_buf + mdata->num_xfered, cnt);
- remainder = trans->len % 4;
+ remainder = mdata->xfer_len % 4;
if (remainder > 0) {
reg_val = 0;
- memcpy(®_val, trans->tx_buf + (cnt * 4), remainder);
+ memcpy(®_val,
+ trans->tx_buf + (cnt * 4) + mdata->num_xfered,
+ remainder);
writel(reg_val, mdata->base + SPI_TX_DATA_REG);
}
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index e2be7da..eb2d2de 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -299,7 +299,7 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi *mcspi;
unsigned int wcnt;
- int max_fifo_depth, fifo_depth, bytes_per_word;
+ int max_fifo_depth, bytes_per_word;
u32 chconf, xferlevel;
mcspi = spi_master_get_devdata(master);
@@ -315,10 +315,6 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
else
max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH;
- fifo_depth = gcd(t->len, max_fifo_depth);
- if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0)
- goto disable_fifo;
-
wcnt = t->len / bytes_per_word;
if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT)
goto disable_fifo;
@@ -326,16 +322,17 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
xferlevel = wcnt << 16;
if (t->rx_buf != NULL) {
chconf |= OMAP2_MCSPI_CHCONF_FFER;
- xferlevel |= (fifo_depth - 1) << 8;
+ xferlevel |= (bytes_per_word - 1) << 8;
}
+
if (t->tx_buf != NULL) {
chconf |= OMAP2_MCSPI_CHCONF_FFET;
- xferlevel |= fifo_depth - 1;
+ xferlevel |= bytes_per_word - 1;
}
mcspi_write_reg(master, OMAP2_MCSPI_XFERLEVEL, xferlevel);
mcspi_write_chconf0(spi, chconf);
- mcspi->fifo_depth = fifo_depth;
+ mcspi->fifo_depth = max_fifo_depth;
return;
}
@@ -585,7 +582,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
struct dma_slave_config cfg;
enum dma_slave_buswidth width;
unsigned es;
- u32 burst;
void __iomem *chstat_reg;
void __iomem *irqstat_reg;
int wait_res;
@@ -605,22 +601,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
}
count = xfer->len;
- burst = 1;
-
- if (mcspi->fifo_depth > 0) {
- if (count > mcspi->fifo_depth)
- burst = mcspi->fifo_depth / es;
- else
- burst = count / es;
- }
memset(&cfg, 0, sizeof(cfg));
cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
cfg.src_addr_width = width;
cfg.dst_addr_width = width;
- cfg.src_maxburst = burst;
- cfg.dst_maxburst = burst;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c
index f8a45af..288002f 100644
--- a/drivers/spi/spi-pic32.c
+++ b/drivers/spi/spi-pic32.c
@@ -320,7 +320,7 @@ static int pic32_spi_dma_transfer(struct pic32_spi *pic32s,
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
xfer->rx_sg.sgl,
xfer->rx_sg.nents,
- DMA_FROM_DEVICE,
+ DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx) {
ret = -EINVAL;
@@ -330,7 +330,7 @@ static int pic32_spi_dma_transfer(struct pic32_spi *pic32s,
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
xfer->tx_sg.sgl,
xfer->tx_sg.nents,
- DMA_TO_DEVICE,
+ DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
ret = -EINVAL;
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index f413338..5253881 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1470,7 +1470,13 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
}
ssp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ssp->clk))
+ return NULL;
+
ssp->irq = platform_get_irq(pdev, 0);
+ if (ssp->irq < 0)
+ return NULL;
+
ssp->type = type;
ssp->pdev = pdev;
ssp->port_id = pxa2xx_spi_get_port_id(adev);
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 4b77fa1..e054260 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -446,6 +446,9 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs)
struct dma_slave_config rxconf, txconf;
struct dma_async_tx_descriptor *rxdesc, *txdesc;
+ memset(&rxconf, 0, sizeof(rxconf));
+ memset(&txconf, 0, sizeof(txconf));
+
spin_lock_irqsave(&rs->lock, flags);
rs->state &= ~RXBUSY;
rs->state &= ~TXBUSY;
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 101cd6aa..30ea0a2 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1343,8 +1343,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
i = platform_get_irq(pdev, 0);
if (i < 0) {
- dev_err(&pdev->dev, "cannot get platform IRQ\n");
- ret = -ENOENT;
+ dev_err(&pdev->dev, "cannot get IRQ\n");
+ ret = i;
goto err1;
}
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index df5960b..f1fc2bd 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -367,6 +367,9 @@ static int sprd_adi_restart_handler(struct notifier_block *this,
val |= BIT_WDG_RUN | BIT_WDG_RST;
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val);
+ /* Lock the watchdog */
+ sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, ~WDG_UNLOCK_KEY);
+
mdelay(1000);
dev_emerg(sadi->dev, "Unable to restart system\n");
diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c
index a4e43fc..5df01ff 100644
--- a/drivers/spi/spi-st-ssc4.c
+++ b/drivers/spi/spi-st-ssc4.c
@@ -385,6 +385,7 @@ static int spi_st_probe(struct platform_device *pdev)
return 0;
clk_disable:
+ pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(spi_st->clk);
put_master:
spi_master_put(master);
@@ -396,6 +397,8 @@ static int spi_st_remove(struct platform_device *pdev)
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_st *spi_st = spi_master_get_devdata(master);
+ pm_runtime_disable(&pdev->dev);
+
clk_disable_unprepare(spi_st->clk);
pinctrl_pm_select_sleep_state(&pdev->dev);
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index 1427f34..d118731 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1078,7 +1078,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
ret = clk_enable(tspi->clk);
if (ret < 0) {
dev_err(&pdev->dev, "Clock enable failed %d\n", ret);
- goto exit_free_master;
+ goto exit_clk_unprepare;
}
spi_irq = platform_get_irq(pdev, 0);
@@ -1151,6 +1151,8 @@ static int tegra_slink_probe(struct platform_device *pdev)
free_irq(spi_irq, tspi);
exit_clk_disable:
clk_disable(tspi->clk);
+exit_clk_unprepare:
+ clk_unprepare(tspi->clk);
exit_free_master:
spi_master_put(master);
return ret;
@@ -1164,6 +1166,7 @@ static int tegra_slink_remove(struct platform_device *pdev)
free_irq(tspi->irq, tspi);
clk_disable(tspi->clk);
+ clk_unprepare(tspi->clk);
if (tspi->tx_dma_chan)
tegra_slink_deinit_dma_param(tspi, false);
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index b9fb649..95c28ab 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -69,6 +69,7 @@ struct ti_qspi {
u32 dc;
bool mmap_enabled;
+ int current_cs;
};
#define QSPI_PID (0x0)
@@ -494,6 +495,7 @@ static void ti_qspi_enable_memory_map(struct spi_device *spi)
MEM_CS_EN(spi->chip_select));
}
qspi->mmap_enabled = true;
+ qspi->current_cs = spi->chip_select;
}
static void ti_qspi_disable_memory_map(struct spi_device *spi)
@@ -505,6 +507,7 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg,
MEM_CS_MASK, 0);
qspi->mmap_enabled = false;
+ qspi->current_cs = -1;
}
static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
@@ -550,7 +553,7 @@ static int ti_qspi_exec_mem_op(struct spi_mem *mem,
mutex_lock(&qspi->list_lock);
- if (!qspi->mmap_enabled)
+ if (!qspi->mmap_enabled || qspi->current_cs != mem->spi->chip_select)
ti_qspi_enable_memory_map(mem->spi);
ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
op->addr.nbytes, op->dummy.nbytes);
@@ -807,6 +810,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
}
}
qspi->mmap_enabled = false;
+ qspi->current_cs = -1;
ret = devm_spi_register_master(&pdev->dev, master);
if (!ret)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index cda1071..0287255 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -634,6 +634,9 @@ static int spidev_release(struct inode *inode, struct file *filp)
if (dofree)
kfree(spidev);
}
+#ifdef CONFIG_SPI_SLAVE
+ spi_slave_abort(spidev->spi);
+#endif
mutex_unlock(&device_list_lock);
return 0;
@@ -724,11 +727,9 @@ static int spidev_probe(struct spi_device *spi)
* compatible string, it is a Linux implementation thing
* rather than a description of the hardware.
*/
- if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
- dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
- WARN_ON(spi->dev.of_node &&
- !of_match_device(spidev_dt_ids, &spi->dev));
- }
+ WARN(spi->dev.of_node &&
+ of_device_is_compatible(spi->dev.of_node, "spidev"),
+ "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
spidev_probe_acpi(spi);
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index be81533..e3df4bf 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -350,8 +350,23 @@ static inline vm_flags_t calc_vm_may_flags(unsigned long prot)
_calc_vm_trans(prot, PROT_EXEC, VM_MAYEXEC);
}
+static int ashmem_vmfile_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /* do not allow to mmap ashmem backing shmem file directly */
+ return -EPERM;
+}
+
+static unsigned long
+ashmem_vmfile_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
+}
+
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{
+ static struct file_operations vmfile_fops;
struct ashmem_area *asma = file->private_data;
int ret = 0;
@@ -392,6 +407,19 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
}
vmfile->f_mode |= FMODE_LSEEK;
asma->file = vmfile;
+ /*
+ * override mmap operation of the vmfile so that it can't be
+ * remapped which would lead to creation of a new vma with no
+ * asma permission checks. Have to override get_unmapped_area
+ * as well to prevent VM_BUG_ON check for f_ops modification.
+ */
+ if (!vmfile_fops.mmap) {
+ vmfile_fops = *vmfile->f_op;
+ vmfile_fops.mmap = ashmem_vmfile_mmap;
+ vmfile_fops.get_unmapped_area =
+ ashmem_vmfile_get_unmapped_area;
+ }
+ vmfile->f_op = &vmfile_fops;
}
get_file(asma->file);
diff --git a/drivers/staging/android/ion/ion_secure_util.c b/drivers/staging/android/ion/ion_secure_util.c
index e15c90b..c0b4c4d 100644
--- a/drivers/staging/android/ion/ion_secure_util.c
+++ b/drivers/staging/android/ion/ion_secure_util.c
@@ -90,6 +90,7 @@ int ion_populate_vm_list(unsigned long flags, unsigned int *vm_list,
}
return 0;
}
+EXPORT_SYMBOL(ion_populate_vm_list);
int ion_hyp_unassign_sg(struct sg_table *sgt, int *source_vm_list,
int source_nelems, bool clear_page_private,
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index d029608..4e52520 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -3,7 +3,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved.
*
*/
@@ -78,7 +78,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
page = ion_page_pool_alloc(pool, from_pool);
- if (pool_auto_refill_en &&
+ if (pool_auto_refill_en && pool->order &&
pool_count_below_lowmark(pool) && vmid <= 0) {
wake_up_process(heap->kworker[cached]);
}
@@ -413,11 +413,13 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
buffer->private_flags |= ION_PRIV_FLAG_SHRINKER_FREE;
if (vmid > 0)
- ion_hyp_unassign_sg(table, &vmid, 1, true, false);
+ if (ion_hyp_unassign_sg(table, &vmid, 1, true, false))
+ goto err_free_table_sync;
for_each_sg(table->sgl, sg, table->nents, i)
free_buffer_page(sys_heap, buffer, sg_page(sg),
get_order(sg->length));
+err_free_table_sync:
if (nents_sync)
sg_free_table(&table_sync);
err_free_sg:
diff --git a/drivers/staging/android/ion/ion_system_secure_heap.c b/drivers/staging/android/ion/ion_system_secure_heap.c
index f0d8d72..1971385 100644
--- a/drivers/staging/android/ion/ion_system_secure_heap.c
+++ b/drivers/staging/android/ion/ion_system_secure_heap.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
@@ -114,7 +114,9 @@ static void process_one_prefetch(struct ion_heap *sys_heap,
goto out;
ret = ion_hyp_assign_sg(buffer.sg_table, &vmid, 1, true);
- if (ret)
+ if (ret == -EADDRNOTAVAIL)
+ goto out1;
+ else if (ret < 0)
goto out;
/* Now free it to the secure heap */
@@ -123,6 +125,12 @@ static void process_one_prefetch(struct ion_heap *sys_heap,
out:
sys_heap->ops->free(&buffer);
+out1:
+ /*
+ * The security state of the pages is unknown after a failure;
+ * They can neither be added back to the secure pool nor buddy system.
+ */
+ return;
}
/*
diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c
index 4bdf44d..dc62db1 100644
--- a/drivers/staging/comedi/drivers/gsc_hpdi.c
+++ b/drivers/staging/comedi/drivers/gsc_hpdi.c
@@ -623,6 +623,11 @@ static int gsc_hpdi_auto_attach(struct comedi_device *dev,
dma_alloc_coherent(&pcidev->dev, DMA_BUFFER_SIZE,
&devpriv->dio_buffer_phys_addr[i],
GFP_KERNEL);
+ if (!devpriv->dio_buffer[i]) {
+ dev_warn(dev->class_dev,
+ "failed to allocate DMA buffer\n");
+ return -ENOMEM;
+ }
}
/* allocate dma descriptors */
devpriv->dma_desc = dma_alloc_coherent(&pcidev->dev,
@@ -630,6 +635,11 @@ static int gsc_hpdi_auto_attach(struct comedi_device *dev,
NUM_DMA_DESCRIPTORS,
&devpriv->dma_desc_phys_addr,
GFP_KERNEL);
+ if (!devpriv->dma_desc) {
+ dev_warn(dev->class_dev,
+ "failed to allocate DMA descriptors\n");
+ return -ENOMEM;
+ }
if (devpriv->dma_desc_phys_addr & 0xf) {
dev_warn(dev->class_dev,
" dma descriptors not quad-word aligned (bug)\n");
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index e18c072..0d38589 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2004-2014 Bernd Porr, mail@berndporr.me.uk
+ * Copyright (C) 2004-2019 Bernd Porr, mail@berndporr.me.uk
*/
/*
@@ -8,7 +8,7 @@
* Description: University of Stirling USB DAQ & INCITE Technology Limited
* Devices: [ITL] USB-DUX-FAST (usbduxfast)
* Author: Bernd Porr <mail@berndporr.me.uk>
- * Updated: 10 Oct 2014
+ * Updated: 16 Nov 2019
* Status: stable
*/
@@ -22,6 +22,7 @@
*
*
* Revision history:
+ * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest
* 0.9: Dropping the first data packet which seems to be from the last transfer.
* Buffer overflows in the FX2 are handed over to comedi.
* 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
@@ -350,6 +351,7 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
+ int err2 = 0;
unsigned int steps;
unsigned int arg;
@@ -399,11 +401,16 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
*/
steps = (cmd->convert_arg * 30) / 1000;
if (cmd->chanlist_len != 1)
- err |= comedi_check_trigger_arg_min(&steps,
- MIN_SAMPLING_PERIOD);
- err |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
- arg = (steps * 1000) / 30;
- err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ err2 |= comedi_check_trigger_arg_min(&steps,
+ MIN_SAMPLING_PERIOD);
+ else
+ err2 |= comedi_check_trigger_arg_min(&steps, 1);
+ err2 |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
+ if (err2) {
+ err |= err2;
+ arg = (steps * 1000) / 30;
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
if (cmd->stop_src == TRIG_COUNT)
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
diff --git a/drivers/staging/erofs/xattr.c b/drivers/staging/erofs/xattr.c
index 2db99cf..d48687c 100644
--- a/drivers/staging/erofs/xattr.c
+++ b/drivers/staging/erofs/xattr.c
@@ -638,6 +638,8 @@ ssize_t erofs_listxattr(struct dentry *dentry,
struct listxattr_iter it;
ret = init_inode_xattrs(d_inode(dentry));
+ if (ret == -ENOATTR)
+ return 0;
if (ret)
return ret;
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index 16fcf63..3fe4738 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -771,7 +771,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
fbdefio->deferred_io = fbtft_deferred_io;
fb_deferred_io_init(info);
- strncpy(info->fix.id, dev->driver->name, 16);
+ snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.xpanstep = 0;
diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c
index f66dd3e..856bcfa 100644
--- a/drivers/staging/iio/addac/adt7316-i2c.c
+++ b/drivers/staging/iio/addac/adt7316-i2c.c
@@ -35,6 +35,8 @@ static int adt7316_i2c_read(void *client, u8 reg, u8 *data)
return ret;
}
+ *data = ret;
+
return 0;
}
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index d17ce1f..0f8fdc3 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -166,6 +166,9 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv,
struct v4l2_subdev *sd;
struct media_pad *pad;
+ if (!IS_ENABLED(CONFIG_OF))
+ return -ENXIO;
+
if (!priv->src_sd)
return -EPIPE;
@@ -1072,7 +1075,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sink_fmt)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = {};
+ struct v4l2_fwnode_endpoint upstream_ep;
bool is_csi2;
int ret;
diff --git a/drivers/staging/mt7621-pinctrl/Kconfig b/drivers/staging/mt7621-pinctrl/Kconfig
index 37cf9c3..fc36127 100644
--- a/drivers/staging/mt7621-pinctrl/Kconfig
+++ b/drivers/staging/mt7621-pinctrl/Kconfig
@@ -2,3 +2,4 @@
bool "RT2800 pinctrl driver for RALINK/Mediatek SOCs"
depends on RALINK
select PINMUX
+ select GENERIC_PINCONF
diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
index aa98fbb..80e7067 100644
--- a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
+++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
@@ -73,48 +74,12 @@ static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev,
return 0;
}
-static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev,
- struct device_node *np_config,
- struct pinctrl_map **map,
- unsigned int *num_maps)
-{
- struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
- struct property *prop;
- const char *function_name, *group_name;
- int ret;
- int ngroups = 0;
- unsigned int reserved_maps = 0;
-
- for_each_node_with_property(np_config, "group")
- ngroups++;
-
- *map = NULL;
- ret = pinctrl_utils_reserve_map(pctrldev, map, &reserved_maps,
- num_maps, ngroups);
- if (ret) {
- dev_err(p->dev, "can't reserve map: %d\n", ret);
- return ret;
- }
-
- of_property_for_each_string(np_config, "group", prop, group_name) {
- ret = pinctrl_utils_add_map_mux(pctrldev, map, &reserved_maps,
- num_maps, group_name,
- function_name);
- if (ret) {
- dev_err(p->dev, "can't add map: %d\n", ret);
- return ret;
- }
- }
-
- return 0;
-}
-
static const struct pinctrl_ops rt2880_pctrl_ops = {
.get_groups_count = rt2880_get_group_count,
.get_group_name = rt2880_get_group_name,
.get_group_pins = rt2880_get_group_pins,
- .dt_node_to_map = rt2880_pinctrl_dt_node_to_map,
- .dt_free_map = pinctrl_utils_free_map,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinconf_generic_dt_free_map,
};
static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev)
diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c
index dd9b02d..c6a5b62 100644
--- a/drivers/staging/rtl8188eu/core/rtw_xmit.c
+++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c
@@ -778,7 +778,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr
memcpy(pwlanhdr->addr2, get_bssid(pmlmepriv), ETH_ALEN);
memcpy(pwlanhdr->addr3, pattrib->src, ETH_ALEN);
- if (psta->qos_option)
+ if (psta && psta->qos_option)
qos_option = true;
} else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
@@ -786,7 +786,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr
memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), ETH_ALEN);
- if (psta->qos_option)
+ if (psta && psta->qos_option)
qos_option = true;
} else {
RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("fw_state:%x is not allowed to xmit frame\n", get_fwstate(pmlmepriv)));
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index dfee698..55952dd 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -70,7 +70,7 @@ static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)
phost_conf = pusbd->actconfig;
pconf_desc = &phost_conf->desc;
- phost_iface = &usb_intf->altsetting[0];
+ phost_iface = usb_intf->cur_altsetting;
piface_desc = &phost_iface->desc;
pdvobjpriv->NumInterfaces = pconf_desc->bNumInterfaces;
@@ -348,8 +348,10 @@ static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj,
}
padapter->HalData = kzalloc(sizeof(struct hal_data_8188e), GFP_KERNEL);
- if (!padapter->HalData)
- DBG_88E("cant not alloc memory for HAL DATA\n");
+ if (!padapter->HalData) {
+ DBG_88E("Failed to allocate memory for HAL data\n");
+ goto free_adapter;
+ }
/* step read_chip_version */
rtw_hal_read_chip_version(padapter);
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
index d260515..7cdced0 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
@@ -1627,14 +1627,15 @@ static void _rtl92e_hard_data_xmit(struct sk_buff *skb, struct net_device *dev,
memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
skb_push(skb, priv->rtllib->tx_headroom);
ret = _rtl92e_tx(dev, skb);
- if (ret != 0)
- kfree_skb(skb);
if (queue_index != MGNT_QUEUE) {
priv->rtllib->stats.tx_bytes += (skb->len -
priv->rtllib->tx_headroom);
priv->rtllib->stats.tx_packets++;
}
+
+ if (ret != 0)
+ kfree_skb(skb);
}
static int _rtl92e_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index e218b5c..2066a1d 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -1467,7 +1467,7 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
(struct tx_fwinfo_819x_usb *)(skb->data + USB_HWDESC_HEADER_LEN);
struct usb_device *udev = priv->udev;
int pend;
- int status;
+ int status, rt = -1;
struct urb *tx_urb = NULL, *tx_urb_zero = NULL;
unsigned int idx_pipe;
@@ -1611,8 +1611,10 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
}
if (bSend0Byte) {
tx_urb_zero = usb_alloc_urb(0, GFP_ATOMIC);
- if (!tx_urb_zero)
- return -ENOMEM;
+ if (!tx_urb_zero) {
+ rt = -ENOMEM;
+ goto error;
+ }
usb_fill_bulk_urb(tx_urb_zero, udev,
usb_sndbulkpipe(udev, idx_pipe),
&zero, 0, tx_zero_isr, dev);
@@ -1622,7 +1624,7 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
"Error TX URB for zero byte %d, error %d",
atomic_read(&priv->tx_pending[tcb_desc->queue_index]),
status);
- return -1;
+ goto error;
}
}
netif_trans_update(dev);
@@ -1633,7 +1635,12 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
RT_TRACE(COMP_ERR, "Error TX URB %d, error %d",
atomic_read(&priv->tx_pending[tcb_desc->queue_index]),
status);
- return -1;
+
+error:
+ dev_kfree_skb_any(skb);
+ usb_free_urb(tx_urb);
+ usb_free_urb(tx_urb_zero);
+ return rt;
}
static short rtl8192_usb_initendpoints(struct net_device *dev)
diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c
index 85eaddd..5e2cdc2 100644
--- a/drivers/staging/rtl8712/usb_intf.c
+++ b/drivers/staging/rtl8712/usb_intf.c
@@ -275,7 +275,7 @@ static uint r8712_usb_dvobj_init(struct _adapter *padapter)
pdvobjpriv->padapter = padapter;
padapter->EepromAddressSize = 6;
- phost_iface = &pintf->altsetting[0];
+ phost_iface = pintf->cur_altsetting;
piface_desc = &phost_iface->desc;
pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints;
if (pusbd->speed == USB_SPEED_HIGH) {
diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
index 6d02904..49ea780 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
@@ -17,18 +17,13 @@
static const struct sdio_device_id sdio_ids[] =
{
{ SDIO_DEVICE(0x024c, 0x0523), },
+ { SDIO_DEVICE(0x024c, 0x0525), },
{ SDIO_DEVICE(0x024c, 0x0623), },
{ SDIO_DEVICE(0x024c, 0x0626), },
{ SDIO_DEVICE(0x024c, 0xb723), },
{ /* end: all zeroes */ },
};
-static const struct acpi_device_id acpi_ids[] = {
- {"OBDA8723", 0x0000},
- {}
-};
-
MODULE_DEVICE_TABLE(sdio, sdio_ids);
-MODULE_DEVICE_TABLE(acpi, acpi_ids);
static int rtw_drv_init(struct sdio_func *func, const struct sdio_device_id *id);
static void rtw_dev_remove(struct sdio_func *func);
diff --git a/drivers/target/iscsi/cxgbit/cxgbit_cm.c b/drivers/target/iscsi/cxgbit/cxgbit_cm.c
index b19c960..d46eee3 100644
--- a/drivers/target/iscsi/cxgbit/cxgbit_cm.c
+++ b/drivers/target/iscsi/cxgbit/cxgbit_cm.c
@@ -1832,7 +1832,7 @@ static void cxgbit_fw4_ack(struct cxgbit_sock *csk, struct sk_buff *skb)
while (credits) {
struct sk_buff *p = cxgbit_sock_peek_wr(csk);
- const u32 csum = (__force u32)p->csum;
+ u32 csum;
if (unlikely(!p)) {
pr_err("csk 0x%p,%u, cr %u,%u+%u, empty.\n",
@@ -1841,6 +1841,7 @@ static void cxgbit_fw4_ack(struct cxgbit_sock *csk, struct sk_buff *skb)
break;
}
+ csum = (__force u32)p->csum;
if (unlikely(credits < csum)) {
pr_warn("csk 0x%p,%u, cr %u,%u+%u, < %u.\n",
csk, csk->tid,
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 03e9cb1..317d0f3 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1157,7 +1157,9 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length,
conn->cid);
- target_get_sess_cmd(&cmd->se_cmd, true);
+ if (target_get_sess_cmd(&cmd->se_cmd, true) < 0)
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_WAITING_FOR_LOGOUT, buf);
cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd,
scsilun_to_int(&hdr->lun));
@@ -1998,7 +2000,9 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
conn->sess->se_sess, 0, DMA_NONE,
TCM_SIMPLE_TAG, cmd->sense_buffer + 2);
- target_get_sess_cmd(&cmd->se_cmd, true);
+ if (target_get_sess_cmd(&cmd->se_cmd, true) < 0)
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_WAITING_FOR_LOGOUT, buf);
/*
* TASK_REASSIGN for ERL=2 / connection stays inside of
@@ -4204,6 +4208,8 @@ int iscsit_close_connection(
* must wait until they have completed.
*/
iscsit_check_conn_usage_count(conn);
+ target_sess_cmd_list_set_waiting(sess->se_sess);
+ target_wait_for_sess_cmds(sess->se_sess);
ahash_request_free(conn->conn_tx_hash);
if (conn->conn_rx_hash) {
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c
index e2fa3a3..b6bf605 100644
--- a/drivers/target/iscsi/iscsi_target_auth.c
+++ b/drivers/target/iscsi/iscsi_target_auth.c
@@ -78,7 +78,7 @@ static int chap_check_algorithm(const char *a_str)
if (!token)
goto out;
- if (!strncmp(token, "5", 1)) {
+ if (!strcmp(token, "5")) {
pr_debug("Selected MD5 Algorithm\n");
kfree(orig);
return CHAP_DIGEST_MD5;
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 47b5ef1..e9ff2a7 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -1129,27 +1129,6 @@ passthrough_parse_cdb(struct se_cmd *cmd,
unsigned int size;
/*
- * Clear a lun set in the cdb if the initiator talking to use spoke
- * and old standards version, as we can't assume the underlying device
- * won't choke up on it.
- */
- switch (cdb[0]) {
- case READ_10: /* SBC - RDProtect */
- case READ_12: /* SBC - RDProtect */
- case READ_16: /* SBC - RDProtect */
- case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */
- case VERIFY: /* SBC - VRProtect */
- case VERIFY_16: /* SBC - VRProtect */
- case WRITE_VERIFY: /* SBC - VRProtect */
- case WRITE_VERIFY_12: /* SBC - VRProtect */
- case MAINTENANCE_IN: /* SPC - Parameter Data Format for SA RTPG */
- break;
- default:
- cdb[1] &= 0x1f; /* clear logical unit number */
- break;
- }
-
- /*
* For REPORT LUNS we always need to emulate the response, for everything
* else, pass it up.
*/
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 7159e83..7ee0a75 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -962,7 +962,7 @@ static int add_to_qfull_queue(struct tcmu_cmd *tcmu_cmd)
* 0 success
* 1 internally queued to wait for ring memory to free.
*/
-static sense_reason_t queue_cmd_ring(struct tcmu_cmd *tcmu_cmd, int *scsi_err)
+static int queue_cmd_ring(struct tcmu_cmd *tcmu_cmd, sense_reason_t *scsi_err)
{
struct tcmu_dev *udev = tcmu_cmd->tcmu_dev;
struct se_cmd *se_cmd = tcmu_cmd->se_cmd;
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index e1aafe8..2f254f9 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -696,8 +696,10 @@ static int __init optee_driver_init(void)
return -ENODEV;
np = of_find_matching_node(fw_np, optee_match);
- if (!np)
+ if (!np || !of_device_is_available(np)) {
+ of_node_put(np);
return -ENODEV;
+ }
optee = optee_probe(np);
of_node_put(np);
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index e16b3cb..1c9830b 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -526,8 +526,8 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
/* First memory region points towards the status register */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
+ if (!res)
+ return -EIO;
/*
* Edit the resource start address and length to map over all the
diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
index c5820e5..71785f2 100644
--- a/drivers/thermal/qcom/Makefile
+++ b/drivers/thermal/qcom/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o
-obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o
+obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o adc-tm7.o
obj-$(CONFIG_QTI_VIRTUAL_SENSOR) += qti_virtual_sensor.o
obj-$(CONFIG_QTI_QMI_SENSOR) += thermal_sensor_service_v01.o qmi_sensors.o
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
diff --git a/drivers/thermal/qcom/adc-tm-common.c b/drivers/thermal/qcom/adc-tm-common.c
index 343a631..89cb5ce 100644
--- a/drivers/thermal/qcom/adc-tm-common.c
+++ b/drivers/thermal/qcom/adc-tm-common.c
@@ -47,6 +47,181 @@ static const struct adc_tm_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
{ 46000, 125000 },
};
+/*
+ * Resistance to temperature table for NTCG104EF104 thermistor with
+ * 100k pull-up.
+ */
+static const struct adc_tm_map_pt adcmap_100k_adc7[] = {
+ { 4250657, -40960 },
+ { 3962085, -39936 },
+ { 3694875, -38912 },
+ { 3447322, -37888 },
+ { 3217867, -36864 },
+ { 3005082, -35840 },
+ { 2807660, -34816 },
+ { 2624405, -33792 },
+ { 2454218, -32768 },
+ { 2296094, -31744 },
+ { 2149108, -30720 },
+ { 2012414, -29696 },
+ { 1885232, -28672 },
+ { 1766846, -27648 },
+ { 1656598, -26624 },
+ { 1553884, -25600 },
+ { 1458147, -24576 },
+ { 1368873, -23552 },
+ { 1285590, -22528 },
+ { 1207863, -21504 },
+ { 1135290, -20480 },
+ { 1067501, -19456 },
+ { 1004155, -18432 },
+ { 944935, -17408 },
+ { 889550, -16384 },
+ { 837731, -15360 },
+ { 789229, -14336 },
+ { 743813, -13312 },
+ { 701271, -12288 },
+ { 661405, -11264 },
+ { 624032, -10240 },
+ { 588982, -9216 },
+ { 556100, -8192 },
+ { 525239, -7168 },
+ { 496264, -6144 },
+ { 469050, -5120 },
+ { 443480, -4096 },
+ { 419448, -3072 },
+ { 396851, -2048 },
+ { 375597, -1024 },
+ { 355598, 0 },
+ { 336775, 1024 },
+ { 319052, 2048 },
+ { 302359, 3072 },
+ { 286630, 4096 },
+ { 271806, 5120 },
+ { 257829, 6144 },
+ { 244646, 7168 },
+ { 232209, 8192 },
+ { 220471, 9216 },
+ { 209390, 10240 },
+ { 198926, 11264 },
+ { 189040, 12288 },
+ { 179698, 13312 },
+ { 170868, 14336 },
+ { 162519, 15360 },
+ { 154622, 16384 },
+ { 147150, 17408 },
+ { 140079, 18432 },
+ { 133385, 19456 },
+ { 127046, 20480 },
+ { 121042, 21504 },
+ { 115352, 22528 },
+ { 109960, 23552 },
+ { 104848, 24576 },
+ { 100000, 25600 },
+ { 95402, 26624 },
+ { 91038, 27648 },
+ { 86897, 28672 },
+ { 82965, 29696 },
+ { 79232, 30720 },
+ { 75686, 31744 },
+ { 72316, 32768 },
+ { 69114, 33792 },
+ { 66070, 34816 },
+ { 63176, 35840 },
+ { 60423, 36864 },
+ { 57804, 37888 },
+ { 55312, 38912 },
+ { 52940, 39936 },
+ { 50681, 40960 },
+ { 48531, 41984 },
+ { 46482, 43008 },
+ { 44530, 44032 },
+ { 42670, 45056 },
+ { 40897, 46080 },
+ { 39207, 47104 },
+ { 37595, 48128 },
+ { 36057, 49152 },
+ { 34590, 50176 },
+ { 33190, 51200 },
+ { 31853, 52224 },
+ { 30577, 53248 },
+ { 29358, 54272 },
+ { 28194, 55296 },
+ { 27082, 56320 },
+ { 26020, 57344 },
+ { 25004, 58368 },
+ { 24033, 59392 },
+ { 23104, 60416 },
+ { 22216, 61440 },
+ { 21367, 62464 },
+ { 20554, 63488 },
+ { 19776, 64512 },
+ { 19031, 65536 },
+ { 18318, 66560 },
+ { 17636, 67584 },
+ { 16982, 68608 },
+ { 16355, 69632 },
+ { 15755, 70656 },
+ { 15180, 71680 },
+ { 14628, 72704 },
+ { 14099, 73728 },
+ { 13592, 74752 },
+ { 13106, 75776 },
+ { 12640, 76800 },
+ { 12192, 77824 },
+ { 11762, 78848 },
+ { 11350, 79872 },
+ { 10954, 80896 },
+ { 10574, 81920 },
+ { 10209, 82944 },
+ { 9858, 83968 },
+ { 9521, 84992 },
+ { 9197, 86016 },
+ { 8886, 87040 },
+ { 8587, 88064 },
+ { 8299, 89088 },
+ { 8023, 90112 },
+ { 7757, 91136 },
+ { 7501, 92160 },
+ { 7254, 93184 },
+ { 7017, 94208 },
+ { 6789, 95232 },
+ { 6570, 96256 },
+ { 6358, 97280 },
+ { 6155, 98304 },
+ { 5959, 99328 },
+ { 5770, 100352 },
+ { 5588, 101376 },
+ { 5412, 102400 },
+ { 5243, 103424 },
+ { 5080, 104448 },
+ { 4923, 105472 },
+ { 4771, 106496 },
+ { 4625, 107520 },
+ { 4484, 108544 },
+ { 4348, 109568 },
+ { 4217, 110592 },
+ { 4090, 111616 },
+ { 3968, 112640 },
+ { 3850, 113664 },
+ { 3736, 114688 },
+ { 3626, 115712 },
+ { 3519, 116736 },
+ { 3417, 117760 },
+ { 3317, 118784 },
+ { 3221, 119808 },
+ { 3129, 120832 },
+ { 3039, 121856 },
+ { 2952, 122880 },
+ { 2868, 123904 },
+ { 2787, 124928 },
+ { 2709, 125952 },
+ { 2633, 126976 },
+ { 2560, 128000 },
+ { 2489, 129024 },
+ { 2420, 130048 }
+};
+
static void adc_tm_map_voltage_temp(const struct adc_tm_map_pt *pts,
size_t tablesize, int input, int *output)
{
@@ -140,6 +315,7 @@ int therm_fwd_scale(int64_t code, uint32_t adc_hc_vdd_ref_mv,
volt = (s64) code * adc_hc_vdd_ref_mv;
volt = div64_s64(volt, (data->full_scale_code_volt));
+ /* Same API can be used for resistance-temperature table */
adc_tm_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
(int) volt, &result);
@@ -148,6 +324,25 @@ int therm_fwd_scale(int64_t code, uint32_t adc_hc_vdd_ref_mv,
}
EXPORT_SYMBOL(therm_fwd_scale);
+int therm_fwd_scale_adc7(int64_t code)
+{
+ int64_t resistance = 0;
+ int result = 0;
+
+ if (code >= RATIO_MAX_ADC7)
+ return -EINVAL;
+
+ resistance = (s64) code * R_PU_100K;
+ resistance = div64_s64(resistance, (RATIO_MAX_ADC7 - code));
+
+ adc_tm_map_voltage_temp(adcmap_100k_adc7,
+ ARRAY_SIZE(adcmap_100k_adc7),
+ (int) resistance, &result);
+
+ return result;
+}
+EXPORT_SYMBOL(therm_fwd_scale_adc7);
+
void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
const struct adc_tm_data *data)
{
@@ -188,6 +383,47 @@ void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
}
EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k);
+void adc_tm_scale_therm_voltage_100k_adc7(struct adc_tm_config *param)
+{
+ int temp;
+ int64_t resistance = 0;
+
+ /* High temperature maps to lower threshold voltage */
+ /* Same API can be used for resistance-temperature table */
+ adc_tm_map_temp_voltage(
+ adcmap_100k_adc7,
+ ARRAY_SIZE(adcmap_100k_adc7),
+ param->high_thr_temp, &resistance);
+
+ param->low_thr_voltage = resistance * RATIO_MAX_ADC7;
+ param->low_thr_voltage = div64_s64(param->low_thr_voltage,
+ (resistance + R_PU_100K));
+
+ temp = therm_fwd_scale_adc7(param->low_thr_voltage);
+ if (temp == -EINVAL)
+ return;
+
+ if (temp < param->high_thr_temp)
+ param->low_thr_voltage--;
+
+ /* Low temperature maps to higher threshold voltage */
+ /* Same API can be used for resistance-temperature table */
+ adc_tm_map_temp_voltage(
+ adcmap_100k_adc7,
+ ARRAY_SIZE(adcmap_100k_adc7),
+ param->low_thr_temp, &resistance);
+
+ param->high_thr_voltage = resistance * RATIO_MAX_ADC7;
+ param->high_thr_voltage = div64_s64(param->high_thr_voltage,
+ (resistance + R_PU_100K));
+
+ temp = therm_fwd_scale_adc7(param->high_thr_voltage);
+
+ if (temp > param->low_thr_temp)
+ param->high_thr_voltage++;
+}
+EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k_adc7);
+
int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
struct adc_tm_config *tm_config)
{
@@ -195,11 +431,13 @@ int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
low_thr = div_s64(tm_config->low_thr_voltage, tm_config->prescal);
low_thr *= data->full_scale_code_volt;
+
low_thr = div64_s64(low_thr, ADC_HC_VDD_REF);
tm_config->low_thr_voltage = low_thr;
high_thr = div_s64(tm_config->high_thr_voltage, tm_config->prescal);
high_thr *= data->full_scale_code_volt;
+
high_thr = div64_s64(high_thr, ADC_HC_VDD_REF);
tm_config->high_thr_voltage = high_thr;
@@ -207,5 +445,25 @@ int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
}
EXPORT_SYMBOL(adc_tm_absolute_rthr);
+int32_t adc_tm_absolute_rthr_adc7(struct adc_tm_config *tm_config)
+{
+ int64_t low_thr = 0, high_thr = 0;
+
+ low_thr = div_s64(tm_config->low_thr_voltage, tm_config->prescal);
+ low_thr *= MAX_CODE_VOLT;
+
+ low_thr = div64_s64(low_thr, ADC_HC_VDD_REF);
+ tm_config->low_thr_voltage = low_thr;
+
+ high_thr = div_s64(tm_config->high_thr_voltage, tm_config->prescal);
+ high_thr *= MAX_CODE_VOLT;
+
+ high_thr = div64_s64(high_thr, ADC_HC_VDD_REF);
+ tm_config->high_thr_voltage = high_thr;
+
+ return 0;
+}
+EXPORT_SYMBOL(adc_tm_absolute_rthr_adc7);
+
MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/qcom/adc-tm.c b/drivers/thermal/qcom/adc-tm.c
index 537912c..195d2e9 100644
--- a/drivers/thermal/qcom/adc-tm.c
+++ b/drivers/thermal/qcom/adc-tm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/err.h>
@@ -51,6 +51,35 @@ static int adc_tm_init(struct adc_tm_chip *adc_tm, uint32_t dt_chans)
return 0;
}
+int32_t adc_tm5_channel_measure(struct adc_tm_chip *adc_tm,
+ struct adc_tm_param *param)
+{
+ if (adc_tm->ops->channel_measure)
+ return adc_tm->ops->channel_measure(adc_tm, param);
+
+ return 0;
+}
+EXPORT_SYMBOL(adc_tm5_channel_measure);
+
+int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *adc_tm,
+ struct adc_tm_param *param)
+{
+ if (adc_tm->ops->disable_chan)
+ return adc_tm->ops->disable_chan(adc_tm, param);
+
+ return 0;
+}
+EXPORT_SYMBOL(adc_tm5_disable_chan_meas);
+
+static void notify_adc_tm_fn(struct work_struct *work)
+{
+ struct adc_tm_sensor *adc_tm = container_of(work,
+ struct adc_tm_sensor, work);
+
+ if (adc_tm->chip->ops->notify)
+ adc_tm->chip->ops->notify(adc_tm);
+}
+
static struct thermal_zone_of_device_ops adc_tm_ops = {
.get_temp = adc_tm_get_temp,
.set_trips = adc_tm_set_trip_temp,
@@ -63,19 +92,24 @@ static struct thermal_zone_of_device_ops adc_tm_ops_iio = {
static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num,
bool set_trips)
{
- unsigned int i;
+ unsigned int i, channel;
struct thermal_zone_device *tzd;
for (i = 0; i < dt_chan_num; i++) {
adc_tm->sensor[i].chip = adc_tm;
+ if (adc_tm->data == &data_adc_tm7)
+ channel = V_CHAN(adc_tm->sensor[i]);
+ else
+ channel = adc_tm->sensor[i].adc_ch;
+
if (!adc_tm->sensor[i].non_thermal) {
if (set_trips)
tzd = devm_thermal_zone_of_sensor_register(
- adc_tm->dev, adc_tm->sensor[i].adc_ch,
- &adc_tm->sensor[i], &adc_tm_ops);
+ adc_tm->dev, channel,
+ &adc_tm->sensor[i], &adc_tm_ops);
else
tzd = devm_thermal_zone_of_sensor_register(
- adc_tm->dev, adc_tm->sensor[i].adc_ch,
+ adc_tm->dev, channel,
&adc_tm->sensor[i], &adc_tm_ops_iio);
if (IS_ERR(tzd)) {
@@ -136,7 +170,6 @@ struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name)
node = of_parse_phandle(dev->of_node, prop_name, 0);
if (node == NULL)
return ERR_PTR(-ENODEV);
-
list_for_each_entry(chip, &adc_tm_device_list, list) {
pdev = to_platform_device(chip->dev);
if (pdev->dev.of_node == node)
@@ -166,6 +199,10 @@ static const struct of_device_id adc_tm_match_table[] = {
.compatible = "qcom,adc-tm5-iio",
.data = &data_adc_tm5,
},
+ {
+ .compatible = "qcom,adc-tm7",
+ .data = &data_adc_tm7,
+ },
{}
};
@@ -224,7 +261,9 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
for_each_child_of_node(node, child) {
int channel_num, i = 0, adc_rscale_fn = 0;
int calib_type = 0, ret, hw_settle_time = 0;
+ int decimation, fast_avg_samples;
int prescal = 0;
+ u32 sid = 0;
struct iio_channel *chan_adc;
bool non_thermal = false;
@@ -234,6 +273,11 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
return -EINVAL;
}
+ if (data == &data_adc_tm7) {
+ sid = (channel_num >> ADC_CHANNEL_OFFSET);
+ channel_num &= ADC_CHANNEL_MASK;
+ }
+
ret = of_property_read_u32(child, "qcom,hw-settle-time",
&hw_settle_time);
if (!ret) {
@@ -244,9 +288,34 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
return ret;
}
hw_settle_time = ret;
- } else {
+ } else
hw_settle_time = ADC_TM_DEF_HW_SETTLE_TIME;
- }
+
+ ret = of_property_read_u32(child, "qcom,decimation",
+ &decimation);
+ if (!ret) {
+ ret = adc_tm_decimation_from_dt(decimation,
+ data->decimation);
+ if (ret < 0) {
+ dev_err(dev, "Invalid decimation value\n");
+ return ret;
+ }
+ decimation = ret;
+ } else
+ decimation = ADC_TM_DECIMATION_DEFAULT;
+
+ ret = of_property_read_u32(child, "qcom,avg-samples",
+ &fast_avg_samples);
+ if (!ret) {
+ ret = adc_tm_avg_samples_from_dt(fast_avg_samples);
+ if (ret < 0) {
+ dev_err(dev, "Invalid fast average with %d\n",
+ ret);
+ return -EINVAL;
+ }
+ fast_avg_samples = ret;
+ } else
+ fast_avg_samples = ADC_TM_DEF_AVG_SAMPLES;
if (of_property_read_bool(child, "qcom,ratiometric"))
calib_type = ADC_RATIO_CAL;
@@ -271,6 +340,16 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
/* Default to 1 second timer select */
adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2;
adc_tm->sensor[idx].hw_settle_time = hw_settle_time;
+
+ if (data == &data_adc_tm7) {
+ /* Default to 1 second time select */
+ adc_tm->sensor[idx].meas_time = MEAS_INT_1S;
+ adc_tm->sensor[idx].fast_avg_samples = fast_avg_samples;
+ adc_tm->sensor[idx].decimation = decimation;
+ adc_tm->sensor[idx].sid = sid;
+ adc_tm->sensor[idx].btm_ch = idx;
+ }
+
adc_tm->sensor[idx].adc_rscale_fn = adc_rscale_fn;
adc_tm->sensor[idx].non_thermal = non_thermal;
adc_tm->sensor[idx].prescaling = prescal;
diff --git a/drivers/thermal/qcom/adc-tm.h b/drivers/thermal/qcom/adc-tm.h
index 46f6db4..b616aff 100644
--- a/drivers/thermal/qcom/adc-tm.h
+++ b/drivers/thermal/qcom/adc-tm.h
@@ -15,7 +15,7 @@
#include <linux/qpnp/qpnp-revid.h>
#include <linux/adc-tm-clients.h>
-#define ADC_TM_DECIMATION_DEFAULT 840
+#define ADC_TM_DECIMATION_DEFAULT 2
#define ADC_TM_DECIMATION_SAMPLES_MAX 3
#define ADC_TM_DEF_AVG_SAMPLES 0 /* 1 sample */
#define ADC_TM_DEF_HW_SETTLE_TIME 0 /* 15 us */
@@ -25,7 +25,13 @@
#define ADC_TM_TIMER2 10 /* 1 second */
#define ADC_TM_TIMER3 4 /* 4 second */
#define ADC_HC_VDD_REF 1875000
-#define MAX_PROP_NAME_LEN 32
+#define MAX_PROP_NAME_LEN 32
+#define R_PU_100K 100000
+#define RATIO_MAX_ADC7 0x4000
+#define MAX_CODE_VOLT 0x70e4
+#define ADC_CHANNEL_OFFSET 8
+#define V_CHAN(x) (((x).sid << ADC_CHANNEL_OFFSET) | (x).adc_ch)
+#define ADC_CHANNEL_MASK 0xff
enum adc_cal_method {
ADC_NO_CAL = 0,
@@ -47,6 +53,14 @@ enum adc_timer_select {
ADC_TIMER_SEL_NONE,
};
+enum adc_time_select {
+ MEAS_INT_50MS = 0,
+ MEAS_INT_100MS,
+ MEAS_INT_1S,
+ MEAS_INT_SET,
+ MEAS_INT_NONE,
+};
+
/**
* enum adc_tm_rscale_fn_type - Scaling function used to convert the
* channels input voltage/temperature to corresponding ADC code that is
@@ -73,12 +87,21 @@ struct adc_tm_sensor {
unsigned int btm_ch;
unsigned int prescaling;
unsigned int timer_select;
+ unsigned int decimation; /* PMIC7 */
+ unsigned int fast_avg_samples; /* PMIC7 */
+ unsigned int sid; /* PMIC7 */
+ unsigned int meas_time; /* PMIC7 */
+ int64_t high_thr_voltage; /* PMIC7 */
+ int64_t low_thr_voltage; /* PMIC7 */
enum adc_tm_rscale_fn_type adc_rscale_fn;
struct iio_channel *adc;
struct list_head thr_list;
- bool non_thermal;
+ bool non_thermal;
bool high_thr_triggered;
bool low_thr_triggered;
+ int high_thr_en; /* PMIC7 */
+ int low_thr_en; /* PMIC7 */
+ int meas_en; /* PMIC7 */
struct workqueue_struct *req_wq;
struct work_struct work;
};
@@ -108,6 +131,11 @@ struct adc_tm_ops {
int (*init)(struct adc_tm_chip *chip, uint32_t dt_chans);
int (*set_trips)(struct adc_tm_sensor *sensor, int low_temp,
int high_temp);
+ int32_t (*channel_measure)(struct adc_tm_chip *chip,
+ struct adc_tm_param *param);
+ int32_t (*disable_chan)(struct adc_tm_chip *chip,
+ struct adc_tm_param *param);
+ void (*notify)(struct adc_tm_sensor *adc_tm);
int (*interrupts_reg)(struct adc_tm_chip *chip);
int (*shutdown)(struct adc_tm_chip *chip);
};
@@ -135,6 +163,8 @@ struct adc_tm_data {
};
extern const struct adc_tm_data data_adc_tm5;
+extern const struct adc_tm_data data_adc_tm7;
+
/**
* Channel index for the corresponding index to adc_tm_channel_select
*/
@@ -230,6 +260,10 @@ struct adc_tm_reverse_scale_fn {
struct adc_tm_config *tm_config);
};
+struct adc_tm_reverse_scale_fn_adc7 {
+ int32_t (*chan)(struct adc_tm_config *tm_config);
+};
+
/**
* struct adc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
@@ -265,7 +299,11 @@ void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
struct adc_tm_config *tm_config);
-void notify_adc_tm_fn(struct work_struct *work);
+int therm_fwd_scale_adc7(int64_t code);
+
+void adc_tm_scale_therm_voltage_100k_adc7(struct adc_tm_config *param);
+
+int32_t adc_tm_absolute_rthr_adc7(struct adc_tm_config *tm_config);
int adc_tm_is_valid(struct adc_tm_chip *chip);
diff --git a/drivers/thermal/qcom/adc-tm5.c b/drivers/thermal/qcom/adc-tm5.c
index 286be8c..3371802 100644
--- a/drivers/thermal/qcom/adc-tm5.c
+++ b/drivers/thermal/qcom/adc-tm5.c
@@ -418,7 +418,8 @@ static int32_t adc_tm5_manage_thresholds(struct adc_tm_sensor *sensor)
return 0;
}
-void notify_adc_tm_fn(struct work_struct *work)
+/* Used to notify non-thermal clients of threshold crossing */
+void notify_adc_tm5_fn(struct adc_tm_sensor *adc_tm)
{
struct adc_tm_client_info *client_info = NULL;
struct adc_tm_chip *chip;
@@ -426,9 +427,6 @@ void notify_adc_tm_fn(struct work_struct *work)
uint32_t btm_chan_num = 0, btm_chan_idx = 0;
int ret = 0;
- struct adc_tm_sensor *adc_tm = container_of(work,
- struct adc_tm_sensor, work);
-
chip = adc_tm->chip;
btm_chan_num = adc_tm->btm_ch;
@@ -510,7 +508,8 @@ void notify_adc_tm_fn(struct work_struct *work)
}
}
-int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip,
+/* Used by non-thermal clients to configure an ADC_TM channel */
+static int32_t adc_tm_5_channel_measure(struct adc_tm_chip *chip,
struct adc_tm_param *param)
{
@@ -612,9 +611,9 @@ int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip,
mutex_unlock(&chip->adc_mutex_lock);
return ret;
}
-EXPORT_SYMBOL(adc_tm5_channel_measure);
-int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip,
+/* Used by non-thermal clients to release an ADC_TM channel */
+static int32_t adc_tm_5_disable_chan_meas(struct adc_tm_chip *chip,
struct adc_tm_param *param)
{
int ret = 0, i = 0;
@@ -672,7 +671,6 @@ int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip,
spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
return ret;
}
-EXPORT_SYMBOL(adc_tm5_disable_chan_meas);
static int adc_tm5_set_mode(struct adc_tm_sensor *sensor,
enum thermal_device_mode mode)
@@ -1110,6 +1108,9 @@ static const struct adc_tm_ops ops_adc_tm5 = {
.set_trips = adc_tm5_set_trip_temp,
.interrupts_reg = adc_tm5_register_interrupts,
.get_temp = adc_tm5_get_temp,
+ .channel_measure = adc_tm_5_channel_measure,
+ .disable_chan = adc_tm_5_disable_chan_meas,
+ .notify = notify_adc_tm5_fn,
};
const struct adc_tm_data data_adc_tm5 = {
diff --git a/drivers/thermal/qcom/adc-tm7.c b/drivers/thermal/qcom/adc-tm7.c
new file mode 100644
index 0000000..2d45ced
--- /dev/null
+++ b/drivers/thermal/qcom/adc-tm7.c
@@ -0,0 +1,834 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/iio/consumer.h>
+#include "adc-tm.h"
+#include "../thermal_core.h"
+
+#define ADC_TM_STATUS1 0x08
+#define ADC_TM_STATUS_LOW_SET 0x09
+#define ADC_TM_STATUS_LOW_CLR 0x0a
+#define ADC_TM_STATUS_HIGH_SET 0x0b
+#define ADC_TM_STATUS_HIGH_CLR 0x0c
+
+#define ADC_TM_CFG_HS_SET 0x0d
+#define ADC_TM_CFG_HS_FLAG BIT(0)
+#define ADC_TM_CFG_HS_CLR 0x0e
+
+#define ADC_TM_NUM_BTM_CHAN 0x0f
+
+#define ADC_TM_SID 0x40
+
+#define ADC_TM_CH_CTL 0x41
+#define ADC_TM_TM_CH_SEL GENMASK(7, 5)
+#define ADC_TM_TM_CH_SEL_MASK_SHIFT 5
+#define ADC_TM_MEAS_INT_SEL GENMASK(3, 2)
+#define ADC_TM_MEAS_INT_SEL_MASK_SHIFT 2
+
+#define ADC_TM_ADC_DIG_PARAM 0x42
+#define ADC_TM_CTL_DEC_RATIO_MASK GENMASK(3, 2)
+#define ADC_TM_CTL_DEC_RATIO_SHIFT 2
+#define ADC_TM_CTL_CAL_SEL GENMASK(5, 4)
+#define ADC_TM_CTL_CAL_SEL_MASK_SHIFT 4
+
+#define ADC_TM_FAST_AVG_CTL 0x43
+#define ADC_TM_FAST_AVG_EN BIT(7)
+
+#define ADC_TM_ADC_CH_SEL_CTL 0x44
+
+#define ADC_TM_DELAY_CTL 0x45
+
+#define ADC_TM_EN_CTL1 0x46
+#define ADC_TM_EN BIT(7)
+
+#define ADC_TM_CONV_REQ 0x47
+#define ADC_TM_CONV_REQ_EN BIT(7)
+
+#define ADC_TM_DATA_HOLD_CTL 0x48
+
+#define ADC_TM_LOW_THR0 0x49
+#define ADC_TM_LOW_THR1 0x4a
+#define ADC_TM_HIGH_THR0 0x4b
+#define ADC_TM_HIGH_THR1 0x4c
+#define ADC_TM_LOWER_MASK(n) ((n) & GENMASK(7, 0))
+#define ADC_TM_UPPER_MASK(n) (((n) & GENMASK(15, 8)) >> 8)
+
+#define ADC_TM_MEAS_IRQ_EN 0x4d
+#define ADC_TM_MEAS_EN BIT(7)
+
+#define ADC_TM_MEAS_INT_LSB 0x50
+#define ADC_TM_MEAS_INT_MSB 0x51
+#define ADC_TM_MEAS_INT_MODE 0x52
+
+#define ADC_TM_Mn_DATA0(n) ((n * 2) + 0xa0)
+#define ADC_TM_Mn_DATA1(n) ((n * 2) + 0xa1)
+#define ADC_TM_DATA_SHIFT 8
+
+#define ADC_TM_POLL_DELAY_MIN_US 100
+#define ADC_TM_POLL_DELAY_MAX_US 110
+#define ADC_TM_POLL_RETRY_COUNT 3
+
+static struct adc_tm_reverse_scale_fn_adc7 adc_tm_rscale_fn[] = {
+ [SCALE_R_ABSOLUTE] = {adc_tm_absolute_rthr_adc7},
+};
+
+static int adc_tm_get_temp(struct adc_tm_sensor *sensor, int *temp)
+{
+ int ret, milli_celsius;
+
+ if (!sensor || !sensor->adc)
+ return -EINVAL;
+
+ ret = iio_read_channel_processed(sensor->adc, &milli_celsius);
+ if (ret < 0)
+ return ret;
+
+ *temp = milli_celsius;
+
+ return 0;
+}
+
+static int32_t adc_tm_read_reg(struct adc_tm_chip *chip,
+ int16_t reg, u8 *data, int len)
+{
+ int ret;
+
+ ret = regmap_bulk_read(chip->regmap, (chip->base + reg), data, len);
+ if (ret < 0)
+ pr_err("adc-tm read reg %d failed with %d\n", reg, ret);
+
+ return ret;
+}
+
+static int32_t adc_tm_write_reg(struct adc_tm_chip *chip,
+ int16_t reg, u8 *data, int len)
+{
+ int ret;
+
+ ret = regmap_bulk_write(chip->regmap, (chip->base + reg), data, len);
+ if (ret < 0)
+ pr_err("adc-tm write reg %d failed with %d\n", reg, ret);
+
+ return ret;
+}
+
+static int32_t adc_tm7_conv_req(struct adc_tm_chip *chip)
+{
+ int rc = 0;
+ u8 data = 0;
+ unsigned int count;
+
+ data = ADC_TM_EN;
+ rc = adc_tm_write_reg(chip, ADC_TM_EN_CTL1, &data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm enable failed with %d\n", rc);
+ return rc;
+ }
+
+ data = ADC_TM_CFG_HS_FLAG;
+ rc = adc_tm_write_reg(chip, ADC_TM_CFG_HS_SET, &data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm handshake failed with %d\n", rc);
+ return rc;
+ }
+
+ data = ADC_TM_CONV_REQ_EN;
+ rc = adc_tm_write_reg(chip, ADC_TM_CONV_REQ, &data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm request conversion failed with %d\n", rc);
+ return rc;
+ }
+
+ for (count = 0; count < ADC_TM_POLL_RETRY_COUNT; count++) {
+ rc = adc_tm_read_reg(chip, ADC_TM_CFG_HS_SET, &data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (!(data & ADC_TM_CFG_HS_FLAG))
+ return rc;
+ usleep_range(ADC_TM_POLL_DELAY_MIN_US,
+ ADC_TM_POLL_DELAY_MAX_US);
+ }
+
+ pr_err("adc-tm conversion request handshake timed out\n");
+
+ return -ETIMEDOUT;
+}
+
+static int adc_tm7_configure(struct adc_tm_sensor *sensor)
+{
+ struct adc_tm_chip *chip = sensor->chip;
+ u8 buf[14];
+ uint32_t mask = 0;
+ int ret;
+
+ ret = adc_tm_read_reg(chip, ADC_TM_SID, buf, 14);
+ if (ret < 0) {
+ pr_err("adc-tm block read failed with %d\n", ret);
+ return ret;
+ }
+
+ buf[0] = sensor->sid;
+ buf[1] &= ~ADC_TM_TM_CH_SEL;
+ buf[1] |= sensor->btm_ch << ADC_TM_TM_CH_SEL_MASK_SHIFT;
+ buf[1] &= ~ADC_TM_MEAS_INT_SEL;
+ buf[1] |= sensor->meas_time << ADC_TM_MEAS_INT_SEL_MASK_SHIFT;
+
+ /* Set calibration select, hw_settle delay */
+ buf[2] &= ~ADC_TM_CTL_DEC_RATIO_MASK;
+ buf[2] |= sensor->decimation << ADC_TM_CTL_DEC_RATIO_SHIFT;
+ buf[2] &= ~ADC_TM_CTL_CAL_SEL;
+ buf[2] |= sensor->cal_sel << ADC_TM_CTL_CAL_SEL_MASK_SHIFT;
+
+ buf[3] = sensor->fast_avg_samples | ADC_TM_FAST_AVG_EN;
+
+ buf[4] = sensor->adc_ch;
+
+ buf[5] = sensor->hw_settle_time;
+
+ mask = lower_32_bits(sensor->low_thr_voltage);
+ buf[9] = ADC_TM_LOWER_MASK(mask);
+ buf[10] = ADC_TM_UPPER_MASK(mask);
+
+ mask = lower_32_bits(sensor->high_thr_voltage);
+ buf[11] = ADC_TM_LOWER_MASK(mask);
+ buf[12] = ADC_TM_UPPER_MASK(mask);
+
+ buf[13] = (sensor->meas_en | sensor->high_thr_en << 1 |
+ sensor->low_thr_en);
+
+ ret = adc_tm_write_reg(chip, ADC_TM_SID, buf, 14);
+ if (ret < 0)
+ pr_err("adc-tm block write failed with %d\n", ret);
+
+ return ret;
+}
+
+static int32_t adc_tm_add_to_list(struct adc_tm_chip *chip,
+ uint32_t dt_index,
+ struct adc_tm_param *param)
+{
+ struct adc_tm_client_info *client_info = NULL;
+ bool client_info_exists = false;
+
+ list_for_each_entry(client_info,
+ &chip->sensor[dt_index].thr_list, list) {
+ if (client_info->param->id == param->id) {
+ client_info->low_thr_requested = param->low_thr;
+ client_info->high_thr_requested = param->high_thr;
+ client_info->state_request = param->state_request;
+ client_info->notify_low_thr = false;
+ client_info->notify_high_thr = false;
+ client_info_exists = true;
+ pr_debug("client found\n");
+ }
+ }
+
+ if (!client_info_exists) {
+ client_info = devm_kzalloc(chip->dev,
+ sizeof(struct adc_tm_client_info), GFP_KERNEL);
+ if (!client_info)
+ return -ENOMEM;
+
+ pr_debug("new client\n");
+ client_info->param->id = (uintptr_t) client_info;
+ client_info->low_thr_requested = param->low_thr;
+ client_info->high_thr_requested = param->high_thr;
+ client_info->state_request = param->state_request;
+
+ list_add_tail(&client_info->list,
+ &chip->sensor[dt_index].thr_list);
+ }
+ return 0;
+}
+
+static int32_t adc_tm7_manage_thresholds(struct adc_tm_sensor *sensor)
+{
+ int high_thr = INT_MAX, low_thr = INT_MIN;
+ struct adc_tm_client_info *client_info = NULL;
+ struct list_head *thr_list;
+ uint32_t scale_type = 0;
+ struct adc_tm_config tm_config;
+
+ sensor->high_thr_en = 0;
+ sensor->low_thr_en = 0;
+
+ /*
+ * Reset the high_thr_set and low_thr_set of all
+ * clients since the thresholds will be recomputed.
+ */
+ list_for_each(thr_list, &sensor->thr_list) {
+ client_info = list_entry(thr_list,
+ struct adc_tm_client_info, list);
+ client_info->high_thr_set = false;
+ client_info->low_thr_set = false;
+ }
+
+ /* Find the min of high_thr and max of low_thr */
+ list_for_each(thr_list, &sensor->thr_list) {
+ client_info = list_entry(thr_list,
+ struct adc_tm_client_info, list);
+
+ if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) ||
+ (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE))
+ if (client_info->high_thr_requested < high_thr)
+ high_thr = client_info->high_thr_requested;
+
+ if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) ||
+ (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE))
+ if (client_info->low_thr_requested > low_thr)
+ low_thr = client_info->low_thr_requested;
+
+ pr_debug("threshold compared is high:%d and low:%d\n",
+ client_info->high_thr_requested,
+ client_info->low_thr_requested);
+ pr_debug("current threshold is high:%d and low:%d\n",
+ high_thr, low_thr);
+ }
+
+ /* Check which of the high_thr and low_thr got set */
+ list_for_each(thr_list, &sensor->thr_list) {
+ client_info = list_entry(thr_list,
+ struct adc_tm_client_info, list);
+
+ if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) ||
+ (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE))
+ if (high_thr == client_info->high_thr_requested) {
+ sensor->high_thr_en = 1;
+ client_info->high_thr_set = true;
+ }
+
+ if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) ||
+ (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE))
+ if (low_thr == client_info->low_thr_requested) {
+ sensor->low_thr_en = 1;
+ client_info->low_thr_set = true;
+ }
+ }
+
+ tm_config.high_thr_voltage = (int64_t)high_thr;
+ tm_config.low_thr_voltage = (int64_t)low_thr;
+ tm_config.prescal = sensor->prescaling;
+
+ scale_type = sensor->adc_rscale_fn;
+ if (scale_type >= SCALE_RSCALE_NONE)
+ return -EBADF;
+
+ adc_tm_rscale_fn[scale_type].chan(&tm_config);
+
+ sensor->low_thr_voltage = tm_config.low_thr_voltage;
+ sensor->high_thr_voltage = tm_config.high_thr_voltage;
+
+ pr_debug("threshold written is high:%d and low:%d\n",
+ high_thr, low_thr);
+
+ return 0;
+}
+
+/* Used to notify non-thermal clients of threshold crossing */
+void notify_adc_tm7_fn(struct adc_tm_sensor *adc_tm)
+{
+ struct adc_tm_client_info *client_info = NULL;
+ struct adc_tm_chip *chip;
+ struct list_head *thr_list;
+ int ret;
+
+ chip = adc_tm->chip;
+
+ mutex_lock(&chip->adc_mutex_lock);
+ if (adc_tm->low_thr_triggered) {
+ /* adjust thr, calling manage_thr */
+ list_for_each(thr_list, &adc_tm->thr_list) {
+ client_info = list_entry(thr_list,
+ struct adc_tm_client_info, list);
+ if (client_info->low_thr_set) {
+ client_info->low_thr_set = false;
+ client_info->notify_low_thr = true;
+ if (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)
+ client_info->state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ else
+ client_info->state_request =
+ ADC_TM_LOW_THR_DISABLE;
+ }
+ }
+ adc_tm->low_thr_triggered = false;
+ }
+
+ if (adc_tm->high_thr_triggered) {
+ /* adjust thr, calling manage_thr */
+ list_for_each(thr_list, &adc_tm->thr_list) {
+ client_info = list_entry(thr_list,
+ struct adc_tm_client_info, list);
+ if (client_info->high_thr_set) {
+ client_info->high_thr_set = false;
+ client_info->notify_high_thr = true;
+ if (client_info->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)
+ client_info->state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ else
+ client_info->state_request =
+ ADC_TM_HIGH_THR_DISABLE;
+ }
+ }
+ adc_tm->high_thr_triggered = false;
+ }
+ ret = adc_tm7_manage_thresholds(adc_tm);
+ if (ret < 0)
+ pr_err("Error in reverse scaling:%d\n", ret);
+
+ ret = adc_tm7_configure(adc_tm);
+ if (ret < 0)
+ pr_err("Error during adc-tm configure:%d\n", ret);
+
+ ret = adc_tm7_conv_req(chip);
+ if (ret < 0)
+ pr_err("Error enabling adc-tm with %d\n", ret);
+
+ mutex_unlock(&chip->adc_mutex_lock);
+
+ list_for_each_entry(client_info, &adc_tm->thr_list, list) {
+ if (client_info->notify_low_thr) {
+ if (client_info->param->threshold_notification
+ != NULL) {
+ pr_debug("notify kernel with low state\n");
+ client_info->param->threshold_notification(
+ ADC_TM_LOW_STATE,
+ client_info->param->btm_ctx);
+ client_info->notify_low_thr = false;
+ }
+ }
+
+ if (client_info->notify_high_thr) {
+ if (client_info->param->threshold_notification
+ != NULL) {
+ pr_debug("notify kernel with high state\n");
+ client_info->param->threshold_notification(
+ ADC_TM_HIGH_STATE,
+ client_info->param->btm_ctx);
+ client_info->notify_high_thr = false;
+ }
+ }
+ }
+}
+
+/* Used by non-thermal clients to configure an ADC_TM channel */
+static int32_t adc_tm7_channel_measure(struct adc_tm_chip *chip,
+ struct adc_tm_param *param)
+
+{
+ int ret, i;
+ uint32_t v_channel, dt_index = 0;
+ bool chan_found = false;
+
+ ret = adc_tm_is_valid(chip);
+ if (ret || (param == NULL))
+ return -EINVAL;
+
+ if (param->threshold_notification == NULL) {
+ pr_debug("No notification for high/low temp\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < chip->dt_channels; i++) {
+ v_channel = V_CHAN(chip->sensor[i]);
+ if (v_channel == param->channel) {
+ dt_index = i;
+ chan_found = true;
+ break;
+ }
+ }
+
+ if (!chan_found) {
+ pr_err("not a valid ADC_TM channel\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->adc_mutex_lock);
+
+ /* add channel client to channel list */
+ adc_tm_add_to_list(chip, dt_index, param);
+
+ /* set right thresholds for the sensor */
+ ret = adc_tm7_manage_thresholds(&chip->sensor[dt_index]);
+ if (ret < 0)
+ pr_err("Error in reverse scaling:%d\n", ret);
+
+ chip->sensor[dt_index].meas_en = ADC_TM_MEAS_EN;
+
+ /* configure channel */
+ ret = adc_tm7_configure(&chip->sensor[dt_index]);
+ if (ret < 0) {
+ pr_err("Error during adc-tm configure:%d\n", ret);
+ goto fail_unlock;
+ }
+
+ ret = adc_tm7_conv_req(chip);
+ if (ret < 0)
+ pr_err("Error enabling adc-tm with %d\n", ret);
+
+fail_unlock:
+ mutex_unlock(&chip->adc_mutex_lock);
+ return ret;
+}
+
+/* Used by non-thermal clients to release an ADC_TM channel */
+static int32_t adc_tm7_disable_chan_meas(struct adc_tm_chip *chip,
+ struct adc_tm_param *param)
+{
+ int ret, i;
+ uint32_t dt_index = 0, v_channel;
+ struct adc_tm_client_info *client_info = NULL;
+
+ ret = adc_tm_is_valid(chip);
+ if (ret || (param == NULL))
+ return -EINVAL;
+
+ for (i = 0; i < chip->dt_channels; i++) {
+ v_channel = V_CHAN(chip->sensor[i]);
+ if (v_channel == param->channel) {
+ dt_index = i;
+ break;
+ }
+ }
+
+ if (i == chip->dt_channels) {
+ pr_err("not a valid ADC_TM channel\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->adc_mutex_lock);
+ list_for_each_entry(client_info,
+ &chip->sensor[i].thr_list, list) {
+ if (client_info->param->id == param->id) {
+ client_info->state_request =
+ ADC_TM_HIGH_LOW_THR_DISABLE;
+ ret = adc_tm7_manage_thresholds(&chip->sensor[i]);
+ if (ret < 0) {
+ pr_err("Error in reverse scaling:%d\n",
+ ret);
+ goto fail;
+ }
+ ret = adc_tm7_configure(&chip->sensor[i]);
+ if (ret < 0) {
+ pr_err("Error during adc-tm configure:%d\n",
+ ret);
+ goto fail;
+ }
+ ret = adc_tm7_conv_req(chip);
+ if (ret < 0) {
+ pr_err("Error enabling adc-tm with %d\n", ret);
+ goto fail;
+ }
+ }
+ }
+
+fail:
+ mutex_unlock(&chip->adc_mutex_lock);
+ return ret;
+}
+
+static int adc_tm7_set_trip_temp(struct adc_tm_sensor *sensor,
+ int low_temp, int high_temp)
+{
+ struct adc_tm_chip *chip;
+ struct adc_tm_config tm_config;
+ int ret;
+
+ if (!sensor)
+ return -EINVAL;
+
+ pr_debug("%s:low_temp(mdegC):%d, high_temp(mdegC):%d\n", __func__,
+ low_temp, high_temp);
+
+ chip = sensor->chip;
+ tm_config.high_thr_temp = tm_config.low_thr_temp = 0;
+ if (high_temp != INT_MAX)
+ tm_config.high_thr_temp = high_temp;
+ if (low_temp != INT_MIN)
+ tm_config.low_thr_temp = low_temp;
+
+ if ((high_temp == INT_MAX) && (low_temp == INT_MIN)) {
+ pr_err("No trips to set\n");
+ return -EINVAL;
+ }
+
+ pr_debug("requested a low temp- %d and high temp- %d\n",
+ tm_config.low_thr_temp, tm_config.high_thr_temp);
+ adc_tm_scale_therm_voltage_100k_adc7(&tm_config);
+
+ pr_debug("high_thr:0x%llx, low_thr:0x%llx\n",
+ tm_config.high_thr_voltage, tm_config.low_thr_voltage);
+
+ mutex_lock(&chip->adc_mutex_lock);
+
+ if (high_temp != INT_MAX) {
+ sensor->low_thr_voltage = tm_config.low_thr_voltage;
+ sensor->low_thr_en = 1;
+ } else
+ sensor->low_thr_en = 0;
+
+ if (low_temp != INT_MIN) {
+ sensor->high_thr_voltage = tm_config.high_thr_voltage;
+ sensor->high_thr_en = 1;
+ } else
+ sensor->high_thr_en = 0;
+
+ sensor->meas_en = ADC_TM_MEAS_EN;
+
+ ret = adc_tm7_configure(sensor);
+ if (ret < 0) {
+ pr_err("Error during adc-tm configure:%d\n", ret);
+ goto fail;
+ }
+
+ ret = adc_tm7_conv_req(chip);
+ if (ret < 0) {
+ pr_err("Error enabling adc-tm with %d\n", ret);
+ goto fail;
+ }
+
+fail:
+ mutex_unlock(&chip->adc_mutex_lock);
+
+ return ret;
+}
+
+static irqreturn_t adc_tm7_handler(int irq, void *data)
+{
+ struct adc_tm_chip *chip = data;
+ u8 status_low, status_high, buf[16], val;
+ int ret, i;
+
+ ret = adc_tm_read_reg(chip, ADC_TM_STATUS_LOW_CLR, &status_low, 1);
+ if (ret < 0) {
+ pr_err("adc-tm read status low failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ ret = adc_tm_read_reg(chip, ADC_TM_STATUS_HIGH_CLR, &status_high, 1);
+ if (ret < 0) {
+ pr_err("adc-tm read status high failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ ret = adc_tm_write_reg(chip, ADC_TM_STATUS_LOW_CLR, &status_low, 1);
+ if (ret < 0) {
+ pr_err("adc-tm clear status low failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ ret = adc_tm_write_reg(chip, ADC_TM_STATUS_HIGH_CLR, &status_high, 1);
+ if (ret < 0) {
+ pr_err("adc-tm clear status high failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ val = BIT(0);
+ ret = adc_tm_write_reg(chip, ADC_TM_DATA_HOLD_CTL, &val, 1);
+ if (ret < 0) {
+ pr_err("adc-tm set hold failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ ret = adc_tm_read_reg(chip, ADC_TM_Mn_DATA0(0), buf, 16);
+ if (ret < 0) {
+ pr_err("adc-tm read conversion data failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ val = 0;
+ ret = adc_tm_write_reg(chip, ADC_TM_DATA_HOLD_CTL, &val, 1);
+ if (ret < 0) {
+ pr_err("adc-tm clear hold failed with %d\n", ret);
+ goto handler_end;
+ }
+
+ for (i = 0; i < chip->dt_channels; i++) {
+ bool upper_set = false, lower_set = false;
+ u8 data_low = 0, data_high = 0;
+ u16 code = 0;
+ int temp;
+
+ if (!chip->sensor[i].non_thermal &&
+ IS_ERR(chip->sensor[i].tzd)) {
+ pr_err("thermal device not found\n");
+ continue;
+ }
+
+ if (!chip->sensor[i].non_thermal) {
+ data_low = buf[2 * i];
+ data_high = buf[2 * i + 1];
+ code = ((data_high << ADC_TM_DATA_SHIFT) | data_low);
+ }
+
+ mutex_lock(&chip->adc_mutex_lock);
+
+ if ((status_low & 0x1) && (chip->sensor[i].meas_en)
+ && (chip->sensor[i].low_thr_en))
+ lower_set = true;
+
+ if ((status_high & 0x1) && (chip->sensor[i].meas_en) &&
+ (chip->sensor[i].high_thr_en))
+ upper_set = true;
+
+ status_low >>= 1;
+ status_high >>= 1;
+ mutex_unlock(&chip->adc_mutex_lock);
+ if (!(upper_set || lower_set))
+ continue;
+
+ if (!chip->sensor[i].non_thermal) {
+ /*
+ * Expected behavior is while notifying
+ * of_thermal, thermal core will call set_trips
+ * with new thresholds and activate/disable
+ * the appropriate trips.
+ */
+ pr_debug("notifying of_thermal\n");
+ temp = therm_fwd_scale_adc7((int64_t)code);
+ if (temp == -EINVAL) {
+ pr_err("Invalid temperature reading\n");
+ continue;
+ }
+ of_thermal_handle_trip_temp(chip->sensor[i].tzd,
+ temp);
+ } else {
+ if (lower_set) {
+ mutex_lock(&chip->adc_mutex_lock);
+ chip->sensor[i].low_thr_en = 0;
+ chip->sensor[i].low_thr_triggered = true;
+ mutex_unlock(&chip->adc_mutex_lock);
+ queue_work(chip->sensor[i].req_wq,
+ &chip->sensor[i].work);
+ }
+
+ if (upper_set) {
+ mutex_lock(&chip->adc_mutex_lock);
+ chip->sensor[i].high_thr_en = 0;
+ chip->sensor[i].high_thr_triggered = true;
+ mutex_unlock(&chip->adc_mutex_lock);
+ queue_work(chip->sensor[i].req_wq,
+ &chip->sensor[i].work);
+ }
+ }
+ }
+
+handler_end:
+ return IRQ_HANDLED;
+}
+
+static int adc_tm7_register_interrupts(struct adc_tm_chip *chip)
+{
+ struct platform_device *pdev;
+ int ret, irq;
+
+ if (!chip)
+ return -EINVAL;
+
+ pdev = to_platform_device(chip->dev);
+
+ irq = platform_get_irq_byname(pdev, "thr-int-en");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq %s\n",
+ "thr-int-en");
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ adc_tm7_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "thr-int-en", chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get irq %s\n",
+ "thr-int-en");
+ return ret;
+ }
+
+ enable_irq_wake(irq);
+
+ return ret;
+}
+
+static int adc_tm7_init(struct adc_tm_chip *chip, uint32_t dt_chans)
+{
+ u8 channels_available;
+ int ret;
+
+ ret = adc_tm_read_reg(chip, ADC_TM_NUM_BTM_CHAN,
+ &channels_available, 1);
+ if (ret < 0) {
+ pr_err("read failed for BTM channels\n");
+ return ret;
+ }
+
+ if (dt_chans > channels_available) {
+ pr_err("Number of nodes greater than channels supported:%d\n",
+ channels_available);
+ return -EINVAL;
+ }
+
+ mutex_init(&chip->adc_mutex_lock);
+
+ return ret;
+}
+
+static int adc_tm7_shutdown(struct adc_tm_chip *chip)
+{
+ u8 data = 0;
+ int i;
+
+ for (i = 0; i < chip->dt_channels; i++)
+ if (chip->sensor[i].req_wq)
+ destroy_workqueue(chip->sensor[i].req_wq);
+
+ mutex_lock(&chip->adc_mutex_lock);
+
+ adc_tm_write_reg(chip, ADC_TM_EN_CTL1, &data, 1);
+ data = ADC_TM_CONV_REQ_EN;
+ adc_tm_write_reg(chip, ADC_TM_CONV_REQ, &data, 1);
+
+ mutex_unlock(&chip->adc_mutex_lock);
+
+ mutex_destroy(&chip->adc_mutex_lock);
+
+ list_del(&chip->list);
+ return 0;
+}
+
+static const struct adc_tm_ops ops_adc_tm7 = {
+ .init = adc_tm7_init,
+ .set_trips = adc_tm7_set_trip_temp,
+ .interrupts_reg = adc_tm7_register_interrupts,
+ .get_temp = adc_tm_get_temp,
+ .channel_measure = adc_tm7_channel_measure,
+ .disable_chan = adc_tm7_disable_chan_meas,
+ .notify = notify_adc_tm7_fn,
+ .shutdown = adc_tm7_shutdown,
+};
+
+const struct adc_tm_data data_adc_tm7 = {
+ .ops = &ops_adc_tm7,
+ .decimation = (unsigned int []) {85, 340, 1360},
+ .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
+ 1000, 2000, 4000, 8000, 16000, 32000,
+ 64000, 128000},
+};
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 8df2ce9..4dc30e7 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -434,8 +434,8 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
rcar_thermal_for_each_priv(priv, common) {
if (rcar_thermal_had_changed(priv, status)) {
rcar_thermal_irq_disable(priv);
- schedule_delayed_work(&priv->work,
- msecs_to_jiffies(300));
+ queue_delayed_work(system_freezable_wq, &priv->work,
+ msecs_to_jiffies(300));
}
}
@@ -493,7 +493,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
pm_runtime_get_sync(dev);
for (i = 0; i < chip->nirqs; i++) {
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!irq)
continue;
if (!common->base) {
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 5cd6bdf..d436a153 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -142,9 +142,20 @@ static void __iomem *ring_options_base(struct tb_ring *ring)
return io;
}
-static void ring_iowrite16desc(struct tb_ring *ring, u32 value, u32 offset)
+static void ring_iowrite_cons(struct tb_ring *ring, u16 cons)
{
- iowrite16(value, ring_desc_base(ring) + offset);
+ /*
+ * The other 16-bits in the register is read-only and writes to it
+ * are ignored by the hardware so we can save one ioread32() by
+ * filling the read-only bits with zeroes.
+ */
+ iowrite32(cons, ring_desc_base(ring) + 8);
+}
+
+static void ring_iowrite_prod(struct tb_ring *ring, u16 prod)
+{
+ /* See ring_iowrite_cons() above for explanation */
+ iowrite32(prod << 16, ring_desc_base(ring) + 8);
}
static void ring_iowrite32desc(struct tb_ring *ring, u32 value, u32 offset)
@@ -196,7 +207,10 @@ static void ring_write_descriptors(struct tb_ring *ring)
descriptor->sof = frame->sof;
}
ring->head = (ring->head + 1) % ring->size;
- ring_iowrite16desc(ring, ring->head, ring->is_tx ? 10 : 8);
+ if (ring->is_tx)
+ ring_iowrite_prod(ring, ring->head);
+ else
+ ring_iowrite_cons(ring, ring->head);
}
}
@@ -660,7 +674,7 @@ void tb_ring_stop(struct tb_ring *ring)
ring_iowrite32options(ring, 0, 0);
ring_iowrite64desc(ring, 0, 0);
- ring_iowrite16desc(ring, 0, ring->is_tx ? 10 : 8);
+ ring_iowrite32desc(ring, 0, 8);
ring_iowrite32desc(ring, 0, 12);
ring->head = 0;
ring->tail = 0;
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index bc7efa6..678bf33 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -167,7 +167,7 @@ static int nvm_validate_and_write(struct tb_switch *sw)
static int nvm_authenticate_host(struct tb_switch *sw)
{
- int ret;
+ int ret = 0;
/*
* Root switch NVM upgrade requires that we disconnect the
@@ -175,6 +175,8 @@ static int nvm_authenticate_host(struct tb_switch *sw)
* already).
*/
if (!sw->safe_mode) {
+ u32 status;
+
ret = tb_domain_disconnect_all_paths(sw->tb);
if (ret)
return ret;
@@ -183,7 +185,16 @@ static int nvm_authenticate_host(struct tb_switch *sw)
* everything goes well so getting timeout is expected.
*/
ret = dma_port_flash_update_auth(sw->dma_port);
- return ret == -ETIMEDOUT ? 0 : ret;
+ if (!ret || ret == -ETIMEDOUT)
+ return 0;
+
+ /*
+ * Any error from update auth operation requires power
+ * cycling of the host router.
+ */
+ tb_sw_warn(sw, "failed to authenticate NVM, power cycling\n");
+ if (dma_port_flash_update_auth_status(sw->dma_port, &status) > 0)
+ nvm_set_auth_status(sw, status);
}
/*
@@ -191,7 +202,7 @@ static int nvm_authenticate_host(struct tb_switch *sw)
* switch.
*/
dma_port_power_cycle(sw->dma_port);
- return 0;
+ return ret;
}
static int nvm_authenticate_device(struct tb_switch *sw)
@@ -199,8 +210,16 @@ static int nvm_authenticate_device(struct tb_switch *sw)
int ret, retries = 10;
ret = dma_port_flash_update_auth(sw->dma_port);
- if (ret && ret != -ETIMEDOUT)
+ switch (ret) {
+ case 0:
+ case -ETIMEDOUT:
+ case -EACCES:
+ case -EINVAL:
+ /* Power cycle is required */
+ break;
+ default:
return ret;
+ }
/*
* Poll here for the authentication status. It takes some time
@@ -937,8 +956,6 @@ static ssize_t nvm_authenticate_store(struct device *dev,
*/
nvm_authenticate_start(sw);
ret = nvm_authenticate_host(sw);
- if (ret)
- nvm_authenticate_complete(sw);
} else {
ret = nvm_authenticate_device(sw);
}
@@ -1332,13 +1349,16 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
int ret;
switch (sw->generation) {
- case 3:
- break;
-
case 2:
/* Only root switch can be upgraded */
if (tb_route(sw))
return 0;
+
+ /* fallthrough */
+ case 3:
+ ret = tb_switch_set_uuid(sw);
+ if (ret)
+ return ret;
break;
default:
@@ -1359,6 +1379,19 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
return 0;
/*
+ * If there is status already set then authentication failed
+ * when the dma_port_flash_update_auth() returned. Power cycling
+ * is not needed (it was done already) so only thing we do here
+ * is to unblock runtime PM of the root port.
+ */
+ nvm_get_auth_status(sw, &status);
+ if (status) {
+ if (!tb_route(sw))
+ nvm_authenticate_complete(sw);
+ return 0;
+ }
+
+ /*
* Check status of the previous flash authentication. If there
* is one we need to power cycle the switch in any case to make
* it functional again.
@@ -1373,9 +1406,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
if (status) {
tb_sw_info(sw, "switch flash authentication failed\n");
- ret = tb_switch_set_uuid(sw);
- if (ret)
- return ret;
nvm_set_auth_status(sw, status);
}
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 59eaa62..80fd06f 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -107,6 +107,14 @@ static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count)
return got;
}
+/**
+ * hvterm_raw_put_chars: send characters to firmware for given vterm adapter
+ * @vtermno: The virtual terminal number.
+ * @buf: The characters to send. Because of the underlying hypercall in
+ * hvc_put_chars(), this buffer must be at least 16 bytes long, even if
+ * you are sending fewer chars.
+ * @count: number of chars to send.
+ */
static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count)
{
struct hvterm_priv *pv = hvterm_privs[vtermno];
@@ -219,6 +227,7 @@ static const struct hv_ops hvterm_hvsi_ops = {
static void udbg_hvc_putc(char c)
{
int count = -1;
+ unsigned char bounce_buffer[16];
if (!hvterm_privs[0])
return;
@@ -229,7 +238,12 @@ static void udbg_hvc_putc(char c)
do {
switch(hvterm_privs[0]->proto) {
case HV_PROTOCOL_RAW:
- count = hvterm_raw_put_chars(0, &c, 1);
+ /*
+ * hvterm_raw_put_chars requires at least a 16-byte
+ * buffer, so go via the bounce buffer
+ */
+ bounce_buffer[0] = c;
+ count = hvterm_raw_put_chars(0, bounce_buffer, 1);
break;
case HV_PROTOCOL_HVSI:
count = hvterm_hvsi_put_chars(0, &c, 1);
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index bb63519..0636e10 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -613,7 +613,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
}
/* no data */
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN;
break;
}
@@ -680,7 +680,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
if (tbuf)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
error = -EAGAIN;
break;
}
@@ -968,6 +968,11 @@ static int __init n_hdlc_init(void)
} /* end of init_module() */
+#ifdef CONFIG_SPARC
+#undef __exitdata
+#define __exitdata
+#endif
+
static const char hdlc_unregister_ok[] __exitdata =
KERN_INFO "N_HDLC: line discipline unregistered\n";
static const char hdlc_unregister_fail[] __exitdata =
diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
index dbf1ab3..a3969b7 100644
--- a/drivers/tty/n_r3964.c
+++ b/drivers/tty/n_r3964.c
@@ -1078,7 +1078,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
pMsg = remove_msg(pInfo, pClient);
if (pMsg == NULL) {
/* no messages available. */
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN;
goto unlock;
}
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 0d46212..3175601 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1735,7 +1735,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
down_read(&tty->termios_rwsem);
- while (1) {
+ do {
/*
* When PARMRK is set, each input char may take up to 3 chars
* in the read buf; reduce the buffer space avail by 3x
@@ -1777,7 +1777,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
fp += n;
count -= n;
rcvd += n;
- }
+ } while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));
tty->receive_room = room;
@@ -2249,7 +2249,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
break;
if (!timeout)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
@@ -2403,7 +2403,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
}
if (!nr)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 678406e..00099a84 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -28,6 +28,7 @@
#include <linux/mount.h>
#include <linux/file.h>
#include <linux/ioctl.h>
+#include <linux/compat.h>
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
@@ -488,6 +489,7 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
static long pty_bsd_compat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -495,8 +497,11 @@ static long pty_bsd_compat_ioctl(struct tty_struct *tty,
* PTY ioctls don't require any special translation between 32-bit and
* 64-bit userspace, they are already compatible.
*/
- return pty_bsd_ioctl(tty, cmd, arg);
+ return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
}
+#else
+#define pty_bsd_compat_ioctl NULL
+#endif
static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
/*
@@ -676,6 +681,7 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
static long pty_unix98_compat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -683,8 +689,12 @@ static long pty_unix98_compat_ioctl(struct tty_struct *tty,
* PTY ioctls don't require any special translation between 32-bit and
* 64-bit userspace, they are already compatible.
*/
- return pty_unix98_ioctl(tty, cmd, arg);
+ return pty_unix98_ioctl(tty, cmd,
+ cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
}
+#else
+#define pty_unix98_compat_ioctl NULL
+#endif
/**
* ptm_unix98_lookup - find a pty master
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 8fe3d0e..69aaee5 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -946,6 +946,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
return NULL;
}
+static void serial_8250_overrun_backoff_work(struct work_struct *work)
+{
+ struct uart_8250_port *up =
+ container_of(to_delayed_work(work), struct uart_8250_port,
+ overrun_backoff);
+ struct uart_port *port = &up->port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ up->ier |= UART_IER_RLSI | UART_IER_RDI;
+ up->port.read_status_mask |= UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
/**
* serial8250_register_8250_port - register a serial port
* @up: serial port template
@@ -1059,7 +1074,18 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
ret = 0;
}
+
+ /* Initialise interrupt backoff work if required */
+ if (up->overrun_backoff_time_ms > 0) {
+ uart->overrun_backoff_time_ms =
+ up->overrun_backoff_time_ms;
+ INIT_DELAYED_WORK(&uart->overrun_backoff,
+ serial_8250_overrun_backoff_work);
+ } else {
+ uart->overrun_backoff_time_ms = 0;
+ }
}
+
mutex_unlock(&serial_mutex);
return ret;
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index 6640a4c..bb9571e 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -45,8 +45,29 @@ int fsl8250_handle_irq(struct uart_port *port)
lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
- if (lsr & (UART_LSR_DR | UART_LSR_BI))
+ /* Process incoming characters first */
+ if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
+ (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
lsr = serial8250_rx_chars(up, lsr);
+ }
+
+ /* Stop processing interrupts on input overrun */
+ if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
+ unsigned long delay;
+
+ up->ier = port->serial_in(port, UART_IER);
+ if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
+ port->ops->stop_rx(port);
+ } else {
+ /* Keep restarting the timer until
+ * the input overrun subsides.
+ */
+ cancel_delayed_work(&up->overrun_backoff);
+ }
+
+ delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
+ schedule_delayed_work(&up->overrun_backoff, delay);
+ }
serial8250_modem_status(up);
diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c
index 127017c..057b1ea 100644
--- a/drivers/tty/serial/8250/8250_men_mcb.c
+++ b/drivers/tty/serial/8250/8250_men_mcb.c
@@ -71,8 +71,8 @@ static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
{
struct serial_8250_men_mcb_data *data;
struct resource *mem;
- unsigned int num_ports;
- unsigned int i;
+ int num_ports;
+ int i;
void __iomem *membase;
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
@@ -87,7 +87,7 @@ static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n",
mdev->id, num_ports);
- if (num_ports == 0 || num_ports > 4) {
+ if (num_ports <= 0 || num_ports > 4) {
dev_err(&mdev->dev, "unexpected number of ports: %u\n",
num_ports);
return -ENODEV;
@@ -132,7 +132,7 @@ static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
{
- unsigned int num_ports, i;
+ int num_ports, i;
struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
if (!data)
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 98125de..2488de1 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -244,6 +244,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
port8250.capabilities |= UART_CAP_AFE;
+ if (of_property_read_u32(ofdev->dev.of_node,
+ "overrun-throttle-ms",
+ &port8250.overrun_backoff_time_ms) != 0)
+ port8250.overrun_backoff_time_ms = 0;
+
ret = serial8250_register_8250_port(&port8250);
if (ret < 0)
goto err_dispose;
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 89ade21..af21122 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -813,10 +813,8 @@ __acquires(&uap->port.lock)
if (!uap->using_tx_dma)
return;
- /* Avoid deadlock with the DMA engine callback */
- spin_unlock(&uap->port.lock);
- dmaengine_terminate_all(uap->dmatx.chan);
- spin_lock(&uap->port.lock);
+ dmaengine_terminate_async(uap->dmatx.chan);
+
if (uap->dmatx.queued) {
dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1,
DMA_TO_DEVICE);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index dd8949e..f34520e 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2154,27 +2154,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode |= ATMEL_US_USMODE_NORMAL;
}
- /* set the mode, clock divisor, parity, stop bits and data size */
- atmel_uart_writel(port, ATMEL_US_MR, mode);
-
- /*
- * when switching the mode, set the RTS line state according to the
- * new mode, otherwise keep the former state
- */
- if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
- unsigned int rts_state;
-
- if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
- /* let the hardware control the RTS line */
- rts_state = ATMEL_US_RTSDIS;
- } else {
- /* force RTS line to low level */
- rts_state = ATMEL_US_RTSEN;
- }
-
- atmel_uart_writel(port, ATMEL_US_CR, rts_state);
- }
-
/*
* Set the baud rate:
* Fractional baudrate allows to setup output frequency more
@@ -2200,6 +2179,28 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
quot = cd | fp << ATMEL_US_FP_OFFSET;
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
+
+ /* set the mode, clock divisor, parity, stop bits and data size */
+ atmel_uart_writel(port, ATMEL_US_MR, mode);
+
+ /*
+ * when switching the mode, set the RTS line state according to the
+ * new mode, otherwise keep the former state
+ */
+ if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
+ unsigned int rts_state;
+
+ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+ /* let the hardware control the RTS line */
+ rts_state = ATMEL_US_RTSDIS;
+ } else {
+ /* force RTS line to low level */
+ rts_state = ATMEL_US_RTSEN;
+ }
+
+ atmel_uart_writel(port, ATMEL_US_CR, rts_state);
+ }
+
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
atmel_port->tx_stopped = false;
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 50b6746..ee8a5cb 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -376,8 +376,8 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
}
sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
- sport->dma_tx_nents,
- DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+ ret, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT);
if (!sport->dma_tx_desc) {
dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
dev_err(dev, "Cannot prepare TX slave DMA!\n");
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index ffefd21..31033d5 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -1230,6 +1230,9 @@ static int ifx_spi_spi_remove(struct spi_device *spi)
struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
/* stop activity */
tasklet_kill(&ifx_dev->io_work_tasklet);
+
+ pm_runtime_disable(&spi->dev);
+
/* free irq */
free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev);
free_irq(gpio_to_irq(ifx_dev->gpio.srdy), ifx_dev);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 105de92..989ca7d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2071,7 +2071,7 @@ imx_uart_console_setup(struct console *co, char *options)
retval = clk_prepare(sport->clk_per);
if (retval)
- clk_disable_unprepare(sport->clk_ipg);
+ clk_unprepare(sport->clk_ipg);
error_console:
return retval;
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index bd3e6cf..0c35c3c 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -844,12 +844,9 @@ static void max310x_wq_proc(struct work_struct *ws)
static unsigned int max310x_tx_empty(struct uart_port *port)
{
- unsigned int lvl, sts;
+ u8 lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
- lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
- sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
-
- return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
+ return lvl ? 0 : TIOCSER_TEMT;
}
static unsigned int max310x_get_mctrl(struct uart_port *port)
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 8e27de9..e218a134 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/bitmap.h>
@@ -23,6 +23,7 @@
#include <linux/tty_flip.h>
#include <linux/ioctl.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/dma-mapping.h>
/* UART specific GENI registers */
#define SE_UART_LOOPBACK_CFG (0x22C)
@@ -173,6 +174,8 @@ struct msm_geni_serial_port {
struct msm_geni_serial_ver_info ver_info;
u32 cur_tx_remaining;
bool startup_in_progress;
+ bool is_console;
+ bool rumi_platform;
};
static const struct uart_ops msm_geni_serial_pops;
@@ -1091,7 +1094,6 @@ static void start_rx_sequencer(struct uart_port *uport)
unsigned int geni_m_irq_en;
unsigned int geni_status;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- int ret;
u32 geni_se_param = UART_PARAM_RFR_OPEN;
if (port->startup_in_progress)
@@ -1099,17 +1101,11 @@ static void start_rx_sequencer(struct uart_port *uport)
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
if (geni_status & S_GENI_CMD_ACTIVE) {
- if (port->xfer_mode == SE_DMA && !port->rx_dma) {
+ if (port->xfer_mode == SE_DMA) {
IPC_LOG_MSG(port->ipc_log_misc,
"%s: GENI: 0x%x\n", __func__, geni_status);
- ret = geni_se_rx_dma_prep(port->wrapper_dev,
- uport->membase, port->rx_buf, DMA_RX_BUF_SIZE,
- &port->rx_dma);
- if (ret) {
- IPC_LOG_MSG(port->ipc_log_misc,
- "%s: RX buff Fail %d\n", __func__, ret);
- goto exit_start_rx_sequencer;
- }
+ geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE,
+ &port->rx_dma);
}
msm_geni_serial_stop_rx(uport);
}
@@ -1131,21 +1127,14 @@ static void start_rx_sequencer(struct uart_port *uport)
geni_write_reg_nolog(geni_m_irq_en, uport->membase,
SE_GENI_M_IRQ_EN);
} else if (port->xfer_mode == SE_DMA) {
- ret = geni_se_rx_dma_prep(port->wrapper_dev, uport->membase,
- port->rx_buf, DMA_RX_BUF_SIZE, &port->rx_dma);
- if (ret) {
- dev_err(uport->dev, "%s: RX Prep dma failed %d\n",
- __func__, ret);
- msm_geni_serial_stop_rx(uport);
- goto exit_start_rx_sequencer;
- }
+ geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE,
+ &port->rx_dma);
}
/*
* Ensure the writes to the secondary sequencer and interrupt enables
* go through.
*/
mb();
-exit_start_rx_sequencer:
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x, dma_dbg:0x%x\n", __func__,
geni_status, geni_read_reg(uport->membase, SE_DMA_DEBUG_REG0));
@@ -1272,12 +1261,9 @@ static void stop_rx_sequencer(struct uart_port *uport)
msm_geni_serial_abort_rx(uport);
}
exit_rx_seq:
- if (port->xfer_mode == SE_DMA && port->rx_dma) {
+ if (port->xfer_mode == SE_DMA && port->rx_dma)
msm_geni_serial_rx_fsm_rst(uport);
- geni_se_rx_dma_unprep(port->wrapper_dev, port->rx_dma,
- DMA_RX_BUF_SIZE);
- port->rx_dma = (dma_addr_t)NULL;
- }
+
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x\n", __func__, geni_status);
}
@@ -1467,16 +1453,13 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx)
if (!(geni_status & S_GENI_CMD_ACTIVE))
return 0;
- geni_se_rx_dma_unprep(msm_port->wrapper_dev, msm_port->rx_dma,
- DMA_RX_BUF_SIZE);
- msm_port->rx_dma = (dma_addr_t)NULL;
-
- rx_bytes = geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
if (unlikely(!msm_port->rx_buf)) {
IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: NULL Rx_buf\n",
__func__);
return 0;
}
+
+ rx_bytes = geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
if (unlikely(!rx_bytes)) {
IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: Size %d\n",
__func__, rx_bytes);
@@ -1498,10 +1481,9 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx)
dump_ipc(msm_port->ipc_log_rx, "DMA Rx", (char *)msm_port->rx_buf, 0,
rx_bytes);
exit_handle_dma_rx:
- ret = geni_se_rx_dma_prep(msm_port->wrapper_dev, uport->membase,
- msm_port->rx_buf, DMA_RX_BUF_SIZE, &msm_port->rx_dma);
- if (ret)
- IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: %d\n", __func__, ret);
+ geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE,
+ &msm_port->rx_dma);
+
return ret;
}
@@ -1787,6 +1769,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport)
int ret = 0;
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
unsigned long cfg0, cfg1;
+ dma_addr_t dma_address;
unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
set_rfr_wm(msm_port);
@@ -1812,14 +1795,15 @@ static int msm_geni_serial_port_setup(struct uart_port *uport)
goto exit_portsetup;
}
- msm_port->rx_buf = devm_kzalloc(uport->dev, DMA_RX_BUF_SIZE,
- GFP_KERNEL);
+ msm_port->rx_buf = dma_alloc_coherent(msm_port->wrapper_dev,
+ DMA_RX_BUF_SIZE, &dma_address, GFP_KERNEL);
if (!msm_port->rx_buf) {
devm_kfree(uport->dev, msm_port->rx_fifo);
msm_port->rx_fifo = NULL;
ret = -ENOMEM;
goto exit_portsetup;
}
+ msm_port->rx_dma = dma_address;
} else {
/*
* Make an unconditional cancel on the main sequencer to reset
@@ -1841,12 +1825,12 @@ static int msm_geni_serial_port_setup(struct uart_port *uport)
ret = geni_se_init(uport->membase, msm_port->rx_wm, msm_port->rx_rfr);
if (ret) {
dev_err(uport->dev, "%s: Fail\n", __func__);
- goto exit_portsetup;
+ goto free_dma;
}
ret = geni_se_select_mode(uport->membase, msm_port->xfer_mode);
if (ret)
- goto exit_portsetup;
+ goto free_dma;
msm_port->port_setup = true;
/*
@@ -1854,6 +1838,14 @@ static int msm_geni_serial_port_setup(struct uart_port *uport)
* framework.
*/
mb();
+
+ return 0;
+free_dma:
+ if (msm_port->rx_dma) {
+ dma_free_coherent(msm_port->wrapper_dev, DMA_RX_BUF_SIZE,
+ msm_port->rx_buf, msm_port->rx_dma);
+ msm_port->rx_dma = (dma_addr_t)NULL;
+ }
exit_portsetup:
return ret;
}
@@ -1922,30 +1914,6 @@ static int msm_geni_serial_startup(struct uart_port *uport)
return ret;
}
-static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
-{
- unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
- 32000000, 48000000, 64000000, 80000000, 96000000, 100000000,
- 102400000, 112000000, 120000000, 128000000};
- int i;
- int match = -1;
-
- for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
- if (clk_freq > root_freq[i])
- continue;
-
- if (!(root_freq[i] % clk_freq)) {
- match = i;
- break;
- }
- }
- if (match != -1)
- *ser_clk = root_freq[match];
- else
- pr_err("clk_freq %ld\n", clk_freq);
- return match;
-}
-
static void geni_serial_write_term_regs(struct uart_port *uport, u32 loopback,
u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
@@ -1971,6 +1939,31 @@ static void geni_serial_write_term_regs(struct uart_port *uport, u32 loopback,
geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG);
}
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
+{
+ unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
+ 32000000, 48000000, 64000000, 80000000, 96000000, 100000000,
+ 102400000, 112000000, 120000000, 128000000};
+ int i;
+ int match = -1;
+
+ for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
+ if (clk_freq > root_freq[i])
+ continue;
+
+ if (!(root_freq[i] % clk_freq)) {
+ match = i;
+ break;
+ }
+ }
+ if (match != -1)
+ *ser_clk = root_freq[match];
+ else
+ pr_err("clk_freq %ld\n", clk_freq);
+ return match;
+}
+
static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
{
unsigned long ser_clk;
@@ -1991,6 +1984,7 @@ static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
exit_get_clk_div_rate:
return clk_div;
}
+#endif
static void msm_geni_serial_set_termios(struct uart_port *uport,
struct ktermios *termios, struct ktermios *old)
@@ -2002,11 +1996,22 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
unsigned int rx_trans_cfg;
unsigned int rx_parity_cfg;
unsigned int stop_bit_len;
- int clk_div;
+ int clk_div, ret;
unsigned long ser_clk_cfg = 0;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
unsigned long clk_rate;
unsigned long flags;
+ unsigned long desired_rate;
+ unsigned int clk_idx;
+ int uart_sampling;
+ int clk_freq_diff;
+
+ /* QUP_2.5.0 and older RUMI has sampling rate as 32 */
+ if (port->rumi_platform && port->is_console) {
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_M_CLK_CFG);
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_S_CLK_CFG);
+ geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG);
+ }
if (!uart_console(uport)) {
int ret = msm_geni_serial_power_on(uport);
@@ -2030,12 +2035,31 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
port->cur_baud = baud;
- clk_div = get_clk_div_rate(baud, &clk_rate);
+ uart_sampling = IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) ?
+ UART_OVERSAMPLING / 2 : UART_OVERSAMPLING;
+ desired_rate = baud * uart_sampling;
+
+ /*
+ * Request for nearest possible required frequency instead of the exact
+ * required frequency.
+ */
+ ret = geni_se_clk_freq_match(&port->serial_rsc, desired_rate,
+ &clk_idx, &clk_rate, false);
+ if (ret) {
+ dev_err(uport->dev, "%s: Failed(%d) to find src clk for 0x%x\n",
+ __func__, ret, baud);
+ goto exit_set_termios;
+ }
+
+ clk_div = DIV_ROUND_UP(clk_rate, desired_rate);
if (clk_div <= 0)
goto exit_set_termios;
- if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING))
- clk_div *= 2;
+ clk_freq_diff = (desired_rate - (clk_rate / clk_div));
+ if (clk_freq_diff)
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "src_clk freq_diff:%d baud:%d clk_rate:%d clk_div:%d\n",
+ clk_freq_diff, baud, clk_rate, clk_div);
uport->uartclk = clk_rate;
clk_set_rate(port->serial_rsc.se_clk, clk_rate);
@@ -2347,6 +2371,13 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev,
*/
msm_geni_serial_poll_cancel_tx(uport);
+ /* Only for earlyconsole */
+ if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING)) {
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_M_CLK_CFG);
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_S_CLK_CFG);
+ geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG);
+ }
+
se_get_packing_config(8, 1, false, &cfg0, &cfg1);
geni_se_init(uport->membase, (DEF_FIFO_DEPTH_WORDS >> 1),
(DEF_FIFO_DEPTH_WORDS - 2));
@@ -2539,7 +2570,13 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport)
int hw_ver, ret = 0;
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
- se_geni_clks_on(&msm_port->serial_rsc);
+ /*
+ * At this time early console is still active and transfers are
+ * in-coming. Make sure UART doesn't turn on/off clocks for
+ * console usecase.
+ */
+ if (!msm_port->is_console)
+ se_geni_clks_on(&msm_port->serial_rsc);
/* Basic HW and FW info */
if (unlikely(get_se_proto(uport->membase) != UART)) {
dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
@@ -2567,7 +2604,8 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport)
msm_port->ver_info.hw_minor_ver,
msm_port->ver_info.hw_step_ver);
exit_ver_info:
- se_geni_clks_off(&msm_port->serial_rsc);
+ if (!msm_port->is_console)
+ se_geni_clks_off(&msm_port->serial_rsc);
return ret;
}
@@ -2619,6 +2657,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
line, ret);
goto exit_geni_serial_probe;
}
+ dev_port->is_console = is_console;
uport = &dev_port->uport;
@@ -2659,6 +2698,10 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
dev_port->serial_rsc.ctrl_dev = &pdev->dev;
+ /* RUMI specific */
+ dev_port->rumi_platform = of_property_read_bool(pdev->dev.of_node,
+ "qcom,rumi_platform");
+
if (of_property_read_u32(pdev->dev.of_node, "qcom,wakeup-byte",
&wake_char)) {
dev_dbg(&pdev->dev, "No Wakeup byte specified\n");
@@ -2781,6 +2824,13 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
}
+ if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) &&
+ dev_port->rumi_platform && dev_port->is_console) {
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_M_CLK_CFG);
+ geni_write_reg_nolog(0x21, uport->membase, GENI_SER_S_CLK_CFG);
+ geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG);
+ }
+
dev_info(&pdev->dev, "Serial port%d added.FifoSize %d is_console%d\n",
line, uport->fifosize, is_console);
device_create_file(uport->dev, &dev_attr_loopback);
@@ -2805,6 +2855,11 @@ static int msm_geni_serial_remove(struct platform_device *pdev)
wakeup_source_trash(&port->geni_wake);
uart_remove_one_port(drv, &port->uport);
+ if (port->rx_dma) {
+ dma_free_coherent(port->wrapper_dev, DMA_RX_BUF_SIZE,
+ port->rx_buf, port->rx_dma);
+ port->rx_dma = (dma_addr_t)NULL;
+ }
return 0;
}
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 310bbae..4caeca6 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -980,6 +980,7 @@ static unsigned int msm_get_mctrl(struct uart_port *port)
static void msm_reset(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ unsigned int mr;
/* reset everything */
msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
@@ -987,7 +988,10 @@ static void msm_reset(struct uart_port *port)
msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
- msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+ msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
+ mr = msm_read(port, UART_MR1);
+ mr &= ~UART_MR1_RX_RDY_CTL;
+ msm_write(port, mr, UART_MR1);
/* Disable DM modes */
if (msm_port->is_uartdm)
@@ -1576,6 +1580,7 @@ static void __msm_console_write(struct uart_port *port, const char *s,
int num_newlines = 0;
bool replaced = false;
void __iomem *tf;
+ int locked = 1;
if (is_uartdm)
tf = port->membase + UARTDM_TF;
@@ -1588,7 +1593,13 @@ static void __msm_console_write(struct uart_port *port, const char *s,
num_newlines++;
count += num_newlines;
- spin_lock(&port->lock);
+ if (port->sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else
+ spin_lock(&port->lock);
+
if (is_uartdm)
msm_reset_dm_count(port, count);
@@ -1624,7 +1635,9 @@ static void __msm_console_write(struct uart_port *port, const char *s,
iowrite32_rep(tf, buf, 1);
i += num_chars;
}
- spin_unlock(&port->lock);
+
+ if (locked)
+ spin_unlock(&port->lock);
}
static void msm_console_write(struct console *co, const char *s,
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 34acdf2..4c188f4 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1634,8 +1634,9 @@ static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s)
/*
* If something went wrong, rollback.
+ * Be careful: i may be unsigned.
*/
- while (err && (--i >= 0))
+ while (err && (i-- > 0))
if (irq[i] >= 0)
free_irq(irq[i], s);
diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c
index 29a6dc6..73fcc6b 100644
--- a/drivers/tty/serial/owl-uart.c
+++ b/drivers/tty/serial/owl-uart.c
@@ -742,7 +742,7 @@ static int __init owl_uart_init(void)
return ret;
}
-static void __init owl_uart_exit(void)
+static void __exit owl_uart_exit(void)
{
platform_driver_unregister(&owl_uart_platform_driver);
uart_unregister_driver(&owl_uart_driver);
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 5b96df4..b3f7d1a 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -113,6 +113,8 @@ struct qcom_geni_serial_port {
u32 *rx_fifo;
u32 loopback;
bool brk;
+
+ unsigned int tx_remaining;
};
static const struct uart_ops qcom_geni_console_pops;
@@ -435,6 +437,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
struct qcom_geni_serial_port *port;
bool locked = true;
unsigned long flags;
+ u32 geni_status;
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
@@ -448,6 +451,8 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
else
spin_lock_irqsave(&uport->lock, flags);
+ geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+
/* Cancel the current write to log the fault */
if (!locked) {
geni_se_cancel_m_cmd(&port->se);
@@ -461,9 +466,19 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
}
writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
+ } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) {
+ /*
+ * It seems we can't interrupt existing transfers if all data
+ * has been sent, in which case we need to look for done first.
+ */
+ qcom_geni_serial_poll_tx_done(uport);
}
__qcom_geni_serial_console_write(uport, s, count);
+
+ if (port->tx_remaining)
+ qcom_geni_serial_setup_tx(uport, port->tx_remaining);
+
if (locked)
spin_unlock_irqrestore(&uport->lock, flags);
}
@@ -694,40 +709,45 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
port->handle_rx(uport, total_bytes, drop);
}
-static void qcom_geni_serial_handle_tx(struct uart_port *uport)
+static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
+ bool active)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
struct circ_buf *xmit = &uport->state->xmit;
size_t avail;
size_t remaining;
+ size_t pending;
int i;
u32 status;
unsigned int chunk;
int tail;
- u32 irq_en;
- chunk = uart_circ_chars_pending(xmit);
status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
- /* Both FIFO and framework buffer are drained */
- if (!chunk && !status) {
+
+ /* Complete the current tx command before taking newly added data */
+ if (active)
+ pending = port->tx_remaining;
+ else
+ pending = uart_circ_chars_pending(xmit);
+
+ /* All data has been transmitted and acknowledged as received */
+ if (!pending && !status && done) {
qcom_geni_serial_stop_tx(uport);
goto out_write_wakeup;
}
- if (!uart_console(uport)) {
- irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
- writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- }
+ avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
+ avail *= port->tx_bytes_pw;
- avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = xmit->tail;
- chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail);
+ chunk = min3(avail, pending, (size_t)(UART_XMIT_SIZE - tail));
if (!chunk)
goto out_write_wakeup;
- qcom_geni_serial_setup_tx(uport, chunk);
+ if (!port->tx_remaining) {
+ qcom_geni_serial_setup_tx(uport, pending);
+ port->tx_remaining = pending;
+ }
remaining = chunk;
for (i = 0; i < chunk; ) {
@@ -746,11 +766,10 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
tail += tx_bytes;
uport->icount.tx += tx_bytes;
remaining -= tx_bytes;
+ port->tx_remaining -= tx_bytes;
}
xmit->tail = tail & (UART_XMIT_SIZE - 1);
- if (uart_console(uport))
- qcom_geni_serial_poll_tx_done(uport);
out_write_wakeup:
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport);
@@ -760,6 +779,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
{
unsigned int m_irq_status;
unsigned int s_irq_status;
+ unsigned int geni_status;
struct uart_port *uport = dev;
unsigned long flags;
unsigned int m_irq_en;
@@ -773,6 +793,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
spin_lock_irqsave(&uport->lock, flags);
m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
+ geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
@@ -787,7 +808,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
- qcom_geni_serial_handle_tx(uport);
+ qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN,
+ geni_status & M_GENI_CMD_ACTIVE);
if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) {
if (s_irq_status & S_GP_IRQ_0_EN)
@@ -851,6 +873,23 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+ u32 proto;
+
+ if (uart_console(uport))
+ port->tx_bytes_pw = 1;
+ else
+ port->tx_bytes_pw = 4;
+ port->rx_bytes_pw = RX_BYTES_PW;
+
+ proto = geni_se_read_proto(&port->se);
+ if (proto != GENI_SE_UART) {
+ dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
+ return -ENXIO;
+ }
+
+ qcom_geni_serial_stop_rx(uport);
+
+ get_tx_fifo_size(port);
set_rfr_wm(port);
writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
@@ -874,30 +913,19 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
return -ENOMEM;
}
port->setup = true;
+
return 0;
}
static int qcom_geni_serial_startup(struct uart_port *uport)
{
int ret;
- u32 proto;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
scnprintf(port->name, sizeof(port->name),
"qcom_serial_%s%d",
(uart_console(uport) ? "console" : "uart"), uport->line);
- if (!uart_console(uport)) {
- port->tx_bytes_pw = 4;
- port->rx_bytes_pw = RX_BYTES_PW;
- }
- proto = geni_se_read_proto(&port->se);
- if (proto != GENI_SE_UART) {
- dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
- return -ENXIO;
- }
-
- get_tx_fifo_size(port);
if (!port->setup) {
ret = qcom_geni_serial_port_setup(uport);
if (ret)
@@ -1056,6 +1084,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int ret;
if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
return -ENXIO;
@@ -1071,21 +1100,10 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
if (unlikely(!uport->membase))
return -ENXIO;
- if (geni_se_resources_on(&port->se)) {
- dev_err(port->se.dev, "Error turning on resources\n");
- return -ENXIO;
- }
-
- if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
- geni_se_resources_off(&port->se);
- return -ENXIO;
- }
-
if (!port->setup) {
- port->tx_bytes_pw = 1;
- port->rx_bytes_pw = RX_BYTES_PW;
- qcom_geni_serial_stop_rx(uport);
- qcom_geni_serial_port_setup(uport);
+ ret = qcom_geni_serial_port_setup(uport);
+ if (ret)
+ return ret;
}
if (options)
@@ -1203,11 +1221,12 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ /* If we've never been called, treat it as off */
+ if (old_state == UART_PM_STATE_UNDEFINED)
+ old_state = UART_PM_STATE_OFF;
+
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
geni_se_resources_on(&port->se);
- else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON &&
- old_state == UART_PM_STATE_UNDEFINED))
- geni_se_resources_on(&port->se);
else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON)
geni_se_resources_off(&port->se);
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index c6058b5..2a49b6d 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1944,7 +1944,11 @@ static int s3c24xx_serial_resume(struct device *dev)
if (port) {
clk_prepare_enable(ourport->clk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_prepare_enable(ourport->baudclk);
s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
clk_disable_unprepare(ourport->clk);
uart_resume_port(&s3c24xx_uart_drv, port);
@@ -1967,7 +1971,11 @@ static int s3c24xx_serial_resume_noirq(struct device *dev)
if (rx_enabled(port))
uintm &= ~S3C64XX_UINTM_RXD_MSK;
clk_prepare_enable(ourport->clk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_prepare_enable(ourport->baudclk);
wr_regl(port, S3C64XX_UINTM, uintm);
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
clk_disable_unprepare(ourport->clk);
}
}
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 372cc7f..ebea4a9 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -328,6 +328,7 @@ struct sc16is7xx_port {
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work irq_work;
+ struct mutex efr_lock;
struct sc16is7xx_one p[0];
};
@@ -499,6 +500,21 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
div /= 4;
}
+ /* In an amazing feat of design, the Enhanced Features Register shares
+ * the address of the Interrupt Identification Register, and is
+ * switched in by writing a magic value (0xbf) to the Line Control
+ * Register. Any interrupt firing during this time will see the EFR
+ * where it expects the IIR to be, leading to "Unexpected interrupt"
+ * messages.
+ *
+ * Prevent this possibility by claiming a mutex while accessing the
+ * EFR, and claiming the same mutex from within the interrupt handler.
+ * This is similar to disabling the interrupt, but that doesn't work
+ * because the bulk of the interrupt processing is run as a workqueue
+ * job in thread context.
+ */
+ mutex_lock(&s->efr_lock);
+
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
/* Open the LCR divisors for configuration */
@@ -514,6 +530,8 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
/* Put LCR back to the normal mode */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+ mutex_unlock(&s->efr_lock);
+
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_CLKSEL_BIT,
prescaler);
@@ -696,6 +714,8 @@ static void sc16is7xx_ist(struct kthread_work *ws)
{
struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work);
+ mutex_lock(&s->efr_lock);
+
while (1) {
bool keep_polling = false;
int i;
@@ -705,6 +725,8 @@ static void sc16is7xx_ist(struct kthread_work *ws)
if (!keep_polling)
break;
}
+
+ mutex_unlock(&s->efr_lock);
}
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
@@ -899,6 +921,9 @@ static void sc16is7xx_set_termios(struct uart_port *port,
if (!(termios->c_cflag & CREAD))
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
+ /* As above, claim the mutex while accessing the EFR. */
+ mutex_lock(&s->efr_lock);
+
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_B);
@@ -920,6 +945,8 @@ static void sc16is7xx_set_termios(struct uart_port *port,
/* Update LCR register */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+ mutex_unlock(&s->efr_lock);
+
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 4 / 0xffff,
@@ -1185,6 +1212,7 @@ static int sc16is7xx_probe(struct device *dev,
s->regmap = regmap;
s->devtype = devtype;
dev_set_drvdata(dev, s);
+ mutex_init(&s->efr_lock);
kthread_init_worker(&s->kworker);
kthread_init_work(&s->irq_work, sc16is7xx_ist);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index fe9261f..dc56cdd 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1112,7 +1112,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)
if (!uport)
goto out;
- if (uport->type != PORT_UNKNOWN)
+ if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl)
uport->ops->break_ctl(uport, break_state);
ret = 0;
out:
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 07f3186..af0412a 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -60,6 +60,9 @@ EXPORT_SYMBOL_GPL(mctrl_gpio_set);
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
enum mctrl_gpio_idx gidx)
{
+ if (gpios == NULL)
+ return NULL;
+
return gpios->gpio[gidx];
}
EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 5550289..9e1a6af 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1359,7 +1359,7 @@ static int sci_submit_rx(struct sci_port *s, bool port_lock_held)
dmaengine_terminate_async(chan);
for (i = 0; i < 2; i++)
s->cookie_rx[i] = -EINVAL;
- s->active_rx = -EINVAL;
+ s->active_rx = 0;
s->chan_rx = NULL;
sci_start_rx(port);
if (!port_lock_held)
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 77efa0a..66d49d5 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1279,24 +1279,11 @@ static struct uart_driver cdns_uart_uart_driver = {
static int cdns_uart_suspend(struct device *device)
{
struct uart_port *port = dev_get_drvdata(device);
- struct tty_struct *tty;
- struct device *tty_dev;
- int may_wake = 0;
+ int may_wake;
- /* Get the tty which could be NULL so don't assume it's valid */
- tty = tty_port_tty_get(&port->state->port);
- if (tty) {
- tty_dev = tty->dev;
- may_wake = device_may_wakeup(tty_dev);
- tty_kref_put(tty);
- }
+ may_wake = device_may_wakeup(device);
- /*
- * Call the API provided in serial_core.c file which handles
- * the suspend.
- */
- uart_suspend_port(&cdns_uart_uart_driver, port);
- if (!(console_suspend_enabled && !may_wake)) {
+ if (console_suspend_enabled && may_wake) {
unsigned long flags = 0;
spin_lock_irqsave(&port->lock, flags);
@@ -1311,7 +1298,11 @@ static int cdns_uart_suspend(struct device *device)
spin_unlock_irqrestore(&port->lock, flags);
}
- return 0;
+ /*
+ * Call the API provided in serial_core.c file which handles
+ * the suspend.
+ */
+ return uart_suspend_port(&cdns_uart_uart_driver, port);
}
/**
@@ -1325,17 +1316,9 @@ static int cdns_uart_resume(struct device *device)
struct uart_port *port = dev_get_drvdata(device);
unsigned long flags = 0;
u32 ctrl_reg;
- struct tty_struct *tty;
- struct device *tty_dev;
- int may_wake = 0;
+ int may_wake;
- /* Get the tty which could be NULL so don't assume it's valid */
- tty = tty_port_tty_get(&port->state->port);
- if (tty) {
- tty_dev = tty->dev;
- may_wake = device_may_wakeup(tty_dev);
- tty_kref_put(tty);
- }
+ may_wake = device_may_wakeup(device);
if (console_suspend_enabled && !may_wake) {
struct cdns_uart *cdns_uart = port->private_data;
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index a940865..b88ecf1 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -1186,14 +1186,13 @@ static long slgt_compat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct slgt_info *info = tty->driver_data;
- int rc = -ENOIOCTLCMD;
+ int rc;
if (sanity_check(info, tty->name, "compat_ioctl"))
return -ENODEV;
DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
switch (cmd) {
-
case MGSL_IOCSPARAMS32:
rc = set_params32(info, compat_ptr(arg));
break;
@@ -1213,18 +1212,11 @@ static long slgt_compat_ioctl(struct tty_struct *tty,
case MGSL_IOCWAITGPIO:
case MGSL_IOCGXSYNC:
case MGSL_IOCGXCTRL:
- case MGSL_IOCSTXIDLE:
- case MGSL_IOCTXENABLE:
- case MGSL_IOCRXENABLE:
- case MGSL_IOCTXABORT:
- case TIOCMIWAIT:
- case MGSL_IOCSIF:
- case MGSL_IOCSXSYNC:
- case MGSL_IOCSXCTRL:
- rc = ioctl(tty, cmd, arg);
+ rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
break;
+ default:
+ rc = ioctl(tty, cmd, arg);
}
-
DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
return rc;
}
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 7c7217a..c2c0aa5 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -336,6 +336,11 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
{
int ret;
+ /* Kindly asking blocked readers to release the read side */
+ set_bit(TTY_LDISC_CHANGING, &tty->flags);
+ wake_up_interruptible_all(&tty->read_wait);
+ wake_up_interruptible_all(&tty->write_wait);
+
ret = __tty_ldisc_lock(tty, timeout);
if (!ret)
return -EBUSY;
@@ -346,6 +351,8 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
void tty_ldisc_unlock(struct tty_struct *tty)
{
clear_bit(TTY_LDISC_HALTED, &tty->flags);
+ /* Can be cleared here - ldisc_unlock will wake up writers firstly */
+ clear_bit(TTY_LDISC_CHANGING, &tty->flags);
__tty_ldisc_unlock(tty);
}
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 0617e87..8d6074f 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1491,7 +1491,7 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
kbd_rawcode(value);
- if (event_type == EV_KEY)
+ if (event_type == EV_KEY && event_code <= KEY_MAX)
kbd_keycode(event_code, value, HW_RAW(handle->dev));
spin_unlock(&kbd_event_lock);
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 2384ea8..2fb509d 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -437,6 +437,9 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
size_t ret;
char *con_buf;
+ if (use_unicode(inode))
+ return -EOPNOTSUPP;
+
con_buf = (char *) __get_free_page(GFP_KERNEL);
if (!con_buf)
return -ENOMEM;
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index e401be8..170fa1f 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -131,11 +131,12 @@ static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
= container_of(kobj, struct vmbus_channel, kobj);
struct hv_device *dev = channel->primary_channel->device_obj;
u16 q_idx = channel->offermsg.offer.sub_channel_index;
+ void *ring_buffer = page_address(channel->ringbuffer_page);
dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n",
q_idx, vma_pages(vma), vma->vm_pgoff);
- return vm_iomap_memory(vma, virt_to_phys(channel->ringbuffer_pages),
+ return vm_iomap_memory(vma, virt_to_phys(ring_buffer),
channel->ringbuffer_pagecount << PAGE_SHIFT);
}
@@ -224,7 +225,7 @@ hv_uio_probe(struct hv_device *dev,
/* mem resources */
pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
pdata->info.mem[TXRX_RING_MAP].addr
- = (uintptr_t)dev->channel->ringbuffer_pages;
+ = (uintptr_t)page_address(dev->channel->ringbuffer_page);
pdata->info.mem[TXRX_RING_MAP].size
= dev->channel->ringbuffer_pagecount << PAGE_SHIFT;
pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL;
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 2754b4c..60e292c 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -2168,10 +2168,11 @@ static void uea_intr(struct urb *urb)
/*
* Start the modem : init the data and start kernel thread
*/
-static int uea_boot(struct uea_softc *sc)
+static int uea_boot(struct uea_softc *sc, struct usb_interface *intf)
{
- int ret, size;
struct intr_pkt *intr;
+ int ret = -ENOMEM;
+ int size;
uea_enters(INS_TO_USBDEV(sc));
@@ -2196,6 +2197,11 @@ static int uea_boot(struct uea_softc *sc)
if (UEA_CHIP_VERSION(sc) == ADI930)
load_XILINX_firmware(sc);
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1) {
+ ret = -ENODEV;
+ goto err0;
+ }
+
intr = kmalloc(size, GFP_KERNEL);
if (!intr)
goto err0;
@@ -2207,8 +2213,7 @@ static int uea_boot(struct uea_softc *sc)
usb_fill_int_urb(sc->urb_int, sc->usb_dev,
usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
intr, size, uea_intr, sc,
- sc->usb_dev->actconfig->interface[0]->altsetting[0].
- endpoint[0].desc.bInterval);
+ intf->cur_altsetting->endpoint[0].desc.bInterval);
ret = usb_submit_urb(sc->urb_int, GFP_KERNEL);
if (ret < 0) {
@@ -2223,6 +2228,7 @@ static int uea_boot(struct uea_softc *sc)
sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm");
if (IS_ERR(sc->kthread)) {
uea_err(INS_TO_USBDEV(sc), "failed to create thread\n");
+ ret = PTR_ERR(sc->kthread);
goto err2;
}
@@ -2237,7 +2243,7 @@ static int uea_boot(struct uea_softc *sc)
kfree(intr);
err0:
uea_leaves(INS_TO_USBDEV(sc));
- return -ENOMEM;
+ return ret;
}
/*
@@ -2598,7 +2604,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
if (ret < 0)
goto error;
- ret = uea_boot(sc);
+ ret = uea_boot(sc, intf);
if (ret < 0)
goto error_rm_grp;
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index db4ceff..f25d482 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -203,14 +203,17 @@ static void ci_otg_work(struct work_struct *work)
}
pm_runtime_get_sync(ci->dev);
+
if (ci->id_event) {
ci->id_event = false;
ci_handle_id_switch(ci);
- } else if (ci->b_sess_valid_event) {
+ }
+
+ if (ci->b_sess_valid_event) {
ci->b_sess_valid_event = false;
ci_handle_vbus_change(ci);
- } else
- dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
+ }
+
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq);
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 34ad5bf8..424ecb1 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -343,6 +343,8 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
} else if (data->oc_polarity == 1) {
/* High active */
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
+ } else {
+ reg &= ~(MX6_BM_OVER_CUR_DIS);
}
writel(reg, usbmisc->base + data->index * 4);
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 83ffa5a..e6a7c86 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -342,7 +342,8 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
}
-static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
+static int usbtmc_ioctl_abort_bulk_out_tag(struct usbtmc_device_data *data,
+ u8 tag)
{
struct device *dev;
u8 *buffer;
@@ -359,8 +360,8 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
- data->bTag_last_write, data->bulk_out,
- buffer, 2, USBTMC_TIMEOUT);
+ tag, data->bulk_out,
+ buffer, 2, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -379,12 +380,14 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
n = 0;
usbtmc_abort_bulk_out_check_status:
+ /* do not stress device with subsequent requests */
+ msleep(50);
rv = usb_control_msg(data->usb_dev,
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
0, data->bulk_out, buffer, 0x08,
- USBTMC_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
n++;
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -418,6 +421,11 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
return rv;
}
+static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
+{
+ return usbtmc_ioctl_abort_bulk_out_tag(data, data->bTag_last_write);
+}
+
static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
void __user *arg)
{
@@ -1008,6 +1016,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
do {
dev_dbg(dev, "Reading from bulk in EP\n");
+ actual = 0;
rv = usb_bulk_msg(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev,
data->bulk_in),
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 08e6fea..3d2eb45 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -203,9 +203,58 @@ static const unsigned short super_speed_maxpacket_maxes[4] = {
[USB_ENDPOINT_XFER_INT] = 1024,
};
-static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
- int asnum, struct usb_host_interface *ifp, int num_ep,
- unsigned char *buffer, int size)
+static bool endpoint_is_duplicate(struct usb_endpoint_descriptor *e1,
+ struct usb_endpoint_descriptor *e2)
+{
+ if (e1->bEndpointAddress == e2->bEndpointAddress)
+ return true;
+
+ if (usb_endpoint_xfer_control(e1) || usb_endpoint_xfer_control(e2)) {
+ if (usb_endpoint_num(e1) == usb_endpoint_num(e2))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Check for duplicate endpoint addresses in other interfaces and in the
+ * altsetting currently being parsed.
+ */
+static bool config_endpoint_is_duplicate(struct usb_host_config *config,
+ int inum, int asnum, struct usb_endpoint_descriptor *d)
+{
+ struct usb_endpoint_descriptor *epd;
+ struct usb_interface_cache *intfc;
+ struct usb_host_interface *alt;
+ int i, j, k;
+
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intfc = config->intf_cache[i];
+
+ for (j = 0; j < intfc->num_altsetting; ++j) {
+ alt = &intfc->altsetting[j];
+
+ if (alt->desc.bInterfaceNumber == inum &&
+ alt->desc.bAlternateSetting != asnum)
+ continue;
+
+ for (k = 0; k < alt->desc.bNumEndpoints; ++k) {
+ epd = &alt->endpoint[k].desc;
+
+ if (endpoint_is_duplicate(epd, d))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static int usb_parse_endpoint(struct device *ddev, int cfgno,
+ struct usb_host_config *config, int inum, int asnum,
+ struct usb_host_interface *ifp, int num_ep,
+ unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
struct usb_endpoint_descriptor *d;
@@ -242,13 +291,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
goto skip_to_next_endpoint_or_interface_descriptor;
/* Check for duplicate endpoint addresses */
- for (i = 0; i < ifp->desc.bNumEndpoints; ++i) {
- if (ifp->endpoint[i].desc.bEndpointAddress ==
- d->bEndpointAddress) {
- dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
- cfgno, inum, asnum, d->bEndpointAddress);
- goto skip_to_next_endpoint_or_interface_descriptor;
- }
+ if (config_endpoint_is_duplicate(config, inum, asnum, d)) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
}
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
@@ -348,6 +394,11 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
/* Validate the wMaxPacketSize field */
maxp = usb_endpoint_maxp(&endpoint->desc);
+ if (maxp == 0) {
+ dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has wMaxPacketSize 0, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
+ }
/* Find the highest legal maxpacket size for this endpoint */
i = 0; /* additional transactions per microframe */
@@ -517,8 +568,8 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
if (((struct usb_descriptor_header *) buffer)->bDescriptorType
== USB_DT_INTERFACE)
break;
- retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,
- num_ep, buffer, size);
+ retval = usb_parse_endpoint(ddev, cfgno, config, inum, asnum,
+ alt, num_ep, buffer, size);
if (retval < 0)
return retval;
++n;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 29c6414..0020482 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -739,8 +739,15 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
- else
+ else {
+ unsigned int old_suppress;
+
+ /* suppress uevents while claiming interface */
+ old_suppress = dev_get_uevent_suppress(&intf->dev);
+ dev_set_uevent_suppress(&intf->dev, 1);
err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
+ dev_set_uevent_suppress(&intf->dev, old_suppress);
+ }
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
return err;
@@ -760,7 +767,13 @@ static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum)
if (!intf)
err = -ENOENT;
else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
+ unsigned int old_suppress;
+
+ /* suppress uevents while releasing interface */
+ old_suppress = dev_get_uevent_suppress(&intf->dev);
+ dev_set_uevent_suppress(&intf->dev, 1);
usb_driver_release_interface(&usbfs_driver, intf);
+ dev_set_uevent_suppress(&intf->dev, old_suppress);
err = 0;
}
return err;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 4884591b..5c6f1c4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -112,6 +112,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
+static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
+ u16 portstatus);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -1122,6 +1124,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
USB_PORT_FEAT_ENABLE);
}
+ /* Make sure a warm-reset request is handled by port_event */
+ if (type == HUB_RESUME &&
+ hub_port_warm_reset_required(hub, port1, portstatus))
+ set_bit(port1, hub->event_bits);
+
/*
* Add debounce if USB3 link is in polling/link training state.
* Link will automatically transition to Enabled state after
@@ -2661,7 +2668,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define SET_ADDRESS_TRIES 2
#define GET_DESCRIPTOR_TRIES 2
#define SET_CONFIG_TRIES (2 * (use_both_schemes + 1))
-#define USE_NEW_SCHEME(i, scheme) ((i) / 2 == (int)scheme)
+#define USE_NEW_SCHEME(i, scheme) ((i) / 2 == (int)(scheme))
#define HUB_ROOT_RESET_TIME 60 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
@@ -5747,7 +5754,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/**
* usb_reset_device - warn interface drivers and perform a USB port reset
- * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
+ * @udev: device to reset (not in NOTATTACHED state)
*
* Warns all drivers bound to registered interfaces (using their pre_reset
* method), performs the port reset, and then lets the drivers know that
@@ -5775,8 +5782,7 @@ int usb_reset_device(struct usb_device *udev)
struct usb_host_config *config = udev->actconfig;
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
- if (udev->state == USB_STATE_NOTATTACHED ||
- udev->state == USB_STATE_SUSPENDED) {
+ if (udev->state == USB_STATE_NOTATTACHED) {
dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
udev->state);
return -EINVAL;
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index f51750b..5e84409 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -45,6 +45,7 @@ void usb_init_urb(struct urb *urb)
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref);
+ INIT_LIST_HEAD(&urb->urb_list);
INIT_LIST_HEAD(&urb->anchor_list);
}
}
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 51d83f7..633ba01 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -524,7 +524,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset |= GRSTCTL_CSFTRST;
dwc2_writel(hsotg, greset, GRSTCTL);
- if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) {
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
__func__);
return -EBUSY;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index aad7edc..a5c8329 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3568,6 +3568,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 port_status;
u32 speed;
u32 pcgctl;
+ u32 pwr;
switch (typereq) {
case ClearHubFeature:
@@ -3616,8 +3617,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 &= ~HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0);
+ if (pwr)
+ dwc2_vbus_supply_exit(hsotg);
break;
case USB_PORT_FEAT_INDICATOR:
@@ -3827,8 +3831,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 |= HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
break;
case USB_PORT_FEAT_RESET:
@@ -3845,6 +3852,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dwc2_writel(hsotg, 0, PCGCTL);
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
/* Clear suspend bit if resetting from suspend state */
hprt0 &= ~HPRT0_SUSP;
@@ -3858,6 +3866,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"In host mode, hprt0=%08x\n", hprt0);
dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
}
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
@@ -4400,6 +4410,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
struct usb_bus *bus = hcd_to_bus(hcd);
unsigned long flags;
+ u32 hprt0;
int ret;
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
@@ -4416,12 +4427,16 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
dwc2_hcd_reinit(hsotg);
- /* enable external vbus supply before resuming root hub */
- spin_unlock_irqrestore(&hsotg->lock, flags);
- ret = dwc2_vbus_supply_init(hsotg);
- if (ret)
- return ret;
- spin_lock_irqsave(&hsotg->lock, flags);
+ hprt0 = dwc2_read_hprt0(hsotg);
+ /* Has vbus power been turned on in dwc2_core_host_init ? */
+ if (hprt0 & HPRT0_PWR) {
+ /* Enable external vbus supply before resuming root hub */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ ret = dwc2_vbus_supply_init(hsotg);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&hsotg->lock, flags);
+ }
/* Initialize and connect root hub if one is not already attached */
if (bus->root_hub) {
@@ -4443,6 +4458,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
+ u32 hprt0;
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
@@ -4451,6 +4467,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
synchronize_irq(hcd->irq);
spin_lock_irqsave(&hsotg->lock, flags);
+ hprt0 = dwc2_read_hprt0(hsotg);
/* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
@@ -4459,7 +4476,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
- dwc2_vbus_supply_exit(hsotg);
+ /* keep balanced supply init/exit by checking HPRT0_PWR */
+ if (hprt0 & HPRT0_PWR)
+ dwc2_vbus_supply_exit(hsotg);
usleep_range(1000, 3000);
}
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index dff2c6e..a93415f 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -88,6 +88,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->host_perio_tx_fifo_size = 256;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
+ p->power_down = 0;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 8a0f116..818a358 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -296,8 +296,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
- if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
- "request value same as default, ignoring\n")) {
+ if (dft != dwc->fladj) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
@@ -1070,6 +1069,19 @@ int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
}
+ if (dwc->gen2_tx_de_emph != -1)
+ dwc3_writel(dwc->regs, DWC31_LCSR_TX_DEEMPH(0),
+ dwc->gen2_tx_de_emph & DWC31_TX_DEEMPH_MASK);
+ if (dwc->gen2_tx_de_emph1 != -1)
+ dwc3_writel(dwc->regs, DWC31_LCSR_TX_DEEMPH_1(0),
+ dwc->gen2_tx_de_emph1 & DWC31_TX_DEEMPH_MASK);
+ if (dwc->gen2_tx_de_emph2 != -1)
+ dwc3_writel(dwc->regs, DWC31_LCSR_TX_DEEMPH_2(0),
+ dwc->gen2_tx_de_emph2 & DWC31_TX_DEEMPH_MASK);
+ if (dwc->gen2_tx_de_emph3 != -1)
+ dwc3_writel(dwc->regs, DWC31_LCSR_TX_DEEMPH_3(0),
+ dwc->gen2_tx_de_emph3 & DWC31_TX_DEEMPH_MASK);
+
dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT, 0);
/* set inter-packet gap 199.794ns to improve EL_23 margin */
@@ -1323,6 +1335,22 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
+ dwc->gen2_tx_de_emph = -1;
+ device_property_read_u32(dev, "snps,gen2-tx-de-emph",
+ &dwc->gen2_tx_de_emph);
+
+ dwc->gen2_tx_de_emph1 = -1;
+ device_property_read_u32(dev, "snps,gen2-tx-de-emph1",
+ &dwc->gen2_tx_de_emph1);
+
+ dwc->gen2_tx_de_emph2 = -1;
+ device_property_read_u32(dev, "snps,gen2-tx-de-emph2",
+ &dwc->gen2_tx_de_emph2);
+
+ dwc->gen2_tx_de_emph3 = -1;
+ device_property_read_u32(dev, "snps,gen2-tx-de-emph3",
+ &dwc->gen2_tx_de_emph3);
+
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 9dcbfc5..d28be09 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -171,6 +171,13 @@
#define GEN1_U3_EXIT_RSP_RX_CLK_MASK GEN1_U3_EXIT_RSP_RX_CLK(0xff)
#define DWC31_LINK_GDBGLTSSM 0xd050
+/* DWC 3.1 Tx De-emphasis Registers */
+#define DWC31_LCSR_TX_DEEMPH(n) (0xd060 + ((n) * 0x80))
+#define DWC31_LCSR_TX_DEEMPH_1(n) (0xd064 + ((n) * 0x80))
+#define DWC31_LCSR_TX_DEEMPH_2(n) (0xd068 + ((n) * 0x80))
+#define DWC31_LCSR_TX_DEEMPH_3(n) (0xd06c + ((n) * 0x80))
+#define DWC31_TX_DEEMPH_MASK 0x3ffff
+
/* Bit fields */
/* Global SoC Bus Configuration Register 1 */
@@ -696,9 +703,9 @@ struct dwc3_ep_events {
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
+ * @cancelled_list: list of cancelled requests for this endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
- * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
* @lock: spinlock for endpoint request queue traversal
* @regs: pointer to first endpoint register
* @trb_dma_pool: dma pool used to get aligned trb memory pool
@@ -726,11 +733,10 @@ struct dwc3_ep_events {
*/
struct dwc3_ep {
struct usb_ep endpoint;
+ struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
- wait_queue_head_t wait_end_transfer;
-
spinlock_t lock;
void __iomem *regs;
@@ -920,11 +926,12 @@ struct dwc3_hwparams {
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
- * @unaligned: true for OUT endpoints with length not divisible by maxp
+ * @num_trbs: number of TRBs used by this request
+ * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
+ * or unaligned OUT)
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
* @started: request is started
- * @zero: wants a ZLP
*/
struct dwc3_request {
struct usb_request request;
@@ -940,11 +947,12 @@ struct dwc3_request {
struct dwc3_trb *trb;
dma_addr_t trb_dma;
- unsigned unaligned:1;
+ unsigned num_trbs;
+
+ unsigned needs_extra_trb:1;
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
- unsigned zero:1;
};
/*
@@ -1347,6 +1355,10 @@ struct dwc3 {
int retries_on_error;
/* If true, GDSC collapse will happen in HOST mode bus suspend */
bool gdsc_collapse_in_host_suspend;
+ u32 gen2_tx_de_emph;
+ u32 gen2_tx_de_emph1;
+ u32 gen2_tx_de_emph2;
+ u32 gen2_tx_de_emph3;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index f1659b7..dc71517 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -155,6 +155,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
/**
+ * dwc3_gadget_hs_link_string - returns highspeed and below link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "On";
+ case DWC3_LINK_STATE_U2:
+ return "Sleep";
+ case DWC3_LINK_STATE_U3:
+ return "Suspend";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "Disconnected";
+ case DWC3_LINK_STATE_RX_DET:
+ return "Early Suspend";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
+/**
* dwc3_trb_type_string - returns TRB type as a string
* @type: the type of the TRB
*/
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 9e47b00..c5b7974 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -482,6 +482,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
unsigned long flags;
enum dwc3_link_state state;
u32 reg;
+ u8 speed;
if (atomic_read(&dwc->in_lpm)) {
seq_puts(s, "USB device is powered off\n");
@@ -491,9 +492,12 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ speed = reg & DWC3_DSTS_CONNECTSPD;
- seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
+ seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ?
+ dwc3_gadget_link_string(state) :
+ dwc3_gadget_hs_link_string(state));
+ spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
@@ -511,6 +515,8 @@ static ssize_t dwc3_link_state_write(struct file *file,
unsigned long flags;
enum dwc3_link_state state = 0;
char buf[32] = {};
+ u32 reg;
+ u8 speed;
if (atomic_read(&dwc->in_lpm)) {
seq_puts(s, "USB device is powered off\n");
@@ -536,6 +542,15 @@ static ssize_t dwc3_link_state_write(struct file *file,
return -EINVAL;
spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+
+ if (speed < DWC3_DSTS_SUPERSPEED &&
+ state != DWC3_LINK_STATE_RECOV) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -EINVAL;
+ }
+
dwc3_gadget_set_link_state(dwc, state);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 81d667e..e6ae1fe 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -1047,21 +1047,30 @@ static void gsi_ring_db(struct usb_ep *ep, struct usb_gsi_request *request)
gsi_dbl_address_lsb = devm_ioremap_nocache(mdwc->dev,
request->db_reg_phs_addr_lsb, sizeof(u32));
- if (!gsi_dbl_address_lsb)
- dev_dbg(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+ if (!gsi_dbl_address_lsb) {
+ dev_err(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+ return;
+ }
gsi_dbl_address_msb = devm_ioremap_nocache(mdwc->dev,
request->db_reg_phs_addr_msb, sizeof(u32));
- if (!gsi_dbl_address_msb)
- dev_dbg(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+ if (!gsi_dbl_address_msb) {
+ dev_err(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+ return;
+ }
offset = dwc3_trb_dma_offset(dep, &dep->trb_pool[num_trbs-1]);
dev_dbg(mdwc->dev, "Writing link TRB addr: %pa to %pK (%x) for ep:%s\n",
&offset, gsi_dbl_address_lsb, request->db_reg_phs_addr_lsb,
ep->name);
+ dbg_log_string("ep:%s link TRB addr:%pa db:%x\n",
+ ep->name, &offset, request->db_reg_phs_addr_lsb);
+
writel_relaxed(offset, gsi_dbl_address_lsb);
+ readl_relaxed(gsi_dbl_address_lsb);
writel_relaxed(0, gsi_dbl_address_msb);
+ readl_relaxed(gsi_dbl_address_msb);
}
/**
@@ -1145,7 +1154,7 @@ static int gsi_prepare_trbs(struct usb_ep *ep, struct usb_gsi_request *req)
req->buf_base_addr = dma_alloc_attrs(dwc->sysdev, len, &req->dma,
GFP_KERNEL, dma_attr);
if (!req->buf_base_addr) {
- dev_err(dwc->dev, "%s: buf_base_addr allocate failed %s\n",
+ dev_err(dwc->dev, "buf_base_addr allocate failed %s\n",
dep->name);
return -ENOMEM;
}
@@ -1444,6 +1453,7 @@ static void gsi_set_clear_dbell(struct usb_ep *ep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
+ dbg_log_string("block_db(%d)", block_db);
dwc3_msm_write_reg_field(mdwc->base,
GSI_GENERAL_CFG_REG(mdwc->gsi_reg[GENERAL_CFG_REG]),
BLOCK_GSI_WR_GO_MASK, block_db);
@@ -1579,6 +1589,11 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
break;
case GSI_EP_OP_SET_CLR_BLOCK_DBL:
block_db = *((bool *)op_data);
+ if (!dwc->pullups_connected && !block_db) {
+ dbg_log_string("No Pullup\n");
+ return -ESHUTDOWN;
+ }
+
gsi_set_clear_dbell(ep, block_db);
break;
case GSI_EP_OP_CHECK_FOR_SUSPEND:
@@ -4112,10 +4127,10 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
if (on) {
dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);
+ mdwc->hs_phy->flags |= PHY_HOST_MODE;
pm_runtime_get_sync(mdwc->dev);
dbg_event(0xFF, "StrtHost gync",
atomic_read(&mdwc->dev->power.usage_count));
- mdwc->hs_phy->flags |= PHY_HOST_MODE;
if (dwc->maximum_speed >= USB_SPEED_SUPER) {
mdwc->ss_phy->flags |= PHY_HOST_MODE;
usb_phy_notify_connect(mdwc->ss_phy,
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 8cced36..8fa39e6 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -29,7 +29,8 @@
#define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
-#define PCI_DEVICE_ID_INTEL_CMLH 0x02ee
+#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee
+#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee
#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee
#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
@@ -256,7 +257,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
ret = platform_device_add_properties(dwc->dwc3, p);
if (ret < 0)
- return ret;
+ goto err;
ret = dwc3_pci_quirks(dwc);
if (ret)
@@ -306,6 +307,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD),
(kernel_ulong_t) &dwc3_pci_mrfld_properties, },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLLP),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLH),
(kernel_ulong_t) &dwc3_pci_intel_properties, },
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 76b3fb4..1b70b8d 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -206,7 +206,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc || !dwc->softconnect ||
+ if (!dep->endpoint.desc || !dwc->pullups_connected ||
!dwc->vbus_active) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
@@ -1237,10 +1237,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- struct dwc3_ep *dep;
- u8 epnum = event->endpoint_number;
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+ u8 cmd;
- dep = dwc->eps[epnum];
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
@@ -1262,6 +1261,10 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
dep->dbg_ep_events.streamevent++;
break;
case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER)
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dep->dbg_ep_events.epcmdcomplete++;
break;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7139758..cfe0baa 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -283,8 +283,7 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
req->started = false;
list_del(&req->list);
req->remaining = 0;
- req->unaligned = false;
- req->zero = false;
+ req->needs_extra_trb = false;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
@@ -375,27 +374,36 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 3000;
+ u32 saved_config = 0;
u32 reg;
int cmd_status = 0;
- int susphy = false;
int ret = -EINVAL;
/*
- * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
- * we're issuing an endpoint command, we must check if
- * GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
+ * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
+ * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
+ * endpoint command.
*
- * We will also set SUSPHY bit to what it was before returning as stated
- * by the same section on Synopsys databook.
+ * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
+ * settings. Restore them after the command is completed.
+ *
+ * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
if (dwc->gadget.speed <= USB_SPEED_HIGH) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
- susphy = true;
+ saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
+
+ if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
+ saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ }
+
+ if (saved_config)
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
@@ -472,24 +480,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
- if (ret == 0) {
- switch (DWC3_DEPCMD_CMD(cmd)) {
- case DWC3_DEPCMD_STARTTRANSFER:
- dep->flags |= DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_get_transfer_index(dep);
- break;
- case DWC3_DEPCMD_ENDTRANSFER:
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- break;
- default:
- /* nothing */
- break;
- }
+ if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_get_transfer_index(dep);
}
- if (unlikely(susphy)) {
+ if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ reg |= saved_config;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
@@ -755,8 +753,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
- init_waitqueue_head(&dep->wait_end_transfer);
-
if (usb_endpoint_xfer_control(desc))
goto out;
@@ -841,6 +837,12 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
+ while (!list_empty(&dep->cancelled_list)) {
+ req = next_request(&dep->cancelled_list);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+
dbg_log_string("DONE for %s(%d)", dep->name, dep->number);
}
@@ -864,6 +866,15 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0);
dwc3_remove_requests(dwc, dep);
+ if (dep->endpoint.ep_type != EP_TYPE_GSI &&
+ !dep->endpoint.endless) {
+ if (dep->trb_pool) {
+ memset(&dep->trb_pool[0], 0,
+ sizeof(struct dwc3_trb) *
+ dep->num_trbs);
+ dbg_event(dep->number, "Clr_TRB", 0);
+ }
+ }
}
dbg_log_string("DONE");
}
@@ -1200,6 +1211,12 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
+ /*
+ * Ensure that updates of buffer address and size happens
+ * before we set the DWC3_TRB_CTRL_HWO so that core
+ * does not process any stale TRB.
+ */
+ mb();
trb->ctrl |= DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_enq(dep);
@@ -1240,6 +1257,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
}
+ req->num_trbs++;
+
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
stream_id, short_not_ok, no_interrupt);
}
@@ -1274,13 +1293,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->unaligned = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, i);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
maxp - rem, false, 1,
req->request.stream_id,
@@ -1318,13 +1338,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->unaligned = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
false, 1, req->request.stream_id,
req->request.short_not_ok,
@@ -1334,13 +1355,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->zero = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 1, req->request.stream_id,
req->request.short_not_ok,
@@ -1605,6 +1627,42 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
return ret;
}
+static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ int i;
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ */
+ for (i = 0; i < req->num_trbs; i++) {
+ struct dwc3_trb *trb;
+
+ trb = req->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+
+ req->num_trbs = 0;
+}
+
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+ dwc3_gadget_ep_skip_trbs(dep, req);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ }
+}
+
static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *request)
{
@@ -1640,64 +1698,14 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
/* wait until it is processed */
dwc3_stop_active_transfer(dwc, dep->number, true);
- /*
- * If request was already started, this means we had to
- * stop the transfer. With that we also need to ignore
- * all TRBs used by the request, however TRBs can only
- * be modified after completion of END_TRANSFER
- * command. So what we do here is that we wait for
- * END_TRANSFER completion and only after that, we jump
- * over TRBs by clearing HWO and incrementing dequeue
- * pointer.
- *
- * Note that we have 2 possible types of transfers here:
- *
- * i) Linear buffer request
- * ii) SG-list based request
- *
- * SG-list based requests will have r->num_pending_sgs
- * set to a valid number (> 0). Linear requests,
- * normally use a single TRB.
- *
- * For each of these two cases, if r->unaligned flag is
- * set, one extra TRB has been used to align transfer
- * size to wMaxPacketSize.
- *
- * All of these cases need to be taken into
- * consideration so we don't mess up our TRB ring
- * pointers.
- */
if (!r->trb)
goto out0;
- if (r->num_pending_sgs) {
- struct dwc3_trb *trb;
- int i = 0;
-
- for (i = 0; i < r->num_pending_sgs; i++) {
- trb = r->trb + i;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
-
- if (r->unaligned || r->zero) {
- trb = r->trb + r->num_pending_sgs + 1;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
- } else {
- struct dwc3_trb *trb = r->trb;
-
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
-
- if (r->unaligned || r->zero) {
- trb = r->trb + 1;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
- }
- goto out1;
+ dwc3_gadget_move_cancelled_request(req);
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
+ goto out0;
+ else
+ goto out1;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
@@ -1707,8 +1715,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
out1:
dbg_ep_dequeue(dep->number, req);
- /* giveback the request */
-
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -2703,6 +2709,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
+ INIT_LIST_HEAD(&dep->cancelled_list);
return 0;
}
@@ -2786,6 +2793,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
dwc3_ep_inc_deq(dep);
trace_dwc3_complete_trb(dep, trb);
+ req->num_trbs--;
/*
* If we're in the middle of series of chained TRBs and we
@@ -2805,7 +2813,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
- if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+
+ if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2866,6 +2875,13 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
+ /*
+ * For OUT direction, host may send less than the setup
+ * length. Return true for all OUT requests.
+ */
+ if (!req->direction)
+ return true;
+
return req->request.actual == req->request.length;
}
@@ -2893,16 +2909,15 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
- if (req->unaligned || req->zero) {
+ if (req->needs_extra_trb) {
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
- req->unaligned = false;
- req->zero = false;
+ req->needs_extra_trb = false;
}
req->request.actual = req->request.length - req->remaining;
- if (!dwc3_gadget_ep_request_completed(req) &&
+ if (!dwc3_gadget_ep_request_completed(req) ||
req->num_pending_sgs) {
__dwc3_gadget_kick_transfer(dep);
goto out;
@@ -3034,8 +3049,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
- dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
- wake_up(&dep->wait_end_transfer);
+ dep->flags &= ~(DWC3_EP_END_TRANSFER_PENDING |
+ DWC3_EP_TRANSFER_STARTED);
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+ dbg_log_string("DWC3_DEPEVT_EPCMDCMPLT (%d)",
+ dep->number);
}
break;
case DWC3_DEPEVT_STREAMEVT:
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index aa1ec8a..90e166e 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -89,6 +89,21 @@ static inline void dwc3_gadget_move_pending_list_front(struct dwc3_request *req)
list_move(&req->list, &dep->pending_list);
}
+/**
+ * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
+ * @req: the request to be moved
+ *
+ * Caller should take care of locking. This function will move @req from its
+ * current list to the endpoint's cancelled_list.
+ */
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+{
+ struct dwc3_ep *dep = req->dep;
+
+ req->started = false;
+ list_move_tail(&req->list, &dep->cancelled_list);
+}
+
static inline enum dwc3_link_state dwc3_get_link_state(struct dwc3 *dwc)
{
u32 reg;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 97c39be..9086b64 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1588,6 +1588,9 @@ static int count_ext_prop(struct usb_configuration *c, int interface)
struct usb_function *f;
int j;
+ if (interface >= c->next_interface_id)
+ return -EINVAL;
+
f = c->interface[interface];
for (j = 0; j < f->os_desc_n; ++j) {
struct usb_os_desc *d;
@@ -1607,6 +1610,9 @@ static int len_ext_prop(struct usb_configuration *c, int interface)
struct usb_os_desc *d;
int j, res;
+ if (interface >= c->next_interface_id)
+ return -EINVAL;
+
res = 10; /* header length */
f = c->interface[interface];
for (j = 0; j < f->os_desc_n; ++j) {
@@ -1996,6 +2002,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
buf[6] = w_index;
count = count_ext_prop(os_desc_cfg,
interface);
+ if (count < 0)
+ return count;
put_unaligned_le16(count, buf + 8);
count = len_ext_prop(os_desc_cfg,
interface);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 22ca9c2..3006274 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -93,6 +93,8 @@ struct gadget_info {
bool unbinding;
char b_vendor_code;
char qw_sign[OS_STRING_QW_SIGN_LEN];
+ spinlock_t spinlock;
+ bool unbind;
#ifdef CONFIG_USB_CONFIGFS_UEVENT
bool connected;
bool sw_connected;
@@ -1293,6 +1295,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
int ret;
/* the gi->lock is hold by the caller */
+ gi->unbind = 0;
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
ret = composite_dev_prepare(composite, cdev);
@@ -1479,19 +1482,118 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
struct gadget_info *gi;
+ unsigned long flags;
/* the gi->lock is hold by the caller */
cdev = get_gadget_data(gadget);
gi = container_of(cdev, struct gadget_info, cdev);
+ spin_lock_irqsave(&gi->spinlock, flags);
+ gi->unbind = 1;
+ spin_unlock_irqrestore(&gi->spinlock, flags);
kfree(otg_desc[0]);
otg_desc[0] = NULL;
purge_configs_funcs(gi);
composite_dev_cleanup(cdev);
usb_ep_autoconfig_reset(cdev->gadget);
+ spin_lock_irqsave(&gi->spinlock, flags);
cdev->gadget = NULL;
set_gadget_data(gadget, NULL);
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+}
+
+#ifndef CONFIG_USB_CONFIGFS_UEVENT
+static int configfs_composite_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev;
+ struct gadget_info *gi;
+ unsigned long flags;
+ int ret;
+
+ cdev = get_gadget_data(gadget);
+ if (!cdev)
+ return 0;
+
+ gi = container_of(cdev, struct gadget_info, cdev);
+ spin_lock_irqsave(&gi->spinlock, flags);
+ cdev = get_gadget_data(gadget);
+ if (!cdev || gi->unbind) {
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+ return 0;
+ }
+
+ ret = composite_setup(gadget, ctrl);
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+ return ret;
+}
+
+static void configfs_composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev;
+ struct gadget_info *gi;
+ unsigned long flags;
+
+ cdev = get_gadget_data(gadget);
+ if (!cdev)
+ return;
+
+ gi = container_of(cdev, struct gadget_info, cdev);
+ spin_lock_irqsave(&gi->spinlock, flags);
+ cdev = get_gadget_data(gadget);
+ if (!cdev || gi->unbind) {
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+ return;
+ }
+
+ composite_disconnect(gadget);
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+}
+#endif
+
+static void configfs_composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev;
+ struct gadget_info *gi;
+ unsigned long flags;
+
+ cdev = get_gadget_data(gadget);
+ if (!cdev)
+ return;
+
+ gi = container_of(cdev, struct gadget_info, cdev);
+ spin_lock_irqsave(&gi->spinlock, flags);
+ cdev = get_gadget_data(gadget);
+ if (!cdev || gi->unbind) {
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+ return;
+ }
+
+ composite_suspend(gadget);
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+}
+
+static void configfs_composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev;
+ struct gadget_info *gi;
+ unsigned long flags;
+
+ cdev = get_gadget_data(gadget);
+ if (!cdev)
+ return;
+
+ gi = container_of(cdev, struct gadget_info, cdev);
+ spin_lock_irqsave(&gi->spinlock, flags);
+ cdev = get_gadget_data(gadget);
+ if (!cdev || gi->unbind) {
+ spin_unlock_irqrestore(&gi->spinlock, flags);
+ return;
+ }
+
+ composite_resume(gadget);
+ spin_unlock_irqrestore(&gi->spinlock, flags);
}
#ifdef CONFIG_USB_CONFIGFS_UEVENT
@@ -1588,12 +1690,12 @@ static const struct usb_gadget_driver configfs_driver_template = {
.reset = android_disconnect,
.disconnect = android_disconnect,
#else
- .setup = composite_setup,
- .reset = composite_disconnect,
- .disconnect = composite_disconnect,
+ .setup = configfs_composite_setup,
+ .reset = configfs_composite_disconnect,
+ .disconnect = configfs_composite_disconnect,
#endif
- .suspend = composite_suspend,
- .resume = composite_resume,
+ .suspend = configfs_composite_suspend,
+ .resume = configfs_composite_resume,
.max_speed = USB_SPEED_SUPER_PLUS,
.driver = {
@@ -1728,6 +1830,7 @@ static struct config_group *gadgets_make(
gi->composite.resume = NULL;
gi->composite.max_speed = USB_SPEED_SUPER;
+ spin_lock_init(&gi->spinlock);
mutex_init(&gi->lock);
INIT_LIST_HEAD(&gi->string_list);
INIT_LIST_HEAD(&gi->available_func);
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 54d6ccc..591bff9 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -833,6 +833,12 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
u16 w_length = le16_to_cpu(ctrl->wLength);
unsigned long flags;
+ /*
+ * If instance is not created which is the case in power off charging
+ * mode, dev will be NULL. Hence return error if it is the case.
+ */
+ if (!dev)
+ return -ENODEV;
/*
printk(KERN_INFO "acc_ctrlrequest "
"%02x.%02x v%04x i%04x l%u\n",
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 6ce0440..460d5d7 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -621,8 +621,12 @@ static void ecm_disable(struct usb_function *f)
DBG(cdev, "ecm deactivated\n");
- if (ecm->port.in_ep->enabled)
+ if (ecm->port.in_ep->enabled) {
gether_disconnect(&ecm->port);
+ } else {
+ ecm->port.in_ep->desc = NULL;
+ ecm->port.out_ep->desc = NULL;
+ }
usb_ep_disable(ecm->notify);
ecm->notify->desc = NULL;
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 0b3f905..00dc4cb 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -944,6 +944,10 @@ static void receive_file_work(struct work_struct *data)
mtp_log("- count(%lld) not multiple of mtu(%d)\n",
count, dev->ep_out->maxpacket);
mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ goto fail;
+ }
while (count > 0 || write_req) {
if (count > 0) {
/* queue a request */
@@ -1026,6 +1030,7 @@ static void receive_file_work(struct work_struct *data)
read_req = NULL;
}
}
+fail:
mutex_unlock(&dev->read_mutex);
mtp_log("returning %d\n", r);
/* write the result */
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index f0ae181..f4372d1 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -2,7 +2,7 @@
/*
* f_qdss.c -- QDSS function Driver
*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
@@ -440,7 +440,7 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
&qdss_data_ep_comp_desc);
if (!ep) {
pr_err("%s: ep_autoconfig error\n", __func__);
- goto fail;
+ goto clear_ep;
}
qdss->port.data = ep;
ep->driver_data = qdss;
@@ -450,8 +450,9 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
&qdss_ctrl_in_ep_comp_desc);
if (!ep) {
pr_err("%s: ep_autoconfig error\n", __func__);
- goto fail;
+ goto clear_ep;
}
+
qdss->port.ctrl_in = ep;
ep->driver_data = qdss;
@@ -459,12 +460,21 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
&qdss_ctrl_out_ep_comp_desc);
if (!ep) {
pr_err("%s: ep_autoconfig error\n", __func__);
- goto fail;
+ goto clear_ep;
}
qdss->port.ctrl_out = ep;
ep->driver_data = qdss;
}
+ if (!strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) {
+ ret = alloc_sps_req(qdss->port.data);
+ if (ret) {
+ pr_err("%s: alloc_sps_req error (%d)\n",
+ __func__, ret);
+ goto clear_ep;
+ }
+ }
+
/*update fs descriptors*/
qdss_fs_data_desc.bEndpointAddress =
qdss_ss_data_desc.bEndpointAddress;
@@ -497,9 +507,18 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
return 0;
+
fail:
+ /* check if usb_request allocated */
+ if (qdss->endless_req) {
+ usb_ep_free_request(qdss->port.data,
+ qdss->endless_req);
+ qdss->endless_req = NULL;
+ }
+
+clear_ep:
clear_eps(f);
- clear_desc(gadget, f);
+
return -ENOTSUPP;
}
@@ -513,6 +532,12 @@ static void qdss_unbind(struct usb_configuration *c, struct usb_function *f)
flush_workqueue(qdss->wq);
+ if (qdss->endless_req) {
+ usb_ep_free_request(qdss->port.data,
+ qdss->endless_req);
+ qdss->endless_req = NULL;
+ }
+
/* Reset string ids */
qdss_string_defs[QDSS_DATA_IDX].id = 0;
qdss_string_defs[QDSS_CTRL_IDX].id = 0;
@@ -1025,11 +1050,6 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
spin_unlock_irqrestore(&qdss_lock, flags);
usb_ep_dequeue(qdss->port.data, qdss->endless_req);
spin_lock_irqsave(&qdss_lock, flags);
- if (qdss->endless_req) {
- usb_ep_free_request(qdss->port.data,
- qdss->endless_req);
- qdss->endless_req = NULL;
- }
}
gadget = qdss->gadget;
ch->app_conn = 0;
diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h
index 5a68f23..cd7c554 100644
--- a/drivers/usb/gadget/function/f_qdss.h
+++ b/drivers/usb/gadget/function/f_qdss.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _F_QDSS_H
@@ -74,4 +74,5 @@ struct usb_qdss_opts {
int uninit_data(struct usb_ep *ep);
int set_qdss_data_connection(struct f_qdss *qdss, int enable);
+int alloc_sps_req(struct usb_ep *data_ep);
#endif
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 59d0bd1..d5373bc 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -620,6 +620,7 @@ static void rndis_disable(struct usb_function *f)
gether_disconnect(&rndis->port);
usb_ep_disable(rndis->notify);
+ rndis->notify->desc = NULL;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c
index 1747d1c..7841295 100644
--- a/drivers/usb/gadget/function/u_qdss.c
+++ b/drivers/usb/gadget/function/u_qdss.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -9,7 +9,7 @@
#include <linux/dma-mapping.h>
#include "f_qdss.h"
-static int alloc_sps_req(struct usb_ep *data_ep)
+int alloc_sps_req(struct usb_ep *data_ep)
{
struct usb_request *req = NULL;
struct f_qdss *qdss = data_ep->driver_data;
@@ -24,8 +24,7 @@ static int alloc_sps_req(struct usb_ep *data_ep)
}
req->length = 32*1024;
- sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
- qdss->bam_info.usb_bam_pipe_idx;
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB;
req->udc_priv = sps_params;
qdss->endless_req = req;
@@ -98,7 +97,7 @@ int set_qdss_data_connection(struct f_qdss *qdss, int enable)
&bam_info.usb_bam_pipe_idx,
NULL, bam_info.data_fifo, NULL);
- alloc_sps_req(qdss->port.data);
+ qdss->endless_req->udc_priv |= qdss->bam_info.usb_bam_pipe_idx;
msm_data_fifo_config(qdss->port.data,
bam_info.data_fifo->iova,
bam_info.data_fifo->size,
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 29436f7..d4d317d 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -1246,8 +1246,10 @@ int gserial_alloc_line(unsigned char *line_num)
__func__, port_num, PTR_ERR(tty_dev));
ret = PTR_ERR(tty_dev);
+ mutex_lock(&ports[port_num].lock);
port = ports[port_num].port;
ports[port_num].port = NULL;
+ mutex_unlock(&ports[port_num].lock);
gserial_free_port(port);
goto err;
}
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index b51f0d2..2e4c039 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -9,6 +9,9 @@
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*/
+
+#include <linux/sort.h>
+
#include "u_uvc.h"
#include "uvc_configfs.h"
@@ -31,6 +34,14 @@ static struct configfs_attribute prefix##attr_##cname = { \
.show = prefix##cname##_show, \
}
+static int uvcg_config_compare_u32(const void *l, const void *r)
+{
+ u32 li = *(const u32 *)l;
+ u32 ri = *(const u32 *)r;
+
+ return li < ri ? -1 : li == ri ? 0 : 1;
+}
+
static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_uvc_opts,
@@ -544,6 +555,7 @@ static int uvcg_control_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -579,6 +591,7 @@ static void uvcg_control_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
@@ -764,6 +777,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
format_ptr->fmt = target_fmt;
list_add_tail(&format_ptr->entry, &src_hdr->formats);
++src_hdr->num_fmt;
+ ++target_fmt->linked;
out:
mutex_unlock(&opts->lock);
@@ -801,6 +815,8 @@ static void uvcg_streaming_header_drop_link(struct config_item *src,
break;
}
+ --target_fmt->linked;
+
out:
mutex_unlock(&opts->lock);
mutex_unlock(su_mutex);
@@ -1129,6 +1145,8 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item,
kfree(ch->dw_frame_interval);
ch->dw_frame_interval = frm_intrv;
ch->frame.b_frame_interval_type = n;
+ sort(ch->dw_frame_interval, n, sizeof(*ch->dw_frame_interval),
+ uvcg_config_compare_u32, NULL);
ret = len;
end:
@@ -2038,6 +2056,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -2078,6 +2097,7 @@ static void uvcg_streaming_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index d3567b9..2c9821e 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -125,6 +125,21 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
* Request handling
*/
+static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
+{
+ int ret;
+
+ ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ printk(KERN_INFO "Failed to queue request (%d).\n", ret);
+ /* Isochronous endpoints can't be halted. */
+ if (usb_endpoint_xfer_bulk(video->ep->desc))
+ usb_ep_set_halt(video->ep);
+ }
+
+ return ret;
+}
+
/*
* I somehow feel that synchronisation won't be easy to achieve here. We have
* three events that control USB requests submission:
@@ -189,14 +204,13 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
video->encode(req, video, buf);
- if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
- printk(KERN_INFO "Failed to queue request (%d).\n", ret);
- usb_ep_set_halt(ep);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ ret = uvcg_video_ep_queue(video, req);
+ spin_unlock_irqrestore(&video->queue.irqlock, flags);
+
+ if (ret < 0) {
uvcg_queue_cancel(queue, 0);
goto requeue;
}
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
return;
@@ -316,15 +330,13 @@ int uvcg_video_pump(struct uvc_video *video)
video->encode(req, video, buf);
/* Queue the USB request */
- ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+ ret = uvcg_video_ep_queue(video, req);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
if (ret < 0) {
- printk(KERN_INFO "Failed to queue request (%d)\n", ret);
- usb_ep_set_halt(video->ep);
- spin_unlock_irqrestore(&queue->irqlock, flags);
uvcg_queue_cancel(queue, 0);
break;
}
- spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 8f267be..a4ab230 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -436,9 +436,11 @@ static void submit_request(struct usba_ep *ep, struct usba_request *req)
next_fifo_transaction(ep, req);
if (req->last_transaction) {
usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
- usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+ if (ep_is_control(ep))
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
} else {
- usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ if (ep_is_control(ep))
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
}
}
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 808cefe..4e944c0 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -98,6 +98,17 @@ int usb_ep_enable(struct usb_ep *ep)
if (ep->enabled)
goto out;
+ /* UDC drivers can't handle endpoints with maxpacket size 0 */
+ if (usb_endpoint_maxp(ep->desc) == 0) {
+ /*
+ * We should log an error message here, but we can't call
+ * dev_err() because there's no way to find the gadget
+ * given only ep.
+ */
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = ep->ops->enable(ep, ep->desc);
if (ret)
goto out;
@@ -1175,7 +1186,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
dev_name(&udc->dev)) == 0) {
ret = udc_bind_to_driver(udc, driver);
if (ret != -EPROBE_DEFER)
- list_del(&driver->pending);
+ list_del_init(&driver->pending);
break;
}
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 1505e55..d0248c5 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -1335,7 +1335,7 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
u32 this_sg;
bool next_sg;
- to_host = usb_pipein(urb->pipe);
+ to_host = usb_urb_dir_in(urb);
rbuf = req->req.buf + req->req.actual;
if (!urb->num_sgs) {
@@ -1423,7 +1423,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
/* FIXME update emulated data toggle too */
- to_host = usb_pipein(urb->pipe);
+ to_host = usb_urb_dir_in(urb);
if (unlikely(len == 0))
is_short = 1;
else {
@@ -1844,7 +1844,7 @@ static void dummy_timer(struct timer_list *t)
/* find the gadget's ep for this request (if configured) */
address = usb_pipeendpoint (urb->pipe);
- if (usb_pipein(urb->pipe))
+ if (usb_urb_dir_in(urb))
address |= USB_DIR_IN;
ep = find_endpoint(dum, address);
if (!ep) {
@@ -2399,7 +2399,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
s = "?";
break;
} s; }),
- ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
+ ep, ep ? (usb_urb_dir_in(urb) ? "in" : "out") : "",
({ char *s; \
switch (usb_pipetype(urb->pipe)) { \
case PIPE_CONTROL: \
@@ -2739,7 +2739,7 @@ static struct platform_driver dummy_hcd_driver = {
};
/*-------------------------------------------------------------------------*/
-#define MAX_NUM_UDC 2
+#define MAX_NUM_UDC 32
static struct platform_device *the_udc_pdev[MAX_NUM_UDC];
static struct platform_device *the_hcd_pdev[MAX_NUM_UDC];
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index 587c503..bc6abae 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -741,7 +741,7 @@ static void fotg210_get_status(struct fotg210_udc *fotg210,
fotg210->ep0_req->length = 2;
spin_unlock(&fotg210->lock);
- fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL);
+ fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC);
spin_lock(&fotg210->lock);
}
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index be59309..d44b26d 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -2552,7 +2552,7 @@ static int fsl_udc_remove(struct platform_device *pdev)
dma_pool_destroy(udc_controller->td_pool);
free_irq(udc_controller->irq, udc_controller);
iounmap(dr_regs);
- if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+ if (res && (pdata->operating_mode == FSL_USB2_DR_DEVICE))
release_mem_region(res->start, resource_size(res));
/* free udc --wait for the release() finished */
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index afaea11..991184b 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -1520,7 +1520,6 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
td = phys_to_virt(addr);
addr2 = (dma_addr_t)td->next;
dma_pool_free(dev->data_requests, td, addr);
- td->next = 0x00;
addr = addr2;
}
req->chain_len = 1;
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 7e4c1334..7d20296 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -159,11 +159,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
/* get the PHY device */
phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
if (IS_ERR(phy)) {
- /* Don't bail out if PHY is not absolutely necessary */
- if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY)
- continue;
-
ret = PTR_ERR(phy);
+ if (ret == -ENODEV) { /* no PHY */
+ phy = NULL;
+ continue;
+ }
+
if (ret != -EPROBE_DEFER)
dev_err(dev, "Can't get PHY for port %d: %d\n",
i, ret);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 3276304..f643603 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -27,6 +27,10 @@
/*-------------------------------------------------------------------------*/
+/* PID Codes that are used here, from EHCI specification, Table 3-16. */
+#define PID_CODE_IN 1
+#define PID_CODE_SETUP 2
+
/* fill a qtd, returning how much of the buffer we were able to queue up */
static int
@@ -190,7 +194,7 @@ static int qtd_copy_status (
int status = -EINPROGRESS;
/* count IN/OUT bytes, not SETUP (even short packets) */
- if (likely (QTD_PID (token) != 2))
+ if (likely(QTD_PID(token) != PID_CODE_SETUP))
urb->actual_length += length - QTD_LENGTH (token);
/* don't modify error codes */
@@ -206,6 +210,13 @@ static int qtd_copy_status (
if (token & QTD_STS_BABBLE) {
/* FIXME "must" disable babbling device's port too */
status = -EOVERFLOW;
+ /*
+ * When MMF is active and PID Code is IN, queue is halted.
+ * EHCI Specification, Table 4-13.
+ */
+ } else if ((token & QTD_STS_MMF) &&
+ (QTD_PID(token) == PID_CODE_IN)) {
+ status = -EPROTO;
/* CERR nonzero + halt --> stall */
} else if (QTD_CERR(token)) {
status = -EPIPE;
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 7ba6afc..76c3f29 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -202,10 +202,10 @@ static void xhci_ring_dump_segment(struct seq_file *s,
trb = &seg->trbs[i];
dma = seg->dma + i * sizeof(*trb);
seq_printf(s, "%pad: %s\n", &dma,
- xhci_decode_trb(trb->generic.field[0],
- trb->generic.field[1],
- trb->generic.field[2],
- trb->generic.field[3]));
+ xhci_decode_trb(le32_to_cpu(trb->generic.field[0]),
+ le32_to_cpu(trb->generic.field[1]),
+ le32_to_cpu(trb->generic.field[2]),
+ le32_to_cpu(trb->generic.field[3])));
}
}
@@ -263,10 +263,10 @@ static int xhci_slot_context_show(struct seq_file *s, void *unused)
xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma,
- xhci_decode_slot_context(slot_ctx->dev_info,
- slot_ctx->dev_info2,
- slot_ctx->tt_info,
- slot_ctx->dev_state));
+ xhci_decode_slot_context(le32_to_cpu(slot_ctx->dev_info),
+ le32_to_cpu(slot_ctx->dev_info2),
+ le32_to_cpu(slot_ctx->tt_info),
+ le32_to_cpu(slot_ctx->dev_state)));
return 0;
}
@@ -286,10 +286,10 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, dci);
dma = dev->out_ctx->dma + dci * CTX_SIZE(xhci->hcc_params);
seq_printf(s, "%pad: %s\n", &dma,
- xhci_decode_ep_context(ep_ctx->ep_info,
- ep_ctx->ep_info2,
- ep_ctx->deq,
- ep_ctx->tx_info));
+ xhci_decode_ep_context(le32_to_cpu(ep_ctx->ep_info),
+ le32_to_cpu(ep_ctx->ep_info2),
+ le64_to_cpu(ep_ctx->deq),
+ le32_to_cpu(ep_ctx->tx_info)));
}
return 0;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 1b7ada8..e4c3772 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -831,7 +831,7 @@ static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_bus_state *bus_state,
u16 wIndex, u32 raw_port_status,
- unsigned long flags)
+ unsigned long *flags)
__releases(&xhci->lock)
__acquires(&xhci->lock)
{
@@ -868,6 +868,14 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
status |= USB_PORT_STAT_C_BH_RESET << 16;
if ((raw_port_status & PORT_CEC))
status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
+
+ /* USB3 remote wake resume signaling completed */
+ if (bus_state->port_remote_wakeup & (1 << wIndex) &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_RECOVERY) {
+ bus_state->port_remote_wakeup &= ~(1 << wIndex);
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
+ }
}
if (hcd->speed < HCD_USB3) {
@@ -917,12 +925,12 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
xhci_set_link_state(xhci, port, XDEV_U0);
- spin_unlock_irqrestore(&xhci->lock, flags);
+ spin_unlock_irqrestore(&xhci->lock, *flags);
time_left = wait_for_completion_timeout(
&bus_state->rexit_done[wIndex],
msecs_to_jiffies(
XHCI_MAX_REXIT_TIMEOUT_MS));
- spin_lock_irqsave(&xhci->lock, flags);
+ spin_lock_irqsave(&xhci->lock, *flags);
if (time_left) {
slot_id = xhci_find_slot_id_by_port(hcd,
@@ -1221,7 +1229,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
trace_xhci_get_port_status(wIndex, temp);
status = xhci_get_port_status(hcd, bus_state, wIndex, temp,
- flags);
+ &flags);
if (status == 0xffffffff)
goto error;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 9b8d910..ecf73f6 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2035,13 +2035,17 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->usb3_rhub.num_ports = 0;
xhci->num_active_eps = 0;
kfree(xhci->usb2_rhub.ports);
+ kfree(xhci->usb2_rhub.psi);
kfree(xhci->usb3_rhub.ports);
+ kfree(xhci->usb3_rhub.psi);
kfree(xhci->hw_ports);
kfree(xhci->rh_bw);
kfree(xhci->ext_caps);
xhci->usb2_rhub.ports = NULL;
+ xhci->usb2_rhub.psi = NULL;
xhci->usb3_rhub.ports = NULL;
+ xhci->usb3_rhub.psi = NULL;
xhci->hw_ports = NULL;
xhci->rh_bw = NULL;
xhci->ext_caps = NULL;
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index fa33d6e..d04fdd1 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -113,7 +113,9 @@ static void setup_sch_info(struct usb_device *udev,
}
if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) {
- if (esit_pkts <= sch_ep->esit)
+ if (sch_ep->esit == 1)
+ sch_ep->pkts = esit_pkts;
+ else if (esit_pkts <= sch_ep->esit)
sch_ep->pkts = 1;
else
sch_ep->pkts = roundup_pow_of_two(esit_pkts)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 1493d0f..075c49c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -493,6 +493,18 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
retval = xhci_resume(xhci, hibernated);
return retval;
}
+
+static void xhci_pci_shutdown(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ xhci_shutdown(hcd);
+
+ /* Yet another workaround for spurious wakeups at shutdown with HSW */
+ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+ pci_set_power_state(pdev, PCI_D3hot);
+}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
@@ -530,6 +542,7 @@ static int __init xhci_pci_init(void)
#ifdef CONFIG_PM
xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend;
xhci_pci_hc_driver.pci_resume = xhci_pci_resume;
+ xhci_pci_hc_driver.shutdown = xhci_pci_shutdown;
#endif
return pci_register_driver(&xhci_pci_driver);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index edef3e2..eff69c2 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1609,7 +1609,6 @@ static void handle_port_status(struct xhci_hcd *xhci,
slot_id = xhci_find_slot_id_by_port(hcd, xhci, hcd_portnum + 1);
if (slot_id && xhci->devs[slot_id])
xhci->devs[slot_id]->flags |= VDEV_PORT_ERROR;
- bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
}
if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
@@ -1630,6 +1629,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
bus_state->port_remote_wakeup |= 1 << hcd_portnum;
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
xhci_set_link_state(xhci, port, XDEV_U0);
+ usb_hcd_start_port_resume(&hcd->self, hcd_portnum);
/* Need to wait until the next link state change
* indicates the device is actually in U0.
*/
@@ -1669,7 +1669,6 @@ static void handle_port_status(struct xhci_hcd *xhci,
if (slot_id && xhci->devs[slot_id])
xhci_ring_device(xhci, slot_id);
if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) {
- bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
usb_wakeup_notification(hcd->self.root_hub,
hcd_portnum + 1);
@@ -2330,7 +2329,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
case COMP_SUCCESS:
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
break;
- if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
+ if (xhci->quirks & XHCI_TRUST_TX_LENGTH ||
+ ep_ring->last_td_was_short)
trb_comp_code = COMP_SHORT_PACKET;
else
xhci_warn_ratelimited(xhci,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8b73bfe..c65237b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -798,7 +798,7 @@ static void xhci_stop(struct usb_hcd *hcd)
*
* This will only ever be called with the main usb_hcd (the USB3 roothub).
*/
-static void xhci_shutdown(struct usb_hcd *hcd)
+void xhci_shutdown(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -817,11 +817,8 @@ static void xhci_shutdown(struct usb_hcd *hcd)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"xhci_shutdown completed - status = %x",
readl(&xhci->op_regs->status));
-
- /* Yet another workaround for spurious wakeups at shutdown with HSW */
- if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
- pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot);
}
+EXPORT_SYMBOL_GPL(xhci_shutdown);
#ifdef CONFIG_PM
static void xhci_save_registers(struct xhci_hcd *xhci)
@@ -992,7 +989,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
{
int rc = 0;
- unsigned int delay = XHCI_MAX_HALT_USEC;
+ unsigned int delay = XHCI_MAX_HALT_USEC * 2;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
u32 res;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 1d8c40f..076fdd5 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2065,6 +2065,7 @@ int xhci_start(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
int xhci_run(struct usb_hcd *hcd);
int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
+void xhci_shutdown(struct usb_hcd *hcd);
void xhci_init_driver(struct hc_driver *drv,
const struct xhci_driver_overrides *over);
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 9a51760..b8073f3 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -671,7 +671,7 @@ static int adu_probe(struct usb_interface *interface,
init_waitqueue_head(&dev->read_wait);
init_waitqueue_head(&dev->write_wait);
- res = usb_find_common_endpoints_reverse(&interface->altsetting[0],
+ res = usb_find_common_endpoints_reverse(interface->cur_altsetting,
NULL, NULL,
&dev->interrupt_in_endpoint,
&dev->interrupt_out_endpoint);
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index 1c6da8d..718d692 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -148,8 +148,11 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
pdata->msgdata, 2,
ACD_USB_TIMEOUT);
mutex_unlock(&pdata->sysfslock);
-
- return retval;
+
+ if (retval < 0)
+ return retval;
+ else
+ return 0;
}
static int appledisplay_bl_get_brightness(struct backlight_device *bd)
@@ -167,7 +170,12 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd)
0,
pdata->msgdata, 2,
ACD_USB_TIMEOUT);
- brightness = pdata->msgdata[1];
+ if (retval < 2) {
+ if (retval >= 0)
+ retval = -EMSGSIZE;
+ } else {
+ brightness = pdata->msgdata[1];
+ }
mutex_unlock(&pdata->sysfslock);
if (retval < 0)
@@ -302,6 +310,7 @@ static int appledisplay_probe(struct usb_interface *iface,
if (pdata) {
if (pdata->urb) {
usb_kill_urb(pdata->urb);
+ cancel_delayed_work_sync(&pdata->work);
if (pdata->urbdata)
usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
pdata->urbdata, pdata->urb->transfer_dma);
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 34e6cd6..87067c3 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -384,13 +384,17 @@ static int _chaoskey_fill(struct chaoskey *dev)
!dev->reading,
(started ? NAK_TIMEOUT : ALEA_FIRST_TIMEOUT) );
- if (result < 0)
+ if (result < 0) {
+ usb_kill_urb(dev->urb);
goto out;
+ }
- if (result == 0)
+ if (result == 0) {
result = -ETIMEDOUT;
- else
+ usb_kill_urb(dev->urb);
+ } else {
result = dev->valid;
+ }
out:
/* Let the device go back to sleep eventually */
usb_autopm_put_interface(dev->interface);
@@ -526,7 +530,21 @@ static int chaoskey_suspend(struct usb_interface *interface,
static int chaoskey_resume(struct usb_interface *interface)
{
+ struct chaoskey *dev;
+ struct usb_device *udev = interface_to_usbdev(interface);
+
usb_dbg(interface, "resume");
+ dev = usb_get_intfdata(interface);
+
+ /*
+ * We may have lost power.
+ * In that case the device that needs a long time
+ * for the first requests needs an extended timeout
+ * again
+ */
+ if (le16_to_cpu(udev->descriptor.idVendor) == ALEA_VENDOR_ID)
+ dev->reads_started = false;
+
return 0;
}
#else
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 20b0f91..bb24527 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -337,7 +337,7 @@ static int idmouse_probe(struct usb_interface *interface,
int result;
/* check if we have gotten the data or the hid interface */
- iface_desc = &interface->altsetting[0];
+ iface_desc = interface->cur_altsetting;
if (iface_desc->desc.bInterfaceClass != 0x0A)
return -ENODEV;
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 6b3a6fd..67c1b8f 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -487,7 +487,7 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
}
bytes_to_read = min(count, *actual_buffer);
if (bytes_to_read < *actual_buffer)
- dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n",
+ dev_warn(&dev->intf->dev, "Read buffer overflow, %zu bytes dropped\n",
*actual_buffer-bytes_to_read);
/* copy one interrupt_in_buffer from ring_buffer into userspace */
@@ -495,11 +495,11 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
retval = -EFAULT;
goto unlock_exit;
}
- dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;
-
retval = bytes_to_read;
spin_lock_irq(&dev->rbsl);
+ dev->ring_tail = (dev->ring_tail + 1) % ring_buffer_size;
+
if (dev->buffer_overflow) {
dev->buffer_overflow = 0;
spin_unlock_irq(&dev->rbsl);
@@ -562,8 +562,9 @@ static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
/* write the data into interrupt_out_buffer from userspace */
bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);
if (bytes_to_write < count)
- dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n", count-bytes_to_write);
- dev_dbg(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n",
+ dev_warn(&dev->intf->dev, "Write buffer overflow, %zu bytes dropped\n",
+ count - bytes_to_write);
+ dev_dbg(&dev->intf->dev, "%s: count = %zu, bytes_to_write = %zu\n",
__func__, count, bytes_to_write);
if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
@@ -580,7 +581,7 @@ static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
1 << 8, 0,
dev->interrupt_out_buffer,
bytes_to_write,
- USB_CTRL_SET_TIMEOUT * HZ);
+ USB_CTRL_SET_TIMEOUT);
if (retval < 0)
dev_err(&dev->intf->dev,
"Couldn't submit HID_REQ_SET_REPORT %d\n",
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 62dab24..23061f15 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -878,7 +878,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
get_version_reply,
sizeof(*get_version_reply),
1000);
- if (result < sizeof(*get_version_reply)) {
+ if (result != sizeof(*get_version_reply)) {
if (result >= 0)
result = -EIO;
dev_err(idev, "get version request failed: %d\n", result);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index ac2b4fc..f48a23a 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1039,12 +1039,18 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg
mutex_lock(&rp->fetch_lock);
spin_lock_irqsave(&rp->b_lock, flags);
- mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
- kfree(rp->b_vec);
- rp->b_vec = vec;
- rp->b_size = size;
- rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0;
- rp->cnt_lost = 0;
+ if (rp->mmap_active) {
+ mon_free_buff(vec, size/CHUNK_SIZE);
+ kfree(vec);
+ ret = -EBUSY;
+ } else {
+ mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
+ kfree(rp->b_vec);
+ rp->b_vec = vec;
+ rp->b_size = size;
+ rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0;
+ rp->cnt_lost = 0;
+ }
spin_unlock_irqrestore(&rp->b_lock, flags);
mutex_unlock(&rp->fetch_lock);
}
@@ -1216,13 +1222,21 @@ mon_bin_poll(struct file *file, struct poll_table_struct *wait)
static void mon_bin_vma_open(struct vm_area_struct *vma)
{
struct mon_reader_bin *rp = vma->vm_private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rp->b_lock, flags);
rp->mmap_active++;
+ spin_unlock_irqrestore(&rp->b_lock, flags);
}
static void mon_bin_vma_close(struct vm_area_struct *vma)
{
+ unsigned long flags;
+
struct mon_reader_bin *rp = vma->vm_private_data;
+ spin_lock_irqsave(&rp->b_lock, flags);
rp->mmap_active--;
+ spin_unlock_irqrestore(&rp->b_lock, flags);
}
/*
@@ -1234,16 +1248,12 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf)
unsigned long offset, chunk_idx;
struct page *pageptr;
- mutex_lock(&rp->fetch_lock);
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= rp->b_size) {
- mutex_unlock(&rp->fetch_lock);
+ if (offset >= rp->b_size)
return VM_FAULT_SIGBUS;
- }
chunk_idx = offset / CHUNK_SIZE;
pageptr = rp->b_vec[chunk_idx].pg;
get_page(pageptr);
- mutex_unlock(&rp->fetch_lock);
vmf->page = pageptr;
return 0;
}
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index 48d10a6..8606935 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -185,8 +185,8 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
if (mtu->is_u3_ip) {
/* Enable U3 LTSSM interrupts */
- value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
- VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
+ value = HOT_RST_INTR | WARM_RST_INTR |
+ ENTER_U3_INTR | EXIT_U3_INTR;
mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
}
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index 5c60a8c..bbcd333 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -585,6 +585,17 @@ static const struct usb_gadget_ops mtu3_gadget_ops = {
.udc_stop = mtu3_gadget_stop,
};
+static void mtu3_state_reset(struct mtu3 *mtu)
+{
+ mtu->address = 0;
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+ mtu->may_wakeup = 0;
+ mtu->u1_enable = 0;
+ mtu->u2_enable = 0;
+ mtu->delayed_status = false;
+ mtu->test_mode = false;
+}
+
static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
u32 epnum, u32 is_in)
{
@@ -702,6 +713,7 @@ void mtu3_gadget_disconnect(struct mtu3 *mtu)
spin_lock(&mtu->lock);
}
+ mtu3_state_reset(mtu);
usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
}
@@ -712,12 +724,6 @@ void mtu3_gadget_reset(struct mtu3 *mtu)
/* report disconnect, if we didn't flush EP state */
if (mtu->g.speed != USB_SPEED_UNKNOWN)
mtu3_gadget_disconnect(mtu);
-
- mtu->address = 0;
- mtu->ep0_state = MU3D_EP0_STATE_SETUP;
- mtu->may_wakeup = 0;
- mtu->u1_enable = 0;
- mtu->u2_enable = 0;
- mtu->delayed_status = false;
- mtu->test_mode = false;
+ else
+ mtu3_state_reset(mtu);
}
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
index ff62ba2..326b407 100644
--- a/drivers/usb/mtu3/mtu3_qmu.c
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -427,7 +427,7 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
return;
}
- dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
+ dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req);
mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 4ffcee0..a86bac4 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/completion.h>
@@ -539,6 +539,7 @@ static inline void start_usb_host(struct usbpd *pd, bool ss)
{
enum plug_orientation cc = usbpd_get_plug_orientation(pd);
union extcon_property_value val;
+ int ret = 0;
val.intval = (cc == ORIENTATION_CC2);
extcon_set_property(pd->extcon, EXTCON_USB_HOST,
@@ -549,6 +550,13 @@ static inline void start_usb_host(struct usbpd *pd, bool ss)
EXTCON_PROP_USB_SS, val);
extcon_set_state_sync(pd->extcon, EXTCON_USB_HOST, 1);
+
+ /* blocks until USB host is completely started */
+ ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 1);
+ if (ret) {
+ usbpd_err(&pd->dev, "err(%d) starting host", ret);
+ return;
+ }
}
static inline void stop_usb_peripheral(struct usbpd *pd)
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index f24b20f..fe5504d 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -25,6 +25,6 @@
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
-obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o
+obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o phy-msm-qusb-v2.o
obj-$(CONFIG_USB_MSM_SSPHY_QMP) += phy-msm-ssusb-qmp.o
obj-$(CONFIG_MSM_HSUSB_PHY) += phy-msm-snps-hs.o
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
new file mode 100644
index 0000000..c5d4d82
--- /dev/null
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -0,0 +1,1181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/usb/phy.h>
+#include <linux/reset.h>
+#include <linux/debugfs.h>
+
+/* QUSB2PHY_PWR_CTRL1 register related bits */
+#define PWR_CTRL1_POWR_DOWN BIT(0)
+
+/* QUSB2PHY_PLL_COMMON_STATUS_ONE register related bits */
+#define CORE_READY_STATUS BIT(0)
+
+/* Get TUNE value from efuse bit-mask */
+#define TUNE_VAL_MASK(val, pos, mask) ((val >> pos) & mask)
+
+/* QUSB2PHY_INTR_CTRL register related bits */
+#define DMSE_INTR_HIGH_SEL BIT(4)
+#define DPSE_INTR_HIGH_SEL BIT(3)
+#define CHG_DET_INTR_EN BIT(2)
+#define DMSE_INTR_EN BIT(1)
+#define DPSE_INTR_EN BIT(0)
+
+/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register related bits */
+#define CORE_PLL_RATE BIT(0)
+#define CORE_PLL_RATE_MUX BIT(1)
+#define CORE_PLL_EN BIT(2)
+#define CORE_PLL_EN_MUX BIT(3)
+#define CORE_PLL_EN_FROM_RESET BIT(4)
+#define CORE_RESET BIT(5)
+#define CORE_RESET_MUX BIT(6)
+
+#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
+#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
+#define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */
+
+#define QUSB2PHY_3P3_VOL_MIN 3075000 /* uV */
+#define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */
+#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */
+
+#define LINESTATE_DP BIT(0)
+#define LINESTATE_DM BIT(1)
+
+#define BIAS_CTRL_2_OVERRIDE_VAL 0x28
+
+#define DEBUG_CTRL1_OVERRIDE_VAL 0x09
+
+/* PERIPH_SS_PHY_REFGEN_NORTH_BG_CTRL register bits */
+#define BANDGAP_BYPASS BIT(0)
+
+/* DEBUG_CTRL2 register value to program VSTATUS MUX for PHY status */
+#define DEBUG_CTRL2_MUX_PLL_LOCK_STATUS 0x4
+
+/* STAT5 register bits */
+#define VSTATUS_PLL_LOCK_STATUS_MASK BIT(0)
+
+enum qusb_phy_reg {
+ PORT_TUNE1,
+ PLL_COMMON_STATUS_ONE,
+ PWR_CTRL1,
+ INTR_CTRL,
+ PLL_CORE_INPUT_OVERRIDE,
+ TEST1,
+ BIAS_CTRL_2,
+ DEBUG_CTRL1,
+ DEBUG_CTRL2,
+ STAT5,
+ USB2_PHY_REG_MAX,
+};
+
+struct qusb_phy {
+ struct usb_phy phy;
+ struct mutex lock;
+ void __iomem *base;
+ void __iomem *efuse_reg;
+ void __iomem *refgen_north_bg_reg;
+
+ struct clk *ref_clk_src;
+ struct clk *ref_clk;
+ struct clk *cfg_ahb_clk;
+ struct reset_control *phy_reset;
+
+ struct regulator *vdd;
+ struct regulator *vdda33;
+ struct regulator *vdda18;
+ int vdd_levels[3]; /* none, low, high */
+ int init_seq_len;
+ int *qusb_phy_init_seq;
+ int host_init_seq_len;
+ int *qusb_phy_host_init_seq;
+
+ unsigned int *phy_reg;
+ int qusb_phy_reg_offset_cnt;
+
+ u32 tune_val;
+ int efuse_bit_pos;
+ int efuse_num_of_bits;
+
+ bool cable_connected;
+ bool suspended;
+ bool dpdm_enable;
+
+ struct regulator_desc dpdm_rdesc;
+ struct regulator_dev *dpdm_rdev;
+
+ /* emulation targets specific */
+ void __iomem *emu_phy_base;
+ bool emulation;
+ int *emu_init_seq;
+ int emu_init_seq_len;
+ int *phy_pll_reset_seq;
+ int phy_pll_reset_seq_len;
+ int *emu_dcm_reset_seq;
+ int emu_dcm_reset_seq_len;
+
+ /* override TUNEX registers value */
+ struct dentry *root;
+ u8 tune[5];
+ u8 bias_ctrl2;
+
+ bool override_bias_ctrl2;
+};
+
+static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
+{
+ dev_dbg(qphy->phy.dev, "%s(): on:%d\n", __func__, on);
+
+ if (on) {
+ clk_prepare_enable(qphy->ref_clk_src);
+ if (qphy->ref_clk)
+ clk_prepare_enable(qphy->ref_clk);
+
+ if (qphy->cfg_ahb_clk)
+ clk_prepare_enable(qphy->cfg_ahb_clk);
+
+ } else {
+ if (qphy->cfg_ahb_clk)
+ clk_disable_unprepare(qphy->cfg_ahb_clk);
+
+ if (qphy->ref_clk)
+ clk_disable_unprepare(qphy->ref_clk);
+
+ clk_disable_unprepare(qphy->ref_clk_src);
+ }
+}
+
+static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high)
+{
+ int min, ret;
+
+ min = high ? 1 : 0; /* low or none? */
+ ret = regulator_set_voltage(qphy->vdd, qphy->vdd_levels[min],
+ qphy->vdd_levels[2]);
+ if (ret) {
+ dev_err(qphy->phy.dev, "unable to set voltage for qusb vdd\n");
+ return ret;
+ }
+
+ dev_dbg(qphy->phy.dev, "min_vol:%d max_vol:%d\n",
+ qphy->vdd_levels[min], qphy->vdd_levels[2]);
+ return ret;
+}
+
+static int qusb_phy_disable_power(struct qusb_phy *qphy)
+{
+ int ret = 0;
+
+ mutex_lock(&qphy->lock);
+
+ dev_dbg(qphy->phy.dev, "%s:req to turn off regulators\n",
+ __func__);
+
+ ret = regulator_disable(qphy->vdda33);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable to disable vdda33:%d\n", ret);
+
+ if (!regulator_is_enabled(qphy->vdda33)) {
+ ret = regulator_set_voltage(qphy->vdda33, 0,
+ QUSB2PHY_3P3_VOL_MAX);
+ if (ret)
+ dev_err(qphy->phy.dev,
+ "Unable to set (0) voltage for vdda33:%d\n",
+ ret);
+
+ ret = regulator_set_load(qphy->vdda33, 0);
+ if (ret < 0)
+ dev_err(qphy->phy.dev,
+ "Unable to set (0) HPM of vdda33\n");
+
+ }
+
+ ret = regulator_disable(qphy->vdda18);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable to disable vdda18:%d\n", ret);
+
+ if (!regulator_is_enabled(qphy->vdda18)) {
+ ret = regulator_set_voltage(qphy->vdda18, 0,
+ QUSB2PHY_1P8_VOL_MAX);
+ if (ret)
+ dev_err(qphy->phy.dev,
+ "Unable to set (0) voltage for vdda18:%d\n", ret);
+
+ ret = regulator_set_load(qphy->vdda18, 0);
+ if (ret < 0)
+ dev_err(qphy->phy.dev,
+ "Unable to set LPM of vdda18\n");
+ }
+
+ ret = regulator_disable(qphy->vdd);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret);
+
+ if (!regulator_is_enabled(qphy->vdd)) {
+ ret = qusb_phy_config_vdd(qphy, false);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n",
+ ret);
+ }
+
+ pr_debug("%s(): QUSB PHY's regulators are turned OFF.\n", __func__);
+
+ mutex_unlock(&qphy->lock);
+
+ return ret;
+}
+
+static int qusb_phy_enable_power(struct qusb_phy *qphy)
+{
+ int ret = 0;
+
+ mutex_lock(&qphy->lock);
+
+ dev_dbg(qphy->phy.dev, "%s:req to turn on regulators\n",
+ __func__);
+
+ ret = qusb_phy_config_vdd(qphy, true);
+ if (ret) {
+ dev_err(qphy->phy.dev, "Unable to config VDD:%d\n",
+ ret);
+ goto err_vdd;
+ }
+
+ ret = regulator_enable(qphy->vdd);
+ if (ret) {
+ dev_err(qphy->phy.dev, "Unable to enable VDD\n");
+ goto unconfig_vdd;
+ }
+
+ ret = regulator_set_load(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD);
+ if (ret < 0) {
+ dev_err(qphy->phy.dev, "Unable to set HPM of vdda18:%d\n", ret);
+ goto disable_vdd;
+ }
+
+ ret = regulator_set_voltage(qphy->vdda18, QUSB2PHY_1P8_VOL_MIN,
+ QUSB2PHY_1P8_VOL_MAX);
+ if (ret) {
+ dev_err(qphy->phy.dev,
+ "Unable to set voltage for vdda18:%d\n", ret);
+ goto put_vdda18_lpm;
+ }
+
+ ret = regulator_enable(qphy->vdda18);
+ if (ret) {
+ dev_err(qphy->phy.dev, "Unable to enable vdda18:%d\n", ret);
+ goto unset_vdda18;
+ }
+
+ ret = regulator_set_load(qphy->vdda33, QUSB2PHY_3P3_HPM_LOAD);
+ if (ret < 0) {
+ dev_err(qphy->phy.dev, "Unable to set HPM of vdda33:%d\n", ret);
+ goto disable_vdda18;
+ }
+
+ ret = regulator_set_voltage(qphy->vdda33, QUSB2PHY_3P3_VOL_MIN,
+ QUSB2PHY_3P3_VOL_MAX);
+ if (ret) {
+ dev_err(qphy->phy.dev,
+ "Unable to set voltage for vdda33:%d\n", ret);
+ goto put_vdda33_lpm;
+ }
+
+ ret = regulator_enable(qphy->vdda33);
+ if (ret) {
+ dev_err(qphy->phy.dev, "Unable to enable vdda33:%d\n", ret);
+ goto unset_vdd33;
+ }
+
+ pr_debug("%s(): QUSB PHY's regulators are turned ON.\n", __func__);
+
+ mutex_unlock(&qphy->lock);
+
+ return ret;
+
+unset_vdd33:
+ ret = regulator_set_voltage(qphy->vdda33, 0, QUSB2PHY_3P3_VOL_MAX);
+ if (ret)
+ dev_err(qphy->phy.dev,
+ "Unable to set (0) voltage for vdda33:%d\n", ret);
+
+put_vdda33_lpm:
+ ret = regulator_set_load(qphy->vdda33, 0);
+ if (ret < 0)
+ dev_err(qphy->phy.dev, "Unable to set (0) HPM of vdda33\n");
+
+disable_vdda18:
+ ret = regulator_disable(qphy->vdda18);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable to disable vdda18:%d\n", ret);
+
+unset_vdda18:
+ ret = regulator_set_voltage(qphy->vdda18, 0, QUSB2PHY_1P8_VOL_MAX);
+ if (ret)
+ dev_err(qphy->phy.dev,
+ "Unable to set (0) voltage for vdda18:%d\n", ret);
+
+put_vdda18_lpm:
+ ret = regulator_set_load(qphy->vdda18, 0);
+ if (ret < 0)
+ dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n");
+
+disable_vdd:
+ ret = regulator_disable(qphy->vdd);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n",
+ ret);
+
+unconfig_vdd:
+ ret = qusb_phy_config_vdd(qphy, false);
+ if (ret)
+ dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n",
+ ret);
+err_vdd:
+ mutex_unlock(&qphy->lock);
+
+ return ret;
+}
+
+static void qusb_phy_get_tune1_param(struct qusb_phy *qphy)
+{
+ u8 reg;
+ u32 bit_mask = 1;
+
+ pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__,
+ qphy->efuse_num_of_bits,
+ qphy->efuse_bit_pos);
+
+ /* get bit mask based on number of bits to use with efuse reg */
+ bit_mask = (bit_mask << qphy->efuse_num_of_bits) - 1;
+
+ /*
+ * if efuse reg is updated (i.e non-zero) then use it to program
+ * tune parameters
+ */
+ qphy->tune_val = readl_relaxed(qphy->efuse_reg);
+ pr_debug("%s(): bit_mask:%d efuse based tune1 value:%d\n",
+ __func__, bit_mask, qphy->tune_val);
+
+ qphy->tune_val = TUNE_VAL_MASK(qphy->tune_val,
+ qphy->efuse_bit_pos, bit_mask);
+ reg = readb_relaxed(qphy->base + qphy->phy_reg[PORT_TUNE1]);
+ if (qphy->tune_val) {
+ reg = reg & 0x0f;
+ reg |= (qphy->tune_val << 4);
+ }
+
+ qphy->tune_val = reg;
+}
+
+static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
+ unsigned long delay)
+{
+ int i;
+
+ pr_debug("Seq count:%d\n", cnt);
+ for (i = 0; i < cnt; i = i+2) {
+ pr_debug("write 0x%02x to 0x%02x\n", seq[i], seq[i+1]);
+ writel_relaxed(seq[i], base + seq[i+1]);
+ if (delay)
+ usleep_range(delay, (delay + 2000));
+ }
+}
+
+static void qusb_phy_reset(struct qusb_phy *qphy)
+{
+ int ret;
+
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(qphy->phy.dev, "%s: phy_reset assert failed\n",
+ __func__);
+ usleep_range(100, 150);
+
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(qphy->phy.dev, "%s: phy_reset deassert failed\n",
+ __func__);
+}
+
+static bool qusb_phy_pll_locked(struct qusb_phy *qphy)
+{
+ u32 val;
+
+ writel_relaxed(DEBUG_CTRL2_MUX_PLL_LOCK_STATUS,
+ qphy->base + qphy->phy_reg[DEBUG_CTRL2]);
+
+ val = readl_relaxed(qphy->base + qphy->phy_reg[STAT5]);
+
+ return (val & VSTATUS_PLL_LOCK_STATUS_MASK);
+}
+
+static void qusb_phy_host_init(struct usb_phy *phy)
+{
+ u8 reg;
+ int p_index;
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+
+ dev_dbg(phy->dev, "%s\n", __func__);
+
+ qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq,
+ qphy->host_init_seq_len, 0);
+
+ if (qphy->efuse_reg) {
+ if (!qphy->tune_val)
+ qusb_phy_get_tune1_param(qphy);
+ } else {
+ /* For non fused chips we need to write the TUNE1 param as
+ * specified in DT otherwise we will end up writing 0 to
+ * to TUNE1
+ */
+ qphy->tune_val = readb_relaxed(qphy->base +
+ qphy->phy_reg[PORT_TUNE1]);
+ }
+
+ writel_relaxed(qphy->tune_val | BIT(7),
+ qphy->base + qphy->phy_reg[PORT_TUNE1]);
+ pr_debug("%s(): Programming TUNE1 parameter as:%x\n",
+ __func__, readb_relaxed(qphy->base +
+ qphy->phy_reg[PORT_TUNE1]));
+ writel_relaxed(DEBUG_CTRL1_OVERRIDE_VAL,
+ qphy->base + qphy->phy_reg[DEBUG_CTRL1]);
+
+ /* if debugfs based tunex params are set, use that value. */
+ for (p_index = 0; p_index < 5; p_index++) {
+ if (qphy->tune[p_index])
+ writel_relaxed(qphy->tune[p_index],
+ qphy->base + qphy->phy_reg[PORT_TUNE1] +
+ (4 * p_index));
+ }
+
+ if (qphy->refgen_north_bg_reg && qphy->override_bias_ctrl2)
+ if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS)
+ writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
+ if (qphy->bias_ctrl2)
+ writel_relaxed(qphy->bias_ctrl2,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
+ /* Ensure above write is completed before turning ON ref clk */
+ wmb();
+
+ /* Require to get phy pll lock successfully */
+ usleep_range(150, 160);
+
+ reg = readb_relaxed(qphy->base + qphy->phy_reg[PLL_COMMON_STATUS_ONE]);
+ dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg);
+ if (!(reg & CORE_READY_STATUS))
+ dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg);
+}
+
+static int qusb_phy_init(struct usb_phy *phy)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+ int p_index;
+ u8 reg;
+
+ dev_dbg(phy->dev, "%s\n", __func__);
+
+ qusb_phy_reset(qphy);
+
+ if (qphy->qusb_phy_host_init_seq && qphy->phy.flags & PHY_HOST_MODE) {
+ qusb_phy_host_init(phy);
+ return 0;
+ }
+
+ if (qphy->emulation) {
+ if (qphy->emu_init_seq)
+ qusb_phy_write_seq(qphy->emu_phy_base + 0x8000,
+ qphy->emu_init_seq,
+ qphy->emu_init_seq_len, 10000);
+
+ if (qphy->qusb_phy_init_seq)
+ qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq,
+ qphy->init_seq_len, 0);
+
+ /* Wait for 5ms as per QUSB2 RUMI sequence */
+ usleep_range(5000, 7000);
+
+ if (qphy->phy_pll_reset_seq)
+ qusb_phy_write_seq(qphy->base, qphy->phy_pll_reset_seq,
+ qphy->phy_pll_reset_seq_len, 10000);
+
+ if (qphy->emu_dcm_reset_seq)
+ qusb_phy_write_seq(qphy->emu_phy_base,
+ qphy->emu_dcm_reset_seq,
+ qphy->emu_dcm_reset_seq_len, 10000);
+
+ return 0;
+ }
+
+ /* Disable the PHY */
+ writel_relaxed(readl_relaxed(qphy->base + qphy->phy_reg[PWR_CTRL1]) |
+ PWR_CTRL1_POWR_DOWN,
+ qphy->base + qphy->phy_reg[PWR_CTRL1]);
+
+ if (qphy->qusb_phy_init_seq)
+ qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq,
+ qphy->init_seq_len, 0);
+ if (qphy->efuse_reg) {
+ if (!qphy->tune_val)
+ qusb_phy_get_tune1_param(qphy);
+
+ pr_debug("%s(): Programming TUNE1 parameter as:%x\n", __func__,
+ qphy->tune_val);
+ writel_relaxed(qphy->tune_val,
+ qphy->base + qphy->phy_reg[PORT_TUNE1]);
+ }
+
+ /* if debugfs based tunex params are set, use that value. */
+ for (p_index = 0; p_index < 5; p_index++) {
+ if (qphy->tune[p_index])
+ writel_relaxed(qphy->tune[p_index],
+ qphy->base + qphy->phy_reg[PORT_TUNE1] +
+ (4 * p_index));
+ }
+
+ if (qphy->refgen_north_bg_reg && qphy->override_bias_ctrl2)
+ if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS)
+ writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
+ if (qphy->bias_ctrl2)
+ writel_relaxed(qphy->bias_ctrl2,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
+ /* ensure above writes are completed before re-enabling PHY */
+ wmb();
+
+ /* Enable the PHY */
+ writel_relaxed(readl_relaxed(qphy->base + qphy->phy_reg[PWR_CTRL1]) &
+ ~PWR_CTRL1_POWR_DOWN,
+ qphy->base + qphy->phy_reg[PWR_CTRL1]);
+
+ /* Ensure above write is completed before turning ON ref clk */
+ wmb();
+
+ /* Require to get phy pll lock successfully */
+ usleep_range(150, 160);
+
+ reg = readb_relaxed(qphy->base + qphy->phy_reg[PLL_COMMON_STATUS_ONE]);
+ dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg);
+ if (!(reg & CORE_READY_STATUS)) {
+ dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg);
+ WARN_ON(1);
+ }
+ return 0;
+}
+
+static void qusb_phy_shutdown(struct usb_phy *phy)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+
+ dev_dbg(phy->dev, "%s\n", __func__);
+
+ qusb_phy_disable_power(qphy);
+
+}
+
+static u32 qusb_phy_get_linestate(struct qusb_phy *qphy)
+{
+ u32 linestate = 0;
+
+ if (qphy->cable_connected) {
+ if (qphy->phy.flags & PHY_HSFS_MODE)
+ linestate |= LINESTATE_DP;
+ else if (qphy->phy.flags & PHY_LS_MODE)
+ linestate |= LINESTATE_DM;
+ }
+ return linestate;
+}
+
+/**
+ * Performs QUSB2 PHY suspend/resume functionality.
+ *
+ * @uphy - usb phy pointer.
+ * @suspend - to enable suspend or not. 1 - suspend, 0 - resume
+ *
+ */
+static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+ u32 linestate = 0, intr_mask = 0;
+
+ if (qphy->suspended == suspend) {
+ dev_dbg(phy->dev, "%s: USB PHY is already suspended\n",
+ __func__);
+ return 0;
+ }
+
+ if (suspend) {
+ /* Bus suspend case */
+ if (qphy->cable_connected) {
+ /* Disable all interrupts */
+ writel_relaxed(0x00,
+ qphy->base + qphy->phy_reg[INTR_CTRL]);
+
+ linestate = qusb_phy_get_linestate(qphy);
+ /*
+ * D+/D- interrupts are level-triggered, but we are
+ * only interested if the line state changes, so enable
+ * the high/low trigger based on current state. In
+ * other words, enable the triggers _opposite_ of what
+ * the current D+/D- levels are.
+ * e.g. if currently D+ high, D- low (HS 'J'/Suspend),
+ * configure the mask to trigger on D+ low OR D- high
+ */
+ intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
+ if (!(linestate & LINESTATE_DP)) /* D+ low */
+ intr_mask |= DPSE_INTR_HIGH_SEL;
+ if (!(linestate & LINESTATE_DM)) /* D- low */
+ intr_mask |= DMSE_INTR_HIGH_SEL;
+
+ writel_relaxed(intr_mask,
+ qphy->base + qphy->phy_reg[INTR_CTRL]);
+
+ if (linestate & (LINESTATE_DP | LINESTATE_DM)) {
+ /* enable phy auto-resume */
+ writel_relaxed(0x91,
+ qphy->base + qphy->phy_reg[TEST1]);
+ /* Delay recommended between TEST1 writes */
+ usleep_range(10, 20);
+ writel_relaxed(0x90,
+ qphy->base + qphy->phy_reg[TEST1]);
+ }
+
+ dev_dbg(phy->dev, "%s: intr_mask = %x\n",
+ __func__, intr_mask);
+
+ /* Makes sure that above write goes through */
+ wmb();
+ qusb_phy_enable_clocks(qphy, false);
+ } else { /* Cable disconnect case */
+ /* Disable all interrupts */
+ writel_relaxed(0x00,
+ qphy->base + qphy->phy_reg[INTR_CTRL]);
+ qusb_phy_reset(qphy);
+ qusb_phy_enable_clocks(qphy, false);
+ qusb_phy_disable_power(qphy);
+ }
+ qphy->suspended = true;
+ } else {
+ /* Bus resume case */
+ if (qphy->cable_connected) {
+ qusb_phy_enable_clocks(qphy, true);
+ /* Clear all interrupts on resume */
+ writel_relaxed(0x00,
+ qphy->base + qphy->phy_reg[INTR_CTRL]);
+
+ /* Reset PLL if needed */
+ if (!qusb_phy_pll_locked(qphy)) {
+ dev_dbg(phy->dev, "%s: reset PLL\n", __func__);
+ /* hold core PLL into reset */
+ writel_relaxed(CORE_PLL_EN_FROM_RESET |
+ CORE_RESET | CORE_RESET_MUX,
+ qphy->base +
+ qphy->phy_reg[PLL_CORE_INPUT_OVERRIDE]);
+
+ /* Wait for PLL to get reset */
+ usleep_range(10, 20);
+
+ /* bring core PLL out of reset */
+ writel_relaxed(CORE_PLL_EN_FROM_RESET,
+ qphy->base +
+ qphy->phy_reg[PLL_CORE_INPUT_OVERRIDE]);
+
+ /* Makes sure that above write goes through */
+ wmb();
+ }
+ } else { /* Cable connect case */
+ qusb_phy_enable_power(qphy);
+ qusb_phy_enable_clocks(qphy, true);
+ }
+ qphy->suspended = false;
+ }
+
+ return 0;
+}
+
+static int qusb_phy_notify_connect(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+
+ qphy->cable_connected = true;
+
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
+ return 0;
+}
+
+static int qusb_phy_notify_disconnect(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+
+ qphy->cable_connected = false;
+
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
+ return 0;
+}
+
+static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+ struct qusb_phy *qphy = rdev_get_drvdata(rdev);
+
+ dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n",
+ __func__, qphy->dpdm_enable);
+
+ if (!qphy->dpdm_enable) {
+ ret = qusb_phy_enable_power(qphy);
+ if (ret < 0) {
+ dev_dbg(qphy->phy.dev,
+ "dpdm regulator enable failed:%d\n", ret);
+ return ret;
+ }
+ qphy->dpdm_enable = true;
+ qusb_phy_reset(qphy);
+ }
+
+ return ret;
+}
+
+static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+ struct qusb_phy *qphy = rdev_get_drvdata(rdev);
+
+ dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n",
+ __func__, qphy->dpdm_enable);
+
+ if (qphy->dpdm_enable) {
+ ret = qusb_phy_disable_power(qphy);
+ if (ret < 0) {
+ dev_dbg(qphy->phy.dev,
+ "dpdm regulator disable failed:%d\n", ret);
+ return ret;
+ }
+ qphy->dpdm_enable = false;
+ }
+
+ return ret;
+}
+
+static int qusb_phy_dpdm_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct qusb_phy *qphy = rdev_get_drvdata(rdev);
+
+ dev_dbg(qphy->phy.dev, "%s qphy->dpdm_enable = %d\n", __func__,
+ qphy->dpdm_enable);
+ return qphy->dpdm_enable;
+}
+
+static struct regulator_ops qusb_phy_dpdm_regulator_ops = {
+ .enable = qusb_phy_dpdm_regulator_enable,
+ .disable = qusb_phy_dpdm_regulator_disable,
+ .is_enabled = qusb_phy_dpdm_regulator_is_enabled,
+};
+
+static int qusb_phy_regulator_init(struct qusb_phy *qphy)
+{
+ struct device *dev = qphy->phy.dev;
+ struct regulator_config cfg = {};
+ struct regulator_init_data *init_data;
+
+ init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
+ if (!init_data)
+ return -ENOMEM;
+
+ init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+ qphy->dpdm_rdesc.owner = THIS_MODULE;
+ qphy->dpdm_rdesc.type = REGULATOR_VOLTAGE;
+ qphy->dpdm_rdesc.ops = &qusb_phy_dpdm_regulator_ops;
+ qphy->dpdm_rdesc.name = kbasename(dev->of_node->full_name);
+
+ cfg.dev = dev;
+ cfg.init_data = init_data;
+ cfg.driver_data = qphy;
+ cfg.of_node = dev->of_node;
+
+ qphy->dpdm_rdev = devm_regulator_register(dev, &qphy->dpdm_rdesc, &cfg);
+
+ return PTR_ERR_OR_ZERO(qphy->dpdm_rdev);
+}
+
+static int qusb_phy_create_debugfs(struct qusb_phy *qphy)
+{
+ struct dentry *file;
+ int ret = 0, i;
+ char name[6];
+
+ qphy->root = debugfs_create_dir(dev_name(qphy->phy.dev), NULL);
+ if (IS_ERR_OR_NULL(qphy->root)) {
+ dev_err(qphy->phy.dev,
+ "can't create debugfs root for %s\n",
+ dev_name(qphy->phy.dev));
+ ret = -ENOMEM;
+ goto create_err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ snprintf(name, sizeof(name), "tune%d", (i + 1));
+ file = debugfs_create_x8(name, 0644, qphy->root,
+ &qphy->tune[i]);
+ if (IS_ERR_OR_NULL(file)) {
+ dev_err(qphy->phy.dev,
+ "can't create debugfs entry for %s\n", name);
+ debugfs_remove_recursive(qphy->root);
+ ret = -ENOMEM;
+ goto create_err;
+ }
+ }
+
+ file = debugfs_create_x8("bias_ctrl2", 0644, qphy->root,
+ &qphy->bias_ctrl2);
+ if (IS_ERR_OR_NULL(file)) {
+ dev_err(qphy->phy.dev,
+ "can't create debugfs entry for bias_ctrl2\n");
+ debugfs_remove_recursive(qphy->root);
+ ret = -ENOMEM;
+ goto create_err;
+ }
+
+create_err:
+ return ret;
+}
+
+static int qusb_phy_probe(struct platform_device *pdev)
+{
+ struct qusb_phy *qphy;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0, size = 0;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->phy.dev = dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "qusb_phy_base");
+ qphy->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qphy->base))
+ return PTR_ERR(qphy->base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "emu_phy_base");
+ if (res) {
+ qphy->emu_phy_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qphy->emu_phy_base)) {
+ dev_dbg(dev, "couldn't ioremap emu_phy_base\n");
+ qphy->emu_phy_base = NULL;
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "efuse_addr");
+ if (res) {
+ qphy->efuse_reg = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (!IS_ERR_OR_NULL(qphy->efuse_reg)) {
+ ret = of_property_read_u32(dev->of_node,
+ "qcom,efuse-bit-pos",
+ &qphy->efuse_bit_pos);
+ if (!ret) {
+ ret = of_property_read_u32(dev->of_node,
+ "qcom,efuse-num-bits",
+ &qphy->efuse_num_of_bits);
+ }
+
+ if (ret) {
+ dev_err(dev,
+ "DT Value for efuse is invalid.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "refgen_north_bg_reg_addr");
+ if (res)
+ qphy->refgen_north_bg_reg = devm_ioremap(dev, res->start,
+ resource_size(res));
+
+ /* ref_clk_src is needed irrespective of SE_CLK or DIFF_CLK usage */
+ qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+ if (IS_ERR(qphy->ref_clk_src)) {
+ dev_dbg(dev, "clk get failed for ref_clk_src\n");
+ ret = PTR_ERR(qphy->ref_clk_src);
+ return ret;
+ }
+
+ /* ref_clk is needed only for DIFF_CLK case, hence make it optional. */
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "ref_clk") >= 0) {
+ qphy->ref_clk = devm_clk_get(dev, "ref_clk");
+ if (IS_ERR(qphy->ref_clk)) {
+ ret = PTR_ERR(qphy->ref_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_dbg(dev,
+ "clk get failed for ref_clk\n");
+ return ret;
+ }
+
+ clk_set_rate(qphy->ref_clk, 19200000);
+ }
+
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "cfg_ahb_clk") >= 0) {
+ qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
+ if (IS_ERR(qphy->cfg_ahb_clk)) {
+ ret = PTR_ERR(qphy->cfg_ahb_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "clk get failed for cfg_ahb_clk ret %d\n", ret);
+ return ret;
+ }
+ }
+
+ qphy->phy_reset = devm_reset_control_get(dev, "phy_reset");
+ if (IS_ERR(qphy->phy_reset))
+ return PTR_ERR(qphy->phy_reset);
+
+ qphy->emulation = of_property_read_bool(dev->of_node,
+ "qcom,emulation");
+
+ of_get_property(dev->of_node, "qcom,emu-init-seq", &size);
+ if (size) {
+ qphy->emu_init_seq = devm_kzalloc(dev,
+ size, GFP_KERNEL);
+ if (qphy->emu_init_seq) {
+ qphy->emu_init_seq_len =
+ (size / sizeof(*qphy->emu_init_seq));
+ if (qphy->emu_init_seq_len % 2) {
+ dev_err(dev, "invalid emu_init_seq_len\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(dev->of_node,
+ "qcom,emu-init-seq",
+ qphy->emu_init_seq,
+ qphy->emu_init_seq_len);
+ } else {
+ dev_dbg(dev,
+ "error allocating memory for emu_init_seq\n");
+ }
+ }
+
+ size = 0;
+ of_get_property(dev->of_node, "qcom,phy-pll-reset-seq", &size);
+ if (size) {
+ qphy->phy_pll_reset_seq = devm_kzalloc(dev,
+ size, GFP_KERNEL);
+ if (qphy->phy_pll_reset_seq) {
+ qphy->phy_pll_reset_seq_len =
+ (size / sizeof(*qphy->phy_pll_reset_seq));
+ if (qphy->phy_pll_reset_seq_len % 2) {
+ dev_err(dev, "invalid phy_pll_reset_seq_len\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(dev->of_node,
+ "qcom,phy-pll-reset-seq",
+ qphy->phy_pll_reset_seq,
+ qphy->phy_pll_reset_seq_len);
+ } else {
+ dev_dbg(dev,
+ "error allocating memory for phy_pll_reset_seq\n");
+ }
+ }
+
+ size = 0;
+ of_get_property(dev->of_node, "qcom,emu-dcm-reset-seq", &size);
+ if (size) {
+ qphy->emu_dcm_reset_seq = devm_kzalloc(dev,
+ size, GFP_KERNEL);
+ if (qphy->emu_dcm_reset_seq) {
+ qphy->emu_dcm_reset_seq_len =
+ (size / sizeof(*qphy->emu_dcm_reset_seq));
+ if (qphy->emu_dcm_reset_seq_len % 2) {
+ dev_err(dev, "invalid emu_dcm_reset_seq_len\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(dev->of_node,
+ "qcom,emu-dcm-reset-seq",
+ qphy->emu_dcm_reset_seq,
+ qphy->emu_dcm_reset_seq_len);
+ } else {
+ dev_dbg(dev,
+ "error allocating memory for emu_dcm_reset_seq\n");
+ }
+ }
+
+ size = 0;
+ of_get_property(dev->of_node, "qcom,qusb-phy-reg-offset", &size);
+ if (size) {
+ qphy->phy_reg = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (qphy->phy_reg) {
+ qphy->qusb_phy_reg_offset_cnt =
+ size / sizeof(*qphy->phy_reg);
+ if (qphy->qusb_phy_reg_offset_cnt != USB2_PHY_REG_MAX) {
+ dev_err(dev, "invalid reg offset count\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(dev->of_node,
+ "qcom,qusb-phy-reg-offset",
+ qphy->phy_reg,
+ qphy->qusb_phy_reg_offset_cnt);
+ } else {
+ dev_err(dev, "err mem alloc for qusb_phy_reg_offset\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(dev, "err provide qcom,qmp-phy-reg-offset\n");
+ return -EINVAL;
+ }
+
+ size = 0;
+ of_get_property(dev->of_node, "qcom,qusb-phy-init-seq", &size);
+ if (size) {
+ qphy->qusb_phy_init_seq = devm_kzalloc(dev,
+ size, GFP_KERNEL);
+ if (qphy->qusb_phy_init_seq) {
+ qphy->init_seq_len =
+ (size / sizeof(*qphy->qusb_phy_init_seq));
+ if (qphy->init_seq_len % 2) {
+ dev_err(dev, "invalid init_seq_len\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(dev->of_node,
+ "qcom,qusb-phy-init-seq",
+ qphy->qusb_phy_init_seq,
+ qphy->init_seq_len);
+ } else {
+ dev_err(dev,
+ "error allocating memory for phy_init_seq\n");
+ }
+ }
+
+ qphy->host_init_seq_len = of_property_count_elems_of_size(dev->of_node,
+ "qcom,qusb-phy-host-init-seq",
+ sizeof(*qphy->qusb_phy_host_init_seq));
+ if (qphy->host_init_seq_len > 0) {
+ qphy->qusb_phy_host_init_seq = devm_kcalloc(dev,
+ qphy->host_init_seq_len,
+ sizeof(*qphy->qusb_phy_host_init_seq),
+ GFP_KERNEL);
+ if (qphy->qusb_phy_host_init_seq)
+ of_property_read_u32_array(dev->of_node,
+ "qcom,qusb-phy-host-init-seq",
+ qphy->qusb_phy_host_init_seq,
+ qphy->host_init_seq_len);
+ else
+ return -ENOMEM;
+ }
+
+ qphy->override_bias_ctrl2 = of_property_read_bool(dev->of_node,
+ "qcom,override-bias-ctrl2");
+
+ ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
+ (u32 *) qphy->vdd_levels,
+ ARRAY_SIZE(qphy->vdd_levels));
+ if (ret) {
+ dev_err(dev, "error reading qcom,vdd-voltage-level property\n");
+ return ret;
+ }
+
+ qphy->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(qphy->vdd)) {
+ dev_err(dev, "unable to get vdd supply\n");
+ return PTR_ERR(qphy->vdd);
+ }
+
+ qphy->vdda33 = devm_regulator_get(dev, "vdda33");
+ if (IS_ERR(qphy->vdda33)) {
+ dev_err(dev, "unable to get vdda33 supply\n");
+ return PTR_ERR(qphy->vdda33);
+ }
+
+ qphy->vdda18 = devm_regulator_get(dev, "vdda18");
+ if (IS_ERR(qphy->vdda18)) {
+ dev_err(dev, "unable to get vdda18 supply\n");
+ return PTR_ERR(qphy->vdda18);
+ }
+
+ mutex_init(&qphy->lock);
+ platform_set_drvdata(pdev, qphy);
+
+ qphy->phy.label = "msm-qusb-phy-v2";
+ qphy->phy.init = qusb_phy_init;
+ qphy->phy.set_suspend = qusb_phy_set_suspend;
+ qphy->phy.shutdown = qusb_phy_shutdown;
+ qphy->phy.type = USB_PHY_TYPE_USB2;
+ qphy->phy.notify_connect = qusb_phy_notify_connect;
+ qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
+
+ ret = usb_add_phy_dev(&qphy->phy);
+ if (ret)
+ return ret;
+
+ ret = qusb_phy_regulator_init(qphy);
+ if (ret)
+ usb_remove_phy(&qphy->phy);
+
+ qphy->suspended = true;
+ qusb_phy_create_debugfs(qphy);
+
+ return ret;
+}
+
+static int qusb_phy_remove(struct platform_device *pdev)
+{
+ struct qusb_phy *qphy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&qphy->phy);
+ qphy->cable_connected = false;
+ qusb_phy_set_suspend(&qphy->phy, true);
+ debugfs_remove_recursive(qphy->root);
+
+ return 0;
+}
+
+static const struct of_device_id qusb_phy_id_table[] = {
+ { .compatible = "qcom,qusb2phy-v2", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qusb_phy_id_table);
+
+static struct platform_driver qusb_phy_driver = {
+ .probe = qusb_phy_probe,
+ .remove = qusb_phy_remove,
+ .driver = {
+ .name = "msm-qusb-phy-v2",
+ .of_match_table = of_match_ptr(qusb_phy_id_table),
+ },
+};
+
+module_platform_driver(qusb_phy_driver);
+
+MODULE_DESCRIPTION("MSM QUSB2 PHY v2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 0b488b1..821b710 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -20,6 +20,17 @@
#include <linux/usb/phy.h>
#include <linux/reset.h>
+#define QUSB2PHY_PLL_PWR_CTL 0x18
+#define REF_BUF_EN BIT(0)
+#define REXT_EN BIT(1)
+#define PLL_BYPASSNL BIT(2)
+#define REXT_TRIM_0 BIT(4)
+
+#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1C
+#define PLL_RESET_N_CNT_5 0x5
+#define PLL_RESET_N BIT(4)
+#define PLL_AUTOPGM_EN BIT(7)
+
#define QUSB2PHY_PLL_STATUS 0x38
#define QUSB2PHY_PLL_LOCK BIT(5)
@@ -48,6 +59,7 @@
#define CORE_READY_STATUS BIT(0)
#define QUSB2PHY_PORT_UTMI_CTRL1 0xC0
+#define SUSPEND_N BIT(5)
#define TERM_SELECT BIT(4)
#define XCVR_SELECT_FS BIT(2)
#define OP_MODE_NON_DRIVE BIT(0)
@@ -725,6 +737,86 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
return 0;
}
+static int qusb_phy_drive_dp_pulse(struct usb_phy *phy,
+ unsigned int pulse_width)
+{
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+ int ret;
+
+ dev_dbg(qphy->phy.dev, "connected to a CDP, drive DP up\n");
+ ret = qusb_phy_enable_power(qphy, true);
+ if (ret < 0) {
+ dev_dbg(qphy->phy.dev,
+ "dpdm regulator enable failed:%d\n", ret);
+ return ret;
+ }
+ qusb_phy_gdsc(qphy, true);
+ qusb_phy_enable_clocks(qphy, true);
+
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(qphy->phy.dev, "phyassert failed\n");
+ usleep_range(100, 150);
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(qphy->phy.dev, "deassert failed\n");
+
+ /* Configure PHY to enable control on DP/DM lines */
+ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN,
+ qphy->base + QUSB2PHY_PORT_POWERDOWN);
+
+ writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE |
+ SUSPEND_N, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);
+
+ writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL,
+ qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);
+
+ writel_relaxed(PLL_RESET_N_CNT_5,
+ qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);
+
+ writel_relaxed(CLAMP_N_EN | FREEZIO_N,
+ qphy->base + QUSB2PHY_PORT_POWERDOWN);
+
+ writel_relaxed(REF_BUF_EN | REXT_EN | PLL_BYPASSNL | REXT_TRIM_0,
+ qphy->base + QUSB2PHY_PLL_PWR_CTL);
+
+ usleep_range(5, 10);
+
+ writel_relaxed(0x15, qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);
+ writel_relaxed(PLL_RESET_N | PLL_RESET_N_CNT_5,
+ qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);
+
+ writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1);
+ writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2);
+
+ usleep_range(50, 60);
+ /* Enable Rdp_en to pull DP up to 3V */
+ writel_relaxed(RDP_UP_EN, qphy->base + QUSB2PHY_PORT_QC2);
+ msleep(pulse_width);
+
+ /* Put the PHY and DP back to normal state */
+ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN,
+ qphy->base + QUSB2PHY_PORT_POWERDOWN); /* 23 */
+
+ writel_relaxed(PLL_AUTOPGM_EN | PLL_RESET_N | PLL_RESET_N_CNT_5,
+ qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1);
+
+ writel_relaxed(UTMI_ULPI_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);
+
+ writel_relaxed(TERM_SELECT, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);
+
+ qusb_phy_enable_clocks(qphy, false);
+ qusb_phy_gdsc(qphy, false);
+
+ ret = qusb_phy_enable_power(qphy, false);
+ if (ret < 0) {
+ dev_dbg(qphy->phy.dev,
+ "dpdm regulator disable failed:%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
{
int ret = 0;
@@ -1129,6 +1221,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.type = USB_PHY_TYPE_USB2;
qphy->phy.notify_connect = qusb_phy_notify_connect;
qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
+ qphy->phy.drive_dp_pulse = qusb_phy_drive_dp_pulse;
/*
* On some platforms multiple QUSB PHYs are available. If QUSB PHY is
diff --git a/drivers/usb/phy/phy-msm-snps-hs.c b/drivers/usb/phy/phy-msm-snps-hs.c
index 974231c..818c5a0 100644
--- a/drivers/usb/phy/phy-msm-snps-hs.c
+++ b/drivers/usb/phy/phy-msm-snps-hs.c
@@ -615,6 +615,7 @@ static int msm_hsphy_dpdm_regulator_enable(struct regulator_dev *rdev)
UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN,
UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN);
+ msm_hsphy_enable_clocks(phy, false);
phy->dpdm_enable = true;
}
mutex_unlock(&phy->phy_lock);
@@ -633,7 +634,6 @@ static int msm_hsphy_dpdm_regulator_disable(struct regulator_dev *rdev)
mutex_lock(&phy->phy_lock);
if (phy->dpdm_enable) {
if (!phy->cable_connected) {
- msm_hsphy_enable_clocks(phy, false);
ret = msm_hsphy_enable_power(phy, false);
if (ret < 0) {
mutex_unlock(&phy->phy_lock);
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index c47b721..63a75fd 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -157,11 +157,12 @@ struct usbhs_priv;
#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */
#define VALID (1 << 3) /* USB Request Receive */
-#define DVSQ_MASK (0x3 << 4) /* Device State */
+#define DVSQ_MASK (0x7 << 4) /* Device State */
#define POWER_STATE (0 << 4)
#define DEFAULT_STATE (1 << 4)
#define ADDRESS_STATE (2 << 4)
#define CONFIGURATION_STATE (3 << 4)
+#define SUSPENDED_STATE (4 << 4)
#define CTSQ_MASK (0x7) /* Control Transfer Stage */
#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 7feac41..f36248e 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -456,12 +456,18 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
{
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ int state = usbhs_status_get_device_state(irq_state);
gpriv->gadget.speed = usbhs_bus_get_speed(priv);
- dev_dbg(dev, "state = %x : speed : %d\n",
- usbhs_status_get_device_state(irq_state),
- gpriv->gadget.speed);
+ dev_dbg(dev, "state = %x : speed : %d\n", state, gpriv->gadget.speed);
+
+ if (gpriv->gadget.speed != USB_SPEED_UNKNOWN &&
+ (state & SUSPENDED_STATE)) {
+ if (gpriv->driver && gpriv->driver->suspend)
+ gpriv->driver->suspend(&gpriv->gadget);
+ usb_gadget_set_state(&gpriv->gadget, USB_STATE_SUSPENDED);
+ }
return 0;
}
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 99116af..1dd492e 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -130,8 +130,8 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get);
void usb_role_switch_put(struct usb_role_switch *sw)
{
if (!IS_ERR_OR_NULL(sw)) {
- put_device(&sw->dev);
module_put(sw->dev.parent->driver->owner);
+ put_device(&sw->dev);
}
}
EXPORT_SYMBOL_GPL(usb_role_switch_put);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index e732949..7ae1215 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -125,6 +125,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
+ { USB_DEVICE(0x10C4, 0x83AA) }, /* Mark-10 Digital Force Gauge */
{ USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */
{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index e0035c0..2c58649 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -769,7 +769,7 @@ static void cypress_send(struct usb_serial_port *port)
usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
- port->interrupt_out_buffer, port->interrupt_out_size,
+ port->interrupt_out_buffer, actual_size,
cypress_write_int_callback, port, priv->write_urb_interval);
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
if (result) {
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 4dfbff2..db6c93c 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -45,14 +45,17 @@
#define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS)
#define F81534_DEF_CONF_ADDRESS_START 0x3000
-#define F81534_DEF_CONF_SIZE 8
+#define F81534_DEF_CONF_SIZE 12
#define F81534_CUSTOM_ADDRESS_START 0x2f00
#define F81534_CUSTOM_DATA_SIZE 0x10
#define F81534_CUSTOM_NO_CUSTOM_DATA 0xff
#define F81534_CUSTOM_VALID_TOKEN 0xf0
#define F81534_CONF_OFFSET 1
-#define F81534_CONF_GPIO_OFFSET 4
+#define F81534_CONF_INIT_GPIO_OFFSET 4
+#define F81534_CONF_WORK_GPIO_OFFSET 8
+#define F81534_CONF_GPIO_SHUTDOWN 7
+#define F81534_CONF_GPIO_RS232 1
#define F81534_MAX_DATA_BLOCK 64
#define F81534_MAX_BUS_RETRY 20
@@ -1359,8 +1362,19 @@ static int f81534_set_port_output_pin(struct usb_serial_port *port)
serial_priv = usb_get_serial_data(serial);
port_priv = usb_get_serial_port_data(port);
- idx = F81534_CONF_GPIO_OFFSET + port_priv->phy_num;
+ idx = F81534_CONF_INIT_GPIO_OFFSET + port_priv->phy_num;
value = serial_priv->conf_data[idx];
+ if (value >= F81534_CONF_GPIO_SHUTDOWN) {
+ /*
+ * Newer IC configure will make transceiver in shutdown mode on
+ * initial power on. We need enable it before using UARTs.
+ */
+ idx = F81534_CONF_WORK_GPIO_OFFSET + port_priv->phy_num;
+ value = serial_priv->conf_data[idx];
+ if (value >= F81534_CONF_GPIO_SHUTDOWN)
+ value = F81534_CONF_GPIO_RS232;
+ }
+
pins = &f81534_port_out_pins[port_priv->phy_num];
for (i = 0; i < ARRAY_SIZE(pins->pin); ++i) {
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index f06706e..3c0f38c 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1023,6 +1023,9 @@ static const struct usb_device_id id_table_combined[] = {
/* Sienna devices */
{ USB_DEVICE(FTDI_VID, FTDI_SIENNA_PID) },
{ USB_DEVICE(ECHELON_VID, ECHELON_U20_PID) },
+ /* U-Blox devices */
+ { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ZED_PID) },
+ { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 22d6621..e837352 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -1558,3 +1558,10 @@
*/
#define UNJO_VID 0x22B7
#define UNJO_ISODEBUG_V1_PID 0x150D
+
+/*
+ * U-Blox products (http://www.u-blox.com).
+ */
+#define UBLOX_VID 0x1546
+#define UBLOX_C099F9P_ZED_PID 0x0502
+#define UBLOX_C099F9P_ODIN_PID 0x0503
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 97c69d3..97eb738 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2919,16 +2919,18 @@ static int edge_startup(struct usb_serial *serial)
response = 0;
if (edge_serial->is_epic) {
+ struct usb_host_interface *alt;
+
+ alt = serial->interface->cur_altsetting;
+
/* EPIC thing, set up our interrupt polling now and our read
* urb, so that the device knows it really is connected. */
interrupt_in_found = bulk_in_found = bulk_out_found = false;
- for (i = 0; i < serial->interface->altsetting[0]
- .desc.bNumEndpoints; ++i) {
+ for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
struct usb_endpoint_descriptor *endpoint;
int buffer_size;
- endpoint = &serial->interface->altsetting[0].
- endpoint[i].desc;
+ endpoint = &alt->endpoint[i].desc;
buffer_size = usb_endpoint_maxp(endpoint);
if (!interrupt_in_found &&
(usb_endpoint_is_int_in(endpoint))) {
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index e8f275a..c0232b6 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -1894,10 +1894,6 @@ static int mos7720_startup(struct usb_serial *serial)
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
- /* setting configuration feature to one */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
-
if (product == MOSCHIP_DEVICE_ID_7715) {
struct urb *urb = serial->port[0]->interrupt_in_urb;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index b42bad8..4a7bd26 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -118,11 +118,15 @@
/* This driver also supports
* ATEN UC2324 device using Moschip MCS7840
* ATEN UC2322 device using Moschip MCS7820
+ * MOXA UPort 2210 device using Moschip MCS7820
*/
#define USB_VENDOR_ID_ATENINTL 0x0557
#define ATENINTL_DEVICE_ID_UC2324 0x2011
#define ATENINTL_DEVICE_ID_UC2322 0x7820
+#define USB_VENDOR_ID_MOXA 0x110a
+#define MOXA_DEVICE_ID_2210 0x2210
+
/* Interrupt Routine Defines */
#define SERIAL_IIR_RLS 0x06
@@ -193,6 +197,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)},
+ {USB_DEVICE(USB_VENDOR_ID_MOXA, MOXA_DEVICE_ID_2210)},
{} /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
@@ -2053,6 +2058,7 @@ static int mos7840_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+ u16 vid = le16_to_cpu(serial->dev->descriptor.idVendor);
u8 *buf;
int device_type;
@@ -2062,6 +2068,11 @@ static int mos7840_probe(struct usb_serial *serial,
goto out;
}
+ if (vid == USB_VENDOR_ID_MOXA && product == MOXA_DEVICE_ID_2210) {
+ device_type = MOSCHIP_DEVICE_ID_7820;
+ goto out;
+ }
+
buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -2314,11 +2325,6 @@ static int mos7840_port_probe(struct usb_serial_port *port)
goto error;
} else
dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status);
-
- /* setting configuration feature to one */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- 0x03, 0x00, 0x01, 0x00, NULL, 0x00,
- MOS_WDR_TIMEOUT);
}
return 0;
error:
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 3cc659a..553adab 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -197,6 +197,7 @@ static void option_instat_callback(struct urb *urb);
#define DELL_PRODUCT_5804_MINICARD_ATT 0x819b /* Novatel E371 */
#define DELL_PRODUCT_5821E 0x81d7
+#define DELL_PRODUCT_5821E_ESIM 0x81e0
#define KYOCERA_VENDOR_ID 0x0c88
#define KYOCERA_PRODUCT_KPC650 0x17da
@@ -1044,6 +1045,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5804_MINICARD_ATT, 0xff, 0xff, 0xff) },
{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E),
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
+ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E_ESIM),
+ .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },
@@ -1169,6 +1172,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1102, 0xff), /* Telit ME910 (ECM) */
.driver_info = NCTRL(0) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x110a, 0xff), /* Telit ME910G1 */
+ .driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
@@ -1990,6 +1995,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x13) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x14) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) },
+ { USB_DEVICE(0x0489, 0xe0b4), /* Foxconn T77W968 */
+ .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
+ { USB_DEVICE(0x0489, 0xe0b5), /* Foxconn T77W968 ESIM */
+ .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 1c7b46a..16b2751 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -571,6 +571,10 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command,
command_port = port->serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
+
+ if (command_port->bulk_out_size < datasize + 1)
+ return -EIO;
+
mutex_lock(&command_info->mutex);
command_info->command_finished = false;
@@ -644,6 +648,7 @@ static void firm_setup_port(struct tty_struct *tty)
struct device *dev = &port->dev;
struct whiteheat_port_settings port_settings;
unsigned int cflag = tty->termios.c_cflag;
+ speed_t baud;
port_settings.port = port->port_number + 1;
@@ -704,11 +709,13 @@ static void firm_setup_port(struct tty_struct *tty)
dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
/* get the baud rate wanted */
- port_settings.baud = tty_get_baud_rate(tty);
- dev_dbg(dev, "%s - baud rate = %d\n", __func__, port_settings.baud);
+ baud = tty_get_baud_rate(tty);
+ port_settings.baud = cpu_to_le32(baud);
+ dev_dbg(dev, "%s - baud rate = %u\n", __func__, baud);
/* fixme: should set validated settings */
- tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
+ tty_encode_baud_rate(tty, baud, baud);
+
/* handle any settings that aren't specified in the tty structure */
port_settings.lloop = 0;
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
index 72c1b0c..56a3e83 100644
--- a/drivers/usb/serial/whiteheat.h
+++ b/drivers/usb/serial/whiteheat.h
@@ -87,7 +87,7 @@ struct whiteheat_simple {
struct whiteheat_port_settings {
__u8 port; /* port number (1 to N) */
- __u32 baud; /* any value 7 - 460800, firmware calculates
+ __le32 baud; /* any value 7 - 460800, firmware calculates
best fit; arrives little endian */
__u8 bits; /* 5, 6, 7, or 8 */
__u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 59d82b4..f287ee8 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -65,7 +65,6 @@ static const char* host_info(struct Scsi_Host *host)
static int slave_alloc (struct scsi_device *sdev)
{
struct us_data *us = host_to_us(sdev->host);
- int maxp;
/*
* Set the INQUIRY transfer length to 36. We don't use any of
@@ -75,15 +74,6 @@ static int slave_alloc (struct scsi_device *sdev)
sdev->inquiry_len = 36;
/*
- * USB has unusual scatter-gather requirements: the length of each
- * scatterlist element except the last must be divisible by the
- * Bulk maxpacket value. Fortunately this value is always a
- * power of 2. Inform the block layer about this requirement.
- */
- maxp = usb_maxpacket(us->pusb_dev, us->recv_bulk_pipe, 0);
- blk_queue_virt_boundary(sdev->request_queue, maxp - 1);
-
- /*
* Some host controllers may have alignment requirements.
* We'll play it safe by requiring 512-byte alignment always.
*/
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 5b1d093..1c6eb3a 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -796,30 +796,10 @@ static int uas_slave_alloc(struct scsi_device *sdev)
{
struct uas_dev_info *devinfo =
(struct uas_dev_info *)sdev->host->hostdata;
- int maxp;
sdev->hostdata = devinfo;
/*
- * We have two requirements here. We must satisfy the requirements
- * of the physical HC and the demands of the protocol, as we
- * definitely want no additional memory allocation in this path
- * ruling out using bounce buffers.
- *
- * For a transmission on USB to continue we must never send
- * a package that is smaller than maxpacket. Hence the length of each
- * scatterlist element except the last must be divisible by the
- * Bulk maxpacket value.
- * If the HC does not ensure that through SG,
- * the upper layer must do that. We must assume nothing
- * about the capabilities off the HC, so we use the most
- * pessimistic requirement.
- */
-
- maxp = usb_maxpacket(devinfo->udev, devinfo->data_in_pipe, 0);
- blk_queue_virt_boundary(sdev->request_queue, maxp - 1);
-
- /*
* The protocol has no requirements on alignment in the strict sense.
* Controllers may or may not have alignment restrictions.
* As this is not exported, we use an extremely conservative guess.
@@ -852,6 +832,10 @@ static int uas_slave_configure(struct scsi_device *sdev)
sdev->wce_default_on = 1;
}
+ /* Some disks cannot handle READ_CAPACITY_16 */
+ if (devinfo->flags & US_FL_NO_READ_CAPACITY_16)
+ sdev->no_read_capacity_16 = 1;
+
/*
* Some disks return the total number of blocks in response
* to READ CAPACITY rather than the highest block number.
@@ -861,6 +845,12 @@ static int uas_slave_configure(struct scsi_device *sdev)
sdev->fix_capacity = 1;
/*
+ * in some cases we have to guess
+ */
+ if (devinfo->flags & US_FL_CAPACITY_HEURISTICS)
+ sdev->guess_capacity = 1;
+
+ /*
* Some devices don't like MODE SENSE with page=0x3f,
* which is the command used for checking if a device
* is write-protected. Now that we tell the sd driver
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 00141e0..1916ee1 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1589,14 +1589,16 @@ struct typec_port *typec_register_port(struct device *parent,
port->sw = typec_switch_get(&port->dev);
if (IS_ERR(port->sw)) {
+ ret = PTR_ERR(port->sw);
put_device(&port->dev);
- return ERR_CAST(port->sw);
+ return ERR_PTR(ret);
}
port->mux = typec_mux_get(&port->dev, "typec-mux");
if (IS_ERR(port->mux)) {
+ ret = PTR_ERR(port->mux);
put_device(&port->dev);
- return ERR_CAST(port->mux);
+ return ERR_PTR(ret);
}
ret = device_add(&port->dev);
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index 819ae3b..39cf190 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -3322,7 +3322,8 @@ static void run_state_machine(struct tcpm_port *port)
case SNK_HARD_RESET_SINK_OFF:
memset(&port->pps_data, 0, sizeof(port->pps_data));
tcpm_set_vconn(port, false);
- tcpm_set_charge(port, false);
+ if (port->pd_capable)
+ tcpm_set_charge(port, false);
tcpm_set_roles(port, port->self_powered, TYPEC_SINK,
TYPEC_DEVICE);
/*
@@ -3354,6 +3355,12 @@ static void run_state_machine(struct tcpm_port *port)
* Similar, dual-mode ports in source mode should transition
* to PE_SNK_Transition_to_default.
*/
+ if (port->pd_capable) {
+ tcpm_set_current_limit(port,
+ tcpm_get_current_limit(port),
+ 5000);
+ tcpm_set_charge(port, true);
+ }
tcpm_set_attached_state(port, true);
tcpm_set_state(port, SNK_STARTUP, 0);
break;
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index a20b65c..8276a20 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -2,6 +2,7 @@
tristate "USB/IP support"
depends on NET
select USB_COMMON
+ select SGL_ALLOC
---help---
This enables pushing USB packets over IP to allow remote
machines direct access to USB devices. It provides the
diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h
index 35618ceb..d112705 100644
--- a/drivers/usb/usbip/stub.h
+++ b/drivers/usb/usbip/stub.h
@@ -52,7 +52,11 @@ struct stub_priv {
unsigned long seqnum;
struct list_head list;
struct stub_device *sdev;
- struct urb *urb;
+ struct urb **urbs;
+ struct scatterlist *sgl;
+ int num_urbs;
+ int completed_urbs;
+ int urb_status;
int unlinking;
};
@@ -86,6 +90,7 @@ extern struct usb_device_driver stub_driver;
struct bus_id_priv *get_busid_priv(const char *busid);
void put_busid_priv(struct bus_id_priv *bid);
int del_match_busid(char *busid);
+void stub_free_priv_and_urb(struct stub_priv *priv);
void stub_device_cleanup_urbs(struct stub_device *sdev);
/* stub_rx.c */
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index bf8a5fe..a20bb2d 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -6,6 +6,7 @@
#include <linux/string.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
@@ -283,13 +284,49 @@ static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
struct stub_priv *priv, *tmp;
list_for_each_entry_safe(priv, tmp, listhead, list) {
- list_del(&priv->list);
+ list_del_init(&priv->list);
return priv;
}
return NULL;
}
+void stub_free_priv_and_urb(struct stub_priv *priv)
+{
+ struct urb *urb;
+ int i;
+
+ for (i = 0; i < priv->num_urbs; i++) {
+ urb = priv->urbs[i];
+
+ if (!urb)
+ return;
+
+ kfree(urb->setup_packet);
+ urb->setup_packet = NULL;
+
+
+ if (urb->transfer_buffer && !priv->sgl) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+
+ if (urb->num_sgs) {
+ sgl_free(urb->sg);
+ urb->sg = NULL;
+ urb->num_sgs = 0;
+ }
+
+ usb_free_urb(urb);
+ }
+ if (!list_empty(&priv->list))
+ list_del(&priv->list);
+ if (priv->sgl)
+ sgl_free(priv->sgl);
+ kfree(priv->urbs);
+ kmem_cache_free(stub_priv_cache, priv);
+}
+
static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
{
unsigned long flags;
@@ -316,25 +353,15 @@ static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
void stub_device_cleanup_urbs(struct stub_device *sdev)
{
struct stub_priv *priv;
- struct urb *urb;
+ int i;
dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
while ((priv = stub_priv_pop(sdev))) {
- urb = priv->urb;
- dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n",
- priv->seqnum);
- usb_kill_urb(urb);
+ for (i = 0; i < priv->num_urbs; i++)
+ usb_kill_urb(priv->urbs[i]);
- kmem_cache_free(stub_priv_cache, priv);
-
- kfree(urb->transfer_buffer);
- urb->transfer_buffer = NULL;
-
- kfree(urb->setup_packet);
- urb->setup_packet = NULL;
-
- usb_free_urb(urb);
+ stub_free_priv_and_urb(priv);
}
}
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index dbfb2f2..8c55cd8 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -7,6 +7,7 @@
#include <linux/kthread.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
@@ -201,7 +202,7 @@ static void tweak_special_requests(struct urb *urb)
static int stub_recv_cmd_unlink(struct stub_device *sdev,
struct usbip_header *pdu)
{
- int ret;
+ int ret, i;
unsigned long flags;
struct stub_priv *priv;
@@ -246,12 +247,14 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev,
* so a driver in a client host will know the failure
* of the unlink request ?
*/
- ret = usb_unlink_urb(priv->urb);
- if (ret != -EINPROGRESS)
- dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb # %lu, ret %d\n",
- priv->seqnum, ret);
-
+ for (i = priv->completed_urbs; i < priv->num_urbs; i++) {
+ ret = usb_unlink_urb(priv->urbs[i]);
+ if (ret != -EINPROGRESS)
+ dev_err(&priv->urbs[i]->dev->dev,
+ "failed to unlink %d/%d urb of seqnum %lu, ret %d\n",
+ i + 1, priv->num_urbs,
+ priv->seqnum, ret);
+ }
return 0;
}
@@ -433,92 +436,191 @@ static void masking_bogus_flags(struct urb *urb)
urb->transfer_flags &= allowed;
}
+static int stub_recv_xbuff(struct usbip_device *ud, struct stub_priv *priv)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < priv->num_urbs; i++) {
+ ret = usbip_recv_xbuff(ud, priv->urbs[i]);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
static void stub_recv_cmd_submit(struct stub_device *sdev,
struct usbip_header *pdu)
{
- int ret;
struct stub_priv *priv;
struct usbip_device *ud = &sdev->ud;
struct usb_device *udev = sdev->udev;
+ struct scatterlist *sgl = NULL, *sg;
+ void *buffer = NULL;
+ unsigned long long buf_len;
+ int nents;
+ int num_urbs = 1;
int pipe = get_pipe(sdev, pdu);
+ int use_sg = pdu->u.cmd_submit.transfer_flags & URB_DMA_MAP_SG;
+ int support_sg = 1;
+ int np = 0;
+ int ret, i;
if (pipe == -1)
return;
+ /*
+ * Smatch reported the error case where use_sg is true and buf_len is 0.
+ * In this case, It adds SDEV_EVENT_ERROR_MALLOC and stub_priv will be
+ * released by stub event handler and connection will be shut down.
+ */
priv = stub_priv_alloc(sdev, pdu);
if (!priv)
return;
- /* setup a urb */
- if (usb_pipeisoc(pipe))
- priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
- GFP_KERNEL);
- else
- priv->urb = usb_alloc_urb(0, GFP_KERNEL);
+ buf_len = (unsigned long long)pdu->u.cmd_submit.transfer_buffer_length;
- if (!priv->urb) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
+ if (use_sg && !buf_len) {
+ dev_err(&udev->dev, "sg buffer with zero length\n");
+ goto err_malloc;
}
/* allocate urb transfer buffer, if needed */
- if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
- priv->urb->transfer_buffer =
- kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
- GFP_KERNEL);
- if (!priv->urb->transfer_buffer) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
+ if (buf_len) {
+ if (use_sg) {
+ sgl = sgl_alloc(buf_len, GFP_KERNEL, &nents);
+ if (!sgl)
+ goto err_malloc;
+
+ /* Check if the server's HCD supports SG */
+ if (!udev->bus->sg_tablesize) {
+ /*
+ * If the server's HCD doesn't support SG, break
+ * a single SG request into several URBs and map
+ * each SG list entry to corresponding URB
+ * buffer. The previously allocated SG list is
+ * stored in priv->sgl (If the server's HCD
+ * support SG, SG list is stored only in
+ * urb->sg) and it is used as an indicator that
+ * the server split single SG request into
+ * several URBs. Later, priv->sgl is used by
+ * stub_complete() and stub_send_ret_submit() to
+ * reassemble the divied URBs.
+ */
+ support_sg = 0;
+ num_urbs = nents;
+ priv->completed_urbs = 0;
+ pdu->u.cmd_submit.transfer_flags &=
+ ~URB_DMA_MAP_SG;
+ }
+ } else {
+ buffer = kzalloc(buf_len, GFP_KERNEL);
+ if (!buffer)
+ goto err_malloc;
}
}
- /* copy urb setup packet */
- priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
- GFP_KERNEL);
- if (!priv->urb->setup_packet) {
- dev_err(&udev->dev, "allocate setup_packet\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
+ /* allocate urb array */
+ priv->num_urbs = num_urbs;
+ priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL);
+ if (!priv->urbs)
+ goto err_urbs;
+
+ /* setup a urb */
+ if (support_sg) {
+ if (usb_pipeisoc(pipe))
+ np = pdu->u.cmd_submit.number_of_packets;
+
+ priv->urbs[0] = usb_alloc_urb(np, GFP_KERNEL);
+ if (!priv->urbs[0])
+ goto err_urb;
+
+ if (buf_len) {
+ if (use_sg) {
+ priv->urbs[0]->sg = sgl;
+ priv->urbs[0]->num_sgs = nents;
+ priv->urbs[0]->transfer_buffer = NULL;
+ } else {
+ priv->urbs[0]->transfer_buffer = buffer;
+ }
+ }
+
+ /* copy urb setup packet */
+ priv->urbs[0]->setup_packet = kmemdup(&pdu->u.cmd_submit.setup,
+ 8, GFP_KERNEL);
+ if (!priv->urbs[0]->setup_packet) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+
+ usbip_pack_pdu(pdu, priv->urbs[0], USBIP_CMD_SUBMIT, 0);
+ } else {
+ for_each_sg(sgl, sg, nents, i) {
+ priv->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ /* The URBs which is previously allocated will be freed
+ * in stub_device_cleanup_urbs() if error occurs.
+ */
+ if (!priv->urbs[i])
+ goto err_urb;
+
+ usbip_pack_pdu(pdu, priv->urbs[i], USBIP_CMD_SUBMIT, 0);
+ priv->urbs[i]->transfer_buffer = sg_virt(sg);
+ priv->urbs[i]->transfer_buffer_length = sg->length;
+ }
+ priv->sgl = sgl;
}
- /* set other members from the base header of pdu */
- priv->urb->context = (void *) priv;
- priv->urb->dev = udev;
- priv->urb->pipe = pipe;
- priv->urb->complete = stub_complete;
+ for (i = 0; i < num_urbs; i++) {
+ /* set other members from the base header of pdu */
+ priv->urbs[i]->context = (void *) priv;
+ priv->urbs[i]->dev = udev;
+ priv->urbs[i]->pipe = pipe;
+ priv->urbs[i]->complete = stub_complete;
- usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
+ /* no need to submit an intercepted request, but harmless? */
+ tweak_special_requests(priv->urbs[i]);
+ masking_bogus_flags(priv->urbs[i]);
+ }
- if (usbip_recv_xbuff(ud, priv->urb) < 0)
+ if (stub_recv_xbuff(ud, priv) < 0)
return;
- if (usbip_recv_iso(ud, priv->urb) < 0)
+ if (usbip_recv_iso(ud, priv->urbs[0]) < 0)
return;
- /* no need to submit an intercepted request, but harmless? */
- tweak_special_requests(priv->urb);
-
- masking_bogus_flags(priv->urb);
/* urb is now ready to submit */
- ret = usb_submit_urb(priv->urb, GFP_KERNEL);
+ for (i = 0; i < priv->num_urbs; i++) {
+ ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
- if (ret == 0)
- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
- else {
- dev_err(&udev->dev, "submit_urb error, %d\n", ret);
- usbip_dump_header(pdu);
- usbip_dump_urb(priv->urb);
+ if (ret == 0)
+ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
+ pdu->base.seqnum);
+ else {
+ dev_err(&udev->dev, "submit_urb error, %d\n", ret);
+ usbip_dump_header(pdu);
+ usbip_dump_urb(priv->urbs[i]);
- /*
- * Pessimistic.
- * This connection will be discarded.
- */
- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ /*
+ * Pessimistic.
+ * This connection will be discarded.
+ */
+ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ break;
+ }
}
usbip_dbg_stub_rx("Leave\n");
+ return;
+
+err_urb:
+ kfree(priv->urbs);
+err_urbs:
+ kfree(buffer);
+ sgl_free(sgl);
+err_malloc:
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
}
/* recv a pdu */
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index f0ec41a..36010a8 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -5,25 +5,11 @@
#include <linux/kthread.h>
#include <linux/socket.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
-static void stub_free_priv_and_urb(struct stub_priv *priv)
-{
- struct urb *urb = priv->urb;
-
- kfree(urb->setup_packet);
- urb->setup_packet = NULL;
-
- kfree(urb->transfer_buffer);
- urb->transfer_buffer = NULL;
-
- list_del(&priv->list);
- kmem_cache_free(stub_priv_cache, priv);
- usb_free_urb(urb);
-}
-
/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
__u32 status)
@@ -85,6 +71,22 @@ void stub_complete(struct urb *urb)
break;
}
+ /*
+ * If the server breaks single SG request into the several URBs, the
+ * URBs must be reassembled before sending completed URB to the vhci.
+ * Don't wake up the tx thread until all the URBs are completed.
+ */
+ if (priv->sgl) {
+ priv->completed_urbs++;
+
+ /* Only save the first error status */
+ if (urb->status && !priv->urb_status)
+ priv->urb_status = urb->status;
+
+ if (priv->completed_urbs < priv->num_urbs)
+ return;
+ }
+
/* link a urb to the queue of tx. */
spin_lock_irqsave(&sdev->priv_lock, flags);
if (sdev->ud.tcp_socket == NULL) {
@@ -156,18 +158,22 @@ static int stub_send_ret_submit(struct stub_device *sdev)
size_t total_size = 0;
while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
- int ret;
- struct urb *urb = priv->urb;
+ struct urb *urb = priv->urbs[0];
struct usbip_header pdu_header;
struct usbip_iso_packet_descriptor *iso_buffer = NULL;
struct kvec *iov = NULL;
+ struct scatterlist *sg;
+ u32 actual_length = 0;
int iovnum = 0;
+ int ret;
+ int i;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
- if (urb->actual_length > 0 && !urb->transfer_buffer) {
+ if (urb->actual_length > 0 && !urb->transfer_buffer &&
+ !urb->num_sgs) {
dev_err(&sdev->udev->dev,
"urb: actual_length %d transfer_buffer null\n",
urb->actual_length);
@@ -176,6 +182,11 @@ static int stub_send_ret_submit(struct stub_device *sdev)
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
iovnum = 2 + urb->number_of_packets;
+ else if (usb_pipein(urb->pipe) && urb->actual_length > 0 &&
+ urb->num_sgs)
+ iovnum = 1 + urb->num_sgs;
+ else if (usb_pipein(urb->pipe) && priv->sgl)
+ iovnum = 1 + priv->num_urbs;
else
iovnum = 2;
@@ -192,6 +203,15 @@ static int stub_send_ret_submit(struct stub_device *sdev)
setup_ret_submit_pdu(&pdu_header, urb);
usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
pdu_header.base.seqnum);
+
+ if (priv->sgl) {
+ for (i = 0; i < priv->num_urbs; i++)
+ actual_length += priv->urbs[i]->actual_length;
+
+ pdu_header.u.ret_submit.status = priv->urb_status;
+ pdu_header.u.ret_submit.actual_length = actual_length;
+ }
+
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
@@ -200,12 +220,47 @@ static int stub_send_ret_submit(struct stub_device *sdev)
txsize += sizeof(pdu_header);
/* 2. setup transfer buffer */
- if (usb_pipein(urb->pipe) &&
+ if (usb_pipein(urb->pipe) && priv->sgl) {
+ /* If the server split a single SG request into several
+ * URBs because the server's HCD doesn't support SG,
+ * reassemble the split URB buffers into a single
+ * return command.
+ */
+ for (i = 0; i < priv->num_urbs; i++) {
+ iov[iovnum].iov_base =
+ priv->urbs[i]->transfer_buffer;
+ iov[iovnum].iov_len =
+ priv->urbs[i]->actual_length;
+ iovnum++;
+ }
+ txsize += actual_length;
+ } else if (usb_pipein(urb->pipe) &&
usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
urb->actual_length > 0) {
- iov[iovnum].iov_base = urb->transfer_buffer;
- iov[iovnum].iov_len = urb->actual_length;
- iovnum++;
+ if (urb->num_sgs) {
+ unsigned int copy = urb->actual_length;
+ int size;
+
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ if (copy == 0)
+ break;
+
+ if (copy < sg->length)
+ size = copy;
+ else
+ size = sg->length;
+
+ iov[iovnum].iov_base = sg_virt(sg);
+ iov[iovnum].iov_len = size;
+
+ iovnum++;
+ copy -= size;
+ }
+ } else {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len = urb->actual_length;
+ iovnum++;
+ }
txsize += urb->actual_length;
} else if (usb_pipein(urb->pipe) &&
usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 9756752..88eaf3c 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -680,8 +680,12 @@ EXPORT_SYMBOL_GPL(usbip_pad_iso);
/* some members of urb must be substituted before. */
int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
{
- int ret;
+ struct scatterlist *sg;
+ int ret = 0;
+ int recv;
int size;
+ int copy;
+ int i;
if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
/* the direction of urb must be OUT. */
@@ -701,29 +705,51 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
if (!(size > 0))
return 0;
- if (size > urb->transfer_buffer_length) {
+ if (size > urb->transfer_buffer_length)
/* should not happen, probably malicious packet */
- if (ud->side == USBIP_STUB) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return 0;
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
- }
- }
+ goto error;
- ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
- if (ret != size) {
- dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
- if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
+ if (urb->num_sgs) {
+ copy = size;
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ int recv_size;
+
+ if (copy < sg->length)
+ recv_size = copy;
+ else
+ recv_size = sg->length;
+
+ recv = usbip_recv(ud->tcp_socket, sg_virt(sg),
+ recv_size);
+
+ if (recv != recv_size)
+ goto error;
+
+ copy -= recv;
+ ret += recv;
+
+ if (!copy)
+ break;
}
+
+ if (ret != size)
+ goto error;
+ } else {
+ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+ if (ret != size)
+ goto error;
}
return ret;
+
+error:
+ dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
+ if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
}
EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 1e592ec..d5a036b 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -702,8 +702,11 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
}
vdev = &vhci_hcd->vdev[portnum-1];
- /* patch to usb_sg_init() is in 2.5.60 */
- BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
+ if (!urb->transfer_buffer && !urb->num_sgs &&
+ urb->transfer_buffer_length) {
+ dev_dbg(dev, "Null URB transfer buffer\n");
+ return -EINVAL;
+ }
spin_lock_irqsave(&vhci->lock, flags);
@@ -1146,6 +1149,15 @@ static int vhci_setup(struct usb_hcd *hcd)
hcd->speed = HCD_USB3;
hcd->self.root_hub->speed = USB_SPEED_SUPER;
}
+
+ /*
+ * Support SG.
+ * sg_tablesize is an arbitrary value to alleviate memory pressure
+ * on the host.
+ */
+ hcd->self.sg_tablesize = 32;
+ hcd->self.no_sg_constraint = 1;
+
return 0;
}
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index 44cd645..00fc987 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -77,19 +77,27 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0);
/* recv transfer buffer */
- if (usbip_recv_xbuff(ud, urb) < 0)
- return;
+ if (usbip_recv_xbuff(ud, urb) < 0) {
+ urb->status = -EPROTO;
+ goto error;
+ }
/* recv iso_packet_descriptor */
- if (usbip_recv_iso(ud, urb) < 0)
- return;
+ if (usbip_recv_iso(ud, urb) < 0) {
+ urb->status = -EPROTO;
+ goto error;
+ }
/* restore the padding in iso packets */
usbip_pad_iso(ud, urb);
+error:
if (usbip_dbg_flag_vhci_rx)
usbip_dump_urb(urb);
+ if (urb->num_sgs)
+ urb->transfer_flags &= ~URB_DMA_MAP_SG;
+
usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
spin_lock_irqsave(&vhci->lock, flags);
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 9aed15a..acac494 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -5,6 +5,7 @@
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "vhci.h"
@@ -50,19 +51,23 @@ static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
static int vhci_send_cmd_submit(struct vhci_device *vdev)
{
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
struct vhci_priv *priv = NULL;
+ struct scatterlist *sg;
struct msghdr msg;
- struct kvec iov[3];
+ struct kvec *iov;
size_t txsize;
size_t total_size = 0;
+ int iovnum;
+ int err = -ENOMEM;
+ int i;
while ((priv = dequeue_from_priv_tx(vdev)) != NULL) {
int ret;
struct urb *urb = priv->urb;
struct usbip_header pdu_header;
- struct usbip_iso_packet_descriptor *iso_buffer = NULL;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
@@ -72,18 +77,45 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n",
priv->seqnum);
+ if (urb->num_sgs && usb_pipeout(urb->pipe))
+ iovnum = 2 + urb->num_sgs;
+ else
+ iovnum = 3;
+
+ iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL);
+ if (!iov) {
+ usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC);
+ return -ENOMEM;
+ }
+
+ if (urb->num_sgs)
+ urb->transfer_flags |= URB_DMA_MAP_SG;
+
/* 1. setup usbip_header */
setup_cmd_submit_pdu(&pdu_header, urb);
usbip_header_correct_endian(&pdu_header, 1);
+ iovnum = 0;
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
+ iov[iovnum].iov_base = &pdu_header;
+ iov[iovnum].iov_len = sizeof(pdu_header);
txsize += sizeof(pdu_header);
+ iovnum++;
/* 2. setup transfer buffer */
if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) {
- iov[1].iov_base = urb->transfer_buffer;
- iov[1].iov_len = urb->transfer_buffer_length;
+ if (urb->num_sgs &&
+ !usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ iov[iovnum].iov_base = sg_virt(sg);
+ iov[iovnum].iov_len = sg->length;
+ iovnum++;
+ }
+ } else {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len =
+ urb->transfer_buffer_length;
+ iovnum++;
+ }
txsize += urb->transfer_buffer_length;
}
@@ -95,30 +127,43 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
if (!iso_buffer) {
usbip_event_add(&vdev->ud,
SDEV_EVENT_ERROR_MALLOC);
- return -1;
+ goto err_iso_buffer;
}
- iov[2].iov_base = iso_buffer;
- iov[2].iov_len = len;
+ iov[iovnum].iov_base = iso_buffer;
+ iov[iovnum].iov_len = len;
+ iovnum++;
txsize += len;
}
- ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+ ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, iovnum,
+ txsize);
if (ret != txsize) {
pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
txsize);
- kfree(iso_buffer);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
- return -1;
+ err = -EPIPE;
+ goto err_tx;
}
+ kfree(iov);
+ /* This is only for isochronous case */
kfree(iso_buffer);
+ iso_buffer = NULL;
+
usbip_dbg_vhci_tx("send txdata\n");
total_size += txsize;
}
return total_size;
+
+err_tx:
+ kfree(iso_buffer);
+err_iso_buffer:
+ kfree(iov);
+
+ return err;
}
static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index a92c286..0a6eb53 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -443,10 +443,14 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
{
if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
u8 pin;
- pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
- if (IS_ENABLED(CONFIG_VFIO_PCI_INTX) && !vdev->nointx && pin)
- return 1;
+ if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) ||
+ vdev->nointx || vdev->pdev->is_virtfn)
+ return 0;
+
+ pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
+
+ return pin ? 1 : 0;
} else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
u8 pos;
u16 flags;
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 115a36f..423ea1f 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -1180,8 +1180,10 @@ static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
return -ENOMEM;
ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags);
- if (ret)
+ if (ret) {
+ kfree(vdev->msi_perm);
return ret;
+ }
return len;
}
@@ -1610,6 +1612,15 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev)
}
/*
+ * Nag about hardware bugs, hopefully to have vendors fix them, but at least
+ * to collect a list of dependencies for the VF INTx pin quirk below.
+ */
+static const struct pci_device_id known_bogus_vf_intx_pin[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x270c) },
+ {}
+};
+
+/*
* For each device we allocate a pci_config_map that indicates the
* capability occupying each dword and thus the struct perm_bits we
* use for read and write. We also allocate a virtualized config
@@ -1674,6 +1685,24 @@ int vfio_config_init(struct vfio_pci_device *vdev)
if (pdev->is_virtfn) {
*(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor);
*(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
+
+ /*
+ * Per SR-IOV spec rev 1.1, 3.4.1.18 the interrupt pin register
+ * does not apply to VFs and VFs must implement this register
+ * as read-only with value zero. Userspace is not readily able
+ * to identify whether a device is a VF and thus that the pin
+ * definition on the device is bogus should it violate this
+ * requirement. We already virtualize the pin register for
+ * other purposes, so we simply need to replace the bogus value
+ * and consider VFs when we determine INTx IRQ count.
+ */
+ if (vconfig[PCI_INTERRUPT_PIN] &&
+ !pci_match_id(known_bogus_vf_intx_pin, pdev))
+ pci_warn(pdev,
+ "Hardware bug: VF reports bogus INTx pin %d\n",
+ vconfig[PCI_INTERRUPT_PIN]);
+
+ vconfig[PCI_INTERRUPT_PIN] = 0; /* Gratuitous for good VFs */
}
if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) || vdev->nointx)
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
index 1c46045..94594dc 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -297,8 +297,8 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
irq = pci_irq_vector(pdev, vector);
if (vdev->ctx[vector].trigger) {
- free_irq(irq, vdev->ctx[vector].trigger);
irq_bypass_unregister_producer(&vdev->ctx[vector].producer);
+ free_irq(irq, vdev->ctx[vector].trigger);
kfree(vdev->ctx[vector].name);
eventfd_ctx_put(vdev->ctx[vector].trigger);
vdev->ctx[vector].trigger = NULL;
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 96721b1..ec53310 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -371,6 +371,7 @@ static void tce_iommu_release(void *iommu_data)
{
struct tce_container *container = iommu_data;
struct tce_iommu_group *tcegrp;
+ struct tce_iommu_prereg *tcemem, *tmtmp;
long i;
while (tce_groups_attached(container)) {
@@ -393,13 +394,8 @@ static void tce_iommu_release(void *iommu_data)
tce_iommu_free_table(container, tbl);
}
- while (!list_empty(&container->prereg_list)) {
- struct tce_iommu_prereg *tcemem;
-
- tcemem = list_first_entry(&container->prereg_list,
- struct tce_iommu_prereg, next);
- WARN_ON_ONCE(tce_iommu_prereg_free(container, tcemem));
- }
+ list_for_each_entry_safe(tcemem, tmtmp, &container->prereg_list, next)
+ WARN_ON(tce_iommu_prereg_free(container, tcemem));
tce_iommu_disable(container);
if (container->mm)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index bab495d..5f5c5de 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -103,7 +103,7 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
struct iov_iter iov_iter;
unsigned out, in;
size_t nbytes;
- size_t len;
+ size_t iov_len, payload_len;
int head;
spin_lock_bh(&vsock->send_pkt_list_lock);
@@ -148,8 +148,24 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
break;
}
- len = iov_length(&vq->iov[out], in);
- iov_iter_init(&iov_iter, READ, &vq->iov[out], in, len);
+ iov_len = iov_length(&vq->iov[out], in);
+ if (iov_len < sizeof(pkt->hdr)) {
+ virtio_transport_free_pkt(pkt);
+ vq_err(vq, "Buffer len [%zu] too small\n", iov_len);
+ break;
+ }
+
+ iov_iter_init(&iov_iter, READ, &vq->iov[out], in, iov_len);
+ payload_len = pkt->len - pkt->off;
+
+ /* If the packet is greater than the space available in the
+ * buffer, we split it using multiple buffers.
+ */
+ if (payload_len > iov_len - sizeof(pkt->hdr))
+ payload_len = iov_len - sizeof(pkt->hdr);
+
+ /* Set the correct length in the header */
+ pkt->hdr.len = cpu_to_le32(payload_len);
nbytes = copy_to_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter);
if (nbytes != sizeof(pkt->hdr)) {
@@ -158,33 +174,47 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
break;
}
- nbytes = copy_to_iter(pkt->buf, pkt->len, &iov_iter);
- if (nbytes != pkt->len) {
+ nbytes = copy_to_iter(pkt->buf + pkt->off, payload_len,
+ &iov_iter);
+ if (nbytes != payload_len) {
virtio_transport_free_pkt(pkt);
vq_err(vq, "Faulted on copying pkt buf\n");
break;
}
- vhost_add_used(vq, head, sizeof(pkt->hdr) + pkt->len);
+ vhost_add_used(vq, head, sizeof(pkt->hdr) + payload_len);
added = true;
- if (pkt->reply) {
- int val;
-
- val = atomic_dec_return(&vsock->queued_replies);
-
- /* Do we have resources to resume tx processing? */
- if (val + 1 == tx_vq->num)
- restart_tx = true;
- }
-
/* Deliver to monitoring devices all correctly transmitted
* packets.
*/
virtio_transport_deliver_tap_pkt(pkt);
- total_len += pkt->len;
- virtio_transport_free_pkt(pkt);
+ pkt->off += payload_len;
+ total_len += payload_len;
+
+ /* If we didn't send all the payload we can requeue the packet
+ * to send it with the next available buffer.
+ */
+ if (pkt->off < pkt->len) {
+ spin_lock_bh(&vsock->send_pkt_list_lock);
+ list_add(&pkt->list, &vsock->send_pkt_list);
+ spin_unlock_bh(&vsock->send_pkt_list_lock);
+ } else {
+ if (pkt->reply) {
+ int val;
+
+ val = atomic_dec_return(&vsock->queued_replies);
+
+ /* Do we have resources to resume tx
+ * processing?
+ */
+ if (val + 1 == tx_vq->num)
+ restart_tx = true;
+ }
+
+ virtio_transport_free_pkt(pkt);
+ }
} while(likely(!vhost_exceeds_weight(vq, ++pkts, total_len)));
if (added)
vhost_signal(&vsock->dev, vq);
@@ -406,7 +436,9 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
virtio_transport_deliver_tap_pkt(pkt);
/* Only accept correctly addressed packets */
- if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid)
+ if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid &&
+ le64_to_cpu(pkt->hdr.dst_cid) ==
+ vhost_transport_get_local_cid())
virtio_transport_recv_pkt(pkt);
else
virtio_transport_free_pkt(pkt);
diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c
index cd50df5..086611c 100644
--- a/drivers/video/backlight/lm3639_bl.c
+++ b/drivers/video/backlight/lm3639_bl.c
@@ -400,10 +400,8 @@ static int lm3639_remove(struct i2c_client *client)
regmap_write(pchip->regmap, REG_ENABLE, 0x00);
- if (&pchip->cdev_torch)
- led_classdev_unregister(&pchip->cdev_torch);
- if (&pchip->cdev_flash)
- led_classdev_unregister(&pchip->cdev_flash);
+ led_classdev_unregister(&pchip->cdev_torch);
+ led_classdev_unregister(&pchip->cdev_flash);
if (pchip->bled)
device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
return 0;
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 591a13a..f99558d 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2,6 +2,18 @@
# fbdev configuration
#
+config FB_CMDLINE
+ bool
+
+config FB_NOTIFY
+ bool
+
+config FB_CLPS711X_OLD
+ tristate
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+
menuconfig FB
tristate "Support for frame buffer devices"
select FB_CMDLINE
@@ -54,12 +66,6 @@
combination with certain motherboards and monitors are known to
suffer from this problem.
-config FB_CMDLINE
- bool
-
-config FB_NOTIFY
- bool
-
config FB_DDC
tristate
depends on FB
@@ -329,12 +335,6 @@
hardware found in Acorn RISC PCs and other ARM-based machines. If
unsure, say N.
-config FB_CLPS711X_OLD
- tristate
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
-
config FB_CLPS711X
tristate "CLPS711X LCD support"
depends on FB && (ARCH_CLPS711X || COMPILE_TEST)
@@ -1456,7 +1456,6 @@
config FB_VIA_DIRECT_PROCFS
bool "direct hardware access via procfs (DEPRECATED)(DANGEROUS)"
- depends on FB_VIA
default n
help
Allow direct hardware access to some output registers via procfs.
@@ -1466,7 +1465,6 @@
config FB_VIA_X_COMPATIBILITY
bool "X server compatibility"
- depends on FB_VIA
default n
help
This option reduces the functionality (power saving, ...) of the
@@ -2308,10 +2306,6 @@
Configuration re: surface address, size, and format must be provided
through device tree, or plain old platform data.
-source "drivers/video/fbdev/omap/Kconfig"
-source "drivers/video/fbdev/omap2/Kconfig"
-source "drivers/video/fbdev/mmp/Kconfig"
-
config FB_SSD1307
tristate "Solomon SSD1307 framebuffer support"
depends on FB && I2C
@@ -2341,3 +2335,7 @@
This driver is also available as a module. The module will be
called sm712fb. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>.
+
+source "drivers/video/fbdev/omap/Kconfig"
+source "drivers/video/fbdev/omap2/Kconfig"
+source "drivers/video/fbdev/mmp/Kconfig"
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c
index 076d24a..4ed55e6 100644
--- a/drivers/video/fbdev/atmel_lcdfb.c
+++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <video/of_videomode.h>
#include <video/of_display_timing.h>
#include <linux/regulator/consumer.h>
#include <video/videomode.h>
@@ -1028,11 +1029,11 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
struct device *dev = &sinfo->pdev->dev;
struct device_node *np =dev->of_node;
struct device_node *display_np;
- struct device_node *timings_np;
- struct display_timings *timings;
struct atmel_lcdfb_power_ctrl_gpio *og;
bool is_gpio_power = false;
+ struct fb_videomode fb_vm;
struct gpio_desc *gpiod;
+ struct videomode vm;
int ret = -ENOENT;
int i;
@@ -1105,44 +1106,18 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
pdata->lcdcon_is_backlight = of_property_read_bool(display_np, "atmel,lcdcon-backlight");
pdata->lcdcon_pol_negative = of_property_read_bool(display_np, "atmel,lcdcon-backlight-inverted");
- timings = of_get_display_timings(display_np);
- if (!timings) {
- dev_err(dev, "failed to get display timings\n");
- ret = -EINVAL;
+ ret = of_get_videomode(display_np, &vm, OF_USE_NATIVE_MODE);
+ if (ret) {
+ dev_err(dev, "failed to get videomode from DT\n");
goto put_display_node;
}
- timings_np = of_get_child_by_name(display_np, "display-timings");
- if (!timings_np) {
- dev_err(dev, "failed to find display-timings node\n");
- ret = -ENODEV;
+ ret = fb_videomode_from_videomode(&vm, &fb_vm);
+ if (ret < 0)
goto put_display_node;
- }
- for (i = 0; i < of_get_child_count(timings_np); i++) {
- struct videomode vm;
- struct fb_videomode fb_vm;
+ fb_add_videomode(&fb_vm, &info->modelist);
- ret = videomode_from_timings(timings, &vm, i);
- if (ret < 0)
- goto put_timings_node;
- ret = fb_videomode_from_videomode(&vm, &fb_vm);
- if (ret < 0)
- goto put_timings_node;
-
- fb_add_videomode(&fb_vm, &info->modelist);
- }
-
- /*
- * FIXME: Make sure we are not referencing any fields in display_np
- * and timings_np and drop our references to them before returning to
- * avoid leaking the nodes on probe deferral and driver unbind.
- */
-
- return 0;
-
-put_timings_node:
- of_node_put(timings_np);
put_display_node:
of_node_put(display_np);
return ret;
diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
index 852d86c..8607439 100644
--- a/drivers/video/fbdev/core/fbmon.c
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -997,98 +997,6 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
DPRINTK("========================================\n");
}
-/**
- * fb_edid_add_monspecs() - add monitor video modes from E-EDID data
- * @edid: 128 byte array with an E-EDID block
- * @spacs: monitor specs to be extended
- */
-void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
- unsigned char *block;
- struct fb_videomode *m;
- int num = 0, i;
- u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
- u8 pos = 4, svd_n = 0;
-
- if (!edid)
- return;
-
- if (!edid_checksum(edid))
- return;
-
- if (edid[0] != 0x2 ||
- edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
- return;
-
- DPRINTK(" Short Video Descriptors\n");
-
- while (pos < edid[2]) {
- u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
- pr_debug("Data block %u of %u bytes\n", type, len);
- if (type == 2) {
- for (i = pos; i < pos + len; i++) {
- u8 idx = edid[pos + i] & 0x7f;
- svd[svd_n++] = idx;
- pr_debug("N%sative mode #%d\n",
- edid[pos + i] & 0x80 ? "" : "on-n", idx);
- }
- } else if (type == 3 && len >= 3) {
- /* Check Vendor Specific Data Block. For HDMI,
- it is always 00-0C-03 for HDMI Licensing, LLC. */
- if (edid[pos + 1] == 3 && edid[pos + 2] == 0xc &&
- edid[pos + 3] == 0)
- specs->misc |= FB_MISC_HDMI;
- }
- pos += len + 1;
- }
-
- block = edid + edid[2];
-
- DPRINTK(" Extended Detailed Timings\n");
-
- for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE;
- i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
- if (PIXEL_CLOCK != 0)
- edt[num++] = block - edid;
-
- /* Yikes, EDID data is totally useless */
- if (!(num + svd_n))
- return;
-
- m = kcalloc(specs->modedb_len + num + svd_n,
- sizeof(struct fb_videomode),
- GFP_KERNEL);
-
- if (!m)
- return;
-
- memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode));
-
- for (i = specs->modedb_len; i < specs->modedb_len + num; i++) {
- get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]);
- if (i == specs->modedb_len)
- m[i].flag |= FB_MODE_IS_FIRST;
- pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh);
- }
-
- for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) {
- int idx = svd[i - specs->modedb_len - num];
- if (!idx || idx >= ARRAY_SIZE(cea_modes)) {
- pr_warn("Reserved SVD code %d\n", idx);
- } else if (!cea_modes[idx].xres) {
- pr_warn("Unimplemented SVD code %d\n", idx);
- } else {
- memcpy(&m[i], cea_modes + idx, sizeof(m[i]));
- pr_debug("Adding SVD #%d: %ux%u@%u\n", idx,
- m[i].xres, m[i].yres, m[i].refresh);
- }
- }
-
- kfree(specs->modedb);
- specs->modedb = m;
- specs->modedb_len = specs->modedb_len + num + svd_n;
-}
-
/*
* VESA Generalized Timing Formula (GTF)
*/
@@ -1498,9 +1406,6 @@ int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
{
}
-void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
-}
void fb_destroy_modedb(struct fb_videomode *modedb)
{
}
@@ -1608,7 +1513,6 @@ EXPORT_SYMBOL(fb_firmware_edid);
EXPORT_SYMBOL(fb_parse_edid);
EXPORT_SYMBOL(fb_edid_to_monspecs);
-EXPORT_SYMBOL(fb_edid_add_monspecs);
EXPORT_SYMBOL(fb_get_mode);
EXPORT_SYMBOL(fb_validate_mode);
EXPORT_SYMBOL(fb_destroy_modedb);
diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c
index ac04987..6473e0d 100644
--- a/drivers/video/fbdev/core/modedb.c
+++ b/drivers/video/fbdev/core/modedb.c
@@ -289,63 +289,6 @@ static const struct fb_videomode modedb[] = {
};
#ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[65] = {
- /* #1: 640x480p@59.94/60Hz */
- [1] = {
- NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #3: 720x480p@59.94/60Hz */
- [3] = {
- NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #5: 1920x1080i@59.94/60Hz */
- [5] = {
- NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
- },
- /* #7: 720(1440)x480iH@59.94/60Hz */
- [7] = {
- NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
- FB_VMODE_INTERLACED, 0,
- },
- /* #9: 720(1440)x240pH@59.94/60Hz */
- [9] = {
- NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #18: 720x576pH@50Hz */
- [18] = {
- NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #19: 1280x720p@50Hz */
- [19] = {
- NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #20: 1920x1080i@50Hz */
- [20] = {
- NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
- },
- /* #32: 1920x1080p@23.98/24Hz */
- [32] = {
- NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #35: (2880)x480p4x@59.94/60Hz */
- [35] = {
- NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
-};
-
const struct fb_videomode vesa_modes[] = {
/* 0 640x350-85 VESA */
{ NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
diff --git a/drivers/video/fbdev/sbuslib.c b/drivers/video/fbdev/sbuslib.c
index a436d44..01a7110 100644
--- a/drivers/video/fbdev/sbuslib.c
+++ b/drivers/video/fbdev/sbuslib.c
@@ -106,11 +106,11 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
struct fbtype __user *f = (struct fbtype __user *) arg;
if (put_user(type, &f->fb_type) ||
- __put_user(info->var.yres, &f->fb_height) ||
- __put_user(info->var.xres, &f->fb_width) ||
- __put_user(fb_depth, &f->fb_depth) ||
- __put_user(0, &f->fb_cmsize) ||
- __put_user(fb_size, &f->fb_cmsize))
+ put_user(info->var.yres, &f->fb_height) ||
+ put_user(info->var.xres, &f->fb_width) ||
+ put_user(fb_depth, &f->fb_depth) ||
+ put_user(0, &f->fb_cmsize) ||
+ put_user(fb_size, &f->fb_cmsize))
return -EFAULT;
return 0;
}
@@ -125,10 +125,10 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
unsigned int index, count, i;
if (get_user(index, &c->index) ||
- __get_user(count, &c->count) ||
- __get_user(ured, &c->red) ||
- __get_user(ugreen, &c->green) ||
- __get_user(ublue, &c->blue))
+ get_user(count, &c->count) ||
+ get_user(ured, &c->red) ||
+ get_user(ugreen, &c->green) ||
+ get_user(ublue, &c->blue))
return -EFAULT;
cmap.len = 1;
@@ -165,13 +165,13 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
u8 red, green, blue;
if (get_user(index, &c->index) ||
- __get_user(count, &c->count) ||
- __get_user(ured, &c->red) ||
- __get_user(ugreen, &c->green) ||
- __get_user(ublue, &c->blue))
+ get_user(count, &c->count) ||
+ get_user(ured, &c->red) ||
+ get_user(ugreen, &c->green) ||
+ get_user(ublue, &c->blue))
return -EFAULT;
- if (index + count > cmap->len)
+ if (index > cmap->len || count > cmap->len - index)
return -EINVAL;
for (i = 0; i < count; i++) {
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 8a3e8f6..164564b 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -1039,12 +1039,12 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame,
if (ptr[0] & 0x10)
frame->active_aspect = ptr[1] & 0xf;
if (ptr[0] & 0x8) {
- frame->top_bar = (ptr[5] << 8) + ptr[6];
- frame->bottom_bar = (ptr[7] << 8) + ptr[8];
+ frame->top_bar = (ptr[6] << 8) | ptr[5];
+ frame->bottom_bar = (ptr[8] << 8) | ptr[7];
}
if (ptr[0] & 0x4) {
- frame->left_bar = (ptr[9] << 8) + ptr[10];
- frame->right_bar = (ptr[11] << 8) + ptr[12];
+ frame->left_bar = (ptr[10] << 8) | ptr[9];
+ frame->right_bar = (ptr[12] << 8) | ptr[11];
}
frame->scan_mode = ptr[0] & 0x3;
diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c
index bf447421..9209100 100644
--- a/drivers/virt/vboxguest/vboxguest_utils.c
+++ b/drivers/virt/vboxguest/vboxguest_utils.c
@@ -217,6 +217,8 @@ static int hgcm_call_preprocess_linaddr(
if (!bounce_buf)
return -ENOMEM;
+ *bounce_buf_ret = bounce_buf;
+
if (copy_in) {
ret = copy_from_user(bounce_buf, (void __user *)buf, len);
if (ret)
@@ -225,7 +227,6 @@ static int hgcm_call_preprocess_linaddr(
memset(bounce_buf, 0, len);
}
- *bounce_buf_ret = bounce_buf;
hgcm_call_add_pagelist_size(bounce_buf, len, extra);
return 0;
}
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index d1c1f62..14ac36c 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -468,6 +468,17 @@ static int virtballoon_migratepage(struct balloon_dev_info *vb_dev_info,
get_page(newpage); /* balloon reference */
+ /*
+ * When we migrate a page to a different zone and adjusted the
+ * managed page count when inflating, we have to fixup the count of
+ * both involved zones.
+ */
+ if (!virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM) &&
+ page_zone(page) != page_zone(newpage)) {
+ adjust_managed_page_count(page, 1);
+ adjust_managed_page_count(newpage, -1);
+ }
+
/* balloon's page migration 1st step -- inflate "newpage" */
spin_lock_irqsave(&vb_dev_info->pages_lock, flags);
balloon_page_insert(vb_dev_info, newpage);
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 9529e28..6228b48 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -431,7 +431,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
kfree(desc);
END_USE(vq);
- return -EIO;
+ return -ENOMEM;
}
/**
diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c
index bf641a1..7c4e33d 100644
--- a/drivers/w1/slaves/w1_ds2438.c
+++ b/drivers/w1/slaves/w1_ds2438.c
@@ -186,8 +186,8 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
return -1;
}
-static uint16_t w1_ds2438_get_voltage(struct w1_slave *sl,
- int adc_input, uint16_t *voltage)
+static int w1_ds2438_get_voltage(struct w1_slave *sl,
+ int adc_input, uint16_t *voltage)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
@@ -235,6 +235,25 @@ static uint16_t w1_ds2438_get_voltage(struct w1_slave *sl,
return ret;
}
+static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage)
+{
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ /* The voltage measured across current sense resistor RSENS. */
+ *voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
static ssize_t iad_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
@@ -257,6 +276,27 @@ static ssize_t iad_write(struct file *filp, struct kobject *kobj,
return ret;
}
+static ssize_t iad_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ int16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_current(sl, &voltage) == 0) {
+ ret = snprintf(buf, count, "%i\n", voltage);
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
static ssize_t page0_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
@@ -272,9 +312,13 @@ static ssize_t page0_read(struct file *filp, struct kobject *kobj,
mutex_lock(&sl->master->bus_mutex);
+ /* Read no more than page0 size */
+ if (count > DS2438_PAGE_SIZE)
+ count = DS2438_PAGE_SIZE;
+
if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
- memcpy(buf, &w1_buf, DS2438_PAGE_SIZE);
- ret = DS2438_PAGE_SIZE;
+ memcpy(buf, &w1_buf, count);
+ ret = count;
} else
ret = -EIO;
@@ -289,7 +333,6 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
- ssize_t c = PAGE_SIZE;
int16_t temp;
if (off != 0)
@@ -298,8 +341,7 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
return -EINVAL;
if (w1_ds2438_get_temperature(sl, &temp) == 0) {
- c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp);
- ret = PAGE_SIZE - c;
+ ret = snprintf(buf, count, "%i\n", temp);
} else
ret = -EIO;
@@ -312,7 +354,6 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj,
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
- ssize_t c = PAGE_SIZE;
uint16_t voltage;
if (off != 0)
@@ -321,8 +362,7 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj,
return -EINVAL;
if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) {
- c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
- ret = PAGE_SIZE - c;
+ ret = snprintf(buf, count, "%u\n", voltage);
} else
ret = -EIO;
@@ -335,7 +375,6 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
- ssize_t c = PAGE_SIZE;
uint16_t voltage;
if (off != 0)
@@ -344,15 +383,14 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
return -EINVAL;
if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) {
- c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
- ret = PAGE_SIZE - c;
+ ret = snprintf(buf, count, "%u\n", voltage);
} else
ret = -EIO;
return ret;
}
-static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, NULL, iad_write, 1);
+static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0);
static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
static BIN_ATTR_RO(temperature, 0/* real length varies */);
static BIN_ATTR_RO(vad, 0/* real length varies */);
diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index ffde179..d84d6cb 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -207,11 +207,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
- /*
- * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only
- * runs at 1MHz. We chose to always run at 1MHz, as there's no
- * good reason to have a faster watchdog counter.
- */
wdt->wdd.info = &aspeed_wdt_info;
wdt->wdd.ops = &aspeed_wdt_ops;
wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
@@ -227,7 +222,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
return -EINVAL;
config = ofdid->data;
- wdt->ctrl = WDT_CTRL_1MHZ_CLK;
+ /*
+ * On clock rates:
+ * - ast2400 wdt can run at PCLK, or 1MHz
+ * - ast2500 only runs at 1MHz, hard coding bit 4 to 1
+ * - ast2600 always runs at 1MHz
+ *
+ * Set the ast2400 to run at 1MHz as it simplifies the driver.
+ */
+ if (of_device_is_compatible(np, "aspeed,ast2400-wdt"))
+ wdt->ctrl = WDT_CTRL_1MHZ_CLK;
/*
* Control reset on a per-device basis to ensure the
diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c
index 69adeab..0a66727 100644
--- a/drivers/watchdog/meson_gxbb_wdt.c
+++ b/drivers/watchdog/meson_gxbb_wdt.c
@@ -89,8 +89,8 @@ static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev)
reg = readl(data->reg_base + GXBB_WDT_TCNT_REG);
- return ((reg >> GXBB_WDT_TCNT_CNT_SHIFT) -
- (reg & GXBB_WDT_TCNT_SETUP_MASK)) / 1000;
+ return ((reg & GXBB_WDT_TCNT_SETUP_MASK) -
+ (reg >> GXBB_WDT_TCNT_CNT_SHIFT)) / 1000;
}
static const struct watchdog_ops meson_gxbb_wdt_ops = {
diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c
index d01efd3..62d9d3ed 100644
--- a/drivers/watchdog/renesas_wdt.c
+++ b/drivers/watchdog/renesas_wdt.c
@@ -239,6 +239,7 @@ static int rwdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&priv->wdev, priv);
watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_set_restart_priority(&priv->wdev, 0);
+ watchdog_stop_on_unregister(&priv->wdev);
/* This overrides the default timeout only if DT configuration was found */
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 2551699..d495336 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -111,9 +111,7 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
u32 value = WDT_SEC2TICKS(timeout);
wdt->mr &= ~AT91_WDT_WDV;
- wdt->mr &= ~AT91_WDT_WDD;
wdt->mr |= AT91_WDT_SET_WDV(value);
- wdt->mr |= AT91_WDT_SET_WDD(value);
/*
* WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
@@ -247,15 +245,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
}
}
- ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "unable to set timeout value\n");
- return ret;
- }
+ watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
timeout = WDT_SEC2TICKS(wdd->timeout);
- wdt->mr |= AT91_WDT_SET_WDD(timeout);
+ wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
wdt->mr |= AT91_WDT_SET_WDV(timeout);
ret = sama5d4_wdt_init(wdt);
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 7817836..4b9365d 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
- nct6795, nct6102 };
+ nct6795, nct6796, nct6102 };
static int timeout; /* in seconds */
module_param(timeout, int, 0);
@@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define NCT6792_ID 0xc9
#define NCT6793_ID 0xd1
#define NCT6795_ID 0xd3
+#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */
#define W83627HF_WDT_TIMEOUT 0xf6
#define W83697HF_WDT_TIMEOUT 0xf4
@@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
case nct6102:
/*
* These chips have a fixed WDTO# output pin (W83627UHG),
@@ -407,6 +409,9 @@ static int wdt_find(int addr)
case NCT6795_ID:
ret = nct6795;
break;
+ case NCT6796_ID:
+ ret = nct6796;
+ break;
case NCT6102_ID:
ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
@@ -450,6 +455,7 @@ static int __init wdt_init(void)
"NCT6792",
"NCT6793",
"NCT6795",
+ "NCT6796",
"NCT6102",
};
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index ffbdc46..4b89333 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -38,7 +38,6 @@
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/hrtimer.h> /* For hrtimers */
#include <linux/kernel.h> /* For printk/panic/... */
-#include <linux/kref.h> /* For data references */
#include <linux/kthread.h> /* For kthread_work */
#include <linux/miscdevice.h> /* For handling misc devices */
#include <linux/module.h> /* For module stuff/... */
@@ -56,14 +55,14 @@
/*
* struct watchdog_core_data - watchdog core internal data
- * @kref: Reference count.
+ * @dev: The watchdog's internal device
* @cdev: The watchdog's Character device.
* @wdd: Pointer to watchdog device.
* @lock: Lock for watchdog core.
* @status: Watchdog core internal status bits.
*/
struct watchdog_core_data {
- struct kref kref;
+ struct device dev;
struct cdev cdev;
struct watchdog_device *wdd;
struct mutex lock;
@@ -822,7 +821,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
file->private_data = wd_data;
if (!hw_running)
- kref_get(&wd_data->kref);
+ get_device(&wd_data->dev);
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
return nonseekable_open(inode, file);
@@ -834,11 +833,11 @@ static int watchdog_open(struct inode *inode, struct file *file)
return err;
}
-static void watchdog_core_data_release(struct kref *kref)
+static void watchdog_core_data_release(struct device *dev)
{
struct watchdog_core_data *wd_data;
- wd_data = container_of(kref, struct watchdog_core_data, kref);
+ wd_data = container_of(dev, struct watchdog_core_data, dev);
kfree(wd_data);
}
@@ -898,7 +897,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
*/
if (!running) {
module_put(wd_data->cdev.owner);
- kref_put(&wd_data->kref, watchdog_core_data_release);
+ put_device(&wd_data->dev);
}
return 0;
}
@@ -917,17 +916,22 @@ static struct miscdevice watchdog_miscdev = {
.fops = &watchdog_fops,
};
+static struct class watchdog_class = {
+ .name = "watchdog",
+ .owner = THIS_MODULE,
+ .dev_groups = wdt_groups,
+};
+
/*
* watchdog_cdev_register: register watchdog character device
* @wdd: watchdog device
- * @devno: character device number
*
* Register a watchdog character device including handling the legacy
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
* thus we set it up like that.
*/
-static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
+static int watchdog_cdev_register(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data;
int err;
@@ -935,7 +939,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL);
if (!wd_data)
return -ENOMEM;
- kref_init(&wd_data->kref);
mutex_init(&wd_data->lock);
wd_data->wdd = wdd;
@@ -964,23 +967,33 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
}
}
+ device_initialize(&wd_data->dev);
+ wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
+ wd_data->dev.class = &watchdog_class;
+ wd_data->dev.parent = wdd->parent;
+ wd_data->dev.groups = wdd->groups;
+ wd_data->dev.release = watchdog_core_data_release;
+ dev_set_drvdata(&wd_data->dev, wdd);
+ dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
+
/* Fill in the data structures */
cdev_init(&wd_data->cdev, &watchdog_fops);
- wd_data->cdev.owner = wdd->ops->owner;
/* Add the device */
- err = cdev_add(&wd_data->cdev, devno, 1);
+ err = cdev_device_add(&wd_data->cdev, &wd_data->dev);
if (err) {
pr_err("watchdog%d unable to add device %d:%d\n",
wdd->id, MAJOR(watchdog_devt), wdd->id);
if (wdd->id == 0) {
misc_deregister(&watchdog_miscdev);
old_wd_data = NULL;
- kref_put(&wd_data->kref, watchdog_core_data_release);
+ put_device(&wd_data->dev);
}
return err;
}
+ wd_data->cdev.owner = wdd->ops->owner;
+
/* Record time of most recent heartbeat as 'just before now'. */
wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1);
@@ -990,7 +1003,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
*/
if (watchdog_hw_running(wdd)) {
__module_get(wdd->ops->owner);
- kref_get(&wd_data->kref);
+ get_device(&wd_data->dev);
if (handle_boot_enabled)
hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL);
else
@@ -1013,34 +1026,28 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
- cdev_del(&wd_data->cdev);
+ cdev_device_del(&wd_data->cdev, &wd_data->dev);
if (wdd->id == 0) {
misc_deregister(&watchdog_miscdev);
old_wd_data = NULL;
}
+ if (watchdog_active(wdd) &&
+ test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
+ watchdog_stop(wdd);
+ }
+
mutex_lock(&wd_data->lock);
wd_data->wdd = NULL;
wdd->wd_data = NULL;
mutex_unlock(&wd_data->lock);
- if (watchdog_active(wdd) &&
- test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
- watchdog_stop(wdd);
- }
-
hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work);
- kref_put(&wd_data->kref, watchdog_core_data_release);
+ put_device(&wd_data->dev);
}
-static struct class watchdog_class = {
- .name = "watchdog",
- .owner = THIS_MODULE,
- .dev_groups = wdt_groups,
-};
-
static int watchdog_reboot_notifier(struct notifier_block *nb,
unsigned long code, void *data)
{
@@ -1071,27 +1078,14 @@ static int watchdog_reboot_notifier(struct notifier_block *nb,
int watchdog_dev_register(struct watchdog_device *wdd)
{
- struct device *dev;
- dev_t devno;
int ret;
- devno = MKDEV(MAJOR(watchdog_devt), wdd->id);
-
- ret = watchdog_cdev_register(wdd, devno);
+ ret = watchdog_cdev_register(wdd);
if (ret)
return ret;
- dev = device_create_with_groups(&watchdog_class, wdd->parent,
- devno, wdd, wdd->groups,
- "watchdog%d", wdd->id);
- if (IS_ERR(dev)) {
- watchdog_cdev_unregister(wdd);
- return PTR_ERR(dev);
- }
-
ret = watchdog_register_pretimeout(wdd);
if (ret) {
- device_destroy(&watchdog_class, devno);
watchdog_cdev_unregister(wdd);
return ret;
}
@@ -1099,7 +1093,8 @@ int watchdog_dev_register(struct watchdog_device *wdd)
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
- ret = devm_register_reboot_notifier(dev, &wdd->reboot_nb);
+ ret = devm_register_reboot_notifier(&wdd->wd_data->dev,
+ &wdd->reboot_nb);
if (ret) {
pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
wdd->id, ret);
@@ -1121,7 +1116,6 @@ int watchdog_dev_register(struct watchdog_device *wdd)
void watchdog_dev_unregister(struct watchdog_device *wdd)
{
watchdog_unregister_pretimeout(wdd);
- device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
watchdog_cdev_unregister(wdd);
}
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 90d387b..0505eeb 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -158,7 +158,8 @@
config XEN_GNTDEV_DMABUF
bool "Add support for dma-buf grant access device driver extension"
- depends on XEN_GNTDEV && XEN_GRANT_DMA_ALLOC && DMA_SHARED_BUFFER
+ depends on XEN_GNTDEV && XEN_GRANT_DMA_ALLOC
+ select DMA_SHARED_BUFFER
help
Allows userspace processes and kernel modules to use Xen backed
dma-buf implementation. With this extension grant references to
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index d4e8b717..6fa7209 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -350,7 +350,10 @@ static enum bp_state reserve_additional_memory(void)
* callers drop the mutex before trying again.
*/
mutex_unlock(&balloon_mutex);
+ /* add_memory_resource() requires the device_hotplug lock */
+ lock_device_hotplug();
rc = add_memory_resource(nid, resource, memhp_auto_online);
+ unlock_device_hotplug();
mutex_lock(&balloon_mutex);
if (rc) {
@@ -392,7 +395,8 @@ static struct notifier_block xen_memory_nb = {
#else
static enum bp_state reserve_additional_memory(void)
{
- balloon_stats.target_pages = balloon_stats.current_pages;
+ balloon_stats.target_pages = balloon_stats.current_pages +
+ balloon_stats.target_unpopulated;
return BP_ECANCELED;
}
#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c
index 3a144ee..d7438fd 100644
--- a/drivers/xen/pvcalls-front.c
+++ b/drivers/xen/pvcalls-front.c
@@ -504,8 +504,10 @@ static int __write_ring(struct pvcalls_data_intf *intf,
virt_mb();
size = pvcalls_queued(prod, cons, array_size);
- if (size >= array_size)
+ if (size > array_size)
return -EINVAL;
+ if (size == array_size)
+ return 0;
if (len > array_size - size)
len = array_size - size;
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 59661db..097410a 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -106,7 +106,8 @@ static void pcistub_device_release(struct kref *kref)
* is called from "unbind" which takes a device_lock mutex.
*/
__pci_reset_function_locked(dev);
- if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
+ if (dev_data &&
+ pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
dev_info(&dev->dev, "Could not reload PCI state\n");
else
pci_restore_state(dev);
diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
index f29c6da..069273a 100644
--- a/fs/afs/dynroot.c
+++ b/fs/afs/dynroot.c
@@ -145,6 +145,9 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
ASSERTCMP(d_inode(dentry), ==, NULL);
+ if (flags & LOOKUP_CREATE)
+ return ERR_PTR(-EOPNOTSUPP);
+
if (dentry->d_name.len >= AFSNAMEMAX) {
_leave(" = -ENAMETOOLONG");
return ERR_PTR(-ENAMETOOLONG);
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 1d329e6..2c7f621 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -34,18 +34,11 @@ static void afs_dec_servers_outstanding(struct afs_net *net)
struct afs_server *afs_find_server(struct afs_net *net,
const struct sockaddr_rxrpc *srx)
{
- const struct sockaddr_in6 *a = &srx->transport.sin6, *b;
const struct afs_addr_list *alist;
struct afs_server *server = NULL;
unsigned int i;
- bool ipv6 = true;
int seq = 0, diff;
- if (srx->transport.sin6.sin6_addr.s6_addr32[0] == 0 ||
- srx->transport.sin6.sin6_addr.s6_addr32[1] == 0 ||
- srx->transport.sin6.sin6_addr.s6_addr32[2] == htonl(0xffff))
- ipv6 = false;
-
rcu_read_lock();
do {
@@ -54,7 +47,8 @@ struct afs_server *afs_find_server(struct afs_net *net,
server = NULL;
read_seqbegin_or_lock(&net->fs_addr_lock, &seq);
- if (ipv6) {
+ if (srx->transport.family == AF_INET6) {
+ const struct sockaddr_in6 *a = &srx->transport.sin6, *b;
hlist_for_each_entry_rcu(server, &net->fs_addresses6, addr6_link) {
alist = rcu_dereference(server->addresses);
for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
@@ -70,15 +64,16 @@ struct afs_server *afs_find_server(struct afs_net *net,
}
}
} else {
+ const struct sockaddr_in *a = &srx->transport.sin, *b;
hlist_for_each_entry_rcu(server, &net->fs_addresses4, addr4_link) {
alist = rcu_dereference(server->addresses);
for (i = 0; i < alist->nr_ipv4; i++) {
- b = &alist->addrs[i].transport.sin6;
- diff = ((u16 __force)a->sin6_port -
- (u16 __force)b->sin6_port);
+ b = &alist->addrs[i].transport.sin;
+ diff = ((u16 __force)a->sin_port -
+ (u16 __force)b->sin_port);
if (diff == 0)
- diff = ((u32 __force)a->sin6_addr.s6_addr32[3] -
- (u32 __force)b->sin6_addr.s6_addr32[3]);
+ diff = ((u32 __force)a->sin_addr.s_addr -
+ (u32 __force)b->sin_addr.s_addr);
if (diff == 0)
goto found;
}
diff --git a/fs/afs/super.c b/fs/afs/super.c
index 4d3e274..bd26082 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -404,7 +404,6 @@ static int afs_fill_super(struct super_block *sb,
/* allocate the root inode and dentry */
if (as->dyn_root) {
inode = afs_iget_pseudo_dir(sb, true);
- sb->s_flags |= SB_RDONLY;
} else {
sprintf(sb->s_id, "%u", as->volume->vid);
afs_activate_volume(as->volume);
diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
index 28d9c2b..70e9afe 100644
--- a/fs/autofs/expire.c
+++ b/fs/autofs/expire.c
@@ -501,9 +501,10 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
*/
how &= ~AUTOFS_EXP_LEAVES;
found = should_expire(expired, mnt, timeout, how);
- if (!found || found != expired)
- /* Something has changed, continue */
+ if (found != expired) { // something has changed, continue
+ dput(found);
goto next;
+ }
if (expired != dentry)
dput(dentry);
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 7cde3f4..e996174 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -14,13 +14,30 @@
#include <linux/err.h>
#include <linux/fs.h>
+static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
+static inline char *next_non_spacetab(char *first, const char *last)
+{
+ for (; first <= last; first++)
+ if (!spacetab(*first))
+ return first;
+ return NULL;
+}
+static inline char *next_terminator(char *first, const char *last)
+{
+ for (; first <= last; first++)
+ if (spacetab(*first) || !*first)
+ return first;
+ return NULL;
+}
+
static int load_script(struct linux_binprm *bprm)
{
const char *i_arg, *i_name;
- char *cp;
+ char *cp, *buf_end;
struct file *file;
int retval;
+ /* Not ours to exec if we don't start with "#!". */
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
return -ENOEXEC;
@@ -33,18 +50,40 @@ static int load_script(struct linux_binprm *bprm)
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
return -ENOENT;
- /*
- * This section does the #! interpretation.
- * Sorta complicated, but hopefully it will work. -TYT
- */
-
+ /* Release since we are not mapping a binary into memory. */
allow_write_access(bprm->file);
fput(bprm->file);
bprm->file = NULL;
- bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
- if ((cp = strchr(bprm->buf, '\n')) == NULL)
- cp = bprm->buf+BINPRM_BUF_SIZE-1;
+ /*
+ * This section handles parsing the #! line into separate
+ * interpreter path and argument strings. We must be careful
+ * because bprm->buf is not yet guaranteed to be NUL-terminated
+ * (though the buffer will have trailing NUL padding when the
+ * file size was smaller than the buffer size).
+ *
+ * We do not want to exec a truncated interpreter path, so either
+ * we find a newline (which indicates nothing is truncated), or
+ * we find a space/tab/NUL after the interpreter path (which
+ * itself may be preceded by spaces/tabs). Truncating the
+ * arguments is fine: the interpreter can re-read the script to
+ * parse them on its own.
+ */
+ buf_end = bprm->buf + sizeof(bprm->buf) - 1;
+ cp = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
+ if (!cp) {
+ cp = next_non_spacetab(bprm->buf + 2, buf_end);
+ if (!cp)
+ return -ENOEXEC; /* Entire buf is spaces/tabs */
+ /*
+ * If there is no later space/tab/NUL we must assume the
+ * interpreter path is truncated.
+ */
+ if (!next_terminator(cp, buf_end))
+ return -ENOEXEC;
+ cp = buf_end;
+ }
+ /* NUL-terminate the buffer and any trailing spaces/tabs. */
*cp = '\0';
while (cp > bprm->buf) {
cp--;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1c25dae..c158bad 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1328,11 +1328,7 @@ static void flush_disk(struct block_device *bdev, bool kill_dirty)
"resized disk %s\n",
bdev->bd_disk ? bdev->bd_disk->disk_name : "");
}
-
- if (!bdev->bd_disk)
- return;
- if (disk_part_scan_enabled(bdev->bd_disk))
- bdev->bd_invalidated = 1;
+ bdev->bd_invalidated = 1;
}
/**
@@ -1430,6 +1426,19 @@ EXPORT_SYMBOL(bd_set_size);
static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
+static void bdev_disk_changed(struct block_device *bdev, bool invalidate)
+{
+ if (disk_part_scan_enabled(bdev->bd_disk)) {
+ if (invalidate)
+ invalidate_partitions(bdev->bd_disk, bdev);
+ else
+ rescan_partitions(bdev->bd_disk, bdev);
+ } else {
+ check_disk_size_change(bdev->bd_disk, bdev, !invalidate);
+ bdev->bd_invalidated = 0;
+ }
+}
+
/*
* bd_mutex locking:
*
@@ -1512,12 +1521,9 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
* The latter is necessary to prevent ghost
* partitions on a removed medium.
*/
- if (bdev->bd_invalidated) {
- if (!ret)
- rescan_partitions(disk, bdev);
- else if (ret == -ENOMEDIUM)
- invalidate_partitions(disk, bdev);
- }
+ if (bdev->bd_invalidated &&
+ (!ret || ret == -ENOMEDIUM))
+ bdev_disk_changed(bdev, ret == -ENOMEDIUM);
if (ret)
goto out_clear;
@@ -1550,12 +1556,9 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
if (bdev->bd_disk->fops->open)
ret = bdev->bd_disk->fops->open(bdev, mode);
/* the same as first opener case, read comment there */
- if (bdev->bd_invalidated) {
- if (!ret)
- rescan_partitions(bdev->bd_disk, bdev);
- else if (ret == -ENOMEDIUM)
- invalidate_partitions(bdev->bd_disk, bdev);
- }
+ if (bdev->bd_invalidated &&
+ (!ret || ret == -ENOMEDIUM))
+ bdev_disk_changed(bdev, ret == -ENOMEDIUM);
if (ret)
goto out_unlock_bdev;
}
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
index d522494..02e4e903 100644
--- a/fs/btrfs/async-thread.c
+++ b/fs/btrfs/async-thread.c
@@ -252,16 +252,17 @@ static inline void thresh_exec_hook(struct __btrfs_workqueue *wq)
}
}
-static void run_ordered_work(struct __btrfs_workqueue *wq)
+static void run_ordered_work(struct __btrfs_workqueue *wq,
+ struct btrfs_work *self)
{
struct list_head *list = &wq->ordered_list;
struct btrfs_work *work;
spinlock_t *lock = &wq->list_lock;
unsigned long flags;
+ void *wtag;
+ bool free_self = false;
while (1) {
- void *wtag;
-
spin_lock_irqsave(lock, flags);
if (list_empty(list))
break;
@@ -287,16 +288,47 @@ static void run_ordered_work(struct __btrfs_workqueue *wq)
list_del(&work->ordered_list);
spin_unlock_irqrestore(lock, flags);
- /*
- * We don't want to call the ordered free functions with the
- * lock held though. Save the work as tag for the trace event,
- * because the callback could free the structure.
- */
- wtag = work;
- work->ordered_free(work);
- trace_btrfs_all_work_done(wq->fs_info, wtag);
+ if (work == self) {
+ /*
+ * This is the work item that the worker is currently
+ * executing.
+ *
+ * The kernel workqueue code guarantees non-reentrancy
+ * of work items. I.e., if a work item with the same
+ * address and work function is queued twice, the second
+ * execution is blocked until the first one finishes. A
+ * work item may be freed and recycled with the same
+ * work function; the workqueue code assumes that the
+ * original work item cannot depend on the recycled work
+ * item in that case (see find_worker_executing_work()).
+ *
+ * Note that the work of one Btrfs filesystem may depend
+ * on the work of another Btrfs filesystem via, e.g., a
+ * loop device. Therefore, we must not allow the current
+ * work item to be recycled until we are really done,
+ * otherwise we break the above assumption and can
+ * deadlock.
+ */
+ free_self = true;
+ } else {
+ /*
+ * We don't want to call the ordered free functions with
+ * the lock held though. Save the work as tag for the
+ * trace event, because the callback could free the
+ * structure.
+ */
+ wtag = work;
+ work->ordered_free(work);
+ trace_btrfs_all_work_done(wq->fs_info, wtag);
+ }
}
spin_unlock_irqrestore(lock, flags);
+
+ if (free_self) {
+ wtag = self;
+ self->ordered_free(self);
+ trace_btrfs_all_work_done(wq->fs_info, wtag);
+ }
}
static void normal_work_helper(struct btrfs_work *work)
@@ -324,7 +356,7 @@ static void normal_work_helper(struct btrfs_work *work)
work->func(work);
if (need_order) {
set_bit(WORK_DONE_BIT, &work->flags);
- run_ordered_work(wq);
+ run_ordered_work(wq, work);
}
if (!need_order)
trace_btrfs_all_work_done(wq->fs_info, wtag);
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 9fd3832..84ff398 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -390,7 +390,7 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
for (node = rb_first(tm_root); node; node = next) {
next = rb_next(node);
tm = rb_entry(node, struct tree_mod_elem, node);
- if (tm->seq > min_seq)
+ if (tm->seq >= min_seq)
continue;
rb_erase(node, tm_root);
kfree(tm);
@@ -3031,6 +3031,10 @@ int btrfs_search_old_slot(struct btrfs_root *root, const struct btrfs_key *key,
again:
b = get_old_root(root, time_seq);
+ if (!b) {
+ ret = -EIO;
+ goto done;
+ }
level = btrfs_header_level(b);
p->locks[level] = BTRFS_READ_LOCK;
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index faca485..d24ecbf 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2747,8 +2747,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
int nitems, bool use_global_rsv);
void btrfs_subvolume_release_metadata(struct btrfs_fs_info *fs_info,
struct btrfs_block_rsv *rsv);
-void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes,
- bool qgroup_free);
+void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes);
int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes);
void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes,
@@ -3102,7 +3101,7 @@ int btrfs_find_name_in_ext_backref(struct extent_buffer *leaf, int slot,
/* file-item.c */
struct btrfs_dio_private;
int btrfs_del_csums(struct btrfs_trans_handle *trans,
- struct btrfs_fs_info *fs_info, u64 bytenr, u64 len);
+ struct btrfs_root *root, u64 bytenr, u64 len);
blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u32 *dst);
blk_status_t btrfs_lookup_bio_sums_dio(struct inode *inode, struct bio *bio,
u64 logical_offset);
@@ -3178,6 +3177,9 @@ void btrfs_destroy_inode(struct inode *inode);
int btrfs_drop_inode(struct inode *inode);
int __init btrfs_init_cachep(void);
void __cold btrfs_destroy_cachep(void);
+struct inode *btrfs_iget_path(struct super_block *s, struct btrfs_key *location,
+ struct btrfs_root *root, int *new,
+ struct btrfs_path *path);
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *was_new);
struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index f51b509..e9522f2 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1939,12 +1939,19 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
}
inode_id = delayed_nodes[n - 1]->inode_id + 1;
-
- for (i = 0; i < n; i++)
- refcount_inc(&delayed_nodes[i]->refs);
+ for (i = 0; i < n; i++) {
+ /*
+ * Don't increase refs in case the node is dead and
+ * about to be removed from the tree in the loop below
+ */
+ if (!refcount_inc_not_zero(&delayed_nodes[i]->refs))
+ delayed_nodes[i] = NULL;
+ }
spin_unlock(&root->inode_lock);
for (i = 0; i < n; i++) {
+ if (!delayed_nodes[i])
+ continue;
__btrfs_kill_delayed_node(delayed_nodes[i]);
btrfs_release_delayed_node(delayed_nodes[i]);
}
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 62ff545..7e5c81e 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -234,8 +234,6 @@ static inline void drop_delayed_ref(struct btrfs_trans_handle *trans,
ref->in_tree = 0;
btrfs_put_delayed_ref(ref);
atomic_dec(&delayed_refs->num_entries);
- if (trans->delayed_ref_updates)
- trans->delayed_ref_updates--;
}
static bool merge_ref(struct btrfs_trans_handle *trans,
@@ -446,7 +444,6 @@ static int insert_delayed_ref(struct btrfs_trans_handle *trans,
if (ref->action == BTRFS_ADD_DELAYED_REF)
list_add_tail(&ref->add_list, &href->ref_add_list);
atomic_inc(&root->num_entries);
- trans->delayed_ref_updates++;
spin_unlock(&href->lock);
return ret;
}
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index 23b13fb..9676380 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -810,16 +810,23 @@ int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)
btrfs_dev_replace_write_unlock(dev_replace);
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
- result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
tgt_device = dev_replace->tgtdev;
src_device = dev_replace->srcdev;
btrfs_dev_replace_write_unlock(dev_replace);
- btrfs_scrub_cancel(fs_info);
- /* btrfs_dev_replace_finishing() will handle the cleanup part */
- btrfs_info_in_rcu(fs_info,
- "dev_replace from %s (devid %llu) to %s canceled",
- btrfs_dev_name(src_device), src_device->devid,
- btrfs_dev_name(tgt_device));
+ ret = btrfs_scrub_cancel(fs_info);
+ if (ret < 0) {
+ result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED;
+ } else {
+ result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
+ /*
+ * btrfs_dev_replace_finishing() will handle the
+ * cleanup part
+ */
+ btrfs_info_in_rcu(fs_info,
+ "dev_replace from %s (devid %llu) to %s canceled",
+ btrfs_dev_name(src_device), src_device->devid,
+ btrfs_dev_name(tgt_device));
+ }
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
/*
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index b2dc613..e12c37f 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1660,8 +1660,8 @@ static void end_workqueue_fn(struct btrfs_work *work)
bio->bi_status = end_io_wq->status;
bio->bi_private = end_io_wq->private;
bio->bi_end_io = end_io_wq->end_io;
- kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
bio_endio(bio);
+ kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
}
static int cleaner_kthread(void *arg)
@@ -4350,6 +4350,8 @@ static int btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info,
unpin = pinned_extents;
again:
while (1) {
+ struct extent_state *cached_state = NULL;
+
/*
* The btrfs_finish_extent_commit() may get the same range as
* ours between find_first_extent_bit and clear_extent_dirty.
@@ -4358,13 +4360,14 @@ static int btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info,
*/
mutex_lock(&fs_info->unused_bg_unpin_mutex);
ret = find_first_extent_bit(unpin, 0, &start, &end,
- EXTENT_DIRTY, NULL);
+ EXTENT_DIRTY, &cached_state);
if (ret) {
mutex_unlock(&fs_info->unused_bg_unpin_mutex);
break;
}
- clear_extent_dirty(unpin, start, end);
+ clear_extent_dirty(unpin, start, end, &cached_state);
+ free_extent_state(cached_state);
btrfs_error_unpin_extent_range(fs_info, start, end);
mutex_unlock(&fs_info->unused_bg_unpin_mutex);
cond_resched();
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 72c7456..47ca1eb 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2492,8 +2492,8 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans,
btrfs_pin_extent(fs_info, head->bytenr,
head->num_bytes, 1);
if (head->is_data) {
- ret = btrfs_del_csums(trans, fs_info, head->bytenr,
- head->num_bytes);
+ ret = btrfs_del_csums(trans, fs_info->csum_root,
+ head->bytenr, head->num_bytes);
}
}
@@ -5980,8 +5980,7 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes,
* temporarily tracked outstanding_extents. This _must_ be used in conjunction
* with btrfs_delalloc_reserve_metadata.
*/
-void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes,
- bool qgroup_free)
+void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
unsigned num_extents;
@@ -5995,7 +5994,7 @@ void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes,
if (btrfs_is_testing(fs_info))
return;
- btrfs_inode_rsv_release(inode, qgroup_free);
+ btrfs_inode_rsv_release(inode, true);
}
/**
@@ -6619,9 +6618,11 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
unpin = &fs_info->freed_extents[0];
while (!trans->aborted) {
+ struct extent_state *cached_state = NULL;
+
mutex_lock(&fs_info->unused_bg_unpin_mutex);
ret = find_first_extent_bit(unpin, 0, &start, &end,
- EXTENT_DIRTY, NULL);
+ EXTENT_DIRTY, &cached_state);
if (ret) {
mutex_unlock(&fs_info->unused_bg_unpin_mutex);
break;
@@ -6631,9 +6632,10 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
ret = btrfs_discard_extent(fs_info, start,
end + 1 - start, NULL);
- clear_extent_dirty(unpin, start, end);
+ clear_extent_dirty(unpin, start, end, &cached_state);
unpin_extent_range(fs_info, start, end, true);
mutex_unlock(&fs_info->unused_bg_unpin_mutex);
+ free_extent_state(cached_state);
cond_resched();
}
@@ -6878,7 +6880,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
btrfs_release_path(path);
if (is_data) {
- ret = btrfs_del_csums(trans, info, bytenr, num_bytes);
+ ret = btrfs_del_csums(trans, info->csum_root, bytenr,
+ num_bytes);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index cb598eb..fed4439 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -3956,7 +3956,7 @@ static int extent_write_cache_pages(struct address_space *mapping,
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i];
- done_index = page->index;
+ done_index = page->index + 1;
/*
* At this point we hold neither the i_pages lock nor
* the page lock: the page may be truncated or
@@ -3993,16 +3993,6 @@ static int extent_write_cache_pages(struct address_space *mapping,
ret = 0;
}
if (ret < 0) {
- /*
- * done_index is set past this page,
- * so media errors will not choke
- * background writeout for the entire
- * file. This has consequences for
- * range_cyclic semantics (ie. it may
- * not be suitable for data integrity
- * writeout).
- */
- done_index = page->index + 1;
done = 1;
break;
}
@@ -4898,12 +4888,14 @@ struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info,
return eb;
eb = alloc_dummy_extent_buffer(fs_info, start);
if (!eb)
- return NULL;
+ return ERR_PTR(-ENOMEM);
eb->fs_info = fs_info;
again:
ret = radix_tree_preload(GFP_NOFS);
- if (ret)
+ if (ret) {
+ exists = ERR_PTR(ret);
goto free_eb;
+ }
spin_lock(&fs_info->buffer_lock);
ret = radix_tree_insert(&fs_info->buffer_radix,
start >> PAGE_SHIFT, eb);
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index ed27bec..a3598b2 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -348,11 +348,11 @@ static inline int set_extent_dirty(struct extent_io_tree *tree, u64 start,
}
static inline int clear_extent_dirty(struct extent_io_tree *tree, u64 start,
- u64 end)
+ u64 end, struct extent_state **cached)
{
return clear_extent_bit(tree, start, end,
EXTENT_DIRTY | EXTENT_DELALLOC |
- EXTENT_DO_ACCOUNTING, 0, 0, NULL);
+ EXTENT_DO_ACCOUNTING, 0, 0, cached);
}
int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ba74827..4cf2817 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -577,9 +577,9 @@ static noinline void truncate_one_csum(struct btrfs_fs_info *fs_info,
* range of bytes.
*/
int btrfs_del_csums(struct btrfs_trans_handle *trans,
- struct btrfs_fs_info *fs_info, u64 bytenr, u64 len)
+ struct btrfs_root *root, u64 bytenr, u64 len)
{
- struct btrfs_root *root = fs_info->csum_root;
+ struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_path *path;
struct btrfs_key key;
u64 end_byte = bytenr + len;
@@ -589,6 +589,9 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
int blocksize_bits = fs_info->sb->s_blocksize_bits;
+ ASSERT(root == fs_info->csum_root ||
+ root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID);
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 4870440d..d9d90f0 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1591,7 +1591,6 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
struct btrfs_root *root = BTRFS_I(inode)->root;
struct page **pages = NULL;
- struct extent_state *cached_state = NULL;
struct extent_changeset *data_reserved = NULL;
u64 release_bytes = 0;
u64 lockstart;
@@ -1612,6 +1611,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
while (iov_iter_count(i) > 0) {
size_t offset = pos & (PAGE_SIZE - 1);
+ struct extent_state *cached_state = NULL;
size_t sector_offset;
size_t write_bytes = min(iov_iter_count(i),
nrptrs * (size_t)PAGE_SIZE -
@@ -1636,6 +1636,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
break;
}
+ only_release_metadata = false;
sector_offset = pos & (fs_info->sectorsize - 1);
reserve_bytes = round_up(write_bytes + sector_offset,
fs_info->sectorsize);
@@ -1692,7 +1693,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
force_page_uptodate);
if (ret) {
btrfs_delalloc_release_extents(BTRFS_I(inode),
- reserve_bytes, true);
+ reserve_bytes);
break;
}
@@ -1704,7 +1705,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
if (extents_locked == -EAGAIN)
goto again;
btrfs_delalloc_release_extents(BTRFS_I(inode),
- reserve_bytes, true);
+ reserve_bytes);
ret = extents_locked;
break;
}
@@ -1758,11 +1759,21 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
if (copied > 0)
ret = btrfs_dirty_pages(inode, pages, dirty_pages,
pos, copied, &cached_state);
+
+ /*
+ * If we have not locked the extent range, because the range's
+ * start offset is >= i_size, we might still have a non-NULL
+ * cached extent state, acquired while marking the extent range
+ * as delalloc through btrfs_dirty_pages(). Therefore free any
+ * possible cached extent state to avoid a memory leak.
+ */
if (extents_locked)
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
lockstart, lockend, &cached_state);
- btrfs_delalloc_release_extents(BTRFS_I(inode), reserve_bytes,
- true);
+ else
+ free_extent_state(cached_state);
+
+ btrfs_delalloc_release_extents(BTRFS_I(inode), reserve_bytes);
if (ret) {
btrfs_drop_pages(pages, num_pages);
break;
@@ -1781,7 +1792,6 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
lockend, EXTENT_NORESERVE, NULL,
NULL, GFP_NOFS);
- only_release_metadata = false;
}
btrfs_drop_pages(pages, num_pages);
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 4381e0a..c9965e8 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -75,7 +75,8 @@ static struct inode *__lookup_free_space_inode(struct btrfs_root *root,
* sure NOFS is set to keep us from deadlocking.
*/
nofs_flag = memalloc_nofs_save();
- inode = btrfs_iget(fs_info->sb, &location, root, NULL);
+ inode = btrfs_iget_path(fs_info->sb, &location, root, NULL, path);
+ btrfs_release_path(path);
memalloc_nofs_restore(nofs_flag);
if (IS_ERR(inode))
return inode;
@@ -381,6 +382,12 @@ static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, struct inode *inode
if (uptodate && !PageUptodate(page)) {
btrfs_readpage(NULL, page);
lock_page(page);
+ if (page->mapping != inode->i_mapping) {
+ btrfs_err(BTRFS_I(inode)->root->fs_info,
+ "free space cache page truncated");
+ io_ctl_drop_pages(io_ctl);
+ return -EIO;
+ }
if (!PageUptodate(page)) {
btrfs_err(BTRFS_I(inode)->root->fs_info,
"error reading free space cache");
@@ -839,6 +846,25 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
path->search_commit_root = 1;
path->skip_locking = 1;
+ /*
+ * We must pass a path with search_commit_root set to btrfs_iget in
+ * order to avoid a deadlock when allocating extents for the tree root.
+ *
+ * When we are COWing an extent buffer from the tree root, when looking
+ * for a free extent, at extent-tree.c:find_free_extent(), we can find
+ * block group without its free space cache loaded. When we find one
+ * we must load its space cache which requires reading its free space
+ * cache's inode item from the root tree. If this inode item is located
+ * in the same leaf that we started COWing before, then we end up in
+ * deadlock on the extent buffer (trying to read lock it when we
+ * previously write locked it).
+ *
+ * It's safe to read the inode item using the commit root because
+ * block groups, once loaded, stay in memory forever (until they are
+ * removed) as well as their space caches once loaded. New block groups
+ * once created get their ->cached field set to BTRFS_CACHE_FINISHED so
+ * we will never try to read their inode item while the fs is mounted.
+ */
inode = lookup_free_space_inode(fs_info, block_group, path);
if (IS_ERR(inode)) {
btrfs_free_path(path);
diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c
index ffca2ab..e1b50c6 100644
--- a/fs/btrfs/inode-map.c
+++ b/fs/btrfs/inode-map.c
@@ -483,12 +483,13 @@ int btrfs_save_ino_cache(struct btrfs_root *root,
ret = btrfs_prealloc_file_range_trans(inode, trans, 0, 0, prealloc,
prealloc, prealloc, &alloc_hint);
if (ret) {
- btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, true);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc);
+ btrfs_delalloc_release_metadata(BTRFS_I(inode), prealloc, true);
goto out_put;
}
ret = btrfs_write_out_ino_cache(root, trans, path, inode);
- btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, false);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc);
out_put:
iput(inode);
out_release:
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 37332f8..70085e9 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2161,12 +2161,16 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
mapping_set_error(page->mapping, ret);
end_extent_writepage(page, ret, page_start, page_end);
ClearPageChecked(page);
- goto out;
+ goto out_reserved;
}
ClearPageChecked(page);
set_page_dirty(page);
- btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, false);
+out_reserved:
+ btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE);
+ if (ret)
+ btrfs_delalloc_release_space(inode, data_reserved, page_start,
+ PAGE_SIZE, true);
out:
unlock_extent_cached(&BTRFS_I(inode)->io_tree, page_start, page_end,
&cached_state);
@@ -3607,10 +3611,11 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/*
* read an inode from the btree into the in-memory inode
*/
-static int btrfs_read_locked_inode(struct inode *inode)
+static int btrfs_read_locked_inode(struct inode *inode,
+ struct btrfs_path *in_path)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
- struct btrfs_path *path;
+ struct btrfs_path *path = in_path;
struct extent_buffer *leaf;
struct btrfs_inode_item *inode_item;
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -3626,15 +3631,18 @@ static int btrfs_read_locked_inode(struct inode *inode)
if (!ret)
filled = true;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ }
memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
ret = btrfs_lookup_inode(NULL, root, path, &location, 0);
if (ret) {
- btrfs_free_path(path);
+ if (path != in_path)
+ btrfs_free_path(path);
return ret;
}
@@ -3774,7 +3782,8 @@ static int btrfs_read_locked_inode(struct inode *inode)
btrfs_ino(BTRFS_I(inode)),
root->root_key.objectid, ret);
}
- btrfs_free_path(path);
+ if (path != in_path)
+ btrfs_free_path(path);
if (!maybe_acls)
cache_no_acl(inode);
@@ -4918,7 +4927,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
if (!page) {
btrfs_delalloc_release_space(inode, data_reserved,
block_start, blocksize, true);
- btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, true);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize);
ret = -ENOMEM;
goto out;
}
@@ -4986,7 +4995,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
if (ret)
btrfs_delalloc_release_space(inode, data_reserved, block_start,
blocksize, true);
- btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, (ret != 0));
+ btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize);
unlock_page(page);
put_page(page);
out:
@@ -5656,7 +5665,6 @@ static void inode_tree_add(struct inode *inode)
static void inode_tree_del(struct inode *inode)
{
- struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
struct btrfs_root *root = BTRFS_I(inode)->root;
int empty = 0;
@@ -5669,7 +5677,6 @@ static void inode_tree_del(struct inode *inode)
spin_unlock(&root->inode_lock);
if (empty && btrfs_root_refs(&root->root_item) == 0) {
- synchronize_srcu(&fs_info->subvol_srcu);
spin_lock(&root->inode_lock);
empty = RB_EMPTY_ROOT(&root->inode_tree);
spin_unlock(&root->inode_lock);
@@ -5716,8 +5723,9 @@ static struct inode *btrfs_iget_locked(struct super_block *s,
/* Get an inode object given its location and corresponding root.
* Returns in *is_new if the inode was read from disk
*/
-struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
- struct btrfs_root *root, int *new)
+struct inode *btrfs_iget_path(struct super_block *s, struct btrfs_key *location,
+ struct btrfs_root *root, int *new,
+ struct btrfs_path *path)
{
struct inode *inode;
@@ -5728,7 +5736,7 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
if (inode->i_state & I_NEW) {
int ret;
- ret = btrfs_read_locked_inode(inode);
+ ret = btrfs_read_locked_inode(inode, path);
if (!ret) {
inode_tree_add(inode);
unlock_new_inode(inode);
@@ -5750,6 +5758,12 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
return inode;
}
+struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
+ struct btrfs_root *root, int *new)
+{
+ return btrfs_iget_path(s, location, root, new, NULL);
+}
+
static struct inode *new_simple_dir(struct super_block *s,
struct btrfs_key *key,
struct btrfs_root *root)
@@ -8660,7 +8674,7 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
} else if (ret >= 0 && (size_t)ret < count)
btrfs_delalloc_release_space(inode, data_reserved,
offset, count - (size_t)ret, true);
- btrfs_delalloc_release_extents(BTRFS_I(inode), count, false);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), count);
}
out:
if (wakeup)
@@ -9013,7 +9027,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
unlock_extent_cached(io_tree, page_start, page_end, &cached_state);
if (!ret2) {
- btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, true);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE);
sb_end_pagefault(inode->i_sb);
extent_changeset_free(data_reserved);
return VM_FAULT_LOCKED;
@@ -9022,7 +9036,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
out_unlock:
unlock_page(page);
out:
- btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, (ret != 0));
+ btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE);
btrfs_delalloc_release_space(inode, data_reserved, page_start,
reserved_space, (ret != 0));
out_noreserve:
@@ -9475,9 +9489,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
btrfs_init_log_ctx(&ctx_dest, new_inode);
/* close the race window with snapshot create/destroy ioctl */
- if (old_ino == BTRFS_FIRST_FREE_OBJECTID)
- down_read(&fs_info->subvol_sem);
- if (new_ino == BTRFS_FIRST_FREE_OBJECTID)
+ if (old_ino == BTRFS_FIRST_FREE_OBJECTID ||
+ new_ino == BTRFS_FIRST_FREE_OBJECTID)
down_read(&fs_info->subvol_sem);
/*
@@ -9494,6 +9507,9 @@ static int btrfs_rename_exchange(struct inode *old_dir,
goto out_notrans;
}
+ if (dest != root)
+ btrfs_record_root_in_trans(trans, dest);
+
/*
* We need to find a free sequence number both in the source and
* in the destination directory for the exchange.
@@ -9688,6 +9704,18 @@ static int btrfs_rename_exchange(struct inode *old_dir,
commit_transaction = true;
}
if (commit_transaction) {
+ /*
+ * We may have set commit_transaction when logging the new name
+ * in the destination root, in which case we left the source
+ * root context in the list of log contextes. So make sure we
+ * remove it to avoid invalid memory accesses, since the context
+ * was allocated in our stack frame.
+ */
+ if (sync_log_root) {
+ mutex_lock(&root->log_mutex);
+ list_del_init(&ctx_root.list);
+ mutex_unlock(&root->log_mutex);
+ }
ret = btrfs_commit_transaction(trans);
} else {
int ret2;
@@ -9696,10 +9724,12 @@ static int btrfs_rename_exchange(struct inode *old_dir,
ret = ret ? ret : ret2;
}
out_notrans:
- if (new_ino == BTRFS_FIRST_FREE_OBJECTID)
+ if (new_ino == BTRFS_FIRST_FREE_OBJECTID ||
+ old_ino == BTRFS_FIRST_FREE_OBJECTID)
up_read(&fs_info->subvol_sem);
- if (old_ino == BTRFS_FIRST_FREE_OBJECTID)
- up_read(&fs_info->subvol_sem);
+
+ ASSERT(list_empty(&ctx_root.list));
+ ASSERT(list_empty(&ctx_dest.list));
return ret;
}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0eb333c..199c70b 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -709,11 +709,17 @@ static noinline int create_subvol(struct inode *dir,
btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2);
ret = btrfs_update_inode(trans, root, dir);
- BUG_ON(ret);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+ }
ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid,
btrfs_ino(BTRFS_I(dir)), index, name, namelen);
- BUG_ON(ret);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+ }
ret = btrfs_uuid_tree_add(trans, root_item->uuid,
BTRFS_UUID_KEY_SUBVOL, objectid);
@@ -1337,7 +1343,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
if (i_done != page_cnt) {
spin_lock(&BTRFS_I(inode)->lock);
- BTRFS_I(inode)->outstanding_extents++;
+ btrfs_mod_outstanding_extents(BTRFS_I(inode), 1);
spin_unlock(&BTRFS_I(inode)->lock);
btrfs_delalloc_release_space(inode, data_reserved,
start_index << PAGE_SHIFT,
@@ -1359,8 +1365,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
unlock_page(pages[i]);
put_page(pages[i]);
}
- btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT,
- false);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT);
extent_changeset_free(data_reserved);
return i_done;
out:
@@ -1371,8 +1376,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
btrfs_delalloc_release_space(inode, data_reserved,
start_index << PAGE_SHIFT,
page_cnt << PAGE_SHIFT, true);
- btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT,
- true);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT);
extent_changeset_free(data_reserved);
return ret;
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 3ea2008..7916f71 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2862,12 +2862,12 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
if (!(fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAG_RESCAN)) {
btrfs_warn(fs_info,
- "qgroup rescan init failed, qgroup is not enabled");
+ "qgroup rescan init failed, qgroup rescan is not queued");
ret = -EINVAL;
} else if (!(fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAG_ON)) {
btrfs_warn(fs_info,
- "qgroup rescan init failed, qgroup rescan is not queued");
+ "qgroup rescan init failed, qgroup is not enabled");
ret = -EINVAL;
}
@@ -3259,7 +3259,7 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
return 0;
BUG_ON(num_bytes != round_down(num_bytes, fs_info->nodesize));
- trace_qgroup_meta_reserve(root, type, (s64)num_bytes);
+ trace_qgroup_meta_reserve(root, (s64)num_bytes, type);
ret = qgroup_reserve(root, num_bytes, enforce, type);
if (ret < 0)
return ret;
@@ -3306,7 +3306,7 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes,
*/
num_bytes = sub_root_meta_rsv(root, num_bytes, type);
BUG_ON(num_bytes != round_down(num_bytes, fs_info->nodesize));
- trace_qgroup_meta_reserve(root, type, -(s64)num_bytes);
+ trace_qgroup_meta_reserve(root, -(s64)num_bytes, type);
btrfs_qgroup_free_refroot(fs_info, root->objectid, num_bytes, type);
}
diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c
index 859274e..4c81ffe 100644
--- a/fs/btrfs/reada.c
+++ b/fs/btrfs/reada.c
@@ -720,21 +720,19 @@ static int reada_start_machine_dev(struct btrfs_device *dev)
static void reada_start_machine_worker(struct btrfs_work *work)
{
struct reada_machine_work *rmw;
- struct btrfs_fs_info *fs_info;
int old_ioprio;
rmw = container_of(work, struct reada_machine_work, work);
- fs_info = rmw->fs_info;
-
- kfree(rmw);
old_ioprio = IOPRIO_PRIO_VALUE(task_nice_ioclass(current),
task_nice_ioprio(current));
set_task_ioprio(current, BTRFS_IOPRIO_READA);
- __reada_start_machine(fs_info);
+ __reada_start_machine(rmw->fs_info);
set_task_ioprio(current, old_ioprio);
- atomic_dec(&fs_info->reada_works_cnt);
+ atomic_dec(&rmw->fs_info->reada_works_cnt);
+
+ kfree(rmw);
}
static void __reada_start_machine(struct btrfs_fs_info *fs_info)
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index bccd9de..f989130 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3188,7 +3188,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
btrfs_delalloc_release_metadata(BTRFS_I(inode),
PAGE_SIZE, true);
btrfs_delalloc_release_extents(BTRFS_I(inode),
- PAGE_SIZE, true);
+ PAGE_SIZE);
ret = -ENOMEM;
goto out;
}
@@ -3209,7 +3209,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
btrfs_delalloc_release_metadata(BTRFS_I(inode),
PAGE_SIZE, true);
btrfs_delalloc_release_extents(BTRFS_I(inode),
- PAGE_SIZE, true);
+ PAGE_SIZE);
ret = -EIO;
goto out;
}
@@ -3238,7 +3238,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
btrfs_delalloc_release_metadata(BTRFS_I(inode),
PAGE_SIZE, true);
btrfs_delalloc_release_extents(BTRFS_I(inode),
- PAGE_SIZE, true);
+ PAGE_SIZE);
clear_extent_bits(&BTRFS_I(inode)->io_tree,
page_start, page_end,
@@ -3254,8 +3254,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
put_page(page);
index++;
- btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE,
- false);
+ btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE);
balance_dirty_pages_ratelimited(inode->i_mapping);
btrfs_throttle(fs_info);
}
@@ -4475,6 +4474,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
fs_root = read_fs_root(fs_info, reloc_root->root_key.offset);
if (IS_ERR(fs_root)) {
err = PTR_ERR(fs_root);
+ list_add_tail(&reloc_root->root_list, &reloc_roots);
goto out_free;
}
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 916c397..6b6008d 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -2145,14 +2145,13 @@ static void scrub_missing_raid56_worker(struct btrfs_work *work)
scrub_write_block_to_dev_replace(sblock);
}
- scrub_block_put(sblock);
-
if (sctx->is_dev_replace && sctx->flush_all_writes) {
mutex_lock(&sctx->wr_lock);
scrub_wr_submit(sctx);
mutex_unlock(&sctx->wr_lock);
}
+ scrub_block_put(sblock);
scrub_pending_bio_dec(sctx);
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 48ddbc1..931a7d1 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -25,6 +25,14 @@
#include "compression.h"
/*
+ * Maximum number of references an extent can have in order for us to attempt to
+ * issue clone operations instead of write operations. This currently exists to
+ * avoid hitting limitations of the backreference walking code (taking a lot of
+ * time and using too much memory for extents with large number of references).
+ */
+#define SEND_MAX_EXTENT_REFS 64
+
+/*
* A fs_path is a helper to dynamically build path names with unknown size.
* It reallocates the internal buffer on demand.
* It allows fast adding of path elements on the right side (normal path) and
@@ -1303,6 +1311,7 @@ static int find_extent_clone(struct send_ctx *sctx,
struct clone_root *cur_clone_root;
struct btrfs_key found_key;
struct btrfs_path *tmp_path;
+ struct btrfs_extent_item *ei;
int compressed;
u32 i;
@@ -1352,7 +1361,6 @@ static int find_extent_clone(struct send_ctx *sctx,
ret = extent_from_logical(fs_info, disk_byte, tmp_path,
&found_key, &flags);
up_read(&fs_info->commit_root_sem);
- btrfs_release_path(tmp_path);
if (ret < 0)
goto out;
@@ -1361,6 +1369,21 @@ static int find_extent_clone(struct send_ctx *sctx,
goto out;
}
+ ei = btrfs_item_ptr(tmp_path->nodes[0], tmp_path->slots[0],
+ struct btrfs_extent_item);
+ /*
+ * Backreference walking (iterate_extent_inodes() below) is currently
+ * too expensive when an extent has a large number of references, both
+ * in time spent and used memory. So for now just fallback to write
+ * operations instead of clone operations when an extent has more than
+ * a certain amount of references.
+ */
+ if (btrfs_extent_refs(tmp_path->nodes[0], ei) > SEND_MAX_EXTENT_REFS) {
+ ret = -ENOENT;
+ goto out;
+ }
+ btrfs_release_path(tmp_path);
+
/*
* Setup the clone roots.
*/
@@ -6616,12 +6639,6 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
spin_unlock(&send_root->root_item_lock);
/*
- * This is done when we lookup the root, it should already be complete
- * by the time we get here.
- */
- WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE);
-
- /*
* Userspace tools do the checks and warn the user if it's
* not RO.
*/
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 8888337..ddbad8d5 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1919,7 +1919,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
}
/* Used to sort the devices by max_avail(descending sort) */
-static int btrfs_cmp_device_free_bytes(const void *dev_info1,
+static inline int btrfs_cmp_device_free_bytes(const void *dev_info1,
const void *dev_info2)
{
if (((struct btrfs_device_info *)dev_info1)->max_avail >
@@ -1948,8 +1948,8 @@ static inline void btrfs_descending_sort_devices(
* The helper to calc the free space on the devices that can be used to store
* file data.
*/
-static int btrfs_calc_avail_data_space(struct btrfs_fs_info *fs_info,
- u64 *free_bytes)
+static inline int btrfs_calc_avail_data_space(struct btrfs_fs_info *fs_info,
+ u64 *free_bytes)
{
struct btrfs_device_info *devices_info;
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c
index 89346da..de8fef9 100644
--- a/fs/btrfs/tests/free-space-tree-tests.c
+++ b/fs/btrfs/tests/free-space-tree-tests.c
@@ -462,9 +462,9 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
root->fs_info->tree_root = root;
root->node = alloc_test_extent_buffer(root->fs_info, nodesize);
- if (!root->node) {
+ if (IS_ERR(root->node)) {
test_err("couldn't allocate dummy buffer");
- ret = -ENOMEM;
+ ret = PTR_ERR(root->node);
goto out;
}
btrfs_set_header_level(root->node, 0);
diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c
index 412b910..d07dd26 100644
--- a/fs/btrfs/tests/qgroup-tests.c
+++ b/fs/btrfs/tests/qgroup-tests.c
@@ -484,9 +484,9 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize)
* *cough*backref walking code*cough*
*/
root->node = alloc_test_extent_buffer(root->fs_info, nodesize);
- if (!root->node) {
+ if (IS_ERR(root->node)) {
test_err("couldn't allocate dummy buffer");
- ret = -ENOMEM;
+ ret = PTR_ERR(root->node);
goto out;
}
btrfs_set_header_level(root->node, 0);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 4d4f57f..fe7165c 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -795,7 +795,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
struct btrfs_ordered_sum,
list);
if (!ret)
- ret = btrfs_del_csums(trans, fs_info,
+ ret = btrfs_del_csums(trans,
+ fs_info->csum_root,
sums->bytenr,
sums->len);
if (!ret)
@@ -3866,6 +3867,28 @@ static int log_inode_item(struct btrfs_trans_handle *trans,
return 0;
}
+static int log_csums(struct btrfs_trans_handle *trans,
+ struct btrfs_root *log_root,
+ struct btrfs_ordered_sum *sums)
+{
+ int ret;
+
+ /*
+ * Due to extent cloning, we might have logged a csum item that covers a
+ * subrange of a cloned extent, and later we can end up logging a csum
+ * item for a larger subrange of the same extent or the entire range.
+ * This would leave csum items in the log tree that cover the same range
+ * and break the searches for checksums in the log tree, resulting in
+ * some checksums missing in the fs/subvolume tree. So just delete (or
+ * trim and adjust) any existing csum items in the log for this range.
+ */
+ ret = btrfs_del_csums(trans, log_root, sums->bytenr, sums->len);
+ if (ret)
+ return ret;
+
+ return btrfs_csum_file_blocks(trans, log_root, sums);
+}
+
static noinline int copy_items(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
struct btrfs_path *dst_path,
@@ -4011,7 +4034,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
struct btrfs_ordered_sum,
list);
if (!ret)
- ret = btrfs_csum_file_blocks(trans, log, sums);
+ ret = log_csums(trans, log, sums);
list_del(&sums->list);
kfree(sums);
}
@@ -4231,7 +4254,7 @@ static int log_extent_csums(struct btrfs_trans_handle *trans,
struct btrfs_ordered_sum,
list);
if (!ret)
- ret = btrfs_csum_file_blocks(trans, log_root, sums);
+ ret = log_csums(trans, log_root, sums);
list_del(&sums->list);
kfree(sums);
}
@@ -5997,9 +6020,28 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
if (IS_ERR(wc.replay_dest)) {
ret = PTR_ERR(wc.replay_dest);
+
+ /*
+ * We didn't find the subvol, likely because it was
+ * deleted. This is ok, simply skip this log and go to
+ * the next one.
+ *
+ * We need to exclude the root because we can't have
+ * other log replays overwriting this log as we'll read
+ * it back in a few more times. This will keep our
+ * block from being modified, and we'll just bail for
+ * each subsequent pass.
+ */
+ if (ret == -ENOENT)
+ ret = btrfs_pin_extent_for_log_replay(fs_info,
+ log->node->start,
+ log->node->len);
free_extent_buffer(log->node);
free_extent_buffer(log->commit_root);
kfree(log);
+
+ if (!ret)
+ goto next;
btrfs_handle_fs_error(fs_info, ret,
"Couldn't read target root for tree log recovery.");
goto error;
@@ -6031,7 +6073,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
&root->highest_objectid);
}
- key.offset = found_key.offset - 1;
wc.replay_dest->log_root = NULL;
free_extent_buffer(log->node);
free_extent_buffer(log->commit_root);
@@ -6039,9 +6080,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (ret)
goto error;
-
+next:
if (found_key.offset == 0)
break;
+ key.offset = found_key.offset - 1;
}
btrfs_release_path(path);
diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c
index 3b2ae34..5bbb977 100644
--- a/fs/btrfs/uuid-tree.c
+++ b/fs/btrfs/uuid-tree.c
@@ -324,6 +324,8 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
}
if (ret < 0 && ret != -ENOENT)
goto out;
+ key.offset++;
+ goto again_search_slot;
}
item_size -= sizeof(subid_le);
offset += sizeof(subid_le);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a8297e7..5bbcdcf 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -96,7 +96,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
.devs_min = 2,
.tolerated_failures = 1,
.devs_increment = 1,
- .ncopies = 2,
+ .ncopies = 1,
.raid_name = "raid5",
.bg_flag = BTRFS_BLOCK_GROUP_RAID5,
.mindev_error = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET,
@@ -108,7 +108,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
.devs_min = 3,
.tolerated_failures = 2,
.devs_increment = 1,
- .ncopies = 3,
+ .ncopies = 1,
.raid_name = "raid6",
.bg_flag = BTRFS_BLOCK_GROUP_RAID6,
.mindev_error = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET,
@@ -6106,12 +6106,6 @@ static noinline void btrfs_schedule_bio(struct btrfs_device *device,
int should_queue = 1;
struct btrfs_pending_bios *pending_bios;
- if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state) ||
- !device->bdev) {
- bio_io_error(bio);
- return;
- }
-
/* don't bother with additional async steps for reads, right now */
if (bio_op(bio) == REQ_OP_READ) {
btrfsic_submit_bio(bio);
@@ -6240,7 +6234,8 @@ blk_status_t btrfs_map_bio(struct btrfs_fs_info *fs_info, struct bio *bio,
for (dev_nr = 0; dev_nr < total_devs; dev_nr++) {
dev = bbio->stripes[dev_nr].dev;
- if (!dev || !dev->bdev ||
+ if (!dev || !dev->bdev || test_bit(BTRFS_DEV_STATE_MISSING,
+ &dev->dev_state) ||
(bio_op(first_bio) == REQ_OP_WRITE &&
!test_bit(BTRFS_DEV_STATE_WRITEABLE, &dev->dev_state))) {
bbio_error(bbio, first_bio, logical);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index c0e3015..ac703b1 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -304,7 +304,6 @@ struct btrfs_bio {
u64 map_type; /* get from map_lookup->type */
bio_end_io_t *end_io;
struct bio *orig_bio;
- unsigned long flags;
void *private;
atomic_t error;
int max_errors;
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index db547af0..4c0b220 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -1053,6 +1053,11 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release)
dout("__ceph_remove_cap %p from %p\n", cap, &ci->vfs_inode);
+ /* remove from inode's cap rbtree, and clear auth cap */
+ rb_erase(&cap->ci_node, &ci->i_caps);
+ if (ci->i_auth_cap == cap)
+ ci->i_auth_cap = NULL;
+
/* remove from session list */
spin_lock(&session->s_cap_lock);
if (session->s_cap_iterator == cap) {
@@ -1088,11 +1093,6 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release)
spin_unlock(&session->s_cap_lock);
- /* remove from inode list */
- rb_erase(&cap->ci_node, &ci->i_caps);
- if (ci->i_auth_cap == cap)
- ci->i_auth_cap = NULL;
-
if (removed)
ceph_put_cap(mdsc, cap);
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 92ab204..91a7ad2 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -1735,7 +1735,6 @@ static long ceph_fallocate(struct file *file, int mode,
struct ceph_file_info *fi = file->private_data;
struct inode *inode = file_inode(file);
struct ceph_inode_info *ci = ceph_inode(inode);
- struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
struct ceph_cap_flush *prealloc_cf;
int want, got = 0;
int dirty;
@@ -1743,10 +1742,7 @@ static long ceph_fallocate(struct file *file, int mode,
loff_t endoff = 0;
loff_t size;
- if ((offset + length) > max(i_size_read(inode), fsc->max_file_size))
- return -EFBIG;
-
- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ if (mode != (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
return -EOPNOTSUPP;
if (!S_ISREG(inode->i_mode))
@@ -1763,18 +1759,6 @@ static long ceph_fallocate(struct file *file, int mode,
goto unlock;
}
- if (!(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)) &&
- ceph_quota_is_max_bytes_exceeded(inode, offset + length)) {
- ret = -EDQUOT;
- goto unlock;
- }
-
- if (ceph_osdmap_flag(&fsc->client->osdc, CEPH_OSDMAP_FULL) &&
- !(mode & FALLOC_FL_PUNCH_HOLE)) {
- ret = -ENOSPC;
- goto unlock;
- }
-
if (ci->i_inline_version != CEPH_INLINE_NONE) {
ret = ceph_uninline_data(file, NULL);
if (ret < 0)
@@ -1782,12 +1766,12 @@ static long ceph_fallocate(struct file *file, int mode,
}
size = i_size_read(inode);
- if (!(mode & FALLOC_FL_KEEP_SIZE)) {
- endoff = offset + length;
- ret = inode_newsize_ok(inode, endoff);
- if (ret)
- goto unlock;
- }
+
+ /* Are we punching a hole beyond EOF? */
+ if (offset >= size)
+ goto unlock;
+ if ((offset + length) > size)
+ length = size - offset;
if (fi->fmode & CEPH_FILE_MODE_LAZY)
want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO;
@@ -1798,16 +1782,8 @@ static long ceph_fallocate(struct file *file, int mode,
if (ret < 0)
goto unlock;
- if (mode & FALLOC_FL_PUNCH_HOLE) {
- if (offset < size)
- ceph_zero_pagecache_range(inode, offset, length);
- ret = ceph_zero_objects(inode, offset, length);
- } else if (endoff > size) {
- truncate_pagecache_range(inode, size, -1);
- if (ceph_inode_set_size(inode, endoff))
- ceph_check_caps(ceph_inode(inode),
- CHECK_CAPS_AUTHONLY, NULL);
- }
+ ceph_zero_pagecache_range(inode, offset, length);
+ ret = ceph_zero_objects(inode, offset, length);
if (!ret) {
spin_lock(&ci->i_ceph_lock);
@@ -1817,9 +1793,6 @@ static long ceph_fallocate(struct file *file, int mode,
spin_unlock(&ci->i_ceph_lock);
if (dirty)
__mark_inode_dirty(inode, dirty);
- if ((endoff > size) &&
- ceph_quota_is_max_bytes_approaching(inode, endoff))
- ceph_check_caps(ci, CHECK_CAPS_NODELAY, NULL);
}
ceph_put_cap_refs(ci, got);
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 8196c21..1e438e0 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1399,6 +1399,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
dout(" final dn %p\n", dn);
} else if ((req->r_op == CEPH_MDS_OP_LOOKUPSNAP ||
req->r_op == CEPH_MDS_OP_MKSNAP) &&
+ test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags) &&
!test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) {
struct dentry *dn = req->r_dentry;
struct inode *dir = req->r_parent;
@@ -1693,7 +1694,6 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
if (IS_ERR(realdn)) {
err = PTR_ERR(realdn);
d_drop(dn);
- dn = NULL;
goto next_item;
}
dn = realdn;
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index ccab249..2bd0b1e 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -248,6 +248,7 @@ static int parse_fsopt_token(char *c, void *private)
return -ENOMEM;
break;
case Opt_fscache_uniq:
+#ifdef CONFIG_CEPH_FSCACHE
kfree(fsopt->fscache_uniq);
fsopt->fscache_uniq = kstrndup(argstr[0].from,
argstr[0].to-argstr[0].from,
@@ -256,7 +257,10 @@ static int parse_fsopt_token(char *c, void *private)
return -ENOMEM;
fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
break;
- /* misc */
+#else
+ pr_err("fscache support is disabled\n");
+ return -EINVAL;
+#endif
case Opt_wsize:
if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
return -EINVAL;
@@ -328,10 +332,15 @@ static int parse_fsopt_token(char *c, void *private)
fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
break;
case Opt_fscache:
+#ifdef CONFIG_CEPH_FSCACHE
fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
kfree(fsopt->fscache_uniq);
fsopt->fscache_uniq = NULL;
break;
+#else
+ pr_err("fscache support is disabled\n");
+ return -EINVAL;
+#endif
case Opt_nofscache:
fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
kfree(fsopt->fscache_uniq);
diff --git a/fs/char_dev.c b/fs/char_dev.c
index 8a63cfa..5fffd50 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -361,7 +361,7 @@ static struct kobject *cdev_get(struct cdev *p)
if (owner && !try_module_get(owner))
return NULL;
- kobj = kobject_get(&p->kobj);
+ kobj = kobject_get_unless_zero(&p->kobj);
if (!kobj)
module_put(owner);
return kobj;
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 0657679..a85aeff 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -210,6 +210,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (!server->rdma)
goto skip_rdma;
+ if (!server->smbd_conn) {
+ seq_printf(m, "\nSMBDirect transport not available");
+ goto skip_rdma;
+ }
+
seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
"transport status: %x",
server->smbd_conn->protocol,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 4dbae6e..71c2dd0 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1286,6 +1286,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
struct cifsInodeInfo {
bool can_cache_brlcks;
struct list_head llist; /* locks helb by this inode */
+ /*
+ * NOTE: Some code paths call down_read(lock_sem) twice, so
+ * we must always use use cifs_down_write() instead of down_write()
+ * for this semaphore to avoid deadlocks.
+ */
struct rw_semaphore lock_sem; /* protect the fields above */
/* BB add in lists for dirty pages i.e. write caching info for oplock */
struct list_head openFileList;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 20adda4..d7ac75e 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -159,6 +159,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid);
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
+extern void cifs_down_write(struct rw_semaphore *sem);
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
struct file *file,
struct tcon_link *tlink,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 86a54b8..8b94719 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1521,9 +1521,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf;
- rdata->iov[0].iov_len = 4;
- rdata->iov[1].iov_base = buf + 4;
- rdata->iov[1].iov_len = server->total_read - 4;
+ rdata->iov[0].iov_len = server->vals->header_preamble_size;
+ rdata->iov[1].iov_base = buf + server->vals->header_preamble_size;
+ rdata->iov[1].iov_len =
+ server->total_read - server->vals->header_preamble_size;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 966e493..7e85070 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -930,6 +930,26 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return 0;
}
+static void
+smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
+
+ /*
+ * SMB1 does not use credits.
+ */
+ if (server->vals->header_preamble_size)
+ return;
+
+ if (shdr->CreditRequest) {
+ spin_lock(&server->req_lock);
+ server->credits += le16_to_cpu(shdr->CreditRequest);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ }
+}
+
+
static int
cifs_demultiplex_thread(void *p)
{
@@ -1059,6 +1079,7 @@ cifs_demultiplex_thread(void *p)
} else if (server->ops->is_oplock_break &&
server->ops->is_oplock_break(bufs[i],
server)) {
+ smb2_add_credits_from_hdr(bufs[i], server);
cifs_dbg(FYI, "Received oplock break\n");
} else {
cifs_dbg(VFS, "No task to wake, unknown frame "
@@ -1070,6 +1091,7 @@ cifs_demultiplex_thread(void *p)
if (server->ops->dump_detail)
server->ops->dump_detail(bufs[i],
server);
+ smb2_add_credits_from_hdr(bufs[i], server);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b4e33ef..ad93b06 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -280,6 +280,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode)
return has_locks;
}
+void
+cifs_down_write(struct rw_semaphore *sem)
+{
+ while (!down_write_trylock(sem))
+ msleep(10);
+}
+
struct cifsFileInfo *
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct tcon_link *tlink, __u32 oplock)
@@ -305,9 +312,6 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
INIT_LIST_HEAD(&fdlocks->locks);
fdlocks->cfile = cfile;
cfile->llist = fdlocks;
- down_write(&cinode->lock_sem);
- list_add(&fdlocks->llist, &cinode->llist);
- up_write(&cinode->lock_sem);
cfile->count = 1;
cfile->pid = current->tgid;
@@ -331,6 +335,10 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
oplock = 0;
}
+ cifs_down_write(&cinode->lock_sem);
+ list_add(&fdlocks->llist, &cinode->llist);
+ up_write(&cinode->lock_sem);
+
spin_lock(&tcon->open_file_lock);
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
oplock = fid->pending_open->oplock;
@@ -461,7 +469,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
- down_write(&cifsi->lock_sem);
+ cifs_down_write(&cifsi->lock_sem);
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist);
cifs_del_lock_waiters(li);
@@ -718,6 +726,13 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (cfile->f_flags & O_SYNC)
+ create_options |= CREATE_WRITE_THROUGH;
+
+ if (cfile->f_flags & O_DIRECT)
+ create_options |= CREATE_NO_BUFFER;
+
if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &cfile->fid);
@@ -1016,7 +1031,7 @@ static void
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
{
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
list_add_tail(&lock->llist, &cfile->llist->locks);
up_write(&cinode->lock_sem);
}
@@ -1038,7 +1053,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
try_again:
exist = false;
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
lock->type, &conf_lock, CIFS_LOCK_OP);
@@ -1060,7 +1075,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
(lock->blist.next == &lock->blist));
if (!rc)
goto try_again;
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
list_del_init(&lock->blist);
}
@@ -1113,7 +1128,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
return rc;
try_again:
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
up_write(&cinode->lock_sem);
return rc;
@@ -1319,7 +1334,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
int rc = 0;
/* we are going to update can_cache_brlcks here - need a write access */
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
up_write(&cinode->lock_sem);
return rc;
@@ -1510,7 +1525,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
if (!buf)
return -ENOMEM;
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
for (i = 0; i < 2; i++) {
cur = buf;
num = 0;
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index fdd908e..66c1012 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -130,10 +130,6 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
{0, 0}
};
-static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
- {0, 0}
-};
-
/*
* Convert a string containing text IPv4 or IPv6 address to binary form.
*
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index b204e84..9168b22 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -137,7 +137,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
cur = buf;
- down_write(&cinode->lock_sem);
+ cifs_down_write(&cinode->lock_sem);
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (flock->fl_start > li->offset ||
(flock->fl_start + length) <
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 0a7ed2e..766974f 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -659,13 +659,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
return false;
- if (rsp->sync_hdr.CreditRequest) {
- spin_lock(&server->req_lock);
- server->credits += le16_to_cpu(rsp->sync_hdr.CreditRequest);
- spin_unlock(&server->req_lock);
- wake_up(&server->request_q);
- }
-
if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44)
@@ -680,10 +673,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+
list_for_each(tmp1, &ses->tcon_list) {
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
- cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) {
cfile = list_entry(tmp2, struct cifsFileInfo,
@@ -695,6 +688,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
continue;
cifs_dbg(FYI, "file id match, oplock break\n");
+ cifs_stats_inc(
+ &tcon->stats.cifs_stats.num_oplock_brks);
cinode = CIFS_I(d_inode(cfile->dentry));
spin_lock(&cfile->file_info_lock);
if (!CIFS_CACHE_WRITE(cinode) &&
@@ -727,9 +722,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
return true;
}
spin_unlock(&tcon->open_file_lock);
- spin_unlock(&cifs_tcp_ses_lock);
- cifs_dbg(FYI, "No matching file for oplock break\n");
- return true;
}
}
spin_unlock(&cifs_tcp_ses_lock);
@@ -751,36 +743,67 @@ smb2_cancelled_close_fid(struct work_struct *work)
kfree(cancelled);
}
+/* Caller should already has an extra reference to @tcon */
+static int
+__smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ __u64 volatile_fid)
+{
+ struct close_cancelled_open *cancelled;
+
+ cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
+ if (!cancelled)
+ return -ENOMEM;
+
+ cancelled->fid.persistent_fid = persistent_fid;
+ cancelled->fid.volatile_fid = volatile_fid;
+ cancelled->tcon = tcon;
+ INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
+ WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
+
+ return 0;
+}
+
+int
+smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ __u64 volatile_fid)
+{
+ int rc;
+
+ cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+ spin_lock(&cifs_tcp_ses_lock);
+ tcon->tc_count++;
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ rc = __smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid);
+ if (rc)
+ cifs_put_tcon(tcon);
+
+ return rc;
+}
+
int
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
{
struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
struct cifs_tcon *tcon;
- struct close_cancelled_open *cancelled;
+ int rc;
if (sync_hdr->Command != SMB2_CREATE ||
sync_hdr->Status != STATUS_SUCCESS)
return 0;
- cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
- if (!cancelled)
- return -ENOMEM;
-
tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
sync_hdr->TreeId);
- if (!tcon) {
- kfree(cancelled);
+ if (!tcon)
return -ENOENT;
- }
- cancelled->fid.persistent_fid = rsp->PersistentFileId;
- cancelled->fid.volatile_fid = rsp->VolatileFileId;
- cancelled->tcon = tcon;
- INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
- queue_work(cifsiod_wq, &cancelled->work);
+ rc = __smb2_handle_cancelled_close(tcon, rsp->PersistentFileId,
+ rsp->VolatileFileId);
+ if (rc)
+ cifs_put_tcon(tcon);
- return 0;
+ return rc;
}
/**
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index f0d966d..6fc1632 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -3000,10 +3000,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
/* set up first two iov to get credits */
rdata->iov[0].iov_base = buf;
- rdata->iov[0].iov_len = 4;
- rdata->iov[1].iov_base = buf + 4;
+ rdata->iov[0].iov_len = 0;
+ rdata->iov[1].iov_base = buf;
rdata->iov[1].iov_len =
- min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4;
+ min_t(unsigned int, buf_len, server->vals->read_rsp_size);
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index b1f5d0d..43f2962 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -168,7 +168,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
if (tcon == NULL)
return 0;
- if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
+ if (smb2_command == SMB2_TREE_CONNECT)
return 0;
if (tcon->tidStatus == CifsExiting) {
@@ -335,16 +335,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,
* SMB information in the SMB header. If the return code is zero, this
* function must have filled in request_buf pointer.
*/
-static int
-smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
- void **request_buf, unsigned int *total_len)
+static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
{
- int rc;
-
- rc = smb2_reconnect(smb2_command, tcon);
- if (rc)
- return rc;
-
/* BB eventually switch this to SMB2 specific small buf size */
if (smb2_command == SMB2_SET_INFO)
*request_buf = cifs_buf_get();
@@ -365,7 +358,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
cifs_stats_inc(&tcon->num_smbs_sent);
}
- return rc;
+ return 0;
+}
+
+static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
+{
+ int rc;
+
+ rc = smb2_reconnect(smb2_command, tcon);
+ if (rc)
+ return rc;
+
+ return __smb2_plain_req_init(smb2_command, tcon, request_buf,
+ total_len);
+}
+
+static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
+{
+ /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
+ return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf,
+ total_len);
+ }
+ return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len);
}
@@ -2283,7 +2300,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
struct cifs_ses *ses = tcon->ses;
struct kvec iov[SMB2_CREATE_IOV_SIZE];
struct kvec rsp_iov = {NULL, 0};
- int resp_buftype;
+ int resp_buftype = CIFS_NO_BUFFER;
int rc = 0;
int flags = 0;
@@ -2386,7 +2403,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
if (!ses || !(ses->server))
return -EIO;
- rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len);
+ rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len);
if (rc)
return rc;
@@ -2570,7 +2587,7 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_ses *ses = tcon->ses;
struct kvec iov[1];
struct kvec rsp_iov;
- int resp_buftype;
+ int resp_buftype = CIFS_NO_BUFFER;
int rc = 0;
cifs_dbg(FYI, "Close\n");
@@ -2612,7 +2629,21 @@ int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
{
- return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
+ int rc;
+ int tmp_rc;
+
+ rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
+
+ /* retry close in a worker thread if this one is interrupted */
+ if (rc == -EINTR) {
+ tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
+ volatile_fid);
+ if (tmp_rc)
+ cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
+ persistent_fid, tmp_rc);
+ }
+
+ return rc;
}
int
@@ -2723,7 +2754,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec iov[1];
struct kvec rsp_iov;
int rc = 0;
- int resp_buftype;
+ int resp_buftype = CIFS_NO_BUFFER;
struct cifs_ses *ses = tcon->ses;
int flags = 0;
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 437257d..308c682 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -777,6 +777,7 @@ struct create_durable_handle_reconnect_v2 {
struct create_context ccontext;
__u8 Name[8];
struct durable_reconnect_context_v2 dcontext;
+ __u8 Pad[4];
} __packed;
/* See MS-SMB2 2.2.13.2.5 */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index b407657..c66d7da 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -204,6 +204,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level);
+extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
+ __u64 persistent_fid,
+ __u64 volatile_fid);
extern int smb2_handle_cancelled_mid(char *buffer,
struct TCP_Server_Info *server);
void smb2_cancelled_close_fid(struct work_struct *work);
diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c
index 1959931..784628e 100644
--- a/fs/cifs/smbdirect.c
+++ b/fs/cifs/smbdirect.c
@@ -1164,7 +1164,7 @@ static int smbd_post_send_data(
if (n_vec > SMBDIRECT_MAX_SGE) {
cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec);
- return -ENOMEM;
+ return -EINVAL;
}
sg_init_table(sgl, n_vec);
@@ -1491,6 +1491,7 @@ void smbd_destroy(struct smbd_connection *info)
info->transport_status == SMBD_DESTROYED);
destroy_workqueue(info->workqueue);
+ log_rdma_event(INFO, "rdma session destroyed\n");
kfree(info);
}
@@ -1528,8 +1529,9 @@ int smbd_reconnect(struct TCP_Server_Info *server)
log_rdma_event(INFO, "creating rdma session\n");
server->smbd_conn = smbd_get_connection(
server, (struct sockaddr *) &server->dstaddr);
- log_rdma_event(INFO, "created rdma session info=%p\n",
- server->smbd_conn);
+
+ if (server->smbd_conn)
+ cifs_dbg(VFS, "RDMA transport re-established\n");
return server->smbd_conn ? 0 : -ENOENT;
}
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index fe77f41..0c4df56 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -286,8 +286,11 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
int val = 1;
__be32 rfc1002_marker;
- if (cifs_rdma_enabled(server) && server->smbd_conn) {
- rc = smbd_send(server, num_rqst, rqst);
+ if (cifs_rdma_enabled(server)) {
+ /* return -EAGAIN when connecting or reconnecting */
+ rc = -EAGAIN;
+ if (server->smbd_conn)
+ rc = smbd_send(server, num_rqst, rqst);
goto smbd_done;
}
if (ssocket == NULL)
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 8f08095..c6fe991 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -141,6 +141,7 @@ struct compat_video_event {
unsigned int frame_rate;
} u;
};
+#define VIDEO_GET_EVENT32 _IOR('o', 28, struct compat_video_event)
static int do_video_get_event(struct file *file,
unsigned int cmd, struct compat_video_event __user *up)
@@ -152,7 +153,7 @@ static int do_video_get_event(struct file *file,
if (kevent == NULL)
return -EFAULT;
- err = do_ioctl(file, cmd, (unsigned long)kevent);
+ err = do_ioctl(file, VIDEO_GET_EVENT, (unsigned long)kevent);
if (!err) {
err = convert_in_user(&kevent->type, &up->type);
err |= convert_in_user(&kevent->timestamp, &up->timestamp);
@@ -171,6 +172,7 @@ struct compat_video_still_picture {
compat_uptr_t iFrame;
int32_t size;
};
+#define VIDEO_STILLPICTURE32 _IOW('o', 30, struct compat_video_still_picture)
static int do_video_stillpicture(struct file *file,
unsigned int cmd, struct compat_video_still_picture __user *up)
@@ -193,7 +195,7 @@ static int do_video_stillpicture(struct file *file,
if (err)
return -EFAULT;
- err = do_ioctl(file, cmd, (unsigned long) up_native);
+ err = do_ioctl(file, VIDEO_STILLPICTURE, (unsigned long) up_native);
return err;
}
@@ -1302,9 +1304,9 @@ static long do_ioctl_trans(unsigned int cmd,
return rtc_ioctl(file, cmd, argp);
/* dvb */
- case VIDEO_GET_EVENT:
+ case VIDEO_GET_EVENT32:
return do_video_get_event(file, cmd, argp);
- case VIDEO_STILLPICTURE:
+ case VIDEO_STILLPICTURE32:
return do_video_stillpicture(file, cmd, argp);
}
@@ -1399,10 +1401,11 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
#endif
case FICLONE:
+ goto do_ioctl;
case FICLONERANGE:
case FIDEDUPERANGE:
case FS_IOC_FIEMAP:
- goto do_ioctl;
+ goto found_handler;
case FIBMAP:
case FIGETBSZ:
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index ccc31fa..16eb59a 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -34,6 +34,15 @@
#include <linux/list.h>
#include <linux/spinlock.h>
+struct configfs_fragment {
+ atomic_t frag_count;
+ struct rw_semaphore frag_sem;
+ bool frag_dead;
+};
+
+void put_fragment(struct configfs_fragment *);
+struct configfs_fragment *get_fragment(struct configfs_fragment *);
+
struct configfs_dirent {
atomic_t s_count;
int s_dependent_count;
@@ -48,6 +57,7 @@ struct configfs_dirent {
#ifdef CONFIG_LOCKDEP
int s_depth;
#endif
+ struct configfs_fragment *s_frag;
};
#define CONFIGFS_ROOT 0x0001
@@ -75,8 +85,8 @@ extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct in
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
extern int configfs_create_bin_file(struct config_item *,
const struct configfs_bin_attribute *);
-extern int configfs_make_dirent(struct configfs_dirent *,
- struct dentry *, void *, umode_t, int);
+extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *,
+ void *, umode_t, int, struct configfs_fragment *);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
extern void configfs_hash_and_remove(struct dentry * dir, const char * name);
@@ -151,6 +161,7 @@ static inline void release_configfs_dirent(struct configfs_dirent * sd)
{
if (!(sd->s_type & CONFIGFS_ROOT)) {
kfree(sd->s_iattr);
+ put_fragment(sd->s_frag);
kmem_cache_free(configfs_dir_cachep, sd);
}
}
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 809c1ed..2cc6b1c 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -164,11 +164,38 @@ configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
#endif /* CONFIG_LOCKDEP */
+static struct configfs_fragment *new_fragment(void)
+{
+ struct configfs_fragment *p;
+
+ p = kmalloc(sizeof(struct configfs_fragment), GFP_KERNEL);
+ if (p) {
+ atomic_set(&p->frag_count, 1);
+ init_rwsem(&p->frag_sem);
+ p->frag_dead = false;
+ }
+ return p;
+}
+
+void put_fragment(struct configfs_fragment *frag)
+{
+ if (frag && atomic_dec_and_test(&frag->frag_count))
+ kfree(frag);
+}
+
+struct configfs_fragment *get_fragment(struct configfs_fragment *frag)
+{
+ if (likely(frag))
+ atomic_inc(&frag->frag_count);
+ return frag;
+}
+
/*
* Allocates a new configfs_dirent and links it to the parent configfs_dirent
*/
static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd,
- void *element, int type)
+ void *element, int type,
+ struct configfs_fragment *frag)
{
struct configfs_dirent * sd;
@@ -188,6 +215,7 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *paren
kmem_cache_free(configfs_dir_cachep, sd);
return ERR_PTR(-ENOENT);
}
+ sd->s_frag = get_fragment(frag);
list_add(&sd->s_sibling, &parent_sd->s_children);
spin_unlock(&configfs_dirent_lock);
@@ -222,11 +250,11 @@ static int configfs_dirent_exists(struct configfs_dirent *parent_sd,
int configfs_make_dirent(struct configfs_dirent * parent_sd,
struct dentry * dentry, void * element,
- umode_t mode, int type)
+ umode_t mode, int type, struct configfs_fragment *frag)
{
struct configfs_dirent * sd;
- sd = configfs_new_dirent(parent_sd, element, type);
+ sd = configfs_new_dirent(parent_sd, element, type, frag);
if (IS_ERR(sd))
return PTR_ERR(sd);
@@ -273,7 +301,8 @@ static void init_symlink(struct inode * inode)
* until it is validated by configfs_dir_set_ready()
*/
-static int configfs_create_dir(struct config_item *item, struct dentry *dentry)
+static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
+ struct configfs_fragment *frag)
{
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
@@ -286,7 +315,8 @@ static int configfs_create_dir(struct config_item *item, struct dentry *dentry)
return error;
error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
- CONFIGFS_DIR | CONFIGFS_USET_CREATING);
+ CONFIGFS_DIR | CONFIGFS_USET_CREATING,
+ frag);
if (unlikely(error))
return error;
@@ -351,9 +381,10 @@ int configfs_create_link(struct configfs_symlink *sl,
{
int err = 0;
umode_t mode = S_IFLNK | S_IRWXUGO;
+ struct configfs_dirent *p = parent->d_fsdata;
- err = configfs_make_dirent(parent->d_fsdata, dentry, sl, mode,
- CONFIGFS_ITEM_LINK);
+ err = configfs_make_dirent(p, dentry, sl, mode,
+ CONFIGFS_ITEM_LINK, p->s_frag);
if (!err) {
err = configfs_create(dentry, mode, init_symlink);
if (err) {
@@ -612,7 +643,8 @@ static int populate_attrs(struct config_item *item)
static int configfs_attach_group(struct config_item *parent_item,
struct config_item *item,
- struct dentry *dentry);
+ struct dentry *dentry,
+ struct configfs_fragment *frag);
static void configfs_detach_group(struct config_item *item);
static void detach_groups(struct config_group *group)
@@ -660,7 +692,8 @@ static void detach_groups(struct config_group *group)
* try using vfs_mkdir. Just a thought.
*/
static int create_default_group(struct config_group *parent_group,
- struct config_group *group)
+ struct config_group *group,
+ struct configfs_fragment *frag)
{
int ret;
struct configfs_dirent *sd;
@@ -676,7 +709,7 @@ static int create_default_group(struct config_group *parent_group,
d_add(child, NULL);
ret = configfs_attach_group(&parent_group->cg_item,
- &group->cg_item, child);
+ &group->cg_item, child, frag);
if (!ret) {
sd = child->d_fsdata;
sd->s_type |= CONFIGFS_USET_DEFAULT;
@@ -690,13 +723,14 @@ static int create_default_group(struct config_group *parent_group,
return ret;
}
-static int populate_groups(struct config_group *group)
+static int populate_groups(struct config_group *group,
+ struct configfs_fragment *frag)
{
struct config_group *new_group;
int ret = 0;
list_for_each_entry(new_group, &group->default_groups, group_entry) {
- ret = create_default_group(group, new_group);
+ ret = create_default_group(group, new_group, frag);
if (ret) {
detach_groups(group);
break;
@@ -810,11 +844,12 @@ static void link_group(struct config_group *parent_group, struct config_group *g
*/
static int configfs_attach_item(struct config_item *parent_item,
struct config_item *item,
- struct dentry *dentry)
+ struct dentry *dentry,
+ struct configfs_fragment *frag)
{
int ret;
- ret = configfs_create_dir(item, dentry);
+ ret = configfs_create_dir(item, dentry, frag);
if (!ret) {
ret = populate_attrs(item);
if (ret) {
@@ -844,12 +879,13 @@ static void configfs_detach_item(struct config_item *item)
static int configfs_attach_group(struct config_item *parent_item,
struct config_item *item,
- struct dentry *dentry)
+ struct dentry *dentry,
+ struct configfs_fragment *frag)
{
int ret;
struct configfs_dirent *sd;
- ret = configfs_attach_item(parent_item, item, dentry);
+ ret = configfs_attach_item(parent_item, item, dentry, frag);
if (!ret) {
sd = dentry->d_fsdata;
sd->s_type |= CONFIGFS_USET_DIR;
@@ -865,7 +901,7 @@ static int configfs_attach_group(struct config_item *parent_item,
*/
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
configfs_adjust_dir_dirent_depth_before_populate(sd);
- ret = populate_groups(to_config_group(item));
+ ret = populate_groups(to_config_group(item), frag);
if (ret) {
configfs_detach_item(item);
d_inode(dentry)->i_flags |= S_DEAD;
@@ -1260,6 +1296,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
struct configfs_dirent *sd;
const struct config_item_type *type;
struct module *subsys_owner = NULL, *new_item_owner = NULL;
+ struct configfs_fragment *frag;
char *name;
sd = dentry->d_parent->d_fsdata;
@@ -1278,6 +1315,12 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
goto out;
}
+ frag = new_fragment();
+ if (!frag) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
/* Get a working ref for the duration of this function */
parent_item = configfs_get_config_item(dentry->d_parent);
type = parent_item->ci_type;
@@ -1380,9 +1423,9 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
spin_unlock(&configfs_dirent_lock);
if (group)
- ret = configfs_attach_group(parent_item, item, dentry);
+ ret = configfs_attach_group(parent_item, item, dentry, frag);
else
- ret = configfs_attach_item(parent_item, item, dentry);
+ ret = configfs_attach_item(parent_item, item, dentry, frag);
spin_lock(&configfs_dirent_lock);
sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
@@ -1419,6 +1462,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
* reference.
*/
config_item_put(parent_item);
+ put_fragment(frag);
out:
return ret;
@@ -1430,6 +1474,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
struct config_item *item;
struct configfs_subsystem *subsys;
struct configfs_dirent *sd;
+ struct configfs_fragment *frag;
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
int ret;
@@ -1487,6 +1532,16 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
}
} while (ret == -EAGAIN);
+ frag = sd->s_frag;
+ if (down_write_killable(&frag->frag_sem)) {
+ spin_lock(&configfs_dirent_lock);
+ configfs_detach_rollback(dentry);
+ spin_unlock(&configfs_dirent_lock);
+ return -EINTR;
+ }
+ frag->frag_dead = true;
+ up_write(&frag->frag_sem);
+
/* Get a working ref for the duration of this function */
item = configfs_get_config_item(dentry);
@@ -1587,7 +1642,7 @@ static int configfs_dir_open(struct inode *inode, struct file *file)
*/
err = -ENOENT;
if (configfs_dirent_is_ready(parent_sd)) {
- file->private_data = configfs_new_dirent(parent_sd, NULL, 0);
+ file->private_data = configfs_new_dirent(parent_sd, NULL, 0, NULL);
if (IS_ERR(file->private_data))
err = PTR_ERR(file->private_data);
else
@@ -1743,8 +1798,13 @@ int configfs_register_group(struct config_group *parent_group,
{
struct configfs_subsystem *subsys = parent_group->cg_subsys;
struct dentry *parent;
+ struct configfs_fragment *frag;
int ret;
+ frag = new_fragment();
+ if (!frag)
+ return -ENOMEM;
+
mutex_lock(&subsys->su_mutex);
link_group(parent_group, group);
mutex_unlock(&subsys->su_mutex);
@@ -1752,7 +1812,7 @@ int configfs_register_group(struct config_group *parent_group,
parent = parent_group->cg_item.ci_dentry;
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- ret = create_default_group(parent_group, group);
+ ret = create_default_group(parent_group, group, frag);
if (ret)
goto err_out;
@@ -1760,12 +1820,14 @@ int configfs_register_group(struct config_group *parent_group,
configfs_dir_set_ready(group->cg_item.ci_dentry->d_fsdata);
spin_unlock(&configfs_dirent_lock);
inode_unlock(d_inode(parent));
+ put_fragment(frag);
return 0;
err_out:
inode_unlock(d_inode(parent));
mutex_lock(&subsys->su_mutex);
unlink_group(group);
mutex_unlock(&subsys->su_mutex);
+ put_fragment(frag);
return ret;
}
EXPORT_SYMBOL(configfs_register_group);
@@ -1781,16 +1843,12 @@ void configfs_unregister_group(struct config_group *group)
struct configfs_subsystem *subsys = group->cg_subsys;
struct dentry *dentry = group->cg_item.ci_dentry;
struct dentry *parent = group->cg_item.ci_parent->ci_dentry;
+ struct configfs_dirent *sd = dentry->d_fsdata;
+ struct configfs_fragment *frag = sd->s_frag;
- mutex_lock(&subsys->su_mutex);
- if (!group->cg_item.ci_parent->ci_group) {
- /*
- * The parent has already been unlinked and detached
- * due to a rmdir.
- */
- goto unlink_group;
- }
- mutex_unlock(&subsys->su_mutex);
+ down_write(&frag->frag_sem);
+ frag->frag_dead = true;
+ up_write(&frag->frag_sem);
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
spin_lock(&configfs_dirent_lock);
@@ -1806,7 +1864,6 @@ void configfs_unregister_group(struct config_group *group)
dput(dentry);
mutex_lock(&subsys->su_mutex);
-unlink_group:
unlink_group(group);
mutex_unlock(&subsys->su_mutex);
}
@@ -1863,10 +1920,17 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
struct dentry *dentry;
struct dentry *root;
struct configfs_dirent *sd;
+ struct configfs_fragment *frag;
+
+ frag = new_fragment();
+ if (!frag)
+ return -ENOMEM;
root = configfs_pin_fs();
- if (IS_ERR(root))
+ if (IS_ERR(root)) {
+ put_fragment(frag);
return PTR_ERR(root);
+ }
if (!group->cg_item.ci_name)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
@@ -1882,7 +1946,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
d_add(dentry, NULL);
err = configfs_attach_group(sd->s_element, &group->cg_item,
- dentry);
+ dentry, frag);
if (err) {
BUG_ON(d_inode(dentry));
d_drop(dentry);
@@ -1900,6 +1964,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
unlink_group(group);
configfs_release_fs();
}
+ put_fragment(frag);
return err;
}
@@ -1909,12 +1974,18 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
struct config_group *group = &subsys->su_group;
struct dentry *dentry = group->cg_item.ci_dentry;
struct dentry *root = dentry->d_sb->s_root;
+ struct configfs_dirent *sd = dentry->d_fsdata;
+ struct configfs_fragment *frag = sd->s_frag;
if (dentry->d_parent != root) {
pr_err("Tried to unregister non-subsystem!\n");
return;
}
+ down_write(&frag->frag_sem);
+ frag->frag_dead = true;
+ up_write(&frag->frag_sem);
+
inode_lock_nested(d_inode(root),
I_MUTEX_PARENT);
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
diff --git a/fs/configfs/file.c b/fs/configfs/file.c
index 62580db..bb0a427 100644
--- a/fs/configfs/file.c
+++ b/fs/configfs/file.c
@@ -53,40 +53,44 @@ struct configfs_buffer {
bool write_in_progress;
char *bin_buffer;
int bin_buffer_size;
+ int cb_max_size;
+ struct config_item *item;
+ struct module *owner;
+ union {
+ struct configfs_attribute *attr;
+ struct configfs_bin_attribute *bin_attr;
+ };
};
-
-/**
- * fill_read_buffer - allocate and fill buffer from item.
- * @dentry: dentry pointer.
- * @buffer: data buffer for file.
- *
- * Allocate @buffer->page, if it hasn't been already, then call the
- * config_item's show() method to fill the buffer with this attribute's
- * data.
- * This is called only once, on the file's first read.
- */
-static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buffer)
+static inline struct configfs_fragment *to_frag(struct file *file)
{
- struct configfs_attribute * attr = to_attr(dentry);
- struct config_item * item = to_item(dentry->d_parent);
- int ret = 0;
- ssize_t count;
+ struct configfs_dirent *sd = file->f_path.dentry->d_fsdata;
+
+ return sd->s_frag;
+}
+
+static int fill_read_buffer(struct file *file, struct configfs_buffer *buffer)
+{
+ struct configfs_fragment *frag = to_frag(file);
+ ssize_t count = -ENOENT;
if (!buffer->page)
buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
- count = attr->show(item, buffer->page);
+ down_read(&frag->frag_sem);
+ if (!frag->frag_dead)
+ count = buffer->attr->show(buffer->item, buffer->page);
+ up_read(&frag->frag_sem);
- BUG_ON(count > (ssize_t)SIMPLE_ATTR_SIZE);
- if (count >= 0) {
- buffer->needs_read_fill = 0;
- buffer->count = count;
- } else
- ret = count;
- return ret;
+ if (count < 0)
+ return count;
+ if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE))
+ return -EIO;
+ buffer->needs_read_fill = 0;
+ buffer->count = count;
+ return 0;
}
/**
@@ -111,12 +115,13 @@ static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buf
static ssize_t
configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
- struct configfs_buffer * buffer = file->private_data;
+ struct configfs_buffer *buffer = file->private_data;
ssize_t retval = 0;
mutex_lock(&buffer->mutex);
if (buffer->needs_read_fill) {
- if ((retval = fill_read_buffer(file->f_path.dentry,buffer)))
+ retval = fill_read_buffer(file, buffer);
+ if (retval)
goto out;
}
pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
@@ -152,10 +157,8 @@ static ssize_t
configfs_read_bin_file(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct configfs_fragment *frag = to_frag(file);
struct configfs_buffer *buffer = file->private_data;
- struct dentry *dentry = file->f_path.dentry;
- struct config_item *item = to_item(dentry->d_parent);
- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
ssize_t retval = 0;
ssize_t len = min_t(size_t, count, PAGE_SIZE);
@@ -170,14 +173,19 @@ configfs_read_bin_file(struct file *file, char __user *buf,
if (buffer->needs_read_fill) {
/* perform first read with buf == NULL to get extent */
- len = bin_attr->read(item, NULL, 0);
+ down_read(&frag->frag_sem);
+ if (!frag->frag_dead)
+ len = buffer->bin_attr->read(buffer->item, NULL, 0);
+ else
+ len = -ENOENT;
+ up_read(&frag->frag_sem);
if (len <= 0) {
retval = len;
goto out;
}
/* do not exceed the maximum value */
- if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) {
+ if (buffer->cb_max_size && len > buffer->cb_max_size) {
retval = -EFBIG;
goto out;
}
@@ -190,7 +198,13 @@ configfs_read_bin_file(struct file *file, char __user *buf,
buffer->bin_buffer_size = len;
/* perform second read to fill buffer */
- len = bin_attr->read(item, buffer->bin_buffer, len);
+ down_read(&frag->frag_sem);
+ if (!frag->frag_dead)
+ len = buffer->bin_attr->read(buffer->item,
+ buffer->bin_buffer, len);
+ else
+ len = -ENOENT;
+ up_read(&frag->frag_sem);
if (len < 0) {
retval = len;
vfree(buffer->bin_buffer);
@@ -240,25 +254,17 @@ fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size
return error ? -EFAULT : count;
}
-
-/**
- * flush_write_buffer - push buffer to config_item.
- * @dentry: dentry to the attribute
- * @buffer: data buffer for file.
- * @count: number of bytes
- *
- * Get the correct pointers for the config_item and the attribute we're
- * dealing with, then call the store() method for the attribute,
- * passing the buffer that we acquired in fill_write_buffer().
- */
-
static int
-flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size_t count)
+flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)
{
- struct configfs_attribute * attr = to_attr(dentry);
- struct config_item * item = to_item(dentry->d_parent);
+ struct configfs_fragment *frag = to_frag(file);
+ int res = -ENOENT;
- return attr->store(item, buffer->page, count);
+ down_read(&frag->frag_sem);
+ if (!frag->frag_dead)
+ res = buffer->attr->store(buffer->item, buffer->page, count);
+ up_read(&frag->frag_sem);
+ return res;
}
@@ -282,13 +288,13 @@ flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size
static ssize_t
configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
- struct configfs_buffer * buffer = file->private_data;
+ struct configfs_buffer *buffer = file->private_data;
ssize_t len;
mutex_lock(&buffer->mutex);
len = fill_write_buffer(buffer, buf, count);
if (len > 0)
- len = flush_write_buffer(file->f_path.dentry, buffer, len);
+ len = flush_write_buffer(file, buffer, len);
if (len > 0)
*ppos += len;
mutex_unlock(&buffer->mutex);
@@ -313,8 +319,6 @@ configfs_write_bin_file(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct configfs_buffer *buffer = file->private_data;
- struct dentry *dentry = file->f_path.dentry;
- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
void *tbuf = NULL;
ssize_t len;
@@ -330,8 +334,8 @@ configfs_write_bin_file(struct file *file, const char __user *buf,
/* buffer grows? */
if (*ppos + count > buffer->bin_buffer_size) {
- if (bin_attr->cb_max_size &&
- *ppos + count > bin_attr->cb_max_size) {
+ if (buffer->cb_max_size &&
+ *ppos + count > buffer->cb_max_size) {
len = -EFBIG;
goto out;
}
@@ -363,31 +367,51 @@ configfs_write_bin_file(struct file *file, const char __user *buf,
return len;
}
-static int check_perm(struct inode * inode, struct file * file, int type)
+static int __configfs_open_file(struct inode *inode, struct file *file, int type)
{
- struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent);
- struct configfs_attribute * attr = to_attr(file->f_path.dentry);
- struct configfs_bin_attribute *bin_attr = NULL;
- struct configfs_buffer * buffer;
- struct configfs_item_operations * ops = NULL;
- int error = 0;
+ struct dentry *dentry = file->f_path.dentry;
+ struct configfs_fragment *frag = to_frag(file);
+ struct configfs_attribute *attr;
+ struct configfs_buffer *buffer;
+ int error;
- if (!item || !attr)
- goto Einval;
+ error = -ENOMEM;
+ buffer = kzalloc(sizeof(struct configfs_buffer), GFP_KERNEL);
+ if (!buffer)
+ goto out;
- if (type & CONFIGFS_ITEM_BIN_ATTR)
- bin_attr = to_bin_attr(file->f_path.dentry);
+ error = -ENOENT;
+ down_read(&frag->frag_sem);
+ if (unlikely(frag->frag_dead))
+ goto out_free_buffer;
- /* Grab the module reference for this attribute if we have one */
- if (!try_module_get(attr->ca_owner)) {
- error = -ENODEV;
- goto Done;
+ error = -EINVAL;
+ buffer->item = to_item(dentry->d_parent);
+ if (!buffer->item)
+ goto out_free_buffer;
+
+ attr = to_attr(dentry);
+ if (!attr)
+ goto out_put_item;
+
+ if (type & CONFIGFS_ITEM_BIN_ATTR) {
+ buffer->bin_attr = to_bin_attr(dentry);
+ buffer->cb_max_size = buffer->bin_attr->cb_max_size;
+ } else {
+ buffer->attr = attr;
}
- if (item->ci_type)
- ops = item->ci_type->ct_item_ops;
- else
- goto Eaccess;
+ buffer->owner = attr->ca_owner;
+ /* Grab the module reference for this attribute if we have one */
+ error = -ENODEV;
+ if (!try_module_get(buffer->owner))
+ goto out_put_item;
+
+ error = -EACCES;
+ if (!buffer->item->ci_type)
+ goto out_put_module;
+
+ buffer->ops = buffer->item->ci_type->ct_item_ops;
/* File needs write support.
* The inode's perms must say it's ok,
@@ -395,13 +419,11 @@ static int check_perm(struct inode * inode, struct file * file, int type)
*/
if (file->f_mode & FMODE_WRITE) {
if (!(inode->i_mode & S_IWUGO))
- goto Eaccess;
-
+ goto out_put_module;
if ((type & CONFIGFS_ITEM_ATTR) && !attr->store)
- goto Eaccess;
-
- if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write)
- goto Eaccess;
+ goto out_put_module;
+ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->write)
+ goto out_put_module;
}
/* File needs read support.
@@ -410,92 +432,72 @@ static int check_perm(struct inode * inode, struct file * file, int type)
*/
if (file->f_mode & FMODE_READ) {
if (!(inode->i_mode & S_IRUGO))
- goto Eaccess;
-
+ goto out_put_module;
if ((type & CONFIGFS_ITEM_ATTR) && !attr->show)
- goto Eaccess;
-
- if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read)
- goto Eaccess;
+ goto out_put_module;
+ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->read)
+ goto out_put_module;
}
- /* No error? Great, allocate a buffer for the file, and store it
- * it in file->private_data for easy access.
- */
- buffer = kzalloc(sizeof(struct configfs_buffer),GFP_KERNEL);
- if (!buffer) {
- error = -ENOMEM;
- goto Enomem;
- }
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
buffer->read_in_progress = false;
buffer->write_in_progress = false;
- buffer->ops = ops;
file->private_data = buffer;
- goto Done;
+ up_read(&frag->frag_sem);
+ return 0;
- Einval:
- error = -EINVAL;
- goto Done;
- Eaccess:
- error = -EACCES;
- Enomem:
- module_put(attr->ca_owner);
- Done:
- if (error && item)
- config_item_put(item);
+out_put_module:
+ module_put(buffer->owner);
+out_put_item:
+ config_item_put(buffer->item);
+out_free_buffer:
+ up_read(&frag->frag_sem);
+ kfree(buffer);
+out:
return error;
}
static int configfs_release(struct inode *inode, struct file *filp)
{
- struct config_item * item = to_item(filp->f_path.dentry->d_parent);
- struct configfs_attribute * attr = to_attr(filp->f_path.dentry);
- struct module * owner = attr->ca_owner;
- struct configfs_buffer * buffer = filp->private_data;
+ struct configfs_buffer *buffer = filp->private_data;
- if (item)
- config_item_put(item);
- /* After this point, attr should not be accessed. */
- module_put(owner);
-
- if (buffer) {
- if (buffer->page)
- free_page((unsigned long)buffer->page);
- mutex_destroy(&buffer->mutex);
- kfree(buffer);
- }
+ module_put(buffer->owner);
+ if (buffer->page)
+ free_page((unsigned long)buffer->page);
+ mutex_destroy(&buffer->mutex);
+ kfree(buffer);
return 0;
}
static int configfs_open_file(struct inode *inode, struct file *filp)
{
- return check_perm(inode, filp, CONFIGFS_ITEM_ATTR);
+ return __configfs_open_file(inode, filp, CONFIGFS_ITEM_ATTR);
}
static int configfs_open_bin_file(struct inode *inode, struct file *filp)
{
- return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR);
+ return __configfs_open_file(inode, filp, CONFIGFS_ITEM_BIN_ATTR);
}
-static int configfs_release_bin_file(struct inode *inode, struct file *filp)
+static int configfs_release_bin_file(struct inode *inode, struct file *file)
{
- struct configfs_buffer *buffer = filp->private_data;
- struct dentry *dentry = filp->f_path.dentry;
- struct config_item *item = to_item(dentry->d_parent);
- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
- ssize_t len = 0;
- int ret;
+ struct configfs_buffer *buffer = file->private_data;
buffer->read_in_progress = false;
if (buffer->write_in_progress) {
+ struct configfs_fragment *frag = to_frag(file);
buffer->write_in_progress = false;
- len = bin_attr->write(item, buffer->bin_buffer,
- buffer->bin_buffer_size);
-
+ down_read(&frag->frag_sem);
+ if (!frag->frag_dead) {
+ /* result of ->release() is ignored */
+ buffer->bin_attr->write(buffer->item,
+ buffer->bin_buffer,
+ buffer->bin_buffer_size);
+ }
+ up_read(&frag->frag_sem);
/* vfree on NULL is safe */
vfree(buffer->bin_buffer);
buffer->bin_buffer = NULL;
@@ -503,10 +505,8 @@ static int configfs_release_bin_file(struct inode *inode, struct file *filp)
buffer->needs_read_fill = 1;
}
- ret = configfs_release(inode, filp);
- if (len < 0)
- return len;
- return ret;
+ configfs_release(inode, file);
+ return 0;
}
@@ -541,7 +541,7 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib
inode_lock_nested(d_inode(dir), I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode,
- CONFIGFS_ITEM_ATTR);
+ CONFIGFS_ITEM_ATTR, parent_sd->s_frag);
inode_unlock(d_inode(dir));
return error;
@@ -563,7 +563,7 @@ int configfs_create_bin_file(struct config_item *item,
inode_lock_nested(dir->d_inode, I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode,
- CONFIGFS_ITEM_BIN_ATTR);
+ CONFIGFS_ITEM_BIN_ATTR, parent_sd->s_frag);
inode_unlock(dir->d_inode);
return error;
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index a5c54af..1996643 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -157,11 +157,42 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
!type->ct_item_ops->allow_link)
goto out_put;
+ /*
+ * This is really sick. What they wanted was a hybrid of
+ * link(2) and symlink(2) - they wanted the target resolved
+ * at syscall time (as link(2) would've done), be a directory
+ * (which link(2) would've refused to do) *AND* be a deep
+ * fucking magic, making the target busy from rmdir POV.
+ * symlink(2) is nothing of that sort, and the locking it
+ * gets matches the normal symlink(2) semantics. Without
+ * attempts to resolve the target (which might very well
+ * not even exist yet) done prior to locking the parent
+ * directory. This perversion, OTOH, needs to resolve
+ * the target, which would lead to obvious deadlocks if
+ * attempted with any directories locked.
+ *
+ * Unfortunately, that garbage is userland ABI and we should've
+ * said "no" back in 2005. Too late now, so we get to
+ * play very ugly games with locking.
+ *
+ * Try *ANYTHING* of that sort in new code, and you will
+ * really regret it. Just ask yourself - what could a BOFH
+ * do to me and do I want to find it out first-hand?
+ *
+ * AV, a thoroughly annoyed bastard.
+ */
+ inode_unlock(dir);
ret = get_target(symname, &path, &target_item, dentry->d_sb);
+ inode_lock(dir);
if (ret)
goto out_put;
- ret = type->ct_item_ops->allow_link(parent_item, target_item);
+ if (dentry->d_inode || d_unhashed(dentry))
+ ret = -EEXIST;
+ else
+ ret = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (!ret)
+ ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) {
mutex_lock(&configfs_symlink_mutex);
ret = create_link(parent_item, target_item, dentry);
diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c
index 6a1529e..f1261fa 100644
--- a/fs/dlm/lockspace.c
+++ b/fs/dlm/lockspace.c
@@ -807,6 +807,7 @@ static int release_lockspace(struct dlm_ls *ls, int force)
dlm_delete_debug_file(ls);
+ idr_destroy(&ls->ls_recover_idr);
kfree(ls->ls_recover_buf);
/*
diff --git a/fs/dlm/member.c b/fs/dlm/member.c
index 3fda383..0bc43b3 100644
--- a/fs/dlm/member.c
+++ b/fs/dlm/member.c
@@ -671,7 +671,7 @@ int dlm_ls_stop(struct dlm_ls *ls)
int dlm_ls_start(struct dlm_ls *ls)
{
struct dlm_recover *rv, *rv_old;
- struct dlm_config_node *nodes;
+ struct dlm_config_node *nodes = NULL;
int error, count;
rv = kzalloc(sizeof(*rv), GFP_NOFS);
@@ -680,7 +680,7 @@ int dlm_ls_start(struct dlm_ls *ls)
error = dlm_config_nodes(ls->ls_name, &nodes, &count);
if (error < 0)
- goto fail;
+ goto fail_rv;
spin_lock(&ls->ls_recover_lock);
@@ -712,8 +712,9 @@ int dlm_ls_start(struct dlm_ls *ls)
return 0;
fail:
- kfree(rv);
kfree(nodes);
+ fail_rv:
+ kfree(rv);
return error;
}
diff --git a/fs/dlm/memory.c b/fs/dlm/memory.c
index 7cd24bc..37be29f 100644
--- a/fs/dlm/memory.c
+++ b/fs/dlm/memory.c
@@ -38,10 +38,8 @@ int __init dlm_memory_init(void)
void dlm_memory_exit(void)
{
- if (lkb_cache)
- kmem_cache_destroy(lkb_cache);
- if (rsb_cache)
- kmem_cache_destroy(rsb_cache);
+ kmem_cache_destroy(lkb_cache);
+ kmem_cache_destroy(rsb_cache);
}
char *dlm_allocate_lvb(struct dlm_ls *ls)
@@ -86,8 +84,7 @@ void dlm_free_lkb(struct dlm_lkb *lkb)
struct dlm_user_args *ua;
ua = lkb->lkb_ua;
if (ua) {
- if (ua->lksb.sb_lvbptr)
- kfree(ua->lksb.sb_lvbptr);
+ kfree(ua->lksb.sb_lvbptr);
kfree(ua);
}
}
diff --git a/fs/dlm/user.c b/fs/dlm/user.c
index 2a66939..3c84c62 100644
--- a/fs/dlm/user.c
+++ b/fs/dlm/user.c
@@ -25,6 +25,7 @@
#include "lvb_table.h"
#include "user.h"
#include "ast.h"
+#include "config.h"
static const char name_prefix[] = "dlm";
static const struct file_operations device_fops;
@@ -404,7 +405,7 @@ static int device_create_lockspace(struct dlm_lspace_params *params)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- error = dlm_new_lockspace(params->name, NULL, params->flags,
+ error = dlm_new_lockspace(params->name, dlm_config.ci_cluster_name, params->flags,
DLM_USER_LVB_LEN, NULL, NULL, NULL,
&lockspace);
if (error)
@@ -702,7 +703,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat,
result.version[0] = DLM_DEVICE_VERSION_MAJOR;
result.version[1] = DLM_DEVICE_VERSION_MINOR;
result.version[2] = DLM_DEVICE_VERSION_PATCH;
- memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb));
+ memcpy(&result.lksb, &ua->lksb, offsetof(struct dlm_lksb, sb_lvbptr));
result.user_lksb = ua->user_lksb;
/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
diff --git a/fs/drop_caches.c b/fs/drop_caches.c
index d31b6c7..dc1a1d5 100644
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -35,11 +35,11 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);
- cond_resched();
invalidate_mapping_pages(inode->i_mapping, 0, -1);
iput(toput_inode);
toput_inode = inode;
+ cond_resched();
spin_lock(&sb->s_inode_list_lock);
}
spin_unlock(&sb->s_inode_list_lock);
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 49121e5..6578a1c 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -325,9 +325,9 @@ static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode)
static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
struct dentry *lower_dentry)
{
- struct inode *inode, *lower_inode = d_inode(lower_dentry);
+ struct path *path = ecryptfs_dentry_to_lower_path(dentry->d_parent);
+ struct inode *inode, *lower_inode;
struct ecryptfs_dentry_info *dentry_info;
- struct vfsmount *lower_mnt;
int rc = 0;
dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
@@ -336,16 +336,23 @@ static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
return ERR_PTR(-ENOMEM);
}
- lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
fsstack_copy_attr_atime(d_inode(dentry->d_parent),
- d_inode(lower_dentry->d_parent));
+ d_inode(path->dentry));
BUG_ON(!d_count(lower_dentry));
ecryptfs_set_dentry_private(dentry, dentry_info);
- dentry_info->lower_path.mnt = lower_mnt;
+ dentry_info->lower_path.mnt = mntget(path->mnt);
dentry_info->lower_path.dentry = lower_dentry;
- if (d_really_is_negative(lower_dentry)) {
+ /*
+ * negative dentry can go positive under us here - its parent is not
+ * locked. That's OK and that could happen just as we return from
+ * ecryptfs_lookup() anyway. Just need to be careful and fetch
+ * ->d_inode only once - it's not stable here.
+ */
+ lower_inode = READ_ONCE(lower_dentry->d_inode);
+
+ if (!lower_inode) {
/* We want to add because we couldn't find in lower */
d_add(dentry, NULL);
return NULL;
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index 7d61e3f..8fb98bb 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -705,21 +705,18 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
/*
* Read the superblock from the OSD and fill in the fields
*/
-static int exofs_fill_super(struct super_block *sb, void *data, int silent)
+static int exofs_fill_super(struct super_block *sb,
+ struct exofs_mountopt *opts,
+ struct exofs_sb_info *sbi,
+ int silent)
{
struct inode *root;
- struct exofs_mountopt *opts = data;
- struct exofs_sb_info *sbi; /*extended info */
struct osd_dev *od; /* Master device */
struct exofs_fscb fscb; /*on-disk superblock info */
struct ore_comp comp;
unsigned table_count;
int ret;
- sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
- if (!sbi)
- return -ENOMEM;
-
/* use mount options to fill superblock */
if (opts->is_osdname) {
struct osd_dev_info odi = {.systemid_len = 0};
@@ -863,7 +860,9 @@ static struct dentry *exofs_mount(struct file_system_type *type,
int flags, const char *dev_name,
void *data)
{
+ struct super_block *s;
struct exofs_mountopt opts;
+ struct exofs_sb_info *sbi;
int ret;
ret = parse_options(data, &opts);
@@ -872,9 +871,31 @@ static struct dentry *exofs_mount(struct file_system_type *type,
return ERR_PTR(ret);
}
+ sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+ if (!sbi) {
+ kfree(opts.dev_name);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ s = sget(type, NULL, set_anon_super, flags, NULL);
+
+ if (IS_ERR(s)) {
+ kfree(opts.dev_name);
+ kfree(sbi);
+ return ERR_CAST(s);
+ }
+
if (!opts.dev_name)
opts.dev_name = dev_name;
- return mount_nodev(type, flags, &opts, exofs_fill_super);
+
+
+ ret = exofs_fill_super(s, &opts, sbi, flags & SB_SILENT ? 1 : 0);
+ if (ret) {
+ deactivate_locked_super(s);
+ return ERR_PTR(ret);
+ }
+ s->s_flags |= SB_ACTIVE;
+ return dget(s->s_root);
}
/*
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 63707ab..808cae6 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -517,26 +517,33 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
* inode is actually connected to the parent.
*/
err = exportfs_get_name(mnt, target_dir, nbuf, result);
- if (!err) {
- inode_lock(target_dir->d_inode);
- nresult = lookup_one_len(nbuf, target_dir,
- strlen(nbuf));
- inode_unlock(target_dir->d_inode);
- if (!IS_ERR(nresult)) {
- if (nresult->d_inode) {
- dput(result);
- result = nresult;
- } else
- dput(nresult);
- }
+ if (err) {
+ dput(target_dir);
+ goto err_result;
}
+ inode_lock(target_dir->d_inode);
+ nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
+ if (!IS_ERR(nresult)) {
+ if (unlikely(nresult->d_inode != result->d_inode)) {
+ dput(nresult);
+ nresult = ERR_PTR(-ESTALE);
+ }
+ }
+ inode_unlock(target_dir->d_inode);
/*
* At this point we are done with the parent, but it's pinned
* by the child dentry anyway.
*/
dput(target_dir);
+ if (IS_ERR(nresult)) {
+ err = PTR_ERR(nresult);
+ goto err_result;
+ }
+ dput(result);
+ result = nresult;
+
/*
* And finally make sure the dentry is actually acceptable
* to NFSD.
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index e4bb938..36a2ab6 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -699,10 +699,13 @@ static int ext2_get_blocks(struct inode *inode,
if (!partial) {
count++;
mutex_unlock(&ei->truncate_mutex);
- if (err)
- goto cleanup;
goto got_it;
}
+
+ if (err) {
+ mutex_unlock(&ei->truncate_mutex);
+ goto cleanup;
+ }
}
/*
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 44c2fff..17a75fc 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -77,6 +77,11 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
error_msg = "rec_len is too small for name_len";
else if (unlikely(((char *) de - buf) + rlen > size))
error_msg = "directory entry overrun";
+ else if (unlikely(((char *) de - buf) + rlen >
+ size - EXT4_DIR_REC_LEN(1) &&
+ ((char *) de - buf) + rlen != size)) {
+ error_msg = "directory entry too close to block end";
+ }
else if (unlikely(le32_to_cpu(de->inode) >
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
error_msg = "inode out of bounds";
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 0ed91c3..25dea6a 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -197,7 +197,12 @@ void ext4_evict_inode(struct inode *inode)
{
handle_t *handle;
int err;
- int extra_credits = 3;
+ /*
+ * Credits for final inode cleanup and freeing:
+ * sb + inode (ext4_orphan_del()), block bitmap, group descriptor
+ * (xattr block freeing), bitmap, group descriptor (inode freeing)
+ */
+ int extra_credits = 6;
struct ext4_xattr_inode_array *ea_inode_array = NULL;
trace_ext4_evict_inode(inode);
@@ -253,8 +258,12 @@ void ext4_evict_inode(struct inode *inode)
if (!IS_NOQUOTA(inode))
extra_credits += EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb);
+ /*
+ * Block bitmap, group descriptor, and inode are accounted in both
+ * ext4_blocks_for_truncate() and extra_credits. So subtract 3.
+ */
handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE,
- ext4_blocks_for_truncate(inode)+extra_credits);
+ ext4_blocks_for_truncate(inode) + extra_credits - 3);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
/*
@@ -3561,8 +3570,14 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
return ret;
}
+ /*
+ * Writes that span EOF might trigger an I/O size update on completion,
+ * so consider them to be dirty for the purposes of O_DSYNC, even if
+ * there is no other metadata changes being made or are pending here.
+ */
iomap->flags = 0;
- if (ext4_inode_datasync_dirty(inode))
+ if (ext4_inode_datasync_dirty(inode) ||
+ offset + length > i_size_read(inode))
iomap->flags |= IOMAP_F_DIRTY;
iomap->bdev = inode->i_sb->s_bdev;
iomap->dax_dev = sbi->s_daxdev;
@@ -3870,7 +3885,13 @@ static ssize_t ext4_direct_IO_read(struct kiocb *iocb, struct iov_iter *iter)
* writes & truncates and since we take care of writing back page cache,
* we are protected against page writeback as well.
*/
- inode_lock_shared(inode);
+ if (iocb->ki_flags & IOCB_NOWAIT) {
+ if (!inode_trylock_shared(inode))
+ return -EAGAIN;
+ } else {
+ inode_lock_shared(inode);
+ }
+
ret = filemap_write_and_wait_range(mapping, iocb->ki_pos,
iocb->ki_pos + count - 1);
if (ret)
@@ -5522,11 +5543,15 @@ static void ext4_wait_for_tail_page_commit(struct inode *inode)
offset = inode->i_size & (PAGE_SIZE - 1);
/*
- * All buffers in the last page remain valid? Then there's nothing to
- * do. We do the check mainly to optimize the common PAGE_SIZE ==
- * blocksize case
+ * If the page is fully truncated, we don't need to wait for any commit
+ * (and we even should not as __ext4_journalled_invalidatepage() may
+ * strip all buffers from the page but keep the page dirty which can then
+ * confuse e.g. concurrent ext4_writepage() seeing dirty page without
+ * buffers). Also we don't need to wait for any commit if all buffers in
+ * the page remain valid. This is most beneficial for the common case of
+ * blocksize == PAGESIZE.
*/
- if (offset > PAGE_SIZE - i_blocksize(inode))
+ if (!offset || offset > (PAGE_SIZE - i_blocksize(inode)))
return;
while (1) {
page = find_lock_page(inode->i_mapping,
@@ -5976,8 +6001,23 @@ static int __ext4_expand_extra_isize(struct inode *inode,
{
struct ext4_inode *raw_inode;
struct ext4_xattr_ibody_header *header;
+ unsigned int inode_size = EXT4_INODE_SIZE(inode->i_sb);
+ struct ext4_inode_info *ei = EXT4_I(inode);
int error;
+ /* this was checked at iget time, but double check for good measure */
+ if ((EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > inode_size) ||
+ (ei->i_extra_isize & 3)) {
+ EXT4_ERROR_INODE(inode, "bad extra_isize %u (inode size %u)",
+ ei->i_extra_isize,
+ EXT4_INODE_SIZE(inode->i_sb));
+ return -EFSCORRUPTED;
+ }
+ if ((new_extra_isize < ei->i_extra_isize) ||
+ (new_extra_isize < 4) ||
+ (new_extra_isize > inode_size - EXT4_GOOD_OLD_INODE_SIZE))
+ return -EINVAL; /* Should never happen */
+
raw_inode = ext4_raw_inode(iloc);
header = IHDR(inode, raw_inode);
@@ -6071,7 +6111,7 @@ int ext4_expand_extra_isize(struct inode *inode,
error = ext4_journal_get_write_access(handle, iloc->bh);
if (error) {
brelse(iloc->bh);
- goto out_stop;
+ goto out_unlock;
}
error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc,
@@ -6081,8 +6121,8 @@ int ext4_expand_extra_isize(struct inode *inode,
if (!error)
error = rc;
+out_unlock:
ext4_write_unlock_xattr(inode, &no_expand);
-out_stop:
ext4_journal_stop(handle);
return error;
}
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index a0bbcd1..c8fa2d1 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -132,6 +132,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) ||
IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) ||
+ (EXT4_I(inode)->i_flags & EXT4_JOURNAL_DATA_FL) ||
ext4_has_inline_data(inode)) {
err = -EINVAL;
goto journal_err_out;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 3c93a29..f941208 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2284,7 +2284,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
dxroot->info.indirect_levels += 1;
dxtrace(printk(KERN_DEBUG
"Creating %d level index...\n",
- info->indirect_levels));
+ dxroot->info.indirect_levels));
err = ext4_handle_dirty_dx_node(handle, dir, frame->bh);
if (err)
goto journal_error;
@@ -2693,7 +2693,7 @@ bool ext4_empty_dir(struct inode *inode)
{
unsigned int offset;
struct buffer_head *bh;
- struct ext4_dir_entry_2 *de, *de1;
+ struct ext4_dir_entry_2 *de;
struct super_block *sb;
if (ext4_has_inline_data(inode)) {
@@ -2718,19 +2718,25 @@ bool ext4_empty_dir(struct inode *inode)
return true;
de = (struct ext4_dir_entry_2 *) bh->b_data;
- de1 = ext4_next_entry(de, sb->s_blocksize);
- if (le32_to_cpu(de->inode) != inode->i_ino ||
- le32_to_cpu(de1->inode) == 0 ||
- strcmp(".", de->name) || strcmp("..", de1->name)) {
- ext4_warning_inode(inode, "directory missing '.' and/or '..'");
+ if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
+ 0) ||
+ le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) {
+ ext4_warning_inode(inode, "directory missing '.'");
brelse(bh);
return true;
}
- offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize) +
- ext4_rec_len_from_disk(de1->rec_len, sb->s_blocksize);
- de = ext4_next_entry(de1, sb->s_blocksize);
+ offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
+ de = ext4_next_entry(de, sb->s_blocksize);
+ if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
+ offset) ||
+ le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) {
+ ext4_warning_inode(inode, "directory missing '..'");
+ brelse(bh);
+ return true;
+ }
+ offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
while (offset < inode->i_size) {
- if ((void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
+ if (!(offset & (sb->s_blocksize - 1))) {
unsigned int lblock;
brelse(bh);
lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb);
@@ -2741,12 +2747,11 @@ bool ext4_empty_dir(struct inode *inode)
}
if (IS_ERR(bh))
return true;
- de = (struct ext4_dir_entry_2 *) bh->b_data;
}
+ de = (struct ext4_dir_entry_2 *) (bh->b_data +
+ (offset & (sb->s_blocksize - 1)));
if (ext4_check_dir_entry(inode, NULL, de, bh,
bh->b_data, bh->b_size, offset)) {
- de = (struct ext4_dir_entry_2 *)(bh->b_data +
- sb->s_blocksize);
offset = (offset | (sb->s_blocksize - 1)) + 1;
continue;
}
@@ -2755,7 +2760,6 @@ bool ext4_empty_dir(struct inode *inode)
return false;
}
offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
- de = ext4_next_entry(de, sb->s_blocksize);
}
brelse(bh);
return true;
@@ -3056,18 +3060,17 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
if (IS_DIRSYNC(dir))
ext4_handle_sync(handle);
- if (inode->i_nlink == 0) {
- ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
- dentry->d_name.len, dentry->d_name.name);
- set_nlink(inode, 1);
- }
retval = ext4_delete_entry(handle, dir, de, bh);
if (retval)
goto end_unlink;
dir->i_ctime = dir->i_mtime = current_time(dir);
ext4_update_dx_flag(dir);
ext4_mark_inode_dirty(handle, dir);
- drop_nlink(inode);
+ if (inode->i_nlink == 0)
+ ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
+ dentry->d_name.len, dentry->d_name.name);
+ else
+ drop_nlink(inode);
if (!inode->i_nlink)
ext4_orphan_add(handle, inode);
inode->i_ctime = current_time(inode);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 49452c8..55d33c5 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3525,12 +3525,15 @@ static void ext4_clamp_want_extra_isize(struct super_block *sb)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es;
+ unsigned def_extra_isize = sizeof(struct ext4_inode) -
+ EXT4_GOOD_OLD_INODE_SIZE;
- /* determine the minimum size of new large inodes, if present */
- if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE &&
- sbi->s_want_extra_isize == 0) {
- sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
- EXT4_GOOD_OLD_INODE_SIZE;
+ if (sbi->s_inode_size == EXT4_GOOD_OLD_INODE_SIZE) {
+ sbi->s_want_extra_isize = 0;
+ return;
+ }
+ if (sbi->s_want_extra_isize < 4) {
+ sbi->s_want_extra_isize = def_extra_isize;
if (ext4_has_feature_extra_isize(sb)) {
if (sbi->s_want_extra_isize <
le16_to_cpu(es->s_want_extra_isize))
@@ -3543,10 +3546,10 @@ static void ext4_clamp_want_extra_isize(struct super_block *sb)
}
}
/* Check if enough inode space is available */
- if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
- sbi->s_inode_size) {
- sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
- EXT4_GOOD_OLD_INODE_SIZE;
+ if ((sbi->s_want_extra_isize > sbi->s_inode_size) ||
+ (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
+ sbi->s_inode_size)) {
+ sbi->s_want_extra_isize = def_extra_isize;
ext4_msg(sb, KERN_INFO,
"required extra inode space not available");
}
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 548a747..e1354c3 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -1401,8 +1401,10 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
/* Flush all the NAT/SIT pages */
f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
- f2fs_bug_on(sbi, get_pages(sbi, F2FS_DIRTY_META) &&
- !f2fs_cp_error(sbi));
+ if (get_pages(sbi, F2FS_DIRTY_META) && !f2fs_cp_error(sbi)) {
+ WARN_ON(1);
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ }
/*
* modify checkpoint
@@ -1510,8 +1512,10 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
/* Here, we have one bio having CP pack except cp pack 2 page */
f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
- f2fs_bug_on(sbi, get_pages(sbi, F2FS_DIRTY_META) &&
- !f2fs_cp_error(sbi));
+ if (get_pages(sbi, F2FS_DIRTY_META) && !f2fs_cp_error(sbi)) {
+ WARN_ON(1);
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ }
/* wait for previous submitted meta pages writeback */
f2fs_wait_on_all_pages_writeback(sbi);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 20ac8ea..5d18cf2 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2685,6 +2685,20 @@ static inline void clear_file(struct inode *inode, int type)
f2fs_mark_inode_dirty_sync(inode, true);
}
+static inline bool f2fs_is_time_consistent(struct inode *inode)
+{
+ if (!timespec64_equal(F2FS_I(inode)->i_disk_time, &inode->i_atime))
+ return false;
+ if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 1, &inode->i_ctime))
+ return false;
+ if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 2, &inode->i_mtime))
+ return false;
+ if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 3,
+ &F2FS_I(inode)->i_crtime))
+ return false;
+ return true;
+}
+
static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync)
{
bool ret;
@@ -2702,14 +2716,7 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync)
i_size_read(inode) & ~PAGE_MASK)
return false;
- if (!timespec64_equal(F2FS_I(inode)->i_disk_time, &inode->i_atime))
- return false;
- if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 1, &inode->i_ctime))
- return false;
- if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 2, &inode->i_mtime))
- return false;
- if (!timespec64_equal(F2FS_I(inode)->i_disk_time + 3,
- &F2FS_I(inode)->i_crtime))
+ if (!f2fs_is_time_consistent(inode))
return false;
down_read(&F2FS_I(inode)->i_sem);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index f953085..4d3412a 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -794,6 +794,29 @@ static int move_data_block(struct inode *inode, block_t bidx,
if (lfs_mode)
down_write(&fio.sbi->io_order_lock);
+ mpage = f2fs_grab_cache_page(META_MAPPING(fio.sbi),
+ fio.old_blkaddr, false);
+ if (!mpage)
+ goto up_out;
+
+ fio.encrypted_page = mpage;
+
+ /* read source block in mpage */
+ if (!PageUptodate(mpage)) {
+ err = f2fs_submit_page_bio(&fio);
+ if (err) {
+ f2fs_put_page(mpage, 1);
+ goto up_out;
+ }
+ lock_page(mpage);
+ if (unlikely(mpage->mapping != META_MAPPING(fio.sbi) ||
+ !PageUptodate(mpage))) {
+ err = -EIO;
+ f2fs_put_page(mpage, 1);
+ goto up_out;
+ }
+ }
+
f2fs_allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
&sum, CURSEG_COLD_DATA, NULL, false);
@@ -801,44 +824,18 @@ static int move_data_block(struct inode *inode, block_t bidx,
newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS);
if (!fio.encrypted_page) {
err = -ENOMEM;
+ f2fs_put_page(mpage, 1);
goto recover_block;
}
- mpage = f2fs_pagecache_get_page(META_MAPPING(fio.sbi),
- fio.old_blkaddr, FGP_LOCK, GFP_NOFS);
- if (mpage) {
- bool updated = false;
-
- if (PageUptodate(mpage)) {
- memcpy(page_address(fio.encrypted_page),
- page_address(mpage), PAGE_SIZE);
- updated = true;
- }
- f2fs_put_page(mpage, 1);
- invalidate_mapping_pages(META_MAPPING(fio.sbi),
- fio.old_blkaddr, fio.old_blkaddr);
- if (updated)
- goto write_page;
- }
-
- err = f2fs_submit_page_bio(&fio);
- if (err)
- goto put_page_out;
-
- /* write page */
- lock_page(fio.encrypted_page);
-
- if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) {
- err = -EIO;
- goto put_page_out;
- }
- if (unlikely(!PageUptodate(fio.encrypted_page))) {
- err = -EIO;
- goto put_page_out;
- }
-
-write_page:
+ /* write target block */
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true, true);
+ memcpy(page_address(fio.encrypted_page),
+ page_address(mpage), PAGE_SIZE);
+ f2fs_put_page(mpage, 1);
+ invalidate_mapping_pages(META_MAPPING(fio.sbi),
+ fio.old_blkaddr, fio.old_blkaddr);
+
set_page_dirty(fio.encrypted_page);
if (clear_page_dirty_for_io(fio.encrypted_page))
dec_page_count(fio.sbi, F2FS_DIRTY_META);
@@ -869,11 +866,12 @@ static int move_data_block(struct inode *inode, block_t bidx,
put_page_out:
f2fs_put_page(fio.encrypted_page, 1);
recover_block:
- if (lfs_mode)
- up_write(&fio.sbi->io_order_lock);
if (err)
f2fs_do_replace_block(fio.sbi, &sum, newaddr, fio.old_blkaddr,
true, true);
+up_out:
+ if (lfs_mode)
+ up_write(&fio.sbi->io_order_lock);
put_out:
f2fs_put_dnode(&dn);
out:
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index e269959..15d0923 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -627,7 +627,11 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
inode->i_ino == F2FS_META_INO(sbi))
return 0;
- if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
+ /*
+ * atime could be updated without dirtying f2fs inode in lazytime mode
+ */
+ if (f2fs_is_time_consistent(inode) &&
+ !is_inode_flag_set(inode, FI_DIRTY_INODE))
return 0;
if (f2fs_is_checkpoint_ready(sbi))
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 2fdcbe9..dddd245 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -962,7 +962,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!old_dir_entry || whiteout)
file_lost_pino(old_inode);
else
- F2FS_I(old_inode)->i_pino = new_dir->i_ino;
+ /* adjust dir's i_pino to pass fsck check */
+ f2fs_i_pino_write(old_inode, new_dir->i_ino);
up_write(&F2FS_I(old_inode)->i_sem);
old_inode->i_ctime = current_time(old_inode);
@@ -1123,7 +1124,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_set_link(old_dir, old_entry, old_page, new_inode);
down_write(&F2FS_I(old_inode)->i_sem);
- file_lost_pino(old_inode);
+ if (!old_dir_entry)
+ file_lost_pino(old_inode);
+ else
+ /* adjust dir's i_pino to pass fsck check */
+ f2fs_i_pino_write(old_inode, new_dir->i_ino);
up_write(&F2FS_I(old_inode)->i_sem);
old_dir->i_ctime = current_time(old_dir);
@@ -1138,7 +1143,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
down_write(&F2FS_I(new_inode)->i_sem);
- file_lost_pino(new_inode);
+ if (!new_dir_entry)
+ file_lost_pino(new_inode);
+ else
+ /* adjust dir's i_pino to pass fsck check */
+ f2fs_i_pino_write(new_inode, old_dir->i_ino);
up_write(&F2FS_I(new_inode)->i_sem);
new_dir->i_ctime = current_time(new_dir);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 7ee86d8..a89e273 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -582,10 +582,13 @@ void wbc_attach_and_unlock_inode(struct writeback_control *wbc,
spin_unlock(&inode->i_lock);
/*
- * A dying wb indicates that the memcg-blkcg mapping has changed
- * and a new wb is already serving the memcg. Switch immediately.
+ * A dying wb indicates that either the blkcg associated with the
+ * memcg changed or the associated memcg is dying. In the first
+ * case, a replacement wb should already be available and we should
+ * refresh the wb immediately. In the second case, trying to
+ * refresh will keep failing.
*/
- if (unlikely(wb_dying(wbc->wb)))
+ if (unlikely(wb_dying(wbc->wb) && !css_is_dying(wbc->wb->memcg_css)))
inode_switch_wbs(inode, wbc->wb_id);
}
diff --git a/fs/fuse/control.c b/fs/fuse/control.c
index 0b69465..acc3581 100644
--- a/fs/fuse/control.c
+++ b/fs/fuse/control.c
@@ -107,7 +107,7 @@ static ssize_t fuse_conn_max_background_read(struct file *file,
if (!fc)
return 0;
- val = fc->max_background;
+ val = READ_ONCE(fc->max_background);
fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val);
@@ -144,7 +144,7 @@ static ssize_t fuse_conn_congestion_threshold_read(struct file *file,
if (!fc)
return 0;
- val = fc->congestion_threshold;
+ val = READ_ONCE(fc->congestion_threshold);
fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index a3516b6..2c4ceb75 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -234,7 +234,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
kfree(forget);
if (ret == -ENOMEM)
goto out;
- if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+ if (ret || fuse_invalid_attr(&outarg.attr) ||
+ (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
goto invalid;
forget_all_cached_acls(inode);
@@ -343,6 +344,12 @@ int fuse_valid_type(int m)
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
}
+bool fuse_invalid_attr(struct fuse_attr *attr)
+{
+ return !fuse_valid_type(attr->mode) ||
+ attr->size > LLONG_MAX;
+}
+
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
struct fuse_entry_out *outarg, struct inode **inode)
{
@@ -374,7 +381,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
err = -EIO;
if (!outarg->nodeid)
goto out_put_forget;
- if (!fuse_valid_type(outarg->attr.mode))
+ if (fuse_invalid_attr(&outarg->attr))
goto out_put_forget;
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
@@ -497,7 +504,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
goto out_free_ff;
err = -EIO;
- if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
+ if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) ||
+ fuse_invalid_attr(&outentry.attr))
goto out_free_ff;
ff->fh = outopen.fh;
@@ -604,7 +612,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
goto out_put_forget_req;
err = -EIO;
- if (invalid_nodeid(outarg.nodeid))
+ if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
goto out_put_forget_req;
if ((outarg.attr.mode ^ mode) & S_IFMT)
@@ -883,7 +891,8 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
spin_lock(&fc->lock);
fi->attr_version = ++fc->attr_version;
- inc_nlink(inode);
+ if (likely(inode->i_nlink < UINT_MAX))
+ inc_nlink(inode);
spin_unlock(&fc->lock);
fuse_invalidate_attr(inode);
fuse_update_ctime(inode);
@@ -963,7 +972,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
args.out.args[0].value = &outarg;
err = fuse_simple_request(fc, &args);
if (!err) {
- if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+ if (fuse_invalid_attr(&outarg.attr) ||
+ (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
make_bad_inode(inode);
err = -EIO;
} else {
@@ -1275,7 +1285,7 @@ static int fuse_direntplus_link(struct file *file,
if (invalid_nodeid(o->nodeid))
return -EIO;
- if (!fuse_valid_type(o->attr.mode))
+ if (fuse_invalid_attr(&o->attr))
return -EIO;
fc = get_fuse_conn(dir);
@@ -1711,6 +1721,19 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
if (attr->ia_valid & ATTR_SIZE)
is_truncate = true;
+ /* Flush dirty data/metadata before non-truncate SETATTR */
+ if (is_wb && S_ISREG(inode->i_mode) &&
+ attr->ia_valid &
+ (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
+ ATTR_TIMES_SET)) {
+ err = write_inode_now(inode, true);
+ if (err)
+ return err;
+
+ fuse_set_nowrite(inode);
+ fuse_release_nowrite(inode);
+ }
+
if (is_truncate) {
fuse_set_nowrite(inode);
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
@@ -1739,7 +1762,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
goto error;
}
- if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+ if (fuse_invalid_attr(&outarg.attr) ||
+ (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
make_bad_inode(inode);
err = -EIO;
goto error;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 04a0cfc..f4f9ad9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -202,7 +202,7 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
{
struct fuse_conn *fc = get_fuse_conn(inode);
int err;
- bool lock_inode = (file->f_flags & O_TRUNC) &&
+ bool is_wb_truncate = (file->f_flags & O_TRUNC) &&
fc->atomic_o_trunc &&
fc->writeback_cache;
@@ -210,16 +210,20 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
if (err)
return err;
- if (lock_inode)
+ if (is_wb_truncate) {
inode_lock(inode);
+ fuse_set_nowrite(inode);
+ }
err = fuse_do_open(fc, get_node_id(inode), file, isdir);
if (!err)
fuse_finish_open(inode, file);
- if (lock_inode)
+ if (is_wb_truncate) {
+ fuse_release_nowrite(inode);
inode_unlock(inode);
+ }
return err;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 5440c81..3d53a17 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -912,6 +912,8 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc);
*/
int fuse_valid_type(int m);
+bool fuse_invalid_attr(struct fuse_attr *attr);
+
/**
* Is current process allowed to perform filesystem operation?
*/
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 52fecced..096b479 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -2122,6 +2122,8 @@ static int do_grow(struct inode *inode, u64 size)
}
error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT +
+ (unstuff &&
+ gfs2_is_jdata(ip) ? RES_JDATA : 0) +
(sdp->sd_args.ar_quota == GFS2_QUOTA_OFF ?
0 : RES_QUOTA), 0);
if (error)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index b96d39c..5d72e8b 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -623,6 +623,7 @@ enum {
SDF_RORECOVERY = 7, /* read only recovery */
SDF_SKIP_DLM_UNLOCK = 8,
SDF_FORCE_AIL_FLUSH = 9,
+ SDF_AIL1_IO_ERROR = 10,
};
enum gfs2_freeze_state {
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index cd85092..d3f0612 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -108,7 +108,9 @@ __acquires(&sdp->sd_ail_lock)
gfs2_assert(sdp, bd->bd_tr == tr);
if (!buffer_busy(bh)) {
- if (!buffer_uptodate(bh)) {
+ if (!buffer_uptodate(bh) &&
+ !test_and_set_bit(SDF_AIL1_IO_ERROR,
+ &sdp->sd_flags)) {
gfs2_io_error_bh(sdp, bh);
*withdraw = true;
}
@@ -206,7 +208,8 @@ static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
gfs2_assert(sdp, bd->bd_tr == tr);
if (buffer_busy(bh))
continue;
- if (!buffer_uptodate(bh)) {
+ if (!buffer_uptodate(bh) &&
+ !test_and_set_bit(SDF_AIL1_IO_ERROR, &sdp->sd_flags)) {
gfs2_io_error_bh(sdp, bh);
*withdraw = true;
}
@@ -610,6 +613,14 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
list_add(&bd->bd_list, &sdp->sd_log_le_revoke);
}
+void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
+{
+ if (atomic_dec_return(&gl->gl_revokes) == 0) {
+ clear_bit(GLF_LFLUSH, &gl->gl_flags);
+ gfs2_glock_queue_put(gl);
+ }
+}
+
void gfs2_write_revokes(struct gfs2_sbd *sdp)
{
struct gfs2_trans *tr;
diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h
index 20241436..015766c 100644
--- a/fs/gfs2/log.h
+++ b/fs/gfs2/log.h
@@ -80,6 +80,7 @@ extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
extern int gfs2_logd(void *data);
extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
+extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl);
extern void gfs2_write_revokes(struct gfs2_sbd *sdp);
#endif /* __LOG_DOT_H__ */
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 8f99b39..2b3b755 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -662,10 +662,7 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list);
gl = bd->bd_gl;
- if (atomic_dec_return(&gl->gl_revokes) == 0) {
- clear_bit(GLF_LFLUSH, &gl->gl_flags);
- gfs2_glock_queue_put(gl);
- }
+ gfs2_glock_remove_revoke(gl);
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
}
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 449d0cb..c94c4ac 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -642,7 +642,10 @@ static void __rs_deltree(struct gfs2_blkreserv *rs)
RB_CLEAR_NODE(&rs->rs_node);
if (rs->rs_free) {
- struct gfs2_bitmap *bi = rbm_bi(&rs->rs_rbm);
+ u64 last_block = gfs2_rbm_to_block(&rs->rs_rbm) +
+ rs->rs_free - 1;
+ struct gfs2_rbm last_rbm = { .rgd = rs->rs_rbm.rgd, };
+ struct gfs2_bitmap *start, *last;
/* return reserved blocks to the rgrp */
BUG_ON(rs->rs_rbm.rgd->rd_reserved < rs->rs_free);
@@ -653,7 +656,13 @@ static void __rs_deltree(struct gfs2_blkreserv *rs)
it will force the number to be recalculated later. */
rgd->rd_extfail_pt += rs->rs_free;
rs->rs_free = 0;
- clear_bit(GBF_FULL, &bi->bi_flags);
+ if (gfs2_rbm_from_block(&last_rbm, last_block))
+ return;
+ start = rbm_bi(&rs->rs_rbm);
+ last = rbm_bi(&last_rbm);
+ do
+ clear_bit(GBF_FULL, &start->bi_flags);
+ while (start++ != last);
}
}
@@ -1227,7 +1236,7 @@ static int update_rgrp_lvb(struct gfs2_rgrpd *rgd)
rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags);
rl_flags &= ~GFS2_RDF_MASK;
rgd->rd_flags &= GFS2_RDF_MASK;
- rgd->rd_flags |= (rl_flags | GFS2_RDF_UPTODATE | GFS2_RDF_CHECK);
+ rgd->rd_flags |= (rl_flags | GFS2_RDF_CHECK);
if (rgd->rd_rgl->rl_unlinked == 0)
rgd->rd_flags &= ~GFS2_RDF_CHECK;
rgd->rd_free = be32_to_cpu(rgd->rd_rgl->rl_free);
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index c212893..a971862 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -854,10 +854,10 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return error;
+ flush_workqueue(gfs2_delete_workqueue);
kthread_stop(sdp->sd_quotad_process);
kthread_stop(sdp->sd_logd_process);
- flush_workqueue(gfs2_delete_workqueue);
gfs2_quota_sync(sdp->sd_vfs, 0);
gfs2_statfs_sync(sdp->sd_vfs, 0);
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 064c9a0..812b5d5 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -266,6 +266,8 @@ void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
list_del_init(&bd->bd_list);
gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke);
sdp->sd_log_num_revoke--;
+ if (bd->bd_gl)
+ gfs2_glock_remove_revoke(bd->bd_gl);
kmem_cache_free(gfs2_bufdata_cachep, bd);
tr->tr_num_revoke_rm++;
if (--n == 0)
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 59c811d..6a02cc8 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -256,12 +256,13 @@ void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *function, char *file, unsigned int line,
bool withdraw)
{
- fs_err(sdp,
- "fatal: I/O error\n"
- " block = %llu\n"
- " function = %s, file = %s, line = %u\n",
- (unsigned long long)bh->b_blocknr,
- function, file, line);
+ if (!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+ fs_err(sdp,
+ "fatal: I/O error\n"
+ " block = %llu\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)bh->b_blocknr,
+ function, file, line);
if (withdraw)
gfs2_lm_withdraw(sdp, NULL);
}
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
index da25c49..8963965 100644
--- a/fs/hfs/brec.c
+++ b/fs/hfs/brec.c
@@ -445,6 +445,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd)
/* restore search_key */
hfs_bnode_read_key(node, fd->search_key, 14);
}
+ new_node = NULL;
}
if (!rec && node->parent)
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 9bdff5e..19017d2 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -220,6 +220,30 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
return node;
}
+/* Make sure @tree has enough space for the @rsvd_nodes */
+int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+{
+ struct inode *inode = tree->inode;
+ u32 count;
+ int res;
+
+ while (tree->free_nodes < rsvd_nodes) {
+ res = hfs_extend_file(inode);
+ if (res)
+ return res;
+ HFS_I(inode)->phys_size = inode->i_size =
+ (loff_t)HFS_I(inode)->alloc_blocks *
+ HFS_SB(tree->sb)->alloc_blksz;
+ HFS_I(inode)->fs_blocks = inode->i_size >>
+ tree->sb->s_blocksize_bits;
+ inode_set_bytes(inode, inode->i_size);
+ count = inode->i_size >> tree->node_size_shift;
+ tree->free_nodes += count - tree->node_count;
+ tree->node_count = count;
+ }
+ return 0;
+}
+
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
{
struct hfs_bnode *node, *next_node;
@@ -229,26 +253,11 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
u16 off16;
u16 len;
u8 *data, byte, m;
- int i;
+ int i, res;
- while (!tree->free_nodes) {
- struct inode *inode = tree->inode;
- u32 count;
- int res;
-
- res = hfs_extend_file(inode);
- if (res)
- return ERR_PTR(res);
- HFS_I(inode)->phys_size = inode->i_size =
- (loff_t)HFS_I(inode)->alloc_blocks *
- HFS_SB(tree->sb)->alloc_blksz;
- HFS_I(inode)->fs_blocks = inode->i_size >>
- tree->sb->s_blocksize_bits;
- inode_set_bytes(inode, inode->i_size);
- count = inode->i_size >> tree->node_size_shift;
- tree->free_nodes = count - tree->node_count;
- tree->node_count = count;
- }
+ res = hfs_bmap_reserve(tree, 1);
+ if (res)
+ return ERR_PTR(res);
nidx = 0;
node = hfs_bnode_find(tree, nidx);
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h
index c8b252db..dcc2aab 100644
--- a/fs/hfs/btree.h
+++ b/fs/hfs/btree.h
@@ -82,6 +82,7 @@ struct hfs_find_data {
extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
extern void hfs_btree_close(struct hfs_btree *);
extern void hfs_btree_write(struct hfs_btree *);
+extern int hfs_bmap_reserve(struct hfs_btree *, int);
extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
extern void hfs_bmap_free(struct hfs_bnode *node);
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 8a66405..d365bf0 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -97,6 +97,14 @@ int hfs_cat_create(u32 cnid, struct inode *dir, const struct qstr *str, struct i
if (err)
return err;
+ /*
+ * Fail early and avoid ENOSPC during the btree operations. We may
+ * have to split the root node at most once.
+ */
+ err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
+ if (err)
+ goto err2;
+
hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
HFS_CDR_THD : HFS_CDR_FTH,
@@ -295,6 +303,14 @@ int hfs_cat_move(u32 cnid, struct inode *src_dir, const struct qstr *src_name,
return err;
dst_fd = src_fd;
+ /*
+ * Fail early and avoid ENOSPC during the btree operations. We may
+ * have to split the root node at most once.
+ */
+ err = hfs_bmap_reserve(src_fd.tree, 2 * src_fd.tree->depth);
+ if (err)
+ goto out;
+
/* find the old dir entry and read the data */
hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
err = hfs_brec_find(&src_fd);
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
index 5d01826..263d502 100644
--- a/fs/hfs/extent.c
+++ b/fs/hfs/extent.c
@@ -117,6 +117,10 @@ static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
if (res != -ENOENT)
return res;
+ /* Fail early and avoid ENOSPC during the btree operation */
+ res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
+ if (res)
+ return res;
hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
} else {
@@ -300,7 +304,7 @@ int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
return 0;
blocks = 0;
- for (i = 0; i < 3; extent++, i++)
+ for (i = 0; i < 3; i++)
blocks += be16_to_cpu(extent[i].count);
res = hfs_free_extents(sb, extent, blocks, blocks);
@@ -341,7 +345,9 @@ int hfs_get_block(struct inode *inode, sector_t block,
ablock = (u32)block / HFS_SB(sb)->fs_div;
if (block >= HFS_I(inode)->fs_blocks) {
- if (block > HFS_I(inode)->fs_blocks || !create)
+ if (!create)
+ return 0;
+ if (block > HFS_I(inode)->fs_blocks)
return -EIO;
if (ablock >= HFS_I(inode)->alloc_blocks) {
res = hfs_extend_file(inode);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index a2dfa1b..da243c8 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -642,6 +642,8 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr)
truncate_setsize(inode, attr->ia_size);
hfs_file_truncate(inode);
+ inode->i_atime = inode->i_mtime = inode->i_ctime =
+ current_time(inode);
}
setattr_copy(inode, attr);
diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c
index 2bab6b3..e6d5544 100644
--- a/fs/hfsplus/attributes.c
+++ b/fs/hfsplus/attributes.c
@@ -217,6 +217,11 @@ int hfsplus_create_attr(struct inode *inode,
if (err)
goto failed_init_create_attr;
+ /* Fail early and avoid ENOSPC during the btree operation */
+ err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1);
+ if (err)
+ goto failed_create_attr;
+
if (name) {
err = hfsplus_attr_build_key(sb, fd.search_key,
inode->i_ino, name);
@@ -313,6 +318,11 @@ int hfsplus_delete_attr(struct inode *inode, const char *name)
if (err)
return err;
+ /* Fail early and avoid ENOSPC during the btree operation */
+ err = hfs_bmap_reserve(fd.tree, fd.tree->depth);
+ if (err)
+ goto out;
+
if (name) {
err = hfsplus_attr_build_key(sb, fd.search_key,
inode->i_ino, name);
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
index aa17a39..1918544 100644
--- a/fs/hfsplus/brec.c
+++ b/fs/hfsplus/brec.c
@@ -449,6 +449,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd)
/* restore search_key */
hfs_bnode_read_key(node, fd->search_key, 14);
}
+ new_node = NULL;
}
if (!rec && node->parent)
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 3de3bc4..66774f4 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -342,6 +342,34 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
return node;
}
+/* Make sure @tree has enough space for the @rsvd_nodes */
+int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+{
+ struct inode *inode = tree->inode;
+ struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
+ u32 count;
+ int res;
+
+ if (rsvd_nodes <= 0)
+ return 0;
+
+ while (tree->free_nodes < rsvd_nodes) {
+ res = hfsplus_file_extend(inode, hfs_bnode_need_zeroout(tree));
+ if (res)
+ return res;
+ hip->phys_size = inode->i_size =
+ (loff_t)hip->alloc_blocks <<
+ HFSPLUS_SB(tree->sb)->alloc_blksz_shift;
+ hip->fs_blocks =
+ hip->alloc_blocks << HFSPLUS_SB(tree->sb)->fs_shift;
+ inode_set_bytes(inode, inode->i_size);
+ count = inode->i_size >> tree->node_size_shift;
+ tree->free_nodes += count - tree->node_count;
+ tree->node_count = count;
+ }
+ return 0;
+}
+
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
{
struct hfs_bnode *node, *next_node;
@@ -351,27 +379,11 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
u16 off16;
u16 len;
u8 *data, byte, m;
- int i;
+ int i, res;
- while (!tree->free_nodes) {
- struct inode *inode = tree->inode;
- struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
- u32 count;
- int res;
-
- res = hfsplus_file_extend(inode, hfs_bnode_need_zeroout(tree));
- if (res)
- return ERR_PTR(res);
- hip->phys_size = inode->i_size =
- (loff_t)hip->alloc_blocks <<
- HFSPLUS_SB(tree->sb)->alloc_blksz_shift;
- hip->fs_blocks =
- hip->alloc_blocks << HFSPLUS_SB(tree->sb)->fs_shift;
- inode_set_bytes(inode, inode->i_size);
- count = inode->i_size >> tree->node_size_shift;
- tree->free_nodes = count - tree->node_count;
- tree->node_count = count;
- }
+ res = hfs_bmap_reserve(tree, 1);
+ if (res)
+ return ERR_PTR(res);
nidx = 0;
node = hfs_bnode_find(tree, nidx);
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index a196369..35472cb 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -265,6 +265,14 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
if (err)
return err;
+ /*
+ * Fail early and avoid ENOSPC during the btree operations. We may
+ * have to split the root node at most once.
+ */
+ err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
+ if (err)
+ goto err2;
+
hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
entry_size = hfsplus_fill_cat_thread(sb, &entry,
S_ISDIR(inode->i_mode) ?
@@ -333,6 +341,14 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
if (err)
return err;
+ /*
+ * Fail early and avoid ENOSPC during the btree operations. We may
+ * have to split the root node at most once.
+ */
+ err = hfs_bmap_reserve(fd.tree, 2 * (int)fd.tree->depth - 2);
+ if (err)
+ goto out;
+
if (!str) {
int len;
@@ -433,6 +449,14 @@ int hfsplus_rename_cat(u32 cnid,
return err;
dst_fd = src_fd;
+ /*
+ * Fail early and avoid ENOSPC during the btree operations. We may
+ * have to split the root node at most twice.
+ */
+ err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1);
+ if (err)
+ goto out;
+
/* find the old dir entry and read the data */
err = hfsplus_cat_build_key(sb, src_fd.search_key,
src_dir->i_ino, src_name);
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index 8e0f597..a930ddd 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -100,6 +100,10 @@ static int __hfsplus_ext_write_extent(struct inode *inode,
if (hip->extent_state & HFSPLUS_EXT_NEW) {
if (res != -ENOENT)
return res;
+ /* Fail early and avoid ENOSPC during the btree operation */
+ res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
+ if (res)
+ return res;
hfs_brec_insert(fd, hip->cached_extents,
sizeof(hfsplus_extent_rec));
hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
@@ -233,7 +237,9 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
ablock = iblock >> sbi->fs_shift;
if (iblock >= hip->fs_blocks) {
- if (iblock > hip->fs_blocks || !create)
+ if (!create)
+ return 0;
+ if (iblock > hip->fs_blocks)
return -EIO;
if (ablock >= hip->alloc_blocks) {
res = hfsplus_file_extend(inode, false);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 8e03943..dd7ad9f 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -311,6 +311,7 @@ static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
#define hfs_btree_open hfsplus_btree_open
#define hfs_btree_close hfsplus_btree_close
#define hfs_btree_write hfsplus_btree_write
+#define hfs_bmap_reserve hfsplus_bmap_reserve
#define hfs_bmap_alloc hfsplus_bmap_alloc
#define hfs_bmap_free hfsplus_bmap_free
#define hfs_bnode_read hfsplus_bnode_read
@@ -395,6 +396,7 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors,
struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
void hfs_btree_close(struct hfs_btree *tree);
int hfs_btree_write(struct hfs_btree *tree);
+int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
void hfs_bmap_free(struct hfs_bnode *node);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 8e9427a..d7ab9d8 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -261,6 +261,7 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
}
truncate_setsize(inode, attr->ia_size);
hfsplus_file_truncate(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
}
setattr_copy(inode, attr);
diff --git a/fs/inode.c b/fs/inode.c
index 88e78f8..7c19642 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -660,6 +660,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty)
struct inode *inode, *next;
LIST_HEAD(dispose);
+again:
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) {
spin_lock(&inode->i_lock);
@@ -682,6 +683,12 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty)
inode_lru_list_del(inode);
spin_unlock(&inode->i_lock);
list_add(&inode->i_lru, &dispose);
+ if (need_resched()) {
+ spin_unlock(&sb->s_inode_list_lock);
+ cond_resched();
+ dispose_list(&dispose);
+ goto again;
+ }
}
spin_unlock(&sb->s_inode_list_lock);
diff --git a/fs/iomap.c b/fs/iomap.c
index fac4520..03edf62 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -150,13 +150,14 @@ static void
iomap_adjust_read_range(struct inode *inode, struct iomap_page *iop,
loff_t *pos, loff_t length, unsigned *offp, unsigned *lenp)
{
+ loff_t orig_pos = *pos;
+ loff_t isize = i_size_read(inode);
unsigned block_bits = inode->i_blkbits;
unsigned block_size = (1 << block_bits);
unsigned poff = offset_in_page(*pos);
unsigned plen = min_t(loff_t, PAGE_SIZE - poff, length);
unsigned first = poff >> block_bits;
unsigned last = (poff + plen - 1) >> block_bits;
- unsigned end = offset_in_page(i_size_read(inode)) >> block_bits;
/*
* If the block size is smaller than the page size we need to check the
@@ -191,8 +192,12 @@ iomap_adjust_read_range(struct inode *inode, struct iomap_page *iop,
* handle both halves separately so that we properly zero data in the
* page cache for blocks that are entirely outside of i_size.
*/
- if (first <= end && last > end)
- plen -= (last - end) * block_size;
+ if (orig_pos <= isize && orig_pos + length > isize) {
+ unsigned end = offset_in_page(isize - 1) >> block_bits;
+
+ if (first <= end && last > end)
+ plen -= (last - end) * block_size;
+ }
*offp = poff;
*lenp = plen;
@@ -1603,7 +1608,7 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
struct bio *bio;
bool need_zeroout = false;
bool use_fua = false;
- int nr_pages, ret;
+ int nr_pages, ret = 0;
size_t copied = 0;
if ((pos | length | align) & ((1 << blkbits) - 1))
@@ -1619,12 +1624,13 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
if (iomap->flags & IOMAP_F_NEW) {
need_zeroout = true;
- } else {
+ } else if (iomap->type == IOMAP_MAPPED) {
/*
- * Use a FUA write if we need datasync semantics, this
- * is a pure data IO that doesn't require any metadata
- * updates and the underlying device supports FUA. This
- * allows us to avoid cache flushes on IO completion.
+ * Use a FUA write if we need datasync semantics, this is a pure
+ * data IO that doesn't require any metadata updates (including
+ * after IO completion such as unwritten extent conversion) and
+ * the underlying device supports FUA. This allows us to avoid
+ * cache flushes on IO completion.
*/
if (!(iomap->flags & (IOMAP_F_SHARED|IOMAP_F_DIRTY)) &&
(dio->flags & IOMAP_DIO_WRITE_FUA) &&
@@ -1667,8 +1673,14 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
ret = bio_iov_iter_get_pages(bio, &iter);
if (unlikely(ret)) {
+ /*
+ * We have to stop part way through an IO. We must fall
+ * through to the sub-block tail zeroing here, otherwise
+ * this short IO may expose stale data in the tail of
+ * the block we haven't written data to.
+ */
bio_put(bio);
- return copied ? copied : ret;
+ goto zero_tail;
}
n = bio->bi_iter.bi_size;
@@ -1699,13 +1711,21 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,
dio->submit.cookie = submit_bio(bio);
} while (nr_pages);
- if (need_zeroout) {
+ /*
+ * We need to zeroout the tail of a sub-block write if the extent type
+ * requires zeroing or the write extends beyond EOF. If we don't zero
+ * the block tail in the latter case, we can expose stale data via mmap
+ * reads of the EOF block.
+ */
+zero_tail:
+ if (need_zeroout ||
+ ((dio->flags & IOMAP_DIO_WRITE) && pos >= i_size_read(inode))) {
/* zero out from the end of the write to the end of the block */
pad = pos & (fs_block_size - 1);
if (pad)
iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
}
- return copied;
+ return copied ? copied : ret;
}
static loff_t
@@ -1884,8 +1904,15 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
}
pos += ret;
- if (iov_iter_rw(iter) == READ && pos >= dio->i_size)
+ if (iov_iter_rw(iter) == READ && pos >= dio->i_size) {
+ /*
+ * We only report that we've read data up to i_size.
+ * Revert iter to a state corresponding to that as
+ * some callers (such as splice code) rely on it.
+ */
+ iov_iter_revert(iter, pos - dio->i_size);
break;
+ }
} while ((count = iov_iter_count(iter)) > 0);
blk_finish_plug(&plug);
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 24f86ff..020bd7a 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -724,7 +724,6 @@ void jbd2_journal_commit_transaction(journal_t *journal)
submit_bh(REQ_OP_WRITE, REQ_SYNC, bh);
}
cond_resched();
- stats.run.rs_blocks_logged += bufs;
/* Force a new descriptor to be generated next
time round the loop. */
@@ -811,6 +810,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
if (unlikely(!buffer_uptodate(bh)))
err = -EIO;
jbd2_unfile_log_bh(bh);
+ stats.run.rs_blocks_logged++;
/*
* The list contains temporary buffer heads created by
@@ -856,6 +856,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
BUFFER_TRACE(bh, "ph5: control buffer writeout done: unfile");
clear_buffer_jwrite(bh);
jbd2_unfile_log_bh(bh);
+ stats.run.rs_blocks_logged++;
__brelse(bh); /* One for getblk */
/* AKPM: bforget here */
}
@@ -877,6 +878,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
}
if (cbh)
err = journal_wait_on_commit_record(journal, cbh);
+ stats.run.rs_blocks_logged++;
if (jbd2_has_feature_async_commit(journal) &&
journal->j_flags & JBD2_BARRIER) {
blkdev_issue_flush(journal->j_dev, GFP_NOFS, NULL);
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 853a69e..a4a538a 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -624,7 +624,6 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
{
struct kernfs_node *kn;
u32 gen;
- int cursor;
int ret;
name = kstrdup_const(name, GFP_KERNEL);
@@ -637,11 +636,11 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
idr_preload(GFP_KERNEL);
spin_lock(&kernfs_idr_lock);
- cursor = idr_get_cursor(&root->ino_idr);
ret = idr_alloc_cyclic(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
- if (ret >= 0 && ret < cursor)
+ if (ret >= 0 && ret < root->last_ino)
root->next_generation++;
gen = root->next_generation;
+ root->last_ino = ret;
spin_unlock(&kernfs_idr_lock);
idr_preload_end();
if (ret < 0)
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 305b220..162f43b 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -72,6 +72,9 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
if (base == kn)
break;
+ if ((s - path) + 3 >= PATH_MAX)
+ return -ENAMETOOLONG;
+
strcpy(s, "../");
s += 3;
base = base->parent;
@@ -88,7 +91,7 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
if (len < 2)
return -EINVAL;
len--;
- if ((s - path) + len > PATH_MAX)
+ if ((s - path) + len >= PATH_MAX)
return -ENAMETOOLONG;
/* reverse fillup of target string from target to base */
diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
index 00d5ef5..214a2fa 100644
--- a/fs/lockd/clnt4xdr.c
+++ b/fs/lockd/clnt4xdr.c
@@ -128,24 +128,14 @@ static void encode_netobj(struct xdr_stream *xdr,
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
- u32 length;
- __be32 *p;
+ ssize_t ret;
- p = xdr_inline_decode(xdr, 4);
- if (unlikely(p == NULL))
- goto out_overflow;
- length = be32_to_cpup(p++);
- if (unlikely(length > XDR_MAX_NETOBJ))
- goto out_size;
- obj->len = length;
- obj->data = (u8 *)p;
+ ret = xdr_stream_decode_opaque_inline(xdr, (void *)&obj->data,
+ XDR_MAX_NETOBJ);
+ if (unlikely(ret < 0))
+ return -EIO;
+ obj->len = ret;
return 0;
-out_size:
- dprintk("NFS: returned netobj was too long: %u\n", length);
- return -EIO;
-out_overflow:
- print_overflow_msg(__func__, xdr);
- return -EIO;
}
/*
diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
index 2c61763..747b9c8 100644
--- a/fs/lockd/clntxdr.c
+++ b/fs/lockd/clntxdr.c
@@ -125,24 +125,14 @@ static void encode_netobj(struct xdr_stream *xdr,
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
- u32 length;
- __be32 *p;
+ ssize_t ret;
- p = xdr_inline_decode(xdr, 4);
- if (unlikely(p == NULL))
- goto out_overflow;
- length = be32_to_cpup(p++);
- if (unlikely(length > XDR_MAX_NETOBJ))
- goto out_size;
- obj->len = length;
- obj->data = (u8 *)p;
+ ret = xdr_stream_decode_opaque_inline(xdr, (void *)&obj->data,
+ XDR_MAX_NETOBJ);
+ if (unlikely(ret < 0))
+ return -EIO;
+ obj->len = ret;
return 0;
-out_size:
- dprintk("NFS: returned netobj was too long: %u\n", length);
- return -EIO;
-out_overflow:
- print_overflow_msg(__func__, xdr);
- return -EIO;
}
/*
diff --git a/fs/locks.c b/fs/locks.c
index 2ecb4db..28270e7 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -2678,7 +2678,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
}
if (inode) {
/* userspace relies on this representation of dev_t */
- seq_printf(f, "%d %02x:%02x:%ld ", fl_pid,
+ seq_printf(f, "%d %02x:%02x:%lu ", fl_pid,
MAJOR(inode->i_sb->s_dev),
MINOR(inode->i_sb->s_dev), inode->i_ino);
} else {
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 1624618..74ff459 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -54,6 +54,16 @@ nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
return false;
}
+struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode)
+{
+ struct nfs_delegation *delegation;
+
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (nfs4_is_valid_delegation(delegation, 0))
+ return delegation;
+ return NULL;
+}
+
static int
nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
{
@@ -93,7 +103,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t flags)
return nfs4_do_check_delegation(inode, flags, false);
}
-static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
+static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid)
{
struct inode *inode = state->inode;
struct file_lock *fl;
@@ -108,7 +118,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
spin_lock(&flctx->flc_lock);
restart:
list_for_each_entry(fl, list, fl_list) {
- if (nfs_file_open_context(fl->fl_file) != ctx)
+ if (nfs_file_open_context(fl->fl_file)->state != state)
continue;
spin_unlock(&flctx->flc_lock);
status = nfs4_lock_delegation_recall(fl, state, stateid);
@@ -155,7 +165,7 @@ static int nfs_delegation_claim_opens(struct inode *inode,
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
err = nfs4_open_delegation_recall(ctx, state, stateid);
if (!err)
- err = nfs_delegation_claim_locks(ctx, state, stateid);
+ err = nfs_delegation_claim_locks(state, stateid);
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
err = -EAGAIN;
mutex_unlock(&sp->so_delegreturn_mutex);
@@ -1154,7 +1164,7 @@ bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
if (delegation != NULL &&
nfs4_stateid_match_other(dst, &delegation->stateid)) {
dst->seqid = delegation->stateid.seqid;
- return ret;
+ ret = true;
}
rcu_read_unlock();
out:
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index c954778..dd0f3ee 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -66,6 +66,7 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state,
bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, struct rpc_cred **cred);
bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
+struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode);
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
int nfs4_have_delegation(struct inode *inode, fmode_t flags);
int nfs4_check_delegation(struct inode *inode, fmode_t flags);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 621e3cf..792f882 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1393,8 +1393,6 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode,
return 0;
if ((delegation->type & fmode) != fmode)
return 0;
- if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
- return 0;
switch (claim) {
case NFS4_OPEN_CLAIM_NULL:
case NFS4_OPEN_CLAIM_FH:
@@ -1751,7 +1749,6 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
{
struct nfs4_state *state = opendata->state;
- struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_delegation *delegation;
int open_mode = opendata->o_arg.open_flags;
fmode_t fmode = opendata->o_arg.fmode;
@@ -1768,7 +1765,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
}
spin_unlock(&state->owner->so_lock);
rcu_read_lock();
- delegation = rcu_dereference(nfsi->delegation);
+ delegation = nfs4_get_valid_delegation(state->inode);
if (!can_open_delegated(delegation, fmode, claim)) {
rcu_read_unlock();
break;
@@ -2293,7 +2290,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
data->o_arg.open_flags, claim))
goto out_no_action;
rcu_read_lock();
- delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
+ delegation = nfs4_get_valid_delegation(data->state->inode);
if (can_open_delegated(delegation, data->o_arg.fmode, claim))
goto unlock_no_action;
rcu_read_unlock();
@@ -5943,6 +5940,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
}
status = task->tk_status;
if (setclientid.sc_cred) {
+ kfree(clp->cl_acceptor);
clp->cl_acceptor = rpcauth_stringify_acceptor(setclientid.sc_cred);
put_rpccred(setclientid.sc_cred);
}
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index c36ef75..b3086e9 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -2613,7 +2613,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
return;
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return;
- } while (refcount_read(&clp->cl_count) > 1);
+ } while (refcount_read(&clp->cl_count) > 1 && !signalled());
goto out_drain;
out_error:
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 5ab9979..117ffd90 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -783,7 +783,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *head;
- atomic_long_dec(&nfsi->nrequests);
if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
head = req->wb_head;
@@ -796,8 +795,10 @@ static void nfs_inode_remove_request(struct nfs_page *req)
spin_unlock(&mapping->private_lock);
}
- if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags))
+ if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
nfs_release_request(req);
+ atomic_long_dec(&nfsi->nrequests);
+ }
}
static void
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 9c247fa1..5188f9f 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -662,7 +662,7 @@ struct cld_net {
struct cld_upcall {
struct list_head cu_list;
struct cld_net *cu_net;
- struct task_struct *cu_task;
+ struct completion cu_done;
struct cld_msg cu_msg;
};
@@ -671,23 +671,18 @@ __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
{
int ret;
struct rpc_pipe_msg msg;
+ struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_msg);
memset(&msg, 0, sizeof(msg));
msg.data = cmsg;
msg.len = sizeof(*cmsg);
- /*
- * Set task state before we queue the upcall. That prevents
- * wake_up_process in the downcall from racing with schedule.
- */
- set_current_state(TASK_UNINTERRUPTIBLE);
ret = rpc_queue_upcall(pipe, &msg);
if (ret < 0) {
- set_current_state(TASK_RUNNING);
goto out;
}
- schedule();
+ wait_for_completion(&cup->cu_done);
if (msg.errno < 0)
ret = msg.errno;
@@ -754,7 +749,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (copy_from_user(&cup->cu_msg, src, mlen) != 0)
return -EFAULT;
- wake_up_process(cup->cu_task);
+ complete(&cup->cu_done);
return mlen;
}
@@ -769,7 +764,7 @@ cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
if (msg->errno >= 0)
return;
- wake_up_process(cup->cu_task);
+ complete(&cup->cu_done);
}
static const struct rpc_pipe_ops cld_upcall_ops = {
@@ -900,7 +895,7 @@ alloc_cld_upcall(struct cld_net *cn)
goto restart_search;
}
}
- new->cu_task = current;
+ init_completion(&new->cu_done);
new->cu_msg.cm_vers = CLD_UPCALL_VERSION;
put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid);
new->cu_net = cn;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5f62007..c8ce128 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3072,12 +3072,17 @@ static bool replay_matches_cache(struct svc_rqst *rqstp,
(bool)seq->cachethis)
return false;
/*
- * If there's an error than the reply can have fewer ops than
- * the call. But if we cached a reply with *more* ops than the
- * call you're sending us now, then this new call is clearly not
- * really a replay of the old one:
+ * If there's an error then the reply can have fewer ops than
+ * the call.
*/
- if (slot->sl_opcnt < argp->opcnt)
+ if (slot->sl_opcnt < argp->opcnt && !slot->sl_status)
+ return false;
+ /*
+ * But if we cached a reply with *more* ops than the call you're
+ * sending us now, then this new call is clearly not really a
+ * replay of the old one:
+ */
+ if (slot->sl_opcnt > argp->opcnt)
return false;
/* This is the only check explicitly called by spec: */
if (!same_creds(&rqstp->rq_cred, &slot->sl_cred))
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index b53e763..4fe8db3 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -396,10 +396,23 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
bool get_write_count;
bool size_change = (iap->ia_valid & ATTR_SIZE);
- if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+ if (iap->ia_valid & ATTR_SIZE) {
accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
- if (iap->ia_valid & ATTR_SIZE)
ftype = S_IFREG;
+ }
+
+ /*
+ * If utimes(2) and friends are called with times not NULL, we should
+ * not set NFSD_MAY_WRITE bit. Otherwise fh_verify->nfsd_permission
+ * will return EACCESS, when the caller's effective UID does not match
+ * the owner of the file, and the caller is not privileged. In this
+ * situation, we should return EPERM(notify_change will return this).
+ */
+ if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME)) {
+ accmode |= NFSD_MAY_OWNER_OVERRIDE;
+ if (!(iap->ia_valid & (ATTR_ATIME_SET | ATTR_MTIME_SET)))
+ accmode |= NFSD_MAY_WRITE;
+ }
/* Callers that do fh_verify should do the fh_want_write: */
get_write_count = !fhp->fh_dentry;
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 170a733..e8ee426 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -90,6 +90,7 @@ void fsnotify_unmount_inodes(struct super_block *sb)
iput_inode = inode;
+ cond_resched();
spin_lock(&sb->s_inode_list_lock);
}
spin_unlock(&sb->s_inode_list_lock);
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 917fadca..b73b787 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -335,8 +335,8 @@ int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)
down_read(&OCFS2_I(inode)->ip_xattr_sem);
acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh);
up_read(&OCFS2_I(inode)->ip_xattr_sem);
- if (IS_ERR(acl) || !acl)
- return PTR_ERR(acl);
+ if (IS_ERR_OR_NULL(acl))
+ return PTR_ERR_OR_ZERO(acl);
ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (ret)
return ret;
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 7578bd5..543efa3 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -2056,7 +2056,8 @@ int ocfs2_write_end_nolock(struct address_space *mapping,
inode->i_mtime = inode->i_ctime = current_time(inode);
di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
- ocfs2_update_inode_fsync_trans(handle, inode, 1);
+ if (handle)
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
}
if (handle)
ocfs2_journal_dirty(handle, wc->w_di_bh);
@@ -2153,13 +2154,30 @@ static int ocfs2_dio_wr_get_block(struct inode *inode, sector_t iblock,
struct ocfs2_dio_write_ctxt *dwc = NULL;
struct buffer_head *di_bh = NULL;
u64 p_blkno;
- loff_t pos = iblock << inode->i_sb->s_blocksize_bits;
+ unsigned int i_blkbits = inode->i_sb->s_blocksize_bits;
+ loff_t pos = iblock << i_blkbits;
+ sector_t endblk = (i_size_read(inode) - 1) >> i_blkbits;
unsigned len, total_len = bh_result->b_size;
int ret = 0, first_get_block = 0;
len = osb->s_clustersize - (pos & (osb->s_clustersize - 1));
len = min(total_len, len);
+ /*
+ * bh_result->b_size is count in get_more_blocks according to write
+ * "pos" and "end", we need map twice to return different buffer state:
+ * 1. area in file size, not set NEW;
+ * 2. area out file size, set NEW.
+ *
+ * iblock endblk
+ * |--------|---------|---------|---------
+ * |<-------area in file------->|
+ */
+
+ if ((iblock <= endblk) &&
+ ((iblock + ((len - 1) >> i_blkbits)) > endblk))
+ len = (endblk - iblock + 1) << i_blkbits;
+
mlog(0, "get block of %lu at %llu:%u req %u\n",
inode->i_ino, pos, len, total_len);
@@ -2243,6 +2261,9 @@ static int ocfs2_dio_wr_get_block(struct inode *inode, sector_t iblock,
if (desc->c_needs_zero)
set_buffer_new(bh_result);
+ if (iblock > endblk)
+ set_buffer_new(bh_result);
+
/* May sleep in end_io. It should not happen in a irq context. So defer
* it to dio work queue. */
set_buffer_defer_completion(bh_result);
diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c
index 9f8250d..f9b84f7 100644
--- a/fs/ocfs2/buffer_head_io.c
+++ b/fs/ocfs2/buffer_head_io.c
@@ -99,25 +99,34 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh,
return ret;
}
+/* Caller must provide a bhs[] with all NULL or non-NULL entries, so it
+ * will be easier to handle read failure.
+ */
int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
unsigned int nr, struct buffer_head *bhs[])
{
int status = 0;
unsigned int i;
struct buffer_head *bh;
+ int new_bh = 0;
trace_ocfs2_read_blocks_sync((unsigned long long)block, nr);
if (!nr)
goto bail;
+ /* Don't put buffer head and re-assign it to NULL if it is allocated
+ * outside since the caller can't be aware of this alternation!
+ */
+ new_bh = (bhs[0] == NULL);
+
for (i = 0 ; i < nr ; i++) {
if (bhs[i] == NULL) {
bhs[i] = sb_getblk(osb->sb, block++);
if (bhs[i] == NULL) {
status = -ENOMEM;
mlog_errno(status);
- goto bail;
+ break;
}
}
bh = bhs[i];
@@ -157,9 +166,26 @@ int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
submit_bh(REQ_OP_READ, 0, bh);
}
+read_failure:
for (i = nr; i > 0; i--) {
bh = bhs[i - 1];
+ if (unlikely(status)) {
+ if (new_bh && bh) {
+ /* If middle bh fails, let previous bh
+ * finish its read and then put it to
+ * aovoid bh leak
+ */
+ if (!buffer_jbd(bh))
+ wait_on_buffer(bh);
+ put_bh(bh);
+ bhs[i - 1] = NULL;
+ } else if (bh && buffer_uptodate(bh)) {
+ clear_buffer_uptodate(bh);
+ }
+ continue;
+ }
+
/* No need to wait on the buffer if it's managed by JBD. */
if (!buffer_jbd(bh))
wait_on_buffer(bh);
@@ -169,8 +195,7 @@ int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
* so we can safely record this and loop back
* to cleanup the other buffers. */
status = -EIO;
- put_bh(bh);
- bhs[i - 1] = NULL;
+ goto read_failure;
}
}
@@ -178,6 +203,9 @@ int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
return status;
}
+/* Caller must provide a bhs[] with all NULL or non-NULL entries, so it
+ * will be easier to handle read failure.
+ */
int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
struct buffer_head *bhs[], int flags,
int (*validate)(struct super_block *sb,
@@ -187,6 +215,7 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
int i, ignore_cache = 0;
struct buffer_head *bh;
struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
+ int new_bh = 0;
trace_ocfs2_read_blocks_begin(ci, (unsigned long long)block, nr, flags);
@@ -212,6 +241,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
goto bail;
}
+ /* Don't put buffer head and re-assign it to NULL if it is allocated
+ * outside since the caller can't be aware of this alternation!
+ */
+ new_bh = (bhs[0] == NULL);
+
ocfs2_metadata_cache_io_lock(ci);
for (i = 0 ; i < nr ; i++) {
if (bhs[i] == NULL) {
@@ -220,7 +254,8 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
ocfs2_metadata_cache_io_unlock(ci);
status = -ENOMEM;
mlog_errno(status);
- goto bail;
+ /* Don't forget to put previous bh! */
+ break;
}
}
bh = bhs[i];
@@ -314,16 +349,27 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
}
}
- status = 0;
-
+read_failure:
for (i = (nr - 1); i >= 0; i--) {
bh = bhs[i];
if (!(flags & OCFS2_BH_READAHEAD)) {
- if (status) {
- /* Clear the rest of the buffers on error */
- put_bh(bh);
- bhs[i] = NULL;
+ if (unlikely(status)) {
+ /* Clear the buffers on error including those
+ * ever succeeded in reading
+ */
+ if (new_bh && bh) {
+ /* If middle bh fails, let previous bh
+ * finish its read and then put it to
+ * aovoid bh leak
+ */
+ if (!buffer_jbd(bh))
+ wait_on_buffer(bh);
+ put_bh(bh);
+ bhs[i] = NULL;
+ } else if (bh && buffer_uptodate(bh)) {
+ clear_buffer_uptodate(bh);
+ }
continue;
}
/* We know this can't have changed as we hold the
@@ -341,9 +387,7 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
* uptodate. */
status = -EIO;
clear_buffer_needs_validate(bh);
- put_bh(bh);
- bhs[i] = NULL;
- continue;
+ goto read_failure;
}
if (buffer_needs_validate(bh)) {
@@ -353,11 +397,8 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
BUG_ON(buffer_jbd(bh));
clear_buffer_needs_validate(bh);
status = validate(sb, bh);
- if (status) {
- put_bh(bh);
- bhs[i] = NULL;
- continue;
- }
+ if (status)
+ goto read_failure;
}
}
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
index 9b984ca..1d6dc84 100644
--- a/fs/ocfs2/dlm/dlmdebug.c
+++ b/fs/ocfs2/dlm/dlmdebug.c
@@ -329,7 +329,7 @@ void dlm_print_one_mle(struct dlm_master_list_entry *mle)
{
char *buf;
- buf = (char *) get_zeroed_page(GFP_NOFS);
+ buf = (char *) get_zeroed_page(GFP_ATOMIC);
if (buf) {
dump_mle(mle, buf, PAGE_SIZE - 1);
free_page((unsigned long)buf);
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 933aac5..178cb9e 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3603,7 +3603,7 @@ static int ocfs2_downconvert_lock(struct ocfs2_super *osb,
* we can recover correctly from node failure. Otherwise, we may get
* invalid LVB in LKB, but without DLM_SBF_VALNOTVALID being set.
*/
- if (!ocfs2_is_o2cb_active() &&
+ if (ocfs2_userspace_stack(osb) &&
lockres->l_ops->flags & LOCK_TYPE_USES_LVB)
lvb = 1;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 9fa35cb..a3e077f 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2106,54 +2106,90 @@ static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos)
return 0;
}
-static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
- struct file *file,
- loff_t pos, size_t count,
- int *meta_level)
+static int ocfs2_inode_lock_for_extent_tree(struct inode *inode,
+ struct buffer_head **di_bh,
+ int meta_level,
+ int overwrite_io,
+ int write_sem,
+ int wait)
{
- int ret;
- struct buffer_head *di_bh = NULL;
- u32 cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
- u32 clusters =
- ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos;
+ int ret = 0;
- ret = ocfs2_inode_lock(inode, &di_bh, 1);
- if (ret) {
- mlog_errno(ret);
+ if (wait)
+ ret = ocfs2_inode_lock(inode, NULL, meta_level);
+ else
+ ret = ocfs2_try_inode_lock(inode,
+ overwrite_io ? NULL : di_bh, meta_level);
+ if (ret < 0)
goto out;
+
+ if (wait) {
+ if (write_sem)
+ down_write(&OCFS2_I(inode)->ip_alloc_sem);
+ else
+ down_read(&OCFS2_I(inode)->ip_alloc_sem);
+ } else {
+ if (write_sem)
+ ret = down_write_trylock(&OCFS2_I(inode)->ip_alloc_sem);
+ else
+ ret = down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem);
+
+ if (!ret) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
}
- *meta_level = 1;
-
- ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
- if (ret)
- mlog_errno(ret);
-out:
- brelse(di_bh);
return ret;
+
+out_unlock:
+ brelse(*di_bh);
+ ocfs2_inode_unlock(inode, meta_level);
+out:
+ return ret;
+}
+
+static void ocfs2_inode_unlock_for_extent_tree(struct inode *inode,
+ struct buffer_head **di_bh,
+ int meta_level,
+ int write_sem)
+{
+ if (write_sem)
+ up_write(&OCFS2_I(inode)->ip_alloc_sem);
+ else
+ up_read(&OCFS2_I(inode)->ip_alloc_sem);
+
+ brelse(*di_bh);
+ *di_bh = NULL;
+
+ if (meta_level >= 0)
+ ocfs2_inode_unlock(inode, meta_level);
}
static int ocfs2_prepare_inode_for_write(struct file *file,
loff_t pos, size_t count, int wait)
{
int ret = 0, meta_level = 0, overwrite_io = 0;
+ int write_sem = 0;
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = d_inode(dentry);
struct buffer_head *di_bh = NULL;
loff_t end;
+ u32 cpos;
+ u32 clusters;
/*
* We start with a read level meta lock and only jump to an ex
* if we need to make modifications here.
*/
for(;;) {
- if (wait)
- ret = ocfs2_inode_lock(inode, NULL, meta_level);
- else
- ret = ocfs2_try_inode_lock(inode,
- overwrite_io ? NULL : &di_bh, meta_level);
+ ret = ocfs2_inode_lock_for_extent_tree(inode,
+ &di_bh,
+ meta_level,
+ overwrite_io,
+ write_sem,
+ wait);
if (ret < 0) {
- meta_level = -1;
if (ret != -EAGAIN)
mlog_errno(ret);
goto out;
@@ -2165,15 +2201,8 @@ static int ocfs2_prepare_inode_for_write(struct file *file,
*/
if (!wait && !overwrite_io) {
overwrite_io = 1;
- if (!down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem)) {
- ret = -EAGAIN;
- goto out_unlock;
- }
ret = ocfs2_overwrite_io(inode, di_bh, pos, count);
- brelse(di_bh);
- di_bh = NULL;
- up_read(&OCFS2_I(inode)->ip_alloc_sem);
if (ret < 0) {
if (ret != -EAGAIN)
mlog_errno(ret);
@@ -2192,7 +2221,10 @@ static int ocfs2_prepare_inode_for_write(struct file *file,
* set inode->i_size at the end of a write. */
if (should_remove_suid(dentry)) {
if (meta_level == 0) {
- ocfs2_inode_unlock(inode, meta_level);
+ ocfs2_inode_unlock_for_extent_tree(inode,
+ &di_bh,
+ meta_level,
+ write_sem);
meta_level = 1;
continue;
}
@@ -2208,18 +2240,32 @@ static int ocfs2_prepare_inode_for_write(struct file *file,
ret = ocfs2_check_range_for_refcount(inode, pos, count);
if (ret == 1) {
- ocfs2_inode_unlock(inode, meta_level);
- meta_level = -1;
+ ocfs2_inode_unlock_for_extent_tree(inode,
+ &di_bh,
+ meta_level,
+ write_sem);
+ ret = ocfs2_inode_lock_for_extent_tree(inode,
+ &di_bh,
+ meta_level,
+ overwrite_io,
+ 1,
+ wait);
+ write_sem = 1;
+ if (ret < 0) {
+ if (ret != -EAGAIN)
+ mlog_errno(ret);
+ goto out;
+ }
- ret = ocfs2_prepare_inode_for_refcount(inode,
- file,
- pos,
- count,
- &meta_level);
+ cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
+ clusters =
+ ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos;
+ ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
}
if (ret < 0) {
- mlog_errno(ret);
+ if (ret != -EAGAIN)
+ mlog_errno(ret);
goto out_unlock;
}
@@ -2230,10 +2276,10 @@ static int ocfs2_prepare_inode_for_write(struct file *file,
trace_ocfs2_prepare_inode_for_write(OCFS2_I(inode)->ip_blkno,
pos, count, wait);
- brelse(di_bh);
-
- if (meta_level >= 0)
- ocfs2_inode_unlock(inode, meta_level);
+ ocfs2_inode_unlock_for_extent_tree(inode,
+ &di_bh,
+ meta_level,
+ write_sem);
out:
return ret;
@@ -2343,7 +2389,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb,
written = __generic_file_write_iter(iocb, from);
/* buffered aio wouldn't have proper lock coverage today */
- BUG_ON(written == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT));
+ BUG_ON(written == -EIOCBQUEUED && !direct_io);
/*
* deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io
@@ -2463,7 +2509,7 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
trace_generic_file_read_iter_ret(ret);
/* buffered aio wouldn't have proper lock coverage today */
- BUG_ON(ret == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT));
+ BUG_ON(ret == -EIOCBQUEUED && !direct_io);
/* see ocfs2_file_write_iter */
if (ret == -EIOCBQUEUED || !ocfs2_iocb_is_rw_locked(iocb)) {
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726a..a6c3282 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -290,7 +290,7 @@ static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
if (inode_alloc)
inode_lock(inode_alloc);
- if (o2info_coherent(&fi->ifi_req)) {
+ if (inode_alloc && o2info_coherent(&fi->ifi_req)) {
status = ocfs2_inode_lock(inode_alloc, &bh, 0);
if (status < 0) {
mlog_errno(status);
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index c492cbb..fc1f209e 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -1018,7 +1018,8 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
mlog_errno(status);
}
- if (status == 0) {
+ /* Shutdown the kernel journal system */
+ if (!jbd2_journal_destroy(journal->j_journal) && !status) {
/*
* Do not toggle if flush was unsuccessful otherwise
* will leave dirty metadata in a "clean" journal
@@ -1027,9 +1028,6 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
if (status < 0)
mlog_errno(status);
}
-
- /* Shutdown the kernel journal system */
- jbd2_journal_destroy(journal->j_journal);
journal->j_journal = NULL;
OCFS2_I(inode)->ip_open_count--;
@@ -1379,15 +1377,23 @@ static int __ocfs2_recovery_thread(void *arg)
int rm_quota_used = 0, i;
struct ocfs2_quota_recovery *qrec;
+ /* Whether the quota supported. */
+ int quota_enabled = OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb,
+ OCFS2_FEATURE_RO_COMPAT_USRQUOTA)
+ || OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb,
+ OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+
status = ocfs2_wait_on_mount(osb);
if (status < 0) {
goto bail;
}
- rm_quota = kcalloc(osb->max_slots, sizeof(int), GFP_NOFS);
- if (!rm_quota) {
- status = -ENOMEM;
- goto bail;
+ if (quota_enabled) {
+ rm_quota = kcalloc(osb->max_slots, sizeof(int), GFP_NOFS);
+ if (!rm_quota) {
+ status = -ENOMEM;
+ goto bail;
+ }
}
restart:
status = ocfs2_super_lock(osb, 1);
@@ -1423,9 +1429,14 @@ static int __ocfs2_recovery_thread(void *arg)
* then quota usage would be out of sync until some node takes
* the slot. So we remember which nodes need quota recovery
* and when everything else is done, we recover quotas. */
- for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++);
- if (i == rm_quota_used)
- rm_quota[rm_quota_used++] = slot_num;
+ if (quota_enabled) {
+ for (i = 0; i < rm_quota_used
+ && rm_quota[i] != slot_num; i++)
+ ;
+
+ if (i == rm_quota_used)
+ rm_quota[rm_quota_used++] = slot_num;
+ }
status = ocfs2_recover_node(osb, node_num, slot_num);
skip_recovery:
@@ -1453,16 +1464,19 @@ static int __ocfs2_recovery_thread(void *arg)
/* Now it is right time to recover quotas... We have to do this under
* superblock lock so that no one can start using the slot (and crash)
* before we recover it */
- for (i = 0; i < rm_quota_used; i++) {
- qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
- if (IS_ERR(qrec)) {
- status = PTR_ERR(qrec);
- mlog_errno(status);
- continue;
+ if (quota_enabled) {
+ for (i = 0; i < rm_quota_used; i++) {
+ qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
+ if (IS_ERR(qrec)) {
+ status = PTR_ERR(qrec);
+ mlog_errno(status);
+ continue;
+ }
+ ocfs2_queue_recovery_completion(osb->journal,
+ rm_quota[i],
+ NULL, NULL, qrec,
+ ORPHAN_NEED_TRUNCATE);
}
- ocfs2_queue_recovery_completion(osb->journal, rm_quota[i],
- NULL, NULL, qrec,
- ORPHAN_NEED_TRUNCATE);
}
ocfs2_super_unlock(osb, 1);
@@ -1484,7 +1498,8 @@ static int __ocfs2_recovery_thread(void *arg)
mutex_unlock(&osb->recovery_lock);
- kfree(rm_quota);
+ if (quota_enabled)
+ kfree(rm_quota);
/* no one is callint kthread_stop() for us so the kthread() api
* requires that we call do_exit(). And it isn't exported, but
diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c
index f55f82c..1565dd8 100644
--- a/fs/ocfs2/move_extents.c
+++ b/fs/ocfs2/move_extents.c
@@ -25,6 +25,7 @@
#include "ocfs2_ioctl.h"
#include "alloc.h"
+#include "localalloc.h"
#include "aops.h"
#include "dlmglue.h"
#include "extent_map.h"
@@ -222,6 +223,7 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
struct ocfs2_refcount_tree *ref_tree = NULL;
u32 new_phys_cpos, new_len;
u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+ int need_free = 0;
if ((ext_flags & OCFS2_EXT_REFCOUNTED) && *len) {
BUG_ON(!ocfs2_is_refcount_inode(inode));
@@ -312,6 +314,7 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
if (!partial) {
context->range->me_flags &= ~OCFS2_MOVE_EXT_FL_COMPLETE;
ret = -ENOSPC;
+ need_free = 1;
goto out_commit;
}
}
@@ -336,6 +339,20 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
mlog_errno(ret);
out_commit:
+ if (need_free && context->data_ac) {
+ struct ocfs2_alloc_context *data_ac = context->data_ac;
+
+ if (context->data_ac->ac_which == OCFS2_AC_USE_LOCAL)
+ ocfs2_free_local_alloc_bits(osb, handle, data_ac,
+ new_phys_cpos, new_len);
+ else
+ ocfs2_free_clusters(handle,
+ data_ac->ac_inode,
+ data_ac->ac_bh,
+ ocfs2_clusters_to_blocks(osb->sb, new_phys_cpos),
+ new_len);
+ }
+
ocfs2_commit_trans(osb, handle);
out_unlock_mutex:
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index 7a92219..eda8348 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -728,7 +728,7 @@ static int ocfs2_release_dquot(struct dquot *dquot)
mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
- if (atomic_read(&dquot->dq_count) > 1)
+ if (dquot_is_busy(dquot))
goto out;
/* Running from downconvert thread? Postpone quota processing to wq */
if (current == osb->dc_task) {
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index d6c350b..c4b029c 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -48,12 +48,6 @@ static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl";
*/
static struct ocfs2_stack_plugin *active_stack;
-inline int ocfs2_is_o2cb_active(void)
-{
- return !strcmp(active_stack->sp_name, OCFS2_STACK_PLUGIN_O2CB);
-}
-EXPORT_SYMBOL_GPL(ocfs2_is_o2cb_active);
-
static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name)
{
struct ocfs2_stack_plugin *p;
diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h
index e3036e1..f2dce10 100644
--- a/fs/ocfs2/stackglue.h
+++ b/fs/ocfs2/stackglue.h
@@ -298,9 +298,6 @@ void ocfs2_stack_glue_set_max_proto_version(struct ocfs2_protocol_version *max_p
int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin);
void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin);
-/* In ocfs2_downconvert_lock(), we need to know which stack we are using */
-int ocfs2_is_o2cb_active(void);
-
extern struct kset *ocfs2_kset;
#endif /* STACKGLUE_H */
diff --git a/fs/orangefs/orangefs-sysfs.c b/fs/orangefs/orangefs-sysfs.c
index dd28079..19739aa 100644
--- a/fs/orangefs/orangefs-sysfs.c
+++ b/fs/orangefs/orangefs-sysfs.c
@@ -323,7 +323,7 @@ static ssize_t sysfs_service_op_show(struct kobject *kobj,
/* Can't do a service_operation if the client is not running... */
rc = is_daemon_in_service();
if (rc) {
- pr_info("%s: Client not running :%d:\n",
+ pr_info_ratelimited("%s: Client not running :%d:\n",
__func__,
is_daemon_in_service());
goto out;
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 57cf4b6..7b5357a 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1177,7 +1177,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
if (newdentry == trap)
goto out_dput;
- if (WARN_ON(olddentry->d_inode == newdentry->d_inode))
+ if (olddentry->d_inode == newdentry->d_inode)
goto out_dput;
err = 0;
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index be6463e..595ea0d 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -203,8 +203,14 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
(!ovl_verify_lower(dentry->d_sb) &&
(is_dir || lowerstat.nlink == 1))) {
- stat->ino = lowerstat.ino;
lower_layer = ovl_layer_lower(dentry);
+ /*
+ * Cannot use origin st_dev;st_ino because
+ * origin inode content may differ from overlay
+ * inode content.
+ */
+ if (samefs || lower_layer->fsid)
+ stat->ino = lowerstat.ino;
}
/*
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 64c1f23..9a98bc9 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -864,6 +864,8 @@ static int show_smap(struct seq_file *m, void *v)
__show_smap(m, &mss);
+ seq_printf(m, "THPeligible: %d\n", transparent_hugepage_enabled(vma));
+
if (arch_pkeys_enabled())
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
show_smap_vma_flags(m, vma);
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index cbde728..5c5f161 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -177,6 +177,16 @@ int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma,
}
/*
+ * Architectures which support memory encryption override this.
+ */
+ssize_t __weak
+copy_oldmem_page_encrypted(unsigned long pfn, char *buf, size_t csize,
+ unsigned long offset, int userbuf)
+{
+ return copy_oldmem_page(pfn, buf, csize, offset, userbuf);
+}
+
+/*
* Copy to either kernel or user space
*/
static int copy_to(void *target, void *src, size_t size, int userbuf)
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 015d74e..bafbab2 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -301,6 +301,7 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
GFP_KERNEL);
if (!tmp_prz)
return -ENOMEM;
+ prz = tmp_prz;
free_prz = true;
while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) {
@@ -323,7 +324,6 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
goto out;
}
record->id = 0;
- prz = tmp_prz;
}
}
@@ -437,6 +437,17 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
prz = cxt->dprzs[cxt->dump_write_cnt];
+ /*
+ * Since this is a new crash dump, we need to reset the buffer in
+ * case it still has an old dump present. Without this, the new dump
+ * will get appended, which would seriously confuse anything trying
+ * to check dump file contents. Specifically, ramoops_read_kmsg_hdr()
+ * expects to find a dump header in the beginning of buffer data, so
+ * we must to reset the buffer values, in order to ensure that the
+ * header will be written to the beginning of the buffer.
+ */
+ persistent_ram_zap(prz);
+
/* Build header and append record contents. */
hlen = ramoops_write_kmsg_hdr(prz, record);
size = record->size;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index dd1783e..1d1d393 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -491,7 +491,7 @@ int dquot_release(struct dquot *dquot)
mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
- if (atomic_read(&dquot->dq_count) > 1)
+ if (dquot_is_busy(dquot))
goto out_dqlock;
if (dqopt->ops[dquot->dq_id.type]->release_dqblk) {
ret = dqopt->ops[dquot->dq_id.type]->release_dqblk(dquot);
@@ -617,7 +617,7 @@ EXPORT_SYMBOL(dquot_scan_active);
/* Write all dquot structures to quota files */
int dquot_writeback_dquots(struct super_block *sb, int type)
{
- struct list_head *dirty;
+ struct list_head dirty;
struct dquot *dquot;
struct quota_info *dqopt = sb_dqopt(sb);
int cnt;
@@ -631,9 +631,10 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
if (!sb_has_quota_active(sb, cnt))
continue;
spin_lock(&dq_list_lock);
- dirty = &dqopt->info[cnt].dqi_dirty_list;
- while (!list_empty(dirty)) {
- dquot = list_first_entry(dirty, struct dquot,
+ /* Move list away to avoid livelock. */
+ list_replace_init(&dqopt->info[cnt].dqi_dirty_list, &dirty);
+ while (!list_empty(&dirty)) {
+ dquot = list_first_entry(&dirty, struct dquot,
dq_dirty);
WARN_ON(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags));
@@ -979,6 +980,7 @@ static int add_dquot_ref(struct super_block *sb, int type)
* later.
*/
old_inode = inode;
+ cond_resched();
spin_lock(&sb->s_inode_list_lock);
}
spin_unlock(&sb->s_inode_list_lock);
@@ -2852,68 +2854,73 @@ EXPORT_SYMBOL(dquot_quotactl_sysfile_ops);
static int do_proc_dqstats(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
- unsigned int type = (int *)table->data - dqstats.stat;
+ unsigned int type = (unsigned long *)table->data - dqstats.stat;
+ s64 value = percpu_counter_sum(&dqstats.counter[type]);
+
+ /* Filter negative values for non-monotonic counters */
+ if (value < 0 && (type == DQST_ALLOC_DQUOTS ||
+ type == DQST_FREE_DQUOTS))
+ value = 0;
/* Update global table */
- dqstats.stat[type] =
- percpu_counter_sum_positive(&dqstats.counter[type]);
- return proc_dointvec(table, write, buffer, lenp, ppos);
+ dqstats.stat[type] = value;
+ return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
}
static struct ctl_table fs_dqstats_table[] = {
{
.procname = "lookups",
.data = &dqstats.stat[DQST_LOOKUPS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "drops",
.data = &dqstats.stat[DQST_DROPS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "reads",
.data = &dqstats.stat[DQST_READS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "writes",
.data = &dqstats.stat[DQST_WRITES],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "cache_hits",
.data = &dqstats.stat[DQST_CACHE_HITS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "allocated_dquots",
.data = &dqstats.stat[DQST_ALLOC_DQUOTS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "free_dquots",
.data = &dqstats.stat[DQST_FREE_DQUOTS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
{
.procname = "syncs",
.data = &dqstats.stat[DQST_SYNCS],
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned long),
.mode = 0444,
.proc_handler = do_proc_dqstats,
},
diff --git a/fs/read_write.c b/fs/read_write.c
index 2f86e55..4954fae 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1719,6 +1719,34 @@ static int clone_verify_area(struct file *file, loff_t pos, u64 len, bool write)
return security_file_permission(file, write ? MAY_WRITE : MAY_READ);
}
+/*
+ * Ensure that we don't remap a partial EOF block in the middle of something
+ * else. Assume that the offsets have already been checked for block
+ * alignment.
+ *
+ * For deduplication we always scale down to the previous block because we
+ * can't meaningfully compare post-EOF contents.
+ *
+ * For clone we only link a partial EOF block above the destination file's EOF.
+ */
+static int generic_remap_check_len(struct inode *inode_in,
+ struct inode *inode_out,
+ loff_t pos_out,
+ u64 *len,
+ bool is_dedupe)
+{
+ u64 blkmask = i_blocksize(inode_in) - 1;
+
+ if ((*len & blkmask) == 0)
+ return 0;
+
+ if (is_dedupe)
+ *len &= ~blkmask;
+ else if (pos_out + *len < i_size_read(inode_out))
+ return -EINVAL;
+
+ return 0;
+}
/*
* Check that the two inodes are eligible for cloning, the ranges make
@@ -1825,6 +1853,11 @@ int vfs_clone_file_prep_inodes(struct inode *inode_in, loff_t pos_in,
return -EBADE;
}
+ ret = generic_remap_check_len(inode_in, inode_out, pos_out, len,
+ is_dedupe);
+ if (ret)
+ return ret;
+
return 1;
}
EXPORT_SYMBOL(vfs_clone_file_prep_inodes);
diff --git a/fs/readdir.c b/fs/readdir.c
index d97f548..443270f 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -65,6 +65,40 @@ int iterate_dir(struct file *file, struct dir_context *ctx)
EXPORT_SYMBOL(iterate_dir);
/*
+ * POSIX says that a dirent name cannot contain NULL or a '/'.
+ *
+ * It's not 100% clear what we should really do in this case.
+ * The filesystem is clearly corrupted, but returning a hard
+ * error means that you now don't see any of the other names
+ * either, so that isn't a perfect alternative.
+ *
+ * And if you return an error, what error do you use? Several
+ * filesystems seem to have decided on EUCLEAN being the error
+ * code for EFSCORRUPTED, and that may be the error to use. Or
+ * just EIO, which is perhaps more obvious to users.
+ *
+ * In order to see the other file names in the directory, the
+ * caller might want to make this a "soft" error: skip the
+ * entry, and return the error at the end instead.
+ *
+ * Note that this should likely do a "memchr(name, 0, len)"
+ * check too, since that would be filesystem corruption as
+ * well. However, that case can't actually confuse user space,
+ * which has to do a strlen() on the name anyway to find the
+ * filename length, and the above "soft error" worry means
+ * that it's probably better left alone until we have that
+ * issue clarified.
+ */
+static int verify_dirent_name(const char *name, int len)
+{
+ if (!len)
+ return -EIO;
+ if (memchr(name, '/', len))
+ return -EIO;
+ return 0;
+}
+
+/*
* Traditional linux readdir() handling..
*
* "count=1" is a special case, meaning that the buffer is one
@@ -173,6 +207,9 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
sizeof(long));
+ buf->error = verify_dirent_name(name, namlen);
+ if (unlikely(buf->error))
+ return buf->error;
buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
return -EINVAL;
@@ -259,6 +296,9 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
sizeof(u64));
+ buf->error = verify_dirent_name(name, namlen);
+ if (unlikely(buf->error))
+ return buf->error;
buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
return -EINVAL;
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 132ec44..6419e6d 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2097,6 +2097,15 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,
goto out_inserted_sd;
}
+ /*
+ * Mark it private if we're creating the privroot
+ * or something under it.
+ */
+ if (IS_PRIVATE(dir) || dentry == REISERFS_SB(sb)->priv_root) {
+ inode->i_flags |= S_PRIVATE;
+ inode->i_opflags &= ~IOP_XATTR;
+ }
+
if (reiserfs_posixacl(inode->i_sb)) {
reiserfs_write_unlock(inode->i_sb);
retval = reiserfs_inherit_default_acl(th, dir, dentry, inode);
@@ -2111,8 +2120,7 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,
reiserfs_warning(inode->i_sb, "jdm-13090",
"ACLs aren't enabled in the fs, "
"but vfs thinks they are!");
- } else if (IS_PRIVATE(dir))
- inode->i_flags |= S_PRIVATE;
+ }
if (security->name) {
reiserfs_write_unlock(inode->i_sb);
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 97f3fc4..959a066 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -377,10 +377,13 @@ static struct dentry *reiserfs_lookup(struct inode *dir, struct dentry *dentry,
/*
* Propagate the private flag so we know we're
- * in the priv tree
+ * in the priv tree. Also clear IOP_XATTR
+ * since we don't have xattrs on xattr files.
*/
- if (IS_PRIVATE(dir))
+ if (IS_PRIVATE(dir)) {
inode->i_flags |= S_PRIVATE;
+ inode->i_opflags &= ~IOP_XATTR;
+ }
}
reiserfs_write_unlock(dir->i_sb);
if (retval == IO_ERROR) {
diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h
index e5ca9ed..7265801 100644
--- a/fs/reiserfs/reiserfs.h
+++ b/fs/reiserfs/reiserfs.h
@@ -1168,6 +1168,8 @@ static inline int bmap_would_wrap(unsigned bmap_nr)
return bmap_nr > ((1LL << 16) - 1);
}
+extern const struct xattr_handler *reiserfs_xattr_handlers[];
+
/*
* this says about version of key of all items (but stat data) the
* object consists of
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 1fc934d..a350749 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -2052,6 +2052,8 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
if (replay_only(s))
goto error_unlocked;
+ s->s_xattr = reiserfs_xattr_handlers;
+
if (bdev_read_only(s->s_bdev) && !sb_rdonly(s)) {
SWARN(silent, s, "clm-7000",
"Detected readonly device, marking FS readonly");
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 32d8986..2c5c459 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -122,13 +122,13 @@ static struct dentry *open_xa_root(struct super_block *sb, int flags)
struct dentry *xaroot;
if (d_really_is_negative(privroot))
- return ERR_PTR(-ENODATA);
+ return ERR_PTR(-EOPNOTSUPP);
inode_lock_nested(d_inode(privroot), I_MUTEX_XATTR);
xaroot = dget(REISERFS_SB(sb)->xattr_root);
if (!xaroot)
- xaroot = ERR_PTR(-ENODATA);
+ xaroot = ERR_PTR(-EOPNOTSUPP);
else if (d_really_is_negative(xaroot)) {
int err = -ENODATA;
@@ -610,6 +610,10 @@ int reiserfs_xattr_set(struct inode *inode, const char *name,
int error, error2;
size_t jbegin_count = reiserfs_xattr_nblocks(inode, buffer_size);
+ /* Check before we start a transaction and then do nothing. */
+ if (!d_really_is_positive(REISERFS_SB(inode->i_sb)->priv_root))
+ return -EOPNOTSUPP;
+
if (!(flags & XATTR_REPLACE))
jbegin_count += reiserfs_xattr_jcreate_nblocks(inode);
@@ -832,8 +836,7 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size)
if (d_really_is_negative(dentry))
return -EINVAL;
- if (!dentry->d_sb->s_xattr ||
- get_inode_sd_version(d_inode(dentry)) == STAT_DATA_V1)
+ if (get_inode_sd_version(d_inode(dentry)) == STAT_DATA_V1)
return -EOPNOTSUPP;
dir = open_xa_dir(d_inode(dentry), XATTR_REPLACE);
@@ -873,6 +876,7 @@ static int create_privroot(struct dentry *dentry)
}
d_inode(dentry)->i_flags |= S_PRIVATE;
+ d_inode(dentry)->i_opflags &= ~IOP_XATTR;
reiserfs_info(dentry->d_sb, "Created %s - reserved for xattr "
"storage.\n", PRIVROOT_NAME);
@@ -886,7 +890,7 @@ static int create_privroot(struct dentry *dentry) { return 0; }
#endif
/* Actual operations that are exported to VFS-land */
-static const struct xattr_handler *reiserfs_xattr_handlers[] = {
+const struct xattr_handler *reiserfs_xattr_handlers[] = {
#ifdef CONFIG_REISERFS_FS_XATTR
&reiserfs_xattr_user_handler,
&reiserfs_xattr_trusted_handler,
@@ -957,8 +961,10 @@ int reiserfs_lookup_privroot(struct super_block *s)
if (!IS_ERR(dentry)) {
REISERFS_SB(s)->priv_root = dentry;
d_set_d_op(dentry, &xattr_lookup_poison_ops);
- if (d_really_is_positive(dentry))
+ if (d_really_is_positive(dentry)) {
d_inode(dentry)->i_flags |= S_PRIVATE;
+ d_inode(dentry)->i_opflags &= ~IOP_XATTR;
+ }
} else
err = PTR_ERR(dentry);
inode_unlock(d_inode(s->s_root));
@@ -987,7 +993,6 @@ int reiserfs_xattr_init(struct super_block *s, int mount_flags)
}
if (d_really_is_positive(privroot)) {
- s->s_xattr = reiserfs_xattr_handlers;
inode_lock(d_inode(privroot));
if (!REISERFS_SB(s)->xattr_root) {
struct dentry *dentry;
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index aa9380b..05f6667 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -320,10 +320,8 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
* would be useless since permissions are ignored, and a pain because
* it introduces locking cycles
*/
- if (IS_PRIVATE(dir)) {
- inode->i_flags |= S_PRIVATE;
+ if (IS_PRIVATE(inode))
goto apply_umask;
- }
err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
if (err)
diff --git a/fs/splice.c b/fs/splice.c
index 485e409..fd28c7da 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -946,11 +946,17 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
sd->flags &= ~SPLICE_F_NONBLOCK;
more = sd->flags & SPLICE_F_MORE;
+ WARN_ON_ONCE(pipe->nrbufs != 0);
+
while (len) {
+ unsigned int pipe_pages;
size_t read_len;
loff_t pos = sd->pos, prev_pos = pos;
- ret = do_splice_to(in, &pos, pipe, len, flags);
+ /* Don't try to read more the pipe has space for. */
+ pipe_pages = pipe->buffers - pipe->nrbufs;
+ read_len = min(len, (size_t)pipe_pages << PAGE_SHIFT);
+ ret = do_splice_to(in, &pos, pipe, read_len, flags);
if (unlikely(ret <= 0))
goto out_release;
@@ -1170,8 +1176,15 @@ static long do_splice(struct file *in, loff_t __user *off_in,
pipe_lock(opipe);
ret = wait_for_space(opipe, flags);
- if (!ret)
+ if (!ret) {
+ unsigned int pipe_pages;
+
+ /* Don't try to read more the pipe has space for. */
+ pipe_pages = opipe->buffers - opipe->nrbufs;
+ len = min(len, (size_t)pipe_pages << PAGE_SHIFT);
+
ret = do_splice_to(in, &offset, opipe, len, flags);
+ }
pipe_unlock(opipe);
if (ret > 0)
wakeup_pipe_readers(opipe);
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 3dffa59..32b495e 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -63,6 +63,17 @@
/* Default time granularity in nanoseconds */
#define DEFAULT_TIME_GRAN 1000000000
+static int get_default_compressor(struct ubifs_info *c)
+{
+ if (ubifs_compr_present(c, UBIFS_COMPR_LZO))
+ return UBIFS_COMPR_LZO;
+
+ if (ubifs_compr_present(c, UBIFS_COMPR_ZLIB))
+ return UBIFS_COMPR_ZLIB;
+
+ return UBIFS_COMPR_NONE;
+}
+
/**
* create_default_filesystem - format empty UBI volume.
* @c: UBIFS file-system description object
@@ -186,7 +197,7 @@ static int create_default_filesystem(struct ubifs_info *c)
if (c->mount_opts.override_compr)
sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
else
- sup->default_compr = cpu_to_le16(UBIFS_COMPR_LZO);
+ sup->default_compr = cpu_to_le16(get_default_compressor(c));
generate_random_uuid(sup->uuid);
diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c
index dba87d0..95630f9 100644
--- a/fs/ubifs/tnc_commit.c
+++ b/fs/ubifs/tnc_commit.c
@@ -219,7 +219,7 @@ static int is_idx_node_in_use(struct ubifs_info *c, union ubifs_key *key,
/**
* layout_leb_in_gaps - layout index nodes using in-the-gaps method.
* @c: UBIFS file-system description object
- * @p: return LEB number here
+ * @p: return LEB number in @c->gap_lebs[p]
*
* This function lays out new index nodes for dirty znodes using in-the-gaps
* method of TNC commit.
@@ -228,7 +228,7 @@ static int is_idx_node_in_use(struct ubifs_info *c, union ubifs_key *key,
* This function returns the number of index nodes written into the gaps, or a
* negative error code on failure.
*/
-static int layout_leb_in_gaps(struct ubifs_info *c, int *p)
+static int layout_leb_in_gaps(struct ubifs_info *c, int p)
{
struct ubifs_scan_leb *sleb;
struct ubifs_scan_node *snod;
@@ -243,7 +243,7 @@ static int layout_leb_in_gaps(struct ubifs_info *c, int *p)
* filled, however we do not check there at present.
*/
return lnum; /* Error code */
- *p = lnum;
+ c->gap_lebs[p] = lnum;
dbg_gc("LEB %d", lnum);
/*
* Scan the index LEB. We use the generic scan for this even though
@@ -362,7 +362,7 @@ static int get_leb_cnt(struct ubifs_info *c, int cnt)
*/
static int layout_in_gaps(struct ubifs_info *c, int cnt)
{
- int err, leb_needed_cnt, written, *p;
+ int err, leb_needed_cnt, written, p = 0, old_idx_lebs, *gap_lebs;
dbg_gc("%d znodes to write", cnt);
@@ -371,9 +371,9 @@ static int layout_in_gaps(struct ubifs_info *c, int cnt)
if (!c->gap_lebs)
return -ENOMEM;
- p = c->gap_lebs;
+ old_idx_lebs = c->lst.idx_lebs;
do {
- ubifs_assert(c, p < c->gap_lebs + c->lst.idx_lebs);
+ ubifs_assert(c, p < c->lst.idx_lebs);
written = layout_leb_in_gaps(c, p);
if (written < 0) {
err = written;
@@ -399,9 +399,29 @@ static int layout_in_gaps(struct ubifs_info *c, int cnt)
leb_needed_cnt = get_leb_cnt(c, cnt);
dbg_gc("%d znodes remaining, need %d LEBs, have %d", cnt,
leb_needed_cnt, c->ileb_cnt);
+ /*
+ * Dynamically change the size of @c->gap_lebs to prevent
+ * oob, because @c->lst.idx_lebs could be increased by
+ * function @get_idx_gc_leb (called by layout_leb_in_gaps->
+ * ubifs_find_dirty_idx_leb) during loop. Only enlarge
+ * @c->gap_lebs when needed.
+ *
+ */
+ if (leb_needed_cnt > c->ileb_cnt && p >= old_idx_lebs &&
+ old_idx_lebs < c->lst.idx_lebs) {
+ old_idx_lebs = c->lst.idx_lebs;
+ gap_lebs = krealloc(c->gap_lebs, sizeof(int) *
+ (old_idx_lebs + 1), GFP_NOFS);
+ if (!gap_lebs) {
+ kfree(c->gap_lebs);
+ c->gap_lebs = NULL;
+ return -ENOMEM;
+ }
+ c->gap_lebs = gap_lebs;
+ }
} while (leb_needed_cnt > c->ileb_cnt);
- *p = -1;
+ c->gap_lebs[p] = -1;
return 0;
}
diff --git a/fs/udf/super.c b/fs/udf/super.c
index c495db7..7af011d 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -989,12 +989,62 @@ static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index)
return bitmap;
}
+static int check_partition_desc(struct super_block *sb,
+ struct partitionDesc *p,
+ struct udf_part_map *map)
+{
+ bool umap, utable, fmap, ftable;
+ struct partitionHeaderDesc *phd;
+
+ switch (le32_to_cpu(p->accessType)) {
+ case PD_ACCESS_TYPE_READ_ONLY:
+ case PD_ACCESS_TYPE_WRITE_ONCE:
+ case PD_ACCESS_TYPE_REWRITABLE:
+ case PD_ACCESS_TYPE_NONE:
+ goto force_ro;
+ }
+
+ /* No Partition Header Descriptor? */
+ if (strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) &&
+ strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03))
+ goto force_ro;
+
+ phd = (struct partitionHeaderDesc *)p->partitionContentsUse;
+ utable = phd->unallocSpaceTable.extLength;
+ umap = phd->unallocSpaceBitmap.extLength;
+ ftable = phd->freedSpaceTable.extLength;
+ fmap = phd->freedSpaceBitmap.extLength;
+
+ /* No allocation info? */
+ if (!utable && !umap && !ftable && !fmap)
+ goto force_ro;
+
+ /* We don't support blocks that require erasing before overwrite */
+ if (ftable || fmap)
+ goto force_ro;
+ /* UDF 2.60: 2.3.3 - no mixing of tables & bitmaps, no VAT. */
+ if (utable && umap)
+ goto force_ro;
+
+ if (map->s_partition_type == UDF_VIRTUAL_MAP15 ||
+ map->s_partition_type == UDF_VIRTUAL_MAP20)
+ goto force_ro;
+
+ return 0;
+force_ro:
+ if (!sb_rdonly(sb))
+ return -EACCES;
+ UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
+ return 0;
+}
+
static int udf_fill_partdesc_info(struct super_block *sb,
struct partitionDesc *p, int p_index)
{
struct udf_part_map *map;
struct udf_sb_info *sbi = UDF_SB(sb);
struct partitionHeaderDesc *phd;
+ int err;
map = &sbi->s_partmaps[p_index];
@@ -1014,8 +1064,16 @@ static int udf_fill_partdesc_info(struct super_block *sb,
p_index, map->s_partition_type,
map->s_partition_root, map->s_partition_len);
- if (strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) &&
- strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03))
+ err = check_partition_desc(sb, p, map);
+ if (err)
+ return err;
+
+ /*
+ * Skip loading allocation info it we cannot ever write to the fs.
+ * This is a correctness thing as we may have decided to force ro mount
+ * to avoid allocation info we don't support.
+ */
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
return 0;
phd = (struct partitionHeaderDesc *)p->partitionContentsUse;
@@ -1051,9 +1109,6 @@ static int udf_fill_partdesc_info(struct super_block *sb,
p_index, bitmap->s_extPosition);
}
- if (phd->partitionIntegrityTable.extLength)
- udf_debug("partitionIntegrityTable (part %d)\n", p_index);
-
if (phd->freedSpaceTable.extLength) {
struct kernel_lb_addr loc = {
.logicalBlockNum = le32_to_cpu(
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 5a519bc..feb109a 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -1845,13 +1845,12 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,
if (copy_from_user(&uffdio_api, buf, sizeof(uffdio_api)))
goto out;
features = uffdio_api.features;
- if (uffdio_api.api != UFFD_API || (features & ~UFFD_API_FEATURES)) {
- memset(&uffdio_api, 0, sizeof(uffdio_api));
- if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api)))
- goto out;
- ret = -EINVAL;
- goto out;
- }
+ ret = -EINVAL;
+ if (uffdio_api.api != UFFD_API || (features & ~UFFD_API_FEATURES))
+ goto err_out;
+ ret = -EPERM;
+ if ((features & UFFD_FEATURE_EVENT_FORK) && !capable(CAP_SYS_PTRACE))
+ goto err_out;
/* report all available features and ioctls to userland */
uffdio_api.features = UFFD_API_FEATURES;
uffdio_api.ioctls = UFFD_API_IOCTLS;
@@ -1864,6 +1863,11 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,
ret = 0;
out:
return ret;
+err_out:
+ memset(&uffdio_api, 0, sizeof(uffdio_api));
+ if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api)))
+ ret = -EFAULT;
+ goto out;
}
static long userfaultfd_ioctl(struct file *file, unsigned cmd,
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 38dc0b4..0b7145f 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -5239,7 +5239,7 @@ __xfs_bunmapi(
* Make sure we don't touch multiple AGF headers out of order
* in a single transaction, as that could cause AB-BA deadlocks.
*/
- if (!wasdel) {
+ if (!wasdel && !isrt) {
agno = XFS_FSB_TO_AGNO(mp, del.br_startblock);
if (prev_agno != NULLAGNUMBER && prev_agno > agno)
break;
diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c
index 95374ab..77d8010 100644
--- a/fs/xfs/libxfs/xfs_symlink_remote.c
+++ b/fs/xfs/libxfs/xfs_symlink_remote.c
@@ -199,7 +199,10 @@ xfs_symlink_local_to_remote(
ifp->if_bytes - 1);
}
-/* Verify the consistency of an inline symlink. */
+/*
+ * Verify the in-memory consistency of an inline symlink data fork. This
+ * does not do on-disk format checks.
+ */
xfs_failaddr_t
xfs_symlink_shortform_verify(
struct xfs_inode *ip)
@@ -215,9 +218,12 @@ xfs_symlink_shortform_verify(
size = ifp->if_bytes;
endp = sfp + size;
- /* Zero length symlinks can exist while we're deleting a remote one. */
- if (size == 0)
- return NULL;
+ /*
+ * Zero length symlinks should never occur in memory as they are
+ * never alllowed to exist on disk.
+ */
+ if (!size)
+ return __this_address;
/* No negative sizes or overly long symlink targets. */
if (size < 0 || size > XFS_SYMLINK_MAXLEN)
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index 2d4324d..51ea2ab 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -14,8 +14,15 @@
static inline bool
xchk_should_terminate(
struct xfs_scrub *sc,
- int *error)
+ int *error)
{
+ /*
+ * If preemption is disabled, we need to yield to the scheduler every
+ * few seconds so that we don't run afoul of the soft lockup watchdog
+ * or RCU stall detector.
+ */
+ cond_resched();
+
if (fatal_signal_pending(current)) {
if (*error == 0)
*error = -EAGAIN;
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 41ad9ea..e638740 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1244,11 +1244,7 @@ xfs_prepare_shift(
* Writeback and invalidate cache for the remainder of the file as we're
* about to shift down every extent from offset to EOF.
*/
- error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, offset, -1);
- if (error)
- return error;
- error = invalidate_inode_pages2_range(VFS_I(ip)->i_mapping,
- offset >> PAGE_SHIFT, -1);
+ error = xfs_flush_unmap_range(ip, offset, XFS_ISIZE(ip));
if (error)
return error;
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index e839907..c1f7c0d 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -37,6 +37,32 @@ static kmem_zone_t *xfs_buf_zone;
#define xb_to_gfp(flags) \
((((flags) & XBF_READ_AHEAD) ? __GFP_NORETRY : GFP_NOFS) | __GFP_NOWARN)
+/*
+ * Locking orders
+ *
+ * xfs_buf_ioacct_inc:
+ * xfs_buf_ioacct_dec:
+ * b_sema (caller holds)
+ * b_lock
+ *
+ * xfs_buf_stale:
+ * b_sema (caller holds)
+ * b_lock
+ * lru_lock
+ *
+ * xfs_buf_rele:
+ * b_lock
+ * pag_buf_lock
+ * lru_lock
+ *
+ * xfs_buftarg_wait_rele
+ * lru_lock
+ * b_lock (trylock due to inversion)
+ *
+ * xfs_buftarg_isolate
+ * lru_lock
+ * b_lock (trylock due to inversion)
+ */
static inline int
xfs_buf_is_vmapped(
@@ -1006,8 +1032,18 @@ xfs_buf_rele(
ASSERT(atomic_read(&bp->b_hold) > 0);
- release = atomic_dec_and_lock(&bp->b_hold, &pag->pag_buf_lock);
+ /*
+ * We grab the b_lock here first to serialise racing xfs_buf_rele()
+ * calls. The pag_buf_lock being taken on the last reference only
+ * serialises against racing lookups in xfs_buf_find(). IOWs, the second
+ * to last reference we drop here is not serialised against the last
+ * reference until we take bp->b_lock. Hence if we don't grab b_lock
+ * first, the last "release" reference can win the race to the lock and
+ * free the buffer before the second-to-last reference is processed,
+ * leading to a use-after-free scenario.
+ */
spin_lock(&bp->b_lock);
+ release = atomic_dec_and_lock(&bp->b_hold, &pag->pag_buf_lock);
if (!release) {
/*
* Drop the in-flight state if the buffer is already on the LRU
@@ -1470,8 +1506,7 @@ __xfs_buf_submit(
xfs_buf_ioerror(bp, -EIO);
bp->b_flags &= ~XBF_DONE;
xfs_buf_stale(bp);
- if (bp->b_flags & XBF_ASYNC)
- xfs_buf_ioend(bp);
+ xfs_buf_ioend(bp);
return -EIO;
}
@@ -1989,6 +2024,13 @@ xfs_buf_delwri_submit_buffers(
* is only safely useable for callers that can track I/O completion by higher
* level means, e.g. AIL pushing as the @buffer_list is consumed in this
* function.
+ *
+ * Note: this function will skip buffers it would block on, and in doing so
+ * leaves them on @buffer_list so they can be retried on a later pass. As such,
+ * it is up to the caller to ensure that the buffer list is fully submitted or
+ * cancelled appropriately when they are finished with the list. Failure to
+ * cancel or resubmit the list until it is empty will result in leaked buffers
+ * at unmount time.
*/
int
xfs_buf_delwri_submit_nowait(
diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
index fba115f..b044f7d 100644
--- a/fs/xfs/xfs_ioctl32.c
+++ b/fs/xfs/xfs_ioctl32.c
@@ -241,6 +241,32 @@ xfs_compat_ioc_bulkstat(
int done;
int error;
+ /*
+ * Output structure handling functions. Depending on the command,
+ * either the xfs_bstat and xfs_inogrp structures are written out
+ * to userpace memory via bulkreq.ubuffer. Normally the compat
+ * functions and structure size are the correct ones to use ...
+ */
+ inumbers_fmt_pf inumbers_func = xfs_inumbers_fmt_compat;
+ bulkstat_one_pf bs_one_func = xfs_bulkstat_one_compat;
+ size_t bs_one_size = sizeof(struct compat_xfs_bstat);
+
+#ifdef CONFIG_X86_X32
+ if (in_x32_syscall()) {
+ /*
+ * ... but on x32 the input xfs_fsop_bulkreq has pointers
+ * which must be handled in the "compat" (32-bit) way, while
+ * the xfs_bstat and xfs_inogrp structures follow native 64-
+ * bit layout convention. So adjust accordingly, otherwise
+ * the data written out in compat layout will not match what
+ * x32 userspace expects.
+ */
+ inumbers_func = xfs_inumbers_fmt;
+ bs_one_func = xfs_bulkstat_one;
+ bs_one_size = sizeof(struct xfs_bstat);
+ }
+#endif
+
/* done = 1 if there are more stats to get and if bulkstat */
/* should be called again (unused here, but used in dmapi) */
@@ -272,15 +298,15 @@ xfs_compat_ioc_bulkstat(
if (cmd == XFS_IOC_FSINUMBERS_32) {
error = xfs_inumbers(mp, &inlast, &count,
- bulkreq.ubuffer, xfs_inumbers_fmt_compat);
+ bulkreq.ubuffer, inumbers_func);
} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {
int res;
- error = xfs_bulkstat_one_compat(mp, inlast, bulkreq.ubuffer,
- sizeof(compat_xfs_bstat_t), NULL, &res);
+ error = bs_one_func(mp, inlast, bulkreq.ubuffer,
+ bs_one_size, NULL, &res);
} else if (cmd == XFS_IOC_FSBULKSTAT_32) {
error = xfs_bulkstat(mp, &inlast, &count,
- xfs_bulkstat_one_compat, sizeof(compat_xfs_bstat_t),
+ bs_one_func, bs_one_size,
bulkreq.ubuffer, &done);
} else
error = -EINVAL;
@@ -336,6 +362,7 @@ xfs_compat_attrlist_by_handle(
{
int error;
attrlist_cursor_kern_t *cursor;
+ compat_xfs_fsop_attrlist_handlereq_t __user *p = arg;
compat_xfs_fsop_attrlist_handlereq_t al_hreq;
struct dentry *dentry;
char *kbuf;
@@ -370,6 +397,11 @@ xfs_compat_attrlist_by_handle(
if (error)
goto out_kfree;
+ if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
+ error = -EFAULT;
+ goto out_kfree;
+ }
+
if (copy_to_user(compat_ptr(al_hreq.buffer), kbuf, al_hreq.buflen))
error = -EFAULT;
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index c3b610b..7bba551cb 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1578,6 +1578,8 @@ xlog_alloc_log(
if (iclog->ic_bp)
xfs_buf_free(iclog->ic_bp);
kmem_free(iclog);
+ if (prev_iclog == log->l_iclog)
+ break;
}
spinlock_destroy(&log->l_icloglock);
xfs_buf_free(log->l_xbuf);
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 926ed31..484eb0a 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -1198,13 +1198,11 @@ xfs_rtmount_inodes(
xfs_sb_t *sbp;
sbp = &mp->m_sb;
- if (sbp->sb_rbmino == NULLFSINO)
- return 0;
error = xfs_iget(mp, NULL, sbp->sb_rbmino, 0, 0, &mp->m_rbmip);
if (error)
return error;
ASSERT(mp->m_rbmip != NULL);
- ASSERT(sbp->sb_rsumino != NULLFSINO);
+
error = xfs_iget(mp, NULL, sbp->sb_rsumino, 0, 0, &mp->m_rsumip);
if (error) {
xfs_irele(mp->m_rbmip);
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index a3e98c6..b2c1177 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -192,6 +192,7 @@ xfs_symlink(
pathlen = strlen(target_path);
if (pathlen >= XFS_SYMLINK_MAXLEN) /* total string too long */
return -ENAMETOOLONG;
+ ASSERT(pathlen > 0);
udqp = gdqp = NULL;
prid = xfs_get_initial_prid(dp);
@@ -378,6 +379,12 @@ xfs_symlink(
/*
* Free a symlink that has blocks associated with it.
+ *
+ * Note: zero length symlinks are not allowed to exist. When we set the size to
+ * zero, also change it to a regular file so that it does not get written to
+ * disk as a zero length symlink. The inode is on the unlinked list already, so
+ * userspace cannot find this inode anymore, so this change is not user visible
+ * but allows us to catch corrupt zero-length symlinks in the verifiers.
*/
STATIC int
xfs_inactive_symlink_rmt(
@@ -412,13 +419,14 @@ xfs_inactive_symlink_rmt(
xfs_trans_ijoin(tp, ip, 0);
/*
- * Lock the inode, fix the size, and join it to the transaction.
- * Hold it so in the normal path, we still have it locked for
- * the second transaction. In the error paths we need it
+ * Lock the inode, fix the size, turn it into a regular file and join it
+ * to the transaction. Hold it so in the normal path, we still have it
+ * locked for the second transaction. In the error paths we need it
* held so the cancel won't rele it, see below.
*/
size = (int)ip->i_d.di_size;
ip->i_d.di_size = 0;
+ VFS_I(ip)->i_mode = (VFS_I(ip)->i_mode & ~S_IFMT) | S_IFREG;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
/*
* Find the block(s) so we can inval and unmap them.
@@ -494,17 +502,10 @@ xfs_inactive_symlink(
return -EIO;
xfs_ilock(ip, XFS_ILOCK_EXCL);
-
- /*
- * Zero length symlinks _can_ exist.
- */
pathlen = (int)ip->i_d.di_size;
- if (!pathlen) {
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- return 0;
- }
+ ASSERT(pathlen);
- if (pathlen < 0 || pathlen > XFS_SYMLINK_MAXLEN) {
+ if (pathlen <= 0 || pathlen > XFS_SYMLINK_MAXLEN) {
xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)",
__func__, (unsigned long long)ip->i_ino, pathlen);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -512,12 +513,12 @@ xfs_inactive_symlink(
return -EFSCORRUPTED;
}
+ /*
+ * Inline fork state gets removed by xfs_difree() so we have nothing to
+ * do here in that case.
+ */
if (ip->i_df.if_flags & XFS_IFINLINE) {
- if (ip->i_df.if_bytes > 0)
- xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
- XFS_DATA_FORK);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
- ASSERT(ip->i_df.if_bytes == 0);
return 0;
}
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index 55326f9..d3a4e89 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -531,17 +531,33 @@ xfsaild(
set_current_state(TASK_INTERRUPTIBLE);
/*
- * Check kthread_should_stop() after we set the task state
- * to guarantee that we either see the stop bit and exit or
- * the task state is reset to runnable such that it's not
- * scheduled out indefinitely and detects the stop bit at
- * next iteration.
- *
+ * Check kthread_should_stop() after we set the task state to
+ * guarantee that we either see the stop bit and exit or the
+ * task state is reset to runnable such that it's not scheduled
+ * out indefinitely and detects the stop bit at next iteration.
* A memory barrier is included in above task state set to
* serialize again kthread_stop().
*/
if (kthread_should_stop()) {
__set_current_state(TASK_RUNNING);
+
+ /*
+ * The caller forces out the AIL before stopping the
+ * thread in the common case, which means the delwri
+ * queue is drained. In the shutdown case, the queue may
+ * still hold relogged buffers that haven't been
+ * submitted because they were pinned since added to the
+ * queue.
+ *
+ * Log I/O error processing stales the underlying buffer
+ * and clears the delwri state, expecting the buf to be
+ * removed on the next submission attempt. That won't
+ * happen if we're shutting down, so this is the last
+ * opportunity to release such buffers from the queue.
+ */
+ ASSERT(list_empty(&ailp->ail_buf_list) ||
+ XFS_FORCED_SHUTDOWN(ailp->ail_mount));
+ xfs_buf_delwri_cancel(&ailp->ail_buf_list);
break;
}
diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp
new file mode 100644
index 0000000..8685f11
--- /dev/null
+++ b/gen_headers_arm.bp
@@ -0,0 +1,1014 @@
+// ***** DO NOT EDIT *****
+// This file is generated by kernel_headers.py
+
+gen_headers_arm = [
+
+ // Matching generated-y:
+
+ "asm/unistd-common.h",
+ "asm/unistd-oabi.h",
+ "asm/unistd-eabi.h",
+
+ // Matching generic-y:
+
+ "asm/bitsperlong.h",
+ "asm/bpf_perf_event.h",
+ "asm/errno.h",
+ "asm/ioctl.h",
+ "asm/ipcbuf.h",
+ "asm/msgbuf.h",
+ "asm/param.h",
+ "asm/poll.h",
+ "asm/resource.h",
+ "asm/sembuf.h",
+ "asm/shmbuf.h",
+ "asm/siginfo.h",
+ "asm/socket.h",
+ "asm/sockios.h",
+ "asm/termbits.h",
+ "asm/termios.h",
+
+ // From include/uapi/**/*.h
+
+ "asm-generic/auxvec.h",
+ "asm-generic/bitsperlong.h",
+ "asm-generic/bpf_perf_event.h",
+ "asm-generic/errno-base.h",
+ "asm-generic/errno.h",
+ "asm-generic/fcntl.h",
+ "asm-generic/hugetlb_encode.h",
+ "asm-generic/int-l64.h",
+ "asm-generic/int-ll64.h",
+ "asm-generic/ioctl.h",
+ "asm-generic/ioctls.h",
+ "asm-generic/ipcbuf.h",
+ "asm-generic/kvm_para.h",
+ "asm-generic/mman-common.h",
+ "asm-generic/mman.h",
+ "asm-generic/msgbuf.h",
+ "asm-generic/param.h",
+ "asm-generic/poll.h",
+ "asm-generic/posix_types.h",
+ "asm-generic/resource.h",
+ "asm-generic/sembuf.h",
+ "asm-generic/setup.h",
+ "asm-generic/shmbuf.h",
+ "asm-generic/shmparam.h",
+ "asm-generic/siginfo.h",
+ "asm-generic/signal-defs.h",
+ "asm-generic/signal.h",
+ "asm-generic/socket.h",
+ "asm-generic/sockios.h",
+ "asm-generic/stat.h",
+ "asm-generic/statfs.h",
+ "asm-generic/swab.h",
+ "asm-generic/termbits.h",
+ "asm-generic/termios.h",
+ "asm-generic/types.h",
+ "asm-generic/ucontext.h",
+ "asm-generic/unistd.h",
+ "drm/amdgpu_drm.h",
+ "drm/armada_drm.h",
+ "drm/drm.h",
+ "drm/drm_fourcc.h",
+ "drm/drm_mode.h",
+ "drm/drm_sarea.h",
+ "drm/etnaviv_drm.h",
+ "drm/exynos_drm.h",
+ "drm/i810_drm.h",
+ "drm/i915_drm.h",
+ "drm/mga_drm.h",
+ "drm/msm_drm.h",
+ "drm/msm_drm_pp.h",
+ "drm/nouveau_drm.h",
+ "drm/omap_drm.h",
+ "drm/qxl_drm.h",
+ "drm/r128_drm.h",
+ "drm/radeon_drm.h",
+ "drm/savage_drm.h",
+ "drm/sde_drm.h",
+ "drm/sis_drm.h",
+ "drm/tegra_drm.h",
+ "drm/v3d_drm.h",
+ "drm/vc4_drm.h",
+ "drm/vgem_drm.h",
+ "drm/via_drm.h",
+ "drm/virtgpu_drm.h",
+ "drm/vmwgfx_drm.h",
+ "linux/acct.h",
+ "linux/adb.h",
+ "linux/adfs_fs.h",
+ "linux/affs_hardblocks.h",
+ "linux/agpgart.h",
+ "linux/aio_abi.h",
+ "linux/am437x-vpfe.h",
+ "linux/apm_bios.h",
+ "linux/arcfb.h",
+ "linux/arm_sdei.h",
+ "linux/aspeed-lpc-ctrl.h",
+ "linux/atalk.h",
+ "linux/atm.h",
+ "linux/atm_eni.h",
+ "linux/atm_he.h",
+ "linux/atm_idt77105.h",
+ "linux/atm_nicstar.h",
+ "linux/atm_tcp.h",
+ "linux/atm_zatm.h",
+ "linux/atmapi.h",
+ "linux/atmarp.h",
+ "linux/atmbr2684.h",
+ "linux/atmclip.h",
+ "linux/atmdev.h",
+ "linux/atmioc.h",
+ "linux/atmlec.h",
+ "linux/atmmpc.h",
+ "linux/atmppp.h",
+ "linux/atmsap.h",
+ "linux/atmsvc.h",
+ "linux/audit.h",
+ "linux/auto_dev-ioctl.h",
+ "linux/auto_fs.h",
+ "linux/auto_fs4.h",
+ "linux/auxvec.h",
+ "linux/ax25.h",
+ "linux/b1lli.h",
+ "linux/batadv_packet.h",
+ "linux/batman_adv.h",
+ "linux/baycom.h",
+ "linux/bcache.h",
+ "linux/bcm933xx_hcs.h",
+ "linux/bfs_fs.h",
+ "linux/binfmts.h",
+ "linux/blkpg.h",
+ "linux/blktrace_api.h",
+ "linux/blkzoned.h",
+ "linux/bpf.h",
+ "linux/bpf_common.h",
+ "linux/bpf_perf_event.h",
+ "linux/bpfilter.h",
+ "linux/bpqether.h",
+ "linux/bsg.h",
+ "linux/bt-bmc.h",
+ "linux/btf.h",
+ "linux/btrfs.h",
+ "linux/btrfs_tree.h",
+ "linux/can.h",
+ "linux/capability.h",
+ "linux/capi.h",
+ "linux/cciss_defs.h",
+ "linux/cciss_ioctl.h",
+ "linux/cdrom.h",
+ "linux/cec-funcs.h",
+ "linux/cec.h",
+ "linux/cgroupstats.h",
+ "linux/chio.h",
+ "linux/cm4000_cs.h",
+ "linux/cn_proc.h",
+ "linux/coda.h",
+ "linux/coda_psdev.h",
+ "linux/coff.h",
+ "linux/connector.h",
+ "linux/const.h",
+ "linux/coresight-stm.h",
+ "linux/cramfs_fs.h",
+ "linux/cryptouser.h",
+ "linux/cuda.h",
+ "linux/cyclades.h",
+ "linux/cycx_cfm.h",
+ "linux/dcbnl.h",
+ "linux/dccp.h",
+ "linux/devlink.h",
+ "linux/dlm.h",
+ "linux/dlm_device.h",
+ "linux/dlm_netlink.h",
+ "linux/dlm_plock.h",
+ "linux/dlmconstants.h",
+ "linux/dm-ioctl.h",
+ "linux/dm-log-userspace.h",
+ "linux/dma-buf.h",
+ "linux/dn.h",
+ "linux/dqblk_xfs.h",
+ "linux/edd.h",
+ "linux/efs_fs_sb.h",
+ "linux/elf-em.h",
+ "linux/elf-fdpic.h",
+ "linux/elf.h",
+ "linux/elfcore.h",
+ "linux/errno.h",
+ "linux/errqueue.h",
+ "linux/erspan.h",
+ "linux/esoc_ctrl.h",
+ "linux/ethtool.h",
+ "linux/eventpoll.h",
+ "linux/fadvise.h",
+ "linux/falloc.h",
+ "linux/fanotify.h",
+ "linux/fb.h",
+ "linux/fcntl.h",
+ "linux/fd.h",
+ "linux/fdreg.h",
+ "linux/fib_rules.h",
+ "linux/fiemap.h",
+ "linux/filter.h",
+ "linux/fips_status.h",
+ "linux/firewire-cdev.h",
+ "linux/firewire-constants.h",
+ "linux/flat.h",
+ "linux/fou.h",
+ "linux/fpga-dfl.h",
+ "linux/fs.h",
+ "linux/fsi.h",
+ "linux/fsl_hypervisor.h",
+ "linux/fsmap.h",
+ "linux/fuse.h",
+ "linux/futex.h",
+ "linux/gameport.h",
+ "linux/gen_stats.h",
+ "linux/genetlink.h",
+ "linux/gfs2_ondisk.h",
+ "linux/gigaset_dev.h",
+ "linux/gpio.h",
+ "linux/gsmmux.h",
+ "linux/gtp.h",
+ "linux/hash_info.h",
+ "linux/hdlc.h",
+ "linux/hdlcdrv.h",
+ "linux/hdreg.h",
+ "linux/hid.h",
+ "linux/hiddev.h",
+ "linux/hidraw.h",
+ "linux/hpet.h",
+ "linux/hsr_netlink.h",
+ "linux/hw_breakpoint.h",
+ "linux/hyperv.h",
+ "linux/hysdn_if.h",
+ "linux/i2c-dev.h",
+ "linux/i2c.h",
+ "linux/i2o-dev.h",
+ "linux/i8k.h",
+ "linux/icmp.h",
+ "linux/icmpv6.h",
+ "linux/if.h",
+ "linux/if_addr.h",
+ "linux/if_addrlabel.h",
+ "linux/if_alg.h",
+ "linux/if_arcnet.h",
+ "linux/if_arp.h",
+ "linux/if_bonding.h",
+ "linux/if_bridge.h",
+ "linux/if_cablemodem.h",
+ "linux/if_eql.h",
+ "linux/if_ether.h",
+ "linux/if_fc.h",
+ "linux/if_fddi.h",
+ "linux/if_frad.h",
+ "linux/if_hippi.h",
+ "linux/if_infiniband.h",
+ "linux/if_link.h",
+ "linux/if_ltalk.h",
+ "linux/if_macsec.h",
+ "linux/if_packet.h",
+ "linux/if_phonet.h",
+ "linux/if_plip.h",
+ "linux/if_ppp.h",
+ "linux/if_pppol2tp.h",
+ "linux/if_pppox.h",
+ "linux/if_slip.h",
+ "linux/if_team.h",
+ "linux/if_tun.h",
+ "linux/if_tunnel.h",
+ "linux/if_vlan.h",
+ "linux/if_x25.h",
+ "linux/if_xdp.h",
+ "linux/ife.h",
+ "linux/igmp.h",
+ "linux/ila.h",
+ "linux/in.h",
+ "linux/in6.h",
+ "linux/in_route.h",
+ "linux/inet_diag.h",
+ "linux/inotify.h",
+ "linux/input-event-codes.h",
+ "linux/input.h",
+ "linux/ioctl.h",
+ "linux/ion.h",
+ "linux/ip.h",
+ "linux/ip6_tunnel.h",
+ "linux/ip_vs.h",
+ "linux/ipa_qmi_service_v01.h",
+ "linux/ipc.h",
+ "linux/ipmi.h",
+ "linux/ipmi_bmc.h",
+ "linux/ipmi_msgdefs.h",
+ "linux/ipsec.h",
+ "linux/ipv6.h",
+ "linux/ipv6_route.h",
+ "linux/ipx.h",
+ "linux/irqnr.h",
+ "linux/isdn.h",
+ "linux/isdn_divertif.h",
+ "linux/isdn_ppp.h",
+ "linux/isdnif.h",
+ "linux/iso_fs.h",
+ "linux/ivtv.h",
+ "linux/ivtvfb.h",
+ "linux/jffs2.h",
+ "linux/joystick.h",
+ "linux/kcm.h",
+ "linux/kcmp.h",
+ "linux/kcov.h",
+ "linux/kd.h",
+ "linux/kdev_t.h",
+ "linux/kernel-page-flags.h",
+ "linux/kernel.h",
+ "linux/kernelcapi.h",
+ "linux/kexec.h",
+ "linux/keyboard.h",
+ "linux/keyctl.h",
+ "linux/kfd_ioctl.h",
+ "linux/kvm.h",
+ "linux/kvm_para.h",
+ "linux/l2tp.h",
+ "linux/libc-compat.h",
+ "linux/lightnvm.h",
+ "linux/limits.h",
+ "linux/lirc.h",
+ "linux/llc.h",
+ "linux/loop.h",
+ "linux/lp.h",
+ "linux/lwtunnel.h",
+ "linux/magic.h",
+ "linux/major.h",
+ "linux/map_to_7segment.h",
+ "linux/matroxfb.h",
+ "linux/max2175.h",
+ "linux/mdio.h",
+ "linux/mdss_rotator.h",
+ "linux/media-bus-format.h",
+ "linux/media.h",
+ "linux/mei.h",
+ "linux/membarrier.h",
+ "linux/memfd.h",
+ "linux/mempolicy.h",
+ "linux/meye.h",
+ "linux/mic_common.h",
+ "linux/mic_ioctl.h",
+ "linux/mii.h",
+ "linux/minix_fs.h",
+ "linux/mman.h",
+ "linux/mmtimer.h",
+ "linux/module.h",
+ "linux/mpls.h",
+ "linux/mpls_iptunnel.h",
+ "linux/mqueue.h",
+ "linux/mroute.h",
+ "linux/mroute6.h",
+ "linux/msdos_fs.h",
+ "linux/msg.h",
+ "linux/msm_dsps.h",
+ "linux/msm_ion.h",
+ "linux/msm_ipa.h",
+ "linux/msm_kgsl.h",
+ "linux/msm_mdp.h",
+ "linux/msm_mdp_ext.h",
+ "linux/msm_npu.h",
+ "linux/msm_rmnet.h",
+ "linux/msm_rotator.h",
+ "linux/mtio.h",
+ "linux/n_r3964.h",
+ "linux/nbd-netlink.h",
+ "linux/nbd.h",
+ "linux/ncsi.h",
+ "linux/ndctl.h",
+ "linux/neighbour.h",
+ "linux/net.h",
+ "linux/net_dropmon.h",
+ "linux/net_namespace.h",
+ "linux/net_tstamp.h",
+ "linux/netconf.h",
+ "linux/netdevice.h",
+ "linux/netfilter.h",
+ "linux/netfilter_arp.h",
+ "linux/netfilter_bridge.h",
+ "linux/netfilter_decnet.h",
+ "linux/netfilter_ipv4.h",
+ "linux/netfilter_ipv6.h",
+ "linux/netlink.h",
+ "linux/netlink_diag.h",
+ "linux/netrom.h",
+ "linux/nfc.h",
+ "linux/nfs.h",
+ "linux/nfs2.h",
+ "linux/nfs3.h",
+ "linux/nfs4.h",
+ "linux/nfs4_mount.h",
+ "linux/nfs_fs.h",
+ "linux/nfs_idmap.h",
+ "linux/nfs_mount.h",
+ "linux/nfsacl.h",
+ "linux/nilfs2_api.h",
+ "linux/nilfs2_ondisk.h",
+ "linux/nl80211.h",
+ "linux/nsfs.h",
+ "linux/nubus.h",
+ "linux/nvme_ioctl.h",
+ "linux/nvram.h",
+ "linux/okl4-link-shbuf.h",
+ "linux/okl4-vipc.h",
+ "linux/omap3isp.h",
+ "linux/omapfb.h",
+ "linux/oom.h",
+ "linux/openvswitch.h",
+ "linux/packet_diag.h",
+ "linux/param.h",
+ "linux/parport.h",
+ "linux/patchkey.h",
+ "linux/pci.h",
+ "linux/pci_regs.h",
+ "linux/pcitest.h",
+ "linux/perf_event.h",
+ "linux/personality.h",
+ "linux/pfkeyv2.h",
+ "linux/pg.h",
+ "linux/phantom.h",
+ "linux/phonet.h",
+ "linux/pkt_cls.h",
+ "linux/pkt_sched.h",
+ "linux/pktcdvd.h",
+ "linux/pmu.h",
+ "linux/poll.h",
+ "linux/posix_acl.h",
+ "linux/posix_acl_xattr.h",
+ "linux/posix_types.h",
+ "linux/ppdev.h",
+ "linux/ppp-comp.h",
+ "linux/ppp-ioctl.h",
+ "linux/ppp_defs.h",
+ "linux/pps.h",
+ "linux/pr.h",
+ "linux/prctl.h",
+ "linux/psample.h",
+ "linux/psci.h",
+ "linux/psp-sev.h",
+ "linux/ptp_clock.h",
+ "linux/ptrace.h",
+ "linux/qbt_handler.h",
+ "linux/qcedev.h",
+ "linux/qcota.h",
+ "linux/qemu_fw_cfg.h",
+ "linux/qg-profile.h",
+ "linux/qg.h",
+ "linux/qnx4_fs.h",
+ "linux/qnxtypes.h",
+ "linux/qrng.h",
+ "linux/qrtr.h",
+ "linux/qseecom.h",
+ "linux/quota.h",
+ "linux/radeonfb.h",
+ "linux/random.h",
+ "linux/raw.h",
+ "linux/rds.h",
+ "linux/reboot.h",
+ "linux/reiserfs_fs.h",
+ "linux/reiserfs_xattr.h",
+ "linux/resource.h",
+ "linux/rfkill.h",
+ "linux/rio_cm_cdev.h",
+ "linux/rio_mport_cdev.h",
+ "linux/rmnet_data.h",
+ "linux/rmnet_ipa_fd_ioctl.h",
+ "linux/romfs_fs.h",
+ "linux/rose.h",
+ "linux/route.h",
+ "linux/rpmsg.h",
+ "linux/rseq.h",
+ "linux/rtc.h",
+ "linux/rtnetlink.h",
+ "linux/rxrpc.h",
+ "linux/scc.h",
+ "linux/sched.h",
+ "linux/scif_ioctl.h",
+ "linux/screen_info.h",
+ "linux/sctp.h",
+ "linux/sdla.h",
+ "linux/seccomp.h",
+ "linux/securebits.h",
+ "linux/sed-opal.h",
+ "linux/seg6.h",
+ "linux/seg6_genl.h",
+ "linux/seg6_hmac.h",
+ "linux/seg6_iptunnel.h",
+ "linux/seg6_local.h",
+ "linux/selinux_netlink.h",
+ "linux/sem.h",
+ "linux/serial.h",
+ "linux/serial_core.h",
+ "linux/serial_reg.h",
+ "linux/serio.h",
+ "linux/shm.h",
+ "linux/signal.h",
+ "linux/signalfd.h",
+ "linux/smc.h",
+ "linux/smc_diag.h",
+ "linux/smcinvoke.h",
+ "linux/smiapp.h",
+ "linux/snmp.h",
+ "linux/sock_diag.h",
+ "linux/socket.h",
+ "linux/sockev.h",
+ "linux/sockios.h",
+ "linux/sonet.h",
+ "linux/sonypi.h",
+ "linux/sound.h",
+ "linux/soundcard.h",
+ "linux/spcom.h",
+ "linux/spss_utils.h",
+ "linux/stat.h",
+ "linux/stddef.h",
+ "linux/stm.h",
+ "linux/string.h",
+ "linux/suspend_ioctls.h",
+ "linux/swab.h",
+ "linux/switchtec_ioctl.h",
+ "linux/sync_file.h",
+ "linux/synclink.h",
+ "linux/sysctl.h",
+ "linux/sysinfo.h",
+ "linux/sysstats.h",
+ "linux/target_core_user.h",
+ "linux/taskstats.h",
+ "linux/tcp.h",
+ "linux/tcp_metrics.h",
+ "linux/tee.h",
+ "linux/termios.h",
+ "linux/thermal.h",
+ "linux/time.h",
+ "linux/timerfd.h",
+ "linux/times.h",
+ "linux/timex.h",
+ "linux/tiocl.h",
+ "linux/tipc.h",
+ "linux/tipc_config.h",
+ "linux/tipc_netlink.h",
+ "linux/tipc_sockets_diag.h",
+ "linux/tls.h",
+ "linux/toshiba.h",
+ "linux/tty.h",
+ "linux/tty_flags.h",
+ "linux/types.h",
+ "linux/udf_fs_i.h",
+ "linux/udp.h",
+ "linux/uhid.h",
+ "linux/uinput.h",
+ "linux/uio.h",
+ "linux/uleds.h",
+ "linux/ultrasound.h",
+ "linux/un.h",
+ "linux/unistd.h",
+ "linux/unix_diag.h",
+ "linux/usbdevice_fs.h",
+ "linux/usbip.h",
+ "linux/userfaultfd.h",
+ "linux/userio.h",
+ "linux/utime.h",
+ "linux/utsname.h",
+ "linux/uuid.h",
+ "linux/uvcvideo.h",
+ "linux/v4l2-common.h",
+ "linux/v4l2-controls.h",
+ "linux/v4l2-dv-timings.h",
+ "linux/v4l2-mediabus.h",
+ "linux/v4l2-subdev.h",
+ "linux/vbox_err.h",
+ "linux/vbox_vmmdev_types.h",
+ "linux/vboxguest.h",
+ "linux/veth.h",
+ "linux/vfio.h",
+ "linux/vfio_ccw.h",
+ "linux/vhost.h",
+ "linux/videodev2.h",
+ "linux/virtio_9p.h",
+ "linux/virtio_balloon.h",
+ "linux/virtio_blk.h",
+ "linux/virtio_config.h",
+ "linux/virtio_console.h",
+ "linux/virtio_crypto.h",
+ "linux/virtio_gpu.h",
+ "linux/virtio_ids.h",
+ "linux/virtio_input.h",
+ "linux/virtio_mmio.h",
+ "linux/virtio_net.h",
+ "linux/virtio_pci.h",
+ "linux/virtio_ring.h",
+ "linux/virtio_rng.h",
+ "linux/virtio_scsi.h",
+ "linux/virtio_types.h",
+ "linux/virtio_vsock.h",
+ "linux/vm_sockets.h",
+ "linux/vm_sockets_diag.h",
+ "linux/vmcore.h",
+ "linux/vsockmon.h",
+ "linux/vt.h",
+ "linux/vtpm_proxy.h",
+ "linux/wait.h",
+ "linux/wanrouter.h",
+ "linux/watchdog.h",
+ "linux/wil6210_uapi.h",
+ "linux/wimax.h",
+ "linux/wireless.h",
+ "linux/wmi.h",
+ "linux/x25.h",
+ "linux/xattr.h",
+ "linux/xfrm.h",
+ "linux/xilinx-v4l2-controls.h",
+ "linux/zorro.h",
+ "linux/zorro_ids.h",
+ "media/msm_cvp_private.h",
+ "media/msm_cvp_utils.h",
+ "media/msm_media_info.h",
+ "media/msm_sde_rotator.h",
+ "media/msm_vidc_private.h",
+ "media/msm_vidc_utils.h",
+ "media/radio-iris-commands.h",
+ "media/radio-iris.h",
+ "media/synx.h",
+ "misc/cxl.h",
+ "misc/ocxl.h",
+ "misc/wigig_sensing_uapi.h",
+ "mtd/inftl-user.h",
+ "mtd/mtd-abi.h",
+ "mtd/mtd-user.h",
+ "mtd/nftl-user.h",
+ "mtd/ubi-user.h",
+ "rdma/bnxt_re-abi.h",
+ "rdma/cxgb3-abi.h",
+ "rdma/cxgb4-abi.h",
+ "rdma/hns-abi.h",
+ "rdma/i40iw-abi.h",
+ "rdma/ib_user_cm.h",
+ "rdma/ib_user_ioctl_cmds.h",
+ "rdma/ib_user_ioctl_verbs.h",
+ "rdma/ib_user_mad.h",
+ "rdma/ib_user_sa.h",
+ "rdma/ib_user_verbs.h",
+ "rdma/mlx4-abi.h",
+ "rdma/mlx5-abi.h",
+ "rdma/mlx5_user_ioctl_cmds.h",
+ "rdma/mlx5_user_ioctl_verbs.h",
+ "rdma/mthca-abi.h",
+ "rdma/nes-abi.h",
+ "rdma/ocrdma-abi.h",
+ "rdma/qedr-abi.h",
+ "rdma/rdma_netlink.h",
+ "rdma/rdma_user_cm.h",
+ "rdma/rdma_user_ioctl.h",
+ "rdma/rdma_user_ioctl_cmds.h",
+ "rdma/rdma_user_rxe.h",
+ "rdma/vmw_pvrdma-abi.h",
+ "scsi/cxlflash_ioctl.h",
+ "scsi/scsi_bsg_fc.h",
+ "scsi/scsi_ioctl.h",
+ "scsi/scsi_netlink.h",
+ "scsi/scsi_netlink_fc.h",
+ "scsi/sg.h",
+ "sound/asequencer.h",
+ "sound/asoc.h",
+ "sound/asound.h",
+ "sound/asound_fm.h",
+ "sound/compress_offload.h",
+ "sound/compress_params.h",
+ "sound/emu10k1.h",
+ "sound/firewire.h",
+ "sound/hdsp.h",
+ "sound/hdspm.h",
+ "sound/sb16_csp.h",
+ "sound/sfnt_info.h",
+ "sound/skl-tplg-interface.h",
+ "sound/snd_sst_tokens.h",
+ "sound/tlv.h",
+ "sound/usb_stream.h",
+ "video/edid.h",
+ "video/msm_hdmi_hdcp_mgr.h",
+ "video/msm_hdmi_modes.h",
+ "video/sisfb.h",
+ "video/uvesafb.h",
+ "xen/evtchn.h",
+ "xen/gntalloc.h",
+ "xen/gntdev.h",
+ "xen/privcmd.h",
+ "linux/android/binder.h",
+ "linux/byteorder/big_endian.h",
+ "linux/byteorder/little_endian.h",
+ "linux/caif/caif_socket.h",
+ "linux/caif/if_caif.h",
+ "linux/can/bcm.h",
+ "linux/can/error.h",
+ "linux/can/gw.h",
+ "linux/can/netlink.h",
+ "linux/can/raw.h",
+ "linux/can/vxcan.h",
+ "linux/cifs/cifs_mount.h",
+ "linux/dvb/audio.h",
+ "linux/dvb/ca.h",
+ "linux/dvb/dmx.h",
+ "linux/dvb/frontend.h",
+ "linux/dvb/net.h",
+ "linux/dvb/osd.h",
+ "linux/dvb/version.h",
+ "linux/dvb/video.h",
+ "linux/genwqe/genwqe_card.h",
+ "linux/hdlc/ioctl.h",
+ "linux/hsi/cs-protocol.h",
+ "linux/hsi/hsi_char.h",
+ "linux/iio/events.h",
+ "linux/iio/types.h",
+ "linux/isdn/capicmd.h",
+ "linux/mmc/core.h",
+ "linux/mmc/ioctl.h",
+ "linux/mmc/mmc.h",
+ "linux/netfilter/nf_conntrack_common.h",
+ "linux/netfilter/nf_conntrack_ftp.h",
+ "linux/netfilter/nf_conntrack_sctp.h",
+ "linux/netfilter/nf_conntrack_tcp.h",
+ "linux/netfilter/nf_conntrack_tuple_common.h",
+ "linux/netfilter/nf_log.h",
+ "linux/netfilter/nf_nat.h",
+ "linux/netfilter/nf_tables.h",
+ "linux/netfilter/nf_tables_compat.h",
+ "linux/netfilter/nfnetlink.h",
+ "linux/netfilter/nfnetlink_acct.h",
+ "linux/netfilter/nfnetlink_compat.h",
+ "linux/netfilter/nfnetlink_conntrack.h",
+ "linux/netfilter/nfnetlink_cthelper.h",
+ "linux/netfilter/nfnetlink_cttimeout.h",
+ "linux/netfilter/nfnetlink_log.h",
+ "linux/netfilter/nfnetlink_osf.h",
+ "linux/netfilter/nfnetlink_queue.h",
+ "linux/netfilter/x_tables.h",
+ "linux/netfilter/xt_AUDIT.h",
+ "linux/netfilter/xt_CHECKSUM.h",
+ "linux/netfilter/xt_CLASSIFY.h",
+ "linux/netfilter/xt_CONNMARK.h",
+ "linux/netfilter/xt_CONNSECMARK.h",
+ "linux/netfilter/xt_CT.h",
+ "linux/netfilter/xt_DSCP.h",
+ "linux/netfilter/xt_HARDIDLETIMER.h",
+ "linux/netfilter/xt_HMARK.h",
+ "linux/netfilter/xt_IDLETIMER.h",
+ "linux/netfilter/xt_LED.h",
+ "linux/netfilter/xt_LOG.h",
+ "linux/netfilter/xt_MARK.h",
+ "linux/netfilter/xt_NFLOG.h",
+ "linux/netfilter/xt_NFQUEUE.h",
+ "linux/netfilter/xt_RATEEST.h",
+ "linux/netfilter/xt_SECMARK.h",
+ "linux/netfilter/xt_SYNPROXY.h",
+ "linux/netfilter/xt_TCPMSS.h",
+ "linux/netfilter/xt_TCPOPTSTRIP.h",
+ "linux/netfilter/xt_TEE.h",
+ "linux/netfilter/xt_TPROXY.h",
+ "linux/netfilter/xt_addrtype.h",
+ "linux/netfilter/xt_bpf.h",
+ "linux/netfilter/xt_cgroup.h",
+ "linux/netfilter/xt_cluster.h",
+ "linux/netfilter/xt_comment.h",
+ "linux/netfilter/xt_connbytes.h",
+ "linux/netfilter/xt_connlabel.h",
+ "linux/netfilter/xt_connlimit.h",
+ "linux/netfilter/xt_connmark.h",
+ "linux/netfilter/xt_conntrack.h",
+ "linux/netfilter/xt_cpu.h",
+ "linux/netfilter/xt_dccp.h",
+ "linux/netfilter/xt_devgroup.h",
+ "linux/netfilter/xt_dscp.h",
+ "linux/netfilter/xt_ecn.h",
+ "linux/netfilter/xt_esp.h",
+ "linux/netfilter/xt_hashlimit.h",
+ "linux/netfilter/xt_helper.h",
+ "linux/netfilter/xt_ipcomp.h",
+ "linux/netfilter/xt_iprange.h",
+ "linux/netfilter/xt_ipvs.h",
+ "linux/netfilter/xt_l2tp.h",
+ "linux/netfilter/xt_length.h",
+ "linux/netfilter/xt_limit.h",
+ "linux/netfilter/xt_mac.h",
+ "linux/netfilter/xt_mark.h",
+ "linux/netfilter/xt_multiport.h",
+ "linux/netfilter/xt_nfacct.h",
+ "linux/netfilter/xt_osf.h",
+ "linux/netfilter/xt_owner.h",
+ "linux/netfilter/xt_physdev.h",
+ "linux/netfilter/xt_pkttype.h",
+ "linux/netfilter/xt_policy.h",
+ "linux/netfilter/xt_quota.h",
+ "linux/netfilter/xt_rateest.h",
+ "linux/netfilter/xt_realm.h",
+ "linux/netfilter/xt_recent.h",
+ "linux/netfilter/xt_rpfilter.h",
+ "linux/netfilter/xt_sctp.h",
+ "linux/netfilter/xt_set.h",
+ "linux/netfilter/xt_socket.h",
+ "linux/netfilter/xt_state.h",
+ "linux/netfilter/xt_statistic.h",
+ "linux/netfilter/xt_string.h",
+ "linux/netfilter/xt_tcpmss.h",
+ "linux/netfilter/xt_tcpudp.h",
+ "linux/netfilter/xt_time.h",
+ "linux/netfilter/xt_u32.h",
+ "linux/netfilter_arp/arp_tables.h",
+ "linux/netfilter_arp/arpt_mangle.h",
+ "linux/netfilter_bridge/ebt_802_3.h",
+ "linux/netfilter_bridge/ebt_among.h",
+ "linux/netfilter_bridge/ebt_arp.h",
+ "linux/netfilter_bridge/ebt_arpreply.h",
+ "linux/netfilter_bridge/ebt_ip.h",
+ "linux/netfilter_bridge/ebt_ip6.h",
+ "linux/netfilter_bridge/ebt_limit.h",
+ "linux/netfilter_bridge/ebt_log.h",
+ "linux/netfilter_bridge/ebt_mark_m.h",
+ "linux/netfilter_bridge/ebt_mark_t.h",
+ "linux/netfilter_bridge/ebt_nat.h",
+ "linux/netfilter_bridge/ebt_nflog.h",
+ "linux/netfilter_bridge/ebt_pkttype.h",
+ "linux/netfilter_bridge/ebt_redirect.h",
+ "linux/netfilter_bridge/ebt_stp.h",
+ "linux/netfilter_bridge/ebt_vlan.h",
+ "linux/netfilter_bridge/ebtables.h",
+ "linux/netfilter_ipv4/ip_tables.h",
+ "linux/netfilter_ipv4/ipt_CLUSTERIP.h",
+ "linux/netfilter_ipv4/ipt_ECN.h",
+ "linux/netfilter_ipv4/ipt_LOG.h",
+ "linux/netfilter_ipv4/ipt_REJECT.h",
+ "linux/netfilter_ipv4/ipt_TTL.h",
+ "linux/netfilter_ipv4/ipt_ah.h",
+ "linux/netfilter_ipv4/ipt_ecn.h",
+ "linux/netfilter_ipv4/ipt_ttl.h",
+ "linux/netfilter_ipv6/ip6_tables.h",
+ "linux/netfilter_ipv6/ip6t_HL.h",
+ "linux/netfilter_ipv6/ip6t_LOG.h",
+ "linux/netfilter_ipv6/ip6t_NPT.h",
+ "linux/netfilter_ipv6/ip6t_REJECT.h",
+ "linux/netfilter_ipv6/ip6t_ah.h",
+ "linux/netfilter_ipv6/ip6t_frag.h",
+ "linux/netfilter_ipv6/ip6t_hl.h",
+ "linux/netfilter_ipv6/ip6t_ipv6header.h",
+ "linux/netfilter_ipv6/ip6t_mh.h",
+ "linux/netfilter_ipv6/ip6t_opts.h",
+ "linux/netfilter_ipv6/ip6t_rt.h",
+ "linux/netfilter_ipv6/ip6t_srh.h",
+ "linux/nfc/nfcinfo.h",
+ "linux/nfsd/cld.h",
+ "linux/nfsd/debug.h",
+ "linux/nfsd/export.h",
+ "linux/nfsd/nfsfh.h",
+ "linux/nfsd/stats.h",
+ "linux/raid/md_p.h",
+ "linux/raid/md_u.h",
+ "linux/sched/types.h",
+ "linux/spi/spidev.h",
+ "linux/sunrpc/debug.h",
+ "linux/tc_act/tc_bpf.h",
+ "linux/tc_act/tc_connmark.h",
+ "linux/tc_act/tc_csum.h",
+ "linux/tc_act/tc_defact.h",
+ "linux/tc_act/tc_gact.h",
+ "linux/tc_act/tc_ife.h",
+ "linux/tc_act/tc_ipt.h",
+ "linux/tc_act/tc_mirred.h",
+ "linux/tc_act/tc_nat.h",
+ "linux/tc_act/tc_pedit.h",
+ "linux/tc_act/tc_sample.h",
+ "linux/tc_act/tc_skbedit.h",
+ "linux/tc_act/tc_skbmod.h",
+ "linux/tc_act/tc_tunnel_key.h",
+ "linux/tc_act/tc_vlan.h",
+ "linux/tc_ematch/tc_em_cmp.h",
+ "linux/tc_ematch/tc_em_ipt.h",
+ "linux/tc_ematch/tc_em_meta.h",
+ "linux/tc_ematch/tc_em_nbyte.h",
+ "linux/tc_ematch/tc_em_text.h",
+ "linux/usb/audio.h",
+ "linux/usb/cdc-wdm.h",
+ "linux/usb/cdc.h",
+ "linux/usb/ch11.h",
+ "linux/usb/ch9.h",
+ "linux/usb/charger.h",
+ "linux/usb/f_accessory.h",
+ "linux/usb/f_mtp.h",
+ "linux/usb/functionfs.h",
+ "linux/usb/g_printer.h",
+ "linux/usb/g_uvc.h",
+ "linux/usb/gadgetfs.h",
+ "linux/usb/midi.h",
+ "linux/usb/tmc.h",
+ "linux/usb/usb_ctrl_qti.h",
+ "linux/usb/video.h",
+ "linux/wimax/i2400m.h",
+ "rdma/hfi/hfi1_ioctl.h",
+ "rdma/hfi/hfi1_user.h",
+ "scsi/fc/fc_els.h",
+ "scsi/fc/fc_fs.h",
+ "scsi/fc/fc_gs.h",
+ "scsi/fc/fc_ns.h",
+ "scsi/ufs/ioctl.h",
+ "scsi/ufs/ufs.h",
+ "linux/netfilter/ipset/ip_set.h",
+ "linux/netfilter/ipset/ip_set_bitmap.h",
+ "linux/netfilter/ipset/ip_set_hash.h",
+ "linux/netfilter/ipset/ip_set_list.h",
+
+ // From arch/arm/include/uapi/**/*.h
+
+ "asm/auxvec.h",
+ "asm/byteorder.h",
+ "asm/fcntl.h",
+ "asm/hwcap.h",
+ "asm/ioctls.h",
+ "asm/kvm.h",
+ "asm/kvm_para.h",
+ "asm/mman.h",
+ "asm/perf_regs.h",
+ "asm/posix_types.h",
+ "asm/ptrace.h",
+ "asm/setup.h",
+ "asm/sigcontext.h",
+ "asm/signal.h",
+ "asm/stat.h",
+ "asm/statfs.h",
+ "asm/swab.h",
+ "asm/types.h",
+ "asm/unistd.h",
+
+ // From techpack/*
+
+ "media/cam_cpas.h",
+ "media/cam_custom.h",
+ "media/cam_defs.h",
+ "media/cam_fd.h",
+ "media/cam_icp.h",
+ "media/cam_isp.h",
+ "media/cam_isp_ife.h",
+ "media/cam_isp_vfe.h",
+ "media/cam_jpeg.h",
+ "media/cam_lrme.h",
+ "media/cam_req_mgr.h",
+ "media/cam_sensor.h",
+ "media/cam_sync.h",
+]
+
+genrule {
+ // This module generates the gen_headers_<arch>.bp file
+ // (i.e., a new version of this file) so that it can be
+ // checked later to ensure that it matches the checked-
+ // in version (this file).
+ name: "qti_generate_gen_headers_arm",
+ srcs: ["arch/arm/include/uapi/asm/Kbuild", "include/uapi/asm-generic/Kbuild.asm", "arch/arm/include/uapi/**/*.h"],
+ tool_files: ["kernel_headers.py"],
+ cmd: "python3 $(location kernel_headers.py) " +
+ kernel_headers_verbose +
+ "--header_arch arm " +
+ "--gen_dir $(genDir) " +
+ "--arch_asm_kbuild $(location arch/arm/include/uapi/asm/Kbuild) " +
+ "--arch_include_uapi $(locations arch/arm/include/uapi/**/*.h) " +
+ "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild.asm) " +
+ "blueprints",
+ out: ["gen_headers_arm.bp"],
+}
+
+genrule {
+ name: "qti_generate_kernel_headers_arm",
+ tools: ["headers_install.sh"],
+ tool_files: [
+ "kernel_headers.py",
+ "arch/arm/tools/syscallhdr.sh",
+ ],
+ srcs: [
+ "arch/arm/include/uapi/asm/Kbuild",
+ "include/uapi/asm-generic/Kbuild.asm",
+ "gen_headers_arm.bp",
+ ":qti_generate_gen_headers_arm",
+ "Makefile",
+ "arch/arm/tools/syscall.tbl",
+ "include/uapi/**/*.h",
+ "arch/arm/include/uapi/**/*.h",
+ ],
+ exclude_srcs: [
+ "include/uapi/linux/a.out.h",
+ ],
+ cmd: "python3 $(location kernel_headers.py) " +
+ kernel_headers_verbose +
+ "--header_arch arm " +
+ "--gen_dir $(genDir) " +
+ "--arch_asm_kbuild $(location arch/arm/include/uapi/asm/Kbuild) " +
+ "--arch_include_uapi $(locations arch/arm/include/uapi/**/*.h) " +
+ "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild.asm) " +
+ "headers " +
+ "--old_gen_headers_bp $(location gen_headers_arm.bp) " +
+ "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm) " +
+ "--version_makefile $(location Makefile) " +
+ "--arch_syscall_tool $(location arch/arm/tools/syscallhdr.sh) " +
+ "--arch_syscall_tbl $(location arch/arm/tools/syscall.tbl) " +
+ "--headers_install $(location headers_install.sh) " +
+ "--include_uapi $(locations include/uapi/**/*.h)",
+ out: ["linux/version.h"] + gen_headers_arm,
+}
diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp
new file mode 100644
index 0000000..2ba2ac6
--- /dev/null
+++ b/gen_headers_arm64.bp
@@ -0,0 +1,1005 @@
+// ***** DO NOT EDIT *****
+// This file is generated by kernel_headers.py
+
+gen_headers_arm64 = [
+
+ // Matching generic-y:
+
+ "asm/errno.h",
+ "asm/ioctl.h",
+ "asm/ioctls.h",
+ "asm/ipcbuf.h",
+ "asm/kvm_para.h",
+ "asm/mman.h",
+ "asm/msgbuf.h",
+ "asm/poll.h",
+ "asm/resource.h",
+ "asm/sembuf.h",
+ "asm/shmbuf.h",
+ "asm/socket.h",
+ "asm/sockios.h",
+ "asm/swab.h",
+ "asm/termbits.h",
+ "asm/termios.h",
+ "asm/types.h",
+
+ // From include/uapi/**/*.h
+
+ "asm-generic/auxvec.h",
+ "asm-generic/bitsperlong.h",
+ "asm-generic/bpf_perf_event.h",
+ "asm-generic/errno-base.h",
+ "asm-generic/errno.h",
+ "asm-generic/fcntl.h",
+ "asm-generic/hugetlb_encode.h",
+ "asm-generic/int-l64.h",
+ "asm-generic/int-ll64.h",
+ "asm-generic/ioctl.h",
+ "asm-generic/ioctls.h",
+ "asm-generic/ipcbuf.h",
+ "asm-generic/kvm_para.h",
+ "asm-generic/mman-common.h",
+ "asm-generic/mman.h",
+ "asm-generic/msgbuf.h",
+ "asm-generic/param.h",
+ "asm-generic/poll.h",
+ "asm-generic/posix_types.h",
+ "asm-generic/resource.h",
+ "asm-generic/sembuf.h",
+ "asm-generic/setup.h",
+ "asm-generic/shmbuf.h",
+ "asm-generic/shmparam.h",
+ "asm-generic/siginfo.h",
+ "asm-generic/signal-defs.h",
+ "asm-generic/signal.h",
+ "asm-generic/socket.h",
+ "asm-generic/sockios.h",
+ "asm-generic/stat.h",
+ "asm-generic/statfs.h",
+ "asm-generic/swab.h",
+ "asm-generic/termbits.h",
+ "asm-generic/termios.h",
+ "asm-generic/types.h",
+ "asm-generic/ucontext.h",
+ "asm-generic/unistd.h",
+ "drm/amdgpu_drm.h",
+ "drm/armada_drm.h",
+ "drm/drm.h",
+ "drm/drm_fourcc.h",
+ "drm/drm_mode.h",
+ "drm/drm_sarea.h",
+ "drm/etnaviv_drm.h",
+ "drm/exynos_drm.h",
+ "drm/i810_drm.h",
+ "drm/i915_drm.h",
+ "drm/mga_drm.h",
+ "drm/msm_drm.h",
+ "drm/msm_drm_pp.h",
+ "drm/nouveau_drm.h",
+ "drm/omap_drm.h",
+ "drm/qxl_drm.h",
+ "drm/r128_drm.h",
+ "drm/radeon_drm.h",
+ "drm/savage_drm.h",
+ "drm/sde_drm.h",
+ "drm/sis_drm.h",
+ "drm/tegra_drm.h",
+ "drm/v3d_drm.h",
+ "drm/vc4_drm.h",
+ "drm/vgem_drm.h",
+ "drm/via_drm.h",
+ "drm/virtgpu_drm.h",
+ "drm/vmwgfx_drm.h",
+ "linux/acct.h",
+ "linux/adb.h",
+ "linux/adfs_fs.h",
+ "linux/affs_hardblocks.h",
+ "linux/agpgart.h",
+ "linux/aio_abi.h",
+ "linux/am437x-vpfe.h",
+ "linux/apm_bios.h",
+ "linux/arcfb.h",
+ "linux/arm_sdei.h",
+ "linux/aspeed-lpc-ctrl.h",
+ "linux/atalk.h",
+ "linux/atm.h",
+ "linux/atm_eni.h",
+ "linux/atm_he.h",
+ "linux/atm_idt77105.h",
+ "linux/atm_nicstar.h",
+ "linux/atm_tcp.h",
+ "linux/atm_zatm.h",
+ "linux/atmapi.h",
+ "linux/atmarp.h",
+ "linux/atmbr2684.h",
+ "linux/atmclip.h",
+ "linux/atmdev.h",
+ "linux/atmioc.h",
+ "linux/atmlec.h",
+ "linux/atmmpc.h",
+ "linux/atmppp.h",
+ "linux/atmsap.h",
+ "linux/atmsvc.h",
+ "linux/audit.h",
+ "linux/auto_dev-ioctl.h",
+ "linux/auto_fs.h",
+ "linux/auto_fs4.h",
+ "linux/auxvec.h",
+ "linux/ax25.h",
+ "linux/b1lli.h",
+ "linux/batadv_packet.h",
+ "linux/batman_adv.h",
+ "linux/baycom.h",
+ "linux/bcache.h",
+ "linux/bcm933xx_hcs.h",
+ "linux/bfs_fs.h",
+ "linux/binfmts.h",
+ "linux/blkpg.h",
+ "linux/blktrace_api.h",
+ "linux/blkzoned.h",
+ "linux/bpf.h",
+ "linux/bpf_common.h",
+ "linux/bpf_perf_event.h",
+ "linux/bpfilter.h",
+ "linux/bpqether.h",
+ "linux/bsg.h",
+ "linux/bt-bmc.h",
+ "linux/btf.h",
+ "linux/btrfs.h",
+ "linux/btrfs_tree.h",
+ "linux/can.h",
+ "linux/capability.h",
+ "linux/capi.h",
+ "linux/cciss_defs.h",
+ "linux/cciss_ioctl.h",
+ "linux/cdrom.h",
+ "linux/cec-funcs.h",
+ "linux/cec.h",
+ "linux/cgroupstats.h",
+ "linux/chio.h",
+ "linux/cm4000_cs.h",
+ "linux/cn_proc.h",
+ "linux/coda.h",
+ "linux/coda_psdev.h",
+ "linux/coff.h",
+ "linux/connector.h",
+ "linux/const.h",
+ "linux/coresight-stm.h",
+ "linux/cramfs_fs.h",
+ "linux/cryptouser.h",
+ "linux/cuda.h",
+ "linux/cyclades.h",
+ "linux/cycx_cfm.h",
+ "linux/dcbnl.h",
+ "linux/dccp.h",
+ "linux/devlink.h",
+ "linux/dlm.h",
+ "linux/dlm_device.h",
+ "linux/dlm_netlink.h",
+ "linux/dlm_plock.h",
+ "linux/dlmconstants.h",
+ "linux/dm-ioctl.h",
+ "linux/dm-log-userspace.h",
+ "linux/dma-buf.h",
+ "linux/dn.h",
+ "linux/dqblk_xfs.h",
+ "linux/edd.h",
+ "linux/efs_fs_sb.h",
+ "linux/elf-em.h",
+ "linux/elf-fdpic.h",
+ "linux/elf.h",
+ "linux/elfcore.h",
+ "linux/errno.h",
+ "linux/errqueue.h",
+ "linux/erspan.h",
+ "linux/esoc_ctrl.h",
+ "linux/ethtool.h",
+ "linux/eventpoll.h",
+ "linux/fadvise.h",
+ "linux/falloc.h",
+ "linux/fanotify.h",
+ "linux/fb.h",
+ "linux/fcntl.h",
+ "linux/fd.h",
+ "linux/fdreg.h",
+ "linux/fib_rules.h",
+ "linux/fiemap.h",
+ "linux/filter.h",
+ "linux/fips_status.h",
+ "linux/firewire-cdev.h",
+ "linux/firewire-constants.h",
+ "linux/flat.h",
+ "linux/fou.h",
+ "linux/fpga-dfl.h",
+ "linux/fs.h",
+ "linux/fsi.h",
+ "linux/fsl_hypervisor.h",
+ "linux/fsmap.h",
+ "linux/fuse.h",
+ "linux/futex.h",
+ "linux/gameport.h",
+ "linux/gen_stats.h",
+ "linux/genetlink.h",
+ "linux/gfs2_ondisk.h",
+ "linux/gigaset_dev.h",
+ "linux/gpio.h",
+ "linux/gsmmux.h",
+ "linux/gtp.h",
+ "linux/hash_info.h",
+ "linux/hdlc.h",
+ "linux/hdlcdrv.h",
+ "linux/hdreg.h",
+ "linux/hid.h",
+ "linux/hiddev.h",
+ "linux/hidraw.h",
+ "linux/hpet.h",
+ "linux/hsr_netlink.h",
+ "linux/hw_breakpoint.h",
+ "linux/hyperv.h",
+ "linux/hysdn_if.h",
+ "linux/i2c-dev.h",
+ "linux/i2c.h",
+ "linux/i2o-dev.h",
+ "linux/i8k.h",
+ "linux/icmp.h",
+ "linux/icmpv6.h",
+ "linux/if.h",
+ "linux/if_addr.h",
+ "linux/if_addrlabel.h",
+ "linux/if_alg.h",
+ "linux/if_arcnet.h",
+ "linux/if_arp.h",
+ "linux/if_bonding.h",
+ "linux/if_bridge.h",
+ "linux/if_cablemodem.h",
+ "linux/if_eql.h",
+ "linux/if_ether.h",
+ "linux/if_fc.h",
+ "linux/if_fddi.h",
+ "linux/if_frad.h",
+ "linux/if_hippi.h",
+ "linux/if_infiniband.h",
+ "linux/if_link.h",
+ "linux/if_ltalk.h",
+ "linux/if_macsec.h",
+ "linux/if_packet.h",
+ "linux/if_phonet.h",
+ "linux/if_plip.h",
+ "linux/if_ppp.h",
+ "linux/if_pppol2tp.h",
+ "linux/if_pppox.h",
+ "linux/if_slip.h",
+ "linux/if_team.h",
+ "linux/if_tun.h",
+ "linux/if_tunnel.h",
+ "linux/if_vlan.h",
+ "linux/if_x25.h",
+ "linux/if_xdp.h",
+ "linux/ife.h",
+ "linux/igmp.h",
+ "linux/ila.h",
+ "linux/in.h",
+ "linux/in6.h",
+ "linux/in_route.h",
+ "linux/inet_diag.h",
+ "linux/inotify.h",
+ "linux/input-event-codes.h",
+ "linux/input.h",
+ "linux/ioctl.h",
+ "linux/ion.h",
+ "linux/ip.h",
+ "linux/ip6_tunnel.h",
+ "linux/ip_vs.h",
+ "linux/ipa_qmi_service_v01.h",
+ "linux/ipc.h",
+ "linux/ipmi.h",
+ "linux/ipmi_bmc.h",
+ "linux/ipmi_msgdefs.h",
+ "linux/ipsec.h",
+ "linux/ipv6.h",
+ "linux/ipv6_route.h",
+ "linux/ipx.h",
+ "linux/irqnr.h",
+ "linux/isdn.h",
+ "linux/isdn_divertif.h",
+ "linux/isdn_ppp.h",
+ "linux/isdnif.h",
+ "linux/iso_fs.h",
+ "linux/ivtv.h",
+ "linux/ivtvfb.h",
+ "linux/jffs2.h",
+ "linux/joystick.h",
+ "linux/kcm.h",
+ "linux/kcmp.h",
+ "linux/kcov.h",
+ "linux/kd.h",
+ "linux/kdev_t.h",
+ "linux/kernel-page-flags.h",
+ "linux/kernel.h",
+ "linux/kernelcapi.h",
+ "linux/kexec.h",
+ "linux/keyboard.h",
+ "linux/keyctl.h",
+ "linux/kfd_ioctl.h",
+ "linux/kvm.h",
+ "linux/l2tp.h",
+ "linux/libc-compat.h",
+ "linux/lightnvm.h",
+ "linux/limits.h",
+ "linux/lirc.h",
+ "linux/llc.h",
+ "linux/loop.h",
+ "linux/lp.h",
+ "linux/lwtunnel.h",
+ "linux/magic.h",
+ "linux/major.h",
+ "linux/map_to_7segment.h",
+ "linux/matroxfb.h",
+ "linux/max2175.h",
+ "linux/mdio.h",
+ "linux/mdss_rotator.h",
+ "linux/media-bus-format.h",
+ "linux/media.h",
+ "linux/mei.h",
+ "linux/membarrier.h",
+ "linux/memfd.h",
+ "linux/mempolicy.h",
+ "linux/meye.h",
+ "linux/mic_common.h",
+ "linux/mic_ioctl.h",
+ "linux/mii.h",
+ "linux/minix_fs.h",
+ "linux/mman.h",
+ "linux/mmtimer.h",
+ "linux/module.h",
+ "linux/mpls.h",
+ "linux/mpls_iptunnel.h",
+ "linux/mqueue.h",
+ "linux/mroute.h",
+ "linux/mroute6.h",
+ "linux/msdos_fs.h",
+ "linux/msg.h",
+ "linux/msm_dsps.h",
+ "linux/msm_ion.h",
+ "linux/msm_ipa.h",
+ "linux/msm_kgsl.h",
+ "linux/msm_mdp.h",
+ "linux/msm_mdp_ext.h",
+ "linux/msm_npu.h",
+ "linux/msm_rmnet.h",
+ "linux/msm_rotator.h",
+ "linux/mtio.h",
+ "linux/n_r3964.h",
+ "linux/nbd-netlink.h",
+ "linux/nbd.h",
+ "linux/ncsi.h",
+ "linux/ndctl.h",
+ "linux/neighbour.h",
+ "linux/net.h",
+ "linux/net_dropmon.h",
+ "linux/net_namespace.h",
+ "linux/net_tstamp.h",
+ "linux/netconf.h",
+ "linux/netdevice.h",
+ "linux/netfilter.h",
+ "linux/netfilter_arp.h",
+ "linux/netfilter_bridge.h",
+ "linux/netfilter_decnet.h",
+ "linux/netfilter_ipv4.h",
+ "linux/netfilter_ipv6.h",
+ "linux/netlink.h",
+ "linux/netlink_diag.h",
+ "linux/netrom.h",
+ "linux/nfc.h",
+ "linux/nfs.h",
+ "linux/nfs2.h",
+ "linux/nfs3.h",
+ "linux/nfs4.h",
+ "linux/nfs4_mount.h",
+ "linux/nfs_fs.h",
+ "linux/nfs_idmap.h",
+ "linux/nfs_mount.h",
+ "linux/nfsacl.h",
+ "linux/nilfs2_api.h",
+ "linux/nilfs2_ondisk.h",
+ "linux/nl80211.h",
+ "linux/nsfs.h",
+ "linux/nubus.h",
+ "linux/nvme_ioctl.h",
+ "linux/nvram.h",
+ "linux/okl4-link-shbuf.h",
+ "linux/okl4-vipc.h",
+ "linux/omap3isp.h",
+ "linux/omapfb.h",
+ "linux/oom.h",
+ "linux/openvswitch.h",
+ "linux/packet_diag.h",
+ "linux/param.h",
+ "linux/parport.h",
+ "linux/patchkey.h",
+ "linux/pci.h",
+ "linux/pci_regs.h",
+ "linux/pcitest.h",
+ "linux/perf_event.h",
+ "linux/personality.h",
+ "linux/pfkeyv2.h",
+ "linux/pg.h",
+ "linux/phantom.h",
+ "linux/phonet.h",
+ "linux/pkt_cls.h",
+ "linux/pkt_sched.h",
+ "linux/pktcdvd.h",
+ "linux/pmu.h",
+ "linux/poll.h",
+ "linux/posix_acl.h",
+ "linux/posix_acl_xattr.h",
+ "linux/posix_types.h",
+ "linux/ppdev.h",
+ "linux/ppp-comp.h",
+ "linux/ppp-ioctl.h",
+ "linux/ppp_defs.h",
+ "linux/pps.h",
+ "linux/pr.h",
+ "linux/prctl.h",
+ "linux/psample.h",
+ "linux/psci.h",
+ "linux/psp-sev.h",
+ "linux/ptp_clock.h",
+ "linux/ptrace.h",
+ "linux/qbt_handler.h",
+ "linux/qcedev.h",
+ "linux/qcota.h",
+ "linux/qemu_fw_cfg.h",
+ "linux/qg-profile.h",
+ "linux/qg.h",
+ "linux/qnx4_fs.h",
+ "linux/qnxtypes.h",
+ "linux/qrng.h",
+ "linux/qrtr.h",
+ "linux/qseecom.h",
+ "linux/quota.h",
+ "linux/radeonfb.h",
+ "linux/random.h",
+ "linux/raw.h",
+ "linux/rds.h",
+ "linux/reboot.h",
+ "linux/reiserfs_fs.h",
+ "linux/reiserfs_xattr.h",
+ "linux/resource.h",
+ "linux/rfkill.h",
+ "linux/rio_cm_cdev.h",
+ "linux/rio_mport_cdev.h",
+ "linux/rmnet_data.h",
+ "linux/rmnet_ipa_fd_ioctl.h",
+ "linux/romfs_fs.h",
+ "linux/rose.h",
+ "linux/route.h",
+ "linux/rpmsg.h",
+ "linux/rseq.h",
+ "linux/rtc.h",
+ "linux/rtnetlink.h",
+ "linux/rxrpc.h",
+ "linux/scc.h",
+ "linux/sched.h",
+ "linux/scif_ioctl.h",
+ "linux/screen_info.h",
+ "linux/sctp.h",
+ "linux/sdla.h",
+ "linux/seccomp.h",
+ "linux/securebits.h",
+ "linux/sed-opal.h",
+ "linux/seg6.h",
+ "linux/seg6_genl.h",
+ "linux/seg6_hmac.h",
+ "linux/seg6_iptunnel.h",
+ "linux/seg6_local.h",
+ "linux/selinux_netlink.h",
+ "linux/sem.h",
+ "linux/serial.h",
+ "linux/serial_core.h",
+ "linux/serial_reg.h",
+ "linux/serio.h",
+ "linux/shm.h",
+ "linux/signal.h",
+ "linux/signalfd.h",
+ "linux/smc.h",
+ "linux/smc_diag.h",
+ "linux/smcinvoke.h",
+ "linux/smiapp.h",
+ "linux/snmp.h",
+ "linux/sock_diag.h",
+ "linux/socket.h",
+ "linux/sockev.h",
+ "linux/sockios.h",
+ "linux/sonet.h",
+ "linux/sonypi.h",
+ "linux/sound.h",
+ "linux/soundcard.h",
+ "linux/spcom.h",
+ "linux/spss_utils.h",
+ "linux/stat.h",
+ "linux/stddef.h",
+ "linux/stm.h",
+ "linux/string.h",
+ "linux/suspend_ioctls.h",
+ "linux/swab.h",
+ "linux/switchtec_ioctl.h",
+ "linux/sync_file.h",
+ "linux/synclink.h",
+ "linux/sysctl.h",
+ "linux/sysinfo.h",
+ "linux/sysstats.h",
+ "linux/target_core_user.h",
+ "linux/taskstats.h",
+ "linux/tcp.h",
+ "linux/tcp_metrics.h",
+ "linux/tee.h",
+ "linux/termios.h",
+ "linux/thermal.h",
+ "linux/time.h",
+ "linux/timerfd.h",
+ "linux/times.h",
+ "linux/timex.h",
+ "linux/tiocl.h",
+ "linux/tipc.h",
+ "linux/tipc_config.h",
+ "linux/tipc_netlink.h",
+ "linux/tipc_sockets_diag.h",
+ "linux/tls.h",
+ "linux/toshiba.h",
+ "linux/tty.h",
+ "linux/tty_flags.h",
+ "linux/types.h",
+ "linux/udf_fs_i.h",
+ "linux/udp.h",
+ "linux/uhid.h",
+ "linux/uinput.h",
+ "linux/uio.h",
+ "linux/uleds.h",
+ "linux/ultrasound.h",
+ "linux/un.h",
+ "linux/unistd.h",
+ "linux/unix_diag.h",
+ "linux/usbdevice_fs.h",
+ "linux/usbip.h",
+ "linux/userfaultfd.h",
+ "linux/userio.h",
+ "linux/utime.h",
+ "linux/utsname.h",
+ "linux/uuid.h",
+ "linux/uvcvideo.h",
+ "linux/v4l2-common.h",
+ "linux/v4l2-controls.h",
+ "linux/v4l2-dv-timings.h",
+ "linux/v4l2-mediabus.h",
+ "linux/v4l2-subdev.h",
+ "linux/vbox_err.h",
+ "linux/vbox_vmmdev_types.h",
+ "linux/vboxguest.h",
+ "linux/veth.h",
+ "linux/vfio.h",
+ "linux/vfio_ccw.h",
+ "linux/vhost.h",
+ "linux/videodev2.h",
+ "linux/virtio_9p.h",
+ "linux/virtio_balloon.h",
+ "linux/virtio_blk.h",
+ "linux/virtio_config.h",
+ "linux/virtio_console.h",
+ "linux/virtio_crypto.h",
+ "linux/virtio_gpu.h",
+ "linux/virtio_ids.h",
+ "linux/virtio_input.h",
+ "linux/virtio_mmio.h",
+ "linux/virtio_net.h",
+ "linux/virtio_pci.h",
+ "linux/virtio_ring.h",
+ "linux/virtio_rng.h",
+ "linux/virtio_scsi.h",
+ "linux/virtio_types.h",
+ "linux/virtio_vsock.h",
+ "linux/vm_sockets.h",
+ "linux/vm_sockets_diag.h",
+ "linux/vmcore.h",
+ "linux/vsockmon.h",
+ "linux/vt.h",
+ "linux/vtpm_proxy.h",
+ "linux/wait.h",
+ "linux/wanrouter.h",
+ "linux/watchdog.h",
+ "linux/wil6210_uapi.h",
+ "linux/wimax.h",
+ "linux/wireless.h",
+ "linux/wmi.h",
+ "linux/x25.h",
+ "linux/xattr.h",
+ "linux/xfrm.h",
+ "linux/xilinx-v4l2-controls.h",
+ "linux/zorro.h",
+ "linux/zorro_ids.h",
+ "media/msm_cvp_private.h",
+ "media/msm_cvp_utils.h",
+ "media/msm_media_info.h",
+ "media/msm_sde_rotator.h",
+ "media/msm_vidc_private.h",
+ "media/msm_vidc_utils.h",
+ "media/radio-iris-commands.h",
+ "media/radio-iris.h",
+ "media/synx.h",
+ "misc/cxl.h",
+ "misc/ocxl.h",
+ "misc/wigig_sensing_uapi.h",
+ "mtd/inftl-user.h",
+ "mtd/mtd-abi.h",
+ "mtd/mtd-user.h",
+ "mtd/nftl-user.h",
+ "mtd/ubi-user.h",
+ "rdma/bnxt_re-abi.h",
+ "rdma/cxgb3-abi.h",
+ "rdma/cxgb4-abi.h",
+ "rdma/hns-abi.h",
+ "rdma/i40iw-abi.h",
+ "rdma/ib_user_cm.h",
+ "rdma/ib_user_ioctl_cmds.h",
+ "rdma/ib_user_ioctl_verbs.h",
+ "rdma/ib_user_mad.h",
+ "rdma/ib_user_sa.h",
+ "rdma/ib_user_verbs.h",
+ "rdma/mlx4-abi.h",
+ "rdma/mlx5-abi.h",
+ "rdma/mlx5_user_ioctl_cmds.h",
+ "rdma/mlx5_user_ioctl_verbs.h",
+ "rdma/mthca-abi.h",
+ "rdma/nes-abi.h",
+ "rdma/ocrdma-abi.h",
+ "rdma/qedr-abi.h",
+ "rdma/rdma_netlink.h",
+ "rdma/rdma_user_cm.h",
+ "rdma/rdma_user_ioctl.h",
+ "rdma/rdma_user_ioctl_cmds.h",
+ "rdma/rdma_user_rxe.h",
+ "rdma/vmw_pvrdma-abi.h",
+ "scsi/cxlflash_ioctl.h",
+ "scsi/scsi_bsg_fc.h",
+ "scsi/scsi_ioctl.h",
+ "scsi/scsi_netlink.h",
+ "scsi/scsi_netlink_fc.h",
+ "scsi/sg.h",
+ "sound/asequencer.h",
+ "sound/asoc.h",
+ "sound/asound.h",
+ "sound/asound_fm.h",
+ "sound/compress_offload.h",
+ "sound/compress_params.h",
+ "sound/emu10k1.h",
+ "sound/firewire.h",
+ "sound/hdsp.h",
+ "sound/hdspm.h",
+ "sound/sb16_csp.h",
+ "sound/sfnt_info.h",
+ "sound/skl-tplg-interface.h",
+ "sound/snd_sst_tokens.h",
+ "sound/tlv.h",
+ "sound/usb_stream.h",
+ "video/edid.h",
+ "video/msm_hdmi_hdcp_mgr.h",
+ "video/msm_hdmi_modes.h",
+ "video/sisfb.h",
+ "video/uvesafb.h",
+ "xen/evtchn.h",
+ "xen/gntalloc.h",
+ "xen/gntdev.h",
+ "xen/privcmd.h",
+ "linux/android/binder.h",
+ "linux/byteorder/big_endian.h",
+ "linux/byteorder/little_endian.h",
+ "linux/caif/caif_socket.h",
+ "linux/caif/if_caif.h",
+ "linux/can/bcm.h",
+ "linux/can/error.h",
+ "linux/can/gw.h",
+ "linux/can/netlink.h",
+ "linux/can/raw.h",
+ "linux/can/vxcan.h",
+ "linux/cifs/cifs_mount.h",
+ "linux/dvb/audio.h",
+ "linux/dvb/ca.h",
+ "linux/dvb/dmx.h",
+ "linux/dvb/frontend.h",
+ "linux/dvb/net.h",
+ "linux/dvb/osd.h",
+ "linux/dvb/version.h",
+ "linux/dvb/video.h",
+ "linux/genwqe/genwqe_card.h",
+ "linux/hdlc/ioctl.h",
+ "linux/hsi/cs-protocol.h",
+ "linux/hsi/hsi_char.h",
+ "linux/iio/events.h",
+ "linux/iio/types.h",
+ "linux/isdn/capicmd.h",
+ "linux/mmc/core.h",
+ "linux/mmc/ioctl.h",
+ "linux/mmc/mmc.h",
+ "linux/netfilter/nf_conntrack_common.h",
+ "linux/netfilter/nf_conntrack_ftp.h",
+ "linux/netfilter/nf_conntrack_sctp.h",
+ "linux/netfilter/nf_conntrack_tcp.h",
+ "linux/netfilter/nf_conntrack_tuple_common.h",
+ "linux/netfilter/nf_log.h",
+ "linux/netfilter/nf_nat.h",
+ "linux/netfilter/nf_tables.h",
+ "linux/netfilter/nf_tables_compat.h",
+ "linux/netfilter/nfnetlink.h",
+ "linux/netfilter/nfnetlink_acct.h",
+ "linux/netfilter/nfnetlink_compat.h",
+ "linux/netfilter/nfnetlink_conntrack.h",
+ "linux/netfilter/nfnetlink_cthelper.h",
+ "linux/netfilter/nfnetlink_cttimeout.h",
+ "linux/netfilter/nfnetlink_log.h",
+ "linux/netfilter/nfnetlink_osf.h",
+ "linux/netfilter/nfnetlink_queue.h",
+ "linux/netfilter/x_tables.h",
+ "linux/netfilter/xt_AUDIT.h",
+ "linux/netfilter/xt_CHECKSUM.h",
+ "linux/netfilter/xt_CLASSIFY.h",
+ "linux/netfilter/xt_CONNMARK.h",
+ "linux/netfilter/xt_CONNSECMARK.h",
+ "linux/netfilter/xt_CT.h",
+ "linux/netfilter/xt_DSCP.h",
+ "linux/netfilter/xt_HARDIDLETIMER.h",
+ "linux/netfilter/xt_HMARK.h",
+ "linux/netfilter/xt_IDLETIMER.h",
+ "linux/netfilter/xt_LED.h",
+ "linux/netfilter/xt_LOG.h",
+ "linux/netfilter/xt_MARK.h",
+ "linux/netfilter/xt_NFLOG.h",
+ "linux/netfilter/xt_NFQUEUE.h",
+ "linux/netfilter/xt_RATEEST.h",
+ "linux/netfilter/xt_SECMARK.h",
+ "linux/netfilter/xt_SYNPROXY.h",
+ "linux/netfilter/xt_TCPMSS.h",
+ "linux/netfilter/xt_TCPOPTSTRIP.h",
+ "linux/netfilter/xt_TEE.h",
+ "linux/netfilter/xt_TPROXY.h",
+ "linux/netfilter/xt_addrtype.h",
+ "linux/netfilter/xt_bpf.h",
+ "linux/netfilter/xt_cgroup.h",
+ "linux/netfilter/xt_cluster.h",
+ "linux/netfilter/xt_comment.h",
+ "linux/netfilter/xt_connbytes.h",
+ "linux/netfilter/xt_connlabel.h",
+ "linux/netfilter/xt_connlimit.h",
+ "linux/netfilter/xt_connmark.h",
+ "linux/netfilter/xt_conntrack.h",
+ "linux/netfilter/xt_cpu.h",
+ "linux/netfilter/xt_dccp.h",
+ "linux/netfilter/xt_devgroup.h",
+ "linux/netfilter/xt_dscp.h",
+ "linux/netfilter/xt_ecn.h",
+ "linux/netfilter/xt_esp.h",
+ "linux/netfilter/xt_hashlimit.h",
+ "linux/netfilter/xt_helper.h",
+ "linux/netfilter/xt_ipcomp.h",
+ "linux/netfilter/xt_iprange.h",
+ "linux/netfilter/xt_ipvs.h",
+ "linux/netfilter/xt_l2tp.h",
+ "linux/netfilter/xt_length.h",
+ "linux/netfilter/xt_limit.h",
+ "linux/netfilter/xt_mac.h",
+ "linux/netfilter/xt_mark.h",
+ "linux/netfilter/xt_multiport.h",
+ "linux/netfilter/xt_nfacct.h",
+ "linux/netfilter/xt_osf.h",
+ "linux/netfilter/xt_owner.h",
+ "linux/netfilter/xt_physdev.h",
+ "linux/netfilter/xt_pkttype.h",
+ "linux/netfilter/xt_policy.h",
+ "linux/netfilter/xt_quota.h",
+ "linux/netfilter/xt_rateest.h",
+ "linux/netfilter/xt_realm.h",
+ "linux/netfilter/xt_recent.h",
+ "linux/netfilter/xt_rpfilter.h",
+ "linux/netfilter/xt_sctp.h",
+ "linux/netfilter/xt_set.h",
+ "linux/netfilter/xt_socket.h",
+ "linux/netfilter/xt_state.h",
+ "linux/netfilter/xt_statistic.h",
+ "linux/netfilter/xt_string.h",
+ "linux/netfilter/xt_tcpmss.h",
+ "linux/netfilter/xt_tcpudp.h",
+ "linux/netfilter/xt_time.h",
+ "linux/netfilter/xt_u32.h",
+ "linux/netfilter_arp/arp_tables.h",
+ "linux/netfilter_arp/arpt_mangle.h",
+ "linux/netfilter_bridge/ebt_802_3.h",
+ "linux/netfilter_bridge/ebt_among.h",
+ "linux/netfilter_bridge/ebt_arp.h",
+ "linux/netfilter_bridge/ebt_arpreply.h",
+ "linux/netfilter_bridge/ebt_ip.h",
+ "linux/netfilter_bridge/ebt_ip6.h",
+ "linux/netfilter_bridge/ebt_limit.h",
+ "linux/netfilter_bridge/ebt_log.h",
+ "linux/netfilter_bridge/ebt_mark_m.h",
+ "linux/netfilter_bridge/ebt_mark_t.h",
+ "linux/netfilter_bridge/ebt_nat.h",
+ "linux/netfilter_bridge/ebt_nflog.h",
+ "linux/netfilter_bridge/ebt_pkttype.h",
+ "linux/netfilter_bridge/ebt_redirect.h",
+ "linux/netfilter_bridge/ebt_stp.h",
+ "linux/netfilter_bridge/ebt_vlan.h",
+ "linux/netfilter_bridge/ebtables.h",
+ "linux/netfilter_ipv4/ip_tables.h",
+ "linux/netfilter_ipv4/ipt_CLUSTERIP.h",
+ "linux/netfilter_ipv4/ipt_ECN.h",
+ "linux/netfilter_ipv4/ipt_LOG.h",
+ "linux/netfilter_ipv4/ipt_REJECT.h",
+ "linux/netfilter_ipv4/ipt_TTL.h",
+ "linux/netfilter_ipv4/ipt_ah.h",
+ "linux/netfilter_ipv4/ipt_ecn.h",
+ "linux/netfilter_ipv4/ipt_ttl.h",
+ "linux/netfilter_ipv6/ip6_tables.h",
+ "linux/netfilter_ipv6/ip6t_HL.h",
+ "linux/netfilter_ipv6/ip6t_LOG.h",
+ "linux/netfilter_ipv6/ip6t_NPT.h",
+ "linux/netfilter_ipv6/ip6t_REJECT.h",
+ "linux/netfilter_ipv6/ip6t_ah.h",
+ "linux/netfilter_ipv6/ip6t_frag.h",
+ "linux/netfilter_ipv6/ip6t_hl.h",
+ "linux/netfilter_ipv6/ip6t_ipv6header.h",
+ "linux/netfilter_ipv6/ip6t_mh.h",
+ "linux/netfilter_ipv6/ip6t_opts.h",
+ "linux/netfilter_ipv6/ip6t_rt.h",
+ "linux/netfilter_ipv6/ip6t_srh.h",
+ "linux/nfc/nfcinfo.h",
+ "linux/nfsd/cld.h",
+ "linux/nfsd/debug.h",
+ "linux/nfsd/export.h",
+ "linux/nfsd/nfsfh.h",
+ "linux/nfsd/stats.h",
+ "linux/raid/md_p.h",
+ "linux/raid/md_u.h",
+ "linux/sched/types.h",
+ "linux/spi/spidev.h",
+ "linux/sunrpc/debug.h",
+ "linux/tc_act/tc_bpf.h",
+ "linux/tc_act/tc_connmark.h",
+ "linux/tc_act/tc_csum.h",
+ "linux/tc_act/tc_defact.h",
+ "linux/tc_act/tc_gact.h",
+ "linux/tc_act/tc_ife.h",
+ "linux/tc_act/tc_ipt.h",
+ "linux/tc_act/tc_mirred.h",
+ "linux/tc_act/tc_nat.h",
+ "linux/tc_act/tc_pedit.h",
+ "linux/tc_act/tc_sample.h",
+ "linux/tc_act/tc_skbedit.h",
+ "linux/tc_act/tc_skbmod.h",
+ "linux/tc_act/tc_tunnel_key.h",
+ "linux/tc_act/tc_vlan.h",
+ "linux/tc_ematch/tc_em_cmp.h",
+ "linux/tc_ematch/tc_em_ipt.h",
+ "linux/tc_ematch/tc_em_meta.h",
+ "linux/tc_ematch/tc_em_nbyte.h",
+ "linux/tc_ematch/tc_em_text.h",
+ "linux/usb/audio.h",
+ "linux/usb/cdc-wdm.h",
+ "linux/usb/cdc.h",
+ "linux/usb/ch11.h",
+ "linux/usb/ch9.h",
+ "linux/usb/charger.h",
+ "linux/usb/f_accessory.h",
+ "linux/usb/f_mtp.h",
+ "linux/usb/functionfs.h",
+ "linux/usb/g_printer.h",
+ "linux/usb/g_uvc.h",
+ "linux/usb/gadgetfs.h",
+ "linux/usb/midi.h",
+ "linux/usb/tmc.h",
+ "linux/usb/usb_ctrl_qti.h",
+ "linux/usb/video.h",
+ "linux/wimax/i2400m.h",
+ "rdma/hfi/hfi1_ioctl.h",
+ "rdma/hfi/hfi1_user.h",
+ "scsi/fc/fc_els.h",
+ "scsi/fc/fc_fs.h",
+ "scsi/fc/fc_gs.h",
+ "scsi/fc/fc_ns.h",
+ "scsi/ufs/ioctl.h",
+ "scsi/ufs/ufs.h",
+ "linux/netfilter/ipset/ip_set.h",
+ "linux/netfilter/ipset/ip_set_bitmap.h",
+ "linux/netfilter/ipset/ip_set_hash.h",
+ "linux/netfilter/ipset/ip_set_list.h",
+
+ // From arch/arm64/include/uapi/**/*.h
+
+ "asm/auxvec.h",
+ "asm/bitsperlong.h",
+ "asm/bpf_perf_event.h",
+ "asm/byteorder.h",
+ "asm/fcntl.h",
+ "asm/hwcap.h",
+ "asm/kvm.h",
+ "asm/param.h",
+ "asm/perf_regs.h",
+ "asm/posix_types.h",
+ "asm/ptrace.h",
+ "asm/setup.h",
+ "asm/sigcontext.h",
+ "asm/siginfo.h",
+ "asm/signal.h",
+ "asm/stat.h",
+ "asm/statfs.h",
+ "asm/ucontext.h",
+ "asm/unistd.h",
+
+ // From techpack/*
+
+ "media/cam_cpas.h",
+ "media/cam_custom.h",
+ "media/cam_defs.h",
+ "media/cam_fd.h",
+ "media/cam_icp.h",
+ "media/cam_isp.h",
+ "media/cam_isp_ife.h",
+ "media/cam_isp_vfe.h",
+ "media/cam_jpeg.h",
+ "media/cam_lrme.h",
+ "media/cam_req_mgr.h",
+ "media/cam_sensor.h",
+ "media/cam_sync.h",
+]
+
+genrule {
+ // This module generates the gen_headers_<arch>.bp file
+ // (i.e., a new version of this file) so that it can be
+ // checked later to ensure that it matches the checked-
+ // in version (this file).
+ name: "qti_generate_gen_headers_arm64",
+ srcs: ["arch/arm64/include/uapi/asm/Kbuild", "include/uapi/asm-generic/Kbuild.asm", "arch/arm64/include/uapi/**/*.h"],
+ tool_files: ["kernel_headers.py"],
+ cmd: "python3 $(location kernel_headers.py) " +
+ kernel_headers_verbose +
+ "--header_arch arm64 " +
+ "--gen_dir $(genDir) " +
+ "--arch_asm_kbuild $(location arch/arm64/include/uapi/asm/Kbuild) " +
+ "--arch_include_uapi $(locations arch/arm64/include/uapi/**/*.h) " +
+ "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild.asm) " +
+ "blueprints",
+ out: ["gen_headers_arm64.bp"],
+}
+
+genrule {
+ name: "qti_generate_kernel_headers_arm64",
+ tools: ["headers_install.sh"],
+ tool_files: [
+ "kernel_headers.py",
+ ],
+ srcs: [
+ "arch/arm64/include/uapi/asm/Kbuild",
+ "include/uapi/asm-generic/Kbuild.asm",
+ "gen_headers_arm64.bp",
+ ":qti_generate_gen_headers_arm64",
+ "Makefile",
+ "include/uapi/**/*.h",
+ "arch/arm64/include/uapi/**/*.h",
+ ],
+ exclude_srcs: [
+ "include/uapi/linux/a.out.h",
+ "include/uapi/linux/kvm_para.h",
+ ],
+ cmd: "python3 $(location kernel_headers.py) " +
+ kernel_headers_verbose +
+ "--header_arch arm64 " +
+ "--gen_dir $(genDir) " +
+ "--arch_asm_kbuild $(location arch/arm64/include/uapi/asm/Kbuild) " +
+ "--arch_include_uapi $(locations arch/arm64/include/uapi/**/*.h) " +
+ "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild.asm) " +
+ "headers " +
+ "--old_gen_headers_bp $(location gen_headers_arm64.bp) " +
+ "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm64) " +
+ "--version_makefile $(location Makefile) " +
+ "--headers_install $(location headers_install.sh) " +
+ "--include_uapi $(locations include/uapi/**/*.h)",
+ out: ["linux/version.h"] + gen_headers_arm64,
+}
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index a0fa620..5e7a4f1 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -339,7 +339,7 @@ struct drm_dp_resource_status_notify {
struct drm_dp_query_payload_ack_reply {
u8 port_number;
- u8 allocated_pbn;
+ u16 allocated_pbn;
};
struct drm_dp_sideband_msg_req_body {
diff --git a/include/dt-bindings/clock/qcom,gcc-bengal.h b/include/dt-bindings/clock/qcom,gcc-bengal.h
index 6963e67..f6d0eb4 100644
--- a/include/dt-bindings/clock/qcom,gcc-bengal.h
+++ b/include/dt-bindings/clock/qcom,gcc-bengal.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DT_BINDINGS_CLK_QCOM_GCC_BENGAL_H
@@ -175,7 +175,8 @@
/* GCC resets */
#define GCC_QUSB2PHY_PRIM_BCR 0
-#define GCC_QUSB2PHY_SEC_BCR 2
+#define GCC_QUSB2PHY_SEC_BCR 1
+#define GCC_SDCC1_BCR 2
#define GCC_UFS_PHY_BCR 3
#define GCC_USB30_PRIM_BCR 4
#define GCC_USB_PHY_CFG_AHB2PHY_BCR 5
diff --git a/include/dt-bindings/clock/rk3328-cru.h b/include/dt-bindings/clock/rk3328-cru.h
index a82a010..9d5f799 100644
--- a/include/dt-bindings/clock/rk3328-cru.h
+++ b/include/dt-bindings/clock/rk3328-cru.h
@@ -178,7 +178,7 @@
#define HCLK_TSP 309
#define HCLK_GMAC 310
#define HCLK_I2S0_8CH 311
-#define HCLK_I2S1_8CH 313
+#define HCLK_I2S1_8CH 312
#define HCLK_I2S2_2CH 313
#define HCLK_SPDIF_8CH 314
#define HCLK_VOP 315
diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h b/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
new file mode 100644
index 0000000..4f89bff
--- /dev/null
+++ b/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H
+#define _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H
+
+#ifndef PMK8350_SID
+#define PMK8350_SID 0
+#endif
+
+/* ADC channels for PMK8350_ADC for PMIC7 */
+#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | 0x0)
+#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | 0x01)
+#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | 0x02)
+#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | 0x03)
+
+#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | 0x04)
+#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | 0x05)
+#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | 0x06)
+#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | 0x07)
+#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | 0x08)
+
+/* 30k pull-up1 */
+#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | 0x24)
+#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | 0x25)
+#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | 0x26)
+#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | 0x27)
+#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | 0x28)
+
+/* 100k pull-up2 */
+#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | 0x44)
+#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | 0x45)
+#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | 0x46)
+#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | 0x47)
+#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | 0x48)
+
+/* 400k pull-up3 */
+#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | 0x64)
+#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | 0x65)
+#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | 0x66)
+#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | 0x67)
+#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | 0x68)
+
+#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H */
diff --git a/include/dt-bindings/iio/qcom,spmi-vadc.h b/include/dt-bindings/iio/qcom,spmi-vadc.h
index 38abbee..0bb1f16 100644
--- a/include/dt-bindings/iio/qcom,spmi-vadc.h
+++ b/include/dt-bindings/iio/qcom,spmi-vadc.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2012-2014,2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014,2018-2020 The Linux Foundation. All rights reserved.
*/
@@ -240,4 +240,84 @@
#define ADC_SCALE_HW_CALIB_BATT_THERM_400K 0xE
#define ADC_SCALE_HW_CALIB_PM5_SMB1398_TEMP 0xF
+/* ADC channels for ADC for PMIC7 */
+
+#define ADC7_REF_GND 0x00
+#define ADC7_1P25VREF 0x01
+#define ADC7_VREF_VADC 0x02
+#define ADC7_DIE_TEMP 0x03
+
+#define ADC7_AMUX_THM1 0x04
+#define ADC7_AMUX_THM2 0x05
+#define ADC7_AMUX_THM3 0x06
+#define ADC7_AMUX_THM4 0x07
+#define ADC7_AMUX_THM5 0x08
+#define ADC7_AMUX_THM6 0x09
+#define ADC7_GPIO1 0x0a
+#define ADC7_GPIO2 0x0b
+#define ADC7_GPIO3 0x0c
+#define ADC7_GPIO4 0x0d
+
+#define ADC7_CHG_TEMP 0x10
+#define ADC7_USB_IN_V_16 0x11
+#define ADC7_VDC_16 0x12
+#define ADC7_CC1_ID 0x13
+#define ADC7_VREF_BAT_THERM 0x15
+#define ADC7_IIN_FB 0x17
+
+/* 30k pull-up1 */
+#define ADC7_AMUX_THM1_30K_PU 0x24
+#define ADC7_AMUX_THM2_30K_PU 0x25
+#define ADC7_AMUX_THM3_30K_PU 0x26
+#define ADC7_AMUX_THM4_30K_PU 0x27
+#define ADC7_AMUX_THM5_30K_PU 0x28
+#define ADC7_AMUX_THM6_30K_PU 0x29
+#define ADC7_GPIO1_30K_PU 0x2a
+#define ADC7_GPIO2_30K_PU 0x2b
+#define ADC7_GPIO3_30K_PU 0x2c
+#define ADC7_GPIO4_30K_PU 0x2d
+#define ADC7_CC1_ID_30K_PU 0x33
+
+/* 100k pull-up2 */
+#define ADC7_AMUX_THM1_100K_PU 0x44
+#define ADC7_AMUX_THM2_100K_PU 0x45
+#define ADC7_AMUX_THM3_100K_PU 0x46
+#define ADC7_AMUX_THM4_100K_PU 0x47
+#define ADC7_AMUX_THM5_100K_PU 0x48
+#define ADC7_AMUX_THM6_100K_PU 0x49
+#define ADC7_GPIO1_100K_PU 0x4a
+#define ADC7_GPIO2_100K_PU 0x4b
+#define ADC7_GPIO3_100K_PU 0x4c
+#define ADC7_GPIO4_100K_PU 0x4d
+#define ADC7_CC1_ID_100K_PU 0x53
+
+/* 400k pull-up3 */
+#define ADC7_AMUX_THM1_400K_PU 0x64
+#define ADC7_AMUX_THM2_400K_PU 0x65
+#define ADC7_AMUX_THM3_400K_PU 0x66
+#define ADC7_AMUX_THM4_400K_PU 0x67
+#define ADC7_AMUX_THM5_400K_PU 0x68
+#define ADC7_AMUX_THM6_400K_PU 0x69
+#define ADC7_GPIO1_400K_PU 0x6a
+#define ADC7_GPIO2_400K_PU 0x6b
+#define ADC7_GPIO3_400K_PU 0x6c
+#define ADC7_GPIO4_400K_PU 0x6d
+#define ADC7_CC1_ID_400K_PU 0x73
+
+/* 1/3 Divider */
+#define ADC7_GPIO1_DIV3 0x8a
+#define ADC7_GPIO2_DIV3 0x8b
+#define ADC7_GPIO3_DIV3 0x8c
+#define ADC7_GPIO4_DIV3 0x8d
+
+#define ADC7_VPH_PWR 0x8e
+#define ADC7_VBAT_SNS 0x8f
+
+#define ADC7_SBUx 0x94
+#define ADC7_VBAT_2S_MID 0x96
+
+/* VADC scale function index */
+#define ADC_SCALE_HW_CALIB_THERM_100K_PU_PM7 0x10
+#define ADC_SCALE_HW_CALIB_PMIC_THERM_PM7 0x11
+
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_H */
diff --git a/include/dt-bindings/phy/qcom,lagoon-qmp-usb3.h b/include/dt-bindings/phy/qcom,lagoon-qmp-usb3.h
new file mode 100644
index 0000000..107ee57
--- /dev/null
+++ b/include/dt-bindings/phy/qcom,lagoon-qmp-usb3.h
@@ -0,0 +1,638 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DT_BINDINGS_PHY_QCOM_LAGOON_QMP_USB_H
+#define _DT_BINDINGS_PHY_QCOM_LAGOON_QMP_USB_H
+
+/* USB3-DP Combo PHY register offsets */
+
+#define USB3_DP_COM_PHY_MODE_CTRL 0x0000
+#define USB3_DP_COM_SW_RESET 0x0004
+#define USB3_DP_COM_POWER_DOWN_CTRL 0x0008
+#define USB3_DP_COM_SWI_CTRL 0x000c
+#define USB3_DP_COM_TYPEC_CTRL 0x0010
+#define USB3_DP_COM_TYPEC_PWRDN_CTRL 0x0014
+#define USB3_DP_COM_DP_BIST_CFG_0 0x0018
+#define USB3_DP_COM_RESET_OVRD_CTRL 0x001c
+#define USB3_DP_COM_TYPEC_STATUS 0x0020
+#define USB3_DP_COM_PLACEHOLDER_STATUS 0x0024
+#define USB3_DP_COM_REVISION_ID0 0x0028
+#define USB3_DP_COM_REVISION_ID1 0x002c
+#define USB3_DP_COM_REVISION_ID2 0x0030
+#define USB3_DP_COM_REVISION_ID3 0x0034
+#define USB3_DP_QSERDES_COM_ATB_SEL1 0x1000
+#define USB3_DP_QSERDES_COM_ATB_SEL2 0x1004
+#define USB3_DP_QSERDES_COM_FREQ_UPDATE 0x1008
+#define USB3_DP_QSERDES_COM_BG_TIMER 0x100c
+#define USB3_DP_QSERDES_COM_SSC_EN_CENTER 0x1010
+#define USB3_DP_QSERDES_COM_SSC_ADJ_PER1 0x1014
+#define USB3_DP_QSERDES_COM_SSC_ADJ_PER2 0x1018
+#define USB3_DP_QSERDES_COM_SSC_PER1 0x101c
+#define USB3_DP_QSERDES_COM_SSC_PER2 0x1020
+#define USB3_DP_QSERDES_COM_SSC_STEP_SIZE1 0x1024
+#define USB3_DP_QSERDES_COM_SSC_STEP_SIZE2 0x1028
+#define USB3_DP_QSERDES_COM_POST_DIV 0x102c
+#define USB3_DP_QSERDES_COM_POST_DIV_MUX 0x1030
+#define USB3_DP_QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x1034
+#define USB3_DP_QSERDES_COM_CLK_ENABLE1 0x1038
+#define USB3_DP_QSERDES_COM_SYS_CLK_CTRL 0x103c
+#define USB3_DP_QSERDES_COM_SYSCLK_BUF_ENABLE 0x1040
+#define USB3_DP_QSERDES_COM_PLL_EN 0x1044
+#define USB3_DP_QSERDES_COM_PLL_IVCO 0x1048
+#define USB3_DP_QSERDES_COM_CMN_IETRIM 0x104c
+#define USB3_DP_QSERDES_COM_CMN_IPTRIM 0x1050
+#define USB3_DP_QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x1054
+#define USB3_DP_QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x1058
+#define USB3_DP_QSERDES_COM_CLK_EP_DIV 0x105c
+#define USB3_DP_QSERDES_COM_CP_CTRL_MODE0 0x1060
+#define USB3_DP_QSERDES_COM_CP_CTRL_MODE1 0x1064
+#define USB3_DP_QSERDES_COM_PLL_RCTRL_MODE0 0x1068
+#define USB3_DP_QSERDES_COM_PLL_RCTRL_MODE1 0x106c
+#define USB3_DP_QSERDES_COM_PLL_CCTRL_MODE0 0x1070
+#define USB3_DP_QSERDES_COM_PLL_CCTRL_MODE1 0x1074
+#define USB3_DP_QSERDES_COM_PLL_CNTRL 0x1078
+#define USB3_DP_QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x107c
+#define USB3_DP_QSERDES_COM_SYSCLK_EN_SEL 0x1080
+#define USB3_DP_QSERDES_COM_CML_SYSCLK_SEL 0x1084
+#define USB3_DP_QSERDES_COM_RESETSM_CNTRL 0x1088
+#define USB3_DP_QSERDES_COM_RESETSM_CNTRL2 0x108c
+#define USB3_DP_QSERDES_COM_LOCK_CMP_EN 0x1090
+#define USB3_DP_QSERDES_COM_LOCK_CMP_CFG 0x1094
+#define USB3_DP_QSERDES_COM_LOCK_CMP1_MODE0 0x1098
+#define USB3_DP_QSERDES_COM_LOCK_CMP2_MODE0 0x109c
+#define USB3_DP_QSERDES_COM_LOCK_CMP3_MODE0 0x10a0
+#define USB3_DP_QSERDES_COM_LOCK_CMP1_MODE1 0x10a4
+#define USB3_DP_QSERDES_COM_LOCK_CMP2_MODE1 0x10a8
+#define USB3_DP_QSERDES_COM_LOCK_CMP3_MODE1 0x10ac
+#define USB3_DP_QSERDES_COM_DEC_START_MODE0 0x10b0
+#define USB3_DP_QSERDES_COM_DEC_START_MODE1 0x10b4
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START1_MODE0 0x10b8
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START2_MODE0 0x10bc
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START3_MODE0 0x10c0
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START1_MODE1 0x10c4
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START2_MODE1 0x10c8
+#define USB3_DP_QSERDES_COM_DIV_FRAC_START3_MODE1 0x10cc
+#define USB3_DP_QSERDES_COM_INTEGLOOP_INITVAL 0x10d0
+#define USB3_DP_QSERDES_COM_INTEGLOOP_EN 0x10d4
+#define USB3_DP_QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x10d8
+#define USB3_DP_QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10dc
+#define USB3_DP_QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x10e0
+#define USB3_DP_QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x10e4
+#define USB3_DP_QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x10e8
+#define USB3_DP_QSERDES_COM_VCO_TUNE_CTRL 0x10ec
+#define USB3_DP_QSERDES_COM_VCO_TUNE_MAP 0x10f0
+#define USB3_DP_QSERDES_COM_VCO_TUNE1_MODE0 0x10f4
+#define USB3_DP_QSERDES_COM_VCO_TUNE2_MODE0 0x10f8
+#define USB3_DP_QSERDES_COM_VCO_TUNE1_MODE1 0x10fc
+#define USB3_DP_QSERDES_COM_VCO_TUNE2_MODE1 0x1100
+#define USB3_DP_QSERDES_COM_VCO_TUNE_INITVAL1 0x1104
+#define USB3_DP_QSERDES_COM_VCO_TUNE_INITVAL2 0x1108
+#define USB3_DP_QSERDES_COM_VCO_TUNE_MINVAL1 0x110c
+#define USB3_DP_QSERDES_COM_VCO_TUNE_MINVAL2 0x1110
+#define USB3_DP_QSERDES_COM_VCO_TUNE_MAXVAL1 0x1114
+#define USB3_DP_QSERDES_COM_VCO_TUNE_MAXVAL2 0x1118
+#define USB3_DP_QSERDES_COM_VCO_TUNE_TIMER1 0x111c
+#define USB3_DP_QSERDES_COM_VCO_TUNE_TIMER2 0x1120
+#define USB3_DP_QSERDES_COM_CMN_STATUS 0x1124
+#define USB3_DP_QSERDES_COM_RESET_SM_STATUS 0x1128
+#define USB3_DP_QSERDES_COM_RESTRIM_CODE_STATUS 0x112c
+#define USB3_DP_QSERDES_COM_PLLCAL_CODE1_STATUS 0x1130
+#define USB3_DP_QSERDES_COM_PLLCAL_CODE2_STATUS 0x1134
+#define USB3_DP_QSERDES_COM_CLK_SELECT 0x1138
+#define USB3_DP_QSERDES_COM_HSCLK_SEL 0x113c
+#define USB3_DP_QSERDES_COM_INTEGLOOP_BINCODE_STATUS 0x1140
+#define USB3_DP_QSERDES_COM_PLL_ANALOG 0x1144
+#define USB3_DP_QSERDES_COM_CORECLK_DIV_MODE0 0x1148
+#define USB3_DP_QSERDES_COM_CORECLK_DIV_MODE1 0x114c
+#define USB3_DP_QSERDES_COM_SW_RESET 0x1150
+#define USB3_DP_QSERDES_COM_CORE_CLK_EN 0x1154
+#define USB3_DP_QSERDES_COM_C_READY_STATUS 0x1158
+#define USB3_DP_QSERDES_COM_CMN_CONFIG 0x115c
+#define USB3_DP_QSERDES_COM_CMN_RATE_OVERRIDE 0x1160
+#define USB3_DP_QSERDES_COM_SVS_MODE_CLK_SEL 0x1164
+#define USB3_DP_QSERDES_COM_DEBUG_BUS0 0x1168
+#define USB3_DP_QSERDES_COM_DEBUG_BUS1 0x116c
+#define USB3_DP_QSERDES_COM_DEBUG_BUS2 0x1170
+#define USB3_DP_QSERDES_COM_DEBUG_BUS3 0x1174
+#define USB3_DP_QSERDES_COM_DEBUG_BUS_SEL 0x1178
+#define USB3_DP_QSERDES_COM_CMN_MISC1 0x117c
+#define USB3_DP_QSERDES_COM_CMN_MISC2 0x1180
+#define USB3_DP_QSERDES_COM_CMN_MODE 0x1184
+#define USB3_DP_QSERDES_COM_CMN_VREG_SEL 0x1188
+#define USB3_DP_QSERDES_TXA_BIST_MODE_LANENO 0x1200
+#define USB3_DP_QSERDES_TXA_BIST_INVERT 0x1204
+#define USB3_DP_QSERDES_TXA_CLKBUF_ENABLE 0x1208
+#define USB3_DP_QSERDES_TXA_TX_EMP_POST1_LVL 0x120c
+#define USB3_DP_QSERDES_TXA_TX_POST2_EMPH 0x1210
+#define USB3_DP_QSERDES_TXA_TX_BOOST_LVL_UP_DN 0x1214
+#define USB3_DP_QSERDES_TXA_TX_IDLE_LVL_LARGE_AMP 0x1218
+#define USB3_DP_QSERDES_TXA_TX_DRV_LVL 0x121c
+#define USB3_DP_QSERDES_TXA_TX_DRV_LVL_OFFSET 0x1220
+#define USB3_DP_QSERDES_TXA_RESET_TSYNC_EN 0x1224
+#define USB3_DP_QSERDES_TXA_PRE_STALL_LDO_BOOST_EN 0x1228
+#define USB3_DP_QSERDES_TXA_TX_BAND 0x122c
+#define USB3_DP_QSERDES_TXA_SLEW_CNTL 0x1230
+#define USB3_DP_QSERDES_TXA_INTERFACE_SELECT 0x1234
+#define USB3_DP_QSERDES_TXA_LPB_EN 0x1238
+#define USB3_DP_QSERDES_TXA_RES_CODE_LANE_TX 0x123c
+#define USB3_DP_QSERDES_TXA_RES_CODE_LANE_RX 0x1240
+#define USB3_DP_QSERDES_TXA_RES_CODE_LANE_OFFSET_TX 0x1244
+#define USB3_DP_QSERDES_TXA_RES_CODE_LANE_OFFSET_RX 0x1248
+#define USB3_DP_QSERDES_TXA_PERL_LENGTH1 0x124c
+#define USB3_DP_QSERDES_TXA_PERL_LENGTH2 0x1250
+#define USB3_DP_QSERDES_TXA_SERDES_BYP_EN_OUT 0x1254
+#define USB3_DP_QSERDES_TXA_DEBUG_BUS_SEL 0x1258
+#define USB3_DP_QSERDES_TXA_TRANSCEIVER_BIAS_EN 0x125c
+#define USB3_DP_QSERDES_TXA_HIGHZ_DRVR_EN 0x1260
+#define USB3_DP_QSERDES_TXA_TX_POL_INV 0x1264
+#define USB3_DP_QSERDES_TXA_PARRATE_REC_DETECT_IDLE_EN 0x1268
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN1 0x126c
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN2 0x1270
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN3 0x1274
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN4 0x1278
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN5 0x127c
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN6 0x1280
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN7 0x1284
+#define USB3_DP_QSERDES_TXA_BIST_PATTERN8 0x1288
+#define USB3_DP_QSERDES_TXA_LANE_MODE_1 0x128c
+#define USB3_DP_QSERDES_TXA_LANE_MODE_2 0x1290
+#define USB3_DP_QSERDES_TXA_LANE_MODE_3 0x1294
+#define USB3_DP_QSERDES_TXA_ATB_SEL1 0x1298
+#define USB3_DP_QSERDES_TXA_ATB_SEL2 0x129c
+#define USB3_DP_QSERDES_TXA_RCV_DETECT_LVL 0x12a0
+#define USB3_DP_QSERDES_TXA_RCV_DETECT_LVL_2 0x12a4
+#define USB3_DP_QSERDES_TXA_PRBS_SEED1 0x12a8
+#define USB3_DP_QSERDES_TXA_PRBS_SEED2 0x12ac
+#define USB3_DP_QSERDES_TXA_PRBS_SEED3 0x12b0
+#define USB3_DP_QSERDES_TXA_PRBS_SEED4 0x12b4
+#define USB3_DP_QSERDES_TXA_RESET_GEN 0x12b8
+#define USB3_DP_QSERDES_TXA_RESET_GEN_MUXES 0x12bc
+#define USB3_DP_QSERDES_TXA_TRAN_DRVR_EMP_EN 0x12c0
+#define USB3_DP_QSERDES_TXA_TX_INTERFACE_MODE 0x12c4
+#define USB3_DP_QSERDES_TXA_PWM_CTRL 0x12c8
+#define USB3_DP_QSERDES_TXA_PWM_ENCODED_OR_DATA 0x12cc
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_1_DIVIDER_BAND2 0x12d0
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_2_DIVIDER_BAND2 0x12d4
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_3_DIVIDER_BAND2 0x12d8
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_4_DIVIDER_BAND2 0x12dc
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_1_DIVIDER_BAND0_1 0x12e0
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_2_DIVIDER_BAND0_1 0x12e4
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_3_DIVIDER_BAND0_1 0x12e8
+#define USB3_DP_QSERDES_TXA_PWM_GEAR_4_DIVIDER_BAND0_1 0x12ec
+#define USB3_DP_QSERDES_TXA_VMODE_CTRL1 0x12f0
+#define USB3_DP_QSERDES_TXA_ALOG_OBSV_BUS_CTRL_1 0x12f4
+#define USB3_DP_QSERDES_TXA_BIST_STATUS 0x12f8
+#define USB3_DP_QSERDES_TXA_BIST_ERROR_COUNT1 0x12fc
+#define USB3_DP_QSERDES_TXA_BIST_ERROR_COUNT2 0x1300
+#define USB3_DP_QSERDES_TXA_ALOG_OBSV_BUS_STATUS_1 0x1304
+#define USB3_DP_QSERDES_RXA_UCDR_FO_GAIN_HALF 0x1400
+#define USB3_DP_QSERDES_RXA_UCDR_FO_GAIN_QUARTER 0x1404
+#define USB3_DP_QSERDES_RXA_UCDR_FO_GAIN 0x1408
+#define USB3_DP_QSERDES_RXA_UCDR_SO_GAIN_HALF 0x140c
+#define USB3_DP_QSERDES_RXA_UCDR_SO_GAIN_QUARTER 0x1410
+#define USB3_DP_QSERDES_RXA_UCDR_SO_GAIN 0x1414
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_FO_GAIN_HALF 0x1418
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_FO_GAIN_QUARTER 0x141c
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_FO_GAIN 0x1420
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_SO_GAIN_HALF 0x1424
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_SO_GAIN_QUARTER 0x1428
+#define USB3_DP_QSERDES_RXA_UCDR_SVS_SO_GAIN 0x142c
+#define USB3_DP_QSERDES_RXA_UCDR_FASTLOCK_FO_GAIN 0x1430
+#define USB3_DP_QSERDES_RXA_UCDR_SO_SATURATION_AND_ENABLE 0x1434
+#define USB3_DP_QSERDES_RXA_UCDR_FO_TO_SO_DELAY 0x1438
+#define USB3_DP_QSERDES_RXA_UCDR_FASTLOCK_COUNT_LOW 0x143c
+#define USB3_DP_QSERDES_RXA_UCDR_FASTLOCK_COUNT_HIGH 0x1440
+#define USB3_DP_QSERDES_RXA_UCDR_PI_CONTROLS 0x1444
+#define USB3_DP_QSERDES_RXA_UCDR_SB2_THRESH1 0x1448
+#define USB3_DP_QSERDES_RXA_UCDR_SB2_THRESH2 0x144c
+#define USB3_DP_QSERDES_RXA_UCDR_SB2_GAIN1 0x1450
+#define USB3_DP_QSERDES_RXA_UCDR_SB2_GAIN2 0x1454
+#define USB3_DP_QSERDES_RXA_AUX_CONTROL 0x1458
+#define USB3_DP_QSERDES_RXA_AUX_DATA_TCOARSE_TFINE 0x145c
+#define USB3_DP_QSERDES_RXA_RCLK_AUXDATA_SEL 0x1460
+#define USB3_DP_QSERDES_RXA_AC_JTAG_ENABLE 0x1464
+#define USB3_DP_QSERDES_RXA_AC_JTAG_INITP 0x1468
+#define USB3_DP_QSERDES_RXA_AC_JTAG_INITN 0x146c
+#define USB3_DP_QSERDES_RXA_AC_JTAG_LVL 0x1470
+#define USB3_DP_QSERDES_RXA_AC_JTAG_MODE 0x1474
+#define USB3_DP_QSERDES_RXA_AC_JTAG_RESET 0x1478
+#define USB3_DP_QSERDES_RXA_RX_TERM_BW 0x147c
+#define USB3_DP_QSERDES_RXA_RX_RCVR_IQ_EN 0x1480
+#define USB3_DP_QSERDES_RXA_RX_IDAC_I_DC_OFFSETS 0x1484
+#define USB3_DP_QSERDES_RXA_RX_IDAC_IBAR_DC_OFFSETS 0x1488
+#define USB3_DP_QSERDES_RXA_RX_IDAC_Q_DC_OFFSETS 0x148c
+#define USB3_DP_QSERDES_RXA_RX_IDAC_QBAR_DC_OFFSETS 0x1490
+#define USB3_DP_QSERDES_RXA_RX_IDAC_A_DC_OFFSETS 0x1494
+#define USB3_DP_QSERDES_RXA_RX_IDAC_ABAR_DC_OFFSETS 0x1498
+#define USB3_DP_QSERDES_RXA_RX_IDAC_EN 0x149c
+#define USB3_DP_QSERDES_RXA_RX_IDAC_ENABLES 0x14a0
+#define USB3_DP_QSERDES_RXA_RX_IDAC_SIGN 0x14a4
+#define USB3_DP_QSERDES_RXA_RX_HIGHZ_HIGHRATE 0x14a8
+#define USB3_DP_QSERDES_RXA_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x14ac
+#define USB3_DP_QSERDES_RXA_DFE_1 0x14b0
+#define USB3_DP_QSERDES_RXA_DFE_2 0x14b4
+#define USB3_DP_QSERDES_RXA_DFE_3 0x14b8
+#define USB3_DP_QSERDES_RXA_VGA_CAL_CNTRL1 0x14bc
+#define USB3_DP_QSERDES_RXA_VGA_CAL_CNTRL2 0x14c0
+#define USB3_DP_QSERDES_RXA_GM_CAL 0x14c4
+#define USB3_DP_QSERDES_RXA_RX_EQ_GAIN2_LSB 0x14c8
+#define USB3_DP_QSERDES_RXA_RX_EQ_GAIN2_MSB 0x14cc
+#define USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL1 0x14d0
+#define USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL2 0x14d4
+#define USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL3 0x14d8
+#define USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL4 0x14dc
+#define USB3_DP_QSERDES_RXA_RX_IDAC_TSETTLE_LOW 0x14e0
+#define USB3_DP_QSERDES_RXA_RX_IDAC_TSETTLE_HIGH 0x14e4
+#define USB3_DP_QSERDES_RXA_RX_IDAC_MEASURE_TIME 0x14e8
+#define USB3_DP_QSERDES_RXA_RX_IDAC_ACCUMULATOR 0x14ec
+#define USB3_DP_QSERDES_RXA_RX_EQ_OFFSET_LSB 0x14f0
+#define USB3_DP_QSERDES_RXA_RX_EQ_OFFSET_MSB 0x14f4
+#define USB3_DP_QSERDES_RXA_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x14f8
+#define USB3_DP_QSERDES_RXA_RX_OFFSET_ADAPTOR_CNTRL2 0x14fc
+#define USB3_DP_QSERDES_RXA_SIGDET_ENABLES 0x1500
+#define USB3_DP_QSERDES_RXA_SIGDET_CNTRL 0x1504
+#define USB3_DP_QSERDES_RXA_SIGDET_LVL 0x1508
+#define USB3_DP_QSERDES_RXA_SIGDET_DEGLITCH_CNTRL 0x150c
+#define USB3_DP_QSERDES_RXA_RX_BAND 0x1510
+#define USB3_DP_QSERDES_RXA_CDR_FREEZE_UP_DN 0x1514
+#define USB3_DP_QSERDES_RXA_CDR_RESET_OVERRIDE 0x1518
+#define USB3_DP_QSERDES_RXA_RX_INTERFACE_MODE 0x151c
+#define USB3_DP_QSERDES_RXA_JITTER_GEN_MODE 0x1520
+#define USB3_DP_QSERDES_RXA_BUJ_AMP 0x1524
+#define USB3_DP_QSERDES_RXA_SJ_AMP1 0x1528
+#define USB3_DP_QSERDES_RXA_SJ_AMP2 0x152c
+#define USB3_DP_QSERDES_RXA_SJ_PER1 0x1530
+#define USB3_DP_QSERDES_RXA_SJ_PER2 0x1534
+#define USB3_DP_QSERDES_RXA_BUJ_STEP_FREQ1 0x1538
+#define USB3_DP_QSERDES_RXA_BUJ_STEP_FREQ2 0x153c
+#define USB3_DP_QSERDES_RXA_PPM_OFFSET1 0x1540
+#define USB3_DP_QSERDES_RXA_PPM_OFFSET2 0x1544
+#define USB3_DP_QSERDES_RXA_SIGN_PPM_PERIOD1 0x1548
+#define USB3_DP_QSERDES_RXA_SIGN_PPM_PERIOD2 0x154c
+#define USB3_DP_QSERDES_RXA_RX_PWM_ENABLE_AND_DATA 0x1550
+#define USB3_DP_QSERDES_RXA_RX_PWM_GEAR1_TIMEOUT_COUNT 0x1554
+#define USB3_DP_QSERDES_RXA_RX_PWM_GEAR2_TIMEOUT_COUNT 0x1558
+#define USB3_DP_QSERDES_RXA_RX_PWM_GEAR3_TIMEOUT_COUNT 0x155c
+#define USB3_DP_QSERDES_RXA_RX_PWM_GEAR4_TIMEOUT_COUNT 0x1560
+#define USB3_DP_QSERDES_RXA_RX_MODE_00 0x1564
+#define USB3_DP_QSERDES_RXA_RX_MODE_01 0x1568
+#define USB3_DP_QSERDES_RXA_RX_MODE_10 0x156c
+#define USB3_DP_QSERDES_RXA_ALOG_OBSV_BUS_CTRL_1 0x1570
+#define USB3_DP_QSERDES_RXA_PI_CTRL1 0x1574
+#define USB3_DP_QSERDES_RXA_PI_CTRL2 0x1578
+#define USB3_DP_QSERDES_RXA_PI_QUAD 0x157c
+#define USB3_DP_QSERDES_RXA_IDATA1 0x1580
+#define USB3_DP_QSERDES_RXA_IDATA2 0x1584
+#define USB3_DP_QSERDES_RXA_AUX_DATA1 0x1588
+#define USB3_DP_QSERDES_RXA_AUX_DATA2 0x158c
+#define USB3_DP_QSERDES_RXA_AC_JTAG_OUTP 0x1590
+#define USB3_DP_QSERDES_RXA_AC_JTAG_OUTN 0x1594
+#define USB3_DP_QSERDES_RXA_RX_SIGDET 0x1598
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_I 0x159c
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_IBAR 0x15a0
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_Q 0x15a4
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_QBAR 0x15a8
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_A 0x15ac
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_ABAR 0x15b0
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_SM_ON 0x15b4
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_CAL_DONE 0x15b8
+#define USB3_DP_QSERDES_RXA_IDAC_STATUS_SIGNERROR 0x15bc
+#define USB3_DP_QSERDES_RXA_READ_EQCODE 0x15c0
+#define USB3_DP_QSERDES_RXA_READ_OFFSETCODE 0x15c4
+#define USB3_DP_QSERDES_RXA_IA_ERROR_COUNTER_LOW 0x15c8
+#define USB3_DP_QSERDES_RXA_IA_ERROR_COUNTER_HIGH 0x15cc
+#define USB3_DP_QSERDES_RXA_VGA_READ_CODE 0x15d0
+#define USB3_DP_QSERDES_RXA_DFE_TAP1_READ_CODE 0x15d4
+#define USB3_DP_QSERDES_RXA_DFE_TAP2_READ_CODE 0x15d8
+#define USB3_DP_QSERDES_RXA_ALOG_OBSV_BUS_STATUS_1 0x15dc
+#define USB3_DP_QSERDES_TXB_BIST_MODE_LANENO 0x1600
+#define USB3_DP_QSERDES_TXB_BIST_INVERT 0x1604
+#define USB3_DP_QSERDES_TXB_CLKBUF_ENABLE 0x1608
+#define USB3_DP_QSERDES_TXB_TX_EMP_POST1_LVL 0x160c
+#define USB3_DP_QSERDES_TXB_TX_POST2_EMPH 0x1610
+#define USB3_DP_QSERDES_TXB_TX_BOOST_LVL_UP_DN 0x1614
+#define USB3_DP_QSERDES_TXB_TX_IDLE_LVL_LARGE_AMP 0x1618
+#define USB3_DP_QSERDES_TXB_TX_DRV_LVL 0x161c
+#define USB3_DP_QSERDES_TXB_TX_DRV_LVL_OFFSET 0x1620
+#define USB3_DP_QSERDES_TXB_RESET_TSYNC_EN 0x1624
+#define USB3_DP_QSERDES_TXB_PRE_STALL_LDO_BOOST_EN 0x1628
+#define USB3_DP_QSERDES_TXB_TX_BAND 0x162c
+#define USB3_DP_QSERDES_TXB_SLEW_CNTL 0x1630
+#define USB3_DP_QSERDES_TXB_INTERFACE_SELECT 0x1634
+#define USB3_DP_QSERDES_TXB_LPB_EN 0x1638
+#define USB3_DP_QSERDES_TXB_RES_CODE_LANE_TX 0x163c
+#define USB3_DP_QSERDES_TXB_RES_CODE_LANE_RX 0x1640
+#define USB3_DP_QSERDES_TXB_RES_CODE_LANE_OFFSET_TX 0x1644
+#define USB3_DP_QSERDES_TXB_RES_CODE_LANE_OFFSET_RX 0x1648
+#define USB3_DP_QSERDES_TXB_PERL_LENGTH1 0x164c
+#define USB3_DP_QSERDES_TXB_PERL_LENGTH2 0x1650
+#define USB3_DP_QSERDES_TXB_SERDES_BYP_EN_OUT 0x1654
+#define USB3_DP_QSERDES_TXB_DEBUG_BUS_SEL 0x1658
+#define USB3_DP_QSERDES_TXB_TRANSCEIVER_BIAS_EN 0x165c
+#define USB3_DP_QSERDES_TXB_HIGHZ_DRVR_EN 0x1660
+#define USB3_DP_QSERDES_TXB_TX_POL_INV 0x1664
+#define USB3_DP_QSERDES_TXB_PARRATE_REC_DETECT_IDLE_EN 0x1668
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN1 0x166c
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN2 0x1670
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN3 0x1674
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN4 0x1678
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN5 0x167c
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN6 0x1680
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN7 0x1684
+#define USB3_DP_QSERDES_TXB_BIST_PATTERN8 0x1688
+#define USB3_DP_QSERDES_TXB_LANE_MODE_1 0x168c
+#define USB3_DP_QSERDES_TXB_LANE_MODE_2 0x1690
+#define USB3_DP_QSERDES_TXB_LANE_MODE_3 0x1694
+#define USB3_DP_QSERDES_TXB_ATB_SEL1 0x1698
+#define USB3_DP_QSERDES_TXB_ATB_SEL2 0x169c
+#define USB3_DP_QSERDES_TXB_RCV_DETECT_LVL 0x16a0
+#define USB3_DP_QSERDES_TXB_RCV_DETECT_LVL_2 0x16a4
+#define USB3_DP_QSERDES_TXB_PRBS_SEED1 0x16a8
+#define USB3_DP_QSERDES_TXB_PRBS_SEED2 0x16ac
+#define USB3_DP_QSERDES_TXB_PRBS_SEED3 0x16b0
+#define USB3_DP_QSERDES_TXB_PRBS_SEED4 0x16b4
+#define USB3_DP_QSERDES_TXB_RESET_GEN 0x16b8
+#define USB3_DP_QSERDES_TXB_RESET_GEN_MUXES 0x16bc
+#define USB3_DP_QSERDES_TXB_TRAN_DRVR_EMP_EN 0x16c0
+#define USB3_DP_QSERDES_TXB_TX_INTERFACE_MODE 0x16c4
+#define USB3_DP_QSERDES_TXB_PWM_CTRL 0x16c8
+#define USB3_DP_QSERDES_TXB_PWM_ENCODED_OR_DATA 0x16cc
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_1_DIVIDER_BAND2 0x16d0
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_2_DIVIDER_BAND2 0x16d4
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_3_DIVIDER_BAND2 0x16d8
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_4_DIVIDER_BAND2 0x16dc
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_1_DIVIDER_BAND0_1 0x16e0
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_2_DIVIDER_BAND0_1 0x16e4
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_3_DIVIDER_BAND0_1 0x16e8
+#define USB3_DP_QSERDES_TXB_PWM_GEAR_4_DIVIDER_BAND0_1 0x16ec
+#define USB3_DP_QSERDES_TXB_VMODE_CTRL1 0x16f0
+#define USB3_DP_QSERDES_TXB_ALOG_OBSV_BUS_CTRL_1 0x16f4
+#define USB3_DP_QSERDES_TXB_BIST_STATUS 0x16f8
+#define USB3_DP_QSERDES_TXB_BIST_ERROR_COUNT1 0x16fc
+#define USB3_DP_QSERDES_TXB_BIST_ERROR_COUNT2 0x1700
+#define USB3_DP_QSERDES_TXB_ALOG_OBSV_BUS_STATUS_1 0x1704
+#define USB3_DP_QSERDES_RXB_UCDR_FO_GAIN_HALF 0x1800
+#define USB3_DP_QSERDES_RXB_UCDR_FO_GAIN_QUARTER 0x1804
+#define USB3_DP_QSERDES_RXB_UCDR_FO_GAIN 0x1808
+#define USB3_DP_QSERDES_RXB_UCDR_SO_GAIN_HALF 0x180c
+#define USB3_DP_QSERDES_RXB_UCDR_SO_GAIN_QUARTER 0x1810
+#define USB3_DP_QSERDES_RXB_UCDR_SO_GAIN 0x1814
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_FO_GAIN_HALF 0x1818
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_FO_GAIN_QUARTER 0x181c
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_FO_GAIN 0x1820
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_SO_GAIN_HALF 0x1824
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_SO_GAIN_QUARTER 0x1828
+#define USB3_DP_QSERDES_RXB_UCDR_SVS_SO_GAIN 0x182c
+#define USB3_DP_QSERDES_RXB_UCDR_FASTLOCK_FO_GAIN 0x1830
+#define USB3_DP_QSERDES_RXB_UCDR_SO_SATURATION_AND_ENABLE 0x1834
+#define USB3_DP_QSERDES_RXB_UCDR_FO_TO_SO_DELAY 0x1838
+#define USB3_DP_QSERDES_RXB_UCDR_FASTLOCK_COUNT_LOW 0x183c
+#define USB3_DP_QSERDES_RXB_UCDR_FASTLOCK_COUNT_HIGH 0x1840
+#define USB3_DP_QSERDES_RXB_UCDR_PI_CONTROLS 0x1844
+#define USB3_DP_QSERDES_RXB_UCDR_SB2_THRESH1 0x1848
+#define USB3_DP_QSERDES_RXB_UCDR_SB2_THRESH2 0x184c
+#define USB3_DP_QSERDES_RXB_UCDR_SB2_GAIN1 0x1850
+#define USB3_DP_QSERDES_RXB_UCDR_SB2_GAIN2 0x1854
+#define USB3_DP_QSERDES_RXB_AUX_CONTROL 0x1858
+#define USB3_DP_QSERDES_RXB_AUX_DATA_TCOARSE_TFINE 0x185c
+#define USB3_DP_QSERDES_RXB_RCLK_AUXDATA_SEL 0x1860
+#define USB3_DP_QSERDES_RXB_AC_JTAG_ENABLE 0x1864
+#define USB3_DP_QSERDES_RXB_AC_JTAG_INITP 0x1868
+#define USB3_DP_QSERDES_RXB_AC_JTAG_INITN 0x186c
+#define USB3_DP_QSERDES_RXB_AC_JTAG_LVL 0x1870
+#define USB3_DP_QSERDES_RXB_AC_JTAG_MODE 0x1874
+#define USB3_DP_QSERDES_RXB_AC_JTAG_RESET 0x1878
+#define USB3_DP_QSERDES_RXB_RX_TERM_BW 0x187c
+#define USB3_DP_QSERDES_RXB_RX_RCVR_IQ_EN 0x1880
+#define USB3_DP_QSERDES_RXB_RX_IDAC_I_DC_OFFSETS 0x1884
+#define USB3_DP_QSERDES_RXB_RX_IDAC_IBAR_DC_OFFSETS 0x1888
+#define USB3_DP_QSERDES_RXB_RX_IDAC_Q_DC_OFFSETS 0x188c
+#define USB3_DP_QSERDES_RXB_RX_IDAC_QBAR_DC_OFFSETS 0x1890
+#define USB3_DP_QSERDES_RXB_RX_IDAC_A_DC_OFFSETS 0x1894
+#define USB3_DP_QSERDES_RXB_RX_IDAC_ABAR_DC_OFFSETS 0x1898
+#define USB3_DP_QSERDES_RXB_RX_IDAC_EN 0x189c
+#define USB3_DP_QSERDES_RXB_RX_IDAC_ENABLES 0x18a0
+#define USB3_DP_QSERDES_RXB_RX_IDAC_SIGN 0x18a4
+#define USB3_DP_QSERDES_RXB_RX_HIGHZ_HIGHRATE 0x18a8
+#define USB3_DP_QSERDES_RXB_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x18ac
+#define USB3_DP_QSERDES_RXB_DFE_1 0x18b0
+#define USB3_DP_QSERDES_RXB_DFE_2 0x18b4
+#define USB3_DP_QSERDES_RXB_DFE_3 0x18b8
+#define USB3_DP_QSERDES_RXB_VGA_CAL_CNTRL1 0x18bc
+#define USB3_DP_QSERDES_RXB_VGA_CAL_CNTRL2 0x18c0
+#define USB3_DP_QSERDES_RXB_GM_CAL 0x18c4
+#define USB3_DP_QSERDES_RXB_RX_EQ_GAIN2_LSB 0x18c8
+#define USB3_DP_QSERDES_RXB_RX_EQ_GAIN2_MSB 0x18cc
+#define USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL1 0x18d0
+#define USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL2 0x18d4
+#define USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL3 0x18d8
+#define USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL4 0x18dc
+#define USB3_DP_QSERDES_RXB_RX_IDAC_TSETTLE_LOW 0x18e0
+#define USB3_DP_QSERDES_RXB_RX_IDAC_TSETTLE_HIGH 0x18e4
+#define USB3_DP_QSERDES_RXB_RX_IDAC_MEASURE_TIME 0x18e8
+#define USB3_DP_QSERDES_RXB_RX_IDAC_ACCUMULATOR 0x18ec
+#define USB3_DP_QSERDES_RXB_RX_EQ_OFFSET_LSB 0x18f0
+#define USB3_DP_QSERDES_RXB_RX_EQ_OFFSET_MSB 0x18f4
+#define USB3_DP_QSERDES_RXB_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x18f8
+#define USB3_DP_QSERDES_RXB_RX_OFFSET_ADAPTOR_CNTRL2 0x18fc
+#define USB3_DP_QSERDES_RXB_SIGDET_ENABLES 0x1900
+#define USB3_DP_QSERDES_RXB_SIGDET_CNTRL 0x1904
+#define USB3_DP_QSERDES_RXB_SIGDET_LVL 0x1908
+#define USB3_DP_QSERDES_RXB_SIGDET_DEGLITCH_CNTRL 0x190c
+#define USB3_DP_QSERDES_RXB_RX_BAND 0x1910
+#define USB3_DP_QSERDES_RXB_CDR_FREEZE_UP_DN 0x1914
+#define USB3_DP_QSERDES_RXB_CDR_RESET_OVERRIDE 0x1918
+#define USB3_DP_QSERDES_RXB_RX_INTERFACE_MODE 0x191c
+#define USB3_DP_QSERDES_RXB_JITTER_GEN_MODE 0x1920
+#define USB3_DP_QSERDES_RXB_BUJ_AMP 0x1924
+#define USB3_DP_QSERDES_RXB_SJ_AMP1 0x1928
+#define USB3_DP_QSERDES_RXB_SJ_AMP2 0x192c
+#define USB3_DP_QSERDES_RXB_SJ_PER1 0x1930
+#define USB3_DP_QSERDES_RXB_SJ_PER2 0x1934
+#define USB3_DP_QSERDES_RXB_BUJ_STEP_FREQ1 0x1938
+#define USB3_DP_QSERDES_RXB_BUJ_STEP_FREQ2 0x193c
+#define USB3_DP_QSERDES_RXB_PPM_OFFSET1 0x1940
+#define USB3_DP_QSERDES_RXB_PPM_OFFSET2 0x1944
+#define USB3_DP_QSERDES_RXB_SIGN_PPM_PERIOD1 0x1948
+#define USB3_DP_QSERDES_RXB_SIGN_PPM_PERIOD2 0x194c
+#define USB3_DP_QSERDES_RXB_RX_PWM_ENABLE_AND_DATA 0x1950
+#define USB3_DP_QSERDES_RXB_RX_PWM_GEAR1_TIMEOUT_COUNT 0x1954
+#define USB3_DP_QSERDES_RXB_RX_PWM_GEAR2_TIMEOUT_COUNT 0x1958
+#define USB3_DP_QSERDES_RXB_RX_PWM_GEAR3_TIMEOUT_COUNT 0x195c
+#define USB3_DP_QSERDES_RXB_RX_PWM_GEAR4_TIMEOUT_COUNT 0x1960
+#define USB3_DP_QSERDES_RXB_RX_MODE_00 0x1964
+#define USB3_DP_QSERDES_RXB_RX_MODE_01 0x1968
+#define USB3_DP_QSERDES_RXB_RX_MODE_10 0x196c
+#define USB3_DP_QSERDES_RXB_ALOG_OBSV_BUS_CTRL_1 0x1970
+#define USB3_DP_QSERDES_RXB_PI_CTRL1 0x1974
+#define USB3_DP_QSERDES_RXB_PI_CTRL2 0x1978
+#define USB3_DP_QSERDES_RXB_PI_QUAD 0x197c
+#define USB3_DP_QSERDES_RXB_IDATA1 0x1980
+#define USB3_DP_QSERDES_RXB_IDATA2 0x1984
+#define USB3_DP_QSERDES_RXB_AUX_DATA1 0x1988
+#define USB3_DP_QSERDES_RXB_AUX_DATA2 0x198c
+#define USB3_DP_QSERDES_RXB_AC_JTAG_OUTP 0x1990
+#define USB3_DP_QSERDES_RXB_AC_JTAG_OUTN 0x1994
+#define USB3_DP_QSERDES_RXB_RX_SIGDET 0x1998
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_I 0x199c
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_IBAR 0x19a0
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_Q 0x19a4
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_QBAR 0x19a8
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_A 0x19ac
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_ABAR 0x19b0
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_SM_ON 0x19b4
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_CAL_DONE 0x19b8
+#define USB3_DP_QSERDES_RXB_IDAC_STATUS_SIGNERROR 0x19bc
+#define USB3_DP_QSERDES_RXB_READ_EQCODE 0x19c0
+#define USB3_DP_QSERDES_RXB_READ_OFFSETCODE 0x19c4
+#define USB3_DP_QSERDES_RXB_IA_ERROR_COUNTER_LOW 0x19c8
+#define USB3_DP_QSERDES_RXB_IA_ERROR_COUNTER_HIGH 0x19cc
+#define USB3_DP_QSERDES_RXB_VGA_READ_CODE 0x19d0
+#define USB3_DP_QSERDES_RXB_DFE_TAP1_READ_CODE 0x19d4
+#define USB3_DP_QSERDES_RXB_DFE_TAP2_READ_CODE 0x19d8
+#define USB3_DP_QSERDES_RXB_ALOG_OBSV_BUS_STATUS_1 0x19dc
+#define USB3_DP_PCS_MISC_TYPEC_CTRL 0x1a00
+#define USB3_DP_PCS_MISC_TYPEC_PWRDN_CTRL 0x1a04
+#define USB3_DP_PCS_MISC_PCS_MISC_CONFIG1 0x1a08
+#define USB3_DP_PCS_MISC_CLAMP_ENABLE 0x1a0c
+#define USB3_DP_PCS_MISC_TYPEC_STATUS 0x1a10
+#define USB3_DP_PCS_MISC_PLACEHOLDER_STATUS 0x1a14
+#define USB3_DP_PCS_SW_RESET 0x1c00
+#define USB3_DP_PCS_POWER_DOWN_CONTROL 0x1c04
+#define USB3_DP_PCS_START_CONTROL 0x1c08
+#define USB3_DP_PCS_TXMGN_V0 0x1c0c
+#define USB3_DP_PCS_TXMGN_V1 0x1c10
+#define USB3_DP_PCS_TXMGN_V2 0x1c14
+#define USB3_DP_PCS_TXMGN_V3 0x1c18
+#define USB3_DP_PCS_TXMGN_V4 0x1c1c
+#define USB3_DP_PCS_TXMGN_LS 0x1c20
+#define USB3_DP_PCS_TXDEEMPH_M6DB_V0 0x1c24
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_V0 0x1c28
+#define USB3_DP_PCS_TXDEEMPH_M6DB_V1 0x1c2c
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_V1 0x1c30
+#define USB3_DP_PCS_TXDEEMPH_M6DB_V2 0x1c34
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_V2 0x1c38
+#define USB3_DP_PCS_TXDEEMPH_M6DB_V3 0x1c3c
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_V3 0x1c40
+#define USB3_DP_PCS_TXDEEMPH_M6DB_V4 0x1c44
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_V4 0x1c48
+#define USB3_DP_PCS_TXDEEMPH_M6DB_LS 0x1c4c
+#define USB3_DP_PCS_TXDEEMPH_M3P5DB_LS 0x1c50
+#define USB3_DP_PCS_ENDPOINT_REFCLK_DRIVE 0x1c54
+#define USB3_DP_PCS_RX_IDLE_DTCT_CNTRL 0x1c58
+#define USB3_DP_PCS_RATE_SLEW_CNTRL 0x1c5c
+#define USB3_DP_PCS_POWER_STATE_CONFIG1 0x1c60
+#define USB3_DP_PCS_POWER_STATE_CONFIG2 0x1c64
+#define USB3_DP_PCS_POWER_STATE_CONFIG3 0x1c68
+#define USB3_DP_PCS_POWER_STATE_CONFIG4 0x1c6c
+#define USB3_DP_PCS_RCVR_DTCT_DLY_P1U2_L 0x1c70
+#define USB3_DP_PCS_RCVR_DTCT_DLY_P1U2_H 0x1c74
+#define USB3_DP_PCS_RCVR_DTCT_DLY_U3_L 0x1c78
+#define USB3_DP_PCS_RCVR_DTCT_DLY_U3_H 0x1c7c
+#define USB3_DP_PCS_LOCK_DETECT_CONFIG1 0x1c80
+#define USB3_DP_PCS_LOCK_DETECT_CONFIG2 0x1c84
+#define USB3_DP_PCS_LOCK_DETECT_CONFIG3 0x1c88
+#define USB3_DP_PCS_TSYNC_RSYNC_TIME 0x1c8c
+#define USB3_DP_PCS_SIGDET_LOW_2_IDLE_TIME 0x1c90
+#define USB3_DP_PCS_BEACON_2_IDLE_TIME_L 0x1c94
+#define USB3_DP_PCS_BEACON_2_IDLE_TIME_H 0x1c98
+#define USB3_DP_PCS_PWRUP_RESET_DLY_TIME_SYSCLK 0x1c9c
+#define USB3_DP_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x1ca0
+#define USB3_DP_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x1ca4
+#define USB3_DP_PCS_PLL_LOCK_CHK_DLY_TIME 0x1ca8
+#define USB3_DP_PCS_LFPS_DET_HIGH_COUNT_VAL 0x1cac
+#define USB3_DP_PCS_LFPS_TX_ECSTART_EQTLOCK 0x1cb0
+#define USB3_DP_PCS_LFPS_TX_END_CNT_P2U3_START 0x1cb4
+#define USB3_DP_PCS_RXEQTRAINING_WAIT_TIME 0x1cb8
+#define USB3_DP_PCS_RXEQTRAINING_RUN_TIME 0x1cbc
+#define USB3_DP_PCS_TXONESZEROS_RUN_LENGTH 0x1cc0
+#define USB3_DP_PCS_FLL_CNTRL1 0x1cc4
+#define USB3_DP_PCS_FLL_CNTRL2 0x1cc8
+#define USB3_DP_PCS_FLL_CNT_VAL_L 0x1ccc
+#define USB3_DP_PCS_FLL_CNT_VAL_H_TOL 0x1cd0
+#define USB3_DP_PCS_FLL_MAN_CODE 0x1cd4
+#define USB3_DP_PCS_AUTONOMOUS_MODE_CTRL 0x1cd8
+#define USB3_DP_PCS_LFPS_RXTERM_IRQ_CLEAR 0x1cdc
+#define USB3_DP_PCS_ARCVR_DTCT_EN_PERIOD 0x1ce0
+#define USB3_DP_PCS_ARCVR_DTCT_CM_DLY 0x1ce4
+#define USB3_DP_PCS_ALFPS_DEGLITCH_VAL 0x1ce8
+#define USB3_DP_PCS_INSIG_SW_CTRL1 0x1cec
+#define USB3_DP_PCS_INSIG_SW_CTRL2 0x1cf0
+#define USB3_DP_PCS_INSIG_SW_CTRL3 0x1cf4
+#define USB3_DP_PCS_INSIG_MX_CTRL1 0x1cf8
+#define USB3_DP_PCS_INSIG_MX_CTRL2 0x1cfc
+#define USB3_DP_PCS_INSIG_MX_CTRL3 0x1d00
+#define USB3_DP_PCS_OUTSIG_SW_CTRL1 0x1d04
+#define USB3_DP_PCS_OUTSIG_MX_CTRL1 0x1d08
+#define USB3_DP_PCS_CLK_DEBUG_BYPASS_CTRL 0x1d0c
+#define USB3_DP_PCS_TEST_CONTROL 0x1d10
+#define USB3_DP_PCS_TEST_CONTROL2 0x1d14
+#define USB3_DP_PCS_TEST_CONTROL3 0x1d18
+#define USB3_DP_PCS_TEST_CONTROL4 0x1d1c
+#define USB3_DP_PCS_TEST_CONTROL5 0x1d20
+#define USB3_DP_PCS_TEST_CONTROL6 0x1d24
+#define USB3_DP_PCS_TEST_CONTROL7 0x1d28
+#define USB3_DP_PCS_COM_RESET_CONTROL 0x1d2c
+#define USB3_DP_PCS_BIST_CTRL 0x1d30
+#define USB3_DP_PCS_PRBS_POLY0 0x1d34
+#define USB3_DP_PCS_PRBS_POLY1 0x1d38
+#define USB3_DP_PCS_PRBS_SEED0 0x1d3c
+#define USB3_DP_PCS_PRBS_SEED1 0x1d40
+#define USB3_DP_PCS_FIXED_PAT_CTRL 0x1d44
+#define USB3_DP_PCS_FIXED_PAT0 0x1d48
+#define USB3_DP_PCS_FIXED_PAT1 0x1d4c
+#define USB3_DP_PCS_FIXED_PAT2 0x1d50
+#define USB3_DP_PCS_FIXED_PAT3 0x1d54
+#define USB3_DP_PCS_COM_CLK_SWITCH_CTRL 0x1d58
+#define USB3_DP_PCS_ELECIDLE_DLY_SEL 0x1d5c
+#define USB3_DP_PCS_SPARE1 0x1d60
+#define USB3_DP_PCS_BIST_CHK_ERR_CNT_L_STATUS 0x1d64
+#define USB3_DP_PCS_BIST_CHK_ERR_CNT_H_STATUS 0x1d68
+#define USB3_DP_PCS_BIST_CHK_STATUS 0x1d6c
+#define USB3_DP_PCS_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x1d70
+#define USB3_DP_PCS_PCS_STATUS 0x1d74
+#define USB3_DP_PCS_PCS_STATUS2 0x1d78
+#define USB3_DP_PCS_PCS_STATUS3 0x1d7c
+#define USB3_DP_PCS_COM_RESET_STATUS 0x1d80
+#define USB3_DP_PCS_OSC_DTCT_STATUS 0x1d84
+#define USB3_DP_PCS_REVISION_ID0 0x1d88
+#define USB3_DP_PCS_REVISION_ID1 0x1d8c
+#define USB3_DP_PCS_REVISION_ID2 0x1d90
+#define USB3_DP_PCS_REVISION_ID3 0x1d94
+#define USB3_DP_PCS_DEBUG_BUS_0_STATUS 0x1d98
+#define USB3_DP_PCS_DEBUG_BUS_1_STATUS 0x1d9c
+#define USB3_DP_PCS_DEBUG_BUS_2_STATUS 0x1da0
+#define USB3_DP_PCS_DEBUG_BUS_3_STATUS 0x1da4
+#define USB3_DP_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1da8
+#define USB3_DP_PCS_OSC_DTCT_ACTIONS 0x1dac
+#define USB3_DP_PCS_SIGDET_CNTRL 0x1db0
+#define USB3_DP_PCS_IDAC_CAL_CNTRL 0x1db4
+#define USB3_DP_PCS_CMN_ACK_OUT_SEL 0x1db8
+#define USB3_DP_PCS_PLL_LOCK_CHK_DLY_TIME_SYSCLK 0x1dbc
+#define USB3_DP_PCS_AUTONOMOUS_MODE_STATUS 0x1dc0
+#define USB3_DP_PCS_ENDPOINT_REFCLK_CNTRL 0x1dc4
+#define USB3_DP_PCS_EPCLK_PRE_PLL_LOCK_DLY_SYSCLK 0x1dc8
+#define USB3_DP_PCS_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x1dcc
+#define USB3_DP_PCS_EPCLK_DLY_COUNT_VAL_L 0x1dd0
+#define USB3_DP_PCS_EPCLK_DLY_COUNT_VAL_H 0x1dd4
+#define USB3_DP_PCS_RX_SIGDET_LVL 0x1dd8
+#define USB3_DP_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1ddc
+#define USB3_DP_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1de0
+#define USB3_DP_PCS_AUTONOMOUS_MODE_CTRL2 0x1de4
+#define USB3_DP_PCS_RXTERMINATION_DLY_SEL 0x1de8
+#define USB3_DP_PCS_LFPS_PER_TIMER_VAL 0x1dec
+#define USB3_DP_PCS_SIGDET_STARTUP_TIMER_VAL 0x1df0
+#define USB3_DP_PCS_LOCK_DETECT_CONFIG4 0x1df4
+#define USB3_DP_PCS_RX_SIGDET_DTCT_CNTRL 0x1df8
+#define USB3_DP_PCS_PCS_STATUS4 0x1dfc
+#define USB3_DP_PCS_PCS_STATUS4_CLEAR 0x1e00
+#define USB3_DP_PCS_DEC_ERROR_COUNT_STATUS 0x1e04
+#define USB3_DP_PCS_COMMA_POS_STATUS 0x1e08
+#define USB3_DP_PCS_REFGEN_REQ_CONFIG1 0x1e0c
+#define USB3_DP_PCS_REFGEN_REQ_CONFIG2 0x1e10
+#define USB3_DP_PCS_REFGEN_REQ_CONFIG3 0x1e14
+#define USB3_DP_PHY_DP_DP_PHY_PD_CTL 0x2a18
+
+#endif /* _DT_BINDINGS_PHY_QCOM_LAGOON_QMP_USB_H */
diff --git a/include/dt-bindings/power/r8a77970-sysc.h b/include/dt-bindings/power/r8a77970-sysc.h
index bf54779..9eaf824 100644
--- a/include/dt-bindings/power/r8a77970-sysc.h
+++ b/include/dt-bindings/power/r8a77970-sysc.h
@@ -19,10 +19,10 @@
#define R8A77970_PD_CR7 13
#define R8A77970_PD_CA53_SCU 21
#define R8A77970_PD_A2IR0 23
-#define R8A77970_PD_A3IR 24
+#define R8A77970_PD_A3IR 24
#define R8A77970_PD_A2IR1 27
-#define R8A77970_PD_A2IR2 28
-#define R8A77970_PD_A2IR3 29
+#define R8A77970_PD_A2DP 28
+#define R8A77970_PD_A2CN 29
#define R8A77970_PD_A2SC0 30
#define R8A77970_PD_A2SC1 31
diff --git a/include/dt-bindings/power/r8a77980-sysc.h b/include/dt-bindings/power/r8a77980-sysc.h
index 2c90c12..e12c858 100644
--- a/include/dt-bindings/power/r8a77980-sysc.h
+++ b/include/dt-bindings/power/r8a77980-sysc.h
@@ -15,14 +15,14 @@
#define R8A77980_PD_A2SC2 0
#define R8A77980_PD_A2SC3 1
#define R8A77980_PD_A2SC4 2
-#define R8A77980_PD_A2PD0 3
-#define R8A77980_PD_A2PD1 4
+#define R8A77980_PD_A2DP0 3
+#define R8A77980_PD_A2DP1 4
#define R8A77980_PD_CA53_CPU0 5
#define R8A77980_PD_CA53_CPU1 6
#define R8A77980_PD_CA53_CPU2 7
#define R8A77980_PD_CA53_CPU3 8
#define R8A77980_PD_A2CN 10
-#define R8A77980_PD_A3VIP 11
+#define R8A77980_PD_A3VIP0 11
#define R8A77980_PD_A2IR5 12
#define R8A77980_PD_CR7 13
#define R8A77980_PD_A2IR4 15
diff --git a/include/dt-bindings/sound/audio-codec-port-types.h b/include/dt-bindings/sound/audio-codec-port-types.h
index 2052bf5..0c45ea8 100644
--- a/include/dt-bindings/sound/audio-codec-port-types.h
+++ b/include/dt-bindings/sound/audio-codec-port-types.h
@@ -35,5 +35,18 @@
#define DMIC9 31
#define DMIC10 32
#define PCM_OUT1 33
+#define SWRM_TX1_CH1 34
+#define SWRM_TX1_CH2 35
+#define SWRM_TX1_CH3 36
+#define SWRM_TX1_CH4 37
+#define SWRM_TX2_CH1 38
+#define SWRM_TX2_CH2 39
+#define SWRM_TX2_CH3 40
+#define SWRM_TX2_CH4 41
+#define SWRM_TX3_CH1 42
+#define SWRM_TX3_CH2 43
+#define SWRM_TX3_CH3 44
+#define SWRM_TX3_CH4 45
+#define SWRM_PCM_IN 46
#endif /* __AUDIO_CODEC_PORT_TYPES_H */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 59a416d..df1252e 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -101,7 +101,7 @@ static inline bool has_acpi_companion(struct device *dev)
static inline void acpi_preset_companion(struct device *dev,
struct acpi_device *parent, u64 addr)
{
- ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, NULL));
+ ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, false));
}
static inline const char *acpi_dev_name(struct acpi_device *adev)
diff --git a/include/linux/adc-tm-clients.h b/include/linux/adc-tm-clients.h
index c3239c5..1e60824 100644
--- a/include/linux/adc-tm-clients.h
+++ b/include/linux/adc-tm-clients.h
@@ -59,6 +59,7 @@ enum adc_tm_state_request {
};
struct adc_tm_param {
+ unsigned long id;
int low_thr;
int high_thr;
uint32_t channel;
@@ -75,10 +76,12 @@ int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip,
struct adc_tm_param *param);
int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip,
struct adc_tm_param *param);
+
#else
static inline struct adc_tm_chip *get_adc_tm(
struct device *dev, const char *name)
{ return ERR_PTR(-ENXIO); }
+
static inline int32_t adc_tm5_channel_measure(
struct adc_tm_chip *chip,
struct adc_tm_param *param)
@@ -87,6 +90,7 @@ static inline int32_t adc_tm5_disable_chan_meas(
struct adc_tm_chip *chip,
struct adc_tm_param *param)
{ return -ENXIO; }
+
#endif
#endif /* __QCOM_ADC_TM_H_CLIENTS__ */
diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
index eaedca5f..345c3b5 100644
--- a/include/linux/ahci_platform.h
+++ b/include/linux/ahci_platform.h
@@ -23,6 +23,8 @@ struct ahci_host_priv;
struct platform_device;
struct scsi_host_template;
+int ahci_platform_enable_phys(struct ahci_host_priv *hpriv);
+void ahci_platform_disable_phys(struct ahci_host_priv *hpriv);
int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv);
diff --git a/include/linux/atalk.h b/include/linux/atalk.h
index d5cfc0b..f6034ba 100644
--- a/include/linux/atalk.h
+++ b/include/linux/atalk.h
@@ -108,7 +108,7 @@ static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb)
#define AARP_RESOLVE_TIME (10 * HZ)
extern struct datalink_proto *ddp_dl, *aarp_dl;
-extern void aarp_proto_init(void);
+extern int aarp_proto_init(void);
/* Inter module exports */
diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h
index 212b382..92d179f 100644
--- a/include/linux/avf/virtchnl.h
+++ b/include/linux/avf/virtchnl.h
@@ -798,8 +798,8 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
if (msglen >= valid_len) {
struct virtchnl_tc_info *vti =
(struct virtchnl_tc_info *)msg;
- valid_len += vti->num_tc *
- sizeof(struct virtchnl_channel_info);
+ valid_len += (vti->num_tc - 1) *
+ sizeof(struct virtchnl_channel_info);
if (vti->num_tc == 0)
err_msg_format = true;
}
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index acf5e8d..b71a033c 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -204,8 +204,13 @@ extern int bitmap_print_to_pagebuf(bool list, char *buf,
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
+/*
+ * The static inlines below do not handle constant nbits==0 correctly,
+ * so make such users (should any ever turn up) call the out-of-line
+ * versions.
+ */
#define small_const_nbits(nbits) \
- (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG && (nbits) > 0)
static inline void bitmap_zero(unsigned long *dst, unsigned int nbits)
{
@@ -398,7 +403,7 @@ static __always_inline void bitmap_clear(unsigned long *map, unsigned int start,
}
static inline void bitmap_shift_right(unsigned long *dst, const unsigned long *src,
- unsigned int shift, int nbits)
+ unsigned int shift, unsigned int nbits)
{
if (small_const_nbits(nbits))
*dst = (*src & BITMAP_LAST_WORD_MASK(nbits)) >> shift;
diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h
index 8804753..7bb2d8d 100644
--- a/include/linux/blktrace_api.h
+++ b/include/linux/blktrace_api.h
@@ -116,7 +116,13 @@ extern void blk_fill_rwbs(char *rwbs, unsigned int op, int bytes);
static inline sector_t blk_rq_trace_sector(struct request *rq)
{
- return blk_rq_is_passthrough(rq) ? 0 : blk_rq_pos(rq);
+ /*
+ * Tracing should ignore starting sector for passthrough requests and
+ * requests where starting sector didn't get set.
+ */
+ if (blk_rq_is_passthrough(rq) || blk_rq_pos(rq) == (sector_t)-1)
+ return 0;
+ return blk_rq_pos(rq);
}
static inline unsigned int blk_rq_trace_nr_sectors(struct request *rq)
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index c2bebe35..522df85 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -59,6 +59,11 @@ extern ssize_t cpu_show_l1tf(struct device *dev,
struct device_attribute *attr, char *buf);
extern ssize_t cpu_show_mds(struct device *dev,
struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_tsx_async_abort(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+extern ssize_t cpu_show_itlb_multihit(struct device *dev,
+ struct device_attribute *attr, char *buf);
extern __printf(4, 5)
struct device *cpu_device_create(struct device *parent, void *drvdata,
@@ -200,28 +205,7 @@ void idle_notifier_register(struct notifier_block *n);
void idle_notifier_unregister(struct notifier_block *n);
void idle_notifier_call_chain(unsigned long val);
-/*
- * These are used for a global "mitigations=" cmdline option for toggling
- * optional CPU mitigations.
- */
-enum cpu_mitigations {
- CPU_MITIGATIONS_OFF,
- CPU_MITIGATIONS_AUTO,
- CPU_MITIGATIONS_AUTO_NOSMT,
-};
-
-extern enum cpu_mitigations cpu_mitigations;
-
-/* mitigations=off */
-static inline bool cpu_mitigations_off(void)
-{
- return cpu_mitigations == CPU_MITIGATIONS_OFF;
-}
-
-/* mitigations=auto,nosmt */
-static inline bool cpu_mitigations_auto_nosmt(void)
-{
- return cpu_mitigations == CPU_MITIGATIONS_AUTO_NOSMT;
-}
+extern bool cpu_mitigations_off(void);
+extern bool cpu_mitigations_auto_nosmt(void);
#endif /* _LINUX_CPU_H_ */
diff --git a/include/linux/cpufeature.h b/include/linux/cpufeature.h
index 986c06c..84d3c81 100644
--- a/include/linux/cpufeature.h
+++ b/include/linux/cpufeature.h
@@ -45,7 +45,7 @@
* 'asm/cpufeature.h' of your favorite architecture.
*/
#define module_cpu_feature_match(x, __initfunc) \
-static struct cpu_feature const cpu_feature_match_ ## x[] = \
+static struct cpu_feature const __maybe_unused cpu_feature_match_ ## x[] = \
{ { .feature = cpu_feature(x) }, { } }; \
MODULE_DEVICE_TABLE(cpu, cpu_feature_match_ ## x); \
\
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 6e44890..12cec9c 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -564,17 +564,6 @@ struct governor_attr {
size_t count);
};
-static inline bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy)
-{
- /*
- * Allow remote callbacks if:
- * - dvfs_possible_from_any_cpu flag is set
- * - the local and remote CPUs share cpufreq policy
- */
- return policy->dvfs_possible_from_any_cpu ||
- cpumask_test_cpu(smp_processor_id(), policy->cpus);
-}
-
/*********************************************************************
* FREQUENCY TABLE HELPERS *
*********************************************************************/
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 35b06712..8ccffba8 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -81,6 +81,7 @@ struct cpuidle_device {
unsigned int registered:1;
unsigned int enabled:1;
unsigned int use_deepest_state:1;
+ unsigned int poll_time_limit:1;
unsigned int cpu;
int last_residency;
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 934633a..7f1478c 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -40,14 +40,14 @@ static inline bool cpusets_enabled(void)
static inline void cpuset_inc(void)
{
- static_branch_inc(&cpusets_pre_enable_key);
- static_branch_inc(&cpusets_enabled_key);
+ static_branch_inc_cpuslocked(&cpusets_pre_enable_key);
+ static_branch_inc_cpuslocked(&cpusets_enabled_key);
}
static inline void cpuset_dec(void)
{
- static_branch_dec(&cpusets_enabled_key);
- static_branch_dec(&cpusets_pre_enable_key);
+ static_branch_dec_cpuslocked(&cpusets_enabled_key);
+ static_branch_dec_cpuslocked(&cpusets_pre_enable_key);
}
extern int cpuset_init(void);
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 22cf17e..29829974 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -919,7 +919,7 @@ static const uint32_t msg_bld_masks_25[] = {
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x1CC6, /* EQUIP ID 1 */
+ 0x1CCA, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 9e15527..f439ffc 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -765,8 +765,7 @@ static inline unsigned int dma_get_max_seg_size(struct device *dev)
return SZ_64K;
}
-static inline unsigned int dma_set_max_seg_size(struct device *dev,
- unsigned int size)
+static inline int dma_set_max_seg_size(struct device *dev, unsigned int size)
{
if (dev->dma_parms) {
dev->dma_parms->max_segment_size = size;
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index d49ec5c..0647f43 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -1373,8 +1373,11 @@ static inline int dma_get_slave_caps(struct dma_chan *chan,
static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx)
{
struct dma_slave_caps caps;
+ int ret;
- dma_get_slave_caps(tx->chan, &caps);
+ ret = dma_get_slave_caps(tx->chan, &caps);
+ if (ret)
+ return ret;
if (caps.descriptor_reuse) {
tx->flags |= DMA_CTRL_REUSE;
diff --git a/include/linux/edac.h b/include/linux/edac.h
index bffb978..958d693 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -17,6 +17,7 @@
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
+#include <linux/numa.h>
#define EDAC_DEVICE_NAME_LEN 31
@@ -670,6 +671,6 @@ struct mem_ctl_info {
/*
* Maximum number of memory controllers in the coherent fabric.
*/
-#define EDAC_MAX_MCS 16
+#define EDAC_MAX_MCS 2 * MAX_NUMNODES
#endif
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 3e7e7538..7bfed84 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -736,8 +736,6 @@ extern int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var);
extern const unsigned char *fb_firmware_edid(struct device *device);
extern void fb_edid_to_monspecs(unsigned char *edid,
struct fb_monspecs *specs);
-extern void fb_edid_add_monspecs(unsigned char *edid,
- struct fb_monspecs *specs);
extern void fb_destroy_modedb(struct fb_videomode *modedb);
extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
@@ -811,7 +809,6 @@ struct dmt_videomode {
extern const char *fb_mode_option;
extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[65];
extern const struct dmt_videomode dmt_modes[];
struct fb_modelist {
diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h
index 3fdfede..5f343b79 100644
--- a/include/linux/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -274,6 +274,8 @@
*/
/* Auto Boot Mode */
#define IFC_NAND_NCFGR_BOOT 0x80000000
+/* SRAM Initialization */
+#define IFC_NAND_NCFGR_SRAM_INIT_EN 0x20000000
/* Addressing Mode-ROW0+n/COL0 */
#define IFC_NAND_NCFGR_ADDR_MODE_RC0 0x00000000
/* Addressing Mode-ROW0+n/COL0+n */
diff --git a/include/linux/futex.h b/include/linux/futex.h
index 821ae50..ccaef00 100644
--- a/include/linux/futex.h
+++ b/include/linux/futex.h
@@ -9,9 +9,6 @@ struct inode;
struct mm_struct;
struct task_struct;
-extern int
-handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi);
-
/*
* Futexes are matched on equal values of this key.
* The key type depends on whether it's a shared or private mapping.
@@ -55,11 +52,6 @@ extern void exit_robust_list(struct task_struct *curr);
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
u32 __user *uaddr2, u32 val2, u32 val3);
-#ifdef CONFIG_HAVE_FUTEX_CMPXCHG
-#define futex_cmpxchg_enabled 1
-#else
-extern int futex_cmpxchg_enabled;
-#endif
#else
static inline void exit_robust_list(struct task_struct *curr)
{
diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h
index 872f930..dd0a452 100644
--- a/include/linux/genalloc.h
+++ b/include/linux/genalloc.h
@@ -51,7 +51,8 @@ typedef unsigned long (*genpool_algo_t)(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
- void *data, struct gen_pool *pool);
+ void *data, struct gen_pool *pool,
+ unsigned long start_addr);
/*
* General purpose special memory pool descriptor.
@@ -131,24 +132,24 @@ extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo,
extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool);
+ struct gen_pool *pool, unsigned long start_addr);
extern unsigned long gen_pool_fixed_alloc(unsigned long *map,
unsigned long size, unsigned long start, unsigned int nr,
- void *data, struct gen_pool *pool);
+ void *data, struct gen_pool *pool, unsigned long start_addr);
extern unsigned long gen_pool_first_fit_align(unsigned long *map,
unsigned long size, unsigned long start, unsigned int nr,
- void *data, struct gen_pool *pool);
+ void *data, struct gen_pool *pool, unsigned long start_addr);
extern unsigned long gen_pool_first_fit_order_align(unsigned long *map,
unsigned long size, unsigned long start, unsigned int nr,
- void *data, struct gen_pool *pool);
+ void *data, struct gen_pool *pool, unsigned long start_addr);
extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool);
+ struct gen_pool *pool, unsigned long start_addr);
extern struct gen_pool *devm_gen_pool_create(struct device *dev,
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 45ac30d..730b56e 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -327,6 +327,29 @@ static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags)
return !!(gfp_flags & __GFP_DIRECT_RECLAIM);
}
+/**
+ * gfpflags_normal_context - is gfp_flags a normal sleepable context?
+ * @gfp_flags: gfp_flags to test
+ *
+ * Test whether @gfp_flags indicates that the allocation is from the
+ * %current context and allowed to sleep.
+ *
+ * An allocation being allowed to block doesn't mean it owns the %current
+ * context. When direct reclaim path tries to allocate memory, the
+ * allocation context is nested inside whatever %current was doing at the
+ * time of the original allocation. The nested allocation may be allowed
+ * to block but modifying anything %current owns can corrupt the outer
+ * context's expectations.
+ *
+ * %true result from this function indicates that the allocation context
+ * can sleep and use anything that's associated with %current.
+ */
+static inline bool gfpflags_normal_context(const gfp_t gfp_flags)
+{
+ return (gfp_flags & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC)) ==
+ __GFP_DIRECT_RECLAIM;
+}
+
#ifdef CONFIG_HIGHMEM
#define OPT_ZONE_HIGHMEM ZONE_HIGHMEM
#else
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 412098b..8dfd830 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -475,7 +475,7 @@ static inline int gpiod_set_consumer_name(struct gpio_desc *desc,
static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
{
- return ERR_PTR(-EINVAL);
+ return NULL;
}
static inline int desc_to_gpio(const struct gpio_desc *desc)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index d44a783..8b3e5e8 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -414,6 +414,7 @@ struct hid_global {
struct hid_local {
unsigned usage[HID_MAX_USAGES]; /* usage array */
+ u8 usage_size[HID_MAX_USAGES]; /* usage size array */
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
unsigned usage_index;
unsigned usage_minimum;
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 781c5b7..8bd9403 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -436,12 +436,18 @@ extern u64 hrtimer_next_event_without(const struct hrtimer *exclude);
extern bool hrtimer_active(const struct hrtimer *timer);
-/*
- * Helper function to check, whether the timer is on one of the queues
+/**
+ * hrtimer_is_queued = check, whether the timer is on one of the queues
+ * @timer: Timer to check
+ *
+ * Returns: True if the timer is queued, false otherwise
+ *
+ * The function can be used lockless, but it gives only a current snapshot.
*/
-static inline int hrtimer_is_queued(struct hrtimer *timer)
+static inline bool hrtimer_is_queued(struct hrtimer *timer)
{
- return timer->state & HRTIMER_STATE_ENQUEUED;
+ /* The READ_ONCE pairs with the update functions of timer->state */
+ return !!(READ_ONCE(timer->state) & HRTIMER_STATE_ENQUEUED);
}
/*
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 7722722..e375f22 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -91,7 +91,11 @@ extern bool is_vma_temporary_stack(struct vm_area_struct *vma);
extern unsigned long transparent_hugepage_flags;
-static inline bool transparent_hugepage_enabled(struct vm_area_struct *vma)
+/*
+ * to be used on vmas which are known to support THP.
+ * Use transparent_hugepage_enabled otherwise
+ */
+static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
{
if (vma->vm_flags & VM_NOHUGEPAGE)
return false;
@@ -115,6 +119,8 @@ static inline bool transparent_hugepage_enabled(struct vm_area_struct *vma)
return false;
}
+bool transparent_hugepage_enabled(struct vm_area_struct *vma);
+
#define transparent_hugepage_use_zero_page() \
(transparent_hugepage_flags & \
(1<<TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG))
@@ -255,6 +261,11 @@ static inline bool thp_migration_supported(void)
#define hpage_nr_pages(x) 1
+static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
+{
+ return false;
+}
+
static inline bool transparent_hugepage_enabled(struct vm_area_struct *vma)
{
return false;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index bbde887..c43e694 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -739,7 +739,7 @@ struct vmbus_channel {
u32 ringbuffer_gpadlhandle;
/* Allocated memory for ring buffer */
- void *ringbuffer_pages;
+ struct page *ringbuffer_page;
u32 ringbuffer_pagecount;
struct hv_ring_buffer_info outbound; /* send to parent */
struct hv_ring_buffer_info inbound; /* receive from parent */
diff --git a/include/linux/idr.h b/include/linux/idr.h
index 3ec8628..b6c6151 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -185,7 +185,7 @@ static inline void idr_preload_end(void)
* is convenient for a "not found" value.
*/
#define idr_for_each_entry(idr, entry, id) \
- for (id = 0; ((entry) = idr_get_next(idr, &(id))) != NULL; ++id)
+ for (id = 0; ((entry) = idr_get_next(idr, &(id))) != NULL; id += 1U)
/**
* idr_for_each_entry_ul() - Iterate over an IDR's elements of a given type.
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 548fd53..d433f5e 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -28,6 +28,14 @@ static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
return (struct ethhdr *)skb_mac_header(skb);
}
+/* Prefer this version in TX path, instead of
+ * skb_reset_mac_header() + eth_hdr()
+ */
+static inline struct ethhdr *skb_eth_hdr(const struct sk_buff *skb)
+{
+ return (struct ethhdr *)skb->data;
+}
+
static inline struct ethhdr *inner_eth_hdr(const struct sk_buff *skb)
{
return (struct ethhdr *)skb_inner_mac_header(skb);
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index c5ba38f1..05bbd92 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -37,7 +37,9 @@ struct in_device {
unsigned long mr_v1_seen;
unsigned long mr_v2_seen;
unsigned long mr_maxdelay;
- unsigned char mr_qrv;
+ unsigned long mr_qi; /* Query Interval */
+ unsigned long mr_qri; /* Query Response Interval */
+ unsigned char mr_qrv; /* Query Robustness Variable */
unsigned char mr_gq_running;
unsigned char mr_ifc_count;
struct timer_list mr_gq_timer; /* general query timer */
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 28004d7..b1b4411 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -286,7 +286,8 @@ enum {
#define QI_DEV_IOTLB_SID(sid) ((u64)((sid) & 0xffff) << 32)
#define QI_DEV_IOTLB_QDEP(qdep) (((qdep) & 0x1f) << 16)
#define QI_DEV_IOTLB_ADDR(addr) ((u64)(addr) & VTD_PAGE_MASK)
-#define QI_DEV_IOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52))
+#define QI_DEV_IOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | \
+ ((u64)((pfsid >> 4) & 0xfff) << 52))
#define QI_DEV_IOTLB_SIZE 1
#define QI_DEV_IOTLB_MAX_INVS 32
@@ -311,7 +312,8 @@ enum {
#define QI_DEV_EIOTLB_PASID(p) (((u64)p) << 32)
#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 16)
#define QI_DEV_EIOTLB_QDEP(qd) ((u64)((qd) & 0x1f) << 4)
-#define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52))
+#define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | \
+ ((u64)((pfsid >> 4) & 0xfff) << 52))
#define QI_DEV_EIOTLB_MAX_INVS 32
#define QI_PGRP_IDX(idx) (((u64)(idx)) << 55)
diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h
index a77b1fc..3eeef26 100644
--- a/include/linux/io-pgtable-fast.h
+++ b/include/linux/io-pgtable-fast.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
*/
#ifndef __LINUX_IO_PGTABLE_FAST_H
@@ -79,7 +79,8 @@ av8l_fast_iova_to_phys_public(struct io_pgtable_ops *ops,
*/
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa
-void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, bool skip_sync);
+void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, u64 base,
+ u64 start, u64 end, bool skip_sync);
void av8l_register_notify(struct notifier_block *nb);
#else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */
@@ -87,6 +88,9 @@ void av8l_register_notify(struct notifier_block *nb);
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0
static inline void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops,
+ u64 base,
+ u64 start,
+ u64 end,
bool skip_sync)
{
}
diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h
index 7d5fd38..1995ce14 100644
--- a/include/linux/ipmi_smi.h
+++ b/include/linux/ipmi_smi.h
@@ -211,10 +211,14 @@ static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd,
* is called, and the lower layer must get the interface from that
* call.
*/
-int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
- void *send_info,
- struct device *dev,
- unsigned char slave_addr);
+int ipmi_add_smi(struct module *owner,
+ const struct ipmi_smi_handlers *handlers,
+ void *send_info,
+ struct device *dev,
+ unsigned char slave_addr);
+
+#define ipmi_register_smi(handlers, send_info, dev, slave_addr) \
+ ipmi_add_smi(THIS_MODULE, handlers, send_info, dev, slave_addr)
/*
* Remove a low-level interface from the IPMI driver. This will
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index 1cf1b9b..268f300 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -1587,7 +1587,7 @@ static inline int jbd2_space_needed(journal_t *journal)
static inline unsigned long jbd2_log_space_left(journal_t *journal)
{
/* Allow for rounding errors */
- unsigned long free = journal->j_free - 32;
+ long free = journal->j_free - 32;
if (journal->j_committing_transaction) {
unsigned long committing = atomic_read(&journal->
@@ -1596,7 +1596,7 @@ static inline unsigned long jbd2_log_space_left(journal_t *journal)
/* Transaction + control blocks */
free -= committing + (committing >> JBD2_CONTROL_BLOCKS_SHIFT);
}
- return free;
+ return max_t(long, free, 0);
}
/*
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 444869d..b85cc8e 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -187,6 +187,7 @@ struct kernfs_root {
/* private fields, do not use outside kernfs proper */
struct idr ino_idr;
+ u32 last_ino;
u32 next_generation;
struct kernfs_syscall_ops *syscall_ops;
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 32cae0f..9adb92a 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -243,10 +243,13 @@ extern int arch_init_kprobes(void);
extern void show_registers(struct pt_regs *regs);
extern void kprobes_inc_nmissed_count(struct kprobe *p);
extern bool arch_within_kprobe_blacklist(unsigned long addr);
+extern int arch_populate_kprobe_blacklist(void);
extern bool arch_kprobe_on_func_entry(unsigned long offset);
extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset);
extern bool within_kprobe_blacklist(unsigned long addr);
+extern int kprobe_add_ksym_blacklist(unsigned long entry);
+extern int kprobe_add_area_blacklist(unsigned long start, unsigned long end);
struct kprobe_insn_cache {
struct mutex mutex;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d42a36e..748016a 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -141,7 +141,7 @@ static inline bool is_error_page(struct page *page)
extern struct kmem_cache *kvm_vcpu_cache;
-extern spinlock_t kvm_lock;
+extern struct mutex kvm_lock;
extern struct list_head vm_list;
struct kvm_io_range {
@@ -911,6 +911,7 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
bool kvm_is_reserved_pfn(kvm_pfn_t pfn);
+bool kvm_is_zone_device_pfn(kvm_pfn_t pfn);
struct kvm_irq_ack_notifier {
struct hlist_node link;
@@ -1034,6 +1035,7 @@ enum kvm_stat_kind {
struct kvm_stat_data {
int offset;
+ int mode;
struct kvm *kvm;
};
@@ -1041,6 +1043,7 @@ struct kvm_stats_debugfs_item {
const char *name;
int offset;
enum kvm_stat_kind kind;
+ int mode;
};
extern struct kvm_stats_debugfs_item debugfs_entries[];
extern struct dentry *kvm_debugfs_dir;
@@ -1303,4 +1306,10 @@ static inline int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
}
#endif /* CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE */
+typedef int (*kvm_vm_thread_fn_t)(struct kvm *kvm, uintptr_t data);
+
+int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn,
+ uintptr_t data, const char *name,
+ struct task_struct **thread_ptr);
+
#endif
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index d6f0ee0..8c8417c 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __LEDS_QPNP_FLASH_H
@@ -15,14 +15,16 @@
#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
-#ifdef CONFIG_LEDS_QPNP_FLASH_V2
+int qpnp_flash_register_led_prepare(struct device *dev, void *data);
+
+#if (defined CONFIG_LEDS_QTI_FLASH || defined CONFIG_LEDS_QPNP_FLASH_V2)
int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
int *max_current);
#else
static inline int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
int *max_current)
{
- return -EINVAL;
+ return -ENODEV;
}
#endif
diff --git a/include/linux/leds-qti-flash.h b/include/linux/leds-qti-flash.h
new file mode 100644
index 0000000..ac87c46
--- /dev/null
+++ b/include/linux/leds-qti-flash.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __LEDS_QTI_FLASH_H
+#define __LEDS_QTI_FLASH_H
+
+#include <linux/leds.h>
+
+#define QUERY_MAX_AVAIL_CURRENT BIT(0)
+
+int qpnp_flash_register_led_prepare(struct device *dev, void *data);
+
+#if (defined CONFIG_LEDS_QTI_FLASH || defined CONFIG_LEDS_QPNP_FLASH_V2)
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current);
+#else
+static inline int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current)
+{
+ return -ENODEV;
+}
+#endif
+#endif
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 38c95d6..aff09d0 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -1190,6 +1190,7 @@ extern unsigned int ata_do_dev_read_id(struct ata_device *dev,
struct ata_taskfile *tf, u16 *id);
extern void ata_qc_complete(struct ata_queued_cmd *qc);
extern int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active);
+extern u64 ata_qc_get_active(struct ata_port *ap);
extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd);
extern int ata_std_bios_param(struct scsi_device *sdev,
struct block_device *bdev,
diff --git a/include/linux/libfdt_env.h b/include/linux/libfdt_env.h
index 2175309..1adf54a 100644
--- a/include/linux/libfdt_env.h
+++ b/include/linux/libfdt_env.h
@@ -2,11 +2,14 @@
#ifndef LIBFDT_ENV_H
#define LIBFDT_ENV_H
-#include <linux/kernel.h>
+#include <linux/kernel.h> /* For INT_MAX */
#include <linux/string.h>
#include <asm/byteorder.h>
+#define INT32_MAX S32_MAX
+#define UINT32_MAX U32_MAX
+
typedef __be16 fdt16_t;
typedef __be32 fdt32_t;
typedef __be64 fdt64_t;
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 90e2653..2aa71ce 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -330,6 +330,7 @@ static inline void remove_memory(int nid, u64 start, u64 size) {}
extern void __ref free_area_init_core_hotplug(int nid);
extern int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
void *arg, int (*func)(struct memory_block *, void *));
+extern int __add_memory(int nid, u64 start, u64 size);
extern int add_memory(int nid, u64 start, u64 size);
extern int add_memory_resource(int nid, struct resource *resource, bool online);
extern int arch_add_memory(int nid, u64 start, u64 size,
diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
index 5aacdb0..806a4f0 100644
--- a/include/linux/mfd/intel_soc_pmic.h
+++ b/include/linux/mfd/intel_soc_pmic.h
@@ -25,6 +25,7 @@ struct intel_soc_pmic {
int irq;
struct regmap *regmap;
struct regmap_irq_chip_data *irq_chip_data;
+ struct regmap_irq_chip_data *irq_chip_data_pwrbtn;
struct regmap_irq_chip_data *irq_chip_data_tmu;
struct regmap_irq_chip_data *irq_chip_data_bcu;
struct regmap_irq_chip_data *irq_chip_data_adc;
diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h
index cf81557..3ae1fe7 100644
--- a/include/linux/mfd/max8997.h
+++ b/include/linux/mfd/max8997.h
@@ -178,7 +178,6 @@ struct max8997_led_platform_data {
struct max8997_platform_data {
/* IRQ */
int ono;
- int wakeup;
/* ---- PMIC ---- */
struct max8997_regulator_data *regulators;
diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h
index 54a3cd8..2ad9bdc 100644
--- a/include/linux/mfd/mc13xxx.h
+++ b/include/linux/mfd/mc13xxx.h
@@ -249,6 +249,7 @@ struct mc13xxx_platform_data {
#define MC13XXX_ADC0_TSMOD0 (1 << 12)
#define MC13XXX_ADC0_TSMOD1 (1 << 13)
#define MC13XXX_ADC0_TSMOD2 (1 << 14)
+#define MC13XXX_ADC0_CHRGRAWDIV (1 << 15)
#define MC13XXX_ADC0_ADINC1 (1 << 16)
#define MC13XXX_ADC0_ADINC2 (1 << 17)
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index d315659..338e0f6 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -443,7 +443,7 @@ enum {
enum {
RK805_ID = 0x8050,
RK808_ID = 0x0000,
- RK818_ID = 0x8181,
+ RK818_ID = 0x8180,
};
struct rk808 {
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index a8ffa7f..1683035 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#ifndef _MHI_H_
#define _MHI_H_
@@ -263,6 +263,7 @@ struct mhi_controller {
void __iomem *bhi;
void __iomem *bhie;
void __iomem *wake_db;
+ void __iomem *tsync_db;
void __iomem *bw_scale_db;
/* device topology */
@@ -306,7 +307,7 @@ struct mhi_controller {
u32 msi_allocated;
int *irq; /* interrupt table */
struct mhi_event *mhi_event;
- struct list_head lp_ev_rings; /* low priority event rings */
+ struct list_head sp_ev_rings; /* special purpose event rings */
/* cmd rings */
struct mhi_cmd *mhi_cmd;
@@ -346,8 +347,9 @@ struct mhi_controller {
/* worker for different state transitions */
struct work_struct st_worker;
- struct work_struct fw_worker;
- struct work_struct low_priority_worker;
+ struct work_struct special_work;
+ struct workqueue_struct *special_wq;
+
wait_queue_head_t state_event;
/* shadow functions */
@@ -381,7 +383,6 @@ struct mhi_controller {
/* supports time sync feature */
struct mhi_timesync *mhi_tsync;
- struct mhi_device *tsync_dev;
u64 local_timer_freq;
u64 remote_timer_freq;
@@ -398,8 +399,7 @@ struct mhi_controller {
/* controller specific data */
const char *name;
bool power_down;
- bool need_force_m3;
- bool force_m3_done;
+ bool initiate_mhi_reset;
void *priv_data;
void *log_buf;
struct dentry *dentry;
@@ -776,6 +776,23 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl);
void mhi_dump_sfr(struct mhi_controller *mhi_cntrl);
/**
+ * mhi_get_remote_time - Get external modem time relative to host time
+ * Trigger event to capture modem time, also capture host time so client
+ * can do a relative drift comparision.
+ * Recommended only tsync device calls this method and do not call this
+ * from atomic context
+ * @mhi_dev: Device associated with the channels
+ * @sequence:unique sequence id track event
+ * @cb_func: callback function to call back
+ */
+int mhi_get_remote_time(struct mhi_device *mhi_dev,
+ u32 sequence,
+ void (*cb_func)(struct mhi_device *mhi_dev,
+ u32 sequence,
+ u64 local_time,
+ u64 remote_time));
+
+/**
* mhi_get_remote_time_sync - Get external soc time relative to local soc time
* using MMIO method.
* @mhi_dev: Device associated with the channels
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 3247a3d..b06b757 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -57,6 +57,7 @@
#define UHID_MINOR 239
#define USERIO_MINOR 240
#define VHOST_VSOCK_MINOR 241
+#define RFKILL_MINOR 242
#define MISC_DYNAMIC_MINOR 255
struct device;
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index e8b92de..ae64fce 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -163,10 +163,7 @@ enum mlx5_dcbx_oper_mode {
};
enum mlx5_dct_atomic_mode {
- MLX5_ATOMIC_MODE_DCT_OFF = 20,
- MLX5_ATOMIC_MODE_DCT_NONE = 0 << MLX5_ATOMIC_MODE_DCT_OFF,
- MLX5_ATOMIC_MODE_DCT_IB_COMP = 1 << MLX5_ATOMIC_MODE_DCT_OFF,
- MLX5_ATOMIC_MODE_DCT_CX = 2 << MLX5_ATOMIC_MODE_DCT_OFF,
+ MLX5_ATOMIC_MODE_DCT_CX = 2,
};
enum {
diff --git a/include/linux/mm.h b/include/linux/mm.h
index cbc05d1..be33bf85 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -624,11 +624,6 @@ static inline void *kvcalloc(size_t n, size_t size, gfp_t flags)
extern void kvfree(const void *addr);
-static inline atomic_t *compound_mapcount_ptr(struct page *page)
-{
- return &page[1].compound_mapcount;
-}
-
static inline int compound_mapcount(struct page *page)
{
VM_BUG_ON_PAGE(!PageCompound(page), page);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 61b7164b..3d7d677 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -226,6 +226,11 @@ struct page_frag_cache {
typedef unsigned long vm_flags_t;
+static inline atomic_t *compound_mapcount_ptr(struct page *page)
+{
+ return &page[1].compound_mapcount;
+}
+
/*
* A region containing a mapping of a non-memory backed file under NOMMU
* conditions. These are held in a global tree and are pinned by the VMAs that
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 12b42dd..526e4d7 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -575,9 +575,9 @@ struct platform_device_id {
#define MDIO_NAME_SIZE 32
#define MDIO_MODULE_PREFIX "mdio:"
-#define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d"
+#define MDIO_ID_FMT "%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u"
#define MDIO_ID_ARGS(_id) \
- (_id)>>31, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \
+ ((_id)>>31) & 1, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \
((_id)>>27) & 1, ((_id)>>26) & 1, ((_id)>>25) & 1, ((_id)>>24) & 1, \
((_id)>>23) & 1, ((_id)>>22) & 1, ((_id)>>21) & 1, ((_id)>>20) & 1, \
((_id)>>19) & 1, ((_id)>>18) & 1, ((_id)>>17) & 1, ((_id)>>16) & 1, \
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index da30c1e..177e1ab 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef MSM_GSI_H
@@ -97,6 +97,7 @@ enum gsi_intr_type {
* @rel_clk_cb: callback to release peripheral clock
* @user_data: cookie used for notifications
* @clk_status_cb: callback to update the current msm bus clock vote
+ * @enable_clk_bug_on: enable IPA clock for dump saving before assert
* @skip_ieob_mask_wa: flag for skipping ieob_mask_wa
* All the callbacks are in interrupt context
*
@@ -120,6 +121,7 @@ struct gsi_per_props {
int (*rel_clk_cb)(void *user_data);
void *user_data;
int (*clk_status_cb)(void);
+ void (*enable_clk_bug_on)(void);
bool skip_ieob_mask_wa;
};
@@ -1315,6 +1317,16 @@ int gsi_read_channel_scratch(unsigned long chan_hdl,
union __packed gsi_channel_scratch *val);
/**
+ * gsi_pending_irq_type - Peripheral should call this function to
+ * check if there is any pending irq
+ *
+ * This function can sleep
+ *
+ * @Return gsi_irq_type
+ */
+int gsi_pending_irq_type(void);
+
+/**
* gsi_update_mhi_channel_scratch - MHI Peripheral should call this
* function to update the scratch area of the channel context. Updating
* will be by read-modify-write method, so non SWI fields will not be
@@ -1654,6 +1666,20 @@ int gsi_map_virtual_ch_to_per_ep(u32 ee, u32 chan_num, u32 per_ep_index);
*/
int gsi_alloc_channel_ee(unsigned int chan_idx, unsigned int ee, int *code);
+/**
+ * gsi_enable_flow_control_ee - Peripheral should call this function
+ * to enable flow control other EE's channel. This is usually done in USB
+ * connent and SSR scenarios.
+ *
+ * @chan_idx: Virtual channel index
+ * @ee: EE
+ * @code: [out] response code for operation
+
+ * @Return gsi_status
+ */
+int gsi_enable_flow_control_ee(unsigned int chan_idx, unsigned int ee,
+ int *code);
+
/*
* Here is a typical sequence of calls
*
@@ -1774,6 +1800,11 @@ static inline int gsi_read_channel_scratch(unsigned long chan_hdl,
return -GSI_STATUS_UNSUPPORTED_OP;
}
+static inline int gsi_pending_irq_type(void)
+{
+ return -GSI_STATUS_UNSUPPORTED_OP;
+}
+
static inline int gsi_update_mhi_channel_scratch(unsigned long chan_hdl,
struct __packed gsi_mhi_channel_scratch mscr)
{
@@ -1923,6 +1954,12 @@ static inline int gsi_alloc_channel_ee(unsigned int chan_idx, unsigned int ee,
return -GSI_STATUS_UNSUPPORTED_OP;
}
+static inline int gsi_enable_flow_control_ee(unsigned int chan_idx,
+ unsigned int ee, int *code)
+{
+ return -GSI_STATUS_UNSUPPORTED_OP;
+}
+
static inline void gsi_wdi3_write_evt_ring_db(
unsigned long chan_hdl, uint32_t db_addr_low,
uint32_t db_addr_high)
diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h
index 077883e..acffc4c 100644
--- a/include/linux/msm_pcie.h
+++ b/include/linux/msm_pcie.h
@@ -12,6 +12,7 @@ enum msm_pcie_config {
MSM_PCIE_CONFIG_NO_CFG_RESTORE = 0x1,
MSM_PCIE_CONFIG_LINKDOWN = 0x2,
MSM_PCIE_CONFIG_NO_RECOVERY = 0x4,
+ MSM_PCIE_CONFIG_NO_L1SS_TO = 0x8,
};
enum msm_pcie_pm_opt {
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index cd0be91..035d641 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -386,7 +386,7 @@ static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
return dev_of_node(&mtd->dev);
}
-static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
+static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
{
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
}
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 14678fd..58b64a1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1619,6 +1619,8 @@ enum netdev_priv_flags {
* @perm_addr: Permanent hw address
* @addr_assign_type: Hw address assignment type
* @addr_len: Hardware address length
+ * @upper_level: Maximum depth level of upper devices.
+ * @lower_level: Maximum depth level of lower devices.
* @neigh_priv_len: Used in neigh_alloc()
* @dev_id: Used to differentiate devices that share
* the same link layer address
@@ -1834,6 +1836,11 @@ struct net_device {
unsigned char if_port;
unsigned char dma;
+ /* Note : dev->mtu is often read without holding a lock.
+ * Writers usually hold RTNL.
+ * It is recommended to use READ_ONCE() to annotate the reads,
+ * and to use WRITE_ONCE() to annotate the writes.
+ */
unsigned int mtu;
unsigned int min_mtu;
unsigned int max_mtu;
@@ -1848,6 +1855,8 @@ struct net_device {
unsigned char perm_addr[MAX_ADDR_LEN];
unsigned char addr_assign_type;
unsigned char addr_len;
+ unsigned char upper_level;
+ unsigned char lower_level;
unsigned short neigh_priv_len;
unsigned short dev_id;
unsigned short dev_port;
@@ -3791,7 +3800,7 @@ static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits)
if (debug_value == 0) /* no output */
return 0;
/* set low N bits */
- return (1 << debug_value) - 1;
+ return (1U << debug_value) - 1;
}
static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu)
diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h
index 496ff75..2f3ae41 100644
--- a/include/linux/nvme-fc-driver.h
+++ b/include/linux/nvme-fc-driver.h
@@ -282,6 +282,8 @@ struct nvme_fc_remote_port {
*
* Host/Initiator Transport Entrypoints/Parameters:
*
+ * @module: The LLDD module using the interface
+ *
* @localport_delete: The LLDD initiates deletion of a localport via
* nvme_fc_deregister_localport(). However, the teardown is
* asynchronous. This routine is called upon the completion of the
@@ -395,6 +397,8 @@ struct nvme_fc_remote_port {
* Value is Mandatory. Allowed to be zero.
*/
struct nvme_fc_port_template {
+ struct module *module;
+
/* initiator-based functions */
void (*localport_delete)(struct nvme_fc_local_port *);
void (*remoteport_delete)(struct nvme_fc_remote_port *);
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 4d99504..c0e5c79 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -580,12 +580,28 @@ static inline int PageTransCompound(struct page *page)
*
* Unlike PageTransCompound, this is safe to be called only while
* split_huge_pmd() cannot run from under us, like if protected by the
- * MMU notifier, otherwise it may result in page->_mapcount < 0 false
+ * MMU notifier, otherwise it may result in page->_mapcount check false
* positives.
+ *
+ * We have to treat page cache THP differently since every subpage of it
+ * would get _mapcount inc'ed once it is PMD mapped. But, it may be PTE
+ * mapped in the current process so comparing subpage's _mapcount to
+ * compound_mapcount to filter out PTE mapped case.
*/
static inline int PageTransCompoundMap(struct page *page)
{
- return PageTransCompound(page) && atomic_read(&page->_mapcount) < 0;
+ struct page *head;
+
+ if (!PageTransCompound(page))
+ return 0;
+
+ if (PageAnon(page))
+ return atomic_read(&page->_mapcount) < 0;
+
+ head = compound_head(page);
+ /* File THP is PMD mapped and not PTE mapped */
+ return atomic_read(&page->_mapcount) ==
+ atomic_read(compound_mapcount_ptr(head));
}
/*
diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h
index 896cb71..1a1d58e 100644
--- a/include/linux/platform_data/dma-dw.h
+++ b/include/linux/platform_data/dma-dw.h
@@ -49,6 +49,7 @@ struct dw_dma_slave {
* @data_width: Maximum data width supported by hardware per AHB master
* (in bytes, power of 2)
* @multi_block: Multi block transfers supported by hardware per channel.
+ * @protctl: Protection control signals setting per channel.
*/
struct dw_dma_platform_data {
unsigned int nr_channels;
@@ -65,6 +66,11 @@ struct dw_dma_platform_data {
unsigned char nr_masters;
unsigned char data_width[DW_DMA_MAX_NR_MASTERS];
unsigned char multi_block[DW_DMA_MAX_NR_CHANNELS];
+#define CHAN_PROTCTL_PRIVILEGED BIT(0)
+#define CHAN_PROTCTL_BUFFERABLE BIT(1)
+#define CHAN_PROTCTL_CACHEABLE BIT(2)
+#define CHAN_PROTCTL_MASK GENMASK(2, 0)
+ unsigned char protctl;
};
#endif /* _PLATFORM_DATA_DMA_DW_H */
diff --git a/include/linux/platform_data/dma-ep93xx.h b/include/linux/platform_data/dma-ep93xx.h
index f8f1f6b..eb9805b 100644
--- a/include/linux/platform_data/dma-ep93xx.h
+++ b/include/linux/platform_data/dma-ep93xx.h
@@ -85,7 +85,7 @@ static inline enum dma_transfer_direction
ep93xx_dma_chan_direction(struct dma_chan *chan)
{
if (!ep93xx_dma_chan_is_m2p(chan))
- return DMA_NONE;
+ return DMA_TRANS_NONE;
/* even channels are for TX, odd for RX */
return (chan->chan_id % 2 == 0) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 3a3bc71..03cb1f2 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -82,29 +82,32 @@ struct posix_clock_operations {
*
* @ops: Functional interface to the clock
* @cdev: Character device instance for this clock
- * @kref: Reference count.
+ * @dev: Pointer to the clock's device.
* @rwsem: Protects the 'zombie' field from concurrent access.
* @zombie: If 'zombie' is true, then the hardware has disappeared.
- * @release: A function to free the structure when the reference count reaches
- * zero. May be NULL if structure is statically allocated.
*
* Drivers should embed their struct posix_clock within a private
* structure, obtaining a reference to it during callbacks using
* container_of().
+ *
+ * Drivers should supply an initialized but not exposed struct device
+ * to posix_clock_register(). It is used to manage lifetime of the
+ * driver's private structure. It's 'release' field should be set to
+ * a release function for this private structure.
*/
struct posix_clock {
struct posix_clock_operations ops;
struct cdev cdev;
- struct kref kref;
+ struct device *dev;
struct rw_semaphore rwsem;
bool zombie;
- void (*release)(struct posix_clock *clk);
};
/**
* posix_clock_register() - register a new clock
- * @clk: Pointer to the clock. Caller must provide 'ops' and 'release'
- * @devid: Allocated device id
+ * @clk: Pointer to the clock. Caller must provide 'ops' field
+ * @dev: Pointer to the initialized device. Caller must provide
+ * 'release' field
*
* A clock driver calls this function to register itself with the
* clock device subsystem. If 'clk' points to dynamically allocated
@@ -113,7 +116,7 @@ struct posix_clock {
*
* Returns zero on success, non-zero otherwise.
*/
-int posix_clock_register(struct posix_clock *clk, dev_t devid);
+int posix_clock_register(struct posix_clock *clk, struct device *dev);
/**
* posix_clock_unregister() - unregister a clock
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 8188789..e9a6d4d 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -106,6 +106,7 @@ enum {
POWER_SUPPLY_DP_DM_FORCE_5V = 13,
POWER_SUPPLY_DP_DM_FORCE_9V = 14,
POWER_SUPPLY_DP_DM_FORCE_12V = 15,
+ POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5 = 16,
};
enum {
@@ -125,6 +126,7 @@ enum {
enum {
POWER_SUPPLY_CP_NONE = 0,
POWER_SUPPLY_CP_HVDCP3,
+ POWER_SUPPLY_CP_HVDCP3P5,
POWER_SUPPLY_CP_PPS,
POWER_SUPPLY_CP_WIRELESS,
};
@@ -344,6 +346,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_SKIN_HEALTH,
POWER_SUPPLY_PROP_AICL_DONE,
POWER_SUPPLY_PROP_VOLTAGE_STEP,
+ POWER_SUPPLY_PROP_APSD_RERUN,
+ POWER_SUPPLY_PROP_APSD_TIMEOUT,
/* Charge pump properties */
POWER_SUPPLY_PROP_CP_STATUS1,
POWER_SUPPLY_PROP_CP_STATUS2,
@@ -386,6 +390,7 @@ enum power_supply_type {
POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
POWER_SUPPLY_TYPE_USB_HVDCP, /* High Voltage DCP */
POWER_SUPPLY_TYPE_USB_HVDCP_3, /* Efficient High Voltage DCP */
+ POWER_SUPPLY_TYPE_USB_HVDCP_3P5, /* Efficient High Voltage DCP */
POWER_SUPPLY_TYPE_WIRELESS, /* Accessory Charger Adapters */
POWER_SUPPLY_TYPE_USB_FLOAT, /* Floating charger */
POWER_SUPPLY_TYPE_BMS, /* Battery Monitor System */
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index 3bcbaaa..c06d81d 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -668,6 +668,19 @@ int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
void *tx_buf, int tx_len, dma_addr_t *tx_dma);
/**
+ * geni_se_rx_dma_start() - Prepare the Serial Engine registers for RX DMA
+ transfers.
+ * @base: Base address of the SE register block.
+ * @rx_len: Length of the RX buffer.
+ * @rx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the Serial Engine registers for DMA RX.
+ *
+ * Return: None.
+ */
+void geni_se_rx_dma_start(void __iomem *base, int rx_len, dma_addr_t *rx_dma);
+
+/**
* geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
* @wrapper_dev: QUPv3 Wrapper Device to which the TX buffer is mapped.
* @base: Base address of the SE register block.
@@ -795,6 +808,7 @@ int geni_se_iommu_free_buf(struct device *wrapper_dev, dma_addr_t *iova,
*/
void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base,
void *ipc);
+
#else
static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
{
@@ -981,5 +995,10 @@ static void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base,
{
}
+static void geni_se_rx_dma_start(void __iomem *base, int rx_len,
+ dma_addr_t *rx_dma)
+{
+}
+
#endif
#endif
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index 5d65521..116b81a 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -65,6 +65,9 @@ extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare);
extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
#else
+
+#include <linux/errno.h>
+
static inline
int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
{
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 4d809ef..02d7251 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __QPNP_REVID
@@ -190,6 +190,15 @@
/* PMI632 */
#define PMI632_SUBTYPE 0x25
+/* PM6350 */
+#define PM6350_SUBTYPE 0x36
+
+/* PMK8350 */
+#define PMK8350_SUBTYPE 0x2F
+
+/* PMR735B */
+#define PMR735B_SUBTYPE 0x34
+
/* PM8008 SUBTYPE */
#define PM8008_SUBTYPE 0x2C
@@ -291,6 +300,30 @@
#define PM6125_V1P0_REV3 0x00
#define PM6125_V1P0_REV4 0x01
+/* PM2250_REV_ID */
+#define PM2250_V1P0_REV1 0x00
+#define PM2250_V1P0_REV2 0x01
+#define PM2250_V1P0_REV3 0x01
+#define PM2250_V1P0_REV4 0x01
+
+/* PM6350_REV_ID */
+#define PM6350_V1P0_REV1 0x10
+#define PM6350_V1P0_REV2 0x00
+#define PM6350_V1P0_REV3 0x00
+#define PM6350_V1P0_REV4 0x01
+
+/* PMK8350_REV_ID */
+#define PMK8350_V1P0_REV1 0x00
+#define PMK8350_V1P0_REV2 0x00
+#define PMK8350_V1P0_REV3 0x00
+#define PMK8350_V1P0_REV4 0x01
+
+/* PMR735B_REV_ID */
+#define PMR735B_V1P0_REV1 0x11
+#define PMR735B_V1P0_REV2 0x00
+#define PMR735B_V1P0_REV3 0x01
+#define PMR735B_V1P0_REV4 0x01
+
/* PMI8998 FAB_ID */
#define PMI8998_FAB_ID_SMIC 0x11
#define PMI8998_FAB_ID_GF 0x30
@@ -306,8 +339,8 @@
/* PM8937 */
#define PM8937_SUBTYPE 0x19
-/* PMI8937 */
-#define PMI8937_SUBTYPE 0x37
+/* PM2250 */
+#define PM2250_SUBTYPE 0x37
/* SMB1390 */
#define SMB1390_SUBTYPE 0x23
diff --git a/include/linux/quota.h b/include/linux/quota.h
index f32dd27..27aab84 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -263,7 +263,7 @@ enum {
};
struct dqstats {
- int stat[_DQST_DQSTAT_LAST];
+ unsigned long stat[_DQST_DQSTAT_LAST];
struct percpu_counter counter[_DQST_DQSTAT_LAST];
};
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 185d948..91e0b76 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -54,6 +54,16 @@ static inline struct dquot *dqgrab(struct dquot *dquot)
atomic_inc(&dquot->dq_count);
return dquot;
}
+
+static inline bool dquot_is_busy(struct dquot *dquot)
+{
+ if (test_bit(DQ_MOD_B, &dquot->dq_flags))
+ return true;
+ if (atomic_read(&dquot->dq_count) > 1)
+ return true;
+ return false;
+}
+
void dqput(struct dquot *dquot);
int dquot_scan_active(struct super_block *sb,
int (*fn)(struct dquot *dquot, unsigned long priv),
diff --git a/include/linux/rculist_nulls.h b/include/linux/rculist_nulls.h
index bc8206a..61974c4 100644
--- a/include/linux/rculist_nulls.h
+++ b/include/linux/rculist_nulls.h
@@ -101,6 +101,43 @@ static inline void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n,
}
/**
+ * hlist_nulls_add_tail_rcu
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist_nulls,
+ * while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_nulls_add_head_rcu()
+ * or hlist_nulls_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_nulls_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs. Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_nulls_add_tail_rcu(struct hlist_nulls_node *n,
+ struct hlist_nulls_head *h)
+{
+ struct hlist_nulls_node *i, *last = NULL;
+
+ /* Note: write side code, so rcu accessors are not needed. */
+ for (i = h->first; !is_a_nulls(i); i = i->next)
+ last = i;
+
+ if (last) {
+ n->next = last->next;
+ n->pprev = &last->next;
+ rcu_assign_pointer(hlist_next_rcu(last), n);
+ } else {
+ hlist_nulls_add_head_rcu(n, h);
+ }
+}
+
+/**
* hlist_nulls_for_each_entry_rcu - iterate over rcu list of given type
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_nulls_node to use as a loop cursor.
diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h
index 6d46f962..06978ce 100644
--- a/include/linux/regulator/ab8500.h
+++ b/include/linux/regulator/ab8500.h
@@ -38,7 +38,6 @@ enum ab8505_regulator_id {
AB8505_LDO_AUX6,
AB8505_LDO_INTCORE,
AB8505_LDO_ADC,
- AB8505_LDO_USB,
AB8505_LDO_AUDIO,
AB8505_LDO_ANAMIC1,
AB8505_LDO_ANAMIC2,
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index 25602af..f3f7605 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -508,7 +508,7 @@ static inline int regulator_get_error_flags(struct regulator *regulator,
static inline int regulator_set_load(struct regulator *regulator, int load_uA)
{
- return REGULATOR_MODE_NORMAL;
+ return 0;
}
static inline int regulator_allow_bypass(struct regulator *regulator,
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index 9326d67..8675ec6 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -7,7 +7,7 @@
struct reset_controller_dev;
/**
- * struct reset_control_ops
+ * struct reset_control_ops - reset controller driver callbacks
*
* @reset: for self-deasserting resets, does all necessary
* things to reset the device
diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h
index 5e85b9f..1e2aac5 100644
--- a/include/linux/sched/cpufreq.h
+++ b/include/linux/sched/cpufreq.h
@@ -17,6 +17,8 @@
#define SCHED_CPUFREQ_CONTINUE (1U << 8)
#ifdef CONFIG_CPU_FREQ
+struct cpufreq_policy;
+
struct update_util_data {
void (*func)(struct update_util_data *data, u64 time, unsigned int flags);
};
@@ -25,6 +27,7 @@ void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data,
void (*func)(struct update_util_data *data, u64 time,
unsigned int flags));
void cpufreq_remove_update_util_hook(int cpu);
+bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy);
static inline unsigned long map_util_freq(unsigned long util,
unsigned long freq, unsigned long cap)
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 5b76a3c7..fd9cfe9 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -76,7 +76,7 @@ extern int sched_updown_migrate_handler(struct ctl_table *table,
size_t *lenp, loff_t *ppos);
#endif
-#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_DEBUG_PREEMPT)
+#if defined(CONFIG_PREEMPTIRQ_EVENTS) || defined(CONFIG_PREEMPT_TRACER)
extern unsigned int sysctl_preemptoff_tracing_threshold_ns;
#endif
#if defined(CONFIG_PREEMPTIRQ_EVENTS) && defined(CONFIG_IRQSOFF_TRACER)
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index 108ede9..44c6f15 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -39,6 +39,8 @@ void __noreturn do_task_dead(void);
extern void proc_caches_init(void);
+extern void fork_init(void);
+
extern void release_task(struct task_struct * p);
#ifdef CONFIG_HAVE_COPY_THREAD_TLS
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 18e2142..5a655ba 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -134,6 +134,10 @@ struct uart_8250_port {
void (*dl_write)(struct uart_8250_port *, int);
struct uart_8250_em485 *em485;
+
+ /* Serial port overrun backoff */
+ struct delayed_work overrun_backoff;
+ u32 overrun_backoff_time_ms;
};
static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 406edae..3460b15 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -173,6 +173,7 @@ struct uart_port {
struct console *cons; /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq; /* sysrq timeout */
+ unsigned int sysrq_ch; /* char for sysrq */
#endif
/* flags must be updated while holding port mutex */
@@ -482,8 +483,42 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
}
return 0;
}
+static inline int
+uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch)
+{
+ if (port->sysrq) {
+ if (ch && time_before(jiffies, port->sysrq)) {
+ port->sysrq_ch = ch;
+ port->sysrq = 0;
+ return 1;
+ }
+ port->sysrq = 0;
+ }
+ return 0;
+}
+static inline void
+uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags)
+{
+ int sysrq_ch;
+
+ sysrq_ch = port->sysrq_ch;
+ port->sysrq_ch = 0;
+
+ spin_unlock_irqrestore(&port->lock, irqflags);
+
+ if (sysrq_ch)
+ handle_sysrq(sysrq_ch);
+}
#else
-#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; })
+static inline int
+uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { return 0; }
+static inline int
+uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch) { return 0; }
+static inline void
+uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags)
+{
+ spin_unlock_irqrestore(&port->lock, irqflags);
+}
#endif
/*
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 28baccb..25407c2 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1251,7 +1251,8 @@ static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6
return skb->hash;
}
-__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb);
+__u32 skb_get_hash_perturb(const struct sk_buff *skb,
+ const siphash_key_t *perturb);
static inline __u32 skb_get_hash_raw(const struct sk_buff *skb)
{
@@ -1380,6 +1381,19 @@ static inline int skb_queue_empty(const struct sk_buff_head *list)
}
/**
+ * skb_queue_empty_lockless - check if a queue is empty
+ * @list: queue head
+ *
+ * Returns true if the queue is empty, false otherwise.
+ * This variant can be used in lockless contexts.
+ */
+static inline bool skb_queue_empty_lockless(const struct sk_buff_head *list)
+{
+ return READ_ONCE(list->next) == (const struct sk_buff *) list;
+}
+
+
+/**
* skb_queue_is_last - check if skb is the last entry in the queue
* @list: queue head
* @skb: buffer
@@ -1655,7 +1669,7 @@ static inline struct sk_buff *skb_peek_next(struct sk_buff *skb,
*/
static inline struct sk_buff *skb_peek_tail(const struct sk_buff_head *list_)
{
- struct sk_buff *skb = list_->prev;
+ struct sk_buff *skb = READ_ONCE(list_->prev);
if (skb == (struct sk_buff *)list_)
skb = NULL;
@@ -1723,9 +1737,13 @@ static inline void __skb_insert(struct sk_buff *newsk,
struct sk_buff *prev, struct sk_buff *next,
struct sk_buff_head *list)
{
- newsk->next = next;
- newsk->prev = prev;
- next->prev = prev->next = newsk;
+ /* See skb_queue_empty_lockless() and skb_peek_tail()
+ * for the opposite READ_ONCE()
+ */
+ WRITE_ONCE(newsk->next, next);
+ WRITE_ONCE(newsk->prev, prev);
+ WRITE_ONCE(next->prev, newsk);
+ WRITE_ONCE(prev->next, newsk);
list->qlen++;
}
@@ -1736,11 +1754,11 @@ static inline void __skb_queue_splice(const struct sk_buff_head *list,
struct sk_buff *first = list->next;
struct sk_buff *last = list->prev;
- first->prev = prev;
- prev->next = first;
+ WRITE_ONCE(first->prev, prev);
+ WRITE_ONCE(prev->next, first);
- last->next = next;
- next->prev = last;
+ WRITE_ONCE(last->next, next);
+ WRITE_ONCE(next->prev, last);
}
/**
@@ -1881,8 +1899,8 @@ static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
next = skb->next;
prev = skb->prev;
skb->next = skb->prev = NULL;
- next->prev = prev;
- prev->next = next;
+ WRITE_ONCE(next->prev, prev);
+ WRITE_ONCE(prev->next, next);
}
/**
diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
index 592653b..ad2e243 100644
--- a/include/linux/sunrpc/sched.h
+++ b/include/linux/sunrpc/sched.h
@@ -188,7 +188,6 @@ struct rpc_timer {
struct rpc_wait_queue {
spinlock_t lock;
struct list_head tasks[RPC_NR_PRIORITY]; /* task queue for each priority level */
- pid_t owner; /* process id of last task serviced */
unsigned char maxpriority; /* maximum priority (0 if queue is not a priority queue) */
unsigned char priority; /* current priority */
unsigned char nr; /* # tasks remaining for cookie */
@@ -204,7 +203,6 @@ struct rpc_wait_queue {
* from a single cookie. The aim is to improve
* performance of NFS operations such as read/write.
*/
-#define RPC_BATCH_COUNT 16
#define RPC_IS_PRIORITY(q) ((q)->maxpriority > 0)
/*
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 7ec05fa..83302aa 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -382,14 +382,8 @@ extern unsigned long vm_total_pages;
extern int node_reclaim_mode;
extern int sysctl_min_unmapped_ratio;
extern int sysctl_min_slab_ratio;
-extern int node_reclaim(struct pglist_data *, gfp_t, unsigned int);
#else
#define node_reclaim_mode 0
-static inline int node_reclaim(struct pglist_data *pgdat, gfp_t mask,
- unsigned int order)
-{
- return 0;
-}
#endif
extern int page_evictable(struct page *page);
diff --git a/include/linux/time.h b/include/linux/time.h
index 27d83fd..5f3e499 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -96,4 +96,17 @@ static inline bool itimerspec64_valid(const struct itimerspec64 *its)
*/
#define time_after32(a, b) ((s32)((u32)(b) - (u32)(a)) < 0)
#define time_before32(b, a) time_after32(a, b)
+
+/**
+ * time_between32 - check if a 32-bit timestamp is within a given time range
+ * @t: the time which may be within [l,h]
+ * @l: the lower bound of the range
+ * @h: the higher bound of the range
+ *
+ * time_before32(t, l, h) returns true if @l <= @t <= @h. All operands are
+ * treated as 32-bit integers.
+ *
+ * Equivalent to !(time_before32(@t, @l) || time_after32(@t, @h)).
+ */
+#define time_between32(t, l, h) ((u32)(h) - (u32)(l) >= (u32)(t) - (u32)(l))
#endif
diff --git a/include/linux/timekeeping32.h b/include/linux/timekeeping32.h
index 8762c2f..479da36b 100644
--- a/include/linux/timekeeping32.h
+++ b/include/linux/timekeeping32.h
@@ -6,8 +6,19 @@
* over time so we can remove the file here.
*/
-extern void do_gettimeofday(struct timeval *tv);
-unsigned long get_seconds(void);
+static inline void do_gettimeofday(struct timeval *tv)
+{
+ struct timespec64 now;
+
+ ktime_get_real_ts64(&now);
+ tv->tv_sec = now.tv_sec;
+ tv->tv_usec = now.tv_nsec/1000;
+}
+
+static inline unsigned long get_seconds(void)
+{
+ return ktime_get_real_seconds();
+}
static inline struct timespec current_kernel_time(void)
{
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 1d352ac..dabb1e5 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -531,6 +531,8 @@ extern int trace_event_raw_init(struct trace_event_call *call);
extern int trace_define_field(struct trace_event_call *call, const char *type,
const char *name, int offset, int size,
int is_signed, int filter_type);
+extern int trace_add_event_call_nolock(struct trace_event_call *call);
+extern int trace_remove_event_call_nolock(struct trace_event_call *call);
extern int trace_add_event_call(struct trace_event_call *call);
extern int trace_remove_event_call(struct trace_event_call *call);
extern int trace_event_get_offsets(struct trace_event_call *call);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 118ca76..761982e 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -370,6 +370,7 @@ struct tty_file_private {
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
#define TTY_HUPPED 18 /* Post driver->hangup() */
#define TTY_HUPPING 19 /* Hangup in progress */
+#define TTY_LDISC_CHANGING 20 /* Change pending - non-block IO */
#define TTY_LDISC_HALTED 22 /* Line discipline is halted */
/* Values for tty->flow_change */
@@ -387,6 +388,12 @@ static inline void tty_set_flow_change(struct tty_struct *tty, int val)
smp_mb();
}
+static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
+{
+ return file->f_flags & O_NONBLOCK ||
+ test_bit(TTY_LDISC_CHANGING, &tty->flags);
+}
+
static inline bool tty_io_error(struct tty_struct *tty)
{
return test_bit(TTY_IO_ERROR, &tty->flags);
diff --git a/include/math-emu/soft-fp.h b/include/math-emu/soft-fp.h
index 3f284bc..5650c16 100644
--- a/include/math-emu/soft-fp.h
+++ b/include/math-emu/soft-fp.h
@@ -138,7 +138,7 @@ do { \
_FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \
} while (0)
-#define _FP_ROUND_ZERO(wc, X) 0
+#define _FP_ROUND_ZERO(wc, X) (void)0
#define _FP_ROUND_PINF(wc, X) \
do { \
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 3093b9c..5b383d0 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -46,7 +46,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
/**
* struct vsp1_du_atomic_config - VSP atomic configuration parameters
* @pixelformat: plane pixel format (V4L2 4CC)
- * @pitch: line pitch in bytes, for all planes
+ * @pitch: line pitch in bytes for the first plane
* @mem: DMA memory address for each plane of the frame buffer
* @src: source rectangle in the frame buffer (integer coordinates)
* @dst: destination rectangle on the display (integer coordinates)
diff --git a/include/net/bonding.h b/include/net/bonding.h
index b46d68a..81166488 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -149,7 +149,6 @@ struct slave {
unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS];
s8 link; /* one of BOND_LINK_XXXX */
s8 link_new_state; /* one of BOND_LINK_XXXX */
- s8 new_link;
u8 backup:1, /* indicates backup slave. Value corresponds with
BOND_STATE_ACTIVE and BOND_STATE_BACKUP */
inactive:1, /* indicates inactive slave */
@@ -539,7 +538,7 @@ static inline void bond_propose_link_state(struct slave *slave, int state)
static inline void bond_commit_link_state(struct slave *slave, bool notify)
{
- if (slave->link == slave->link_new_state)
+ if (slave->link_new_state == BOND_LINK_NOCHANGE)
return;
slave->link = slave->link_new_state;
diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h
index ba61cdd..cf8f792 100644
--- a/include/net/busy_poll.h
+++ b/include/net/busy_poll.h
@@ -134,7 +134,7 @@ static inline void skb_mark_napi_id(struct sk_buff *skb,
static inline void sk_mark_napi_id(struct sock *sk, const struct sk_buff *skb)
{
#ifdef CONFIG_NET_RX_BUSY_POLL
- sk->sk_napi_id = skb->napi_id;
+ WRITE_ONCE(sk->sk_napi_id, skb->napi_id);
#endif
sk_rx_queue_set(sk, skb);
}
@@ -144,8 +144,8 @@ static inline void sk_mark_napi_id_once(struct sock *sk,
const struct sk_buff *skb)
{
#ifdef CONFIG_NET_RX_BUSY_POLL
- if (!sk->sk_napi_id)
- sk->sk_napi_id = skb->napi_id;
+ if (!READ_ONCE(sk->sk_napi_id))
+ WRITE_ONCE(sk->sk_napi_id, skb->napi_id);
#endif
}
diff --git a/include/net/cnss2.h b/include/net/cnss2.h
index ee6cbb7..78143ea 100644
--- a/include/net/cnss2.h
+++ b/include/net/cnss2.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS2_H
#define _NET_CNSS2_H
@@ -158,6 +158,8 @@ enum cnss_recovery_reason {
extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver);
extern void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver);
extern void cnss_device_crashed(struct device *dev);
+extern int cnss_pci_prevent_l1(struct device *dev);
+extern void cnss_pci_allow_l1(struct device *dev);
extern int cnss_pci_link_down(struct device *dev);
extern int cnss_pci_is_device_down(struct device *dev);
extern void cnss_schedule_recovery(struct device *dev,
diff --git a/include/net/dst.h b/include/net/dst.h
index ffc8ee0..35ae45f 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -93,7 +93,7 @@ struct dst_entry {
struct dst_metrics {
u32 metrics[RTAX_MAX];
refcount_t refcnt;
-};
+} __aligned(4); /* Low pointer bits contain DST_METRICS_FLAGS */
extern const struct dst_metrics dst_default_metrics;
u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
@@ -527,7 +527,16 @@ static inline void skb_dst_update_pmtu(struct sk_buff *skb, u32 mtu)
struct dst_entry *dst = skb_dst(skb);
if (dst && dst->ops->update_pmtu)
- dst->ops->update_pmtu(dst, NULL, skb, mtu);
+ dst->ops->update_pmtu(dst, NULL, skb, mtu, true);
+}
+
+/* update dst pmtu but not do neighbor confirm */
+static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ if (dst && dst->ops->update_pmtu)
+ dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}
static inline void skb_tunnel_check_pmtu(struct sk_buff *skb,
@@ -537,7 +546,7 @@ static inline void skb_tunnel_check_pmtu(struct sk_buff *skb,
u32 encap_mtu = dst_mtu(encap_dst);
if (skb->len > encap_mtu - headroom)
- skb_dst_update_pmtu(skb, encap_mtu - headroom);
+ skb_dst_update_pmtu_no_confirm(skb, encap_mtu - headroom);
}
#endif /* _NET_DST_H */
diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h
index 5ec645f..443863c 100644
--- a/include/net/dst_ops.h
+++ b/include/net/dst_ops.h
@@ -27,7 +27,8 @@ struct dst_ops {
struct dst_entry * (*negative_advice)(struct dst_entry *);
void (*link_failure)(struct sk_buff *);
void (*update_pmtu)(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu);
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh);
void (*redirect)(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb);
int (*local_out)(struct net *net, struct sock *sk, struct sk_buff *skb);
diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
index 6a4586d..4618cbb 100644
--- a/include/net/flow_dissector.h
+++ b/include/net/flow_dissector.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <linux/in6.h>
+#include <linux/siphash.h>
#include <uapi/linux/if_ether.h>
/**
@@ -252,7 +253,7 @@ struct flow_keys_basic {
struct flow_keys {
struct flow_dissector_key_control control;
#define FLOW_KEYS_HASH_START_FIELD basic
- struct flow_dissector_key_basic basic;
+ struct flow_dissector_key_basic basic __aligned(SIPHASH_ALIGNMENT);
struct flow_dissector_key_tags tags;
struct flow_dissector_key_vlan vlan;
struct flow_dissector_key_vlan cvlan;
diff --git a/include/net/fq.h b/include/net/fq.h
index ac944a6..c51be50e1 100644
--- a/include/net/fq.h
+++ b/include/net/fq.h
@@ -70,7 +70,7 @@ struct fq {
struct list_head backlogs;
spinlock_t lock;
u32 flows_cnt;
- u32 perturbation;
+ siphash_key_t perturbation;
u32 limit;
u32 memory_limit;
u32 memory_usage;
diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h
index be7c0fa..4b3e18ff 100644
--- a/include/net/fq_impl.h
+++ b/include/net/fq_impl.h
@@ -118,7 +118,7 @@ static struct fq_flow *fq_flow_classify(struct fq *fq,
lockdep_assert_held(&fq->lock);
- hash = skb_get_hash_perturb(skb, fq->perturbation);
+ hash = skb_get_hash_perturb(skb, &fq->perturbation);
idx = reciprocal_scale(hash, fq->flows_cnt);
flow = &fq->flows[idx];
@@ -307,12 +307,12 @@ static int fq_init(struct fq *fq, int flows_cnt)
INIT_LIST_HEAD(&fq->backlogs);
spin_lock_init(&fq->lock);
fq->flows_cnt = max_t(u32, flows_cnt, 1);
- fq->perturbation = prandom_u32();
+ get_random_bytes(&fq->perturbation, sizeof(fq->perturbation));
fq->quantum = 300;
fq->limit = 8192;
fq->memory_limit = 16 << 20; /* 16 MBytes */
- fq->flows = kcalloc(fq->flows_cnt, sizeof(fq->flows[0]), GFP_KERNEL);
+ fq->flows = kvcalloc(fq->flows_cnt, sizeof(fq->flows[0]), GFP_KERNEL);
if (!fq->flows)
return -ENOMEM;
@@ -330,7 +330,7 @@ static void fq_reset(struct fq *fq,
for (i = 0; i < fq->flows_cnt; i++)
fq_flow_reset(fq, &fq->flows[i], free_func);
- kfree(fq->flows);
+ kvfree(fq->flows);
fq->flows = NULL;
}
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 9141e95..b875dce 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -106,13 +106,19 @@ struct inet_bind_hashbucket {
struct hlist_head chain;
};
-/*
- * Sockets can be hashed in established or listening table
+/* Sockets can be hashed in established or listening table.
+ * We must use different 'nulls' end-of-chain value for all hash buckets :
+ * A socket might transition from ESTABLISH to LISTEN state without
+ * RCU grace period. A lookup in ehash table needs to handle this case.
*/
+#define LISTENING_NULLS_BASE (1U << 29)
struct inet_listen_hashbucket {
spinlock_t lock;
unsigned int count;
- struct hlist_head head;
+ union {
+ struct hlist_head head;
+ struct hlist_nulls_head nulls_head;
+ };
};
/* This is for listening sockets, thus all sockets which possess wildcards. */
diff --git a/include/net/ip.h b/include/net/ip.h
index 1a1bbce..d584d02 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -695,4 +695,9 @@ int ip_misc_proc_init(void);
int rtm_getroute_parse_ip_proto(struct nlattr *attr, u8 *ip_proto, u8 family,
struct netlink_ext_ack *extack);
+static inline bool inetdev_valid_mtu(unsigned int mtu)
+{
+ return likely(mtu >= IPV4_MIN_MTU);
+}
+
#endif /* _IP_H */
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 0e3c0d8..af0ede9 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -885,6 +885,7 @@ struct netns_ipvs {
struct delayed_work defense_work; /* Work handler */
int drop_rate;
int drop_counter;
+ int old_secure_tcp;
atomic_t dropentry;
/* locks in ctl.c */
spinlock_t dropentry_lock; /* drop entry handling */
diff --git a/include/net/llc.h b/include/net/llc.h
index 890a873..df282d9 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -66,6 +66,7 @@ struct llc_sap {
int sk_count;
struct hlist_nulls_head sk_laddr_hash[LLC_SK_LADDR_HASH_ENTRIES];
struct hlist_head sk_dev_hash[LLC_SK_DEV_HASH_ENTRIES];
+ struct rcu_head rcu;
};
static inline
diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h
index df528a6..ea985aa 100644
--- a/include/net/llc_conn.h
+++ b/include/net/llc_conn.h
@@ -104,7 +104,7 @@ void llc_sk_reset(struct sock *sk);
/* Access to a connection */
int llc_conn_state_process(struct sock *sk, struct sk_buff *skb);
-int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
+void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb);
void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit);
void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit);
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index beeeed1..5ce0359 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -430,8 +430,8 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
unsigned long now = jiffies;
- if (neigh->used != now)
- neigh->used = now;
+ if (READ_ONCE(neigh->used) != now)
+ WRITE_ONCE(neigh->used, now);
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
return __neigh_event_send(neigh, skb);
return 0;
@@ -459,7 +459,7 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb
do {
seq = read_seqbegin(&hh->hh_lock);
- hh_len = hh->hh_len;
+ hh_len = READ_ONCE(hh->hh_len);
if (likely(hh_len <= HH_DATA_MOD)) {
hh_alen = HH_DATA_MOD;
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 3f7b166..5007eab 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -322,7 +322,7 @@ static inline struct net *read_pnet(const possible_net_t *pnet)
#define __net_initconst __initconst
#endif
-int peernet2id_alloc(struct net *net, struct net *peer);
+int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp);
int peernet2id(struct net *net, struct net *peer);
bool peernet_has_id(struct net *net, struct net *peer);
struct net *get_net_ns_by_id(struct net *net, int id);
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 7685cbd..024636c 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -793,7 +793,8 @@ struct nft_expr_ops {
*/
struct nft_expr {
const struct nft_expr_ops *ops;
- unsigned char data[];
+ unsigned char data[]
+ __attribute__((aligned(__alignof__(u64))));
};
static inline void *nft_expr_priv(const struct nft_expr *expr)
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index c44da48..c9cd508 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -421,6 +421,11 @@ static inline struct Qdisc *qdisc_root(const struct Qdisc *qdisc)
return q;
}
+static inline struct Qdisc *qdisc_root_bh(const struct Qdisc *qdisc)
+{
+ return rcu_dereference_bh(qdisc->dev_queue->qdisc);
+}
+
static inline struct Qdisc *qdisc_root_sleeping(const struct Qdisc *qdisc)
{
return qdisc->dev_queue->qdisc_sleeping;
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ab9242e..2abbc15 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -620,4 +620,9 @@ static inline bool sctp_transport_pmtu_check(struct sctp_transport *t)
return false;
}
+static inline __u32 sctp_min_frag_point(struct sctp_sock *sp, __u16 datasize)
+{
+ return sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT, datasize);
+}
+
#endif /* __net_sctp_h__ */
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index feada35..19f8d58 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1247,6 +1247,9 @@ struct sctp_ep_common {
/* What socket does this endpoint belong to? */
struct sock *sk;
+ /* Cache netns and it won't change once set */
+ struct net *net;
+
/* This is where we receive inbound chunks. */
struct sctp_inq inqueue;
diff --git a/include/net/sock.h b/include/net/sock.h
index 34dfb2d..94ffacc 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -721,6 +721,11 @@ static inline void __sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_h
hlist_nulls_add_head_rcu(&sk->sk_nulls_node, list);
}
+static inline void __sk_nulls_add_node_tail_rcu(struct sock *sk, struct hlist_nulls_head *list)
+{
+ hlist_nulls_add_tail_rcu(&sk->sk_nulls_node, list);
+}
+
static inline void sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_head *list)
{
sock_hold(sk);
@@ -945,8 +950,8 @@ static inline void sk_incoming_cpu_update(struct sock *sk)
{
int cpu = raw_smp_processor_id();
- if (unlikely(sk->sk_incoming_cpu != cpu))
- sk->sk_incoming_cpu = cpu;
+ if (unlikely(READ_ONCE(sk->sk_incoming_cpu) != cpu))
+ WRITE_ONCE(sk->sk_incoming_cpu, cpu);
}
static inline void sock_rps_record_flow_hash(__u32 hash)
@@ -1272,7 +1277,7 @@ static inline void sk_sockets_allocated_inc(struct sock *sk)
percpu_counter_inc(sk->sk_prot->sockets_allocated);
}
-static inline int
+static inline u64
sk_sockets_allocated_read_positive(struct sock *sk)
{
return percpu_counter_read_positive(sk->sk_prot->sockets_allocated);
@@ -2216,12 +2221,17 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp,
* sk_page_frag - return an appropriate page_frag
* @sk: socket
*
- * If socket allocation mode allows current thread to sleep, it means its
- * safe to use the per task page_frag instead of the per socket one.
+ * Use the per task page_frag instead of the per socket one for
+ * optimization when we know that we're in the normal context and owns
+ * everything that's associated with %current.
+ *
+ * gfpflags_allow_blocking() isn't enough here as direct reclaim may nest
+ * inside other socket operations and end up recursing into sk_page_frag()
+ * while it's already in use.
*/
static inline struct page_frag *sk_page_frag(struct sock *sk)
{
- if (gfpflags_allow_blocking(sk->sk_allocation))
+ if (gfpflags_normal_context(sk->sk_allocation))
return ¤t->task_frag;
return &sk->sk_frag;
@@ -2313,7 +2323,7 @@ static inline ktime_t sock_read_timestamp(struct sock *sk)
return kt;
#else
- return sk->sk_stamp;
+ return READ_ONCE(sk->sk_stamp);
#endif
}
@@ -2324,7 +2334,7 @@ static inline void sock_write_timestamp(struct sock *sk, ktime_t kt)
sk->sk_stamp = kt;
write_sequnlock(&sk->sk_stamp_seq);
#else
- sk->sk_stamp = kt;
+ WRITE_ONCE(sk->sk_stamp, kt);
#endif
}
diff --git a/include/net/tcp.h b/include/net/tcp.h
index c911ecb..9275834 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -485,15 +485,16 @@ static inline void tcp_synq_overflow(const struct sock *sk)
reuse = rcu_dereference(sk->sk_reuseport_cb);
if (likely(reuse)) {
last_overflow = READ_ONCE(reuse->synq_overflow_ts);
- if (time_after32(now, last_overflow + HZ))
+ if (!time_between32(now, last_overflow,
+ last_overflow + HZ))
WRITE_ONCE(reuse->synq_overflow_ts, now);
return;
}
}
- last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp;
- if (time_after32(now, last_overflow + HZ))
- tcp_sk(sk)->rx_opt.ts_recent_stamp = now;
+ last_overflow = READ_ONCE(tcp_sk(sk)->rx_opt.ts_recent_stamp);
+ if (!time_between32(now, last_overflow, last_overflow + HZ))
+ WRITE_ONCE(tcp_sk(sk)->rx_opt.ts_recent_stamp, now);
}
/* syncookies: no recent synqueue overflow on this listening socket? */
@@ -508,13 +509,23 @@ static inline bool tcp_synq_no_recent_overflow(const struct sock *sk)
reuse = rcu_dereference(sk->sk_reuseport_cb);
if (likely(reuse)) {
last_overflow = READ_ONCE(reuse->synq_overflow_ts);
- return time_after32(now, last_overflow +
- TCP_SYNCOOKIE_VALID);
+ return !time_between32(now, last_overflow - HZ,
+ last_overflow +
+ TCP_SYNCOOKIE_VALID);
}
}
- last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp;
- return time_after32(now, last_overflow + TCP_SYNCOOKIE_VALID);
+ last_overflow = READ_ONCE(tcp_sk(sk)->rx_opt.ts_recent_stamp);
+
+ /* If last_overflow <= jiffies <= last_overflow + TCP_SYNCOOKIE_VALID,
+ * then we're under synflood. However, we have to use
+ * 'last_overflow - HZ' as lower bound. That's because a concurrent
+ * tcp_synq_overflow() could update .ts_recent_stamp after we read
+ * jiffies but before we store .ts_recent_stamp into last_overflow,
+ * which could lead to rejecting a valid syncookie.
+ */
+ return !time_between32(now, last_overflow - HZ,
+ last_overflow + TCP_SYNCOOKIE_VALID);
}
static inline u32 tcp_cookie_time(void)
@@ -1353,7 +1364,7 @@ static inline int tcp_win_from_space(const struct sock *sk, int space)
/* Note: caller must be prepared to deal with negative returns */
static inline int tcp_space(const struct sock *sk)
{
- return tcp_win_from_space(sk, sk->sk_rcvbuf -
+ return tcp_win_from_space(sk, sk->sk_rcvbuf - sk->sk_backlog.len -
atomic_read(&sk->sk_rmem_alloc));
}
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 4ddd2b1..fb9b19a 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1062,7 +1062,6 @@ struct xfrm_if_parms {
struct xfrm_if {
struct xfrm_if __rcu *next; /* next interface in list */
struct net_device *dev; /* virtual device associated with interface */
- struct net_device *phydev; /* physical device */
struct net *net; /* netns for packet i/o */
struct xfrm_if_parms p; /* interface parms */
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index b7d63c3..54e4d1f 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -310,7 +310,7 @@ struct ib_tm_caps {
struct ib_cq_init_attr {
unsigned int cqe;
- int comp_vector;
+ u32 comp_vector;
u32 flags;
};
@@ -1147,7 +1147,7 @@ struct ib_qp_init_attr {
struct ib_qp_cap cap;
enum ib_sig_type sq_sig_type;
enum ib_qp_type qp_type;
- enum ib_qp_create_flags create_flags;
+ u32 create_flags;
/*
* Only needed for special QP types, or when using the RW API.
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
index df156f1..f0a01a5 100644
--- a/include/scsi/iscsi_proto.h
+++ b/include/scsi/iscsi_proto.h
@@ -638,6 +638,7 @@ struct iscsi_reject {
#define ISCSI_REASON_BOOKMARK_INVALID 9
#define ISCSI_REASON_BOOKMARK_NO_RESOURCES 10
#define ISCSI_REASON_NEGOTIATION_RESET 11
+#define ISCSI_REASON_WAITING_FOR_LOGOUT 12
/* Max. number of Key=Value pairs in a text message */
#define MAX_KEY_VALUE_PAIRS 8192
diff --git a/include/soc/qcom/icnss2.h b/include/soc/qcom/icnss2.h
new file mode 100644
index 0000000..fa65926
--- /dev/null
+++ b/include/soc/qcom/icnss2.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ */
+#ifndef _ICNSS_WLAN_H_
+#define _ICNSS_WLAN_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#define ICNSS_MAX_IRQ_REGISTRATIONS 12
+#define IWCN_MAX_IRQ_REGISTRATIONS 32
+#define ICNSS_MAX_TIMESTAMP_LEN 32
+
+#ifndef ICNSS_API_WITH_DEV
+#define ICNSS_API_WITH_DEV
+#endif
+
+enum icnss_uevent {
+ ICNSS_UEVENT_FW_CRASHED,
+ ICNSS_UEVENT_FW_DOWN,
+};
+
+struct icnss_uevent_fw_down_data {
+ bool crashed;
+};
+
+struct icnss_uevent_data {
+ enum icnss_uevent uevent;
+ void *data;
+};
+
+struct icnss_driver_ops {
+ char *name;
+ unsigned long drv_state;
+ struct device_driver driver;
+ int (*probe)(struct device *dev);
+ void (*remove)(struct device *dev);
+ void (*shutdown)(struct device *dev);
+ int (*reinit)(struct device *dev);
+ void (*crash_shutdown)(void *pdev);
+ int (*pm_suspend)(struct device *dev);
+ int (*pm_resume)(struct device *dev);
+ int (*suspend_noirq)(struct device *dev);
+ int (*resume_noirq)(struct device *dev);
+ int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent);
+ int (*idle_shutdown)(struct device *dev);
+ int (*idle_restart)(struct device *dev);
+};
+
+
+struct ce_tgt_pipe_cfg {
+ u32 pipe_num;
+ u32 pipe_dir;
+ u32 nentries;
+ u32 nbytes_max;
+ u32 flags;
+ u32 reserved;
+};
+
+struct ce_svc_pipe_cfg {
+ u32 service_id;
+ u32 pipe_dir;
+ u32 pipe_num;
+};
+
+struct icnss_shadow_reg_cfg {
+ u16 ce_id;
+ u16 reg_offset;
+};
+
+/* CE configuration to target */
+struct icnss_wlan_enable_cfg {
+ u32 num_ce_tgt_cfg;
+ struct ce_tgt_pipe_cfg *ce_tgt_cfg;
+ u32 num_ce_svc_pipe_cfg;
+ struct ce_svc_pipe_cfg *ce_svc_cfg;
+ u32 num_shadow_reg_cfg;
+ struct icnss_shadow_reg_cfg *shadow_reg_cfg;
+};
+
+/* driver modes */
+enum icnss_driver_mode {
+ ICNSS_MISSION,
+ ICNSS_FTM,
+ ICNSS_EPPING,
+ ICNSS_WALTEST,
+ ICNSS_OFF,
+ ICNSS_CCPM,
+ ICNSS_QVIT,
+ ICNSS_CALIBRATION,
+};
+
+struct icnss_soc_info {
+ void __iomem *v_addr;
+ phys_addr_t p_addr;
+ uint32_t chip_id;
+ uint32_t chip_family;
+ uint32_t board_id;
+ uint32_t soc_id;
+ uint32_t fw_version;
+ char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1];
+};
+
+#define icnss_register_driver(ops) \
+ __icnss_register_driver(ops, THIS_MODULE, KBUILD_MODNAME)
+extern int __icnss_register_driver(struct icnss_driver_ops *ops,
+ struct module *owner, const char *mod_name);
+
+extern int icnss_unregister_driver(struct icnss_driver_ops *ops);
+
+extern int icnss_wlan_enable(struct device *dev,
+ struct icnss_wlan_enable_cfg *config,
+ enum icnss_driver_mode mode,
+ const char *host_version);
+extern int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode);
+extern void icnss_enable_irq(struct device *dev, unsigned int ce_id);
+extern void icnss_disable_irq(struct device *dev, unsigned int ce_id);
+extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info);
+extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx);
+extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
+ irqreturn_t (*handler)(int, void *),
+ unsigned long flags, const char *name, void *ctx);
+extern int icnss_get_ce_id(struct device *dev, int irq);
+extern int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode);
+extern int icnss_athdiag_read(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *output);
+extern int icnss_athdiag_write(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *input);
+extern int icnss_get_irq(struct device *dev, int ce_id);
+extern int icnss_power_on(struct device *dev);
+extern int icnss_power_off(struct device *dev);
+extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev);
+extern struct iommu_domain *icnss_smmu_get_domain(struct device *dev);
+extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
+ uint32_t *iova_addr, size_t size);
+extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
+extern bool icnss_is_qmi_disable(struct device *dev);
+extern bool icnss_is_fw_ready(void);
+extern bool icnss_is_fw_down(void);
+extern bool icnss_is_rejuvenate(void);
+extern int icnss_trigger_recovery(struct device *dev);
+extern void icnss_block_shutdown(bool status);
+extern bool icnss_is_pdr(void);
+extern int icnss_idle_restart(struct device *dev);
+extern int icnss_idle_shutdown(struct device *dev);
+extern int icnss_get_user_msi_assignment(struct device *dev, char *user_name,
+ int *num_vectors, u32 *user_base_data,
+ u32 *base_vector);
+extern int icnss_get_msi_irq(struct device *dev, unsigned int vector);
+extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low,
+ u32 *msi_addr_high);
+#endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
index e6bdf0e..5a7e66d 100644
--- a/include/soc/qcom/minidump.h
+++ b/include/soc/qcom/minidump.h
@@ -1,12 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __MINIDUMP_H
#define __MINIDUMP_H
#define MAX_NAME_LENGTH 12
+
+/* default value for clients not using any id */
+#define MINIDUMP_DEFAULT_ID UINT_MAX
+
/* md_region - Minidump table entry
* @name: Entry name, Minidump will dump binary with this name.
* @id: Entry ID, used only for SDI dumps.
diff --git a/include/soc/qcom/tgu.h b/include/soc/qcom/tgu.h
new file mode 100644
index 0000000..200774d
--- /dev/null
+++ b/include/soc/qcom/tgu.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __QCOM_TGU_H__
+#define __QCOM_TGU_H__
+
+struct tgu_test_notifier {
+ void (*cb)(void);
+};
+
+extern int register_tgu_notifier(struct tgu_test_notifier *tgu_test);
+extern int unregister_tgu_notifier(struct tgu_test_notifier *tgu_test);
+#endif /* __QCOM_MPM_H__ */
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
index c32bf91..445aa66 100644
--- a/include/soc/tegra/pmc.h
+++ b/include/soc/tegra/pmc.h
@@ -134,6 +134,7 @@ enum tegra_io_pad {
TEGRA_IO_PAD_USB2,
TEGRA_IO_PAD_USB3,
TEGRA_IO_PAD_USB_BIAS,
+ TEGRA_IO_PAD_AO_HV,
};
/* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */
diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h
index eeceafa..bb70f46 100644
--- a/include/trace/events/irq.h
+++ b/include/trace/events/irq.h
@@ -160,6 +160,51 @@ DEFINE_EVENT(softirq, softirq_raise,
TP_ARGS(vec_nr)
);
+DECLARE_EVENT_CLASS(tasklet,
+
+ TP_PROTO(void *func),
+
+ TP_ARGS(func),
+
+ TP_STRUCT__entry(
+ __field( void *, func)
+ ),
+
+ TP_fast_assign(
+ __entry->func = func;
+ ),
+
+ TP_printk("function=%ps", __entry->func)
+);
+
+DEFINE_EVENT(tasklet, tasklet_entry,
+
+ TP_PROTO(void *func),
+
+ TP_ARGS(func)
+);
+
+DEFINE_EVENT(tasklet, tasklet_exit,
+
+ TP_PROTO(void *func),
+
+ TP_ARGS(func)
+);
+
+DEFINE_EVENT(tasklet, tasklet_hi_entry,
+
+ TP_PROTO(void *func),
+
+ TP_ARGS(func)
+);
+
+DEFINE_EVENT(tasklet, tasklet_hi_exit,
+
+ TP_PROTO(void *func),
+
+ TP_ARGS(func)
+);
+
#endif /* _TRACE_IRQ_H */
/* This part must be outside protection */
diff --git a/include/trace/events/preemptirq.h b/include/trace/events/preemptirq.h
index 7e3bcd4d..3369655 100644
--- a/include/trace/events/preemptirq.h
+++ b/include/trace/events/preemptirq.h
@@ -90,6 +90,38 @@ TRACE_EVENT(irqs_disable,
__entry->caddr2, __entry->caddr3)
);
+TRACE_EVENT(sched_preempt_disable,
+
+ TP_PROTO(u64 delta, bool irqs_disabled,
+ unsigned long caddr0, unsigned long caddr1,
+ unsigned long caddr2, unsigned long caddr3),
+
+ TP_ARGS(delta, irqs_disabled, caddr0, caddr1, caddr2, caddr3),
+
+ TP_STRUCT__entry(
+ __field(u64, delta)
+ __field(bool, irqs_disabled)
+ __field(void*, caddr0)
+ __field(void*, caddr1)
+ __field(void*, caddr2)
+ __field(void*, caddr3)
+ ),
+
+ TP_fast_assign(
+ __entry->delta = delta;
+ __entry->irqs_disabled = irqs_disabled;
+ __entry->caddr0 = (void *)caddr0;
+ __entry->caddr1 = (void *)caddr1;
+ __entry->caddr2 = (void *)caddr2;
+ __entry->caddr3 = (void *)caddr3;
+ ),
+
+ TP_printk("delta=%llu(ns) irqs_d=%d Callers:(%ps<-%ps<-%ps<-%ps)",
+ __entry->delta, __entry->irqs_disabled,
+ __entry->caddr0, __entry->caddr1,
+ __entry->caddr2, __entry->caddr3)
+);
+
#endif /* _TRACE_PREEMPTIRQ_H */
#include <trace/define_trace.h>
diff --git a/include/trace/events/rpcrdma.h b/include/trace/events/rpcrdma.h
index 53df203..4c91cad 100644
--- a/include/trace/events/rpcrdma.h
+++ b/include/trace/events/rpcrdma.h
@@ -917,6 +917,34 @@ TRACE_EVENT(xprtrdma_cb_setup,
DEFINE_CB_EVENT(xprtrdma_cb_call);
DEFINE_CB_EVENT(xprtrdma_cb_reply);
+TRACE_EVENT(xprtrdma_leaked_rep,
+ TP_PROTO(
+ const struct rpc_rqst *rqst,
+ const struct rpcrdma_rep *rep
+ ),
+
+ TP_ARGS(rqst, rep),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, task_id)
+ __field(unsigned int, client_id)
+ __field(u32, xid)
+ __field(const void *, rep)
+ ),
+
+ TP_fast_assign(
+ __entry->task_id = rqst->rq_task->tk_pid;
+ __entry->client_id = rqst->rq_task->tk_client->cl_clid;
+ __entry->xid = be32_to_cpu(rqst->rq_xid);
+ __entry->rep = rep;
+ ),
+
+ TP_printk("task:%u@%u xid=0x%08x rep=%p",
+ __entry->task_id, __entry->client_id, __entry->xid,
+ __entry->rep
+ )
+);
+
/**
** Server-side RPC/RDMA events
**/
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 0fe169c..a08916e 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -527,10 +527,10 @@ TRACE_EVENT(rxrpc_local,
);
TRACE_EVENT(rxrpc_peer,
- TP_PROTO(struct rxrpc_peer *peer, enum rxrpc_peer_trace op,
+ TP_PROTO(unsigned int peer_debug_id, enum rxrpc_peer_trace op,
int usage, const void *where),
- TP_ARGS(peer, op, usage, where),
+ TP_ARGS(peer_debug_id, op, usage, where),
TP_STRUCT__entry(
__field(unsigned int, peer )
@@ -540,7 +540,7 @@ TRACE_EVENT(rxrpc_peer,
),
TP_fast_assign(
- __entry->peer = peer->debug_id;
+ __entry->peer = peer_debug_id;
__entry->op = op;
__entry->usage = usage;
__entry->where = where;
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index dabbb87..d19200e 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -218,9 +218,14 @@ TRACE_EVENT(sched_switch,
(__entry->prev_state & (TASK_REPORT_MAX - 1)) ?
__print_flags(__entry->prev_state & (TASK_REPORT_MAX - 1), "|",
- { 0x01, "S" }, { 0x02, "D" }, { 0x04, "T" },
- { 0x08, "t" }, { 0x10, "X" }, { 0x20, "Z" },
- { 0x40, "P" }, { 0x80, "I" }) :
+ { TASK_INTERRUPTIBLE, "S" },
+ { TASK_UNINTERRUPTIBLE, "D" },
+ { __TASK_STOPPED, "T" },
+ { __TASK_TRACED, "t" },
+ { EXIT_DEAD, "X" },
+ { EXIT_ZOMBIE, "Z" },
+ { TASK_PARKED, "P" },
+ { TASK_DEAD, "I" }) :
"R",
__entry->prev_state & TASK_REPORT_MAX ? "+" : "",
@@ -1235,6 +1240,7 @@ TRACE_EVENT(sched_task_util,
__field(bool, rtg_skip_min)
__field(int, start_cpu)
__field(u32, unfilter)
+ __field(unsigned long, cpus_allowed)
),
TP_fast_assign(
@@ -1258,16 +1264,17 @@ TRACE_EVENT(sched_task_util,
#else
__entry->unfilter = 0;
#endif
+ __entry->cpus_allowed = cpumask_bits(&p->cpus_allowed)[0];
),
- TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d candidates=%#lx best_energy_cpu=%d sync=%d need_idle=%d fastpath=%d placement_boost=%d latency=%llu stune_boosted=%d is_rtg=%d rtg_skip_min=%d start_cpu=%d unfilter=%u",
+ TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d candidates=%#lx best_energy_cpu=%d sync=%d need_idle=%d fastpath=%d placement_boost=%d latency=%llu stune_boosted=%d is_rtg=%d rtg_skip_min=%d start_cpu=%d unfilter=%u affine=%#lx",
__entry->pid, __entry->comm, __entry->util, __entry->prev_cpu,
__entry->candidates, __entry->best_energy_cpu, __entry->sync,
__entry->need_idle, __entry->fastpath, __entry->placement_boost,
__entry->latency, __entry->stune_boosted,
__entry->is_rtg, __entry->rtg_skip_min, __entry->start_cpu,
- __entry->unfilter)
-)
+ __entry->unfilter, __entry->cpus_allowed)
+);
/*
* Tracepoint for find_best_target
@@ -1649,38 +1656,6 @@ TRACE_EVENT(sched_isolate,
__entry->time, __entry->isolate)
);
-TRACE_EVENT(sched_preempt_disable,
-
- TP_PROTO(u64 delta, bool irqs_disabled,
- unsigned long caddr0, unsigned long caddr1,
- unsigned long caddr2, unsigned long caddr3),
-
- TP_ARGS(delta, irqs_disabled, caddr0, caddr1, caddr2, caddr3),
-
- TP_STRUCT__entry(
- __field(u64, delta)
- __field(bool, irqs_disabled)
- __field(void*, caddr0)
- __field(void*, caddr1)
- __field(void*, caddr2)
- __field(void*, caddr3)
- ),
-
- TP_fast_assign(
- __entry->delta = delta;
- __entry->irqs_disabled = irqs_disabled;
- __entry->caddr0 = (void *)caddr0;
- __entry->caddr1 = (void *)caddr1;
- __entry->caddr2 = (void *)caddr2;
- __entry->caddr3 = (void *)caddr3;
- ),
-
- TP_printk("delta=%llu(ns) irqs_d=%d Callers:(%ps<-%ps<-%ps<-%ps)",
- __entry->delta, __entry->irqs_disabled,
- __entry->caddr0, __entry->caddr1,
- __entry->caddr2, __entry->caddr3)
-);
-
#include "walt.h"
#endif /* CONFIG_SMP */
#endif /* _TRACE_SCHED_H */
diff --git a/include/trace/events/tgu.h b/include/trace/events/tgu.h
new file mode 100644
index 0000000..0d965b8
--- /dev/null
+++ b/include/trace/events/tgu.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tgu
+
+#if !defined(_TRACE_TGU_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TGU_H_
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(tgu_interrupt,
+
+ TP_PROTO(uint32_t irqs),
+
+ TP_ARGS(irqs),
+
+ TP_STRUCT__entry(
+ __field(uint32_t, irqs)
+ ),
+
+ TP_fast_assign(
+ __entry->irqs = irqs;
+ ),
+
+ TP_printk("irq:%u ", __entry->irqs)
+);
+
+#endif
+#define TRACE_INCLUDE_FILE tgu
+#include <trace/define_trace.h>
diff --git a/include/trace/events/wbt.h b/include/trace/events/wbt.h
index b048694..37342a1 100644
--- a/include/trace/events/wbt.h
+++ b/include/trace/events/wbt.h
@@ -33,7 +33,8 @@ TRACE_EVENT(wbt_stat,
),
TP_fast_assign(
- strncpy(__entry->name, dev_name(bdi->dev), 32);
+ strlcpy(__entry->name, dev_name(bdi->dev),
+ ARRAY_SIZE(__entry->name));
__entry->rmean = stat[0].mean;
__entry->rmin = stat[0].min;
__entry->rmax = stat[0].max;
@@ -67,7 +68,8 @@ TRACE_EVENT(wbt_lat,
),
TP_fast_assign(
- strncpy(__entry->name, dev_name(bdi->dev), 32);
+ strlcpy(__entry->name, dev_name(bdi->dev),
+ ARRAY_SIZE(__entry->name));
__entry->lat = div_u64(lat, 1000);
),
@@ -103,7 +105,8 @@ TRACE_EVENT(wbt_step,
),
TP_fast_assign(
- strncpy(__entry->name, dev_name(bdi->dev), 32);
+ strlcpy(__entry->name, dev_name(bdi->dev),
+ ARRAY_SIZE(__entry->name));
__entry->msg = msg;
__entry->step = step;
__entry->window = div_u64(window, 1000);
@@ -138,7 +141,8 @@ TRACE_EVENT(wbt_timer,
),
TP_fast_assign(
- strncpy(__entry->name, dev_name(bdi->dev), 32);
+ strlcpy(__entry->name, dev_name(bdi->dev),
+ ARRAY_SIZE(__entry->name));
__entry->status = status;
__entry->step = step;
__entry->inflight = inflight;
diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h
index 9101697..1247158 100644
--- a/include/uapi/drm/sde_drm.h
+++ b/include/uapi/drm/sde_drm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 2020, The Linux Foundation. All rights reserved.
*/
#ifndef _SDE_DRM_H_
@@ -226,6 +226,7 @@ struct sde_drm_de_v1 {
#define SDE_DRM_QSEED3LITE
#define SDE_DRM_QSEED4
+#define SDE_DRM_INLINE_PREDOWNSCALE
/**
* struct sde_drm_scaler_v2 - version 2 of struct sde_drm_scaler
@@ -259,6 +260,10 @@ struct sde_drm_de_v1 {
* @unsharp_mask_blend: Unsharp Blend Filter Ratio
* @de_blend: Ratio of two unsharp mask filters
* @flags: Scaler configuration flags
+ * @pre_downscale_x_0 Pre-downscale ratio, x-direction, plane 0(Y/RGB)
+ * @pre_downscale_x_1 Pre-downscale ratio, x-direction, plane 1(UV)
+ * @pre_downscale_y_0 Pre-downscale ratio, y-direction, plane 0(Y/RGB)
+ * @pre_downscale_y_1 Pre-downscale ratio, y-direction, plane 1(UV)
*/
struct sde_drm_scaler_v2 {
/*
@@ -316,6 +321,14 @@ struct sde_drm_scaler_v2 {
uint32_t unsharp_mask_blend;
uint32_t de_blend;
uint32_t flags;
+
+ /*
+ * Inline pre-downscale settings
+ */
+ uint32_t pre_downscale_x_0;
+ uint32_t pre_downscale_x_1;
+ uint32_t pre_downscale_y_0;
+ uint32_t pre_downscale_y_1;
};
/* Number of dest scalers supported */
diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h
index 8997d50..4511b85 100644
--- a/include/uapi/linux/cec-funcs.h
+++ b/include/uapi/linux/cec-funcs.h
@@ -923,7 +923,8 @@ static inline void cec_msg_give_deck_status(struct cec_msg *msg,
msg->len = 3;
msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS;
msg->msg[2] = status_req;
- msg->reply = reply ? CEC_MSG_DECK_STATUS : 0;
+ msg->reply = (reply && status_req != CEC_OP_STATUS_REQ_OFF) ?
+ CEC_MSG_DECK_STATUS : 0;
}
static inline void cec_ops_give_deck_status(const struct cec_msg *msg,
@@ -1027,7 +1028,8 @@ static inline void cec_msg_give_tuner_device_status(struct cec_msg *msg,
msg->len = 3;
msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS;
msg->msg[2] = status_req;
- msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0;
+ msg->reply = (reply && status_req != CEC_OP_STATUS_REQ_OFF) ?
+ CEC_MSG_TUNER_DEVICE_STATUS : 0;
}
static inline void cec_ops_give_tuner_device_status(const struct cec_msg *msg,
diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
index 3094af6..d6ba688 100644
--- a/include/uapi/linux/cec.h
+++ b/include/uapi/linux/cec.h
@@ -767,8 +767,8 @@ struct cec_event {
#define CEC_MSG_SELECT_DIGITAL_SERVICE 0x93
#define CEC_MSG_TUNER_DEVICE_STATUS 0x07
/* Recording Flag Operand (rec_flag) */
-#define CEC_OP_REC_FLAG_USED 0
-#define CEC_OP_REC_FLAG_NOT_USED 1
+#define CEC_OP_REC_FLAG_NOT_USED 0
+#define CEC_OP_REC_FLAG_USED 1
/* Tuner Display Info Operand (tuner_display_info) */
#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL 0
#define CEC_OP_TUNER_DISPLAY_INFO_NONE 1
diff --git a/include/uapi/linux/netfilter/xt_sctp.h b/include/uapi/linux/netfilter/xt_sctp.h
index 4bc6d1a..b4d804a 100644
--- a/include/uapi/linux/netfilter/xt_sctp.h
+++ b/include/uapi/linux/netfilter/xt_sctp.h
@@ -41,19 +41,19 @@ struct xt_sctp_info {
#define SCTP_CHUNKMAP_SET(chunkmap, type) \
do { \
(chunkmap)[type / bytes(__u32)] |= \
- 1 << (type % bytes(__u32)); \
+ 1u << (type % bytes(__u32)); \
} while (0)
#define SCTP_CHUNKMAP_CLEAR(chunkmap, type) \
do { \
(chunkmap)[type / bytes(__u32)] &= \
- ~(1 << (type % bytes(__u32))); \
+ ~(1u << (type % bytes(__u32))); \
} while (0)
#define SCTP_CHUNKMAP_IS_SET(chunkmap, type) \
({ \
((chunkmap)[type / bytes (__u32)] & \
- (1 << (type % bytes (__u32)))) ? 1: 0; \
+ (1u << (type % bytes (__u32)))) ? 1: 0; \
})
#define SCTP_CHUNKMAP_RESET(chunkmap) \
diff --git a/include/uapi/linux/spss_utils.h b/include/uapi/linux/spss_utils.h
index a5ff6ed..6a34d19 100644
--- a/include/uapi/linux/spss_utils.h
+++ b/include/uapi/linux/spss_utils.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _UAPI_SPSS_UTILS_H_
@@ -17,14 +17,65 @@
*/
#define SPSS_IOC_MAGIC 'S'
-#define NUM_SPU_UEFI_APPS 3
+
+/* ---------- set fw cmac --------------------------------- */
+#define NUM_SPU_UEFI_APPS 3
+#define CMAC_SIZE_IN_WORDS 4
struct spss_ioc_set_fw_cmac {
- uint32_t cmac[4];
- uint32_t app_cmacs[NUM_SPU_UEFI_APPS][4];
+ uint32_t cmac[CMAC_SIZE_IN_WORDS];
+ uint32_t app_cmacs[NUM_SPU_UEFI_APPS][CMAC_SIZE_IN_WORDS];
} __packed;
#define SPSS_IOC_SET_FW_CMAC \
_IOWR(SPSS_IOC_MAGIC, 1, struct spss_ioc_set_fw_cmac)
+/* ---------- wait for event ------------------------------ */
+enum _spss_event_id {
+ /* signaled from user */
+ SPSS_EVENT_ID_PIL_CALLED = 0,
+ SPSS_EVENT_ID_NVM_READY = 1,
+ SPSS_EVENT_ID_SPU_READY = 2,
+ SPSS_NUM_USER_EVENTS,
+
+ /* signaled from kernel */
+ SPSS_EVENT_ID_SPU_POWER_DOWN = 6,
+ SPSS_EVENT_ID_SPU_POWER_UP = 7,
+ SPSS_NUM_EVENTS,
+} spss_event_id;
+
+enum _spss_event_status {
+ EVENT_STATUS_SIGNALED = 0xAAAA,
+ EVENT_STATUS_NOT_SIGNALED = 0xFFFF,
+ EVENT_STATUS_TIMEOUT = 0xEEE1,
+ EVENT_STATUS_ABORTED = 0xEEE2,
+} spss_event_status;
+
+struct spss_ioc_wait_for_event {
+ uint32_t event_id; /* input */
+ uint32_t timeout_sec; /* input */
+ uint32_t status; /* output */
+} __packed;
+
+#define SPSS_IOC_WAIT_FOR_EVENT \
+ _IOWR(SPSS_IOC_MAGIC, 2, struct spss_ioc_wait_for_event)
+
+/* ---------- signal event ------------------------------ */
+struct spss_ioc_signal_event {
+ uint32_t event_id; /* input */
+ uint32_t status; /* output */
+} __packed;
+
+#define SPSS_IOC_SIGNAL_EVENT \
+ _IOWR(SPSS_IOC_MAGIC, 3, struct spss_ioc_signal_event)
+
+/* ---------- is event isgnaled ------------------------------ */
+struct spss_ioc_is_signaled {
+ uint32_t event_id; /* input */
+ uint32_t status; /* output */
+} __attribute__((packed));
+
+#define SPSS_IOC_IS_EVENT_SIGNALED \
+ _IOWR(SPSS_IOC_MAGIC, 4, struct spss_ioc_is_signaled)
+
#endif /* _UAPI_SPSS_UTILS_H_ */
diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h
index 94dcae8..7e503c9 100644
--- a/include/uapi/sound/compress_params.h
+++ b/include/uapi/sound/compress_params.h
@@ -419,6 +419,13 @@ struct snd_dec_aptx {
__u32 nap;
};
+/** struct snd_dec_dsd - codec for DSD format
+ * @blk_size - dsd channel block size
+ */
+struct snd_dec_dsd {
+ __u32 blk_size;
+};
+
/** struct snd_dec_pcm - codec options for PCM format
* @num_channels: Number of channels
* @ch_map: Channel map for the above corresponding channels
@@ -445,6 +452,7 @@ union snd_codec_options {
struct snd_dec_thd truehd;
struct snd_dec_pcm pcm_dec;
struct snd_dec_amrwb_plus amrwbplus;
+ struct snd_dec_dsd dsd_dec;
};
/** struct snd_codec_desc - description of codec capabilities
diff --git a/init/main.c b/init/main.c
index 020972f..38a603f6 100644
--- a/init/main.c
+++ b/init/main.c
@@ -105,7 +105,6 @@
static int kernel_init(void *);
extern void init_IRQ(void);
-extern void fork_init(void);
extern void radix_tree_init(void);
/*
diff --git a/kernel/Makefile b/kernel/Makefile
index c2aa5bd..a48ec6c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -58,9 +58,6 @@
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-y += time/
obj-$(CONFIG_FUTEX) += futex.o
-ifeq ($(CONFIG_COMPAT),y)
-obj-$(CONFIG_FUTEX) += futex_compat.o
-endif
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += smp.o
ifneq ($(CONFIG_SMP),y)
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index ea43181..04f59df 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -24,6 +24,7 @@ struct audit_tree {
struct audit_chunk {
struct list_head hash;
+ unsigned long key;
struct fsnotify_mark mark;
struct list_head trees; /* with root here */
int dead;
@@ -172,21 +173,6 @@ static unsigned long inode_to_key(const struct inode *inode)
return (unsigned long)&inode->i_fsnotify_marks;
}
-/*
- * Function to return search key in our hash from chunk. Key 0 is special and
- * should never be present in the hash.
- */
-static unsigned long chunk_to_key(struct audit_chunk *chunk)
-{
- /*
- * We have a reference to the mark so it should be attached to a
- * connector.
- */
- if (WARN_ON_ONCE(!chunk->mark.connector))
- return 0;
- return (unsigned long)chunk->mark.connector->obj;
-}
-
static inline struct list_head *chunk_hash(unsigned long key)
{
unsigned long n = key / L1_CACHE_BYTES;
@@ -196,12 +182,12 @@ static inline struct list_head *chunk_hash(unsigned long key)
/* hash_lock & entry->lock is held by caller */
static void insert_hash(struct audit_chunk *chunk)
{
- unsigned long key = chunk_to_key(chunk);
struct list_head *list;
if (!(chunk->mark.flags & FSNOTIFY_MARK_FLAG_ATTACHED))
return;
- list = chunk_hash(key);
+ WARN_ON_ONCE(!chunk->key);
+ list = chunk_hash(chunk->key);
list_add_rcu(&chunk->hash, list);
}
@@ -213,7 +199,7 @@ struct audit_chunk *audit_tree_lookup(const struct inode *inode)
struct audit_chunk *p;
list_for_each_entry_rcu(p, list, hash) {
- if (chunk_to_key(p) == key) {
+ if (p->key == key) {
atomic_long_inc(&p->refs);
return p;
}
@@ -297,6 +283,7 @@ static void untag_chunk(struct node *p)
chunk->dead = 1;
spin_lock(&hash_lock);
+ new->key = chunk->key;
list_replace_init(&chunk->trees, &new->trees);
if (owner->root == chunk) {
list_del_init(&owner->same_root);
@@ -378,6 +365,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree)
tree->root = chunk;
list_add(&tree->same_root, &chunk->trees);
}
+ chunk->key = inode_to_key(inode);
insert_hash(chunk);
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
@@ -462,6 +450,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
fsnotify_put_mark(old_entry);
return 0;
}
+ chunk->key = old->key;
list_replace_init(&old->trees, &chunk->trees);
for (n = 0, p = chunk->owners; n < old->count; n++, p++) {
struct audit_tree *s = old->owners[n].owner;
@@ -661,7 +650,7 @@ void audit_trim_trees(void)
/* this could be NULL if the watch is dying else where... */
node->index |= 1U<<31;
if (iterate_mounts(compare_root,
- (void *)chunk_to_key(chunk),
+ (void *)(chunk->key),
root_mnt))
node->index &= ~(1U<<31);
}
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 787c7af..4f7262e 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -366,12 +366,12 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
struct dentry *d = kern_path_locked(watch->path, parent);
if (IS_ERR(d))
return PTR_ERR(d);
- inode_unlock(d_backing_inode(parent->dentry));
if (d_is_positive(d)) {
/* update watch filter fields */
watch->dev = d->d_sb->s_dev;
watch->ino = d_backing_inode(d)->i_ino;
}
+ inode_unlock(d_backing_inode(parent->dentry));
dput(d);
return 0;
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index b2d1f04..1513873 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1107,7 +1107,7 @@ static void audit_log_execve_info(struct audit_context *context,
}
/* write as much as we can to the audit log */
- if (len_buf > 0) {
+ if (len_buf >= 0) {
/* NOTE: some magic numbers here - basically if we
* can't fit a reasonable amount of data into the
* existing audit buffer, flush it and start with
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 138f030..3e24133 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -5,6 +5,7 @@
#include <uapi/linux/types.h>
#include <linux/seq_file.h>
#include <linux/compiler.h>
+#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/anon_inodes.h>
@@ -426,6 +427,30 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
offset < btf->hdr.str_len;
}
+/* Only C-style identifier is permitted. This can be relaxed if
+ * necessary.
+ */
+static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
+{
+ /* offset must be valid */
+ const char *src = &btf->strings[offset];
+ const char *src_limit;
+
+ if (!isalpha(*src) && *src != '_')
+ return false;
+
+ /* set a limit on identifier length */
+ src_limit = src + KSYM_NAME_LEN;
+ src++;
+ while (*src && src < src_limit) {
+ if (!isalnum(*src) && *src != '_')
+ return false;
+ src++;
+ }
+
+ return !*src;
+}
+
static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
{
if (!offset)
@@ -1143,6 +1168,22 @@ static int btf_ref_type_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* typedef type must have a valid name, and other ref types,
+ * volatile, const, restrict, should have a null name.
+ */
+ if (BTF_INFO_KIND(t->info) == BTF_KIND_TYPEDEF) {
+ if (!t->name_off ||
+ !btf_name_valid_identifier(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ } else {
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ }
+
btf_verifier_log_type(env, t, NULL);
return 0;
@@ -1300,6 +1341,13 @@ static s32 btf_fwd_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* fwd type must have a valid name */
+ if (!t->name_off ||
+ !btf_name_valid_identifier(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
btf_verifier_log_type(env, t, NULL);
return 0;
@@ -1356,6 +1404,12 @@ static s32 btf_array_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* array type should not have a name */
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
if (btf_type_vlen(t)) {
btf_verifier_log_type(env, t, "vlen != 0");
return -EINVAL;
@@ -1532,6 +1586,13 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* struct type either no name or a valid one */
+ if (t->name_off &&
+ !btf_name_valid_identifier(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
btf_verifier_log_type(env, t, NULL);
last_offset = 0;
@@ -1543,6 +1604,12 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* struct member either no name or a valid one */
+ if (member->name_off &&
+ !btf_name_valid_identifier(btf, member->name_off)) {
+ btf_verifier_log_member(env, t, member, "Invalid name");
+ return -EINVAL;
+ }
/* A member cannot be in type void */
if (!member->type || !BTF_TYPE_ID_VALID(member->type)) {
btf_verifier_log_member(env, t, member,
@@ -1730,6 +1797,13 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* enum type either no name or a valid one */
+ if (t->name_off &&
+ !btf_name_valid_identifier(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
btf_verifier_log_type(env, t, NULL);
for (i = 0; i < nr_enums; i++) {
@@ -1739,6 +1813,14 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* enum member must have a valid name */
+ if (!enums[i].name_off ||
+ !btf_name_valid_identifier(btf, enums[i].name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+
btf_verifier_log(env, "\t%s val=%d\n",
btf_name_by_offset(btf, enums[i].name_off),
enums[i].val);
@@ -2067,53 +2149,50 @@ static int btf_check_sec_info(struct btf_verifier_env *env,
return 0;
}
-static int btf_parse_hdr(struct btf_verifier_env *env, void __user *btf_data,
- u32 btf_data_size)
+static int btf_parse_hdr(struct btf_verifier_env *env)
{
+ u32 hdr_len, hdr_copy, btf_data_size;
const struct btf_header *hdr;
- u32 hdr_len, hdr_copy;
- /*
- * Minimal part of the "struct btf_header" that
- * contains the hdr_len.
- */
- struct btf_min_header {
- u16 magic;
- u8 version;
- u8 flags;
- u32 hdr_len;
- } __user *min_hdr;
struct btf *btf;
int err;
btf = env->btf;
- min_hdr = btf_data;
+ btf_data_size = btf->data_size;
- if (btf_data_size < sizeof(*min_hdr)) {
+ if (btf_data_size <
+ offsetof(struct btf_header, hdr_len) + sizeof(hdr->hdr_len)) {
btf_verifier_log(env, "hdr_len not found");
return -EINVAL;
}
- if (get_user(hdr_len, &min_hdr->hdr_len))
- return -EFAULT;
-
+ hdr = btf->data;
+ hdr_len = hdr->hdr_len;
if (btf_data_size < hdr_len) {
btf_verifier_log(env, "btf_header not found");
return -EINVAL;
}
- err = bpf_check_uarg_tail_zero(btf_data, sizeof(btf->hdr), hdr_len);
- if (err) {
- if (err == -E2BIG)
- btf_verifier_log(env, "Unsupported btf_header");
- return err;
+ /* Ensure the unsupported header fields are zero */
+ if (hdr_len > sizeof(btf->hdr)) {
+ u8 *expected_zero = btf->data + sizeof(btf->hdr);
+ u8 *end = btf->data + hdr_len;
+
+ for (; expected_zero < end; expected_zero++) {
+ if (*expected_zero) {
+ btf_verifier_log(env, "Unsupported btf_header");
+ return -E2BIG;
+ }
+ }
}
hdr_copy = min_t(u32, hdr_len, sizeof(btf->hdr));
- if (copy_from_user(&btf->hdr, btf_data, hdr_copy))
- return -EFAULT;
+ memcpy(&btf->hdr, btf->data, hdr_copy);
hdr = &btf->hdr;
+ if (hdr->hdr_len != hdr_len)
+ return -EINVAL;
+
btf_verifier_log_hdr(env, btf_data_size);
if (hdr->magic != BTF_MAGIC) {
@@ -2183,10 +2262,6 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
}
env->btf = btf;
- err = btf_parse_hdr(env, btf_data, btf_data_size);
- if (err)
- goto errout;
-
data = kvmalloc(btf_data_size, GFP_KERNEL | __GFP_NOWARN);
if (!data) {
err = -ENOMEM;
@@ -2195,13 +2270,18 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
btf->data = data;
btf->data_size = btf_data_size;
- btf->nohdr_data = btf->data + btf->hdr.hdr_len;
if (copy_from_user(data, btf_data, btf_data_size)) {
err = -EFAULT;
goto errout;
}
+ err = btf_parse_hdr(env);
+ if (err)
+ goto errout;
+
+ btf->nohdr_data = btf->data + btf->hdr.hdr_len;
+
err = btf_parse_str_sec(env);
if (err)
goto errout;
diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
index 24aac0d..3c18260 100644
--- a/kernel/bpf/cpumap.c
+++ b/kernel/bpf/cpumap.c
@@ -162,10 +162,14 @@ static void cpu_map_kthread_stop(struct work_struct *work)
static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
struct xdp_frame *xdpf)
{
+ unsigned int hard_start_headroom;
unsigned int frame_size;
void *pkt_data_start;
struct sk_buff *skb;
+ /* Part of headroom was reserved to xdpf */
+ hard_start_headroom = sizeof(struct xdp_frame) + xdpf->headroom;
+
/* build_skb need to place skb_shared_info after SKB end, and
* also want to know the memory "truesize". Thus, need to
* know the memory frame size backing xdp_buff.
@@ -183,15 +187,15 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
* is not at a fixed memory location, with mixed length
* packets, which is bad for cache-line hotness.
*/
- frame_size = SKB_DATA_ALIGN(xdpf->len) + xdpf->headroom +
+ frame_size = SKB_DATA_ALIGN(xdpf->len + hard_start_headroom) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
- pkt_data_start = xdpf->data - xdpf->headroom;
+ pkt_data_start = xdpf->data - hard_start_headroom;
skb = build_skb(pkt_data_start, frame_size);
if (!skb)
return NULL;
- skb_reserve(skb, xdpf->headroom);
+ skb_reserve(skb, hard_start_headroom);
__skb_put(skb, xdpf->len);
if (xdpf->metasize)
skb_metadata_set(skb, xdpf->metasize);
@@ -205,6 +209,9 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
* - RX ring dev queue index (skb_record_rx_queue)
*/
+ /* Allow SKB to reuse area used by xdp_frame */
+ xdp_scrub_frame(xdpf);
+
return skb;
}
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index fc500ca..1defea4 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -520,8 +520,7 @@ static int dev_map_notification(struct notifier_block *notifier,
struct bpf_dtab_netdev *dev, *odev;
dev = READ_ONCE(dtab->netdev_map[i]);
- if (!dev ||
- dev->dev->ifindex != netdev->ifindex)
+ if (!dev || netdev != dev->dev)
continue;
odev = cmpxchg(&dtab->netdev_map[i], dev, NULL);
if (dev == odev)
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 7cb7a7f..55fff5e 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -292,7 +292,7 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
bool irq_work_busy = false;
struct stack_map_irq_work *work = NULL;
- if (in_nmi()) {
+ if (irqs_disabled()) {
work = this_cpu_ptr(&up_read_work);
if (work->irq_work.flags & IRQ_WORK_BUSY)
/* cannot queue more up_read, fallback */
@@ -300,8 +300,9 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
}
/*
- * We cannot do up_read() in nmi context. To do build_id lookup
- * in nmi context, we need to run up_read() in irq_work. We use
+ * We cannot do up_read() when the irq is disabled, because of
+ * risk to deadlock with rq_lock. To do build_id lookup when the
+ * irqs are disabled, we need to run up_read() in irq_work. We use
* a percpu variable to do the irq_work. If the irq_work is
* already used by another lookup, we fall back to report ips.
*
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6e544e3..5969592 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -559,12 +559,12 @@ static int map_create(union bpf_attr *attr)
err = bpf_map_new_fd(map, f_flags);
if (err < 0) {
/* failed to allocate fd.
- * bpf_map_put() is needed because the above
+ * bpf_map_put_with_uref() is needed because the above
* bpf_map_alloc_id() has published the map
* to the userspace and the userspace may
* have refcnt-ed it through BPF_MAP_GET_FD_BY_ID.
*/
- bpf_map_put(map);
+ bpf_map_put_with_uref(map);
return err;
}
@@ -1887,7 +1887,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
fd = bpf_map_new_fd(map, f_flags);
if (fd < 0)
- bpf_map_put(map);
+ bpf_map_put_with_uref(map);
return fd;
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1dff5f7..9e72b2f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4272,6 +4272,7 @@ static bool may_access_skb(enum bpf_prog_type type)
static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
struct bpf_reg_state *regs = cur_regs(env);
+ static const int ctx_reg = BPF_REG_6;
u8 mode = BPF_MODE(insn->code);
int i, err;
@@ -4305,11 +4306,11 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
}
/* check whether implicit source operand (register R6) is readable */
- err = check_reg_arg(env, BPF_REG_6, SRC_OP);
+ err = check_reg_arg(env, ctx_reg, SRC_OP);
if (err)
return err;
- if (regs[BPF_REG_6].type != PTR_TO_CTX) {
+ if (regs[ctx_reg].type != PTR_TO_CTX) {
verbose(env,
"at the time of BPF_LD_ABS|IND R6 != pointer to skb\n");
return -EINVAL;
@@ -4322,6 +4323,10 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
return err;
}
+ err = check_ctx_reg(env, ®s[ctx_reg], ctx_reg);
+ if (err < 0)
+ return err;
+
/* reset caller saved regs to unreadable */
for (i = 0; i < CALLER_SAVED_REGS; i++) {
mark_reg_not_init(env, regs, caller_saved[i]);
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d4d666a..5bc24eb 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -837,8 +837,8 @@ static void rebuild_sched_domains_locked(void)
cpumask_var_t *doms;
int ndoms;
+ lockdep_assert_cpus_held();
lockdep_assert_held(&cpuset_mutex);
- get_online_cpus();
/*
* We have raced with CPU hotplug. Don't do anything to avoid
@@ -846,15 +846,13 @@ static void rebuild_sched_domains_locked(void)
* Anyways, hotplug work item will rebuild sched domains.
*/
if (!cpumask_equal(top_cpuset.effective_cpus, cpu_active_mask))
- goto out;
+ return;
/* Generate domain masks and attrs */
ndoms = generate_sched_domains(&doms, &attr);
/* Have scheduler rebuild the domains */
partition_sched_domains(ndoms, doms, attr);
-out:
- put_online_cpus();
}
#else /* !CONFIG_SMP */
static void rebuild_sched_domains_locked(void)
@@ -864,9 +862,11 @@ static void rebuild_sched_domains_locked(void)
void rebuild_sched_domains(void)
{
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
rebuild_sched_domains_locked();
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
}
static int update_cpus_allowed(struct cpuset *cs, struct task_struct *p,
@@ -1634,6 +1634,7 @@ static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft,
cpuset_filetype_t type = cft->private;
int retval = 0;
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs)) {
retval = -ENODEV;
@@ -1671,6 +1672,7 @@ static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft,
}
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
return retval;
}
@@ -1681,6 +1683,7 @@ static int cpuset_write_s64(struct cgroup_subsys_state *css, struct cftype *cft,
cpuset_filetype_t type = cft->private;
int retval = -ENODEV;
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs))
goto out_unlock;
@@ -1695,6 +1698,7 @@ static int cpuset_write_s64(struct cgroup_subsys_state *css, struct cftype *cft,
}
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
return retval;
}
@@ -1733,6 +1737,7 @@ static ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
kernfs_break_active_protection(of->kn);
flush_work(&cpuset_hotplug_work);
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs))
goto out_unlock;
@@ -1758,6 +1763,7 @@ static ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
free_trial_cpuset(trialcs);
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
kernfs_unbreak_active_protection(of->kn);
css_put(&cs->css);
flush_workqueue(cpuset_migrate_mm_wq);
@@ -2007,6 +2013,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
if (!parent)
return 0;
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
set_bit(CS_ONLINE, &cs->flags);
@@ -2058,6 +2065,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
spin_unlock_irq(&callback_lock);
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
return 0;
}
@@ -2071,6 +2079,7 @@ static void cpuset_css_offline(struct cgroup_subsys_state *css)
{
struct cpuset *cs = css_cs(css);
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (is_sched_load_balance(cs))
@@ -2080,6 +2089,7 @@ static void cpuset_css_offline(struct cgroup_subsys_state *css)
clear_bit(CS_ONLINE, &cs->flags);
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
}
static void cpuset_css_free(struct cgroup_subsys_state *css)
diff --git a/kernel/cgroup/pids.c b/kernel/cgroup/pids.c
index c9960baa..940d2e8 100644
--- a/kernel/cgroup/pids.c
+++ b/kernel/cgroup/pids.c
@@ -48,7 +48,7 @@ struct pids_cgroup {
* %PIDS_MAX = (%PID_MAX_LIMIT + 1).
*/
atomic64_t counter;
- int64_t limit;
+ atomic64_t limit;
/* Handle for "pids.events" */
struct cgroup_file events_file;
@@ -76,8 +76,8 @@ pids_css_alloc(struct cgroup_subsys_state *parent)
if (!pids)
return ERR_PTR(-ENOMEM);
- pids->limit = PIDS_MAX;
atomic64_set(&pids->counter, 0);
+ atomic64_set(&pids->limit, PIDS_MAX);
atomic64_set(&pids->events_limit, 0);
return &pids->css;
}
@@ -149,13 +149,14 @@ static int pids_try_charge(struct pids_cgroup *pids, int num)
for (p = pids; parent_pids(p); p = parent_pids(p)) {
int64_t new = atomic64_add_return(num, &p->counter);
+ int64_t limit = atomic64_read(&p->limit);
/*
* Since new is capped to the maximum number of pid_t, if
* p->limit is %PIDS_MAX then we know that this test will never
* fail.
*/
- if (new > p->limit)
+ if (new > limit)
goto revert;
}
@@ -280,7 +281,7 @@ static ssize_t pids_max_write(struct kernfs_open_file *of, char *buf,
* Limit updates don't need to be mutex'd, since it isn't
* critical that any racing fork()s follow the new limit.
*/
- pids->limit = limit;
+ atomic64_set(&pids->limit, limit);
return nbytes;
}
@@ -288,7 +289,7 @@ static int pids_max_show(struct seq_file *sf, void *v)
{
struct cgroup_subsys_state *css = seq_css(sf);
struct pids_cgroup *pids = css_pids(css);
- int64_t limit = pids->limit;
+ int64_t limit = atomic64_read(&pids->limit);
if (limit >= PIDS_MAX)
seq_printf(sf, "%s\n", PIDS_MAX_STR);
diff --git a/kernel/cpu.c b/kernel/cpu.c
index a040bf9..4307574 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -378,6 +378,7 @@ void __init cpu_smt_disable(bool force)
pr_info("SMT: Force disabled\n");
cpu_smt_control = CPU_SMT_FORCE_DISABLED;
} else {
+ pr_info("SMT: disabled\n");
cpu_smt_control = CPU_SMT_DISABLED;
}
}
@@ -2393,7 +2394,18 @@ void idle_notifier_call_chain(unsigned long val)
}
EXPORT_SYMBOL_GPL(idle_notifier_call_chain);
-enum cpu_mitigations cpu_mitigations __ro_after_init = CPU_MITIGATIONS_AUTO;
+/*
+ * These are used for a global "mitigations=" cmdline option for toggling
+ * optional CPU mitigations.
+ */
+enum cpu_mitigations {
+ CPU_MITIGATIONS_OFF,
+ CPU_MITIGATIONS_AUTO,
+ CPU_MITIGATIONS_AUTO_NOSMT,
+};
+
+static enum cpu_mitigations cpu_mitigations __ro_after_init =
+ CPU_MITIGATIONS_AUTO;
static int __init mitigations_parse_cmdline(char *arg)
{
@@ -2410,3 +2422,17 @@ static int __init mitigations_parse_cmdline(char *arg)
return 0;
}
early_param("mitigations", mitigations_parse_cmdline);
+
+/* mitigations=off */
+bool cpu_mitigations_off(void)
+{
+ return cpu_mitigations == CPU_MITIGATIONS_OFF;
+}
+EXPORT_SYMBOL_GPL(cpu_mitigations_off);
+
+/* mitigations=auto,nosmt */
+bool cpu_mitigations_auto_nosmt(void)
+{
+ return cpu_mitigations == CPU_MITIGATIONS_AUTO_NOSMT;
+}
+EXPORT_SYMBOL_GPL(cpu_mitigations_auto_nosmt);
diff --git a/kernel/cred.c b/kernel/cred.c
index 5ab1f7e..a9f0f8b 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -220,7 +220,7 @@ struct cred *cred_alloc_blank(void)
new->magic = CRED_MAGIC;
#endif
- if (security_cred_alloc_blank(new, GFP_KERNEL) < 0)
+ if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0)
goto error;
return new;
@@ -279,7 +279,7 @@ struct cred *prepare_creds(void)
new->security = NULL;
#endif
- if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+ if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
goto error;
validate_creds(new);
return new;
@@ -654,7 +654,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
#ifdef CONFIG_SECURITY
new->security = NULL;
#endif
- if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+ if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
goto error;
put_cred(old);
diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index c007d25..3a23974 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -442,6 +442,7 @@ void debug_dma_dump_mappings(struct device *dev)
}
spin_unlock_irqrestore(&bucket->lock, flags);
+ cond_resched();
}
}
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 4f8a6db..2a8c41f 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -761,34 +761,6 @@ static bool swiotlb_free_buffer(struct device *dev, size_t size,
return true;
}
-static void
-swiotlb_full(struct device *dev, size_t size, enum dma_data_direction dir,
- int do_panic)
-{
- if (swiotlb_force == SWIOTLB_NO_FORCE)
- return;
-
- /*
- * Ran out of IOMMU space for this operation. This is very bad.
- * Unfortunately the drivers cannot handle this operation properly.
- * unless they check for dma_mapping_error (most don't)
- * When the mapping is small enough return a static buffer to limit
- * the damage, or panic when the transfer is too big.
- */
- dev_err_ratelimited(dev, "DMA: Out of SW-IOMMU space for %zu bytes\n",
- size);
-
- if (size <= io_tlb_overflow || !do_panic)
- return;
-
- if (dir == DMA_BIDIRECTIONAL)
- panic("DMA: Random memory could be DMA accessed\n");
- if (dir == DMA_FROM_DEVICE)
- panic("DMA: Random memory could be DMA written\n");
- if (dir == DMA_TO_DEVICE)
- panic("DMA: Random memory could be DMA read\n");
-}
-
/*
* Map a single buffer of the indicated size for DMA in streaming mode. The
* physical address to use is returned.
@@ -817,10 +789,8 @@ dma_addr_t swiotlb_map_page(struct device *dev, struct page *page,
/* Oh well, have to allocate and map a bounce buffer. */
map = map_single(dev, phys, size, dir, attrs);
- if (map == SWIOTLB_MAP_ERROR) {
- swiotlb_full(dev, size, dir, 1);
+ if (map == SWIOTLB_MAP_ERROR)
return __phys_to_dma(dev, io_tlb_overflow_buffer);
- }
dev_addr = __phys_to_dma(dev, map);
@@ -954,7 +924,6 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
if (map == SWIOTLB_MAP_ERROR) {
/* Don't panic here, we expect map_sg users
to do proper error handling. */
- swiotlb_full(hwdev, sg->length, dir, 0);
attrs |= DMA_ATTR_SKIP_CPU_SYNC;
swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir,
attrs);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 18d8f2a..c574f23 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -11717,7 +11717,7 @@ inherit_event(struct perf_event *parent_event,
GFP_KERNEL);
if (!child_ctx->task_ctx_data) {
free_event(child_event);
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
}
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 578d4ac5..c173e41 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1858,7 +1858,7 @@ static void handle_trampoline(struct pt_regs *regs)
sigill:
uprobe_warn(current, "handle uretprobe, sending SIGILL.");
- force_sig_info(SIGILL, SEND_SIG_FORCED, current);
+ force_sig(SIGILL, current);
}
@@ -1974,7 +1974,7 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
if (unlikely(err)) {
uprobe_warn(current, "execute the probed insn, sending SIGILL.");
- force_sig_info(SIGILL, SEND_SIG_FORCED, current);
+ force_sig(SIGILL, current);
}
}
diff --git a/kernel/exit.c b/kernel/exit.c
index aae5cc5..d8eb9c7 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -578,10 +578,6 @@ static struct task_struct *find_child_reaper(struct task_struct *father,
}
write_unlock_irq(&tasklist_lock);
- if (unlikely(pid_ns == &init_pid_ns)) {
- panic("Attempted to kill init! exitcode=0x%08x\n",
- father->signal->group_exit_code ?: father->exit_code);
- }
list_for_each_entry_safe(p, n, dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
@@ -846,6 +842,14 @@ void __noreturn do_exit(long code)
acct_update_integrals(tsk);
group_dead = atomic_dec_and_test(&tsk->signal->live);
if (group_dead) {
+ /*
+ * If the last thread of global init has exited, panic
+ * immediately to get a useable coredump.
+ */
+ if (unlikely(is_global_init(tsk)))
+ panic("Attempted to kill init! exitcode=0x%08x\n",
+ tsk->signal->group_exit_code ?: (int)code);
+
#ifdef CONFIG_POSIX_TIMERS
hrtimer_cancel(&tsk->signal->real_timer);
exit_itimers(tsk->signal);
diff --git a/kernel/fork.c b/kernel/fork.c
index 55ec95a..0834f34 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -164,10 +164,6 @@ static inline void free_task_struct(struct task_struct *tsk)
}
#endif
-void __weak arch_release_thread_stack(unsigned long *stack)
-{
-}
-
#ifndef CONFIG_ARCH_THREAD_STACK_ALLOCATOR
/*
@@ -377,7 +373,6 @@ static void release_task_stack(struct task_struct *tsk)
return; /* Better to leak the stack than to free prematurely */
account_kernel_stack(tsk, -1);
- arch_release_thread_stack(tsk->stack);
free_thread_stack(tsk);
tsk->stack = NULL;
#ifdef CONFIG_VMAP_STACK
diff --git a/kernel/futex.c b/kernel/futex.c
index afdc5ea..e75ad30 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -44,6 +44,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/compat.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fs.h>
@@ -173,8 +174,10 @@
* double_lock_hb() and double_unlock_hb(), respectively.
*/
-#ifndef CONFIG_HAVE_FUTEX_CMPXCHG
-int __read_mostly futex_cmpxchg_enabled;
+#ifdef CONFIG_HAVE_FUTEX_CMPXCHG
+#define futex_cmpxchg_enabled 1
+#else
+static int __read_mostly futex_cmpxchg_enabled;
#endif
/*
@@ -3454,11 +3457,16 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
return ret;
}
+/* Constants for the pending_op argument of handle_futex_death */
+#define HANDLE_DEATH_PENDING true
+#define HANDLE_DEATH_LIST false
+
/*
* Process a futex-list entry, check whether it's owned by the
* dying task, and do notification if so:
*/
-int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
+static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr,
+ bool pi, bool pending_op)
{
u32 uval, uninitialized_var(nval), mval;
int err;
@@ -3471,6 +3479,42 @@ int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
if (get_user(uval, uaddr))
return -1;
+ /*
+ * Special case for regular (non PI) futexes. The unlock path in
+ * user space has two race scenarios:
+ *
+ * 1. The unlock path releases the user space futex value and
+ * before it can execute the futex() syscall to wake up
+ * waiters it is killed.
+ *
+ * 2. A woken up waiter is killed before it can acquire the
+ * futex in user space.
+ *
+ * In both cases the TID validation below prevents a wakeup of
+ * potential waiters which can cause these waiters to block
+ * forever.
+ *
+ * In both cases the following conditions are met:
+ *
+ * 1) task->robust_list->list_op_pending != NULL
+ * @pending_op == true
+ * 2) User space futex value == 0
+ * 3) Regular futex: @pi == false
+ *
+ * If these conditions are met, it is safe to attempt waking up a
+ * potential waiter without touching the user space futex value and
+ * trying to set the OWNER_DIED bit. The user space futex value is
+ * uncontended and the rest of the user space mutex state is
+ * consistent, so a woken waiter will just take over the
+ * uncontended futex. Setting the OWNER_DIED bit would create
+ * inconsistent state and malfunction of the user space owner died
+ * handling.
+ */
+ if (pending_op && !pi && !uval) {
+ futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
+ return 0;
+ }
+
if ((uval & FUTEX_TID_MASK) != task_pid_vnr(curr))
return 0;
@@ -3590,10 +3634,11 @@ void exit_robust_list(struct task_struct *curr)
* A pending lock might already be on the list, so
* don't process it twice:
*/
- if (entry != pending)
+ if (entry != pending) {
if (handle_futex_death((void __user *)entry + futex_offset,
- curr, pi))
+ curr, pi, HANDLE_DEATH_LIST))
return;
+ }
if (rc)
return;
entry = next_entry;
@@ -3607,9 +3652,10 @@ void exit_robust_list(struct task_struct *curr)
cond_resched();
}
- if (pending)
+ if (pending) {
handle_futex_death((void __user *)pending + futex_offset,
- curr, pip);
+ curr, pip, HANDLE_DEATH_PENDING);
+ }
}
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
@@ -3707,6 +3753,193 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
}
+#ifdef CONFIG_COMPAT
+/*
+ * Fetch a robust-list pointer. Bit 0 signals PI futexes:
+ */
+static inline int
+compat_fetch_robust_entry(compat_uptr_t *uentry, struct robust_list __user **entry,
+ compat_uptr_t __user *head, unsigned int *pi)
+{
+ if (get_user(*uentry, head))
+ return -EFAULT;
+
+ *entry = compat_ptr((*uentry) & ~1);
+ *pi = (unsigned int)(*uentry) & 1;
+
+ return 0;
+}
+
+static void __user *futex_uaddr(struct robust_list __user *entry,
+ compat_long_t futex_offset)
+{
+ compat_uptr_t base = ptr_to_compat(entry);
+ void __user *uaddr = compat_ptr(base + futex_offset);
+
+ return uaddr;
+}
+
+/*
+ * Walk curr->robust_list (very carefully, it's a userspace list!)
+ * and mark any locks found there dead, and notify any waiters.
+ *
+ * We silently return on any sign of list-walking problem.
+ */
+void compat_exit_robust_list(struct task_struct *curr)
+{
+ struct compat_robust_list_head __user *head = curr->compat_robust_list;
+ struct robust_list __user *entry, *next_entry, *pending;
+ unsigned int limit = ROBUST_LIST_LIMIT, pi, pip;
+ unsigned int uninitialized_var(next_pi);
+ compat_uptr_t uentry, next_uentry, upending;
+ compat_long_t futex_offset;
+ int rc;
+
+ if (!futex_cmpxchg_enabled)
+ return;
+
+ /*
+ * Fetch the list head (which was registered earlier, via
+ * sys_set_robust_list()):
+ */
+ if (compat_fetch_robust_entry(&uentry, &entry, &head->list.next, &pi))
+ return;
+ /*
+ * Fetch the relative futex offset:
+ */
+ if (get_user(futex_offset, &head->futex_offset))
+ return;
+ /*
+ * Fetch any possibly pending lock-add first, and handle it
+ * if it exists:
+ */
+ if (compat_fetch_robust_entry(&upending, &pending,
+ &head->list_op_pending, &pip))
+ return;
+
+ next_entry = NULL; /* avoid warning with gcc */
+ while (entry != (struct robust_list __user *) &head->list) {
+ /*
+ * Fetch the next entry in the list before calling
+ * handle_futex_death:
+ */
+ rc = compat_fetch_robust_entry(&next_uentry, &next_entry,
+ (compat_uptr_t __user *)&entry->next, &next_pi);
+ /*
+ * A pending lock might already be on the list, so
+ * dont process it twice:
+ */
+ if (entry != pending) {
+ void __user *uaddr = futex_uaddr(entry, futex_offset);
+
+ if (handle_futex_death(uaddr, curr, pi,
+ HANDLE_DEATH_LIST))
+ return;
+ }
+ if (rc)
+ return;
+ uentry = next_uentry;
+ entry = next_entry;
+ pi = next_pi;
+ /*
+ * Avoid excessively long or circular lists:
+ */
+ if (!--limit)
+ break;
+
+ cond_resched();
+ }
+ if (pending) {
+ void __user *uaddr = futex_uaddr(pending, futex_offset);
+
+ handle_futex_death(uaddr, curr, pip, HANDLE_DEATH_PENDING);
+ }
+}
+
+COMPAT_SYSCALL_DEFINE2(set_robust_list,
+ struct compat_robust_list_head __user *, head,
+ compat_size_t, len)
+{
+ if (!futex_cmpxchg_enabled)
+ return -ENOSYS;
+
+ if (unlikely(len != sizeof(*head)))
+ return -EINVAL;
+
+ current->compat_robust_list = head;
+
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
+ compat_uptr_t __user *, head_ptr,
+ compat_size_t __user *, len_ptr)
+{
+ struct compat_robust_list_head __user *head;
+ unsigned long ret;
+ struct task_struct *p;
+
+ if (!futex_cmpxchg_enabled)
+ return -ENOSYS;
+
+ rcu_read_lock();
+
+ ret = -ESRCH;
+ if (!pid)
+ p = current;
+ else {
+ p = find_task_by_vpid(pid);
+ if (!p)
+ goto err_unlock;
+ }
+
+ ret = -EPERM;
+ if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
+ goto err_unlock;
+
+ head = p->compat_robust_list;
+ rcu_read_unlock();
+
+ if (put_user(sizeof(*head), len_ptr))
+ return -EFAULT;
+ return put_user(ptr_to_compat(head), head_ptr);
+
+err_unlock:
+ rcu_read_unlock();
+
+ return ret;
+}
+
+COMPAT_SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
+ struct old_timespec32 __user *, utime, u32 __user *, uaddr2,
+ u32, val3)
+{
+ struct timespec ts;
+ ktime_t t, *tp = NULL;
+ int val2 = 0;
+ int cmd = op & FUTEX_CMD_MASK;
+
+ if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
+ cmd == FUTEX_WAIT_BITSET ||
+ cmd == FUTEX_WAIT_REQUEUE_PI)) {
+ if (compat_get_timespec(&ts, utime))
+ return -EFAULT;
+ if (!timespec_valid(&ts))
+ return -EINVAL;
+
+ t = timespec_to_ktime(ts);
+ if (cmd == FUTEX_WAIT)
+ t = ktime_add_safe(ktime_get(), t);
+ tp = &t;
+ }
+ if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+ cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
+ val2 = (int) (unsigned long) utime;
+
+ return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
+}
+#endif /* CONFIG_COMPAT */
+
static void __init futex_detect_cmpxchg(void)
{
#ifndef CONFIG_HAVE_FUTEX_CMPXCHG
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
deleted file mode 100644
index 83f830ac..0000000
--- a/kernel/futex_compat.c
+++ /dev/null
@@ -1,202 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/kernel/futex_compat.c
- *
- * Futex compatibililty routines.
- *
- * Copyright 2006, Red Hat, Inc., Ingo Molnar
- */
-
-#include <linux/linkage.h>
-#include <linux/compat.h>
-#include <linux/nsproxy.h>
-#include <linux/futex.h>
-#include <linux/ptrace.h>
-#include <linux/syscalls.h>
-
-#include <linux/uaccess.h>
-
-
-/*
- * Fetch a robust-list pointer. Bit 0 signals PI futexes:
- */
-static inline int
-fetch_robust_entry(compat_uptr_t *uentry, struct robust_list __user **entry,
- compat_uptr_t __user *head, unsigned int *pi)
-{
- if (get_user(*uentry, head))
- return -EFAULT;
-
- *entry = compat_ptr((*uentry) & ~1);
- *pi = (unsigned int)(*uentry) & 1;
-
- return 0;
-}
-
-static void __user *futex_uaddr(struct robust_list __user *entry,
- compat_long_t futex_offset)
-{
- compat_uptr_t base = ptr_to_compat(entry);
- void __user *uaddr = compat_ptr(base + futex_offset);
-
- return uaddr;
-}
-
-/*
- * Walk curr->robust_list (very carefully, it's a userspace list!)
- * and mark any locks found there dead, and notify any waiters.
- *
- * We silently return on any sign of list-walking problem.
- */
-void compat_exit_robust_list(struct task_struct *curr)
-{
- struct compat_robust_list_head __user *head = curr->compat_robust_list;
- struct robust_list __user *entry, *next_entry, *pending;
- unsigned int limit = ROBUST_LIST_LIMIT, pi, pip;
- unsigned int uninitialized_var(next_pi);
- compat_uptr_t uentry, next_uentry, upending;
- compat_long_t futex_offset;
- int rc;
-
- if (!futex_cmpxchg_enabled)
- return;
-
- /*
- * Fetch the list head (which was registered earlier, via
- * sys_set_robust_list()):
- */
- if (fetch_robust_entry(&uentry, &entry, &head->list.next, &pi))
- return;
- /*
- * Fetch the relative futex offset:
- */
- if (get_user(futex_offset, &head->futex_offset))
- return;
- /*
- * Fetch any possibly pending lock-add first, and handle it
- * if it exists:
- */
- if (fetch_robust_entry(&upending, &pending,
- &head->list_op_pending, &pip))
- return;
-
- next_entry = NULL; /* avoid warning with gcc */
- while (entry != (struct robust_list __user *) &head->list) {
- /*
- * Fetch the next entry in the list before calling
- * handle_futex_death:
- */
- rc = fetch_robust_entry(&next_uentry, &next_entry,
- (compat_uptr_t __user *)&entry->next, &next_pi);
- /*
- * A pending lock might already be on the list, so
- * dont process it twice:
- */
- if (entry != pending) {
- void __user *uaddr = futex_uaddr(entry, futex_offset);
-
- if (handle_futex_death(uaddr, curr, pi))
- return;
- }
- if (rc)
- return;
- uentry = next_uentry;
- entry = next_entry;
- pi = next_pi;
- /*
- * Avoid excessively long or circular lists:
- */
- if (!--limit)
- break;
-
- cond_resched();
- }
- if (pending) {
- void __user *uaddr = futex_uaddr(pending, futex_offset);
-
- handle_futex_death(uaddr, curr, pip);
- }
-}
-
-COMPAT_SYSCALL_DEFINE2(set_robust_list,
- struct compat_robust_list_head __user *, head,
- compat_size_t, len)
-{
- if (!futex_cmpxchg_enabled)
- return -ENOSYS;
-
- if (unlikely(len != sizeof(*head)))
- return -EINVAL;
-
- current->compat_robust_list = head;
-
- return 0;
-}
-
-COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
- compat_uptr_t __user *, head_ptr,
- compat_size_t __user *, len_ptr)
-{
- struct compat_robust_list_head __user *head;
- unsigned long ret;
- struct task_struct *p;
-
- if (!futex_cmpxchg_enabled)
- return -ENOSYS;
-
- rcu_read_lock();
-
- ret = -ESRCH;
- if (!pid)
- p = current;
- else {
- p = find_task_by_vpid(pid);
- if (!p)
- goto err_unlock;
- }
-
- ret = -EPERM;
- if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
- goto err_unlock;
-
- head = p->compat_robust_list;
- rcu_read_unlock();
-
- if (put_user(sizeof(*head), len_ptr))
- return -EFAULT;
- return put_user(ptr_to_compat(head), head_ptr);
-
-err_unlock:
- rcu_read_unlock();
-
- return ret;
-}
-
-COMPAT_SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
- struct compat_timespec __user *, utime, u32 __user *, uaddr2,
- u32, val3)
-{
- struct timespec ts;
- ktime_t t, *tp = NULL;
- int val2 = 0;
- int cmd = op & FUTEX_CMD_MASK;
-
- if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
- cmd == FUTEX_WAIT_BITSET ||
- cmd == FUTEX_WAIT_REQUEUE_PI)) {
- if (compat_get_timespec(&ts, utime))
- return -EFAULT;
- if (!timespec_valid(&ts))
- return -EINVAL;
-
- t = timespec_to_ktime(ts);
- if (cmd == FUTEX_WAIT)
- t = ktime_add_safe(ktime_get(), t);
- tp = &t;
- }
- if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
- cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
- val2 = (int) (unsigned long) utime;
-
- return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
-}
diff --git a/kernel/irq/matrix.c b/kernel/irq/matrix.c
index 9233770..30cc217 100644
--- a/kernel/irq/matrix.c
+++ b/kernel/irq/matrix.c
@@ -8,7 +8,7 @@
#include <linux/cpu.h>
#include <linux/irq.h>
-#define IRQ_MATRIX_SIZE (BITS_TO_LONGS(IRQ_MATRIX_BITS) * sizeof(unsigned long))
+#define IRQ_MATRIX_SIZE (BITS_TO_LONGS(IRQ_MATRIX_BITS))
struct cpumap {
unsigned int available;
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index f50b90d..faeec82 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -473,6 +473,10 @@ static struct page *kimage_alloc_crash_control_pages(struct kimage *image,
}
}
+ /* Ensure that these pages are decrypted if SME is enabled. */
+ if (pages)
+ arch_kexec_post_alloc_pages(page_address(pages), 1 << order, 0);
+
return pages;
}
@@ -869,6 +873,7 @@ static int kimage_load_crash_segment(struct kimage *image,
result = -ENOMEM;
goto out;
}
+ arch_kexec_post_alloc_pages(page_address(page), 1, 0);
ptr = kmap(page);
ptr += maddr & ~PAGE_MASK;
mchunk = min_t(size_t, mbytes,
@@ -886,6 +891,7 @@ static int kimage_load_crash_segment(struct kimage *image,
result = copy_from_user(ptr, buf, uchunk);
kexec_flush_icache_page(page);
kunmap(page);
+ arch_kexec_pre_free_pages(page_address(page), 1);
if (result) {
result = -EFAULT;
goto out;
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index b8efca9..f4e4095e 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -544,8 +544,14 @@ static void do_free_cleaned_kprobes(void)
struct optimized_kprobe *op, *tmp;
list_for_each_entry_safe(op, tmp, &freeing_list, list) {
- BUG_ON(!kprobe_unused(&op->kp));
list_del_init(&op->list);
+ if (WARN_ON_ONCE(!kprobe_unused(&op->kp))) {
+ /*
+ * This must not happen, but if there is a kprobe
+ * still in use, keep it on kprobes hash list.
+ */
+ continue;
+ }
free_aggr_kprobe(&op->kp);
}
}
@@ -2090,6 +2096,47 @@ void dump_kprobe(struct kprobe *kp)
}
NOKPROBE_SYMBOL(dump_kprobe);
+int kprobe_add_ksym_blacklist(unsigned long entry)
+{
+ struct kprobe_blacklist_entry *ent;
+ unsigned long offset = 0, size = 0;
+
+ if (!kernel_text_address(entry) ||
+ !kallsyms_lookup_size_offset(entry, &size, &offset))
+ return -EINVAL;
+
+ ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+ if (!ent)
+ return -ENOMEM;
+ ent->start_addr = entry;
+ ent->end_addr = entry + size;
+ INIT_LIST_HEAD(&ent->list);
+ list_add_tail(&ent->list, &kprobe_blacklist);
+
+ return (int)size;
+}
+
+/* Add all symbols in given area into kprobe blacklist */
+int kprobe_add_area_blacklist(unsigned long start, unsigned long end)
+{
+ unsigned long entry;
+ int ret = 0;
+
+ for (entry = start; entry < end; entry += ret) {
+ ret = kprobe_add_ksym_blacklist(entry);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) /* In case of alias symbol */
+ ret = 1;
+ }
+ return 0;
+}
+
+int __init __weak arch_populate_kprobe_blacklist(void)
+{
+ return 0;
+}
+
/*
* Lookup and populate the kprobe_blacklist.
*
@@ -2101,26 +2148,24 @@ NOKPROBE_SYMBOL(dump_kprobe);
static int __init populate_kprobe_blacklist(unsigned long *start,
unsigned long *end)
{
+ unsigned long entry;
unsigned long *iter;
- struct kprobe_blacklist_entry *ent;
- unsigned long entry, offset = 0, size = 0;
+ int ret;
for (iter = start; iter < end; iter++) {
entry = arch_deref_entry_point((void *)*iter);
-
- if (!kernel_text_address(entry) ||
- !kallsyms_lookup_size_offset(entry, &size, &offset))
+ ret = kprobe_add_ksym_blacklist(entry);
+ if (ret == -EINVAL)
continue;
-
- ent = kmalloc(sizeof(*ent), GFP_KERNEL);
- if (!ent)
- return -ENOMEM;
- ent->start_addr = entry;
- ent->end_addr = entry + size;
- INIT_LIST_HEAD(&ent->list);
- list_add_tail(&ent->list, &kprobe_blacklist);
+ if (ret < 0)
+ return ret;
}
- return 0;
+
+ /* Symbols in __kprobes_text are blacklisted */
+ ret = kprobe_add_area_blacklist((unsigned long)__kprobes_text_start,
+ (unsigned long)__kprobes_text_end);
+
+ return ret ? : arch_populate_kprobe_blacklist();
}
/* Module notifier call back, checking kprobes on the module */
diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c
index c18dadd..8ab57cc 100644
--- a/kernel/locking/spinlock_debug.c
+++ b/kernel/locking/spinlock_debug.c
@@ -53,19 +53,19 @@ EXPORT_SYMBOL(__rwlock_init);
static void spin_dump(raw_spinlock_t *lock, const char *msg)
{
- struct task_struct *owner = NULL;
+ struct task_struct *owner = READ_ONCE(lock->owner);
- if (lock->owner && lock->owner != SPINLOCK_OWNER_INIT)
- owner = lock->owner;
+ if (owner == SPINLOCK_OWNER_INIT)
+ owner = NULL;
printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n",
msg, raw_smp_processor_id(),
current->comm, task_pid_nr(current));
printk(KERN_EMERG " lock: %pS, .magic: %08x, .owner: %s/%d, "
".owner_cpu: %d\n",
- lock, lock->magic,
+ lock, READ_ONCE(lock->magic),
owner ? owner->comm : "<none>",
owner ? task_pid_nr(owner) : -1,
- lock->owner_cpu);
+ READ_ONCE(lock->owner_cpu));
#ifdef CONFIG_DEBUG_SPINLOCK_BITE_ON_BUG
msm_trigger_wdog_bite();
#elif defined(CONFIG_DEBUG_SPINLOCK_PANIC_ON_BUG)
@@ -87,16 +87,16 @@ static void spin_bug(raw_spinlock_t *lock, const char *msg)
static inline void
debug_spin_lock_before(raw_spinlock_t *lock)
{
- SPIN_BUG_ON(lock->magic != SPINLOCK_MAGIC, lock, "bad magic");
- SPIN_BUG_ON(lock->owner == current, lock, "recursion");
- SPIN_BUG_ON(lock->owner_cpu == raw_smp_processor_id(),
+ SPIN_BUG_ON(READ_ONCE(lock->magic) != SPINLOCK_MAGIC, lock, "bad magic");
+ SPIN_BUG_ON(READ_ONCE(lock->owner) == current, lock, "recursion");
+ SPIN_BUG_ON(READ_ONCE(lock->owner_cpu) == raw_smp_processor_id(),
lock, "cpu recursion");
}
static inline void debug_spin_lock_after(raw_spinlock_t *lock)
{
- lock->owner_cpu = raw_smp_processor_id();
- lock->owner = current;
+ WRITE_ONCE(lock->owner_cpu, raw_smp_processor_id());
+ WRITE_ONCE(lock->owner, current);
}
static inline void debug_spin_unlock(raw_spinlock_t *lock)
@@ -106,8 +106,8 @@ static inline void debug_spin_unlock(raw_spinlock_t *lock)
SPIN_BUG_ON(lock->owner != current, lock, "wrong owner");
SPIN_BUG_ON(lock->owner_cpu != raw_smp_processor_id(),
lock, "wrong CPU");
- lock->owner = SPINLOCK_OWNER_INIT;
- lock->owner_cpu = -1;
+ WRITE_ONCE(lock->owner, SPINLOCK_OWNER_INIT);
+ WRITE_ONCE(lock->owner_cpu, -1);
}
/*
@@ -195,8 +195,8 @@ static inline void debug_write_lock_before(rwlock_t *lock)
static inline void debug_write_lock_after(rwlock_t *lock)
{
- lock->owner_cpu = raw_smp_processor_id();
- lock->owner = current;
+ WRITE_ONCE(lock->owner_cpu, raw_smp_processor_id());
+ WRITE_ONCE(lock->owner, current);
}
static inline void debug_write_unlock(rwlock_t *lock)
@@ -205,8 +205,8 @@ static inline void debug_write_unlock(rwlock_t *lock)
RWLOCK_BUG_ON(lock->owner != current, lock, "wrong owner");
RWLOCK_BUG_ON(lock->owner_cpu != raw_smp_processor_id(),
lock, "wrong CPU");
- lock->owner = SPINLOCK_OWNER_INIT;
- lock->owner_cpu = -1;
+ WRITE_ONCE(lock->owner, SPINLOCK_OWNER_INIT);
+ WRITE_ONCE(lock->owner_cpu, -1);
}
void do_raw_write_lock(rwlock_t *lock)
diff --git a/kernel/module.c b/kernel/module.c
index e27ec1a..fbc2729 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1021,6 +1021,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
free_module(mod);
+ /* someone could wait for the module in add_unformed_module() */
+ wake_up_all(&module_wq);
return 0;
out:
mutex_unlock(&module_mutex);
diff --git a/kernel/panic.c b/kernel/panic.c
index 9bc9c23..50033a7 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -640,7 +640,7 @@ device_initcall(register_warn_debugfs);
*/
__visible void __stack_chk_fail(void)
{
- panic("stack-protector: Kernel stack is corrupted in: %pB\n",
+ panic("stack-protector: Kernel stack is corrupted in: %pB",
__builtin_return_address(0));
}
EXPORT_SYMBOL(__stack_chk_fail);
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 3d37c27..f2635fc 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -736,8 +736,15 @@ static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
* We have found the zone. Now walk the radix tree to find the leaf node
* for our PFN.
*/
+
+ /*
+ * If the zone we wish to scan is the the current zone and the
+ * pfn falls into the current node then we do not need to walk
+ * the tree.
+ */
node = bm->cur.node;
- if (((pfn - zone->start_pfn) & ~BM_BLOCK_MASK) == bm->cur.node_pfn)
+ if (zone == bm->cur.zone &&
+ ((pfn - zone->start_pfn) & ~BM_BLOCK_MASK) == bm->cur.node_pfn)
goto node_found;
node = zone->rtree;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 209434d..8487e27 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -423,6 +423,7 @@ static u32 log_next_idx;
/* the next printk record to write to the console */
static u64 console_seq;
static u32 console_idx;
+static u64 exclusive_console_stop_seq;
/* the next printk record to read after the last 'clear' command */
static u64 clear_seq;
@@ -437,6 +438,7 @@ static u32 clear_idx;
/* record buffer */
#define LOG_ALIGN __alignof__(struct printk_log)
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+#define LOG_BUF_LEN_MAX (u32)(1 << 31)
static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
@@ -1037,18 +1039,23 @@ void log_buf_vmcoreinfo_setup(void)
static unsigned long __initdata new_log_buf_len;
/* we practice scaling the ring buffer by powers of 2 */
-static void __init log_buf_len_update(unsigned size)
+static void __init log_buf_len_update(u64 size)
{
+ if (size > (u64)LOG_BUF_LEN_MAX) {
+ size = (u64)LOG_BUF_LEN_MAX;
+ pr_err("log_buf over 2G is not supported.\n");
+ }
+
if (size)
size = roundup_pow_of_two(size);
if (size > log_buf_len)
- new_log_buf_len = size;
+ new_log_buf_len = (unsigned long)size;
}
/* save requested log_buf_len since it's too early to process it */
static int __init log_buf_len_setup(char *str)
{
- unsigned int size;
+ u64 size;
if (!str)
return -EINVAL;
@@ -1098,7 +1105,7 @@ void __init setup_log_buf(int early)
{
unsigned long flags;
char *new_log_buf;
- int free;
+ unsigned int free;
if (log_buf != __log_buf)
return;
@@ -1118,7 +1125,7 @@ void __init setup_log_buf(int early)
}
if (unlikely(!new_log_buf)) {
- pr_err("log_buf_len: %ld bytes not available\n",
+ pr_err("log_buf_len: %lu bytes not available\n",
new_log_buf_len);
return;
}
@@ -1131,8 +1138,8 @@ void __init setup_log_buf(int early)
memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
logbuf_unlock_irqrestore(flags);
- pr_info("log_buf_len: %d bytes\n", log_buf_len);
- pr_info("early log buf free: %d(%d%%)\n",
+ pr_info("log_buf_len: %u bytes\n", log_buf_len);
+ pr_info("early log buf free: %u(%u%%)\n",
free, (free * 100) / __LOG_BUF_LEN);
}
@@ -1894,8 +1901,9 @@ asmlinkage int vprintk_emit(int facility, int level,
const char *fmt, va_list args)
{
int printed_len;
- bool in_sched = false;
+ bool in_sched = false, pending_output;
unsigned long flags;
+ u64 curr_log_seq;
if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
@@ -1907,11 +1915,13 @@ asmlinkage int vprintk_emit(int facility, int level,
/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
+ curr_log_seq = log_next_seq;
printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
+ pending_output = (curr_log_seq != log_next_seq);
logbuf_unlock_irqrestore(flags);
/* If called from the scheduler, we can not call up(). */
- if (!in_sched) {
+ if (!in_sched && pending_output) {
/*
* Disable preemption to avoid being preempted while holding
* console_sem which would prevent anyone from printing to
@@ -1928,7 +1938,8 @@ asmlinkage int vprintk_emit(int facility, int level,
preempt_enable();
}
- wake_up_klogd();
+ if (pending_output)
+ wake_up_klogd();
return printed_len;
}
EXPORT_SYMBOL(vprintk_emit);
@@ -2014,6 +2025,7 @@ static u64 syslog_seq;
static u32 syslog_idx;
static u64 console_seq;
static u32 console_idx;
+static u64 exclusive_console_stop_seq;
static u64 log_first_seq;
static u32 log_first_idx;
static u64 log_next_seq;
@@ -2360,8 +2372,9 @@ void console_unlock(void)
printk_safe_enter_irqsave(flags);
raw_spin_lock(&logbuf_lock);
if (console_seq < log_first_seq) {
- len = sprintf(text, "** %u printk messages dropped **\n",
- (unsigned)(log_first_seq - console_seq));
+ len = sprintf(text,
+ "** %llu printk messages dropped **\n",
+ log_first_seq - console_seq);
/* messages are gone, move to first one */
console_seq = log_first_seq;
@@ -2385,6 +2398,12 @@ void console_unlock(void)
goto skip;
}
+ /* Output to all consoles once old messages replayed. */
+ if (unlikely(exclusive_console &&
+ console_seq >= exclusive_console_stop_seq)) {
+ exclusive_console = NULL;
+ }
+
len += msg_print_text(msg,
console_msg_format & MSG_FORMAT_SYSLOG,
text + len,
@@ -2427,10 +2446,6 @@ void console_unlock(void)
console_locked = 0;
- /* Release the exclusive_console once it is used */
- if (unlikely(exclusive_console))
- exclusive_console = NULL;
-
raw_spin_unlock(&logbuf_lock);
up_console_sem();
@@ -2708,13 +2723,18 @@ void register_console(struct console *newcon)
logbuf_lock_irqsave(flags);
console_seq = syslog_seq;
console_idx = syslog_idx;
- logbuf_unlock_irqrestore(flags);
/*
* We're about to replay the log buffer. Only do this to the
* just-registered console to avoid excessive message spam to
* the already-registered consoles.
+ *
+ * Set exclusive_console with disabled interrupts to reduce
+ * race window with eventual console_flush_on_panic() that
+ * ignores console_lock.
*/
exclusive_console = newcon;
+ exclusive_console_stop_seq = console_seq;
+ logbuf_unlock_irqrestore(flags);
}
console_unlock();
console_sysfs_notify();
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 26f19e1..0e34c8d 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3354,54 +3354,16 @@ static inline void sched_tick_stop(int cpu) { }
#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
defined(CONFIG_TRACE_PREEMPT_TOGGLE))
/*
- * preemptoff stack tracing threshold in ns.
- * default: 1ms
- */
-unsigned int sysctl_preemptoff_tracing_threshold_ns = 1000000UL;
-
-struct preempt_store {
- u64 ts;
- unsigned long caddr[4];
- bool irqs_disabled;
-};
-
-DEFINE_PER_CPU(struct preempt_store, the_ps);
-
-/*
- * This is only called from __schedule() upon context switch.
- *
- * schedule() calls __schedule() with preemption disabled.
- * if we had entered idle and exiting idle now, reset the preemption
- * tracking otherwise we may think preemption is disabled the whole time
- * when the non idle task re-enables the preemption in schedule().
- */
-static inline void preempt_latency_reset(void)
-{
- if (is_idle_task(this_rq()->curr))
- this_cpu_ptr(&the_ps)->ts = 0;
-}
-
-/*
* If the value passed in is equal to the current preempt count
* then we just disabled preemption. Start timing the latency.
*/
static inline void preempt_latency_start(int val)
{
- int cpu = raw_smp_processor_id();
- struct preempt_store *ps = &per_cpu(the_ps, cpu);
-
if (preempt_count() == val) {
unsigned long ip = get_lock_parent_ip();
#ifdef CONFIG_DEBUG_PREEMPT
current->preempt_disable_ip = ip;
#endif
- ps->ts = sched_clock();
- ps->caddr[0] = CALLER_ADDR0;
- ps->caddr[1] = CALLER_ADDR1;
- ps->caddr[2] = CALLER_ADDR2;
- ps->caddr[3] = CALLER_ADDR3;
- ps->irqs_disabled = irqs_disabled();
-
trace_preempt_off(CALLER_ADDR0, ip);
}
}
@@ -3435,19 +3397,6 @@ NOKPROBE_SYMBOL(preempt_count_add);
static inline void preempt_latency_stop(int val)
{
if (preempt_count() == val) {
- struct preempt_store *ps = &per_cpu(the_ps,
- raw_smp_processor_id());
- u64 delta = ps->ts ? (sched_clock() - ps->ts) : 0;
-
- /*
- * Trace preempt disable stack if preemption
- * is disabled for more than the threshold.
- */
- if (delta > sysctl_preemptoff_tracing_threshold_ns)
- trace_sched_preempt_disable(delta, ps->irqs_disabled,
- ps->caddr[0], ps->caddr[1],
- ps->caddr[2], ps->caddr[3]);
- ps->ts = 0;
trace_preempt_on(CALLER_ADDR0, get_lock_parent_ip());
}
}
@@ -3477,7 +3426,6 @@ NOKPROBE_SYMBOL(preempt_count_sub);
#else
static inline void preempt_latency_start(int val) { }
static inline void preempt_latency_stop(int val) { }
-static inline void preempt_latency_reset(void) { }
#endif
static inline unsigned long get_preempt_disable_ip(struct task_struct *p)
@@ -3701,7 +3649,6 @@ static void __sched notrace __schedule(bool preempt)
if (!prev->on_rq)
prev->last_sleep_ts = wallclock;
- preempt_latency_reset();
update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
rq->nr_switches++;
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 1605829..09a7aa5 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -769,6 +769,13 @@ static bool adjustment_possible(const struct cluster_data *cluster,
cluster->nr_isolated_cpus));
}
+static bool need_all_cpus(const struct cluster_data *cluster)
+{
+
+ return (is_min_capacity_cpu(cluster->first_cpu) &&
+ sched_ravg_window < DEFAULT_SCHED_RAVG_WINDOW);
+}
+
static bool eval_need(struct cluster_data *cluster)
{
unsigned long flags;
@@ -784,7 +791,7 @@ static bool eval_need(struct cluster_data *cluster)
spin_lock_irqsave(&state_lock, flags);
- if (cluster->boost || !cluster->enable) {
+ if (cluster->boost || !cluster->enable || need_all_cpus(cluster)) {
need_cpus = cluster->max_cpus;
} else {
cluster->active_cpus = get_active_cpu_count(cluster);
diff --git a/kernel/sched/cpufreq.c b/kernel/sched/cpufreq.c
index 5e54cbc..42ce32a 100644
--- a/kernel/sched/cpufreq.c
+++ b/kernel/sched/cpufreq.c
@@ -8,6 +8,8 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/cpufreq.h>
+
#include "sched.h"
DEFINE_PER_CPU(struct update_util_data *, cpufreq_update_util_data);
@@ -60,3 +62,19 @@ void cpufreq_remove_update_util_hook(int cpu)
rcu_assign_pointer(per_cpu(cpufreq_update_util_data, cpu), NULL);
}
EXPORT_SYMBOL_GPL(cpufreq_remove_update_util_hook);
+
+/**
+ * cpufreq_this_cpu_can_update - Check if cpufreq policy can be updated.
+ * @policy: cpufreq policy to check.
+ *
+ * Return 'true' if:
+ * - the local and remote CPUs share @policy,
+ * - dvfs_possible_from_any_cpu is set in @policy and the local CPU is not going
+ * offline (in which case it is not expected to run cpufreq updates any more).
+ */
+bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy)
+{
+ return cpumask_test_cpu(smp_processor_id(), policy->cpus) ||
+ (policy->dvfs_possible_from_any_cpu &&
+ rcu_dereference_sched(*this_cpu_ptr(&cpufreq_update_util_data)));
+}
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index e9da7954..6d84301 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -106,12 +106,10 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
* by the hardware, as calculating the frequency is pointless if
* we cannot in fact act on it.
*
- * For the slow switching platforms, the kthread is always scheduled on
- * the right set of CPUs and any CPU can find the next frequency and
- * schedule the kthread.
+ * This is needed on the slow switching platforms too to prevent CPUs
+ * going offline from leaving stale IRQ work items behind.
*/
- if (sg_policy->policy->fast_switch_enabled &&
- !cpufreq_this_cpu_can_update(sg_policy->policy))
+ if (!cpufreq_this_cpu_can_update(sg_policy->policy))
return false;
if (unlikely(sg_policy->limits_changed)) {
@@ -267,7 +265,7 @@ static void sugov_deferred_update(struct sugov_policy *sg_policy, u64 time,
if (use_pelt())
sg_policy->work_in_progress = true;
- sched_irq_work_queue(&sg_policy->irq_work);
+ irq_work_queue(&sg_policy->irq_work);
}
#define TARGET_LOAD 80
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 8805e18..d2872233 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -763,7 +763,7 @@ void vtime_account_system(struct task_struct *tsk)
write_seqcount_begin(&vtime->seqcount);
/* We might have scheduled out from guest path */
- if (current->flags & PF_VCPU)
+ if (tsk->flags & PF_VCPU)
vtime_account_guest(tsk, vtime);
else
__vtime_account_system(tsk, vtime);
@@ -806,7 +806,7 @@ void vtime_guest_enter(struct task_struct *tsk)
*/
write_seqcount_begin(&vtime->seqcount);
__vtime_account_system(tsk, vtime);
- current->flags |= PF_VCPU;
+ tsk->flags |= PF_VCPU;
write_seqcount_end(&vtime->seqcount);
}
EXPORT_SYMBOL_GPL(vtime_guest_enter);
@@ -817,7 +817,7 @@ void vtime_guest_exit(struct task_struct *tsk)
write_seqcount_begin(&vtime->seqcount);
vtime_account_guest(tsk, vtime);
- current->flags &= ~PF_VCPU;
+ tsk->flags &= ~PF_VCPU;
write_seqcount_end(&vtime->seqcount);
}
EXPORT_SYMBOL_GPL(vtime_guest_exit);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d1f660c..5d46f93 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4524,23 +4524,16 @@ static inline u64 sched_cfs_bandwidth_slice(void)
}
/*
- * Replenish runtime according to assigned quota and update expiration time.
- * We use sched_clock_cpu directly instead of rq->clock to avoid adding
- * additional synchronization around rq->lock.
+ * Replenish runtime according to assigned quota. We use sched_clock_cpu
+ * directly instead of rq->clock to avoid adding additional synchronization
+ * around rq->lock.
*
* requires cfs_b->lock
*/
void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b)
{
- u64 now;
-
- if (cfs_b->quota == RUNTIME_INF)
- return;
-
- now = sched_clock_cpu(smp_processor_id());
- cfs_b->runtime = cfs_b->quota;
- cfs_b->runtime_expires = now + ktime_to_ns(cfs_b->period);
- cfs_b->expires_seq++;
+ if (cfs_b->quota != RUNTIME_INF)
+ cfs_b->runtime = cfs_b->quota;
}
static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg)
@@ -4562,8 +4555,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
{
struct task_group *tg = cfs_rq->tg;
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(tg);
- u64 amount = 0, min_amount, expires;
- int expires_seq;
+ u64 amount = 0, min_amount;
/* note: this is a positive sum as runtime_remaining <= 0 */
min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining;
@@ -4580,61 +4572,17 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
cfs_b->idle = 0;
}
}
- expires_seq = cfs_b->expires_seq;
- expires = cfs_b->runtime_expires;
raw_spin_unlock(&cfs_b->lock);
cfs_rq->runtime_remaining += amount;
- /*
- * we may have advanced our local expiration to account for allowed
- * spread between our sched_clock and the one on which runtime was
- * issued.
- */
- if (cfs_rq->expires_seq != expires_seq) {
- cfs_rq->expires_seq = expires_seq;
- cfs_rq->runtime_expires = expires;
- }
return cfs_rq->runtime_remaining > 0;
}
-/*
- * Note: This depends on the synchronization provided by sched_clock and the
- * fact that rq->clock snapshots this value.
- */
-static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq)
-{
- struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
-
- /* if the deadline is ahead of our clock, nothing to do */
- if (likely((s64)(rq_clock(rq_of(cfs_rq)) - cfs_rq->runtime_expires) < 0))
- return;
-
- if (cfs_rq->runtime_remaining < 0)
- return;
-
- /*
- * If the local deadline has passed we have to consider the
- * possibility that our sched_clock is 'fast' and the global deadline
- * has not truly expired.
- *
- * Fortunately we can check determine whether this the case by checking
- * whether the global deadline(cfs_b->expires_seq) has advanced.
- */
- if (cfs_rq->expires_seq == cfs_b->expires_seq) {
- /* extend local deadline, drift is bounded above by 2 ticks */
- cfs_rq->runtime_expires += TICK_NSEC;
- } else {
- /* global deadline is ahead, expiration has passed */
- cfs_rq->runtime_remaining = 0;
- }
-}
-
static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
{
/* dock delta_exec before expiring quota (as it could span periods) */
cfs_rq->runtime_remaining -= delta_exec;
- expire_cfs_rq_runtime(cfs_rq);
if (likely(cfs_rq->runtime_remaining > 0))
return;
@@ -4826,8 +4774,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
resched_curr(rq);
}
-static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
- u64 remaining, u64 expires)
+static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, u64 remaining)
{
struct cfs_rq *cfs_rq;
u64 runtime;
@@ -4852,7 +4799,6 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
remaining -= runtime;
cfs_rq->runtime_remaining += runtime;
- cfs_rq->runtime_expires = expires;
/* we check whether we're throttled above */
if (cfs_rq->runtime_remaining > 0)
@@ -4877,7 +4823,7 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
*/
static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
{
- u64 runtime, runtime_expires;
+ u64 runtime;
int throttled;
/* no need to continue the timer with no bandwidth constraint */
@@ -4905,8 +4851,6 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
/* account preceding periods in which throttling occurred */
cfs_b->nr_throttled += overrun;
- runtime_expires = cfs_b->runtime_expires;
-
/*
* This check is repeated as we are holding onto the new bandwidth while
* we unthrottle. This can potentially race with an unthrottled group
@@ -4919,8 +4863,7 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
cfs_b->distribute_running = 1;
raw_spin_unlock(&cfs_b->lock);
/* we can't nest cfs_b->lock while distributing bandwidth */
- runtime = distribute_cfs_runtime(cfs_b, runtime,
- runtime_expires);
+ runtime = distribute_cfs_runtime(cfs_b, runtime);
raw_spin_lock(&cfs_b->lock);
cfs_b->distribute_running = 0;
@@ -4997,8 +4940,7 @@ static void __return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
return;
raw_spin_lock(&cfs_b->lock);
- if (cfs_b->quota != RUNTIME_INF &&
- cfs_rq->runtime_expires == cfs_b->runtime_expires) {
+ if (cfs_b->quota != RUNTIME_INF) {
cfs_b->runtime += slack_runtime;
/* we are under rq->lock, defer unthrottling using a timer */
@@ -5030,7 +4972,6 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
{
u64 runtime = 0, slice = sched_cfs_bandwidth_slice();
- u64 expires;
/* confirm we're still not at a refresh boundary */
raw_spin_lock(&cfs_b->lock);
@@ -5047,7 +4988,6 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
if (cfs_b->quota != RUNTIME_INF && cfs_b->runtime > slice)
runtime = cfs_b->runtime;
- expires = cfs_b->runtime_expires;
if (runtime)
cfs_b->distribute_running = 1;
@@ -5056,11 +4996,10 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
if (!runtime)
return;
- runtime = distribute_cfs_runtime(cfs_b, runtime, expires);
+ runtime = distribute_cfs_runtime(cfs_b, runtime);
raw_spin_lock(&cfs_b->lock);
- if (expires == cfs_b->runtime_expires)
- cfs_b->runtime -= min(runtime, cfs_b->runtime);
+ cfs_b->runtime -= min(runtime, cfs_b->runtime);
cfs_b->distribute_running = 0;
raw_spin_unlock(&cfs_b->lock);
}
@@ -5155,20 +5094,28 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
if (++count > 3) {
u64 new, old = ktime_to_ns(cfs_b->period);
- new = (old * 147) / 128; /* ~115% */
- new = min(new, max_cfs_quota_period);
+ /*
+ * Grow period by a factor of 2 to avoid losing precision.
+ * Precision loss in the quota/period ratio can cause __cfs_schedulable
+ * to fail.
+ */
+ new = old * 2;
+ if (new < max_cfs_quota_period) {
+ cfs_b->period = ns_to_ktime(new);
+ cfs_b->quota *= 2;
- cfs_b->period = ns_to_ktime(new);
-
- /* since max is 1s, this is limited to 1e9^2, which fits in u64 */
- cfs_b->quota *= new;
- cfs_b->quota = div64_u64(cfs_b->quota, old);
-
- pr_warn_ratelimited(
- "cfs_period_timer[cpu%d]: period too short, scaling up (new cfs_period_us %lld, cfs_quota_us = %lld)\n",
- smp_processor_id(),
- div_u64(new, NSEC_PER_USEC),
- div_u64(cfs_b->quota, NSEC_PER_USEC));
+ pr_warn_ratelimited(
+ "cfs_period_timer[cpu%d]: period too short, scaling up (new cfs_period_us = %lld, cfs_quota_us = %lld)\n",
+ smp_processor_id(),
+ div_u64(new, NSEC_PER_USEC),
+ div_u64(cfs_b->quota, NSEC_PER_USEC));
+ } else {
+ pr_warn_ratelimited(
+ "cfs_period_timer[cpu%d]: period too short, but cannot scale up without losing precision (cfs_period_us = %lld, cfs_quota_us = %lld)\n",
+ smp_processor_id(),
+ div_u64(old, NSEC_PER_USEC),
+ div_u64(cfs_b->quota, NSEC_PER_USEC));
+ }
/* reset count so we don't come right back in here */
count = 0;
@@ -5207,17 +5154,13 @@ static void init_cfs_rq_runtime(struct cfs_rq *cfs_rq)
void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
{
- u64 overrun;
-
lockdep_assert_held(&cfs_b->lock);
if (cfs_b->period_active)
return;
cfs_b->period_active = 1;
- overrun = hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period);
- cfs_b->runtime_expires += (overrun + 1) * ktime_to_ns(cfs_b->period);
- cfs_b->expires_seq++;
+ hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period);
hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED);
}
@@ -10763,13 +10706,22 @@ static int load_balance(int this_cpu, struct rq *this_rq,
sd->nr_balance_failed = 0;
out_one_pinned:
+ ld_moved = 0;
+
+ /*
+ * idle_balance() disregards balance intervals, so we could repeatedly
+ * reach this code, which would lead to balance_interval skyrocketting
+ * in a short amount of time. Skip the balance_interval increase logic
+ * to avoid that.
+ */
+ if (env.idle == CPU_NEWLY_IDLE)
+ goto out;
+
/* tune up the balancing interval */
if (((env.flags & LBF_ALL_PINNED) &&
sd->balance_interval < MAX_PINNED_INTERVAL) ||
(sd->balance_interval < sd->max_interval))
sd->balance_interval *= 2;
-
- ld_moved = 0;
out:
trace_sched_load_balance(this_cpu, idle, *continue_balancing,
group ? group->cpumask[0] : 0,
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 102bda8..979ed34 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -92,7 +92,6 @@ extern unsigned int sched_capacity_margin_up[NR_CPUS];
extern unsigned int sched_capacity_margin_down[NR_CPUS];
struct sched_walt_cpu_load {
- unsigned long prev_window_util;
unsigned long nl;
unsigned long pl;
bool rtgb_active;
@@ -401,8 +400,6 @@ struct cfs_bandwidth {
u64 quota;
u64 runtime;
s64 hierarchical_quota;
- u64 runtime_expires;
- int expires_seq;
short idle;
short period_active;
@@ -620,14 +617,11 @@ struct cfs_rq {
struct list_head leaf_cfs_rq_list;
struct task_group *tg; /* group that "owns" this runqueue */
+#ifdef CONFIG_CFS_BANDWIDTH
#ifdef CONFIG_SCHED_WALT
struct walt_sched_stats walt_stats;
#endif
-
-#ifdef CONFIG_CFS_BANDWIDTH
int runtime_enabled;
- int expires_seq;
- u64 runtime_expires;
s64 runtime_remaining;
u64 throttled_clock;
@@ -1653,7 +1647,7 @@ static const_debug __maybe_unused unsigned int sysctl_sched_features =
0;
#undef SCHED_FEAT
-#define sched_feat(x) (sysctl_sched_features & (1UL << __SCHED_FEAT_##x))
+#define sched_feat(x) !!(sysctl_sched_features & (1UL << __SCHED_FEAT_##x))
#endif /* SCHED_DEBUG && CONFIG_JUMP_LABEL */
@@ -2048,7 +2042,7 @@ cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load);
#define sched_ravg_window TICK_NSEC
static inline u64 sched_ktime_clock(void)
{
- return 0;
+ return sched_clock();
}
#endif
@@ -2510,16 +2504,20 @@ DECLARE_PER_CPU(struct update_util_data *, cpufreq_update_util_data);
static inline void cpufreq_update_util(struct rq *rq, unsigned int flags)
{
struct update_util_data *data;
+ u64 clock;
#ifdef CONFIG_SCHED_WALT
if (!(flags & SCHED_CPUFREQ_WALT))
return;
+ clock = sched_ktime_clock();
+#else
+ clock = rq_clock(rq);
#endif
data = rcu_dereference_sched(*per_cpu_ptr(&cpufreq_update_util_data,
cpu_of(rq)));
if (data)
- data->func(data, sched_ktime_clock(), flags);
+ data->func(data, clock, flags);
}
#else
static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {}
@@ -3091,13 +3089,3 @@ struct sched_avg_stats {
int nr_scaled;
};
extern void sched_get_nr_running_avg(struct sched_avg_stats *stats);
-
-#ifdef CONFIG_SMP
-static inline void sched_irq_work_queue(struct irq_work *work)
-{
- if (likely(cpu_online(raw_smp_processor_id())))
- irq_work_queue(work);
- else
- irq_work_queue_on(work, cpumask_any(cpu_online_mask));
-}
-#endif
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index ffdaf2f..d956d86 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1537,7 +1537,7 @@ void sched_init_numa(void)
int level = 0;
int i, j, k;
- sched_domains_numa_distance = kzalloc(sizeof(int) * nr_node_ids, GFP_KERNEL);
+ sched_domains_numa_distance = kzalloc(sizeof(int) * (nr_node_ids + 1), GFP_KERNEL);
if (!sched_domains_numa_distance)
return;
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 9059baf..85a9f2a 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -94,22 +94,6 @@ static void release_rq_locks_irqrestore(const cpumask_t *cpus,
local_irq_restore(*flags);
}
-#ifdef CONFIG_HZ_300
-/*
- * Tick interval becomes to 3333333 due to
- * rounding error when HZ=300.
- */
-#define MIN_SCHED_RAVG_WINDOW (3333333 * 6)
-#else
-/* Min window size (in ns) = 20ms */
-#define MIN_SCHED_RAVG_WINDOW 20000000
-#endif
-
-/* Max window size (in ns) = 1s */
-#define MAX_SCHED_RAVG_WINDOW 1000000000
-
-#define NR_WINDOWS_PER_SEC (NSEC_PER_SEC / MIN_SCHED_RAVG_WINDOW)
-
__read_mostly unsigned int sysctl_sched_cpu_high_irqload = TICK_NSEC;
unsigned int sysctl_sched_walt_rotate_big_tasks;
@@ -131,8 +115,8 @@ static unsigned int display_sched_ravg_window_nr_ticks =
unsigned int sysctl_sched_dynamic_ravg_window_enable = (HZ == 250);
/* Window size (in ns) */
-__read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
-__read_mostly unsigned int new_sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
+__read_mostly unsigned int sched_ravg_window = DEFAULT_SCHED_RAVG_WINDOW;
+__read_mostly unsigned int new_sched_ravg_window = DEFAULT_SCHED_RAVG_WINDOW;
static DEFINE_SPINLOCK(sched_ravg_window_lock);
u64 sched_ravg_window_change_time;
@@ -172,11 +156,11 @@ unsigned int __read_mostly sched_disable_window_stats;
* The entire range of load from 0 to sched_ravg_window needs to be covered
* in NUM_LOAD_INDICES number of buckets. Therefore the size of each bucket
* is given by sched_ravg_window / NUM_LOAD_INDICES. Since the default value
- * of sched_ravg_window is MIN_SCHED_RAVG_WINDOW, use that to compute
+ * of sched_ravg_window is DEFAULT_SCHED_RAVG_WINDOW, use that to compute
* sched_load_granule.
*/
__read_mostly unsigned int sched_load_granule =
- MIN_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES;
+ DEFAULT_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES;
/* Size of bitmaps maintained to track top tasks */
static const unsigned int top_tasks_bitmap_size =
BITS_TO_LONGS(NUM_LOAD_INDICES + 1) * sizeof(unsigned long);
@@ -193,7 +177,7 @@ static int __init set_sched_ravg_window(char *str)
get_option(&str, &window_size);
- if (window_size < MIN_SCHED_RAVG_WINDOW ||
+ if (window_size < DEFAULT_SCHED_RAVG_WINDOW ||
window_size > MAX_SCHED_RAVG_WINDOW) {
WARN_ON(1);
return -EINVAL;
@@ -218,6 +202,14 @@ early_param("sched_predl", set_sched_predl);
__read_mostly unsigned int walt_scale_demand_divisor;
#define scale_demand(d) ((d)/walt_scale_demand_divisor)
+static inline void walt_irq_work_queue(struct irq_work *work)
+{
+ if (likely(cpu_online(raw_smp_processor_id())))
+ irq_work_queue(work);
+ else
+ irq_work_queue_on(work, cpumask_any(cpu_online_mask));
+}
+
void inc_rq_walt_stats(struct rq *rq, struct task_struct *p)
{
inc_nr_big_task(&rq->walt_stats, p);
@@ -583,7 +575,6 @@ __cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load)
nl = div64_u64(nl * (100 + boost), walt_cpu_util_freq_divisor);
- walt_load->prev_window_util = util;
walt_load->nl = nl;
walt_load->pl = pl;
walt_load->ws = walt_load_reported_window;
@@ -618,7 +609,6 @@ cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load)
mpct = 100;
util = ADJUSTED_ASYM_CAP_CPU_UTIL(util, util_other, mpct);
- walt_load->prev_window_util = util;
walt_load->nl = ADJUSTED_ASYM_CAP_CPU_UTIL(walt_load->nl, wl_other.nl,
mpct);
@@ -987,7 +977,7 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
if (!same_freq_domain(new_cpu, task_cpu(p))) {
src_rq->notif_pending = true;
dest_rq->notif_pending = true;
- sched_irq_work_queue(&walt_migration_irq_work);
+ walt_irq_work_queue(&walt_migration_irq_work);
}
if (is_ed_enabled()) {
@@ -2077,7 +2067,7 @@ static inline void run_walt_irq_work(u64 old_window_start, struct rq *rq)
result = atomic64_cmpxchg(&walt_irq_work_lastq_ws, old_window_start,
rq->window_start);
if (result == old_window_start)
- sched_irq_work_queue(&walt_cpufreq_irq_work);
+ walt_irq_work_queue(&walt_cpufreq_irq_work);
}
/* Reflect task activity on its demand and cpu's busy time statistics */
diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h
index 96762372..4089158 100644
--- a/kernel/sched/walt.h
+++ b/kernel/sched/walt.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __WALT_H
@@ -13,6 +13,21 @@
#define MAX_NR_CLUSTERS 3
+#ifdef CONFIG_HZ_300
+/*
+ * Tick interval becomes to 3333333 due to
+ * rounding error when HZ=300.
+ */
+#define DEFAULT_SCHED_RAVG_WINDOW (3333333 * 6)
+#else
+/* Default window size (in ns) = 20ms */
+#define DEFAULT_SCHED_RAVG_WINDOW 20000000
+#endif
+
+/* Max window size (in ns) = 1s */
+#define MAX_SCHED_RAVG_WINDOW 1000000000
+#define NR_WINDOWS_PER_SEC (NSEC_PER_SEC / DEFAULT_SCHED_RAVG_WINDOW)
+
#define WINDOW_STATS_RECENT 0
#define WINDOW_STATS_MAX 1
#define WINDOW_STATS_MAX_RECENT_AVG 2
@@ -364,6 +379,15 @@ static inline void walt_rq_dump(int cpu)
struct task_struct *tsk = cpu_curr(cpu);
int i;
+ /*
+ * Increment the task reference so that it can't be
+ * freed on a remote CPU. Since we are going to
+ * enter panic, there is no need to decrement the
+ * task reference. Decrementing the task reference
+ * can't be done in atomic context, especially with
+ * rq locks held.
+ */
+ get_task_struct(tsk);
printk_deferred("CPU:%d nr_running:%u current: %d (%s)\n",
cpu, rq->nr_running, tsk->pid, tsk->comm);
@@ -388,7 +412,8 @@ static inline void walt_rq_dump(int cpu)
printk_deferred("rq->load_subs[%d].new_subs=%llu)\n", i,
rq->load_subs[i].new_subs);
}
- walt_task_dump(tsk);
+ if (!exiting_task(tsk))
+ walt_task_dump(tsk);
SCHED_PRINT(sched_capacity_margin_up[cpu]);
SCHED_PRINT(sched_capacity_margin_down[cpu]);
}
diff --git a/kernel/signal.c b/kernel/signal.c
index c5f2cf1..83f85af 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -80,6 +80,10 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
handler = sig_handler(t, sig);
+ /* SIGKILL and SIGSTOP may not be sent to the global init */
+ if (unlikely(is_global_init(t) && sig_kernel_only(sig)))
+ return true;
+
if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) &&
handler == SIG_DFL && !(force && sig_kernel_only(sig)))
return true;
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 9270645..eedc8ee 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -523,7 +523,9 @@ static void tasklet_action_common(struct softirq_action *a,
if (!test_and_clear_bit(TASKLET_STATE_SCHED,
&t->state))
BUG();
+ trace_tasklet_entry(t->func);
t->func(t->data);
+ trace_tasklet_exit(t->func);
tasklet_unlock(t);
continue;
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 04b713e..410e3c1 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -336,7 +336,7 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
-#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_DEBUG_PREEMPT)
+#if defined(CONFIG_PREEMPT_TRACER) && defined(CONFIG_PREEMPTIRQ_EVENTS)
{
.procname = "preemptoff_tracing_threshold_ns",
.data = &sysctl_preemptoff_tracing_threshold_ns,
@@ -1715,7 +1715,7 @@ static struct ctl_table vm_table[] = {
.procname = "drop_caches",
.data = &sysctl_drop_caches,
.maxlen = sizeof(int),
- .mode = 0644,
+ .mode = 0200,
.proc_handler = drop_caches_sysctl_handler,
.extra1 = &one,
.extra2 = &four,
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 2dac07a..cd709fe 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -887,25 +887,33 @@ static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk)
{
struct signal_struct *sig = tsk->signal;
- struct taskstats *stats;
+ struct taskstats *stats_new, *stats;
- if (sig->stats || thread_group_empty(tsk))
- goto ret;
+ /* Pairs with smp_store_release() below. */
+ stats = smp_load_acquire(&sig->stats);
+ if (stats || thread_group_empty(tsk))
+ return stats;
/* No problem if kmem_cache_zalloc() fails */
- stats = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL);
+ stats_new = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL);
spin_lock_irq(&tsk->sighand->siglock);
- if (!sig->stats) {
- sig->stats = stats;
- stats = NULL;
+ stats = sig->stats;
+ if (!stats) {
+ /*
+ * Pairs with smp_store_release() above and order the
+ * kmem_cache_zalloc().
+ */
+ smp_store_release(&sig->stats, stats_new);
+ stats = stats_new;
+ stats_new = NULL;
}
spin_unlock_irq(&tsk->sighand->siglock);
- if (stats)
- kmem_cache_free(taskstats_cache, stats);
-ret:
- return sig->stats;
+ if (stats_new)
+ kmem_cache_free(taskstats_cache, stats_new);
+
+ return stats;
}
/* Send pid data out on exit */
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 4d506f6..3b8e78b 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -963,7 +963,8 @@ static int enqueue_hrtimer(struct hrtimer *timer,
base->cpu_base->active_bases |= 1 << base->index;
- timer->state |= HRTIMER_STATE_ENQUEUED;
+ /* Pairs with the lockless read in hrtimer_is_queued() */
+ WRITE_ONCE(timer->state, timer->state | HRTIMER_STATE_ENQUEUED);
return timerqueue_add(&base->active, &timer->node);
}
@@ -1006,7 +1007,7 @@ static void __remove_hrtimer(struct hrtimer *timer,
* We need to preserve PINNED state here, otherwise we may end up
* migrating pinned hrtimers as well.
*/
- timer->state = newstate | (timer->state & HRTIMER_STATE_PINNED);
+ WRITE_ONCE(timer->state, newstate | (timer->state & HRTIMER_STATE_PINNED));
}
/*
@@ -1015,8 +1016,9 @@ static void __remove_hrtimer(struct hrtimer *timer,
static inline int
remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart)
{
- if (hrtimer_is_queued(timer)) {
- u8 state = timer->state;
+ u8 state = timer->state;
+
+ if (state & HRTIMER_STATE_ENQUEUED) {
int reprogram;
/*
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index fe56c4e..c8a8501 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -27,8 +27,6 @@
#include "posix-timers.h"
-static void delete_clock(struct kref *kref);
-
/*
* Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
*/
@@ -138,7 +136,7 @@ static int posix_clock_open(struct inode *inode, struct file *fp)
err = 0;
if (!err) {
- kref_get(&clk->kref);
+ get_device(clk->dev);
fp->private_data = clk;
}
out:
@@ -154,7 +152,7 @@ static int posix_clock_release(struct inode *inode, struct file *fp)
if (clk->ops.release)
err = clk->ops.release(clk);
- kref_put(&clk->kref, delete_clock);
+ put_device(clk->dev);
fp->private_data = NULL;
@@ -174,38 +172,35 @@ static const struct file_operations posix_clock_file_operations = {
#endif
};
-int posix_clock_register(struct posix_clock *clk, dev_t devid)
+int posix_clock_register(struct posix_clock *clk, struct device *dev)
{
int err;
- kref_init(&clk->kref);
init_rwsem(&clk->rwsem);
cdev_init(&clk->cdev, &posix_clock_file_operations);
+ err = cdev_device_add(&clk->cdev, dev);
+ if (err) {
+ pr_err("%s unable to add device %d:%d\n",
+ dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
+ return err;
+ }
clk->cdev.owner = clk->ops.owner;
- err = cdev_add(&clk->cdev, devid, 1);
+ clk->dev = dev;
- return err;
+ return 0;
}
EXPORT_SYMBOL_GPL(posix_clock_register);
-static void delete_clock(struct kref *kref)
-{
- struct posix_clock *clk = container_of(kref, struct posix_clock, kref);
-
- if (clk->release)
- clk->release(clk);
-}
-
void posix_clock_unregister(struct posix_clock *clk)
{
- cdev_del(&clk->cdev);
+ cdev_device_del(&clk->cdev, clk->dev);
down_write(&clk->rwsem);
clk->zombie = true;
up_write(&clk->rwsem);
- kref_put(&clk->kref, delete_clock);
+ put_device(clk->dev);
}
EXPORT_SYMBOL_GPL(posix_clock_unregister);
diff --git a/kernel/time/time.c b/kernel/time/time.c
index be057d6..f7d4fa5 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -144,9 +144,11 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
struct timezone __user *, tz)
{
if (likely(tv != NULL)) {
- struct timeval ktv;
- do_gettimeofday(&ktv);
- if (copy_to_user(tv, &ktv, sizeof(ktv)))
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+ if (put_user(ts.tv_sec, &tv->tv_sec) ||
+ put_user(ts.tv_nsec / 1000, &tv->tv_usec))
return -EFAULT;
}
if (unlikely(tz != NULL)) {
@@ -227,10 +229,11 @@ COMPAT_SYSCALL_DEFINE2(gettimeofday, struct compat_timeval __user *, tv,
struct timezone __user *, tz)
{
if (tv) {
- struct timeval ktv;
+ struct timespec64 ts;
- do_gettimeofday(&ktv);
- if (compat_put_timeval(&ktv, tv))
+ ktime_get_real_ts64(&ts);
+ if (put_user(ts.tv_sec, &tv->tv_sec) ||
+ put_user(ts.tv_nsec / 1000, &tv->tv_usec))
return -EFAULT;
}
if (tz) {
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index c2708e1..81ee5b8 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1215,22 +1215,6 @@ int get_device_system_crosststamp(int (*get_time_fn)
EXPORT_SYMBOL_GPL(get_device_system_crosststamp);
/**
- * do_gettimeofday - Returns the time of day in a timeval
- * @tv: pointer to the timeval to be set
- *
- * NOTE: Users should be converted to using getnstimeofday()
- */
-void do_gettimeofday(struct timeval *tv)
-{
- struct timespec64 now;
-
- getnstimeofday64(&now);
- tv->tv_sec = now.tv_sec;
- tv->tv_usec = now.tv_nsec/1000;
-}
-EXPORT_SYMBOL(do_gettimeofday);
-
-/**
* do_settimeofday64 - Sets the time of day.
* @ts: pointer to the timespec64 variable containing the new time
*
@@ -2177,14 +2161,6 @@ void getboottime64(struct timespec64 *ts)
}
EXPORT_SYMBOL_GPL(getboottime64);
-unsigned long get_seconds(void)
-{
- struct timekeeper *tk = &tk_core.timekeeper;
-
- return tk->xtime_sec;
-}
-EXPORT_SYMBOL(get_seconds);
-
void ktime_get_coarse_real_ts64(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index b3ce6a9..0000166 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -555,8 +555,7 @@ static int function_stat_show(struct seq_file *m, void *v)
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- avg = rec->time;
- do_div(avg, rec->counter);
+ avg = div64_ul(rec->time, rec->counter);
if (tracing_thresh && (avg < tracing_thresh))
goto out;
#endif
@@ -582,7 +581,8 @@ static int function_stat_show(struct seq_file *m, void *v)
* Divide only 1000 for ns^2 -> us^2 conversion.
* trace_print_graph_duration will divide 1000 again.
*/
- do_div(stddev, rec->counter * (rec->counter - 1) * 1000);
+ stddev = div64_ul(stddev,
+ rec->counter * (rec->counter - 1) * 1000);
}
trace_seq_init(&s);
diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c
index 90822fa..45dbdbca 100644
--- a/kernel/trace/msm_rtb.c
+++ b/kernel/trace/msm_rtb.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/atomic.h>
@@ -291,6 +291,7 @@ static int msm_rtb_probe(struct platform_device *pdev)
md_entry.virt_addr = (uintptr_t)msm_rtb.rtb;
md_entry.phys_addr = msm_rtb.phys;
md_entry.size = msm_rtb.size;
+ md_entry.id = MINIDUMP_DEFAULT_ID;
if (msm_minidump_add_region(&md_entry))
pr_info("Failed to add RTB in Minidump\n");
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 02439f1..6bd66b6 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4399,7 +4399,7 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
if (mask == TRACE_ITER_RECORD_TGID) {
if (!tgid_map)
- tgid_map = kcalloc(PID_MAX_DEFAULT + 1,
+ tgid_map = kvcalloc(PID_MAX_DEFAULT + 1,
sizeof(*tgid_map),
GFP_KERNEL);
if (!tgid_map) {
@@ -5778,6 +5778,7 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
sizeof(struct trace_iterator) -
offsetof(struct trace_iterator, seq));
cpumask_clear(iter->started);
+ trace_seq_init(&iter->seq);
iter->pos = -1;
trace_event_read_lock();
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 0726ab8..4337193 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2302,11 +2302,11 @@ __trace_early_add_new_event(struct trace_event_call *call,
struct ftrace_module_file_ops;
static void __add_event_to_tracers(struct trace_event_call *call);
-/* Add an additional event_call dynamically */
-int trace_add_event_call(struct trace_event_call *call)
+int trace_add_event_call_nolock(struct trace_event_call *call)
{
int ret;
- mutex_lock(&event_mutex);
+ lockdep_assert_held(&event_mutex);
+
mutex_lock(&trace_types_lock);
ret = __register_event(call, NULL);
@@ -2314,6 +2314,16 @@ int trace_add_event_call(struct trace_event_call *call)
__add_event_to_tracers(call);
mutex_unlock(&trace_types_lock);
+ return ret;
+}
+
+/* Add an additional event_call dynamically */
+int trace_add_event_call(struct trace_event_call *call)
+{
+ int ret;
+
+ mutex_lock(&event_mutex);
+ ret = trace_add_event_call_nolock(call);
mutex_unlock(&event_mutex);
return ret;
}
@@ -2363,17 +2373,29 @@ static int probe_remove_event_call(struct trace_event_call *call)
return 0;
}
+/* no event_mutex version */
+int trace_remove_event_call_nolock(struct trace_event_call *call)
+{
+ int ret;
+
+ lockdep_assert_held(&event_mutex);
+
+ mutex_lock(&trace_types_lock);
+ down_write(&trace_event_sem);
+ ret = probe_remove_event_call(call);
+ up_write(&trace_event_sem);
+ mutex_unlock(&trace_types_lock);
+
+ return ret;
+}
+
/* Remove an event_call */
int trace_remove_event_call(struct trace_event_call *call)
{
int ret;
mutex_lock(&event_mutex);
- mutex_lock(&trace_types_lock);
- down_write(&trace_event_sem);
- ret = probe_remove_event_call(call);
- up_write(&trace_event_sem);
- mutex_unlock(&trace_types_lock);
+ ret = trace_remove_event_call_nolock(call);
mutex_unlock(&event_mutex);
return ret;
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 2fb7846..b949c39 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1642,7 +1642,7 @@ static int process_system_preds(struct trace_subsystem_dir *dir,
parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0);
return -EINVAL;
fail_mem:
- kfree(filter);
+ __free_filter(filter);
/* If any call succeeded, we still need to sync */
if (!fail)
tracepoint_synchronize_unregister();
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 32e814e..45c9ed1 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -448,6 +448,8 @@ static bool synth_field_signed(char *type)
{
if (strncmp(type, "u", 1) == 0)
return false;
+ if (strcmp(type, "gfp_t") == 0)
+ return false;
return true;
}
@@ -663,7 +665,26 @@ static notrace void trace_event_raw_event_synth(void *__data,
strscpy(str_field, str_val, STR_VAR_LEN_MAX);
n_u64 += STR_VAR_LEN_MAX / sizeof(u64);
} else {
- entry->fields[n_u64] = var_ref_vals[var_ref_idx + i];
+ struct synth_field *field = event->fields[i];
+ u64 val = var_ref_vals[var_ref_idx + i];
+
+ switch (field->size) {
+ case 1:
+ *(u8 *)&entry->fields[n_u64] = (u8)val;
+ break;
+
+ case 2:
+ *(u16 *)&entry->fields[n_u64] = (u16)val;
+ break;
+
+ case 4:
+ *(u32 *)&entry->fields[n_u64] = (u32)val;
+ break;
+
+ default:
+ entry->fields[n_u64] = val;
+ break;
+ }
n_u64++;
}
}
@@ -912,7 +933,7 @@ static int register_synth_event(struct synth_event *event)
call->data = event;
call->tp = event->tp;
- ret = trace_add_event_call(call);
+ ret = trace_add_event_call_nolock(call);
if (ret) {
pr_warn("Failed to register synthetic event: %s\n",
trace_event_name(call));
@@ -936,7 +957,7 @@ static int unregister_synth_event(struct synth_event *event)
struct trace_event_call *call = &event->call;
int ret;
- ret = trace_remove_event_call(call);
+ ret = trace_remove_event_call_nolock(call);
return ret;
}
@@ -1013,12 +1034,10 @@ static void add_or_delete_synth_event(struct synth_event *event, int delete)
if (delete)
free_synth_event(event);
else {
- mutex_lock(&synth_event_mutex);
if (!find_synth_event(event->name))
list_add(&event->list, &synth_event_list);
else
free_synth_event(event);
- mutex_unlock(&synth_event_mutex);
}
}
@@ -1030,6 +1049,7 @@ static int create_synth_event(int argc, char **argv)
int i, consumed = 0, n_fields = 0, ret = 0;
char *name;
+ mutex_lock(&event_mutex);
mutex_lock(&synth_event_mutex);
/*
@@ -1102,8 +1122,6 @@ static int create_synth_event(int argc, char **argv)
goto err;
}
out:
- mutex_unlock(&synth_event_mutex);
-
if (event) {
if (delete_event) {
ret = unregister_synth_event(event);
@@ -1113,10 +1131,13 @@ static int create_synth_event(int argc, char **argv)
add_or_delete_synth_event(event, ret);
}
}
+ mutex_unlock(&synth_event_mutex);
+ mutex_unlock(&event_mutex);
return ret;
err:
mutex_unlock(&synth_event_mutex);
+ mutex_unlock(&event_mutex);
for (i = 0; i < n_fields; i++)
free_synth_field(fields[i]);
@@ -1127,12 +1148,10 @@ static int create_synth_event(int argc, char **argv)
static int release_all_synth_events(void)
{
- struct list_head release_events;
struct synth_event *event, *e;
int ret = 0;
- INIT_LIST_HEAD(&release_events);
-
+ mutex_lock(&event_mutex);
mutex_lock(&synth_event_mutex);
list_for_each_entry(event, &synth_event_list, list) {
@@ -1142,16 +1161,14 @@ static int release_all_synth_events(void)
}
}
- list_splice_init(&event->list, &release_events);
-
- mutex_unlock(&synth_event_mutex);
-
- list_for_each_entry_safe(event, e, &release_events, list) {
+ list_for_each_entry_safe(event, e, &synth_event_list, list) {
list_del(&event->list);
ret = unregister_synth_event(event);
add_or_delete_synth_event(event, !ret);
}
+ mutex_unlock(&synth_event_mutex);
+ mutex_unlock(&event_mutex);
return ret;
}
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 3a51f9f..64d63a5 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -620,7 +620,7 @@ struct irqsoff_store {
unsigned long caddr[4];
};
-DEFINE_PER_CPU(struct irqsoff_store, the_irqsoff);
+static DEFINE_PER_CPU(struct irqsoff_store, the_irqsoff);
#endif /* CONFIG_PREEMPTIRQ_EVENTS */
/*
@@ -703,9 +703,57 @@ static struct tracer irqsoff_tracer __read_mostly =
#endif /* CONFIG_IRQSOFF_TRACER */
#ifdef CONFIG_PREEMPT_TRACER
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+/*
+ * preemptoff stack tracing threshold in ns.
+ * default: 1ms
+ */
+unsigned int sysctl_preemptoff_tracing_threshold_ns = 1000000UL;
+
+struct preempt_store {
+ u64 ts;
+ unsigned long caddr[4];
+ bool irqs_disabled;
+ int pid;
+ unsigned long ncsw;
+};
+
+static DEFINE_PER_CPU(struct preempt_store, the_ps);
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
+
void tracer_preempt_on(unsigned long a0, unsigned long a1)
{
int pc = preempt_count();
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+ struct preempt_store *ps;
+ u64 delta = 0;
+
+ lockdep_off();
+ ps = &per_cpu(the_ps, raw_smp_processor_id());
+
+ /*
+ * schedule() calls __schedule() with preemption disabled.
+ * if we had entered idle and exiting idle now, we think
+ * preemption is disabled the whole time. Detect this by
+ * checking if the preemption is disabled across the same
+ * task. There is a possiblity that the same task is scheduled
+ * after idle. To rule out this possibility, compare the
+ * context switch count also.
+ */
+ if (ps->ts && ps->pid == current->pid && (ps->ncsw ==
+ current->nvcsw + current->nivcsw))
+ delta = sched_clock() - ps->ts;
+ /*
+ * Trace preempt disable stack if preemption
+ * is disabled for more than the threshold.
+ */
+ if (delta > sysctl_preemptoff_tracing_threshold_ns)
+ trace_sched_preempt_disable(delta, ps->irqs_disabled,
+ ps->caddr[0], ps->caddr[1],
+ ps->caddr[2], ps->caddr[3]);
+ ps->ts = 0;
+ lockdep_on();
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
if (preempt_trace(pc) && !irq_trace())
stop_critical_timing(a0, a1, pc);
@@ -714,6 +762,21 @@ void tracer_preempt_on(unsigned long a0, unsigned long a1)
void tracer_preempt_off(unsigned long a0, unsigned long a1)
{
int pc = preempt_count();
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+ struct preempt_store *ps;
+
+ lockdep_off();
+ ps = &per_cpu(the_ps, raw_smp_processor_id());
+ ps->ts = sched_clock();
+ ps->caddr[0] = CALLER_ADDR0;
+ ps->caddr[1] = CALLER_ADDR1;
+ ps->caddr[2] = CALLER_ADDR2;
+ ps->caddr[3] = CALLER_ADDR3;
+ ps->irqs_disabled = irqs_disabled();
+ ps->pid = current->pid;
+ ps->ncsw = current->nvcsw + current->nivcsw;
+ lockdep_on();
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
if (preempt_trace(pc) && !irq_trace())
start_critical_timing(a0, a1, pc);
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index f9a0cd0..c61b2b0 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -519,11 +519,10 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
#if defined(CONFIG_KPROBES_ON_FTRACE) && \
!defined(CONFIG_KPROBE_EVENTS_ON_NOTRACE)
-static bool within_notrace_func(struct trace_kprobe *tk)
+static bool __within_notrace_func(unsigned long addr)
{
- unsigned long offset, size, addr;
+ unsigned long offset, size;
- addr = trace_kprobe_address(tk);
if (!addr || !kallsyms_lookup_size_offset(addr, &size, &offset))
return false;
@@ -536,6 +535,28 @@ static bool within_notrace_func(struct trace_kprobe *tk)
*/
return !ftrace_location_range(addr, addr + size - 1);
}
+
+static bool within_notrace_func(struct trace_kprobe *tk)
+{
+ unsigned long addr = addr = trace_kprobe_address(tk);
+ char symname[KSYM_NAME_LEN], *p;
+
+ if (!__within_notrace_func(addr))
+ return false;
+
+ /* Check if the address is on a suffixed-symbol */
+ if (!lookup_symbol_name(addr, symname)) {
+ p = strchr(symname, '.');
+ if (!p)
+ return true;
+ *p = '\0';
+ addr = (unsigned long)kprobe_lookup_name(symname, 0);
+ if (addr)
+ return __within_notrace_func(addr);
+ }
+
+ return true;
+}
#else
#define within_notrace_func(tk) (false)
#endif
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
index 9a1c223..9e31bfc 100644
--- a/kernel/trace/tracing_map.c
+++ b/kernel/trace/tracing_map.c
@@ -148,8 +148,8 @@ static int tracing_map_cmp_atomic64(void *val_a, void *val_b)
#define DEFINE_TRACING_MAP_CMP_FN(type) \
static int tracing_map_cmp_##type(void *val_a, void *val_b) \
{ \
- type a = *(type *)val_a; \
- type b = *(type *)val_b; \
+ type a = (type)(*(u64 *)val_a); \
+ type b = (type)(*(u64 *)val_b); \
\
return (a > b) ? 1 : ((a < b) ? -1 : 0); \
}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 5370ade..9d22430 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -2454,8 +2454,14 @@ static int rescuer_thread(void *__rescuer)
*/
if (need_to_create_worker(pool)) {
spin_lock(&wq_mayday_lock);
- get_pwq(pwq);
- list_move_tail(&pwq->mayday_node, &wq->maydays);
+ /*
+ * Queue iff we aren't racing destruction
+ * and somebody else hasn't queued it already.
+ */
+ if (wq->rescuer && list_empty(&pwq->mayday_node)) {
+ get_pwq(pwq);
+ list_add_tail(&pwq->mayday_node, &wq->maydays);
+ }
spin_unlock(&wq_mayday_lock);
}
}
@@ -4195,9 +4201,29 @@ void destroy_workqueue(struct workqueue_struct *wq)
struct pool_workqueue *pwq;
int node;
+ /*
+ * Remove it from sysfs first so that sanity check failure doesn't
+ * lead to sysfs name conflicts.
+ */
+ workqueue_sysfs_unregister(wq);
+
/* drain it before proceeding with destruction */
drain_workqueue(wq);
+ /* kill rescuer, if sanity checks fail, leave it w/o rescuer */
+ if (wq->rescuer) {
+ struct worker *rescuer = wq->rescuer;
+
+ /* this prevents new queueing */
+ spin_lock_irq(&wq_mayday_lock);
+ wq->rescuer = NULL;
+ spin_unlock_irq(&wq_mayday_lock);
+
+ /* rescuer will empty maydays list before exiting */
+ kthread_stop(rescuer->task);
+ kfree(rescuer);
+ }
+
/* sanity checks */
mutex_lock(&wq->mutex);
for_each_pwq(pwq, wq) {
@@ -4229,11 +4255,6 @@ void destroy_workqueue(struct workqueue_struct *wq)
list_del_rcu(&wq->list);
mutex_unlock(&wq_pool_mutex);
- workqueue_sysfs_unregister(wq);
-
- if (wq->rescuer)
- kthread_stop(wq->rescuer->task);
-
if (!(wq->flags & WQ_UNBOUND)) {
/*
* The base ref is never dropped on per-cpu pwqs. Directly
@@ -4505,7 +4526,8 @@ static void show_pwq(struct pool_workqueue *pwq)
pr_info(" pwq %d:", pool->id);
pr_cont_pool_info(pool);
- pr_cont(" active=%d/%d%s\n", pwq->nr_active, pwq->max_active,
+ pr_cont(" active=%d/%d refcnt=%d%s\n",
+ pwq->nr_active, pwq->max_active, pwq->refcnt,
!list_empty(&pwq->mayday_node) ? " MAYDAY" : "");
hash_for_each(pool->busy_hash, bkt, worker, hentry) {
diff --git a/kernel_headers.py b/kernel_headers.py
new file mode 100644
index 0000000..52ccc1d
--- /dev/null
+++ b/kernel_headers.py
@@ -0,0 +1,980 @@
+# Copyright 2019 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Generates gen_headers_<arch>.bp or generates/checks kernel headers."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import filecmp
+import os
+import re
+import subprocess
+import sys
+
+
+def gen_version_h(verbose, gen_dir, version_makefile):
+ """Generate linux/version.h
+
+ Scan the version_makefile for the version info, and then generate
+ linux/version.h in the gen_dir as done in kernel Makefile function
+ filechk_version.h
+
+ Args:
+ verbose: Set True to print progress messages.
+ gen_dir: Where to place the generated files.
+ version_makefile: The makefile that contains version info.
+ Return:
+ If version info not found, False. Otherwise, True.
+ """
+
+ version_re = re.compile(r'VERSION\s*=\s*(\d+)')
+ patchlevel_re = re.compile(r'PATCHLEVEL\s*=\s*(\d+)')
+ sublevel_re = re.compile(r'SUBLEVEL\s*=\s*(\d+)')
+
+ version_str = None
+ patchlevel_str = None
+ sublevel_str = None
+
+ if verbose:
+ print('gen_version_h: processing [%s]' % version_makefile)
+
+ with open(version_makefile, 'r') as f:
+ while not version_str or not patchlevel_str or not sublevel_str:
+ line = f.readline()
+
+ if not line:
+ print(
+ 'error: gen_version_h: failed to parse kernel version from %s' %
+ version_makefile)
+ return False
+
+ line = line.rstrip()
+
+ if verbose:
+ print('gen_version_h: line is %s' % line)
+
+ if not version_str:
+ match = version_re.match(line)
+ if match:
+ if verbose:
+ print('gen_version_h: matched version [%s]' % line)
+ version_str = match.group(1)
+ continue
+
+ if not patchlevel_str:
+ match = patchlevel_re.match(line)
+ if match:
+ if verbose:
+ print('gen_version_h: matched patchlevel [%s]' % line)
+ patchlevel_str = match.group(1)
+ continue
+
+ if not sublevel_str:
+ match = sublevel_re.match(line)
+ if match:
+ if verbose:
+ print('gen_version_h: matched sublevel [%s]' % line)
+ sublevel_str = match.group(1)
+ continue
+
+ version = int(version_str)
+ patchlevel = int(patchlevel_str)
+ sublevel = int(sublevel_str)
+
+ if verbose:
+ print(
+ 'gen_version_h: found kernel version %d.%d.%d' %
+ (version, patchlevel, sublevel))
+
+ version_h = os.path.join(gen_dir, 'linux', 'version.h')
+
+ with open(version_h, 'w') as f:
+ # This code must match the code in Makefile in the make function
+ # filechk_version.h
+ version_code = (version << 16) + (patchlevel << 8) + sublevel
+ f.write('#define LINUX_VERSION_CODE %d\n' % version_code)
+ f.write(
+ '#define KERNEL_VERSION(a,b,c) ' +
+ '(((a) << 16) + ((b) << 8) + (c))\n')
+
+ return True
+
+
+def scan_arch_kbuild(verbose, arch_asm_kbuild, asm_generic_kbuild, arch_include_uapi):
+ """Scan arch_asm_kbuild for generated headers.
+
+ This function processes the Kbuild file to scan for three types of files that
+ need to be generated. The first type are syscall generated headers, which are
+ identified by adding to the generated-y make variable. The second type are
+ generic headers, which are arch-specific headers that simply wrap the
+ asm-generic counterpart, and are identified by adding to the generic-y make
+ variable. The third type are mandatory headers that should be present in the
+ /usr/include/asm folder.
+
+ Args:
+ verbose: Set True to print progress messages.
+ arch_asm_kbuild: The Kbuild file containing lists of headers to generate.
+ asm_generic_kbuild: The Kbuild file containing lists of mandatory headers.
+ arch_include_uapi: Headers in /arch/<arch>/include/uapi directory
+ Return:
+ Two lists of discovered headers, one for generated and one for generic.
+ """
+
+ generated_y_re = re.compile(r'generated-y\s*\+=\s*(\S+)')
+ generic_y_re = re.compile(r'generic-y\s*\+=\s*(\S+)')
+ mandatory_y_re = re.compile(r'mandatory-y\s*\+=\s*(\S+)')
+
+ # This loop parses arch_asm_kbuild for various kinds of headers to generate.
+
+ if verbose:
+ print('scan_arch_kbuild: processing [%s]' % arch_asm_kbuild)
+
+ generated_list = []
+ generic_list = []
+ arch_include_uapi_list = [os.path.basename(x) for x in arch_include_uapi]
+ mandatory_pre_list = []
+ mandatory_list = []
+
+
+ with open(arch_asm_kbuild, 'r') as f:
+ while True:
+ line = f.readline()
+
+ if not line:
+ break
+
+ line = line.rstrip()
+
+ if verbose:
+ print('scan_arch_kbuild: line is %s' % line)
+
+ match = generated_y_re.match(line)
+
+ if match:
+ if verbose:
+ print('scan_arch_kbuild: matched [%s]' % line)
+ generated_list.append(match.group(1))
+ continue
+
+ match = generic_y_re.match(line)
+
+ if match:
+ if verbose:
+ print('scan_arch_kbuild: matched [%s]' % line)
+ generic_list.append(match.group(1))
+ continue
+
+ # This loop parses asm_generic_kbuild for various kinds of headers to generate.
+
+ if verbose:
+ print('scan_arch_kbuild: processing [%s]' % asm_generic_kbuild)
+
+ with open(asm_generic_kbuild, 'r') as f:
+ while True:
+ line = f.readline()
+
+ if not line:
+ break
+
+ line = line.rstrip()
+
+ if verbose:
+ print('scan_arch_kbuild: line is %s' % line)
+
+ match = mandatory_y_re.match(line)
+
+ if match:
+ if verbose:
+ print('scan_arch_kbuild: matched [%s]' % line)
+ mandatory_pre_list.append(match.group(1))
+ continue
+
+ # Mandatory headers need to be generated if they are not already generated.
+ comb_list = generic_list + generated_list + arch_include_uapi_list
+ mandatory_list = [x for x in mandatory_pre_list if x not in comb_list]
+ if verbose:
+ print("generic")
+ for x in generic_list:
+ print(x)
+ print("generated")
+ for x in generated_list:
+ print(x)
+ print("mandatory")
+ for x in mandatory_list:
+ print(x)
+ print("arch_include_uapi_list")
+ for x in arch_include_uapi_list:
+ print(x)
+
+ return (generated_list, generic_list, mandatory_list)
+
+
+def gen_arch_headers(
+ verbose, gen_dir, arch_asm_kbuild, asm_generic_kbuild, arch_syscall_tool, arch_syscall_tbl, arch_include_uapi):
+ """Process arch-specific and asm-generic uapi/asm/Kbuild to generate headers.
+
+ The function consists of a call to scan_arch_kbuild followed by three loops.
+ The first loop generates headers found and placed in the generated_list by
+ scan_arch_kbuild. The second loop generates headers found and placed in the
+ generic_list by the scan_arch_kbuild. The third loop generates headers found
+ in mandatory_list by scan_arch_kbuild.
+
+ The function does some parsing of file names and tool invocations. If that
+ parsing fails for some reason (e.g., we don't know how to generate the
+ header) or a tool invocation fails, then this function will count that as
+ an error but keep processing. In the end, the function returns the number of
+ errors encountered.
+
+ Args:
+ verbose: Set True to print progress messages.
+ gen_dir: Where to place the generated files.
+ arch_asm_kbuild: The Kbuild file containing lists of headers to generate.
+ asm_generic_kbuild: The Kbuild file containing lists of mandatory headers.
+ arch_syscall_tool: The arch script that generates syscall headers, or None.
+ arch_syscall_tbl: The arch script that defines syscall vectors, or None.
+ arch_include_uapi: Headers in arch/<arch>/include/uapi directory.
+ Return:
+ The number of parsing errors encountered.
+ """
+
+ error_count = 0
+
+ # First generate the lists
+
+ (generated_list, generic_list, mandatory_list) = scan_arch_kbuild(verbose, arch_asm_kbuild, asm_generic_kbuild ,arch_include_uapi)
+
+ # Now we're at the first loop, which is able to generate syscall headers
+ # found in the first loop, and placed in generated_list. It's okay for this
+ # list to be empty. In that case, of course, the loop does nothing.
+
+ abi_re = re.compile(r'unistd-(\S+)\.h')
+
+ for generated in generated_list:
+ gen_h = os.path.join(gen_dir, 'asm', generated)
+ match = abi_re.match(generated)
+
+ if match:
+ abi = match.group(1)
+
+ cmd = [
+ '/bin/bash',
+ arch_syscall_tool,
+ arch_syscall_tbl,
+ gen_h,
+ abi,
+ '',
+ '__NR_SYSCALL_BASE',
+ ]
+
+ if verbose:
+ print('gen_arch_headers: cmd is %s' % cmd)
+
+ result = subprocess.call(cmd)
+
+ if result != 0:
+ print('error: gen_arch_headers: cmd %s failed %d' % (cmd, result))
+ error_count += 1
+ else:
+ print('error: gen_arch_headers: syscall header has bad filename: %s' % generated)
+ error_count += 1
+
+ # Now we're at the second loop, which generates wrappers from arch-specific
+ # headers listed in generic_list to the corresponding asm-generic header.
+
+ for generic in generic_list:
+ wrap_h = os.path.join(gen_dir, 'asm', generic)
+ with open(wrap_h, 'w') as f:
+ f.write('#include <asm-generic/%s>\n' % generic)
+
+ # Now we're at the third loop, which generates wrappers from asm
+ # headers listed in mandatory_list to the corresponding asm-generic header.
+
+ for mandatory in mandatory_list:
+ wrap_h = os.path.join(gen_dir, 'asm', mandatory)
+ with open(wrap_h, 'w') as f:
+ f.write('#include <asm-generic/%s>\n' % mandatory)
+ return error_count
+
+
+def run_headers_install(verbose, gen_dir, headers_install, prefix, h):
+ """Process a header through the headers_install script.
+
+ The headers_install script does some processing of a header so that it is
+ appropriate for inclusion in a userland program. This function invokes that
+ script for one header file.
+
+ The input file is a header file found in the directory named by prefix. This
+ function stips the prefix from the header to generate the name of the
+ processed header.
+
+ Args:
+ verbose: Set True to print progress messages.
+ gen_dir: Where to place the generated files.
+ headers_install: The script that munges the header.
+ prefix: The prefix to strip from h to generate the output filename.
+ h: The input header to process.
+ Return:
+ If parsing or the tool fails, False. Otherwise, True
+ """
+
+ if not h.startswith(prefix):
+ print('error: expected prefix [%s] on header [%s]' % (prefix, h))
+ return False
+
+ out_h = os.path.join(gen_dir, h[len(prefix):])
+ (out_h_dirname, out_h_basename) = os.path.split(out_h)
+ h_dirname = os.path.dirname(h)
+
+ cmd = [headers_install, out_h_dirname, h_dirname, out_h_basename]
+
+ if verbose:
+ print('run_headers_install: cmd is %s' % cmd)
+
+ result = subprocess.call(cmd)
+
+ if result != 0:
+ print('error: run_headers_install: cmd %s failed %d' % (cmd, result))
+ return False
+
+ return True
+
+
+def glob_headers(prefix, rel_glob, excludes):
+ """Recursively scan the a directory for headers.
+
+ This function recursively scans the directory identified by prefix for
+ headers. We don't yet have a new enough version of python3 to use the
+ better glob function, so right now we assume the glob is '**/*.h'.
+
+ The function filters out any files that match the items in excludes.
+
+ Args:
+ prefix: The directory to recursively scan for headers.
+ rel_glob: The shell-style glob that identifies the header pattern.
+ excludes: A list of headers to exclude from the glob.
+ Return:
+ A list of headers discovered with excludes excluded.
+ """
+
+ # If we had python 3.5+, we could use the fancy new glob.glob.
+ # full_glob = os.path.join(prefix, rel_glob)
+ # full_srcs = glob.glob(full_glob, recursive=True)
+
+ full_dirs = [prefix]
+ full_srcs = []
+
+ while full_dirs:
+ full_dir = full_dirs.pop(0)
+ items = sorted(os.listdir(full_dir))
+
+ for item in items:
+ full_item = os.path.join(full_dir, item)
+
+ if os.path.isdir(full_item):
+ full_dirs.append(full_item)
+ continue
+
+ if full_item in excludes:
+ continue
+
+ if full_item.endswith('.h'):
+ full_srcs.append(full_item)
+
+ return full_srcs
+
+
+def find_out(verbose, module_dir, prefix, rel_glob, excludes, outs):
+ """Build a list of outputs for the genrule that creates kernel headers.
+
+ This function scans for headers in the source tree and produces a list of
+ output (generated) headers.
+
+ Args:
+ verbose: Set True to print progress messages.
+ module_dir: The root directory of the kernel source.
+ prefix: The prefix with in the kernel source tree to search for headers.
+ rel_glob: The pattern to use when matching headers under prefix.
+ excludes: A list of files to exclude from the glob.
+ outs: The list to populdate with the headers that will be generated.
+ Return:
+ The number of errors encountered.
+ """
+
+ # Turn prefix, which is relative to the soong module, to a full prefix that
+ # is relative to the Android source tree.
+
+ full_prefix = os.path.join(module_dir, prefix)
+
+ # Convert the list of excludes, which are relative to the soong module, to a
+ # set of excludes (for easy hashing), relative to the Android source tree.
+
+ full_excludes = set()
+
+ if excludes:
+ for exclude in excludes:
+ full_exclude = os.path.join(full_prefix, exclude)
+ full_excludes.add(full_exclude)
+
+ # Glob those headers.
+
+ full_srcs = glob_headers(full_prefix, rel_glob, full_excludes)
+
+ # Now convert the file names, which are relative to the Android source tree,
+ # to be relative to the gen dir. This means stripping off the module prefix
+ # and the directory within this module.
+
+ module_dir_sep = module_dir + os.sep
+ prefix_sep = prefix + os.sep
+
+ if verbose:
+ print('find_out: module_dir_sep [%s]' % module_dir_sep)
+ print('find_out: prefix_sep [%s]' % prefix_sep)
+
+ error_count = 0
+
+ for full_src in full_srcs:
+ if verbose:
+ print('find_out: full_src [%s]' % full_src)
+
+ if not full_src.startswith(module_dir_sep):
+ print('error: expected %s to start with %s' % (full_src, module_dir_sep))
+ error_count += 1
+ continue
+
+ local_src = full_src[len(module_dir_sep):]
+
+ if verbose:
+ print('find_out: local_src [%s]' % local_src)
+
+ if not local_src.startswith(prefix_sep):
+ print('error: expected %s to start with %s' % (local_src, prefix_sep))
+ error_count += 1
+ continue
+
+ # After stripping the module directory and the prefix, we're left with the
+ # name of a header that we'll generate, relative to the base of of a the
+ # the include path.
+
+ local_out = local_src[len(prefix_sep):]
+
+ if verbose:
+ print('find_out: local_out [%s]' % local_out)
+
+ outs.append(local_out)
+
+ return error_count
+
+
+def gen_blueprints(
+ verbose, header_arch, gen_dir, arch_asm_kbuild, asm_generic_kbuild, module_dir,
+ rel_arch_asm_kbuild, rel_asm_generic_kbuild, arch_include_uapi, techpack_include_uapi):
+ """Generate a blueprints file containing modules that invoke this script.
+
+ This function generates a blueprints file that contains modules that
+ invoke this script to generate kernel headers. We generate the blueprints
+ file as needed, but we don't actually use the generated file. The blueprints
+ file that we generate ends up in the out directory, and we can use it to
+ detect if the checked-in version of the file (in the source directory) is out
+ of date. This pattern occurs in the Android source tree in several places.
+
+ Args:
+ verbose: Set True to print progress messages.
+ header_arch: The arch for which to generate headers.
+ gen_dir: Where to place the generated files.
+ arch_asm_kbuild: The Kbuild file containing lists of headers to generate.
+ asm_generic_kbuild: The Kbuild file containing lists of mandatory headers.
+ module_dir: The root directory of the kernel source.
+ rel_arch_asm_kbuild: arch_asm_kbuild relative to module_dir.
+ Return:
+ The number of errors encountered.
+ """
+ error_count = 0
+
+ # The old and new blueprints files. We generate the new one, but we need to
+ # refer to the old one in the modules that we generate.
+ old_gen_headers_bp = 'gen_headers_%s.bp' % header_arch
+ new_gen_headers_bp = os.path.join(gen_dir, old_gen_headers_bp)
+
+ # Tools and tool files.
+ headers_install_sh = 'headers_install.sh'
+ kernel_headers_py = 'kernel_headers.py'
+ arm_syscall_tool = 'arch/arm/tools/syscallhdr.sh'
+
+ # Sources
+ makefile = 'Makefile'
+ arm_syscall_tbl = 'arch/arm/tools/syscall.tbl'
+ rel_glob = '**/*.h'
+ generic_prefix = 'include/uapi'
+ arch_prefix = os.path.join('arch', header_arch, generic_prefix)
+ generic_src = os.path.join(generic_prefix, rel_glob)
+ arch_src = os.path.join(arch_prefix, rel_glob)
+ techpack_src = 'techpack/*'
+
+ # Excluded sources, architecture specific.
+ exclude_srcs = []
+
+ if header_arch == "arm":
+ exclude_srcs = ['linux/a.out.h']
+
+ if header_arch == "arm64":
+ exclude_srcs = ['linux/a.out.h', 'linux/kvm_para.h']
+
+ # Scan the arch_asm_kbuild file for files that need to be generated and those
+ # that are generic (i.e., need to be wrapped).
+
+ (generated_list, generic_list, mandatory_list) = scan_arch_kbuild(verbose,
+ arch_asm_kbuild, asm_generic_kbuild, arch_include_uapi)
+
+ generic_out = []
+ error_count += find_out(
+ verbose, module_dir, generic_prefix, rel_glob, exclude_srcs, generic_out)
+
+ arch_out = []
+ error_count += find_out(
+ verbose, module_dir, arch_prefix, rel_glob, None, arch_out)
+
+ techpack_out = [x.split('include/uapi/')[1] for x in techpack_include_uapi]
+
+ if error_count != 0:
+ return error_count
+
+ # Generate the blueprints file.
+
+ if verbose:
+ print('gen_blueprints: generating %s' % new_gen_headers_bp)
+
+ with open(new_gen_headers_bp, 'w') as f:
+ f.write('// ***** DO NOT EDIT *****\n')
+ f.write('// This file is generated by %s\n' % kernel_headers_py)
+ f.write('\n')
+ f.write('gen_headers_%s = [\n' % header_arch)
+
+ if generated_list:
+ f.write('\n')
+ f.write(' // Matching generated-y:\n')
+ f.write('\n')
+ for h in generated_list:
+ f.write(' "asm/%s",\n' % h)
+
+ if generic_list:
+ f.write('\n')
+ f.write(' // Matching generic-y:\n')
+ f.write('\n')
+ for h in generic_list:
+ f.write(' "asm/%s",\n' % h)
+
+ if mandatory_list:
+ f.write('\n')
+ f.write(' // Matching mandatory-y:\n')
+ f.write('\n')
+ for h in mandatory_list:
+ f.write(' "asm/%s",\n' % h)
+
+ if generic_out:
+ f.write('\n')
+ f.write(' // From %s\n' % generic_src)
+ f.write('\n')
+ for h in generic_out:
+ f.write(' "%s",\n' % h)
+
+ if arch_out:
+ f.write('\n')
+ f.write(' // From %s\n' % arch_src)
+ f.write('\n')
+ for h in arch_out:
+ f.write(' "%s",\n' % h)
+
+ if techpack_out:
+ f.write('\n')
+ f.write(' // From %s\n' % techpack_src)
+ f.write('\n')
+ for h in techpack_out:
+ f.write(' "%s",\n' % h)
+
+ f.write(']\n')
+ f.write('\n')
+
+ gen_blueprints_module_name = 'qti_generate_gen_headers_%s' % header_arch
+
+ f.write('genrule {\n')
+ f.write(' // This module generates the gen_headers_<arch>.bp file\n')
+ f.write(' // (i.e., a new version of this file) so that it can be\n')
+ f.write(' // checked later to ensure that it matches the checked-\n')
+ f.write(' // in version (this file).\n')
+ f.write(' name: "%s",\n' % gen_blueprints_module_name)
+ f.write(' srcs: ["%s", "%s", "%s"],\n' % (rel_arch_asm_kbuild, rel_asm_generic_kbuild, arch_src))
+ f.write(' tool_files: ["kernel_headers.py"],\n')
+ f.write(' cmd: "python3 $(location kernel_headers.py) " +\n')
+ f.write(' kernel_headers_verbose +\n')
+ f.write(' "--header_arch %s " +\n' % header_arch)
+ f.write(' "--gen_dir $(genDir) " +\n')
+ f.write(' "--arch_asm_kbuild $(location %s) " +\n' % rel_arch_asm_kbuild)
+ f.write(' "--arch_include_uapi $(locations %s) " +\n' % arch_src)
+ f.write(' "--asm_generic_kbuild $(location %s) " +\n' % rel_asm_generic_kbuild)
+ f.write(' "blueprints",\n')
+ f.write(' out: ["gen_headers_%s.bp"],\n' % header_arch)
+ f.write('}\n')
+ f.write('\n')
+
+ f.write('genrule {\n')
+ f.write(' name: "qti_generate_kernel_headers_%s",\n' % header_arch)
+ f.write(' tools: ["%s"],\n' % headers_install_sh)
+ f.write(' tool_files: [\n')
+ f.write(' "%s",\n' % kernel_headers_py)
+
+ if header_arch == "arm":
+ f.write(' "%s",\n' % arm_syscall_tool)
+
+ f.write(' ],\n')
+ f.write(' srcs: [\n')
+ f.write(' "%s",\n' % rel_arch_asm_kbuild)
+ f.write(' "%s",\n' % rel_asm_generic_kbuild)
+ f.write(' "%s",\n' % old_gen_headers_bp)
+ f.write(' ":%s",\n' % gen_blueprints_module_name)
+ f.write(' "%s",\n' % makefile)
+
+ if header_arch == "arm":
+ f.write(' "%s",\n' % arm_syscall_tbl)
+
+ f.write(' "%s",\n' % generic_src)
+ f.write(' "%s",\n' % arch_src)
+ f.write(' ],\n')
+
+ if exclude_srcs:
+ f.write(' exclude_srcs: [\n')
+ for h in exclude_srcs:
+ f.write(' "%s",\n' % os.path.join(generic_prefix, h))
+ f.write(' ],\n')
+
+ f.write(' cmd: "python3 $(location %s) " +\n' % kernel_headers_py)
+ f.write(' kernel_headers_verbose +\n')
+ f.write(' "--header_arch %s " +\n' % header_arch)
+ f.write(' "--gen_dir $(genDir) " +\n')
+ f.write(' "--arch_asm_kbuild $(location %s) " +\n' % rel_arch_asm_kbuild)
+ f.write(' "--arch_include_uapi $(locations %s) " +\n' % arch_src)
+ f.write(' "--asm_generic_kbuild $(location %s) " +\n' % rel_asm_generic_kbuild)
+ f.write(' "headers " +\n')
+ f.write(' "--old_gen_headers_bp $(location %s) " +\n' % old_gen_headers_bp)
+ f.write(' "--new_gen_headers_bp $(location :%s) " +\n' % gen_blueprints_module_name)
+ f.write(' "--version_makefile $(location %s) " +\n' % makefile)
+
+ if header_arch == "arm":
+ f.write(' "--arch_syscall_tool $(location %s) " +\n' % arm_syscall_tool)
+ f.write(' "--arch_syscall_tbl $(location %s) " +\n' % arm_syscall_tbl)
+
+ f.write(' "--headers_install $(location %s) " +\n' % headers_install_sh)
+ f.write(' "--include_uapi $(locations %s)",\n' % generic_src)
+ f.write(' out: ["linux/version.h"] + gen_headers_%s,\n' % header_arch)
+ f.write('}\n')
+
+ return 0
+
+def parse_bp_for_headers(file_name, headers):
+ parsing_headers = False
+ pattern = re.compile("gen_headers_[a-zA-Z0-9]+\s*=\s*\[\s*")
+ with open(file_name, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if pattern.match(line):
+ parsing_headers = True
+ continue
+
+ if line.find("]") != -1 and parsing_headers:
+ break
+
+ if not parsing_headers:
+ continue
+
+ if line.find("//") == 0:
+ continue
+
+ headers.add(line[1:-2])
+
+def headers_diff(old_file, new_file):
+ old_headers = set()
+ new_headers = set()
+ diff_detected = False
+
+ parse_bp_for_headers(old_file, old_headers)
+ parse_bp_for_headers(new_file, new_headers)
+
+ diff = old_headers - new_headers
+ if len(diff):
+ diff_detected = True
+ print("Headers to remove:")
+ for x in diff:
+ print("\t{}".format(x))
+
+ diff = new_headers - old_headers
+ if len(diff):
+ diff_detected = True
+ print("Headers to add:")
+ for x in diff:
+ print("\t{}".format(x))
+
+ return diff_detected
+
+def gen_headers(
+ verbose, header_arch, gen_dir, arch_asm_kbuild, asm_generic_kbuild, module_dir,
+ old_gen_headers_bp, new_gen_headers_bp, version_makefile,
+ arch_syscall_tool, arch_syscall_tbl, headers_install, include_uapi,
+ arch_include_uapi, techpack_include_uapi):
+ """Generate the kernel headers.
+
+ This script generates the version.h file, the arch-specific headers including
+ syscall-related generated files and wrappers around generic files, and uses
+ the headers_install tool to process other generic uapi and arch-specifc uapi
+ files.
+
+ Args:
+ verbose: Set True to print progress messages.
+ header_arch: The arch for which to generate headers.
+ gen_dir: Where to place the generated files.
+ arch_asm_kbuild: The Kbuild file containing lists of headers to generate.
+ asm_generic_kbuild: The Kbuild file containing mandatory headers.
+ module_dir: The root directory of the kernel source.
+ old_gen_headers_bp: The old gen_headers_<arch>.bp file to check.
+ new_gen_headers_bp: The new gen_headers_<arch>.bp file to check.
+ version_makefile: The kernel Makefile that contains version info.
+ arch_syscall_tool: The arch script that generates syscall headers.
+ arch_syscall_tbl: The arch script that defines syscall vectors.
+ headers_install: The headers_install tool to process input headers.
+ include_uapi: The list of include/uapi header files.
+ arch_include_uapi: The list of arch/<arch>/include/uapi header files.
+ Return:
+ The number of errors encountered.
+ """
+
+ if headers_diff(old_gen_headers_bp, new_gen_headers_bp):
+ print('error: gen_headers blueprints file is out of date, suggested fix:')
+ print('#######Please add or remove the above mentioned headers from %s' % (old_gen_headers_bp))
+ print('then re-run the build')
+ return 1
+
+ error_count = 0
+
+ if not gen_version_h(verbose, gen_dir, version_makefile):
+ error_count += 1
+
+ error_count += gen_arch_headers(
+ verbose, gen_dir, arch_asm_kbuild, asm_generic_kbuild, arch_syscall_tool, arch_syscall_tbl ,arch_include_uapi)
+
+ uapi_include_prefix = os.path.join(module_dir, 'include', 'uapi') + os.sep
+
+ arch_uapi_include_prefix = os.path.join(
+ module_dir, 'arch', header_arch, 'include', 'uapi') + os.sep
+
+ for h in include_uapi:
+ if not run_headers_install(
+ verbose, gen_dir, headers_install,
+ uapi_include_prefix, h):
+ error_count += 1
+
+ for h in arch_include_uapi:
+ if not run_headers_install(
+ verbose, gen_dir, headers_install,
+ arch_uapi_include_prefix, h):
+ error_count += 1
+
+ for h in techpack_include_uapi:
+ techpack_uapi_include_prefix = os.path.join(h.split('/include/uapi')[0], 'include', 'uapi') + os.sep
+ if not run_headers_install(
+ verbose, gen_dir, headers_install,
+ techpack_uapi_include_prefix, h):
+ error_count += 1
+
+ return error_count
+
+def extract_techpack_uapi_headers(verbose, module_dir):
+
+ """EXtract list of uapi headers from techpack/* directories. We need to export
+ these headers to userspace.
+
+ Args:
+ verbose: Verbose option is provided to script
+ module_dir: Base directory
+ Returs:
+ List of uapi headers
+ """
+
+ techpack_subdir = []
+ techpack_dir = os.path.join(module_dir,'techpack')
+ techpack_uapi = []
+ techpack_uapi_sub = []
+
+ #get list of techpack directories under techpack/
+ if os.path.isdir(techpack_dir):
+ items = sorted(os.listdir(techpack_dir))
+ for x in items:
+ p = os.path.join(techpack_dir, x)
+ if os.path.isdir(p):
+ techpack_subdir.append(p)
+
+ #Print list of subdirs obtained
+ if (verbose):
+ for x in techpack_subdir:
+ print(x)
+
+ #For every subdirectory get list of .h files under include/uapi and append to techpack_uapi list
+ for x in techpack_subdir:
+ techpack_uapi_path = os.path.join(x, 'include/uapi')
+ if (os.path.isdir(techpack_uapi_path)):
+ techpack_uapi_sub = []
+ find_out(verbose, x, 'include/uapi', '**/*.h', None, techpack_uapi_sub)
+ tmp = [os.path.join(techpack_uapi_path, y) for y in techpack_uapi_sub]
+ techpack_uapi = techpack_uapi + tmp
+
+ if (verbose):
+ for x in techpack_uapi:
+ print(x)
+
+ return techpack_uapi
+
+def main():
+ """Parse command line arguments and perform top level control."""
+
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+
+ # Arguments that apply to every invocation of this script.
+
+ parser.add_argument(
+ '--verbose',
+ action='store_true',
+ help='Print output that describes the workings of this script.')
+ parser.add_argument(
+ '--header_arch',
+ required=True,
+ help='The arch for which to generate headers.')
+ parser.add_argument(
+ '--gen_dir',
+ required=True,
+ help='Where to place the generated files.')
+ parser.add_argument(
+ '--arch_asm_kbuild',
+ required=True,
+ help='The Kbuild file containing lists of headers to generate.')
+ parser.add_argument(
+ '--asm_generic_kbuild',
+ required=True,
+ help='The Kbuild file containing lists of mandatory headers.')
+ parser.add_argument(
+ '--arch_include_uapi',
+ required=True,
+ nargs='*',
+ help='The list of arch/<arch>/include/uapi header files.')
+
+ # The modes.
+
+ subparsers = parser.add_subparsers(
+ dest='mode',
+ help='Select mode')
+ parser_blueprints = subparsers.add_parser(
+ 'blueprints',
+ help='Generate the gen_headers_<arch>.bp file.')
+ parser_headers = subparsers.add_parser(
+ 'headers',
+ help='Check blueprints, then generate kernel headers.')
+
+ # Arguments that apply to headers mode.
+
+ parser_headers.add_argument(
+ '--old_gen_headers_bp',
+ required=True,
+ help='The old gen_headers_<arch>.bp file to check.')
+ parser_headers.add_argument(
+ '--new_gen_headers_bp',
+ required=True,
+ help='The new gen_headers_<arch>.bp file to check.')
+ parser_headers.add_argument(
+ '--version_makefile',
+ required=True,
+ help='The kernel Makefile that contains version info.')
+ parser_headers.add_argument(
+ '--arch_syscall_tool',
+ help='The arch script that generates syscall headers, if applicable.')
+ parser_headers.add_argument(
+ '--arch_syscall_tbl',
+ help='The arch script that defines syscall vectors, if applicable.')
+ parser_headers.add_argument(
+ '--headers_install',
+ required=True,
+ help='The headers_install tool to process input headers.')
+ parser_headers.add_argument(
+ '--include_uapi',
+ required=True,
+ nargs='*',
+ help='The list of include/uapi header files.')
+
+ args = parser.parse_args()
+
+ if args.verbose:
+ print('mode [%s]' % args.mode)
+ print('header_arch [%s]' % args.header_arch)
+ print('gen_dir [%s]' % args.gen_dir)
+ print('arch_asm_kbuild [%s]' % args.arch_asm_kbuild)
+ print('asm_generic_kbuild [%s]' % args.asm_generic_kbuild)
+
+ # Extract the module_dir from args.arch_asm_kbuild and rel_arch_asm_kbuild.
+
+ rel_arch_asm_kbuild = os.path.join(
+ 'arch', args.header_arch, 'include/uapi/asm/Kbuild')
+
+ suffix = os.sep + rel_arch_asm_kbuild
+
+ if not args.arch_asm_kbuild.endswith(suffix):
+ print('error: expected %s to end with %s' % (args.arch_asm_kbuild, suffix))
+ return 1
+
+ module_dir = args.arch_asm_kbuild[:-len(suffix)]
+
+ rel_asm_generic_kbuild = os.path.join('include/uapi/asm-generic', os.path.basename(args.asm_generic_kbuild))
+
+
+ if args.verbose:
+ print('module_dir [%s]' % module_dir)
+
+ # Get list of techpack uapi headers to be exported from techpack/* directories.
+ techpack_uapi = extract_techpack_uapi_headers(args.verbose, module_dir)
+
+ if args.mode == 'blueprints':
+ return gen_blueprints(
+ args.verbose, args.header_arch, args.gen_dir, args.arch_asm_kbuild,
+ args.asm_generic_kbuild, module_dir, rel_arch_asm_kbuild, rel_asm_generic_kbuild, args.arch_include_uapi, techpack_uapi)
+
+ if args.mode == 'headers':
+ if args.verbose:
+ print('old_gen_headers_bp [%s]' % args.old_gen_headers_bp)
+ print('new_gen_headers_bp [%s]' % args.new_gen_headers_bp)
+ print('version_makefile [%s]' % args.version_makefile)
+ print('arch_syscall_tool [%s]' % args.arch_syscall_tool)
+ print('arch_syscall_tbl [%s]' % args.arch_syscall_tbl)
+ print('headers_install [%s]' % args.headers_install)
+
+ return gen_headers(
+ args.verbose, args.header_arch, args.gen_dir, args.arch_asm_kbuild,
+ args.asm_generic_kbuild, module_dir, args.old_gen_headers_bp, args.new_gen_headers_bp,
+ args.version_makefile, args.arch_syscall_tool, args.arch_syscall_tbl,
+ args.headers_install, args.include_uapi, args.arch_include_uapi, techpack_uapi)
+
+ print('error: unknown mode: %s' % args.mode)
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 2fd07f6..c4ca9ce 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -13,6 +13,7 @@
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
@@ -466,14 +467,15 @@ EXPORT_SYMBOL(bitmap_parse_user);
* ranges if list is specified or hex digits grouped into comma-separated
* sets of 8 digits/set. Returns the number of characters written to buf.
*
- * It is assumed that @buf is a pointer into a PAGE_SIZE area and that
- * sufficient storage remains at @buf to accommodate the
- * bitmap_print_to_pagebuf() output.
+ * It is assumed that @buf is a pointer into a PAGE_SIZE, page-aligned
+ * area and that sufficient storage remains at @buf to accommodate the
+ * bitmap_print_to_pagebuf() output. Returns the number of characters
+ * actually printed to @buf, excluding terminating '\0'.
*/
int bitmap_print_to_pagebuf(bool list, char *buf, const unsigned long *maskp,
int nmaskbits)
{
- ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
+ ptrdiff_t len = PAGE_SIZE - offset_in_page(buf);
int n = 0;
if (len > 1)
diff --git a/lib/dump_stack.c b/lib/dump_stack.c
index 5cff72f..33ffbf3 100644
--- a/lib/dump_stack.c
+++ b/lib/dump_stack.c
@@ -106,7 +106,12 @@ asmlinkage __visible void dump_stack(void)
was_locked = 1;
} else {
local_irq_restore(flags);
- cpu_relax();
+ /*
+ * Wait for the lock to release before jumping to
+ * atomic_cmpxchg() in order to mitigate the thundering herd
+ * problem.
+ */
+ do { cpu_relax(); } while (atomic_read(&dump_lock) != -1);
goto retry;
}
diff --git a/lib/genalloc.c b/lib/genalloc.c
index ca06adc..7e85d1e 100644
--- a/lib/genalloc.c
+++ b/lib/genalloc.c
@@ -35,6 +35,7 @@
#include <linux/interrupt.h>
#include <linux/genalloc.h>
#include <linux/of_device.h>
+#include <linux/vmalloc.h>
static inline size_t chunk_size(const struct gen_pool_chunk *chunk)
{
@@ -187,7 +188,7 @@ int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phy
int nbytes = sizeof(struct gen_pool_chunk) +
BITS_TO_LONGS(nbits) * sizeof(long);
- chunk = kzalloc_node(nbytes, GFP_KERNEL, nid);
+ chunk = vzalloc_node(nbytes, nid);
if (unlikely(chunk == NULL))
return -ENOMEM;
@@ -251,7 +252,7 @@ void gen_pool_destroy(struct gen_pool *pool)
bit = find_next_bit(chunk->bits, end_bit, 0);
BUG_ON(bit < end_bit);
- kfree(chunk);
+ vfree(chunk);
}
kfree_const(pool->name);
kfree(pool);
@@ -311,7 +312,7 @@ unsigned long gen_pool_alloc_algo(struct gen_pool *pool, size_t size,
end_bit = chunk_size(chunk) >> order;
retry:
start_bit = algo(chunk->bits, end_bit, start_bit,
- nbits, data, pool);
+ nbits, data, pool, chunk->start_addr);
if (start_bit >= end_bit)
continue;
remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
@@ -525,7 +526,7 @@ EXPORT_SYMBOL(gen_pool_set_algo);
*/
unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool)
+ struct gen_pool *pool, unsigned long start_addr)
{
return bitmap_find_next_zero_area(map, size, start, nr, 0);
}
@@ -543,16 +544,19 @@ EXPORT_SYMBOL(gen_pool_first_fit);
*/
unsigned long gen_pool_first_fit_align(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool)
+ struct gen_pool *pool, unsigned long start_addr)
{
struct genpool_data_align *alignment;
- unsigned long align_mask;
+ unsigned long align_mask, align_off;
int order;
alignment = data;
order = pool->min_alloc_order;
align_mask = ((alignment->align + (1UL << order) - 1) >> order) - 1;
- return bitmap_find_next_zero_area(map, size, start, nr, align_mask);
+ align_off = (start_addr & (alignment->align - 1)) >> order;
+
+ return bitmap_find_next_zero_area_off(map, size, start, nr,
+ align_mask, align_off);
}
EXPORT_SYMBOL(gen_pool_first_fit_align);
@@ -567,7 +571,7 @@ EXPORT_SYMBOL(gen_pool_first_fit_align);
*/
unsigned long gen_pool_fixed_alloc(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool)
+ struct gen_pool *pool, unsigned long start_addr)
{
struct genpool_data_fixed *fixed_data;
int order;
@@ -601,7 +605,8 @@ EXPORT_SYMBOL(gen_pool_fixed_alloc);
*/
unsigned long gen_pool_first_fit_order_align(unsigned long *map,
unsigned long size, unsigned long start,
- unsigned int nr, void *data, struct gen_pool *pool)
+ unsigned int nr, void *data, struct gen_pool *pool,
+ unsigned long start_addr)
{
unsigned long align_mask = roundup_pow_of_two(nr) - 1;
@@ -624,7 +629,7 @@ EXPORT_SYMBOL(gen_pool_first_fit_order_align);
*/
unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,
- struct gen_pool *pool)
+ struct gen_pool *pool, unsigned long start_addr)
{
unsigned long start_bit = size;
unsigned long len = size + 1;
diff --git a/lib/idr.c b/lib/idr.c
index fab2fd5..6ff3b1c 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -218,37 +218,6 @@ int idr_for_each(const struct idr *idr,
EXPORT_SYMBOL(idr_for_each);
/**
- * idr_get_next() - Find next populated entry.
- * @idr: IDR handle.
- * @nextid: Pointer to an ID.
- *
- * Returns the next populated entry in the tree with an ID greater than
- * or equal to the value pointed to by @nextid. On exit, @nextid is updated
- * to the ID of the found value. To use in a loop, the value pointed to by
- * nextid must be incremented by the user.
- */
-void *idr_get_next(struct idr *idr, int *nextid)
-{
- struct radix_tree_iter iter;
- void __rcu **slot;
- unsigned long base = idr->idr_base;
- unsigned long id = *nextid;
-
- id = (id < base) ? 0 : id - base;
- slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
- if (!slot)
- return NULL;
- id = iter.index + base;
-
- if (WARN_ON_ONCE(id > INT_MAX))
- return NULL;
-
- *nextid = id;
- return rcu_dereference_raw(*slot);
-}
-EXPORT_SYMBOL(idr_get_next);
-
-/**
* idr_get_next_ul() - Find next populated entry.
* @idr: IDR handle.
* @nextid: Pointer to an ID.
@@ -262,20 +231,53 @@ void *idr_get_next_ul(struct idr *idr, unsigned long *nextid)
{
struct radix_tree_iter iter;
void __rcu **slot;
+ void *entry = NULL;
unsigned long base = idr->idr_base;
unsigned long id = *nextid;
id = (id < base) ? 0 : id - base;
- slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
+ radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, id) {
+ entry = rcu_dereference_raw(*slot);
+ if (!entry)
+ continue;
+ if (!radix_tree_deref_retry(entry))
+ break;
+ if (slot != (void *)&idr->idr_rt.rnode &&
+ entry != (void *)RADIX_TREE_INTERNAL_NODE)
+ break;
+ slot = radix_tree_iter_retry(&iter);
+ }
if (!slot)
return NULL;
*nextid = iter.index + base;
- return rcu_dereference_raw(*slot);
+ return entry;
}
EXPORT_SYMBOL(idr_get_next_ul);
/**
+ * idr_get_next() - Find next populated entry.
+ * @idr: IDR handle.
+ * @nextid: Pointer to an ID.
+ *
+ * Returns the next populated entry in the tree with an ID greater than
+ * or equal to the value pointed to by @nextid. On exit, @nextid is updated
+ * to the ID of the found value. To use in a loop, the value pointed to by
+ * nextid must be incremented by the user.
+ */
+void *idr_get_next(struct idr *idr, int *nextid)
+{
+ unsigned long id = *nextid;
+ void *entry = idr_get_next_ul(idr, &id);
+
+ if (WARN_ON_ONCE(id > INT_MAX))
+ return NULL;
+ *nextid = id;
+ return entry;
+}
+EXPORT_SYMBOL(idr_get_next);
+
+/**
* idr_replace() - replace pointer for given ID.
* @idr: IDR handle.
* @ptr: New pointer to associate with the ID.
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index bc03ecc..e5cab5c 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -2172,7 +2172,7 @@ void __rcu **idr_get_free(struct radix_tree_root *root,
offset = radix_tree_find_next_bit(node, IDR_FREE,
offset + 1);
start = next_index(start, node, offset);
- if (start > max)
+ if (start > max || start == 0)
return ERR_PTR(-ENOSPC);
while (offset == RADIX_TREE_MAP_SIZE) {
offset = node->offset + 1;
diff --git a/lib/raid6/unroll.awk b/lib/raid6/unroll.awk
index c6aa036..0809805 100644
--- a/lib/raid6/unroll.awk
+++ b/lib/raid6/unroll.awk
@@ -13,7 +13,7 @@
for (i = 0; i < rep; ++i) {
tmp = $0
gsub(/\$\$/, i, tmp)
- gsub(/\$\#/, n, tmp)
+ gsub(/\$#/, n, tmp)
gsub(/\$\*/, "$", tmp)
print tmp
}
diff --git a/mm/Kconfig b/mm/Kconfig
index 13c907e..210a06e 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -643,6 +643,16 @@
information to userspace via debugfs.
If unsure, say N.
+config VMAP_LAZY_PURGING_FACTOR
+ int "multiplier to the size of purged vmap areas"
+ default "8" if ARM
+ default "32"
+ help
+ It is used as a multiplier to the max VA pages purged in a
+ single attempt. For 32-bit in order to reduce fragmentation
+ of vmalloc space, we decrease the default value to "8".
+
+
config GENERIC_EARLY_IOREMAP
bool
diff --git a/mm/filemap.c b/mm/filemap.c
index 0b794ee..aef0f79 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -444,7 +444,8 @@ int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start,
.range_end = end,
};
- if (!mapping_cap_writeback_dirty(mapping))
+ if (!mapping_cap_writeback_dirty(mapping) ||
+ !mapping_tagged(mapping, PAGECACHE_TAG_DIRTY))
return 0;
wbc_attach_fdatawrite_inode(&wbc, mapping->host);
diff --git a/mm/gup_benchmark.c b/mm/gup_benchmark.c
index 7405c9d8..7e6f2d2 100644
--- a/mm/gup_benchmark.c
+++ b/mm/gup_benchmark.c
@@ -23,6 +23,9 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
int nr;
struct page **pages;
+ if (gup->size > ULONG_MAX)
+ return -EINVAL;
+
nr_pages = gup->size / PAGE_SIZE;
pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
if (!pages)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 8dbf67f..2f1a179 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -63,6 +63,16 @@ static struct shrinker deferred_split_shrinker;
static atomic_t huge_zero_refcount;
struct page *huge_zero_page __read_mostly;
+bool transparent_hugepage_enabled(struct vm_area_struct *vma)
+{
+ if (vma_is_anonymous(vma))
+ return __transparent_hugepage_enabled(vma);
+ if (vma_is_shmem(vma) && shmem_huge_enabled(vma))
+ return __transparent_hugepage_enabled(vma);
+
+ return false;
+}
+
static struct page *get_huge_zero_page(void)
{
struct page *zero_page;
@@ -1329,7 +1339,7 @@ vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf, pmd_t orig_pmd)
get_page(page);
spin_unlock(vmf->ptl);
alloc:
- if (transparent_hugepage_enabled(vma) &&
+ if (__transparent_hugepage_enabled(vma) &&
!transparent_hugepage_debug_cow()) {
huge_gfp = alloc_hugepage_direct_gfpmask(vma);
new_page = alloc_hugepage_vma(huge_gfp, vma, haddr, HPAGE_PMD_ORDER);
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index 68c2f2f..7a93e1e 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -196,7 +196,7 @@ int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
again:
rcu_read_lock();
h_cg = hugetlb_cgroup_from_task(current);
- if (!css_tryget_online(&h_cg->css)) {
+ if (!css_tryget(&h_cg->css)) {
rcu_read_unlock();
goto again;
}
diff --git a/mm/internal.h b/mm/internal.h
index 379f88b..6ffd7d7 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -475,6 +475,16 @@ static inline void mminit_validate_memmodel_limits(unsigned long *start_pfn,
#define NODE_RECLAIM_SOME 0
#define NODE_RECLAIM_SUCCESS 1
+#ifdef CONFIG_NUMA
+extern int node_reclaim(struct pglist_data *, gfp_t, unsigned int);
+#else
+static inline int node_reclaim(struct pglist_data *pgdat, gfp_t mask,
+ unsigned int order)
+{
+ return NODE_RECLAIM_NOSCAN;
+}
+#endif
+
extern int hwpoison_filter(struct page *p);
extern u32 hwpoison_filter_dev_major;
diff --git a/mm/ksm.c b/mm/ksm.c
index b3191c0..9227c0b 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -870,13 +870,13 @@ static int remove_stable_node(struct stable_node *stable_node)
return 0;
}
- if (WARN_ON_ONCE(page_mapped(page))) {
- /*
- * This should not happen: but if it does, just refuse to let
- * merge_across_nodes be switched - there is no need to panic.
- */
- err = -EBUSY;
- } else {
+ /*
+ * Page could be still mapped if this races with __mmput() running in
+ * between ksm_exit() and exit_mmap(). Just refuse to let
+ * merge_across_nodes/max_page_sharing be switched.
+ */
+ err = -EBUSY;
+ if (!page_mapped(page)) {
/*
* The stable node did not yet appear stale to get_ksm_page(),
* since that allows for an unmapped ksm page to be recognized
diff --git a/mm/memblock.c b/mm/memblock.c
index 281dda1..feedf19 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1546,12 +1546,7 @@ void * __init memblock_virt_alloc_try_nid(
*/
void __init __memblock_free_early(phys_addr_t base, phys_addr_t size)
{
- phys_addr_t end = base + size - 1;
-
- memblock_dbg("%s: [%pa-%pa] %pF\n",
- __func__, &base, &end, (void *)_RET_IP_);
- kmemleak_free_part_phys(base, size);
- memblock_remove_range(&memblock.reserved, base, size);
+ memblock_free(base, size);
}
/**
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 65da189..3a3d109 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -851,7 +851,7 @@ struct mem_cgroup *get_mem_cgroup_from_mm(struct mm_struct *mm)
if (unlikely(!memcg))
memcg = root_mem_cgroup;
}
- } while (!css_tryget_online(&memcg->css));
+ } while (!css_tryget(&memcg->css));
rcu_read_unlock();
return memcg;
}
@@ -2225,6 +2225,15 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
}
/*
+ * Memcg doesn't have a dedicated reserve for atomic
+ * allocations. But like the global atomic pool, we need to
+ * put the burden of reclaim on regular allocation requests
+ * and let these go through as privileged allocations.
+ */
+ if (gfp_mask & __GFP_ATOMIC)
+ goto force;
+
+ /*
* Unlike in global OOM situations, memcg is not in a physical
* memory shortage. Allow dying and OOM-killed tasks to
* bypass the last charges so that they can exit quickly and
@@ -2669,7 +2678,7 @@ int memcg_kmem_charge(struct page *page, gfp_t gfp, int order)
struct mem_cgroup *memcg;
int ret = 0;
- if (memcg_kmem_bypass())
+ if (mem_cgroup_disabled() || memcg_kmem_bypass())
return 0;
memcg = get_mem_cgroup_from_current();
diff --git a/mm/memfd.c b/mm/memfd.c
index 908379a..bd4a596 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -41,7 +41,7 @@ static void memfd_tag_pins(struct address_space *mapping)
xa_lock_irq(&mapping->i_pages);
radix_tree_for_each_slot(slot, &mapping->i_pages, &iter, start) {
- page = radix_tree_deref_slot(slot);
+ page = radix_tree_deref_slot_protected(slot, &mapping->i_pages.xa_lock);
if (!page || radix_tree_exception(page)) {
if (radix_tree_deref_retry(page)) {
slot = radix_tree_iter_retry(&iter);
diff --git a/mm/memory.c b/mm/memory.c
index 8fec1a4..88e6f14 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4364,7 +4364,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
vmf.pud = pud_alloc(mm, p4d, address);
if (!vmf.pud)
return VM_FAULT_OOM;
- if (pud_none(*vmf.pud) && transparent_hugepage_enabled(vma)) {
+ if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
ret = create_huge_pud(&vmf);
if (!(ret & VM_FAULT_FALLBACK))
return ret;
@@ -4393,7 +4393,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
vmf.sequence = raw_read_seqcount(&vma->vm_sequence);
#endif
- if (pmd_none(*vmf.pmd) && transparent_hugepage_enabled(vma)) {
+ if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
ret = create_huge_pmd(&vmf);
if (!(ret & VM_FAULT_FALLBACK))
return ret;
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 85aa15b..ab8f46c7 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -326,12 +326,8 @@ static unsigned long find_smallest_section_pfn(int nid, struct zone *zone,
unsigned long start_pfn,
unsigned long end_pfn)
{
- struct mem_section *ms;
-
for (; start_pfn < end_pfn; start_pfn += PAGES_PER_SECTION) {
- ms = __pfn_to_section(start_pfn);
-
- if (unlikely(!valid_section(ms)))
+ if (unlikely(!pfn_to_online_page(start_pfn)))
continue;
if (unlikely(pfn_to_nid(start_pfn) != nid))
@@ -351,15 +347,12 @@ static unsigned long find_biggest_section_pfn(int nid, struct zone *zone,
unsigned long start_pfn,
unsigned long end_pfn)
{
- struct mem_section *ms;
unsigned long pfn;
/* pfn is the end pfn of a memory section. */
pfn = end_pfn - 1;
for (; pfn >= start_pfn; pfn -= PAGES_PER_SECTION) {
- ms = __pfn_to_section(pfn);
-
- if (unlikely(!valid_section(ms)))
+ if (unlikely(!pfn_to_online_page(pfn)))
continue;
if (unlikely(pfn_to_nid(pfn) != nid))
@@ -381,7 +374,6 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
unsigned long z = zone_end_pfn(zone); /* zone_end_pfn namespace clash */
unsigned long zone_end_pfn = z;
unsigned long pfn;
- struct mem_section *ms;
int nid = zone_to_nid(zone);
zone_span_writelock(zone);
@@ -419,9 +411,7 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
*/
pfn = zone_start_pfn;
for (; pfn < zone_end_pfn; pfn += PAGES_PER_SECTION) {
- ms = __pfn_to_section(pfn);
-
- if (unlikely(!valid_section(ms)))
+ if (unlikely(!pfn_to_online_page(pfn)))
continue;
if (page_zone(pfn_to_page(pfn)) != zone)
@@ -442,70 +432,33 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
zone_span_writeunlock(zone);
}
-static void shrink_pgdat_span(struct pglist_data *pgdat,
- unsigned long start_pfn, unsigned long end_pfn)
+static void update_pgdat_span(struct pglist_data *pgdat)
{
- unsigned long pgdat_start_pfn = pgdat->node_start_pfn;
- unsigned long p = pgdat_end_pfn(pgdat); /* pgdat_end_pfn namespace clash */
- unsigned long pgdat_end_pfn = p;
- unsigned long pfn;
- struct mem_section *ms;
- int nid = pgdat->node_id;
+ unsigned long node_start_pfn = 0, node_end_pfn = 0;
+ struct zone *zone;
- if (pgdat_start_pfn == start_pfn) {
- /*
- * If the section is smallest section in the pgdat, it need
- * shrink pgdat->node_start_pfn and pgdat->node_spanned_pages.
- * In this case, we find second smallest valid mem_section
- * for shrinking zone.
- */
- pfn = find_smallest_section_pfn(nid, NULL, end_pfn,
- pgdat_end_pfn);
- if (pfn) {
- pgdat->node_start_pfn = pfn;
- pgdat->node_spanned_pages = pgdat_end_pfn - pfn;
+ for (zone = pgdat->node_zones;
+ zone < pgdat->node_zones + MAX_NR_ZONES; zone++) {
+ unsigned long zone_end_pfn = zone->zone_start_pfn +
+ zone->spanned_pages;
+
+ /* No need to lock the zones, they can't change. */
+ if (!zone->spanned_pages)
+ continue;
+ if (!node_end_pfn) {
+ node_start_pfn = zone->zone_start_pfn;
+ node_end_pfn = zone_end_pfn;
+ continue;
}
- } else if (pgdat_end_pfn == end_pfn) {
- /*
- * If the section is biggest section in the pgdat, it need
- * shrink pgdat->node_spanned_pages.
- * In this case, we find second biggest valid mem_section for
- * shrinking zone.
- */
- pfn = find_biggest_section_pfn(nid, NULL, pgdat_start_pfn,
- start_pfn);
- if (pfn)
- pgdat->node_spanned_pages = pfn - pgdat_start_pfn + 1;
+
+ if (zone_end_pfn > node_end_pfn)
+ node_end_pfn = zone_end_pfn;
+ if (zone->zone_start_pfn < node_start_pfn)
+ node_start_pfn = zone->zone_start_pfn;
}
- /*
- * If the section is not biggest or smallest mem_section in the pgdat,
- * it only creates a hole in the pgdat. So in this case, we need not
- * change the pgdat.
- * But perhaps, the pgdat has only hole data. Thus it check the pgdat
- * has only hole or not.
- */
- pfn = pgdat_start_pfn;
- for (; pfn < pgdat_end_pfn; pfn += PAGES_PER_SECTION) {
- ms = __pfn_to_section(pfn);
-
- if (unlikely(!valid_section(ms)))
- continue;
-
- if (pfn_to_nid(pfn) != nid)
- continue;
-
- /* If the section is current section, it continues the loop */
- if (start_pfn == pfn)
- continue;
-
- /* If we find valid section, we have nothing to do */
- return;
- }
-
- /* The pgdat has no valid section */
- pgdat->node_start_pfn = 0;
- pgdat->node_spanned_pages = 0;
+ pgdat->node_start_pfn = node_start_pfn;
+ pgdat->node_spanned_pages = node_end_pfn - node_start_pfn;
}
static void __remove_zone(struct zone *zone, unsigned long start_pfn)
@@ -514,9 +467,19 @@ static void __remove_zone(struct zone *zone, unsigned long start_pfn)
int nr_pages = PAGES_PER_SECTION;
unsigned long flags;
+#ifdef CONFIG_ZONE_DEVICE
+ /*
+ * Zone shrinking code cannot properly deal with ZONE_DEVICE. So
+ * we will not try to shrink the zones - which is okay as
+ * set_zone_contiguous() cannot deal with ZONE_DEVICE either way.
+ */
+ if (zone_idx(zone) == ZONE_DEVICE)
+ return;
+#endif
+
pgdat_resize_lock(zone->zone_pgdat, &flags);
shrink_zone_span(zone, start_pfn, start_pfn + nr_pages);
- shrink_pgdat_span(pgdat, start_pfn, start_pfn + nr_pages);
+ update_pgdat_span(pgdat);
pgdat_resize_unlock(zone->zone_pgdat, &flags);
}
@@ -927,7 +890,6 @@ static struct zone * __meminit move_pfn_range(int online_type, int nid,
return zone;
}
-/* Must be protected by mem_hotplug_begin() or a device_lock */
int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type)
{
unsigned long flags;
@@ -939,6 +901,8 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
struct memory_notify arg;
struct memory_block *mem;
+ mem_hotplug_begin();
+
/*
* We can't use pfn_to_nid() because nid might be stored in struct page
* which is not yet initialized. Instead, we find nid from memory block.
@@ -1004,6 +968,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
if (onlined_pages)
memory_notify(MEM_ONLINE, &arg);
+ mem_hotplug_done();
return 0;
failed_addition:
@@ -1011,6 +976,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
(unsigned long long) pfn << PAGE_SHIFT,
(((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1);
memory_notify(MEM_CANCEL_ONLINE, &arg);
+ mem_hotplug_done();
return ret;
}
#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
@@ -1189,7 +1155,12 @@ static int online_memory_block(struct memory_block *mem, void *arg)
return device_online(&mem->dev);
}
-/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
+/*
+ * NOTE: The caller must call lock_device_hotplug() to serialize hotplug
+ * and online/offline operations (triggered e.g. by sysfs).
+ *
+ * we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG
+ */
int __ref add_memory_resource(int nid, struct resource *res, bool online)
{
u64 start, size;
@@ -1241,26 +1212,26 @@ int __ref add_memory_resource(int nid, struct resource *res, bool online)
/* create new memmap entry */
firmware_map_add_hotplug(start, start + size, "System RAM");
+ /* device_online() will take the lock when calling online_pages() */
+ mem_hotplug_done();
+
/* online pages if requested */
if (online)
walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1),
NULL, online_memory_block);
- goto out;
-
+ return ret;
error:
/* rollback pgdat allocation and others */
if (new_node)
rollback_node_hotadd(nid);
memblock_remove(start, size);
-
-out:
mem_hotplug_done();
return ret;
}
-EXPORT_SYMBOL_GPL(add_memory_resource);
-int __ref add_memory(int nid, u64 start, u64 size)
+/* requires device_hotplug_lock, see add_memory_resource() */
+int __ref __add_memory(int nid, u64 start, u64 size)
{
struct resource *res;
int ret;
@@ -1274,6 +1245,17 @@ int __ref add_memory(int nid, u64 start, u64 size)
release_memory_resource(res);
return ret;
}
+
+int add_memory(int nid, u64 start, u64 size)
+{
+ int rc;
+
+ lock_device_hotplug();
+ rc = __add_memory(nid, start, size);
+ unlock_device_hotplug();
+
+ return rc;
+}
EXPORT_SYMBOL_GPL(add_memory);
#ifdef CONFIG_MEMORY_HOTREMOVE
@@ -1712,10 +1694,16 @@ static int __ref __offline_pages(unsigned long start_pfn,
return -EINVAL;
if (!IS_ALIGNED(end_pfn, pageblock_nr_pages))
return -EINVAL;
+
+ mem_hotplug_begin();
+
/* This makes hotplug much easier...and readable.
we assume this for now. .*/
- if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start,
+ &valid_end)) {
+ mem_hotplug_done();
return -EINVAL;
+ }
zone = page_zone(pfn_to_page(valid_start));
node = zone_to_nid(zone);
@@ -1724,8 +1712,10 @@ static int __ref __offline_pages(unsigned long start_pfn,
/* set above range as isolated */
ret = start_isolate_page_range(start_pfn, end_pfn,
MIGRATE_MOVABLE, true);
- if (ret)
+ if (ret) {
+ mem_hotplug_done();
return ret;
+ }
arg.start_pfn = start_pfn;
arg.nr_pages = nr_pages;
@@ -1796,6 +1786,7 @@ static int __ref __offline_pages(unsigned long start_pfn,
writeback_set_ratelimit();
memory_notify(MEM_OFFLINE, &arg);
+ mem_hotplug_done();
return 0;
failed_removal:
@@ -1805,10 +1796,10 @@ static int __ref __offline_pages(unsigned long start_pfn,
memory_notify(MEM_CANCEL_OFFLINE, &arg);
/* pushback to free area */
undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE);
+ mem_hotplug_done();
return ret;
}
-/* Must be protected by mem_hotplug_begin() or a device_lock */
int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
{
return __offline_pages(start_pfn, start_pfn + nr_pages);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 4b81d09..9a90d49 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -671,7 +671,9 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
* 1 - there is unmovable page, but MPOL_MF_MOVE* & MPOL_MF_STRICT were
* specified.
* 0 - queue pages successfully or no misplaced page.
- * -EIO - there is misplaced page and only MPOL_MF_STRICT was specified.
+ * errno - i.e. misplaced pages with MPOL_MF_STRICT specified (-EIO) or
+ * memory range specified by nodemask and maxnode points outside
+ * your accessible address space (-EFAULT)
*/
static int
queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
@@ -1286,7 +1288,7 @@ static long do_mbind(unsigned long start, unsigned long len,
flags | MPOL_MF_INVERT, &pagelist);
if (ret < 0) {
- err = -EIO;
+ err = ret;
goto up_out;
}
@@ -1305,10 +1307,12 @@ static long do_mbind(unsigned long start, unsigned long len,
if ((ret > 0) || (nr_failed && (flags & MPOL_MF_STRICT)))
err = -EIO;
- } else
- putback_movable_pages(&pagelist);
-
+ } else {
up_out:
+ if (!list_empty(&pagelist))
+ putback_movable_pages(&pagelist);
+ }
+
up_write(&mm->mmap_sem);
mpol_out:
mpol_put(new);
diff --git a/mm/migrate.c b/mm/migrate.c
index c00733e..fddfaf0 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1512,9 +1512,11 @@ static int do_move_pages_to_node(struct mm_struct *mm,
/*
* Resolves the given address to a struct page, isolates it from the LRU and
* puts it to the given pagelist.
- * Returns -errno if the page cannot be found/isolated or 0 when it has been
- * queued or the page doesn't need to be migrated because it is already on
- * the target node
+ * Returns:
+ * errno - if the page cannot be found/isolated
+ * 0 - when it doesn't have to be migrated because it is already on the
+ * target node
+ * 1 - when it has been queued
*/
static int add_page_for_migration(struct mm_struct *mm, unsigned long addr,
int node, struct list_head *pagelist, bool migrate_all)
@@ -1553,7 +1555,7 @@ static int add_page_for_migration(struct mm_struct *mm, unsigned long addr,
if (PageHuge(page)) {
if (PageHead(page)) {
isolate_huge_page(page, pagelist);
- err = 0;
+ err = 1;
}
} else {
struct page *head;
@@ -1563,7 +1565,7 @@ static int add_page_for_migration(struct mm_struct *mm, unsigned long addr,
if (err)
goto out_putpage;
- err = 0;
+ err = 1;
list_add_tail(&head->lru, pagelist);
mod_node_page_state(page_pgdat(head),
NR_ISOLATED_ANON + page_is_file_cache(head),
@@ -1640,8 +1642,17 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes,
*/
err = add_page_for_migration(mm, addr, current_node,
&pagelist, flags & MPOL_MF_MOVE_ALL);
- if (!err)
+
+ if (!err) {
+ /* The page is already on the target node */
+ err = store_status(status, i, current_node, 1);
+ if (err)
+ goto out_flush;
continue;
+ } else if (err > 0) {
+ /* The page is successfully queued for migration */
+ continue;
+ }
err = store_status(status, i, err, 1);
if (err)
@@ -2052,15 +2063,26 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
/*
- * Clear the old entry under pagetable lock and establish the new PTE.
- * Any parallel GUP will either observe the old page blocking on the
- * page lock, block on the page table lock or observe the new page.
- * The SetPageUptodate on the new page and page_add_new_anon_rmap
- * guarantee the copy is visible before the pagetable update.
+ * Overwrite the old entry under pagetable lock and establish
+ * the new PTE. Any parallel GUP will either observe the old
+ * page blocking on the page lock, block on the page table
+ * lock or observe the new page. The SetPageUptodate on the
+ * new page and page_add_new_anon_rmap guarantee the copy is
+ * visible before the pagetable update.
*/
flush_cache_range(vma, mmun_start, mmun_end);
page_add_anon_rmap(new_page, vma, mmun_start, true);
- pmdp_huge_clear_flush_notify(vma, mmun_start, pmd);
+ /*
+ * At this point the pmd is numa/protnone (i.e. non present) and the TLB
+ * has already been flushed globally. So no TLB can be currently
+ * caching this non present pmd mapping. There's no need to clear the
+ * pmd before doing set_pmd_at(), nor to flush the TLB after
+ * set_pmd_at(). Clearing the pmd here would introduce a race
+ * condition against MADV_DONTNEED, because MADV_DONTNEED only holds the
+ * mmap_sem for reading. If the pmd is set to NULL at any given time,
+ * MADV_DONTNEED won't wait on the pmd lock and it'll skip clearing this
+ * pmd.
+ */
set_pmd_at(mm, mmun_start, pmd, entry);
update_mmu_cache_pmd(vma, address, &entry);
@@ -2074,7 +2096,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
* No need to double call mmu_notifier->invalidate_range() callback as
* the above pmdp_huge_clear_flush_notify() did already call it.
*/
- mmu_notifier_invalidate_range_only_end(mm, mmun_start, mmun_end);
+ mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
/* Take an "isolate" reference and put new page on the LRU. */
get_page(new_page);
diff --git a/mm/mmap.c b/mm/mmap.c
index 8f503c1..10b5f6e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -89,12 +89,6 @@ static void unmap_region(struct mm_struct *mm,
* MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes
* w: (no) no w: (no) no w: (copy) copy w: (no) no
* x: (no) no x: (no) yes x: (no) yes x: (yes) yes
- *
- * On arm64, PROT_EXEC has the following behaviour for both MAP_SHARED and
- * MAP_PRIVATE:
- * r: (no) no
- * w: (no) no
- * x: (yes) yes
*/
pgprot_t protection_map[16] __ro_after_init = {
__P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index ea4fd3a..43df0c5 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2149,6 +2149,13 @@ EXPORT_SYMBOL(tag_pages_for_writeback);
* not miss some pages (e.g., because some other process has cleared TOWRITE
* tag we set). The rule we follow is that TOWRITE tag can be cleared only
* by the process clearing the DIRTY tag (and submitting the page for IO).
+ *
+ * To avoid deadlocks between range_cyclic writeback and callers that hold
+ * pages in PageWriteback to aggregate IO until write_cache_pages() returns,
+ * we do not loop back to the start of the file. Doing so causes a page
+ * lock/page writeback access order inversion - we should only ever lock
+ * multiple pages in ascending page->index order, and looping back to the start
+ * of the file violates that rule and causes deadlocks.
*/
int write_cache_pages(struct address_space *mapping,
struct writeback_control *wbc, writepage_t writepage,
@@ -2163,7 +2170,6 @@ int write_cache_pages(struct address_space *mapping,
pgoff_t index;
pgoff_t end; /* Inclusive */
pgoff_t done_index;
- int cycled;
int range_whole = 0;
int tag;
@@ -2171,23 +2177,17 @@ int write_cache_pages(struct address_space *mapping,
if (wbc->range_cyclic) {
writeback_index = mapping->writeback_index; /* prev offset */
index = writeback_index;
- if (index == 0)
- cycled = 1;
- else
- cycled = 0;
end = -1;
} else {
index = wbc->range_start >> PAGE_SHIFT;
end = wbc->range_end >> PAGE_SHIFT;
if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
range_whole = 1;
- cycled = 1; /* ignore range_cyclic tests */
}
if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
tag = PAGECACHE_TAG_TOWRITE;
else
tag = PAGECACHE_TAG_DIRTY;
-retry:
if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
tag_pages_for_writeback(mapping, index, end);
done_index = index;
@@ -2279,17 +2279,14 @@ int write_cache_pages(struct address_space *mapping,
pagevec_release(&pvec);
cond_resched();
}
- if (!cycled && !done) {
- /*
- * range_cyclic:
- * We hit the last page and there is more work to be done: wrap
- * back to the start of the file
- */
- cycled = 1;
- index = 0;
- end = writeback_index - 1;
- goto retry;
- }
+
+ /*
+ * If we hit the last page and there is more work to be done: wrap
+ * back the index back to the start of the file for the next
+ * time we are called.
+ */
+ if (wbc->range_cyclic && !done)
+ done_index = 0;
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
mapping->writeback_index = done_index;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b8b48c0..9357559 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1828,6 +1828,14 @@ void __init page_alloc_init_late(void)
wait_for_completion(&pgdat_init_all_done_comp);
/*
+ * The number of managed pages has changed due to the initialisation
+ * so the pcpu batch and high limits needs to be updated or the limits
+ * will be artificially small.
+ */
+ for_each_populated_zone(zone)
+ zone_pcp_update(zone);
+
+ /*
* We initialized the rest of the deferred pages. Permanently disable
* on-demand struct page initialization.
*/
@@ -4839,16 +4847,19 @@ unsigned long get_zeroed_page(gfp_t gfp_mask)
}
EXPORT_SYMBOL(get_zeroed_page);
-void __free_pages(struct page *page, unsigned int order)
+static inline void free_the_page(struct page *page, unsigned int order)
{
- if (put_page_testzero(page)) {
- if (order == 0)
- free_unref_page(page);
- else
- __free_pages_ok(page, order);
- }
+ if (order == 0) /* Via pcp? */
+ free_unref_page(page);
+ else
+ __free_pages_ok(page, order);
}
+void __free_pages(struct page *page, unsigned int order)
+{
+ if (put_page_testzero(page))
+ free_the_page(page, order);
+}
EXPORT_SYMBOL(__free_pages);
void free_pages(unsigned long addr, unsigned int order)
@@ -4897,14 +4908,8 @@ void __page_frag_cache_drain(struct page *page, unsigned int count)
{
VM_BUG_ON_PAGE(page_ref_count(page) == 0, page);
- if (page_ref_sub_and_test(page, count)) {
- unsigned int order = compound_order(page);
-
- if (order == 0)
- free_unref_page(page);
- else
- __free_pages_ok(page, order);
- }
+ if (page_ref_sub_and_test(page, count))
+ free_the_page(page, compound_order(page));
}
EXPORT_SYMBOL(__page_frag_cache_drain);
@@ -4970,7 +4975,7 @@ void page_frag_free(void *addr)
struct page *page = virt_to_head_page(addr);
if (unlikely(put_page_testzero(page)))
- __free_pages_ok(page, compound_order(page));
+ free_the_page(page, compound_order(page));
}
EXPORT_SYMBOL(page_frag_free);
@@ -8433,7 +8438,6 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages)
}
#endif
-#ifdef CONFIG_MEMORY_HOTPLUG
/*
* The zone indicated has a new number of managed_pages; batch sizes and percpu
* page high values need to be recalulated.
@@ -8447,7 +8451,6 @@ void __meminit zone_pcp_update(struct zone *zone)
per_cpu_ptr(zone->pageset, cpu));
mutex_unlock(&pcp_batch_high_lock);
}
-#endif
void zone_pcp_reset(struct zone *zone)
{
diff --git a/mm/page_io.c b/mm/page_io.c
index abc1466..e763047 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -77,6 +77,7 @@ static void swap_slot_free_notify(struct page *page)
{
struct swap_info_struct *sis;
struct gendisk *disk;
+ swp_entry_t entry;
/*
* There is no guarantee that the page is in swap cache - the software
@@ -108,11 +109,11 @@ static void swap_slot_free_notify(struct page *page)
* we again wish to reclaim it.
*/
disk = sis->bdev->bd_disk;
- if (disk->fops->swap_slot_free_notify) {
- swp_entry_t entry;
+ entry.val = page_private(page);
+ if (disk->fops->swap_slot_free_notify &&
+ __swap_count(sis, entry) == 1) {
unsigned long offset;
- entry.val = page_private(page);
offset = swp_offset(entry);
SetPageDirty(page);
diff --git a/mm/shmem.c b/mm/shmem.c
index 8ad4588..98c2bf6 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2711,7 +2711,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
}
shmem_falloc.waitq = &shmem_falloc_waitq;
- shmem_falloc.start = unmap_start >> PAGE_SHIFT;
+ shmem_falloc.start = (u64)unmap_start >> PAGE_SHIFT;
shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT;
spin_lock(&inode->i_lock);
inode->i_private = &shmem_falloc;
diff --git a/mm/slub.c b/mm/slub.c
index 9e5a7e6..d5f745f 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1487,6 +1487,25 @@ static int init_cache_random_seq(struct kmem_cache *s)
return 0;
}
+/* re-initialize the random sequence cache */
+static int reinit_cache_random_seq(struct kmem_cache *s)
+{
+ int err;
+
+ if (s->random_seq) {
+ cache_random_seq_destroy(s);
+ err = init_cache_random_seq(s);
+
+ if (err) {
+ pr_err("SLUB: Unable to re-initialize random sequence cache for %s\n",
+ s->name);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
/* Initialize each random sequence freelist per cache */
static void __init init_freelist_randomization(void)
{
@@ -1561,6 +1580,10 @@ static inline int init_cache_random_seq(struct kmem_cache *s)
{
return 0;
}
+static inline int reinit_cache_random_seq(struct kmem_cache *s)
+{
+ return 0;
+}
static inline void init_freelist_randomization(void) { }
static inline bool shuffle_freelist(struct kmem_cache *s, struct page *page)
{
@@ -4935,6 +4958,7 @@ static ssize_t order_store(struct kmem_cache *s,
return -EINVAL;
calculate_sizes(s, order);
+ reinit_cache_random_seq(s);
return length;
}
@@ -5172,6 +5196,7 @@ static ssize_t red_zone_store(struct kmem_cache *s,
s->flags |= SLAB_RED_ZONE;
}
calculate_sizes(s, -1);
+ reinit_cache_random_seq(s);
return length;
}
SLAB_ATTR(red_zone);
@@ -5192,6 +5217,7 @@ static ssize_t poison_store(struct kmem_cache *s,
s->flags |= SLAB_POISON;
}
calculate_sizes(s, -1);
+ reinit_cache_random_seq(s);
return length;
}
SLAB_ATTR(poison);
@@ -5213,6 +5239,7 @@ static ssize_t store_user_store(struct kmem_cache *s,
s->flags |= SLAB_STORE_USER;
}
calculate_sizes(s, -1);
+ reinit_cache_random_seq(s);
return length;
}
SLAB_ATTR(store_user);
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index d03611a..5ee7cfc 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -1229,7 +1229,8 @@ static unsigned long lazy_max_pages(void)
log = fls(num_online_cpus());
- return log * (32UL * 1024 * 1024 / PAGE_SIZE);
+ return log * (1UL * CONFIG_VMAP_LAZY_PURGING_FACTOR *
+ 1024 * 1024 / PAGE_SIZE);
}
static atomic_long_t vmap_lazy_nr = ATOMIC_LONG_INIT(0);
@@ -3437,6 +3438,9 @@ static int s_show(struct seq_file *m, void *p)
v = va->vm;
+ if (v->flags & VM_LOWMEM)
+ return 0;
+
seq_printf(m, "0x%pK-0x%pK %7ld",
v->addr, v->addr + v->size, v->size);
@@ -3464,9 +3468,6 @@ static int s_show(struct seq_file *m, void *p)
if (is_vmalloc_addr(v->pages))
seq_puts(m, " vpages");
- if (v->flags & VM_LOWMEM)
- seq_puts(m, " lowmem");
-
show_numa_info(m, v);
seq_putc(m, '\n');
return 0;
diff --git a/mm/vmstat.c b/mm/vmstat.c
index dc2287c..6f05c71 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1833,12 +1833,13 @@ static bool need_update(int cpu)
/*
* The fast way of checking if there are any vmstat diffs.
- * This works because the diffs are byte sized items.
*/
- if (memchr_inv(p->vm_stat_diff, 0, NR_VM_ZONE_STAT_ITEMS))
+ if (memchr_inv(p->vm_stat_diff, 0, NR_VM_ZONE_STAT_ITEMS *
+ sizeof(p->vm_stat_diff[0])))
return true;
#ifdef CONFIG_NUMA
- if (memchr_inv(p->vm_numa_stat_diff, 0, NR_VM_NUMA_STAT_ITEMS))
+ if (memchr_inv(p->vm_numa_stat_diff, 0, NR_VM_NUMA_STAT_ITEMS *
+ sizeof(p->vm_numa_stat_diff[0])))
return true;
#endif
}
@@ -1980,7 +1981,7 @@ void __init init_mm_internals(void)
#endif
#ifdef CONFIG_PROC_FS
proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
- proc_create_seq("pagetypeinfo", 0444, NULL, &pagetypeinfo_op);
+ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);
proc_create_seq("vmstat", 0444, NULL, &vmstat_op);
proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op);
#endif
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 0ae2f5f..12966a8 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -2092,6 +2092,11 @@ static int zs_page_migrate(struct address_space *mapping, struct page *newpage,
zs_pool_dec_isolated(pool);
}
+ if (page_zone(newpage) != page_zone(page)) {
+ dec_zone_page_state(page, NR_ZSPAGES);
+ inc_zone_page_state(newpage, NR_ZSPAGES);
+ }
+
reset_page(page);
put_page(page);
page = newpage;
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 44df1c3..e9cd8ef 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -114,6 +114,7 @@ int vlan_check_real_dev(struct net_device *real_dev,
void vlan_setup(struct net_device *dev);
int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack);
void unregister_vlan_dev(struct net_device *dev, struct list_head *head);
+void vlan_dev_uninit(struct net_device *dev);
bool vlan_dev_inherit_address(struct net_device *dev,
struct net_device *real_dev);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fce3b7e..84ef837 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -612,7 +612,8 @@ static int vlan_dev_init(struct net_device *dev)
return 0;
}
-static void vlan_dev_uninit(struct net_device *dev)
+/* Note: this function might be called multiple times for the same device. */
+void vlan_dev_uninit(struct net_device *dev)
{
struct vlan_priority_tci_mapping *pm;
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index 9b60c1e..74042b9 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -110,11 +110,13 @@ static int vlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct ifla_vlan_flags *flags;
struct ifla_vlan_qos_mapping *m;
struct nlattr *attr;
- int rem;
+ int rem, err;
if (data[IFLA_VLAN_FLAGS]) {
flags = nla_data(data[IFLA_VLAN_FLAGS]);
- vlan_dev_change_flags(dev, flags->flags, flags->mask);
+ err = vlan_dev_change_flags(dev, flags->flags, flags->mask);
+ if (err)
+ return err;
}
if (data[IFLA_VLAN_INGRESS_QOS]) {
nla_for_each_nested(attr, data[IFLA_VLAN_INGRESS_QOS], rem) {
@@ -125,7 +127,9 @@ static int vlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (data[IFLA_VLAN_EGRESS_QOS]) {
nla_for_each_nested(attr, data[IFLA_VLAN_EGRESS_QOS], rem) {
m = nla_data(attr);
- vlan_dev_set_egress_priority(dev, m->from, m->to);
+ err = vlan_dev_set_egress_priority(dev, m->from, m->to);
+ if (err)
+ return err;
}
}
return 0;
@@ -181,10 +185,11 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
return -EINVAL;
err = vlan_changelink(dev, tb, data, extack);
- if (err < 0)
- return err;
-
- return register_vlan_dev(dev, extack);
+ if (!err)
+ err = register_vlan_dev(dev, extack);
+ if (err)
+ vlan_dev_uninit(dev);
+ return err;
}
static inline size_t vlan_qos_map_size(unsigned int n)
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index 49a16ce..420a98b 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -879,15 +879,24 @@ static struct notifier_block aarp_notifier = {
static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 };
-void __init aarp_proto_init(void)
+int __init aarp_proto_init(void)
{
+ int rc;
+
aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv);
- if (!aarp_dl)
+ if (!aarp_dl) {
printk(KERN_CRIT "Unable to register AARP with SNAP.\n");
+ return -ENOMEM;
+ }
timer_setup(&aarp_timer, aarp_expire_timeout, 0);
aarp_timer.expires = jiffies + sysctl_aarp_expiry_time;
add_timer(&aarp_timer);
- register_netdevice_notifier(&aarp_notifier);
+ rc = register_netdevice_notifier(&aarp_notifier);
+ if (rc) {
+ del_timer_sync(&aarp_timer);
+ unregister_snap_client(aarp_dl);
+ }
+ return rc;
}
/* Remove the AARP entries associated with a device. */
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 9abb18f..2880ac4 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1909,9 +1909,6 @@ static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B };
EXPORT_SYMBOL(atrtr_get_dev);
EXPORT_SYMBOL(atalk_find_dev_addr);
-static const char atalk_err_snap[] __initconst =
- KERN_CRIT "Unable to register DDP with SNAP.\n";
-
/* Called by proto.c on kernel start up */
static int __init atalk_init(void)
{
@@ -1926,17 +1923,23 @@ static int __init atalk_init(void)
goto out_proto;
ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv);
- if (!ddp_dl)
- printk(atalk_err_snap);
+ if (!ddp_dl) {
+ pr_crit("Unable to register DDP with SNAP.\n");
+ rc = -ENOMEM;
+ goto out_sock;
+ }
dev_add_pack(<alk_packet_type);
dev_add_pack(&ppptalk_packet_type);
rc = register_netdevice_notifier(&ddp_notifier);
if (rc)
- goto out_sock;
+ goto out_snap;
- aarp_proto_init();
+ rc = aarp_proto_init();
+ if (rc)
+ goto out_dev;
+
rc = atalk_proc_init();
if (rc)
goto out_aarp;
@@ -1950,11 +1953,13 @@ static int __init atalk_init(void)
atalk_proc_exit();
out_aarp:
aarp_cleanup_module();
+out_dev:
unregister_netdevice_notifier(&ddp_notifier);
-out_sock:
+out_snap:
dev_remove_pack(&ppptalk_packet_type);
dev_remove_pack(<alk_packet_type);
unregister_snap_client(ddp_dl);
+out_sock:
sock_unregister(PF_APPLETALK);
out_proto:
proto_unregister(&ddp_proto);
diff --git a/net/atm/common.c b/net/atm/common.c
index a38c174..6772edd 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -667,7 +667,7 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait)
mask |= EPOLLHUP;
/* readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* writable? */
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 36f2441..f594183 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -35,6 +35,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
+#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/pkt_sched.h>
@@ -379,14 +380,18 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
unsigned char *ogm_buff;
u32 random_seqno;
+ mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
+
/* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno));
atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno);
hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
- if (!ogm_buff)
+ if (!ogm_buff) {
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
return -ENOMEM;
+ }
hard_iface->bat_iv.ogm_buff = ogm_buff;
@@ -398,35 +403,59 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
batadv_ogm_packet->reserved = 0;
batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+
return 0;
}
static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
{
+ mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
+
kfree(hard_iface->bat_iv.ogm_buff);
hard_iface->bat_iv.ogm_buff = NULL;
+
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
struct batadv_ogm_packet *batadv_ogm_packet;
- unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
+ void *ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
+ mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
+
+ ogm_buff = hard_iface->bat_iv.ogm_buff;
+ if (!ogm_buff)
+ goto unlock;
+
+ batadv_ogm_packet = ogm_buff;
ether_addr_copy(batadv_ogm_packet->orig,
hard_iface->net_dev->dev_addr);
ether_addr_copy(batadv_ogm_packet->prev_sender,
hard_iface->net_dev->dev_addr);
+
+unlock:
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}
static void
batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
{
struct batadv_ogm_packet *batadv_ogm_packet;
- unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
+ void *ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
+ mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
+
+ ogm_buff = hard_iface->bat_iv.ogm_buff;
+ if (!ogm_buff)
+ goto unlock;
+
+ batadv_ogm_packet = ogm_buff;
batadv_ogm_packet->ttl = BATADV_TTL;
+
+unlock:
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}
/* when do we schedule our own ogm to be sent */
@@ -924,7 +953,11 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
}
}
-static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+/**
+ * batadv_iv_ogm_schedule_buff() - schedule submission of hardif ogm buffer
+ * @hard_iface: interface whose ogm buffer should be transmitted
+ */
+static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
@@ -935,9 +968,7 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
u16 tvlv_len = 0;
unsigned long send_time;
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
- return;
+ lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
/* the interface gets activated here to avoid race conditions between
* the moment of activating the interface in
@@ -1005,6 +1036,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(primary_if);
}
+static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+{
+ if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
+ hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ return;
+
+ mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
+ batadv_iv_ogm_schedule_buff(hard_iface);
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+}
+
/**
* batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an
* originator
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 36f0962..c4e0435 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -29,6 +29,7 @@
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/printk.h>
#include <linux/rculist.h>
@@ -933,6 +934,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
INIT_LIST_HEAD(&hard_iface->list);
INIT_HLIST_HEAD(&hard_iface->neigh_list);
+ mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
spin_lock_init(&hard_iface->neigh_list_lock);
kref_init(&hard_iface->refcount);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index fdba8a1..87e54f8 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -91,6 +91,9 @@ struct batadv_hard_iface_bat_iv {
/** @ogm_seqno: OGM sequence number - used to identify each OGM */
atomic_t ogm_seqno;
+
+ /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+ struct mutex ogm_buff_mutex;
};
/**
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index a4d6d77..277874b 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -489,7 +489,7 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
if (sk->sk_state == BT_LISTEN)
return bt_accept_poll(sk);
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
@@ -499,7 +499,7 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= EPOLLHUP;
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
if (sk->sk_state == BT_CLOSED)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 15d1cb5..db735d0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -931,6 +931,14 @@ static void hci_req_directed_advertising(struct hci_request *req,
return;
memset(&cp, 0, sizeof(cp));
+
+ /* Some controllers might reject command if intervals are not
+ * within range for undirected advertising.
+ * BCM20702A0 is known to be affected by this.
+ */
+ cp.min_interval = cpu_to_le16(0x0020);
+ cp.max_interval = cpu_to_le16(0x0020);
+
cp.type = LE_ADV_DIRECT_IND;
cp.own_address_type = own_addr_type;
cp.direct_addr_type = conn->dst_type;
@@ -1165,8 +1173,10 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
if (!conn)
return ERR_PTR(-ENOMEM);
- if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0)
+ if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0) {
+ hci_conn_del(conn);
return ERR_PTR(-EBUSY);
+ }
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5afd67e..e03faca 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -841,8 +841,8 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) {
struct hci_cp_le_write_def_data_len cp;
- cp.tx_len = hdev->le_max_tx_len;
- cp.tx_time = hdev->le_max_tx_time;
+ cp.tx_len = cpu_to_le16(hdev->le_max_tx_len);
+ cp.tx_time = cpu_to_le16(hdev->le_max_tx_time);
hci_req_add(req, HCI_OP_LE_WRITE_DEF_DATA_LEN, sizeof(cp), &cp);
}
@@ -4330,7 +4330,14 @@ static void hci_rx_work(struct work_struct *work)
hci_send_to_sock(hdev, skb);
}
- if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
+ /* If the device has been opened in HCI_USER_CHANNEL,
+ * the userspace has exclusive access to device.
+ * When device is HCI_INIT, we still need to process
+ * the data packets to the driver in order
+ * to complete its setup().
+ */
+ if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+ !test_bit(HCI_INIT, &hdev->flags)) {
kfree_skb(skb);
continue;
}
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 9448ebd..a8ddd21 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -1258,6 +1258,14 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
instance_flags = get_adv_instance_flags(hdev, instance);
+ /* If instance already has the flags set skip adding it once
+ * again.
+ */
+ if (adv_instance && eir_get_data(adv_instance->adv_data,
+ adv_instance->adv_data_len, EIR_FLAGS,
+ NULL))
+ goto skip_flags;
+
/* The Add Advertising command allows userspace to set both the general
* and limited discoverable flags.
*/
@@ -1290,6 +1298,7 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
}
}
+skip_flags:
if (adv_instance) {
memcpy(ptr, adv_instance->adv_data,
adv_instance->adv_data_len);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 260ef54..0d84d1f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4918,10 +4918,8 @@ void __l2cap_physical_cfm(struct l2cap_chan *chan, int result)
BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
chan, result, local_amp_id, remote_amp_id);
- if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
- l2cap_chan_unlock(chan);
+ if (chan->state == BT_DISCONN || chan->state == BT_CLOSED)
return;
- }
if (chan->state != BT_CONNECTED) {
l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
@@ -6819,6 +6817,16 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
chan->sdu_len = sdu_len;
chan->sdu_last_frag = skb;
+ /* Detect if remote is not able to use the selected MPS */
+ if (skb->len + L2CAP_SDULEN_SIZE < chan->mps) {
+ u16 mps_len = skb->len + L2CAP_SDULEN_SIZE;
+
+ /* Adjust the number of credits */
+ BT_DBG("chan->mps %u -> %u", chan->mps, mps_len);
+ chan->mps = mps_len;
+ l2cap_chan_le_send_credits(chan);
+ }
+
return 0;
}
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index e682a66..9ce661e 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -246,6 +246,12 @@ static int br_set_mac_address(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+ /* dev_set_mac_addr() can be called by a master device on bridge's
+ * NETDEV_UNREGISTER, but since it's being destroyed do nothing
+ */
+ if (dev->reg_state != NETREG_REGISTERED)
+ return -EBUSY;
+
spin_lock_bh(&br->lock);
if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) {
/* Mac address will be changed in br_stp_change_bridge_id(). */
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 212c184..ccab290 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -646,6 +646,9 @@ static unsigned int br_nf_forward_arp(void *priv,
nf_bridge_pull_encap_header(skb);
}
+ if (unlikely(!pskb_may_pull(skb, sizeof(struct arphdr))))
+ return NF_DROP;
+
if (arp_hdr(skb)->ar_pln != 4) {
if (IS_VLAN_ARP(skb))
nf_bridge_push_encap_header(skb);
diff --git a/net/bridge/br_nf_core.c b/net/bridge/br_nf_core.c
index 8e2d7cf..d88e724 100644
--- a/net/bridge/br_nf_core.c
+++ b/net/bridge/br_nf_core.c
@@ -26,7 +26,8 @@
#endif
static void fake_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
}
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c
index dfc86a0..1d8c834 100644
--- a/net/bridge/netfilter/ebt_dnat.c
+++ b/net/bridge/netfilter/ebt_dnat.c
@@ -19,7 +19,6 @@ static unsigned int
ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct ebt_nat_info *info = par->targinfo;
- struct net_device *dev;
if (!skb_make_writable(skb, 0))
return EBT_DROP;
@@ -32,10 +31,22 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
else
skb->pkt_type = PACKET_MULTICAST;
} else {
- if (xt_hooknum(par) != NF_BR_BROUTING)
- dev = br_port_get_rcu(xt_in(par))->br->dev;
- else
+ const struct net_device *dev;
+
+ switch (xt_hooknum(par)) {
+ case NF_BR_BROUTING:
dev = xt_in(par);
+ break;
+ case NF_BR_PRE_ROUTING:
+ dev = br_port_get_rcu(xt_in(par))->br->dev;
+ break;
+ default:
+ dev = NULL;
+ break;
+ }
+
+ if (!dev) /* NF_BR_LOCAL_OUT */
+ return info->target;
if (ether_addr_equal(info->mac, dev->dev_addr))
skb->pkt_type = PACKET_HOST;
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 7d249af..785e19af 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1876,7 +1876,7 @@ static int ebt_buf_count(struct ebt_entries_buf_state *state, unsigned int sz)
}
static int ebt_buf_add(struct ebt_entries_buf_state *state,
- void *data, unsigned int sz)
+ const void *data, unsigned int sz)
{
if (state->buf_kern_start == NULL)
goto count_only;
@@ -1910,7 +1910,7 @@ enum compat_mwt {
EBT_COMPAT_TARGET,
};
-static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
+static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
enum compat_mwt compat_mwt,
struct ebt_entries_buf_state *state,
const unsigned char *base)
@@ -1988,22 +1988,23 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
/* return size of all matches, watchers or target, including necessary
* alignment and padding.
*/
-static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
+static int ebt_size_mwt(const struct compat_ebt_entry_mwt *match32,
unsigned int size_left, enum compat_mwt type,
struct ebt_entries_buf_state *state, const void *base)
{
+ const char *buf = (const char *)match32;
int growth = 0;
- char *buf;
if (size_left == 0)
return 0;
- buf = (char *) match32;
-
- while (size_left >= sizeof(*match32)) {
+ do {
struct ebt_entry_match *match_kern;
int ret;
+ if (size_left < sizeof(*match32))
+ return -EINVAL;
+
match_kern = (struct ebt_entry_match *) state->buf_kern_start;
if (match_kern) {
char *tmp;
@@ -2040,22 +2041,18 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
if (match_kern)
match_kern->match_size = ret;
- /* rule should have no remaining data after target */
- if (type == EBT_COMPAT_TARGET && size_left)
- return -EINVAL;
-
match32 = (struct compat_ebt_entry_mwt *) buf;
- }
+ } while (size_left);
return growth;
}
/* called for all ebt_entry structures. */
-static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
+static int size_entry_mwt(const struct ebt_entry *entry, const unsigned char *base,
unsigned int *total,
struct ebt_entries_buf_state *state)
{
- unsigned int i, j, startoff, new_offset = 0;
+ unsigned int i, j, startoff, next_expected_off, new_offset = 0;
/* stores match/watchers/targets & offset of next struct ebt_entry: */
unsigned int offsets[4];
unsigned int *offsets_update = NULL;
@@ -2141,11 +2138,13 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
return ret;
}
- startoff = state->buf_user_offset - startoff;
-
- if (WARN_ON(*total < startoff))
+ next_expected_off = state->buf_user_offset - startoff;
+ if (next_expected_off != entry->next_offset)
return -EINVAL;
- *total -= startoff;
+
+ if (*total < entry->next_offset)
+ return -EINVAL;
+ *total -= entry->next_offset;
return 0;
}
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 416717c..4b31f0a 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -953,7 +953,7 @@ static __poll_t caif_poll(struct file *file,
mask |= EPOLLRDHUP;
/* readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue) ||
(sk->sk_shutdown & RCV_SHUTDOWN))
mask |= EPOLLIN | EPOLLRDNORM;
diff --git a/net/core/datagram.c b/net/core/datagram.c
index a487df5..865a8cb 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -95,7 +95,7 @@ int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
if (error)
goto out_err;
- if (sk->sk_receive_queue.prev != skb)
+ if (READ_ONCE(sk->sk_receive_queue.prev) != skb)
goto out;
/* Socket shut down? */
@@ -279,7 +279,7 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,
break;
sk_busy_loop(sk, flags & MSG_DONTWAIT);
- } while (sk->sk_receive_queue.prev != *last);
+ } while (READ_ONCE(sk->sk_receive_queue.prev) != *last);
error = -EAGAIN;
@@ -842,7 +842,7 @@ __poll_t datagram_poll(struct file *file, struct socket *sock,
mask = 0;
/* exceptional events? */
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
@@ -852,7 +852,7 @@ __poll_t datagram_poll(struct file *file, struct socket *sock,
mask |= EPOLLHUP;
/* readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* Connection-based need to check for termination and startup */
diff --git a/net/core/dev.c b/net/core/dev.c
index f7928f9..100784c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -151,6 +151,7 @@
#include "net-sysfs.h"
#define MAX_GRO_SKBS 8
+#define MAX_NEST_DEV 8
/* This should be increased if a protocol with a bigger head is added. */
#define GRO_MAX_HEAD (MAX_HEADER + 128)
@@ -3279,7 +3280,7 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *de
}
skb = next;
- if (netif_xmit_stopped(txq) && skb) {
+ if (netif_tx_queue_stopped(txq) && skb) {
rc = NETDEV_TX_BUSY;
break;
}
@@ -4302,6 +4303,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
struct netdev_rx_queue *rxqueue;
void *orig_data, *orig_data_end;
u32 metalen, act = XDP_DROP;
+ __be16 orig_eth_type;
+ struct ethhdr *eth;
+ bool orig_bcast;
int hlen, off;
u32 mac_len;
@@ -4342,6 +4346,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
xdp->data_hard_start = skb->data - skb_headroom(skb);
orig_data_end = xdp->data_end;
orig_data = xdp->data;
+ eth = (struct ethhdr *)xdp->data;
+ orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
+ orig_eth_type = eth->h_proto;
rxqueue = netif_get_rxqueue(skb);
xdp->rxq = &rxqueue->xdp_rxq;
@@ -4365,6 +4372,14 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
}
+ /* check if XDP changed eth hdr such SKB needs update */
+ eth = (struct ethhdr *)xdp->data;
+ if ((orig_eth_type != eth->h_proto) ||
+ (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) {
+ __skb_push(skb, ETH_HLEN);
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ }
+
switch (act) {
case XDP_REDIRECT:
case XDP_TX:
@@ -6558,6 +6573,21 @@ struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
}
EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu);
+static struct net_device *netdev_next_upper_dev(struct net_device *dev,
+ struct list_head **iter)
+{
+ struct netdev_adjacent *upper;
+
+ upper = list_entry((*iter)->next, struct netdev_adjacent, list);
+
+ if (&upper->list == &dev->adj_list.upper)
+ return NULL;
+
+ *iter = &upper->list;
+
+ return upper->dev;
+}
+
static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev,
struct list_head **iter)
{
@@ -6575,28 +6605,93 @@ static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev,
return upper->dev;
}
+static int netdev_walk_all_upper_dev(struct net_device *dev,
+ int (*fn)(struct net_device *dev,
+ void *data),
+ void *data)
+{
+ struct net_device *udev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
+ struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
+ int ret, cur = 0;
+
+ now = dev;
+ iter = &dev->adj_list.upper;
+
+ while (1) {
+ if (now != dev) {
+ ret = fn(now, data);
+ if (ret)
+ return ret;
+ }
+
+ next = NULL;
+ while (1) {
+ udev = netdev_next_upper_dev(now, &iter);
+ if (!udev)
+ break;
+
+ next = udev;
+ niter = &udev->adj_list.upper;
+ dev_stack[cur] = now;
+ iter_stack[cur++] = iter;
+ break;
+ }
+
+ if (!next) {
+ if (!cur)
+ return 0;
+ next = dev_stack[--cur];
+ niter = iter_stack[cur];
+ }
+
+ now = next;
+ iter = niter;
+ }
+
+ return 0;
+}
+
int netdev_walk_all_upper_dev_rcu(struct net_device *dev,
int (*fn)(struct net_device *dev,
void *data),
void *data)
{
- struct net_device *udev;
- struct list_head *iter;
- int ret;
+ struct net_device *udev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
+ struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
+ int ret, cur = 0;
- for (iter = &dev->adj_list.upper,
- udev = netdev_next_upper_dev_rcu(dev, &iter);
- udev;
- udev = netdev_next_upper_dev_rcu(dev, &iter)) {
- /* first is the upper device itself */
- ret = fn(udev, data);
- if (ret)
- return ret;
+ now = dev;
+ iter = &dev->adj_list.upper;
- /* then look at all of its upper devices */
- ret = netdev_walk_all_upper_dev_rcu(udev, fn, data);
- if (ret)
- return ret;
+ while (1) {
+ if (now != dev) {
+ ret = fn(now, data);
+ if (ret)
+ return ret;
+ }
+
+ next = NULL;
+ while (1) {
+ udev = netdev_next_upper_dev_rcu(now, &iter);
+ if (!udev)
+ break;
+
+ next = udev;
+ niter = &udev->adj_list.upper;
+ dev_stack[cur] = now;
+ iter_stack[cur++] = iter;
+ break;
+ }
+
+ if (!next) {
+ if (!cur)
+ return 0;
+ next = dev_stack[--cur];
+ niter = iter_stack[cur];
+ }
+
+ now = next;
+ iter = niter;
}
return 0;
@@ -6704,23 +6799,42 @@ int netdev_walk_all_lower_dev(struct net_device *dev,
void *data),
void *data)
{
- struct net_device *ldev;
- struct list_head *iter;
- int ret;
+ struct net_device *ldev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
+ struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
+ int ret, cur = 0;
- for (iter = &dev->adj_list.lower,
- ldev = netdev_next_lower_dev(dev, &iter);
- ldev;
- ldev = netdev_next_lower_dev(dev, &iter)) {
- /* first is the lower device itself */
- ret = fn(ldev, data);
- if (ret)
- return ret;
+ now = dev;
+ iter = &dev->adj_list.lower;
- /* then look at all of its lower devices */
- ret = netdev_walk_all_lower_dev(ldev, fn, data);
- if (ret)
- return ret;
+ while (1) {
+ if (now != dev) {
+ ret = fn(now, data);
+ if (ret)
+ return ret;
+ }
+
+ next = NULL;
+ while (1) {
+ ldev = netdev_next_lower_dev(now, &iter);
+ if (!ldev)
+ break;
+
+ next = ldev;
+ niter = &ldev->adj_list.lower;
+ dev_stack[cur] = now;
+ iter_stack[cur++] = iter;
+ break;
+ }
+
+ if (!next) {
+ if (!cur)
+ return 0;
+ next = dev_stack[--cur];
+ niter = iter_stack[cur];
+ }
+
+ now = next;
+ iter = niter;
}
return 0;
@@ -6741,28 +6855,93 @@ static struct net_device *netdev_next_lower_dev_rcu(struct net_device *dev,
return lower->dev;
}
+static u8 __netdev_upper_depth(struct net_device *dev)
+{
+ struct net_device *udev;
+ struct list_head *iter;
+ u8 max_depth = 0;
+
+ for (iter = &dev->adj_list.upper,
+ udev = netdev_next_upper_dev(dev, &iter);
+ udev;
+ udev = netdev_next_upper_dev(dev, &iter)) {
+ if (max_depth < udev->upper_level)
+ max_depth = udev->upper_level;
+ }
+
+ return max_depth;
+}
+
+static u8 __netdev_lower_depth(struct net_device *dev)
+{
+ struct net_device *ldev;
+ struct list_head *iter;
+ u8 max_depth = 0;
+
+ for (iter = &dev->adj_list.lower,
+ ldev = netdev_next_lower_dev(dev, &iter);
+ ldev;
+ ldev = netdev_next_lower_dev(dev, &iter)) {
+ if (max_depth < ldev->lower_level)
+ max_depth = ldev->lower_level;
+ }
+
+ return max_depth;
+}
+
+static int __netdev_update_upper_level(struct net_device *dev, void *data)
+{
+ dev->upper_level = __netdev_upper_depth(dev) + 1;
+ return 0;
+}
+
+static int __netdev_update_lower_level(struct net_device *dev, void *data)
+{
+ dev->lower_level = __netdev_lower_depth(dev) + 1;
+ return 0;
+}
+
int netdev_walk_all_lower_dev_rcu(struct net_device *dev,
int (*fn)(struct net_device *dev,
void *data),
void *data)
{
- struct net_device *ldev;
- struct list_head *iter;
- int ret;
+ struct net_device *ldev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
+ struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
+ int ret, cur = 0;
- for (iter = &dev->adj_list.lower,
- ldev = netdev_next_lower_dev_rcu(dev, &iter);
- ldev;
- ldev = netdev_next_lower_dev_rcu(dev, &iter)) {
- /* first is the lower device itself */
- ret = fn(ldev, data);
- if (ret)
- return ret;
+ now = dev;
+ iter = &dev->adj_list.lower;
- /* then look at all of its lower devices */
- ret = netdev_walk_all_lower_dev_rcu(ldev, fn, data);
- if (ret)
- return ret;
+ while (1) {
+ if (now != dev) {
+ ret = fn(now, data);
+ if (ret)
+ return ret;
+ }
+
+ next = NULL;
+ while (1) {
+ ldev = netdev_next_lower_dev_rcu(now, &iter);
+ if (!ldev)
+ break;
+
+ next = ldev;
+ niter = &ldev->adj_list.lower;
+ dev_stack[cur] = now;
+ iter_stack[cur++] = iter;
+ break;
+ }
+
+ if (!next) {
+ if (!cur)
+ return 0;
+ next = dev_stack[--cur];
+ niter = iter_stack[cur];
+ }
+
+ now = next;
+ iter = niter;
}
return 0;
@@ -7019,6 +7198,9 @@ static int __netdev_upper_dev_link(struct net_device *dev,
if (netdev_has_upper_dev(upper_dev, dev))
return -EBUSY;
+ if ((dev->lower_level + upper_dev->upper_level) > MAX_NEST_DEV)
+ return -EMLINK;
+
if (!master) {
if (netdev_has_upper_dev(dev, upper_dev))
return -EEXIST;
@@ -7045,6 +7227,12 @@ static int __netdev_upper_dev_link(struct net_device *dev,
if (ret)
goto rollback;
+ __netdev_update_upper_level(dev, NULL);
+ netdev_walk_all_lower_dev(dev, __netdev_update_upper_level, NULL);
+
+ __netdev_update_lower_level(upper_dev, NULL);
+ netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level, NULL);
+
return 0;
rollback:
@@ -7127,6 +7315,12 @@ void netdev_upper_dev_unlink(struct net_device *dev,
call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
&changeupper_info.info);
+
+ __netdev_update_upper_level(dev, NULL);
+ netdev_walk_all_lower_dev(dev, __netdev_update_upper_level, NULL);
+
+ __netdev_update_lower_level(upper_dev, NULL);
+ netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level, NULL);
}
EXPORT_SYMBOL(netdev_upper_dev_unlink);
@@ -7611,7 +7805,8 @@ int __dev_set_mtu(struct net_device *dev, int new_mtu)
if (ops->ndo_change_mtu)
return ops->ndo_change_mtu(dev, new_mtu);
- dev->mtu = new_mtu;
+ /* Pairs with all the lockless reads of dev->mtu in the stack */
+ WRITE_ONCE(dev->mtu, new_mtu);
return 0;
}
EXPORT_SYMBOL(__dev_set_mtu);
@@ -8993,6 +9188,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev->gso_max_size = GSO_MAX_SIZE;
dev->gso_max_segs = GSO_MAX_SEGS;
+ dev->upper_level = 1;
+ dev->lower_level = 1;
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
@@ -9241,7 +9438,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
rcu_barrier();
- new_nsid = peernet2id_alloc(dev_net(dev), net);
+ new_nsid = peernet2id_alloc(dev_net(dev), net, GFP_KERNEL);
/* If there is an ifindex conflict assign a new one */
if (__dev_get_by_index(net, dev->ifindex))
new_ifindex = dev_new_index(net);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 996813f..09d828a 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1482,11 +1482,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
{
- struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+ struct ethtool_wolinfo wol;
if (!dev->ethtool_ops->get_wol)
return -EOPNOTSUPP;
+ memset(&wol, 0, sizeof(struct ethtool_wolinfo));
+ wol.cmd = ETHTOOL_GWOL;
dev->ethtool_ops->get_wol(dev, &wol);
if (copy_to_user(useraddr, &wol, sizeof(wol)))
diff --git a/net/core/filter.c b/net/core/filter.c
index e6fa885..91b9502 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2007,6 +2007,7 @@ static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb)
}
skb->dev = dev;
+ skb->tstamp = 0;
__this_cpu_inc(xmit_recursion);
ret = dev_queue_xmit(skb);
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 415b95f..994dd15 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -630,9 +630,10 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
nhoff = skb_network_offset(skb);
hlen = skb_headlen(skb);
#if IS_ENABLED(CONFIG_NET_DSA)
- if (unlikely(skb->dev && netdev_uses_dsa(skb->dev))) {
+ if (unlikely(skb->dev && netdev_uses_dsa(skb->dev) &&
+ proto == htons(ETH_P_XDSA))) {
const struct dsa_device_ops *ops;
- int offset;
+ int offset = 0;
ops = skb->dev->dsa_ptr->tag_ops;
if (ops->flow_dissect &&
@@ -1077,30 +1078,21 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
}
EXPORT_SYMBOL(__skb_flow_dissect);
-static u32 hashrnd __read_mostly;
+static siphash_key_t hashrnd __read_mostly;
static __always_inline void __flow_hash_secret_init(void)
{
net_get_random_once(&hashrnd, sizeof(hashrnd));
}
-static __always_inline u32 __flow_hash_words(const u32 *words, u32 length,
- u32 keyval)
+static const void *flow_keys_hash_start(const struct flow_keys *flow)
{
- return jhash2(words, length, keyval);
-}
-
-static inline const u32 *flow_keys_hash_start(const struct flow_keys *flow)
-{
- const void *p = flow;
-
- BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % sizeof(u32));
- return (const u32 *)(p + FLOW_KEYS_HASH_OFFSET);
+ BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % SIPHASH_ALIGNMENT);
+ return &flow->FLOW_KEYS_HASH_START_FIELD;
}
static inline size_t flow_keys_hash_length(const struct flow_keys *flow)
{
size_t diff = FLOW_KEYS_HASH_OFFSET + sizeof(flow->addrs);
- BUILD_BUG_ON((sizeof(*flow) - FLOW_KEYS_HASH_OFFSET) % sizeof(u32));
BUILD_BUG_ON(offsetof(typeof(*flow), addrs) !=
sizeof(*flow) - sizeof(flow->addrs));
@@ -1115,7 +1107,7 @@ static inline size_t flow_keys_hash_length(const struct flow_keys *flow)
diff -= sizeof(flow->addrs.tipckey);
break;
}
- return (sizeof(*flow) - diff) / sizeof(u32);
+ return sizeof(*flow) - diff;
}
__be32 flow_get_u32_src(const struct flow_keys *flow)
@@ -1181,14 +1173,15 @@ static inline void __flow_hash_consistentify(struct flow_keys *keys)
}
}
-static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
+static inline u32 __flow_hash_from_keys(struct flow_keys *keys,
+ const siphash_key_t *keyval)
{
u32 hash;
__flow_hash_consistentify(keys);
- hash = __flow_hash_words(flow_keys_hash_start(keys),
- flow_keys_hash_length(keys), keyval);
+ hash = siphash(flow_keys_hash_start(keys),
+ flow_keys_hash_length(keys), keyval);
if (!hash)
hash = 1;
@@ -1198,12 +1191,13 @@ static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
u32 flow_hash_from_keys(struct flow_keys *keys)
{
__flow_hash_secret_init();
- return __flow_hash_from_keys(keys, hashrnd);
+ return __flow_hash_from_keys(keys, &hashrnd);
}
EXPORT_SYMBOL(flow_hash_from_keys);
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
- struct flow_keys *keys, u32 keyval)
+ struct flow_keys *keys,
+ const siphash_key_t *keyval)
{
skb_flow_dissect_flow_keys(skb, keys,
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
@@ -1251,7 +1245,7 @@ u32 __skb_get_hash_symmetric(const struct sk_buff *skb)
NULL, 0, 0, 0,
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
- return __flow_hash_from_keys(&keys, hashrnd);
+ return __flow_hash_from_keys(&keys, &hashrnd);
}
EXPORT_SYMBOL_GPL(__skb_get_hash_symmetric);
@@ -1271,13 +1265,14 @@ void __skb_get_hash(struct sk_buff *skb)
__flow_hash_secret_init();
- hash = ___skb_get_hash(skb, &keys, hashrnd);
+ hash = ___skb_get_hash(skb, &keys, &hashrnd);
__skb_set_sw_hash(skb, hash, flow_keys_have_l4(&keys));
}
EXPORT_SYMBOL(__skb_get_hash);
-__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb)
+__u32 skb_get_hash_perturb(const struct sk_buff *skb,
+ const siphash_key_t *perturb)
{
struct flow_keys keys;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index ff55da2..e6260c8 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -18,6 +18,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
+#include <linux/kmemleak.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -363,12 +364,14 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift)
ret = kmalloc(sizeof(*ret), GFP_ATOMIC);
if (!ret)
return NULL;
- if (size <= PAGE_SIZE)
+ if (size <= PAGE_SIZE) {
buckets = kzalloc(size, GFP_ATOMIC);
- else
+ } else {
buckets = (struct neighbour __rcu **)
__get_free_pages(GFP_ATOMIC | __GFP_ZERO,
get_order(size));
+ kmemleak_alloc(buckets, size, 1, GFP_ATOMIC);
+ }
if (!buckets) {
kfree(ret);
return NULL;
@@ -388,10 +391,12 @@ static void neigh_hash_free_rcu(struct rcu_head *head)
size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *);
struct neighbour __rcu **buckets = nht->hash_buckets;
- if (size <= PAGE_SIZE)
+ if (size <= PAGE_SIZE) {
kfree(buckets);
- else
+ } else {
+ kmemleak_free(buckets);
free_pages((unsigned long)buckets, get_order(size));
+ }
kfree(nht);
}
@@ -1092,7 +1097,7 @@ static void neigh_update_hhs(struct neighbour *neigh)
if (update) {
hh = &neigh->hh;
- if (hh->hh_len) {
+ if (READ_ONCE(hh->hh_len)) {
write_seqlock_bh(&hh->hh_lock);
update(hh, neigh->dev, neigh->ha);
write_sequnlock_bh(&hh->hh_lock);
@@ -1355,7 +1360,7 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
struct net_device *dev = neigh->dev;
unsigned int seq;
- if (dev->header_ops->cache && !neigh->hh.hh_len)
+ if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len))
neigh_hh_init(neigh);
do {
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 7320f08..c60123d 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -226,11 +226,11 @@ static int __peernet2id(struct net *net, struct net *peer)
return __peernet2id_alloc(net, peer, &no);
}
-static void rtnl_net_notifyid(struct net *net, int cmd, int id);
+static void rtnl_net_notifyid(struct net *net, int cmd, int id, gfp_t gfp);
/* This function returns the id of a peer netns. If no id is assigned, one will
* be allocated and returned.
*/
-int peernet2id_alloc(struct net *net, struct net *peer)
+int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp)
{
bool alloc = false, alive = false;
int id;
@@ -249,7 +249,7 @@ int peernet2id_alloc(struct net *net, struct net *peer)
id = __peernet2id_alloc(net, peer, &alloc);
spin_unlock_bh(&net->nsid_lock);
if (alloc && id >= 0)
- rtnl_net_notifyid(net, RTM_NEWNSID, id);
+ rtnl_net_notifyid(net, RTM_NEWNSID, id, gfp);
if (alive)
put_net(peer);
return id;
@@ -495,7 +495,8 @@ static void unhash_nsid(struct net *net, struct net *last)
idr_remove(&tmp->netns_ids, id);
spin_unlock_bh(&tmp->nsid_lock);
if (id >= 0)
- rtnl_net_notifyid(tmp, RTM_DELNSID, id);
+ rtnl_net_notifyid(tmp, RTM_DELNSID, id,
+ GFP_KERNEL);
if (tmp == last)
break;
}
@@ -720,7 +721,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
err = alloc_netid(net, peer, nsid);
spin_unlock_bh(&net->nsid_lock);
if (err >= 0) {
- rtnl_net_notifyid(net, RTM_NEWNSID, err);
+ rtnl_net_notifyid(net, RTM_NEWNSID, err, GFP_KERNEL);
err = 0;
} else if (err == -ENOSPC && nsid >= 0) {
err = -EEXIST;
@@ -862,12 +863,12 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static void rtnl_net_notifyid(struct net *net, int cmd, int id)
+static void rtnl_net_notifyid(struct net *net, int cmd, int id, gfp_t gfp)
{
struct sk_buff *msg;
int err = -ENOMEM;
- msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
+ msg = nlmsg_new(rtnl_net_get_size(), gfp);
if (!msg)
goto out;
@@ -875,7 +876,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id)
if (err < 0)
goto err_out;
- rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, 0);
+ rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, gfp);
return;
err_out:
@@ -912,7 +913,8 @@ static int __init net_ns_init(void)
init_net_initialized = true;
up_write(&pernet_ops_rwsem);
- register_pernet_subsys(&net_ns_ops);
+ if (register_pernet_subsys(&net_ns_ops))
+ panic("Could not register network namespace subsystems");
rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL,
RTNL_FLAG_DOIT_UNLOCKED);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 3932eed..dbb3c0c 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1519,7 +1519,7 @@ static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb,
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
const struct net_device *dev,
- struct net *src_net)
+ struct net *src_net, gfp_t gfp)
{
bool put_iflink = false;
@@ -1527,7 +1527,7 @@ static int rtnl_fill_link_netnsid(struct sk_buff *skb,
struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
if (!net_eq(dev_net(dev), link_net)) {
- int id = peernet2id_alloc(src_net, link_net);
+ int id = peernet2id_alloc(src_net, link_net, gfp);
if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
return -EMSGSIZE;
@@ -1585,7 +1585,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
int type, u32 pid, u32 seq, u32 change,
unsigned int flags, u32 ext_filter_mask,
u32 event, int *new_nsid, int new_ifindex,
- int tgt_netnsid)
+ int tgt_netnsid, gfp_t gfp)
{
struct ifinfomsg *ifm;
struct nlmsghdr *nlh;
@@ -1677,7 +1677,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
goto nla_put_failure;
}
- if (rtnl_fill_link_netnsid(skb, dev, src_net))
+ if (rtnl_fill_link_netnsid(skb, dev, src_net, gfp))
goto nla_put_failure;
if (new_nsid &&
@@ -1933,7 +1933,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
cb->nlh->nlmsg_seq, 0,
flags,
ext_filter_mask, 0, NULL, 0,
- netnsid);
+ netnsid, GFP_KERNEL);
if (err < 0) {
if (likely(skb->len))
@@ -2126,6 +2126,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_MAC]) {
struct ifla_vf_mac *ivm = nla_data(tb[IFLA_VF_MAC]);
+ if (ivm->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_mac)
err = ops->ndo_set_vf_mac(dev, ivm->vf,
@@ -2137,6 +2139,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_VLAN]) {
struct ifla_vf_vlan *ivv = nla_data(tb[IFLA_VF_VLAN]);
+ if (ivv->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_vlan)
err = ops->ndo_set_vf_vlan(dev, ivv->vf, ivv->vlan,
@@ -2169,6 +2173,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (len == 0)
return -EINVAL;
+ if (ivvl[0]->vf >= INT_MAX)
+ return -EINVAL;
err = ops->ndo_set_vf_vlan(dev, ivvl[0]->vf, ivvl[0]->vlan,
ivvl[0]->qos, ivvl[0]->vlan_proto);
if (err < 0)
@@ -2179,6 +2185,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
struct ifla_vf_tx_rate *ivt = nla_data(tb[IFLA_VF_TX_RATE]);
struct ifla_vf_info ivf;
+ if (ivt->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_get_vf_config)
err = ops->ndo_get_vf_config(dev, ivt->vf, &ivf);
@@ -2197,6 +2205,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_RATE]) {
struct ifla_vf_rate *ivt = nla_data(tb[IFLA_VF_RATE]);
+ if (ivt->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_rate)
err = ops->ndo_set_vf_rate(dev, ivt->vf,
@@ -2209,6 +2219,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_SPOOFCHK]) {
struct ifla_vf_spoofchk *ivs = nla_data(tb[IFLA_VF_SPOOFCHK]);
+ if (ivs->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_spoofchk)
err = ops->ndo_set_vf_spoofchk(dev, ivs->vf,
@@ -2220,6 +2232,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_LINK_STATE]) {
struct ifla_vf_link_state *ivl = nla_data(tb[IFLA_VF_LINK_STATE]);
+ if (ivl->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_link_state)
err = ops->ndo_set_vf_link_state(dev, ivl->vf,
@@ -2233,6 +2247,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
err = -EOPNOTSUPP;
ivrssq_en = nla_data(tb[IFLA_VF_RSS_QUERY_EN]);
+ if (ivrssq_en->vf >= INT_MAX)
+ return -EINVAL;
if (ops->ndo_set_vf_rss_query_en)
err = ops->ndo_set_vf_rss_query_en(dev, ivrssq_en->vf,
ivrssq_en->setting);
@@ -2243,6 +2259,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_TRUST]) {
struct ifla_vf_trust *ivt = nla_data(tb[IFLA_VF_TRUST]);
+ if (ivt->vf >= INT_MAX)
+ return -EINVAL;
err = -EOPNOTSUPP;
if (ops->ndo_set_vf_trust)
err = ops->ndo_set_vf_trust(dev, ivt->vf, ivt->setting);
@@ -2253,15 +2271,18 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
if (tb[IFLA_VF_IB_NODE_GUID]) {
struct ifla_vf_guid *ivt = nla_data(tb[IFLA_VF_IB_NODE_GUID]);
+ if (ivt->vf >= INT_MAX)
+ return -EINVAL;
if (!ops->ndo_set_vf_guid)
return -EOPNOTSUPP;
-
return handle_vf_guid(dev, ivt, IFLA_VF_IB_NODE_GUID);
}
if (tb[IFLA_VF_IB_PORT_GUID]) {
struct ifla_vf_guid *ivt = nla_data(tb[IFLA_VF_IB_PORT_GUID]);
+ if (ivt->vf >= INT_MAX)
+ return -EINVAL;
if (!ops->ndo_set_vf_guid)
return -EOPNOTSUPP;
@@ -3215,7 +3236,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
err = rtnl_fill_ifinfo(nskb, dev, net,
RTM_NEWLINK, NETLINK_CB(skb).portid,
nlh->nlmsg_seq, 0, 0, ext_filter_mask,
- 0, NULL, 0, netnsid);
+ 0, NULL, 0, netnsid, GFP_KERNEL);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size */
WARN_ON(err == -EMSGSIZE);
@@ -3268,13 +3289,13 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx;
int s_idx = cb->family;
+ int type = cb->nlh->nlmsg_type - RTM_BASE;
if (s_idx == 0)
s_idx = 1;
for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) {
struct rtnl_link **tab;
- int type = cb->nlh->nlmsg_type-RTM_BASE;
struct rtnl_link *link;
rtnl_dumpit_func dumpit;
@@ -3325,7 +3346,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
type, 0, 0, change, 0, 0, event,
- new_nsid, new_ifindex, -1);
+ new_nsid, new_ifindex, -1, flags);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
diff --git a/net/core/sock.c b/net/core/sock.c
index f881eea..bbde5f6 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1005,7 +1005,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
break;
case SO_INCOMING_CPU:
- sk->sk_incoming_cpu = val;
+ WRITE_ONCE(sk->sk_incoming_cpu, val);
break;
case SO_CNX_ADVICE:
@@ -1341,7 +1341,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_INCOMING_CPU:
- v.val = sk->sk_incoming_cpu;
+ v.val = READ_ONCE(sk->sk_incoming_cpu);
break;
case SO_MEMINFO:
@@ -2435,7 +2435,7 @@ int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind)
}
if (sk_has_memory_pressure(sk)) {
- int alloc;
+ u64 alloc;
if (!sk_under_memory_pressure(sk))
return 1;
@@ -3347,6 +3347,7 @@ int sock_load_diag_module(int family, int protocol)
#ifdef CONFIG_INET
if (family == AF_INET &&
+ protocol != IPPROTO_RAW &&
!rcu_access_pointer(inet_protos[protocol]))
return -ENOENT;
#endif
@@ -3483,7 +3484,7 @@ bool sk_busy_loop_end(void *p, unsigned long start_time)
{
struct sock *sk = p;
- return !skb_queue_empty(&sk->sk_receive_queue) ||
+ return !skb_queue_empty_lockless(&sk->sk_receive_queue) ||
sk_busy_loop_timeout(sk, start_time);
}
EXPORT_SYMBOL(sk_busy_loop_end);
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index d67ec17..6cec08c 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -281,6 +281,7 @@ static int proc_dointvec_minmax_bpf_enable(struct ctl_table *table, int write,
return ret;
}
+# ifdef CONFIG_HAVE_EBPF_JIT
static int
proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
@@ -291,6 +292,7 @@ proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write,
return proc_dointvec_minmax(table, write, buffer, lenp, ppos);
}
+# endif /* CONFIG_HAVE_EBPF_JIT */
static int
proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write,
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 8e08cea6..176bdda 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -121,7 +121,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
inet->inet_daddr,
inet->inet_sport,
inet->inet_dport);
- inet->inet_id = dp->dccps_iss ^ jiffies;
+ inet->inet_id = prandom_u32();
err = dccp_connect(sk);
rt = NULL;
@@ -417,7 +417,7 @@ struct sock *dccp_v4_request_recv_sock(const struct sock *sk,
RCU_INIT_POINTER(newinet->inet_opt, rcu_dereference(ireq->ireq_opt));
newinet->mc_index = inet_iif(skb);
newinet->mc_ttl = ip_hdr(skb)->ttl;
- newinet->inet_id = jiffies;
+ newinet->inet_id = prandom_u32();
if (dst == NULL && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL)
goto put_and_exit;
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 7d6ff98..0e6f32d 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -1213,7 +1213,7 @@ static __poll_t dn_poll(struct file *file, struct socket *sock, poll_table *wai
struct dn_scp *scp = DN_SK(sk);
__poll_t mask = datagram_poll(file, sock, wait);
- if (!skb_queue_empty(&scp->other_receive_queue))
+ if (!skb_queue_empty_lockless(&scp->other_receive_queue))
mask |= EPOLLRDBAND;
return mask;
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index bfd43e8..3235540 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -56,7 +56,7 @@
#include <net/dn_neigh.h>
#include <net/dn_fib.h>
-#define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
+#define DN_IFREQ_SIZE (offsetof(struct ifreq, ifr_ifru) + sizeof(struct sockaddr_dn))
static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
static char dn_rt_all_rt_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x03,0x00,0x00};
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 1c002c0..658191f 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -118,7 +118,8 @@ static void dn_dst_ifdown(struct dst_entry *, struct net_device *dev, int how);
static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
static void dn_dst_link_failure(struct sk_buff *);
static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb , u32 mtu);
+ struct sk_buff *skb , u32 mtu,
+ bool confirm_neigh);
static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb);
static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst,
@@ -259,7 +260,8 @@ static int dn_dst_gc(struct dst_ops *ops)
* advertise to the other end).
*/
static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
struct dn_route *rt = (struct dn_route *) dst;
struct neighbour *n = rt->n;
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index a191702..46ae4de 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -49,7 +49,7 @@ static struct dsa_switch_tree *dsa_tree_alloc(int index)
dst->index = index;
INIT_LIST_HEAD(&dst->list);
- list_add_tail(&dsa_tree_list, &dst->list);
+ list_add_tail(&dst->list, &dsa_tree_list);
kref_init(&dst->refcount);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index fd8faa0..ca06e9a 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -239,7 +239,12 @@ int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16
eth->h_proto = type;
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
- hh->hh_len = ETH_HLEN;
+
+ /* Pairs with READ_ONCE() in neigh_resolve_output(),
+ * neigh_hh_output() and neigh_update_hhs().
+ */
+ smp_store_release(&hh->hh_len, ETH_HLEN);
+
return 0;
}
EXPORT_SYMBOL(eth_header_cache);
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index 80107a6..dc8517c 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -77,7 +77,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
reuseport_has_conns(sk, true);
sk->sk_state = TCP_ESTABLISHED;
sk_set_txhash(sk);
- inet->inet_id = jiffies;
+ inet->inet_id = prandom_u32();
sk_dst_set(sk, &rt->dst);
err = 0;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 41412ed..49ca645 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1441,11 +1441,6 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
}
}
-static bool inetdev_valid_mtu(unsigned int mtu)
-{
- return mtu >= IPV4_MIN_MTU;
-}
-
static void inetdev_send_gratuitous_arp(struct net_device *dev,
struct in_device *in_dev)
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index dae743b..7f4ec36 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -946,7 +946,7 @@ void fib_modify_prefix_metric(struct in_ifaddr *ifa, u32 new_metric)
if (!(dev->flags & IFF_UP) ||
ifa->ifa_flags & (IFA_F_SECONDARY | IFA_F_NOPREFIXROUTE) ||
ipv4_is_zeronet(prefix) ||
- prefix == ifa->ifa_local || ifa->ifa_prefixlen == 32)
+ (prefix == ifa->ifa_local && ifa->ifa_prefixlen == 32))
return;
/* add the new */
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 446204c..a8fc4e8 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1421,8 +1421,8 @@ int fib_sync_down_addr(struct net_device *dev, __be32 local)
int ret = 0;
unsigned int hash = fib_laddr_hashfn(local);
struct hlist_head *head = &fib_info_laddrhash[hash];
+ int tb_id = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
struct net *net = dev_net(dev);
- int tb_id = l3mdev_fib_table(dev);
struct fib_info *fi;
if (!fib_info_laddrhash || local == 0)
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c
index f21ea61..0eb4bfa 100644
--- a/net/ipv4/gre_demux.c
+++ b/net/ipv4/gre_demux.c
@@ -87,13 +87,14 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
options = (__be32 *)(greh + 1);
if (greh->flags & GRE_CSUM) {
- if (skb_checksum_simple_validate(skb)) {
+ if (!skb_checksum_simple_validate(skb)) {
+ skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
+ null_compute_pseudo);
+ } else if (csum_err) {
*csum_err = true;
return -EINVAL;
}
- skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
- null_compute_pseudo);
options++;
}
@@ -131,7 +132,7 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))
return -EINVAL;
- ershdr = (struct erspan_base_hdr *)options;
+ ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len);
tpi->key = cpu_to_be32(get_session_id(ershdr));
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 0167e23..4efa5e3 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -254,10 +254,11 @@ bool icmp_global_allow(void)
bool rc = false;
/* Check if token bucket is empty and cannot be refilled
- * without taking the spinlock.
+ * without taking the spinlock. The READ_ONCE() are paired
+ * with the following WRITE_ONCE() in this same function.
*/
- if (!icmp_global.credit) {
- delta = min_t(u32, now - icmp_global.stamp, HZ);
+ if (!READ_ONCE(icmp_global.credit)) {
+ delta = min_t(u32, now - READ_ONCE(icmp_global.stamp), HZ);
if (delta < HZ / 50)
return false;
}
@@ -267,14 +268,14 @@ bool icmp_global_allow(void)
if (delta >= HZ / 50) {
incr = sysctl_icmp_msgs_per_sec * delta / HZ ;
if (incr)
- icmp_global.stamp = now;
+ WRITE_ONCE(icmp_global.stamp, now);
}
credit = min_t(u32, icmp_global.credit + incr, sysctl_icmp_msgs_burst);
if (credit) {
credit--;
rc = true;
}
- icmp_global.credit = credit;
+ WRITE_ONCE(icmp_global.credit, credit);
spin_unlock(&icmp_global.lock);
return rc;
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b2240b7..523d26f 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -111,13 +111,10 @@
#ifdef CONFIG_IP_MULTICAST
/* Parameter names and values are taken from igmp-v2-06 draft */
-#define IGMP_V1_ROUTER_PRESENT_TIMEOUT (400*HZ)
-#define IGMP_V2_ROUTER_PRESENT_TIMEOUT (400*HZ)
#define IGMP_V2_UNSOLICITED_REPORT_INTERVAL (10*HZ)
#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL (1*HZ)
+#define IGMP_QUERY_INTERVAL (125*HZ)
#define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ)
-#define IGMP_QUERY_ROBUSTNESS_VARIABLE 2
-
#define IGMP_INITIAL_REPORT_DELAY (1)
@@ -953,13 +950,15 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
in_dev->mr_v1_seen = jiffies +
- IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+ (in_dev->mr_qrv * in_dev->mr_qi) +
+ in_dev->mr_qri;
group = 0;
} else {
/* v2 router present */
max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
in_dev->mr_v2_seen = jiffies +
- IGMP_V2_ROUTER_PRESENT_TIMEOUT;
+ (in_dev->mr_qrv * in_dev->mr_qi) +
+ in_dev->mr_qri;
}
/* cancel the interface change timer */
in_dev->mr_ifc_count = 0;
@@ -999,8 +998,21 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
if (!max_delay)
max_delay = 1; /* can't mod w/ 0 */
in_dev->mr_maxdelay = max_delay;
- if (ih3->qrv)
- in_dev->mr_qrv = ih3->qrv;
+
+ /* RFC3376, 4.1.6. QRV and 4.1.7. QQIC, when the most recently
+ * received value was zero, use the default or statically
+ * configured value.
+ */
+ in_dev->mr_qrv = ih3->qrv ?: net->ipv4.sysctl_igmp_qrv;
+ in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;
+
+ /* RFC3376, 8.3. Query Response Interval:
+ * The number of seconds represented by the [Query Response
+ * Interval] must be less than the [Query Interval].
+ */
+ if (in_dev->mr_qri >= in_dev->mr_qi)
+ in_dev->mr_qri = (in_dev->mr_qi/HZ - 1)*HZ;
+
if (!group) { /* general query */
if (ih3->nsrcs)
return true; /* no sources allowed */
@@ -1738,18 +1750,30 @@ void ip_mc_down(struct in_device *in_dev)
ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
}
+#ifdef CONFIG_IP_MULTICAST
+static void ip_mc_reset(struct in_device *in_dev)
+{
+ struct net *net = dev_net(in_dev->dev);
+
+ in_dev->mr_qi = IGMP_QUERY_INTERVAL;
+ in_dev->mr_qri = IGMP_QUERY_RESPONSE_INTERVAL;
+ in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv;
+}
+#else
+static void ip_mc_reset(struct in_device *in_dev)
+{
+}
+#endif
+
void ip_mc_init_dev(struct in_device *in_dev)
{
-#ifdef CONFIG_IP_MULTICAST
- struct net *net = dev_net(in_dev->dev);
-#endif
ASSERT_RTNL();
#ifdef CONFIG_IP_MULTICAST
timer_setup(&in_dev->mr_gq_timer, igmp_gq_timer_expire, 0);
timer_setup(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, 0);
- in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv;
#endif
+ ip_mc_reset(in_dev);
spin_lock_init(&in_dev->mc_tomb_lock);
}
@@ -1759,15 +1783,10 @@ void ip_mc_init_dev(struct in_device *in_dev)
void ip_mc_up(struct in_device *in_dev)
{
struct ip_mc_list *pmc;
-#ifdef CONFIG_IP_MULTICAST
- struct net *net = dev_net(in_dev->dev);
-#endif
ASSERT_RTNL();
-#ifdef CONFIG_IP_MULTICAST
- in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv;
-#endif
+ ip_mc_reset(in_dev);
ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
for_each_pmc_rtnl(in_dev, pmc) {
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 6a56ec8..9ef63de 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1096,7 +1096,7 @@ struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu)
if (!dst)
goto out;
}
- dst->ops->update_pmtu(dst, sk, NULL, mtu);
+ dst->ops->update_pmtu(dst, sk, NULL, mtu, true);
dst = __sk_dst_check(sk, 0);
if (!dst)
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 5731670..9742b37 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -918,11 +918,12 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
struct inet_listen_hashbucket *ilb;
+ struct hlist_nulls_node *node;
num = 0;
ilb = &hashinfo->listening_hash[i];
spin_lock(&ilb->lock);
- sk_for_each(sk, &ilb->head) {
+ sk_nulls_for_each(sk, node, &ilb->nulls_head) {
struct inet_sock *inet = inet_sk(sk);
if (!net_eq(sock_net(sk), net))
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index f5c9ef2..b53da26 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -248,7 +248,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
if (sk->sk_bound_dev_if)
score += 4;
}
- if (sk->sk_incoming_cpu == raw_smp_processor_id())
+ if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id())
score++;
}
return score;
@@ -308,6 +308,7 @@ struct sock *__inet_lookup_listener(struct net *net,
bool exact_dif = inet_exact_dif_match(net, skb);
struct inet_listen_hashbucket *ilb2;
struct sock *sk, *result = NULL;
+ struct hlist_nulls_node *node;
int score, hiscore = 0;
unsigned int hash2;
u32 phash = 0;
@@ -343,7 +344,7 @@ struct sock *__inet_lookup_listener(struct net *net,
goto done;
port_lookup:
- sk_for_each_rcu(sk, &ilb->head) {
+ sk_nulls_for_each_rcu(sk, node, &ilb->nulls_head) {
score = compute_score(sk, net, hnum, daddr,
dif, sdif, exact_dif);
if (score > hiscore) {
@@ -560,10 +561,11 @@ static int inet_reuseport_add_sock(struct sock *sk,
struct inet_listen_hashbucket *ilb)
{
struct inet_bind_bucket *tb = inet_csk(sk)->icsk_bind_hash;
+ const struct hlist_nulls_node *node;
struct sock *sk2;
kuid_t uid = sock_i_uid(sk);
- sk_for_each_rcu(sk2, &ilb->head) {
+ sk_nulls_for_each_rcu(sk2, node, &ilb->nulls_head) {
if (sk2 != sk &&
sk2->sk_family == sk->sk_family &&
ipv6_only_sock(sk2) == ipv6_only_sock(sk) &&
@@ -599,9 +601,9 @@ int __inet_hash(struct sock *sk, struct sock *osk)
}
if (IS_ENABLED(CONFIG_IPV6) && sk->sk_reuseport &&
sk->sk_family == AF_INET6)
- hlist_add_tail_rcu(&sk->sk_node, &ilb->head);
+ __sk_nulls_add_node_tail_rcu(sk, &ilb->nulls_head);
else
- hlist_add_head_rcu(&sk->sk_node, &ilb->head);
+ __sk_nulls_add_node_rcu(sk, &ilb->nulls_head);
inet_hash2(hashinfo, sk);
ilb->count++;
sock_set_flag(sk, SOCK_RCU_FREE);
@@ -650,11 +652,9 @@ void inet_unhash(struct sock *sk)
reuseport_detach_sock(sk);
if (ilb) {
inet_unhash2(hashinfo, sk);
- __sk_del_node_init(sk);
- ilb->count--;
- } else {
- __sk_nulls_del_node_init_rcu(sk);
+ ilb->count--;
}
+ __sk_nulls_del_node_init_rcu(sk);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
unlock:
spin_unlock_bh(lock);
@@ -790,7 +790,8 @@ void inet_hashinfo_init(struct inet_hashinfo *h)
for (i = 0; i < INET_LHTABLE_SIZE; i++) {
spin_lock_init(&h->listening_hash[i].lock);
- INIT_HLIST_HEAD(&h->listening_hash[i].head);
+ INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].nulls_head,
+ i + LISTENING_NULLS_BASE);
h->listening_hash[i].count = 0;
}
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index be77859..ff327a6 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -160,7 +160,12 @@ static void inet_peer_gc(struct inet_peer_base *base,
base->total / inet_peer_threshold * HZ;
for (i = 0; i < gc_cnt; i++) {
p = gc_stack[i];
- delta = (__u32)jiffies - p->dtime;
+
+ /* The READ_ONCE() pairs with the WRITE_ONCE()
+ * in inet_putpeer()
+ */
+ delta = (__u32)jiffies - READ_ONCE(p->dtime);
+
if (delta < ttl || !refcount_dec_if_one(&p->refcnt))
gc_stack[i] = NULL;
}
@@ -237,7 +242,10 @@ EXPORT_SYMBOL_GPL(inet_getpeer);
void inet_putpeer(struct inet_peer *p)
{
- p->dtime = (__u32)jiffies;
+ /* The WRITE_ONCE() pairs with itself (we run lockless)
+ * and the READ_ONCE() in inet_peer_gc()
+ */
+ WRITE_ONCE(p->dtime, (__u32)jiffies);
if (refcount_dec_and_test(&p->refcnt))
call_rcu(&p->rcu, inetpeer_free_rcu);
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 0b87558..a3f7744 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -232,13 +232,10 @@ static void gre_err(struct sk_buff *skb, u32 info)
const int type = icmp_hdr(skb)->type;
const int code = icmp_hdr(skb)->code;
struct tnl_ptk_info tpi;
- bool csum_err = false;
- if (gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP),
- iph->ihl * 4) < 0) {
- if (!csum_err) /* ignore csum errors. */
- return;
- }
+ if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IP),
+ iph->ihl * 4) < 0)
+ return;
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
ipv4_update_pmtu(skb, dev_net(skb->dev), info,
@@ -589,9 +586,9 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
key = &tun_info->key;
if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
goto err_free_rt;
+ if (tun_info->options_len < sizeof(*md))
+ goto err_free_rt;
md = ip_tunnel_info_opts(tun_info);
- if (!md)
- goto err_free_rt;
/* ERSPAN has fixed 8 byte GRE header */
version = md->version;
@@ -1469,9 +1466,23 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct ip_tunnel_parm *p = &t->parms;
__be16 o_flags = p->o_flags;
- if ((t->erspan_ver == 1 || t->erspan_ver == 2) &&
- !t->collect_md)
- o_flags |= TUNNEL_KEY;
+ if (t->erspan_ver == 1 || t->erspan_ver == 2) {
+ if (!t->collect_md)
+ o_flags |= TUNNEL_KEY;
+
+ if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
+ goto nla_put_failure;
+
+ if (t->erspan_ver == 1) {
+ if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
+ goto nla_put_failure;
+ } else {
+ if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
+ goto nla_put_failure;
+ if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
+ goto nla_put_failure;
+ }
+ }
if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
nla_put_be16(skb, IFLA_GRE_IFLAGS,
@@ -1507,19 +1518,6 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
}
- if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
- goto nla_put_failure;
-
- if (t->erspan_ver == 1) {
- if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
- goto nla_put_failure;
- } else if (t->erspan_ver == 2) {
- if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
- goto nla_put_failure;
- if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
- goto nla_put_failure;
- }
-
return 0;
nla_put_failure:
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 73894ed..d6309181 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1142,15 +1142,18 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
cork->addr = ipc->addr;
}
- /*
- * We steal reference to this route, caller should not release it
- */
- *rtp = NULL;
cork->fragsize = ip_sk_use_pmtu(sk) ?
- dst_mtu(&rt->dst) : rt->dst.dev->mtu;
+ dst_mtu(&rt->dst) : READ_ONCE(rt->dst.dev->mtu);
+
+ if (!inetdev_valid_mtu(cork->fragsize))
+ return -ENETUNREACH;
cork->gso_size = ipc->gso_size;
+
cork->dst = &rt->dst;
+ /* We stole this route, caller should not release it. */
+ *rtp = NULL;
+
cork->length = 0;
cork->ttl = ipc->ttl;
cork->tos = ipc->tos;
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index b7a2612..82f341e 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1244,7 +1244,7 @@ int ip_setsockopt(struct sock *sk, int level,
return -ENOPROTOOPT;
err = do_ip_setsockopt(sk, level, optname, optval, optlen);
-#ifdef CONFIG_BPFILTER
+#if IS_ENABLED(CONFIG_BPFILTER_UMH)
if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
optname < BPFILTER_IPT_SET_MAX)
err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen);
@@ -1557,7 +1557,7 @@ int ip_getsockopt(struct sock *sk, int level,
int err;
err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
-#ifdef CONFIG_BPFILTER
+#if IS_ENABLED(CONFIG_BPFILTER_UMH)
if (optname >= BPFILTER_IPT_SO_GET_INFO &&
optname < BPFILTER_IPT_GET_MAX)
err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
@@ -1594,7 +1594,7 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname,
err = do_ip_getsockopt(sk, level, optname, optval, optlen,
MSG_CMSG_COMPAT);
-#ifdef CONFIG_BPFILTER
+#if IS_ENABLED(CONFIG_BPFILTER_UMH)
if (optname >= BPFILTER_IPT_SO_GET_INFO &&
optname < BPFILTER_IPT_GET_MAX)
err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index c4f5602..420e891 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -513,7 +513,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,
else
mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
- skb_dst_update_pmtu(skb, mtu);
+ skb_dst_update_pmtu_no_confirm(skb, mtu);
if (skb->protocol == htons(ETH_P_IP)) {
if (!skb_is_gso(skb) &&
@@ -644,13 +644,19 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
dst = tnl_params->daddr;
if (dst == 0) {
/* NBMA tunnel */
+ struct ip_tunnel_info *tun_info;
if (!skb_dst(skb)) {
dev->stats.tx_fifo_errors++;
goto tx_error;
}
- if (skb->protocol == htons(ETH_P_IP)) {
+ tun_info = skb_tunnel_info(skb);
+ if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX) &&
+ ip_tunnel_info_af(tun_info) == AF_INET &&
+ tun_info->key.u.ipv4.dst)
+ dst = tun_info->key.u.ipv4.dst;
+ else if (skb->protocol == htons(ETH_P_IP)) {
rt = skb_rtable(skb);
dst = rt_nexthop(rt, inner_iph->daddr);
}
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 808f8d1..960f4fa 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -235,7 +235,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
mtu = dst_mtu(dst);
if (skb->len > mtu) {
- skb_dst_update_pmtu(skb, mtu);
+ skb_dst_update_pmtu_no_confirm(skb, mtu);
if (skb->protocol == htons(ETH_P_IP)) {
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu));
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index f6275aa..d235478 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2278,7 +2278,8 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
rcu_read_unlock();
return -ENODEV;
}
- skb2 = skb_clone(skb, GFP_ATOMIC);
+
+ skb2 = skb_realloc_headroom(skb, sizeof(struct iphdr));
if (!skb2) {
read_unlock(&mrt_lock);
rcu_read_unlock();
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 2dc83de..0199ee8 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -915,8 +915,6 @@ static int __do_replace(struct net *net, const char *name,
(newinfo->number <= oldinfo->initial_entries))
module_put(t->me);
- xt_table_unlock(t);
-
get_old_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
@@ -931,6 +929,7 @@ static int __do_replace(struct net *net, const char *name,
net_warn_ratelimited("arptables: counters copy to user failed while replacing table\n");
}
vfree(counters);
+ xt_table_unlock(t);
return ret;
put_module:
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index e77872c..62934d3 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -1079,8 +1079,6 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
(newinfo->number <= oldinfo->initial_entries))
module_put(t->me);
- xt_table_unlock(t);
-
get_old_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
@@ -1094,6 +1092,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n");
}
vfree(counters);
+ xt_table_unlock(t);
return ret;
put_module:
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
index 4c7fcd3..41327bb 100644
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
@@ -104,12 +104,26 @@ static int masq_device_event(struct notifier_block *this,
return NOTIFY_DONE;
}
+static int inet_cmp(struct nf_conn *ct, void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ struct nf_conntrack_tuple *tuple;
+
+ if (!device_cmp(ct, (void *)(long)dev->ifindex))
+ return 0;
+
+ tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+
+ return ifa->ifa_address == tuple->dst.u3.ip;
+}
+
static int masq_inet_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
- struct netdev_notifier_info info;
+ struct net *net = dev_net(idev->dev);
/* The masq_dev_notifier will catch the case of the device going
* down. So if the inetdev is dead and being destroyed we have
@@ -119,8 +133,10 @@ static int masq_inet_event(struct notifier_block *this,
if (idev->dead)
return NOTIFY_DONE;
- netdev_notifier_info_init(&info, idev->dev);
- return masq_device_event(this, event, &info);
+ if (event == NETDEV_DOWN)
+ nf_ct_iterate_cleanup_net(net, inet_cmp, ptr, 0, 0);
+
+ return NOTIFY_DONE;
}
static struct notifier_block masq_dev_notifier = {
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 69127f60..4590af5 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -142,7 +142,8 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst);
static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst);
static void ipv4_link_failure(struct sk_buff *skb);
static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu);
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh);
static void ip_do_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb);
static void ipv4_dst_destroy(struct dst_entry *dst);
@@ -1035,7 +1036,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
}
static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
struct rtable *rt = (struct rtable *) dst;
struct flowi4 fl4;
@@ -2559,7 +2561,8 @@ static unsigned int ipv4_blackhole_mtu(const struct dst_entry *dst)
}
static void ipv4_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 0c29bdd..5986c2e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -488,7 +488,7 @@ static void tcp_tx_timestamp(struct sock *sk, u16 tsflags)
static inline bool tcp_stream_is_readable(const struct tcp_sock *tp,
int target, struct sock *sk)
{
- return (tp->rcv_nxt - tp->copied_seq >= target) ||
+ return (READ_ONCE(tp->rcv_nxt) - tp->copied_seq >= target) ||
(sk->sk_prot->stream_memory_read ?
sk->sk_prot->stream_memory_read(sk) : false);
}
@@ -595,7 +595,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
}
/* This barrier is coupled with smp_wmb() in tcp_reset() */
smp_rmb();
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR;
return mask;
@@ -1948,7 +1948,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
if (unlikely(flags & MSG_ERRQUEUE))
return inet_recv_error(sk, msg, len, addr_len);
- if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
+ if (sk_can_busy_loop(sk) && skb_queue_empty_lockless(&sk->sk_receive_queue) &&
(sk->sk_state == TCP_ESTABLISHED))
sk_busy_loop(sk, nonblock);
@@ -2869,7 +2869,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
else if (tp->repair_queue == TCP_SEND_QUEUE)
tp->write_seq = val;
else if (tp->repair_queue == TCP_RECV_QUEUE)
- tp->rcv_nxt = val;
+ WRITE_ONCE(tp->rcv_nxt, val);
else
err = -EINVAL;
break;
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
index 81148f7..c9e97f3 100644
--- a/net/ipv4/tcp_diag.c
+++ b/net/ipv4/tcp_diag.c
@@ -30,7 +30,7 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
} else if (sk->sk_type == SOCK_STREAM) {
const struct tcp_sock *tp = tcp_sk(sk);
- r->idiag_rqueue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
+ r->idiag_rqueue = max_t(int, READ_ONCE(tp->rcv_nxt) - tp->copied_seq, 0);
r->idiag_wqueue = tp->write_seq - tp->snd_una;
}
if (info)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 57e8dad..578b65e 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1716,8 +1716,11 @@ tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb,
}
/* Ignore very old stuff early */
- if (!after(sp[used_sacks].end_seq, prior_snd_una))
+ if (!after(sp[used_sacks].end_seq, prior_snd_una)) {
+ if (i == 0)
+ first_sack_index = -1;
continue;
+ }
used_sacks++;
}
@@ -3348,7 +3351,7 @@ static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
sock_owned_by_me((struct sock *)tp);
tp->bytes_received += delta;
- tp->rcv_nxt = seq;
+ WRITE_ONCE(tp->rcv_nxt, seq);
}
/* Update our send window.
@@ -5829,7 +5832,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
/* Ok.. it's good. Set up sequence numbers and
* move to established.
*/
- tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
+ WRITE_ONCE(tp->rcv_nxt, TCP_SKB_CB(skb)->seq + 1);
tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
/* RFC1323: The window in SYN & SYN/ACK segments is
@@ -5932,7 +5935,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
tp->tcp_header_len = sizeof(struct tcphdr);
}
- tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
+ WRITE_ONCE(tp->rcv_nxt, TCP_SKB_CB(skb)->seq + 1);
tp->copied_seq = tp->rcv_nxt;
tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index da0ce33..da19770 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -305,7 +305,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
inet->inet_daddr);
}
- inet->inet_id = tp->write_seq ^ jiffies;
+ inet->inet_id = prandom_u32();
if (tcp_fastopen_defer_connect(sk, &err))
return err;
@@ -1436,7 +1436,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (inet_opt)
inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
- newinet->inet_id = newtp->write_seq ^ jiffies;
+ newinet->inet_id = prandom_u32();
if (!dst) {
dst = inet_csk_route_child_sock(sk, newsk, req);
@@ -2020,13 +2020,14 @@ static void *listening_get_next(struct seq_file *seq, void *cur)
struct tcp_iter_state *st = seq->private;
struct net *net = seq_file_net(seq);
struct inet_listen_hashbucket *ilb;
+ struct hlist_nulls_node *node;
struct sock *sk = cur;
if (!sk) {
get_head:
ilb = &tcp_hashinfo.listening_hash[st->bucket];
spin_lock(&ilb->lock);
- sk = sk_head(&ilb->head);
+ sk = sk_nulls_head(&ilb->nulls_head);
st->offset = 0;
goto get_sk;
}
@@ -2034,9 +2035,9 @@ static void *listening_get_next(struct seq_file *seq, void *cur)
++st->num;
++st->offset;
- sk = sk_next(sk);
+ sk = sk_nulls_next(sk);
get_sk:
- sk_for_each_from(sk) {
+ sk_nulls_for_each_from(sk, node) {
if (!net_eq(sock_net(sk), net))
continue;
if (sk->sk_family == afinfo->family)
@@ -2333,7 +2334,8 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
/* Because we don't lock the socket,
* we might find a transient negative value.
*/
- rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
+ rx_queue = max_t(int, READ_ONCE(tp->rcv_nxt) -
+ tp->copied_seq, 0);
seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
"%08X %5u %8d %lu %d %pK %lu %lu %u %u %d",
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 12affb7..7ba8a90 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -454,6 +454,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
struct tcp_request_sock *treq = tcp_rsk(req);
struct inet_connection_sock *newicsk;
struct tcp_sock *oldtp, *newtp;
+ u32 seq;
if (!newsk)
return NULL;
@@ -467,8 +468,10 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
/* Now setup tcp_sock */
newtp->pred_flags = 0;
- newtp->rcv_wup = newtp->copied_seq =
- newtp->rcv_nxt = treq->rcv_isn + 1;
+ seq = treq->rcv_isn + 1;
+ newtp->rcv_wup = seq;
+ newtp->copied_seq = seq;
+ WRITE_ONCE(newtp->rcv_nxt, seq);
newtp->segs_in = 1;
newtp->snd_sml = newtp->snd_una =
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 53f910b..1cc20ed 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -60,6 +60,9 @@ static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)
__skb_unlink(skb, &sk->sk_write_queue);
tcp_rbtree_insert(&sk->tcp_rtx_queue, skb);
+ if (tp->highest_sack == NULL)
+ tp->highest_sack = skb;
+
tp->packets_out += tcp_skb_pcount(skb);
if (!prior_packets || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
tcp_rearm_rto(sk);
@@ -740,8 +743,9 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
min_t(unsigned int, eff_sacks,
(remaining - TCPOLEN_SACK_BASE_ALIGNED) /
TCPOLEN_SACK_PERBLOCK);
- size += TCPOLEN_SACK_BASE_ALIGNED +
- opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK;
+ if (likely(opts->num_sack_blocks))
+ size += TCPOLEN_SACK_BASE_ALIGNED +
+ opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK;
}
return size;
@@ -2372,6 +2376,14 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
if (tcp_small_queue_check(sk, skb, 0))
break;
+ /* Argh, we hit an empty skb(), presumably a thread
+ * is sleeping in sendmsg()/sk_stream_wait_memory().
+ * We do not want to send a pure-ack packet and have
+ * a strange looking rtx queue with empty packet(s).
+ */
+ if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq)
+ break;
+
if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
break;
@@ -2919,7 +2931,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
TCP_SKB_CB(skb)->sacked |= TCPCB_EVER_RETRANS;
trace_tcp_retransmit_skb(sk, skb);
} else if (err != -EBUSY) {
- NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
+ NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL, segs);
}
return err;
}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 9d775b8..681882a 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -378,7 +378,7 @@ static void tcp_probe_timer(struct sock *sk)
return;
}
- if (icsk->icsk_probes_out > max_probes) {
+ if (icsk->icsk_probes_out >= max_probes) {
abort: tcp_write_err(sk);
} else {
/* Only send another probe if we didn't close things up. */
@@ -443,10 +443,8 @@ void tcp_retransmit_timer(struct sock *sk)
*/
return;
}
- if (!tp->packets_out)
- goto out;
-
- WARN_ON(tcp_rtx_queue_empty(sk));
+ if (!tp->packets_out || WARN_ON_ONCE(tcp_rtx_queue_empty(sk)))
+ return;
tp->tlp_high_seq = 0;
@@ -484,11 +482,12 @@ void tcp_retransmit_timer(struct sock *sk)
goto out_reset_timer;
}
+ __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTS);
if (tcp_write_timeout(sk))
goto out;
if (icsk->icsk_retransmits == 0) {
- int mib_idx;
+ int mib_idx = 0;
if (icsk->icsk_ca_state == TCP_CA_Recovery) {
if (tcp_is_sack(tp))
@@ -503,10 +502,9 @@ void tcp_retransmit_timer(struct sock *sk)
mib_idx = LINUX_MIB_TCPSACKFAILURES;
else
mib_idx = LINUX_MIB_TCPRENOFAILURES;
- } else {
- mib_idx = LINUX_MIB_TCPTIMEOUTS;
}
- __NET_INC_STATS(sock_net(sk), mib_idx);
+ if (mib_idx)
+ __NET_INC_STATS(sock_net(sk), mib_idx);
}
tcp_enter_loss(sk);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 22b290e..6d22933 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -414,7 +414,7 @@ static int compute_score(struct sock *sk, struct net *net,
score += 4;
}
- if (sk->sk_incoming_cpu == raw_smp_processor_id())
+ if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id())
score++;
return score;
}
@@ -1276,6 +1276,20 @@ static void udp_set_dev_scratch(struct sk_buff *skb)
scratch->_tsize_state |= UDP_SKB_IS_STATELESS;
}
+static void udp_skb_csum_unnecessary_set(struct sk_buff *skb)
+{
+ /* We come here after udp_lib_checksum_complete() returned 0.
+ * This means that __skb_checksum_complete() might have
+ * set skb->csum_valid to 1.
+ * On 64bit platforms, we can set csum_unnecessary
+ * to true, but only if the skb is not shared.
+ */
+#if BITS_PER_LONG == 64
+ if (!skb_shared(skb))
+ udp_skb_scratch(skb)->csum_unnecessary = true;
+#endif
+}
+
static int udp_skb_truesize(struct sk_buff *skb)
{
return udp_skb_scratch(skb)->_tsize_state & ~UDP_SKB_IS_STATELESS;
@@ -1404,7 +1418,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)
* queue contains some other skb
*/
rmem = atomic_add_return(size, &sk->sk_rmem_alloc);
- if (rmem > (size + sk->sk_rcvbuf))
+ if (rmem > (size + (unsigned int)sk->sk_rcvbuf))
goto uncharge_drop;
spin_lock(&list->lock);
@@ -1510,10 +1524,7 @@ static struct sk_buff *__first_packet_length(struct sock *sk,
*total += skb->truesize;
kfree_skb(skb);
} else {
- /* the csum related bits could be changed, refresh
- * the scratch area
- */
- udp_set_dev_scratch(skb);
+ udp_skb_csum_unnecessary_set(skb);
break;
}
}
@@ -1537,7 +1548,7 @@ static int first_packet_length(struct sock *sk)
spin_lock_bh(&rcvq->lock);
skb = __first_packet_length(sk, rcvq, &total);
- if (!skb && !skb_queue_empty(sk_queue)) {
+ if (!skb && !skb_queue_empty_lockless(sk_queue)) {
spin_lock(&sk_queue->lock);
skb_queue_splice_tail_init(sk_queue, rcvq);
spin_unlock(&sk_queue->lock);
@@ -1612,7 +1623,7 @@ struct sk_buff *__skb_recv_udp(struct sock *sk, unsigned int flags,
return skb;
}
- if (skb_queue_empty(sk_queue)) {
+ if (skb_queue_empty_lockless(sk_queue)) {
spin_unlock_bh(&queue->lock);
goto busy_check;
}
@@ -1639,7 +1650,7 @@ struct sk_buff *__skb_recv_udp(struct sock *sk, unsigned int flags,
break;
sk_busy_loop(sk, flags & MSG_DONTWAIT);
- } while (!skb_queue_empty(sk_queue));
+ } while (!skb_queue_empty_lockless(sk_queue));
/* sk_queue is empty, reader_queue may contain peeked packets */
} while (timeo &&
@@ -2685,7 +2696,7 @@ __poll_t udp_poll(struct file *file, struct socket *sock, poll_table *wait)
__poll_t mask = datagram_poll(file, sock, wait);
struct sock *sk = sock->sk;
- if (!skb_queue_empty(&udp_sk(sk)->reader_queue))
+ if (!skb_queue_empty_lockless(&udp_sk(sk)->reader_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* Check for false positives due to checksum errors */
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 2b144b9..1e5e2e4 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -222,12 +222,13 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
}
static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
struct dst_entry *path = xdst->route;
- path->ops->update_pmtu(path, sk, skb, mtu);
+ path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
}
static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 9638c5e..13c0af0 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -179,7 +179,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp);
static void addrconf_dad_work(struct work_struct *w);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
bool send_na);
-static void addrconf_dad_run(struct inet6_dev *idev);
+static void addrconf_dad_run(struct inet6_dev *idev, bool restart);
static void addrconf_rs_timer(struct timer_list *t);
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
@@ -3455,6 +3455,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_change_info *change_info;
struct netdev_notifier_changeupper_info *info;
struct inet6_dev *idev = __in6_dev_get(dev);
struct net *net = dev_net(dev);
@@ -3529,7 +3530,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
break;
}
- if (idev) {
+ if (!IS_ERR_OR_NULL(idev)) {
if (idev->if_flags & IF_READY) {
/* device is already configured -
* but resend MLD reports, we might
@@ -3537,6 +3538,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
* multicast snooping switches
*/
ipv6_mc_up(idev);
+ change_info = ptr;
+ if (change_info->flags_changed & IFF_NOARP)
+ addrconf_dad_run(idev, true);
rt6_sync_up(dev, RTNH_F_LINKDOWN);
break;
}
@@ -3571,7 +3575,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
if (!IS_ERR_OR_NULL(idev)) {
if (run_pending)
- addrconf_dad_run(idev);
+ addrconf_dad_run(idev, false);
/* Device has an address by now */
rt6_sync_up(dev, RTNH_F_DEAD);
@@ -4189,16 +4193,19 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
addrconf_verify_rtnl();
}
-static void addrconf_dad_run(struct inet6_dev *idev)
+static void addrconf_dad_run(struct inet6_dev *idev, bool restart)
{
struct inet6_ifaddr *ifp;
read_lock_bh(&idev->lock);
list_for_each_entry(ifp, &idev->addr_list, if_list) {
spin_lock(&ifp->lock);
- if (ifp->flags & IFA_F_TENTATIVE &&
- ifp->state == INET6_IFADDR_STATE_DAD)
+ if ((ifp->flags & IFA_F_TENTATIVE &&
+ ifp->state == INET6_IFADDR_STATE_DAD) || restart) {
+ if (restart)
+ ifp->state = INET6_IFADDR_STATE_PREDAD;
addrconf_dad_kick(ifp);
+ }
spin_unlock(&ifp->lock);
}
read_unlock_bh(&idev->lock);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 9a31d13..890adad 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -150,7 +150,7 @@ struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu)
if (IS_ERR(dst))
return NULL;
- dst->ops->update_pmtu(dst, sk, NULL, mtu);
+ dst->ops->update_pmtu(dst, sk, NULL, mtu, true);
dst = inet6_csk_route_socket(sk, &fl6);
return IS_ERR(dst) ? NULL : dst;
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 3d7c746..d9e2575 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -118,7 +118,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
if (sk->sk_bound_dev_if)
score++;
}
- if (sk->sk_incoming_cpu == raw_smp_processor_id())
+ if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id())
score++;
}
return score;
@@ -171,6 +171,7 @@ struct sock *inet6_lookup_listener(struct net *net,
bool exact_dif = inet6_exact_dif_match(net, skb);
struct inet_listen_hashbucket *ilb2;
struct sock *sk, *result = NULL;
+ struct hlist_nulls_node *node;
int score, hiscore = 0;
unsigned int hash2;
u32 phash = 0;
@@ -206,7 +207,7 @@ struct sock *inet6_lookup_listener(struct net *net,
goto done;
port_lookup:
- sk_for_each(sk, &ilb->head) {
+ sk_nulls_for_each(sk, node, &ilb->nulls_head) {
score = compute_score(sk, net, hnum, daddr, dif, sdif, exact_dif);
if (score > hiscore) {
if (sk->sk_reuseport) {
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index a23516e..b3515a4 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1000,9 +1000,9 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
dsfield = key->tos;
if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
goto tx_err;
- md = ip_tunnel_info_opts(tun_info);
- if (!md)
+ if (tun_info->options_len < sizeof(*md))
goto tx_err;
+ md = ip_tunnel_info_opts(tun_info);
tun_id = tunnel_id_to_key32(key->tun_id);
if (md->version == 1) {
@@ -1060,7 +1060,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
/* TooBig packet may have updated dst->dev's mtu */
if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu)
- dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu);
+ dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false);
err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
NEXTHDR_GRE);
@@ -2135,9 +2135,23 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct __ip6_tnl_parm *p = &t->parms;
__be16 o_flags = p->o_flags;
- if ((p->erspan_ver == 1 || p->erspan_ver == 2) &&
- !p->collect_md)
- o_flags |= TUNNEL_KEY;
+ if (p->erspan_ver == 1 || p->erspan_ver == 2) {
+ if (!p->collect_md)
+ o_flags |= TUNNEL_KEY;
+
+ if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
+ goto nla_put_failure;
+
+ if (p->erspan_ver == 1) {
+ if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
+ goto nla_put_failure;
+ } else {
+ if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
+ goto nla_put_failure;
+ if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
+ goto nla_put_failure;
+ }
+ }
if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
nla_put_be16(skb, IFLA_GRE_IFLAGS,
@@ -2152,8 +2166,7 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) ||
- nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark) ||
- nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
+ nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark))
goto nla_put_failure;
if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
@@ -2171,19 +2184,6 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
}
- if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
- goto nla_put_failure;
-
- if (p->erspan_ver == 1) {
- if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
- goto nla_put_failure;
- } else if (p->erspan_ver == 2) {
- if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
- goto nla_put_failure;
- if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
- goto nla_put_failure;
- }
-
return 0;
nla_put_failure:
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d0ad85b..e3b4237 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -645,7 +645,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (rel_info > dst_mtu(skb_dst(skb2)))
goto out;
- skb_dst_update_pmtu(skb2, rel_info);
+ skb_dst_update_pmtu_no_confirm(skb2, rel_info);
}
icmp_send(skb2, rel_type, rel_code, htonl(rel_info));
@@ -1137,7 +1137,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
mtu = max(mtu, skb->protocol == htons(ETH_P_IPV6) ?
IPV6_MIN_MTU : IPV4_MIN_MTU);
- skb_dst_update_pmtu(skb, mtu);
+ skb_dst_update_pmtu_no_confirm(skb, mtu);
if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
*pmtu = mtu;
err = -EMSGSIZE;
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 8b6eeff..bfd39db3 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -483,7 +483,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
mtu = dst_mtu(dst);
if (skb->len > mtu) {
- skb_dst_update_pmtu(skb, mtu);
+ skb_dst_update_pmtu_no_confirm(skb, mtu);
if (skb->protocol == htons(ETH_P_IPV6)) {
if (mtu < IPV6_MIN_MTU)
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index daf2e9e..06341def 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -1096,8 +1096,6 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
(newinfo->number <= oldinfo->initial_entries))
module_put(t->me);
- xt_table_unlock(t);
-
get_old_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
@@ -1111,6 +1109,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n");
}
vfree(counters);
+ xt_table_unlock(t);
return ret;
put_module:
diff --git a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
index 37b1d413..0ad0da5 100644
--- a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
@@ -87,18 +87,30 @@ static struct notifier_block masq_dev_notifier = {
struct masq_dev_work {
struct work_struct work;
struct net *net;
+ struct in6_addr addr;
int ifindex;
};
+static int inet_cmp(struct nf_conn *ct, void *work)
+{
+ struct masq_dev_work *w = (struct masq_dev_work *)work;
+ struct nf_conntrack_tuple *tuple;
+
+ if (!device_cmp(ct, (void *)(long)w->ifindex))
+ return 0;
+
+ tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+
+ return ipv6_addr_equal(&w->addr, &tuple->dst.u3.in6);
+}
+
static void iterate_cleanup_work(struct work_struct *work)
{
struct masq_dev_work *w;
- long index;
w = container_of(work, struct masq_dev_work, work);
- index = w->ifindex;
- nf_ct_iterate_cleanup_net(w->net, device_cmp, (void *)index, 0, 0);
+ nf_ct_iterate_cleanup_net(w->net, inet_cmp, (void *)w, 0, 0);
put_net(w->net);
kfree(w);
@@ -147,6 +159,7 @@ static int masq_inet6_event(struct notifier_block *this,
INIT_WORK(&w->work, iterate_cleanup_work);
w->ifindex = dev->ifindex;
w->net = net;
+ w->addr = ifa->addr;
schedule_work(&w->work);
return NOTIFY_DONE;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index b3e3c05..6370f07 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -99,7 +99,8 @@ static int ip6_pkt_prohibit(struct sk_buff *skb);
static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
static void ip6_link_failure(struct sk_buff *skb);
static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu);
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh);
static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb);
static int rt6_score_route(struct fib6_info *rt, int oif, int strict);
@@ -266,7 +267,8 @@ static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
}
static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
}
@@ -521,6 +523,7 @@ static void rt6_probe(struct fib6_info *rt)
{
struct __rt6_probe_work *work = NULL;
const struct in6_addr *nh_gw;
+ unsigned long last_probe;
struct neighbour *neigh;
struct net_device *dev;
struct inet6_dev *idev;
@@ -539,6 +542,7 @@ static void rt6_probe(struct fib6_info *rt)
nh_gw = &rt->fib6_nh.nh_gw;
dev = rt->fib6_nh.nh_dev;
rcu_read_lock_bh();
+ last_probe = READ_ONCE(rt->last_probe);
idev = __in6_dev_get(dev);
neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
if (neigh) {
@@ -554,13 +558,15 @@ static void rt6_probe(struct fib6_info *rt)
__neigh_set_probe_once(neigh);
}
write_unlock(&neigh->lock);
- } else if (time_after(jiffies, rt->last_probe +
+ } else if (time_after(jiffies, last_probe +
idev->cnf.rtr_probe_interval)) {
work = kmalloc(sizeof(*work), GFP_ATOMIC);
}
- if (work) {
- rt->last_probe = jiffies;
+ if (!work || cmpxchg(&rt->last_probe,
+ last_probe, jiffies) != last_probe) {
+ kfree(work);
+ } else {
INIT_WORK(&work->work, rt6_probe_deferred);
work->target = *nh_gw;
dev_hold(dev);
@@ -2348,7 +2354,8 @@ static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
}
static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
- const struct ipv6hdr *iph, u32 mtu)
+ const struct ipv6hdr *iph, u32 mtu,
+ bool confirm_neigh)
{
const struct in6_addr *daddr, *saddr;
struct rt6_info *rt6 = (struct rt6_info *)dst;
@@ -2366,7 +2373,10 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
daddr = NULL;
saddr = NULL;
}
- dst_confirm_neigh(dst, daddr);
+
+ if (confirm_neigh)
+ dst_confirm_neigh(dst, daddr);
+
mtu = max_t(u32, mtu, IPV6_MIN_MTU);
if (mtu >= dst_mtu(dst))
return;
@@ -2397,9 +2407,11 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
}
static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
- __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
+ __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu,
+ confirm_neigh);
}
void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
@@ -2419,7 +2431,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
dst = ip6_route_output(net, NULL, &fl6);
if (!dst->error)
- __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
+ __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true);
dst_release(dst);
}
EXPORT_SYMBOL_GPL(ip6_update_pmtu);
@@ -3066,6 +3078,9 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
if (!rt)
goto out;
+#ifdef CONFIG_IPV6_ROUTER_PREF
+ rt->last_probe = jiffies;
+#endif
if (cfg->fc_flags & RTF_ADDRCONF)
rt->dst_nocount = true;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 41b3fe8..bfed750 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -943,7 +943,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
}
if (tunnel->parms.iph.daddr)
- skb_dst_update_pmtu(skb, mtu);
+ skb_dst_update_pmtu_no_confirm(skb, mtu);
if (skb->len > mtu && !skb_is_gso(skb)) {
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index aebe981..c4932a6 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -734,6 +734,7 @@ static void tcp_v6_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
{
+ bool l3_slave = ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags);
struct inet_request_sock *ireq = inet_rsk(req);
const struct ipv6_pinfo *np = inet6_sk(sk_listener);
@@ -741,7 +742,7 @@ static void tcp_v6_init_req(struct request_sock *req,
ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
/* So that link locals have meaning */
- if (!sk_listener->sk_bound_dev_if &&
+ if ((!sk_listener->sk_bound_dev_if || l3_slave) &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
ireq->ir_iif = tcp_v6_iif(skb);
@@ -1839,7 +1840,8 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
/* Because we don't lock the socket,
* we might find a transient negative value.
*/
- rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
+ rx_queue = max_t(int, READ_ONCE(tp->rcv_nxt) -
+ tp->copied_seq, 0);
if (inet->transparent)
state_seq |= 0x80;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 00a2313..5c5e752 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -154,7 +154,7 @@ static int compute_score(struct sock *sk, struct net *net,
score++;
}
- if (sk->sk_incoming_cpu == raw_smp_processor_id())
+ if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id())
score++;
return score;
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index d35bcf9..3023259 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -221,12 +221,13 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
}
static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
- struct sk_buff *skb, u32 mtu)
+ struct sk_buff *skb, u32 mtu,
+ bool confirm_neigh)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
struct dst_entry *path = xdst->route;
- path->ops->update_pmtu(path, sk, skb, mtu);
+ path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
}
static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
index 4d78375..647c055 100644
--- a/net/llc/llc_c_ac.c
+++ b/net/llc/llc_c_ac.c
@@ -372,6 +372,7 @@ int llc_conn_ac_send_i_cmd_p_set_1(struct sock *sk, struct sk_buff *skb)
llc_pdu_init_as_i_cmd(skb, 1, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
+ skb_get(skb);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
@@ -389,7 +390,8 @@ static int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk, struct sk_buff *skb)
llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
- rc = llc_conn_send_pdu(sk, skb);
+ skb_get(skb);
+ llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
return rc;
@@ -406,6 +408,7 @@ int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb)
llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
+ skb_get(skb);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
@@ -916,7 +919,8 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk,
llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
- rc = llc_conn_send_pdu(sk, skb);
+ skb_get(skb);
+ llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
return rc;
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 4ff89cb..ed2aca1 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -30,7 +30,7 @@
#endif
static int llc_find_offset(int state, int ev_type);
-static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *skb);
+static void llc_conn_send_pdus(struct sock *sk);
static int llc_conn_service(struct sock *sk, struct sk_buff *skb);
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
@@ -193,11 +193,11 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
return rc;
}
-int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
+void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
{
/* queue PDU to send to MAC layer */
skb_queue_tail(&sk->sk_write_queue, skb);
- return llc_conn_send_pdus(sk, skb);
+ llc_conn_send_pdus(sk);
}
/**
@@ -255,7 +255,7 @@ void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
- llc_conn_send_pdus(sk, NULL);
+ llc_conn_send_pdus(sk);
out:;
}
@@ -296,7 +296,7 @@ void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
- llc_conn_send_pdus(sk, NULL);
+ llc_conn_send_pdus(sk);
out:;
}
@@ -340,16 +340,12 @@ int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked)
/**
* llc_conn_send_pdus - Sends queued PDUs
* @sk: active connection
- * @hold_skb: the skb held by caller, or NULL if does not care
*
- * Sends queued pdus to MAC layer for transmission. When @hold_skb is
- * NULL, always return 0. Otherwise, return 0 if @hold_skb is sent
- * successfully, or 1 for failure.
+ * Sends queued pdus to MAC layer for transmission.
*/
-static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *hold_skb)
+static void llc_conn_send_pdus(struct sock *sk)
{
struct sk_buff *skb;
- int ret = 0;
while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) {
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
@@ -361,20 +357,10 @@ static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *hold_skb)
skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
if (!skb2)
break;
- dev_queue_xmit(skb2);
- } else {
- bool is_target = skb == hold_skb;
- int rc;
-
- if (is_target)
- skb_get(skb);
- rc = dev_queue_xmit(skb);
- if (is_target)
- ret = rc;
+ skb = skb2;
}
+ dev_queue_xmit(skb);
}
-
- return ret;
}
/**
diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c
index 260b3dc..64d4bef 100644
--- a/net/llc/llc_core.c
+++ b/net/llc/llc_core.c
@@ -127,9 +127,7 @@ void llc_sap_close(struct llc_sap *sap)
list_del_rcu(&sap->node);
spin_unlock_bh(&llc_sap_list_lock);
- synchronize_rcu();
-
- kfree(sap);
+ kfree_rcu(sap, rcu);
}
static struct packet_type llc_packet_type __read_mostly = {
diff --git a/net/llc/llc_s_ac.c b/net/llc/llc_s_ac.c
index a94bd56..7ae4cc6 100644
--- a/net/llc/llc_s_ac.c
+++ b/net/llc/llc_s_ac.c
@@ -58,8 +58,10 @@ int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb)
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_ui_cmd(skb);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
- if (likely(!rc))
+ if (likely(!rc)) {
+ skb_get(skb);
rc = dev_queue_xmit(skb);
+ }
return rc;
}
@@ -81,8 +83,10 @@ int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb)
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 0);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
- if (likely(!rc))
+ if (likely(!rc)) {
+ skb_get(skb);
rc = dev_queue_xmit(skb);
+ }
return rc;
}
@@ -135,8 +139,10 @@ int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb)
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_test_cmd(skb);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
- if (likely(!rc))
+ if (likely(!rc)) {
+ skb_get(skb);
rc = dev_queue_xmit(skb);
+ }
return rc;
}
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index a7f7b8f..be419062 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -197,29 +197,22 @@ static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb)
* After executing actions of the event, upper layer will be indicated
* if needed(on receiving an UI frame). sk can be null for the
* datalink_proto case.
+ *
+ * This function always consumes a reference to the skb.
*/
static void llc_sap_state_process(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
- /*
- * We have to hold the skb, because llc_sap_next_state
- * will kfree it in the sending path and we need to
- * look at the skb->cb, where we encode llc_sap_state_ev.
- */
- skb_get(skb);
ev->ind_cfm_flag = 0;
llc_sap_next_state(sap, skb);
- if (ev->ind_cfm_flag == LLC_IND) {
- if (skb->sk->sk_state == TCP_LISTEN)
- kfree_skb(skb);
- else {
- llc_save_primitive(skb->sk, skb, ev->prim);
- /* queue skb to the user. */
- if (sock_queue_rcv_skb(skb->sk, skb))
- kfree_skb(skb);
- }
+ if (ev->ind_cfm_flag == LLC_IND && skb->sk->sk_state != TCP_LISTEN) {
+ llc_save_primitive(skb->sk, skb, ev->prim);
+
+ /* queue skb to the user. */
+ if (sock_queue_rcv_skb(skb->sk, skb) == 0)
+ return;
}
kfree_skb(skb);
}
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 204a835..c29170e 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -32,7 +32,7 @@ static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
return LLC_PDU_IS_CMD(pdu) && /* command PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
- !pdu->dsap ? 0 : 1; /* NULL DSAP value */
+ !pdu->dsap; /* NULL DSAP value */
}
static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
@@ -42,7 +42,7 @@ static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
return LLC_PDU_IS_CMD(pdu) && /* command PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
- !pdu->dsap ? 0 : 1; /* NULL DSAP */
+ !pdu->dsap; /* NULL DSAP */
}
static int llc_station_ac_send_xid_r(struct sk_buff *skb)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 26e8fa7..93f72a4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3255,19 +3255,16 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
}
if (bss_conf->he_support) {
- u32 he_oper_params =
- le32_to_cpu(elems.he_operation->he_oper_params);
+ bss_conf->bss_color =
+ le32_get_bits(elems.he_operation->he_oper_params,
+ IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
- bss_conf->bss_color = he_oper_params &
- IEEE80211_HE_OPERATION_BSS_COLOR_MASK;
bss_conf->htc_trig_based_pkt_ext =
- (he_oper_params &
- IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK) <<
- IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET;
+ le32_get_bits(elems.he_operation->he_oper_params,
+ IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
bss_conf->frame_time_rts_th =
- (he_oper_params &
- IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK) <<
- IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ le32_get_bits(elems.he_operation->he_oper_params,
+ IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
bss_conf->multi_sta_back_32bit =
sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 67ebdea..3d55207 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -129,7 +129,7 @@
#define CCK_GROUP \
[MINSTREL_CCK_GROUP] = { \
- .streams = 0, \
+ .streams = 1, \
.flags = 0, \
.duration = { \
CCK_DURATION_LIST(false), \
@@ -282,7 +282,8 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
break;
/* short preamble */
- if (!(mi->supported[group] & BIT(idx)))
+ if ((mi->supported[group] & BIT(idx + 4)) &&
+ (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
idx += 4;
}
return &mi->groups[group].rates[idx];
@@ -1077,18 +1078,23 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
return;
sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES];
+ sample_idx %= MCS_GROUP_RATES;
+
+ if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP] &&
+ (sample_idx >= 4) != txrc->short_preamble)
+ return;
+
info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
rate->count = 1;
- if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
+ if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP]) {
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
rate->idx = mp->cck_rates[idx];
} else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
sample_group->streams);
} else {
- rate->idx = sample_idx % MCS_GROUP_RATES +
- (sample_group->streams - 1) * 8;
+ rate->idx = sample_idx + (sample_group->streams - 1) * 8;
}
rate->flags = sample_group->flags;
@@ -1132,7 +1138,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
u16 sta_cap = sta->ht_cap.cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
- struct sta_info *sinfo = container_of(sta, struct sta_info, sta);
int use_vht;
int n_supported = 0;
int ack_dur;
@@ -1258,8 +1263,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if (!n_supported)
goto use_legacy;
- if (test_sta_flag(sinfo, WLAN_STA_SHORT_PREAMBLE))
- mi->cck_supported_short |= mi->cck_supported_short << 4;
+ mi->supported[MINSTREL_CCK_GROUP] |= mi->cck_supported_short << 4;
/* create an initial rate table with the lowest supported rates */
minstrel_ht_update_stats(mp, mi);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f3420224..507409e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2356,7 +2356,8 @@ unsigned long ieee80211_sta_last_active(struct sta_info *sta)
{
struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta);
- if (time_after(stats->last_rx, sta->status_stats.last_ack))
+ if (!sta->status_stats.last_ack ||
+ time_after(stats->last_rx, sta->status_stats.last_ack))
return stats->last_rx;
return sta->status_stats.last_ack;
}
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 9afd3fc..d5db1c4 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -871,7 +871,8 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
I802_DEBUG_INC(local->dot11FailedCount);
}
- if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
+ if ((ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
+ ieee80211_has_pm(fc) &&
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) &&
!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
local->ps_sdata && !(local->scanning)) {
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
index 4f01321..794e0335 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
@@ -235,6 +235,9 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
else
ether_addr_copy(e.ether, eth_hdr(skb)->h_dest);
+ if (is_zero_ether_addr(e.ether))
+ return -EINVAL;
+
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index e2538c57..1566261 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1977,8 +1977,9 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
}
req_version->version = IPSET_PROTOCOL;
- ret = copy_to_user(user, req_version,
- sizeof(struct ip_set_req_version));
+ if (copy_to_user(user, req_version,
+ sizeof(struct ip_set_req_version)))
+ ret = -EFAULT;
goto done;
}
case IP_SET_OP_GET_BYNAME: {
@@ -2035,7 +2036,8 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
} /* end of switch(op) */
copy:
- ret = copy_to_user(user, data, copylen);
+ if (copy_to_user(user, data, copylen))
+ ret = -EFAULT;
done:
vfree(data);
diff --git a/net/netfilter/ipset/ip_set_hash_ipmac.c b/net/netfilter/ipset/ip_set_hash_ipmac.c
index 16ec822..f2c2f72 100644
--- a/net/netfilter/ipset/ip_set_hash_ipmac.c
+++ b/net/netfilter/ipset/ip_set_hash_ipmac.c
@@ -36,9 +36,6 @@ MODULE_ALIAS("ip_set_hash:ip,mac");
/* Type specific function prefix */
#define HTYPE hash_ipmac
-/* Zero valued element is not supported */
-static const unsigned char invalid_ether[ETH_ALEN] = { 0 };
-
/* IPv4 variant */
/* Member elements */
@@ -104,7 +101,7 @@ hash_ipmac4_kadt(struct ip_set *set, const struct sk_buff *skb,
else
ether_addr_copy(e.ether, eth_hdr(skb)->h_dest);
- if (ether_addr_equal(e.ether, invalid_ether))
+ if (is_zero_ether_addr(e.ether))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
@@ -140,7 +137,7 @@ hash_ipmac4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN);
- if (ether_addr_equal(e.ether, invalid_ether))
+ if (is_zero_ether_addr(e.ether))
return -IPSET_ERR_HASH_ELEM;
return adtfn(set, &e, &ext, &ext, flags);
@@ -215,12 +212,12 @@ hash_ipmac6_kadt(struct ip_set *set, const struct sk_buff *skb,
(skb_mac_header(skb) + ETH_HLEN) > skb->data)
return -EINVAL;
- if (opt->flags & IPSET_DIM_ONE_SRC)
+ if (opt->flags & IPSET_DIM_TWO_SRC)
ether_addr_copy(e.ether, eth_hdr(skb)->h_source);
else
ether_addr_copy(e.ether, eth_hdr(skb)->h_dest);
- if (ether_addr_equal(e.ether, invalid_ether))
+ if (is_zero_ether_addr(e.ether))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
@@ -260,7 +257,7 @@ hash_ipmac6_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN);
- if (ether_addr_equal(e.ether, invalid_ether))
+ if (is_zero_ether_addr(e.ether))
return -IPSET_ERR_HASH_ELEM;
return adtfn(set, &e, &ext, &ext, flags);
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
index 7588aea..80759aa 100644
--- a/net/netfilter/ipvs/ip_vs_app.c
+++ b/net/netfilter/ipvs/ip_vs_app.c
@@ -198,21 +198,29 @@ struct ip_vs_app *register_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *
mutex_lock(&__ip_vs_app_mutex);
+ /* increase the module use count */
+ if (!ip_vs_use_count_inc()) {
+ err = -ENOENT;
+ goto out_unlock;
+ }
+
list_for_each_entry(a, &ipvs->app_list, a_list) {
if (!strcmp(app->name, a->name)) {
err = -EEXIST;
+ /* decrease the module use count */
+ ip_vs_use_count_dec();
goto out_unlock;
}
}
a = kmemdup(app, sizeof(*app), GFP_KERNEL);
if (!a) {
err = -ENOMEM;
+ /* decrease the module use count */
+ ip_vs_use_count_dec();
goto out_unlock;
}
INIT_LIST_HEAD(&a->incs_list);
list_add(&a->a_list, &ipvs->app_list);
- /* increase the module use count */
- ip_vs_use_count_inc();
out_unlock:
mutex_unlock(&__ip_vs_app_mutex);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 3df94a4..c339b5e 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -98,7 +98,6 @@ static bool __ip_vs_addr_is_local_v6(struct net *net,
static void update_defense_level(struct netns_ipvs *ipvs)
{
struct sysinfo i;
- static int old_secure_tcp = 0;
int availmem;
int nomem;
int to_change = -1;
@@ -179,35 +178,35 @@ static void update_defense_level(struct netns_ipvs *ipvs)
spin_lock(&ipvs->securetcp_lock);
switch (ipvs->sysctl_secure_tcp) {
case 0:
- if (old_secure_tcp >= 2)
+ if (ipvs->old_secure_tcp >= 2)
to_change = 0;
break;
case 1:
if (nomem) {
- if (old_secure_tcp < 2)
+ if (ipvs->old_secure_tcp < 2)
to_change = 1;
ipvs->sysctl_secure_tcp = 2;
} else {
- if (old_secure_tcp >= 2)
+ if (ipvs->old_secure_tcp >= 2)
to_change = 0;
}
break;
case 2:
if (nomem) {
- if (old_secure_tcp < 2)
+ if (ipvs->old_secure_tcp < 2)
to_change = 1;
} else {
- if (old_secure_tcp >= 2)
+ if (ipvs->old_secure_tcp >= 2)
to_change = 0;
ipvs->sysctl_secure_tcp = 1;
}
break;
case 3:
- if (old_secure_tcp < 2)
+ if (ipvs->old_secure_tcp < 2)
to_change = 1;
break;
}
- old_secure_tcp = ipvs->sysctl_secure_tcp;
+ ipvs->old_secure_tcp = ipvs->sysctl_secure_tcp;
if (to_change >= 0)
ip_vs_protocol_timeout_change(ipvs,
ipvs->sysctl_secure_tcp > 1);
@@ -1204,7 +1203,8 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
struct ip_vs_service *svc = NULL;
/* increase the module use count */
- ip_vs_use_count_inc();
+ if (!ip_vs_use_count_inc())
+ return -ENOPROTOOPT;
/* Lookup the scheduler by 'u->sched_name' */
if (strcmp(u->sched_name, "none")) {
@@ -2363,9 +2363,6 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
if (copy_from_user(arg, user, len) != 0)
return -EFAULT;
- /* increase the module use count */
- ip_vs_use_count_inc();
-
/* Handle daemons since they have another lock */
if (cmd == IP_VS_SO_SET_STARTDAEMON ||
cmd == IP_VS_SO_SET_STOPDAEMON) {
@@ -2378,13 +2375,13 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
ret = -EINVAL;
if (strscpy(cfg.mcast_ifn, dm->mcast_ifn,
sizeof(cfg.mcast_ifn)) <= 0)
- goto out_dec;
+ return ret;
cfg.syncid = dm->syncid;
ret = start_sync_thread(ipvs, &cfg, dm->state);
} else {
ret = stop_sync_thread(ipvs, dm->state);
}
- goto out_dec;
+ return ret;
}
mutex_lock(&__ip_vs_mutex);
@@ -2479,10 +2476,6 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
out_unlock:
mutex_unlock(&__ip_vs_mutex);
- out_dec:
- /* decrease the module use count */
- ip_vs_use_count_dec();
-
return ret;
}
diff --git a/net/netfilter/ipvs/ip_vs_pe.c b/net/netfilter/ipvs/ip_vs_pe.c
index 0df17caa..714e7e0 100644
--- a/net/netfilter/ipvs/ip_vs_pe.c
+++ b/net/netfilter/ipvs/ip_vs_pe.c
@@ -67,7 +67,8 @@ int register_ip_vs_pe(struct ip_vs_pe *pe)
struct ip_vs_pe *tmp;
/* increase the module use count */
- ip_vs_use_count_inc();
+ if (!ip_vs_use_count_inc())
+ return -ENOENT;
mutex_lock(&ip_vs_pe_mutex);
/* Make sure that the pe with this name doesn't exist
diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c
index a2ff7d7..3bd0ff3 100644
--- a/net/netfilter/ipvs/ip_vs_sched.c
+++ b/net/netfilter/ipvs/ip_vs_sched.c
@@ -184,7 +184,8 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
}
/* increase the module use count */
- ip_vs_use_count_inc();
+ if (!ip_vs_use_count_inc())
+ return -ENOENT;
mutex_lock(&ip_vs_sched_mutex);
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index ecb7106..5acd99f 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -1762,6 +1762,10 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
IP_VS_DBG(7, "Each ip_vs_sync_conn entry needs %zd bytes\n",
sizeof(struct ip_vs_sync_conn_v0));
+ /* increase the module use count */
+ if (!ip_vs_use_count_inc())
+ return -ENOPROTOOPT;
+
/* Do not hold one mutex and then to block on another */
for (;;) {
rtnl_lock();
@@ -1892,9 +1896,6 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
mutex_unlock(&ipvs->sync_mutex);
rtnl_unlock();
- /* increase the module use count */
- ip_vs_use_count_inc();
-
return 0;
out:
@@ -1924,11 +1925,17 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
}
kfree(ti);
}
+
+ /* decrease the module use count */
+ ip_vs_use_count_dec();
return result;
out_early:
mutex_unlock(&ipvs->sync_mutex);
rtnl_unlock();
+
+ /* decrease the module use count */
+ ip_vs_use_count_dec();
return result;
}
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 473cce2..3f75cd9 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -209,7 +209,7 @@ static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu)
struct rtable *ort = skb_rtable(skb);
if (!skb->dev && sk && sk_fullsock(sk))
- ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu);
+ ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu, true);
}
static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af,
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 47e5a07..7ba9ea5 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3576,6 +3576,9 @@ static void __net_exit ctnetlink_net_exit_batch(struct list_head *net_exit_list)
list_for_each_entry(net, net_exit_list, exit_list)
ctnetlink_net_exit(net);
+
+ /* wait for other cpus until they are done with ctnl_notifiers */
+ synchronize_rcu();
}
static struct pernet_operations ctnetlink_net_ops = {
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 8ade405..70bd730 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -187,6 +187,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
{
int err;
+ flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
+
err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[0].node,
nf_flow_offload_rhash_params);
@@ -203,7 +205,6 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
return err;
}
- flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
return 0;
}
EXPORT_SYMBOL_GPL(flow_offload_add);
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index 1f30860..aa1be64 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -18,6 +18,7 @@
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
@@ -316,6 +317,9 @@ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
static void nf_nat_sip_expected(struct nf_conn *ct,
struct nf_conntrack_expect *exp)
{
+ struct nf_conn_help *help = nfct_help(ct->master);
+ struct nf_conntrack_expect *pair_exp;
+ int range_set_for_snat = 0;
struct nf_nat_range2 range;
/* This must be a fresh one. */
@@ -327,15 +331,42 @@ static void nf_nat_sip_expected(struct nf_conn *ct,
range.min_addr = range.max_addr = exp->saved_addr;
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
- /* Change src to where master sends to, but only if the connection
- * actually came from the same source. */
- if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
+ /* Do media streams SRC manip according with the parameters
+ * found in the paired expectation.
+ */
+ if (exp->class != SIP_EXPECT_SIGNALLING) {
+ spin_lock_bh(&nf_conntrack_expect_lock);
+ hlist_for_each_entry(pair_exp, &help->expectations, lnode) {
+ if (pair_exp->tuple.src.l3num == nf_ct_l3num(ct) &&
+ pair_exp->tuple.dst.protonum == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum &&
+ nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, &pair_exp->saved_addr) &&
+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all == pair_exp->saved_proto.all) {
+ range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
+ range.min_proto.all = range.max_proto.all = pair_exp->tuple.dst.u.all;
+ range.min_addr = range.max_addr = pair_exp->tuple.dst.u3;
+ range_set_for_snat = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ }
+
+ /* When no paired expectation has been found, change src to
+ * where master sends to, but only if the connection actually came
+ * from the same source.
+ */
+ if (!range_set_for_snat &&
+ nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
&ct->master->tuplehash[exp->dir].tuple.src.u3)) {
range.flags = NF_NAT_RANGE_MAP_IPS;
range.min_addr = range.max_addr
= ct->master->tuplehash[!exp->dir].tuple.dst.u3;
- nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
+ range_set_for_snat = 1;
}
+
+ /* Perform SRC manip. */
+ if (range_set_for_snat)
+ nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
}
static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index a96a8c1..ee6d983 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -174,7 +174,7 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
goto err;
}
- if (!skb_dst_force(skb) && state->hook != NF_INET_PRE_ROUTING) {
+ if (skb_dst(skb) && !skb_dst_force(skb)) {
status = -ENETDOWN;
goto err;
}
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 24fddf0..4711a8b 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1031,7 +1031,8 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk,
static void nf_tables_table_destroy(struct nft_ctx *ctx)
{
- BUG_ON(ctx->table->use > 0);
+ if (WARN_ON(ctx->table->use > 0))
+ return;
rhltable_destroy(&ctx->table->chains_ht);
kfree(ctx->table->name);
@@ -1446,7 +1447,8 @@ static void nf_tables_chain_destroy(struct nft_ctx *ctx)
{
struct nft_chain *chain = ctx->chain;
- BUG_ON(chain->use > 0);
+ if (WARN_ON(chain->use > 0))
+ return;
/* no concurrent access possible anymore */
nf_tables_chain_free_chain_rules(chain);
@@ -2608,17 +2610,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
if (chain->use == UINT_MAX)
return -EOVERFLOW;
- }
- if (nla[NFTA_RULE_POSITION]) {
- if (!(nlh->nlmsg_flags & NLM_F_CREATE))
- return -EOPNOTSUPP;
-
- pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
- old_rule = __nft_rule_lookup(chain, pos_handle);
- if (IS_ERR(old_rule)) {
- NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
- return PTR_ERR(old_rule);
+ if (nla[NFTA_RULE_POSITION]) {
+ pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
+ old_rule = __nft_rule_lookup(chain, pos_handle);
+ if (IS_ERR(old_rule)) {
+ NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
+ return PTR_ERR(old_rule);
+ }
}
}
@@ -4118,8 +4117,10 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err;
err = -EINVAL;
- if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
+ if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
+ nft_data_release(&elem.key.val, desc.type);
return err;
+ }
priv = set->ops->get(ctx->net, set, &elem, flags);
if (IS_ERR(priv))
@@ -4352,14 +4353,20 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_DATA] == NULL &&
!(flags & NFT_SET_ELEM_INTERVAL_END))
return -EINVAL;
- if (nla[NFTA_SET_ELEM_DATA] != NULL &&
- flags & NFT_SET_ELEM_INTERVAL_END)
- return -EINVAL;
} else {
if (nla[NFTA_SET_ELEM_DATA] != NULL)
return -EINVAL;
}
+ if ((flags & NFT_SET_ELEM_INTERVAL_END) &&
+ (nla[NFTA_SET_ELEM_DATA] ||
+ nla[NFTA_SET_ELEM_OBJREF] ||
+ nla[NFTA_SET_ELEM_TIMEOUT] ||
+ nla[NFTA_SET_ELEM_EXPIRATION] ||
+ nla[NFTA_SET_ELEM_USERDATA] ||
+ nla[NFTA_SET_ELEM_EXPR]))
+ return -EINVAL;
+
timeout = 0;
if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
if (!(set->flags & NFT_SET_TIMEOUT))
@@ -5735,6 +5742,8 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
goto nla_put_failure;
nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);
+ if (!nest)
+ goto nla_put_failure;
if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||
nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))
goto nla_put_failure;
@@ -7253,7 +7262,8 @@ int __nft_release_basechain(struct nft_ctx *ctx)
{
struct nft_rule *rule, *nr;
- BUG_ON(!nft_is_base_chain(ctx->chain));
+ if (WARN_ON(!nft_is_base_chain(ctx->chain)))
+ return 0;
nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c
index fff8073..058ee84 100644
--- a/net/netfilter/nft_bitwise.c
+++ b/net/netfilter/nft_bitwise.c
@@ -83,7 +83,7 @@ static int nft_bitwise_init(const struct nft_ctx *ctx,
tb[NFTA_BITWISE_MASK]);
if (err < 0)
return err;
- if (d1.len != priv->len) {
+ if (d1.type != NFT_DATA_VALUE || d1.len != priv->len) {
err = -EINVAL;
goto err1;
}
@@ -92,7 +92,7 @@ static int nft_bitwise_init(const struct nft_ctx *ctx,
tb[NFTA_BITWISE_XOR]);
if (err < 0)
goto err1;
- if (d2.len != priv->len) {
+ if (d2.type != NFT_DATA_VALUE || d2.len != priv->len) {
err = -EINVAL;
goto err2;
}
diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c
index fa90a84..7007045 100644
--- a/net/netfilter/nft_cmp.c
+++ b/net/netfilter/nft_cmp.c
@@ -79,7 +79,14 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
tb[NFTA_CMP_DATA]);
- BUG_ON(err < 0);
+ if (err < 0)
+ return err;
+
+ if (desc.type != NFT_DATA_VALUE) {
+ err = -EINVAL;
+ nft_data_release(&priv->data, desc.type);
+ return err;
+ }
priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
err = nft_validate_register_load(priv->sreg, desc.len);
@@ -129,7 +136,8 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
err = nft_data_init(NULL, &data, sizeof(data), &desc,
tb[NFTA_CMP_DATA]);
- BUG_ON(err < 0);
+ if (err < 0)
+ return err;
priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
err = nft_validate_register_load(priv->sreg, desc.len);
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 1245e02..469f9da 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -269,6 +269,24 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
kfree(expr->ops);
}
+static int nft_extension_dump_info(struct sk_buff *skb, int attr,
+ const void *info,
+ unsigned int size, unsigned int user_size)
+{
+ unsigned int info_size, aligned_size = XT_ALIGN(size);
+ struct nlattr *nla;
+
+ nla = nla_reserve(skb, attr, aligned_size);
+ if (!nla)
+ return -1;
+
+ info_size = user_size ? : size;
+ memcpy(nla_data(nla), info, info_size);
+ memset(nla_data(nla) + info_size, 0, aligned_size - info_size);
+
+ return 0;
+}
+
static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct xt_target *target = expr->ops->data;
@@ -276,7 +294,8 @@ static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) ||
nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) ||
- nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(target->targetsize), info))
+ nft_extension_dump_info(skb, NFTA_TARGET_INFO, info,
+ target->targetsize, target->usersize))
goto nla_put_failure;
return 0;
@@ -504,7 +523,8 @@ static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr,
if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) ||
nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) ||
- nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(match->matchsize), info))
+ nft_extension_dump_info(skb, NFTA_MATCH_INFO, info,
+ match->matchsize, match->usersize))
goto nla_put_failure;
return 0;
diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c
index cedb96c..2e1d2ec 100644
--- a/net/netfilter/nft_range.c
+++ b/net/netfilter/nft_range.c
@@ -70,11 +70,21 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
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]);
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/netfilter/nft_reject.c b/net/netfilter/nft_reject.c
index 29f5bd2..b48e58c 100644
--- a/net/netfilter/nft_reject.c
+++ b/net/netfilter/nft_reject.c
@@ -94,7 +94,8 @@ static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX + 1] = {
int nft_reject_icmp_code(u8 code)
{
- BUG_ON(code > NFT_REJECT_ICMPX_MAX);
+ if (WARN_ON_ONCE(code > NFT_REJECT_ICMPX_MAX))
+ return ICMP_NET_UNREACH;
return icmp_code_v4[code];
}
@@ -111,7 +112,8 @@ static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX + 1] = {
int nft_reject_icmpv6_code(u8 code)
{
- BUG_ON(code > NFT_REJECT_ICMPX_MAX);
+ if (WARN_ON_ONCE(code > NFT_REJECT_ICMPX_MAX))
+ return ICMPV6_NOROUTE;
return icmp_code_v6[code];
}
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index b3e75f9..0221510 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -77,8 +77,13 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
parent = rcu_dereference_raw(parent->rb_left);
continue;
}
- if (nft_rbtree_interval_end(rbe))
- goto out;
+ if (nft_rbtree_interval_end(rbe)) {
+ if (nft_set_is_anonymous(set))
+ return false;
+ parent = rcu_dereference_raw(parent->rb_left);
+ interval = NULL;
+ continue;
+ }
*ext = &rbe->ext;
return true;
@@ -91,7 +96,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
*ext = &interval->ext;
return true;
}
-out:
+
return false;
}
@@ -139,8 +144,10 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set,
} else if (d > 0) {
parent = rcu_dereference_raw(parent->rb_right);
} else {
- if (!nft_set_elem_active(&rbe->ext, genmask))
+ if (!nft_set_elem_active(&rbe->ext, genmask)) {
parent = rcu_dereference_raw(parent->rb_left);
+ continue;
+ }
if (!nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) ||
(*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) ==
@@ -148,7 +155,11 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set,
*elem = rbe;
return true;
}
- return false;
+
+ if (nft_rbtree_interval_end(rbe))
+ interval = NULL;
+
+ parent = rcu_dereference_raw(parent->rb_left);
}
}
diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c
index f92a82c..9598015 100644
--- a/net/netfilter/nft_tproxy.c
+++ b/net/netfilter/nft_tproxy.c
@@ -50,7 +50,7 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr,
taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr);
if (priv->sreg_port)
- tport = regs->data[priv->sreg_port];
+ tport = nft_reg_load16(®s->data[priv->sreg_port]);
if (!tport)
tport = hp->dest;
@@ -117,7 +117,7 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr,
taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr);
if (priv->sreg_port)
- tport = regs->data[priv->sreg_port];
+ tport = nft_reg_load16(®s->data[priv->sreg_port]);
if (!tport)
tport = hp->dest;
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index e0a2cb8..7d76635 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -566,11 +566,11 @@ static __poll_t llcp_sock_poll(struct file *file, struct socket *sock,
if (sk->sk_state == LLCP_LISTEN)
return llcp_accept_poll(sk);
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
if (sk->sk_state == LLCP_CLOSED)
diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c
index a66f102..040576d 100644
--- a/net/nfc/nci/uart.c
+++ b/net/nfc/nci/uart.c
@@ -348,7 +348,7 @@ static int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data,
nu->rx_packet_len = -1;
nu->rx_skb = nci_skb_alloc(nu->ndev,
NCI_MAX_PACKET_SIZE,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!nu->rx_skb)
return -ENOMEM;
}
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index b366226..3093885 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -1110,7 +1110,6 @@ static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
local = nfc_llcp_find_local(dev);
if (!local) {
- nfc_put_device(dev);
rc = -ENODEV;
goto exit;
}
@@ -1170,7 +1169,6 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
local = nfc_llcp_find_local(dev);
if (!local) {
- nfc_put_device(dev);
rc = -ENODEV;
goto exit;
}
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 35ae64c..6dcb59f 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -897,6 +897,17 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
}
err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype);
+ if (err == NF_ACCEPT &&
+ ct->status & IPS_SRC_NAT && ct->status & IPS_DST_NAT) {
+ if (maniptype == NF_NAT_MANIP_SRC)
+ maniptype = NF_NAT_MANIP_DST;
+ else
+ maniptype = NF_NAT_MANIP_SRC;
+
+ err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range,
+ maniptype);
+ }
+
/* Mark NAT done if successful and update the flow key. */
if (err == NF_ACCEPT)
ovs_nat_update_key(key, skb, maniptype);
@@ -1199,7 +1210,8 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
&info->labels.mask);
if (err)
return err;
- } else if (labels_nonzero(&info->labels.mask)) {
+ } else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
+ labels_nonzero(&info->labels.mask)) {
err = ovs_ct_set_labels(ct, key, &info->labels.value,
&info->labels.mask);
if (err)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 8e396c7..f350fae 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -697,9 +697,13 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts,
{
size_t len = NLMSG_ALIGN(sizeof(struct ovs_header));
- /* OVS_FLOW_ATTR_UFID */
+ /* OVS_FLOW_ATTR_UFID, or unmasked flow key as fallback
+ * see ovs_nla_put_identifier()
+ */
if (sfid && ovs_identifier_is_ufid(sfid))
len += nla_total_size(sfid->ufid_len);
+ else
+ len += nla_total_size(ovs_key_attr_size());
/* OVS_FLOW_ATTR_KEY */
if (!sfid || should_fill_key(sfid, ufid_flags))
@@ -875,7 +879,10 @@ static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
info->snd_portid, info->snd_seq, 0,
cmd, ufid_flags);
- BUG_ON(retval < 0);
+ if (WARN_ON_ONCE(retval < 0)) {
+ kfree_skb(skb);
+ skb = ERR_PTR(retval);
+ }
return skb;
}
@@ -1338,7 +1345,10 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
OVS_FLOW_CMD_DEL,
ufid_flags);
rcu_read_unlock();
- BUG_ON(err < 0);
+ if (WARN_ON_ONCE(err < 0)) {
+ kfree_skb(reply);
+ goto out_free;
+ }
ovs_notify(&dp_flow_genl_family, reply, info);
} else {
@@ -1346,6 +1356,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
}
}
+out_free:
ovs_flow_free(flow, true);
return 0;
unlock:
@@ -1843,7 +1854,7 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = {
/* Called with ovs_mutex or RCU read lock. */
static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
struct net *net, u32 portid, u32 seq,
- u32 flags, u8 cmd)
+ u32 flags, u8 cmd, gfp_t gfp)
{
struct ovs_header *ovs_header;
struct ovs_vport_stats vport_stats;
@@ -1864,7 +1875,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
goto nla_put_failure;
if (!net_eq(net, dev_net(vport->dev))) {
- int id = peernet2id_alloc(net, dev_net(vport->dev));
+ int id = peernet2id_alloc(net, dev_net(vport->dev), gfp);
if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
goto nla_put_failure;
@@ -1905,11 +1916,12 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
struct sk_buff *skb;
int retval;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
- retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd);
+ retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd,
+ GFP_KERNEL);
BUG_ON(retval < 0);
return skb;
@@ -2042,7 +2054,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
info->snd_portid, info->snd_seq, 0,
- OVS_VPORT_CMD_NEW);
+ OVS_VPORT_CMD_NEW, GFP_KERNEL);
if (netdev_get_fwd_headroom(vport->dev) > dp->max_headroom)
update_headroom(dp);
@@ -2101,7 +2113,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
info->snd_portid, info->snd_seq, 0,
- OVS_VPORT_CMD_NEW);
+ OVS_VPORT_CMD_NEW, GFP_ATOMIC);
BUG_ON(err < 0);
ovs_unlock();
@@ -2140,7 +2152,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
info->snd_portid, info->snd_seq, 0,
- OVS_VPORT_CMD_DEL);
+ OVS_VPORT_CMD_DEL, GFP_KERNEL);
BUG_ON(err < 0);
/* the vport deletion may trigger dp headroom update */
@@ -2182,7 +2194,7 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto exit_unlock_free;
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
info->snd_portid, info->snd_seq, 0,
- OVS_VPORT_CMD_NEW);
+ OVS_VPORT_CMD_NEW, GFP_ATOMIC);
BUG_ON(err < 0);
rcu_read_unlock();
@@ -2218,7 +2230,8 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI,
- OVS_VPORT_CMD_NEW) < 0)
+ OVS_VPORT_CMD_NEW,
+ GFP_ATOMIC) < 0)
goto out;
j++;
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 5a304cf..3ebf8ba 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -43,7 +43,8 @@ static struct internal_dev *internal_dev_priv(struct net_device *netdev)
}
/* Called with rcu_read_lock_bh. */
-static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t
+internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
{
int len, err;
@@ -62,7 +63,7 @@ static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
} else {
netdev->stats.tx_errors++;
}
- return 0;
+ return NETDEV_TX_OK;
}
static int internal_dev_open(struct net_device *netdev)
@@ -149,7 +150,7 @@ static void do_setup(struct net_device *netdev)
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_OPENVSWITCH |
IFF_NO_QUEUE;
netdev->needs_free_netdev = true;
- netdev->priv_destructor = internal_dev_destructor;
+ netdev->priv_destructor = NULL;
netdev->ethtool_ops = &internal_dev_ethtool_ops;
netdev->rtnl_link_ops = &internal_dev_link_ops;
@@ -171,7 +172,6 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
struct internal_dev *internal_dev;
struct net_device *dev;
int err;
- bool free_vport = true;
vport = ovs_vport_alloc(0, &ovs_internal_vport_ops, parms);
if (IS_ERR(vport)) {
@@ -202,10 +202,9 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
rtnl_lock();
err = register_netdevice(vport->dev);
- if (err) {
- free_vport = false;
+ if (err)
goto error_unlock;
- }
+ vport->dev->priv_destructor = internal_dev_destructor;
dev_set_promiscuity(vport->dev, 1);
rtnl_unlock();
@@ -219,8 +218,7 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
error_free_netdev:
free_netdev(dev);
error_free_vport:
- if (free_vport)
- ovs_vport_free(vport);
+ ovs_vport_free(vport);
error:
return ERR_PTR(err);
}
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 7e25a6a..fa6cc71 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -552,7 +552,8 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po,
msec = 1;
div = ecmd.base.speed / 1000;
}
- }
+ } else
+ return DEFAULT_PRB_RETIRE_TOV;
mbits = (blk_size_in_bytes * 8) / (1024 * 1024);
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index 3018799..1ae6295 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -351,9 +351,9 @@ static __poll_t pn_socket_poll(struct file *file, struct socket *sock,
if (sk->sk_state == TCP_CLOSE)
return EPOLLERR;
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
- if (!skb_queue_empty(&pn->ctrlreq_queue))
+ if (!skb_queue_empty_lockless(&pn->ctrlreq_queue))
mask |= EPOLLPRI;
if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
return EPOLLHUP;
diff --git a/net/psample/psample.c b/net/psample/psample.c
index 4cea3532..30e8239 100644
--- a/net/psample/psample.c
+++ b/net/psample/psample.c
@@ -223,7 +223,7 @@ void psample_sample_packet(struct psample_group *group, struct sk_buff *skb,
data_len = PSAMPLE_MAX_PACKET_SIZE - meta_len - NLA_HDRLEN
- NLA_ALIGNTO;
- nl_skb = genlmsg_new(meta_len + data_len, GFP_ATOMIC);
+ nl_skb = genlmsg_new(meta_len + nla_total_size(data_len), GFP_ATOMIC);
if (unlikely(!nl_skb))
return;
diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
index 0bd8625..6f1f1d5 100644
--- a/net/qrtr/qrtr.c
+++ b/net/qrtr/qrtr.c
@@ -339,7 +339,8 @@ static void __qrtr_node_release(struct kref *kref)
if (node->nid != QRTR_EP_NID_AUTO) {
radix_tree_for_each_slot(slot, &qrtr_nodes, &iter, 0) {
if (node == *slot)
- radix_tree_delete(&qrtr_nodes, iter.index);
+ radix_tree_iter_delete(&qrtr_nodes, &iter,
+ slot);
}
}
@@ -355,7 +356,7 @@ static void __qrtr_node_release(struct kref *kref)
sock_put(waiter->sk);
kfree(waiter);
}
- radix_tree_delete(&node->qrtr_tx_flow, iter.index);
+ radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot);
kfree(flow);
}
mutex_unlock(&node->qrtr_tx_lock);
@@ -919,14 +920,12 @@ static bool qrtr_must_forward(struct qrtr_node *src,
return false;
}
-static void qrtr_fwd_ctrl_pkt(struct sk_buff *skb)
+static void qrtr_fwd_ctrl_pkt(struct qrtr_node *src, struct sk_buff *skb)
{
struct qrtr_node *node;
- struct qrtr_node *src;
struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb;
qrtr_skb_align_linearize(skb);
- src = qrtr_node_lookup(cb->src_node);
down_read(&qrtr_node_lock);
list_for_each_entry(node, &qrtr_all_epts, item) {
struct sockaddr_qrtr from;
@@ -951,7 +950,6 @@ static void qrtr_fwd_ctrl_pkt(struct sk_buff *skb)
qrtr_node_enqueue(node, skbn, cb->type, &from, &to, 0);
}
up_read(&qrtr_node_lock);
- qrtr_node_release(src);
}
static void qrtr_fwd_pkt(struct sk_buff *skb, struct qrtr_cb *cb)
@@ -987,7 +985,7 @@ static void qrtr_node_rx_work(struct kthread_work *work)
qrtr_node_assign(node, cb->src_node);
if (cb->type != QRTR_TYPE_DATA)
- qrtr_fwd_ctrl_pkt(skb);
+ qrtr_fwd_ctrl_pkt(node, skb);
if (cb->type == QRTR_TYPE_NEW_SERVER &&
skb->len == sizeof(pkt)) {
diff --git a/net/qrtr/tun.c b/net/qrtr/tun.c
index ccff1e5..e35869e 100644
--- a/net/qrtr/tun.c
+++ b/net/qrtr/tun.c
@@ -84,11 +84,14 @@ static ssize_t qrtr_tun_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (!kbuf)
return -ENOMEM;
- if (!copy_from_iter_full(kbuf, len, from))
+ if (!copy_from_iter_full(kbuf, len, from)) {
+ kfree(kbuf);
return -EFAULT;
+ }
ret = qrtr_endpoint_post(&tun->ep, kbuf, len);
+ kfree(kbuf);
return ret < 0 ? ret : len;
}
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index f0a061c..88841a3 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -1009,10 +1009,13 @@ static void rfkill_sync_work(struct work_struct *work)
int __must_check rfkill_register(struct rfkill *rfkill)
{
static unsigned long rfkill_no;
- struct device *dev = &rfkill->dev;
+ struct device *dev;
int error;
- BUG_ON(!rfkill);
+ if (!rfkill)
+ return -EINVAL;
+
+ dev = &rfkill->dev;
mutex_lock(&rfkill_global_mutex);
@@ -1323,10 +1326,12 @@ static const struct file_operations rfkill_fops = {
.llseek = no_llseek,
};
+#define RFKILL_NAME "rfkill"
+
static struct miscdevice rfkill_miscdev = {
- .name = "rfkill",
.fops = &rfkill_fops,
- .minor = MISC_DYNAMIC_MINOR,
+ .name = RFKILL_NAME,
+ .minor = RFKILL_MINOR,
};
static int __init rfkill_init(void)
@@ -1378,3 +1383,6 @@ static void __exit rfkill_exit(void)
class_unregister(&rfkill_class);
}
module_exit(rfkill_exit);
+
+MODULE_ALIAS_MISCDEV(RFKILL_MINOR);
+MODULE_ALIAS("devname:" RFKILL_NAME);
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c
index dc7fdaf..42582a9 100644
--- a/net/rxrpc/peer_event.c
+++ b/net/rxrpc/peer_event.c
@@ -153,6 +153,9 @@ void rxrpc_error_report(struct sock *sk)
struct rxrpc_peer *peer;
struct sk_buff *skb;
+ if (unlikely(!local))
+ return;
+
_enter("%p{%d}", sk, local->debug_id);
skb = sock_dequeue_err_skb(sk);
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c
index 71547e8..b91b090 100644
--- a/net/rxrpc/peer_object.c
+++ b/net/rxrpc/peer_object.c
@@ -220,7 +220,7 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
peer = kzalloc(sizeof(struct rxrpc_peer), gfp);
if (peer) {
atomic_set(&peer->usage, 1);
- peer->local = local;
+ peer->local = rxrpc_get_local(local);
INIT_HLIST_HEAD(&peer->error_targets);
peer->service_conns = RB_ROOT;
seqlock_init(&peer->service_conn_lock);
@@ -311,7 +311,6 @@ void rxrpc_new_incoming_peer(struct rxrpc_sock *rx, struct rxrpc_local *local,
unsigned long hash_key;
hash_key = rxrpc_peer_hash_key(local, &peer->srx);
- peer->local = local;
rxrpc_init_peer(rx, peer, hash_key);
spin_lock(&rxnet->peer_hash_lock);
@@ -386,7 +385,7 @@ struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *peer)
int n;
n = atomic_inc_return(&peer->usage);
- trace_rxrpc_peer(peer, rxrpc_peer_got, n, here);
+ trace_rxrpc_peer(peer->debug_id, rxrpc_peer_got, n, here);
return peer;
}
@@ -400,7 +399,7 @@ struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer)
if (peer) {
int n = atomic_fetch_add_unless(&peer->usage, 1, 0);
if (n > 0)
- trace_rxrpc_peer(peer, rxrpc_peer_got, n + 1, here);
+ trace_rxrpc_peer(peer->debug_id, rxrpc_peer_got, n + 1, here);
else
peer = NULL;
}
@@ -421,6 +420,7 @@ static void __rxrpc_put_peer(struct rxrpc_peer *peer)
list_del_init(&peer->keepalive_link);
spin_unlock_bh(&rxnet->peer_hash_lock);
+ rxrpc_put_local(peer->local);
kfree_rcu(peer, rcu);
}
@@ -430,11 +430,13 @@ static void __rxrpc_put_peer(struct rxrpc_peer *peer)
void rxrpc_put_peer(struct rxrpc_peer *peer)
{
const void *here = __builtin_return_address(0);
+ unsigned int debug_id;
int n;
if (peer) {
+ debug_id = peer->debug_id;
n = atomic_dec_return(&peer->usage);
- trace_rxrpc_peer(peer, rxrpc_peer_put, n, here);
+ trace_rxrpc_peer(debug_id, rxrpc_peer_put, n, here);
if (n == 0)
__rxrpc_put_peer(peer);
}
@@ -447,13 +449,15 @@ void rxrpc_put_peer(struct rxrpc_peer *peer)
void rxrpc_put_peer_locked(struct rxrpc_peer *peer)
{
const void *here = __builtin_return_address(0);
+ unsigned int debug_id = peer->debug_id;
int n;
n = atomic_dec_return(&peer->usage);
- trace_rxrpc_peer(peer, rxrpc_peer_put, n, here);
+ trace_rxrpc_peer(debug_id, rxrpc_peer_put, n, here);
if (n == 0) {
hash_del_rcu(&peer->hash_link);
list_del_init(&peer->keepalive_link);
+ rxrpc_put_local(peer->local);
kfree_rcu(peer, rcu);
}
}
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index 5d6ab4f..3e54ead 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -661,6 +661,7 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
case RXRPC_CALL_SERVER_PREALLOC:
case RXRPC_CALL_SERVER_SECURING:
case RXRPC_CALL_SERVER_ACCEPTING:
+ rxrpc_put_call(call, rxrpc_call_put);
ret = -EBUSY;
goto error_release_sock;
default:
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 33c0cc5..ce14faf 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -46,7 +46,7 @@ static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
int err = -EINVAL;
int rem;
- if (!nla || !n)
+ if (!nla)
return NULL;
keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
@@ -169,6 +169,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
}
parm = nla_data(pattr);
+ if (!parm->nkeys) {
+ NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
+ return -EINVAL;
+ }
ksize = parm->nkeys * sizeof(struct tc_pedit_key);
if (nla_len(pattr) < sizeof(*parm) + ksize) {
NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
@@ -182,12 +186,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
index = parm->index;
err = tcf_idr_check_alloc(tn, &index, a, bind);
if (!err) {
- if (!parm->nkeys) {
- tcf_idr_cleanup(tn, index);
- NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
- ret = -EINVAL;
- goto out_free;
- }
ret = tcf_idr_create(tn, index, est, a,
&act_pedit_ops, bind, false);
if (ret) {
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index 43309ff..e4fc6b2 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -137,6 +137,10 @@ static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst,
if (opt_len < 0)
return opt_len;
opts_len += opt_len;
+ if (opts_len > IP_TUNNEL_OPTS_MAX) {
+ NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size");
+ return -EINVAL;
+ }
if (dst) {
dst_len -= opt_len;
dst += opt_len;
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 9fd37d9..824e3c3 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1666,7 +1666,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (skb_is_gso(skb) && q->rate_flags & CAKE_FLAG_SPLIT_GSO) {
struct sk_buff *segs, *nskb;
netdev_features_t features = netif_skb_features(skb);
- unsigned int slen = 0;
+ unsigned int slen = 0, numsegs = 0;
segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
if (IS_ERR_OR_NULL(segs))
@@ -1682,6 +1682,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
flow_queue_add(flow, segs);
sch->q.qlen++;
+ numsegs++;
slen += segs->len;
q->buffer_used += segs->truesize;
b->packets++;
@@ -1695,7 +1696,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
sch->qstats.backlog += slen;
q->avg_window_bytes += slen;
- qdisc_tree_reduce_backlog(sch, 1, len);
+ qdisc_tree_reduce_backlog(sch, 1-numsegs, len-slen);
consume_skb(skb);
} else {
/* not splitting */
@@ -1757,7 +1758,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
q->avg_window_begin));
u64 b = q->avg_window_bytes * (u64)NSEC_PER_SEC;
- do_div(b, window_interval);
+ b = div64_u64(b, window_interval);
q->avg_peak_bandwidth =
cake_ewma(q->avg_peak_bandwidth, b,
b > q->avg_peak_bandwidth ? 2 : 8);
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 4808713..1ee2b77 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -735,10 +735,12 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
if (tb[TCA_FQ_QUANTUM]) {
u32 quantum = nla_get_u32(tb[TCA_FQ_QUANTUM]);
- if (quantum > 0)
+ if (quantum > 0 && quantum <= (1 << 20)) {
q->quantum = quantum;
- else
+ } else {
+ NL_SET_ERR_MSG_MOD(extack, "invalid quantum");
err = -EINVAL;
+ }
}
if (tb[TCA_FQ_INITIAL_QUANTUM])
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 30e32df..8a4d01e 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -577,6 +577,18 @@ struct Qdisc noop_qdisc = {
.dev_queue = &noop_netdev_queue,
.running = SEQCNT_ZERO(noop_qdisc.running),
.busylock = __SPIN_LOCK_UNLOCKED(noop_qdisc.busylock),
+ .gso_skb = {
+ .next = (struct sk_buff *)&noop_qdisc.gso_skb,
+ .prev = (struct sk_buff *)&noop_qdisc.gso_skb,
+ .qlen = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.gso_skb.lock),
+ },
+ .skb_bad_txq = {
+ .next = (struct sk_buff *)&noop_qdisc.skb_bad_txq,
+ .prev = (struct sk_buff *)&noop_qdisc.skb_bad_txq,
+ .qlen = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.skb_bad_txq.lock),
+ },
};
EXPORT_SYMBOL(noop_qdisc);
@@ -1253,8 +1265,6 @@ static void dev_init_scheduler_queue(struct net_device *dev,
rcu_assign_pointer(dev_queue->qdisc, qdisc);
dev_queue->qdisc_sleeping = qdisc;
- __skb_queue_head_init(&qdisc->gso_skb);
- __skb_queue_head_init(&qdisc->skb_bad_txq);
}
void dev_init_scheduler(struct net_device *dev)
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index a80fe8a..c53b720 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -4,11 +4,11 @@
* Copyright (C) 2013 Nandita Dukkipati <nanditad@google.com>
*/
-#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/vmalloc.h>
+#include <linux/siphash.h>
#include <net/pkt_sched.h>
#include <net/sock.h>
@@ -125,7 +125,7 @@ struct wdrr_bucket {
struct hhf_sched_data {
struct wdrr_bucket buckets[WDRR_BUCKET_CNT];
- u32 perturbation; /* hash perturbation */
+ siphash_key_t perturbation; /* hash perturbation */
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
u32 drop_overlimit; /* number of times max qdisc packet
* limit was hit
@@ -263,7 +263,7 @@ static enum wdrr_bucket_idx hhf_classify(struct sk_buff *skb, struct Qdisc *sch)
}
/* Get hashed flow-id of the skb. */
- hash = skb_get_hash_perturb(skb, q->perturbation);
+ hash = skb_get_hash_perturb(skb, &q->perturbation);
/* Check if this packet belongs to an already established HH flow. */
flow_pos = hash & HHF_BIT_MASK;
@@ -580,7 +580,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
sch->limit = 1000;
q->quantum = psched_mtu(qdisc_dev(sch));
- q->perturbation = prandom_u32();
+ get_random_bytes(&q->perturbation, sizeof(q->perturbation));
INIT_LIST_HEAD(&q->new_buckets);
INIT_LIST_HEAD(&q->old_buckets);
diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
index d6b8ae4..c008a31 100644
--- a/net/sched/sch_mq.c
+++ b/net/sched/sch_mq.c
@@ -158,6 +158,7 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
__gnet_stats_copy_queue(&sch->qstats,
qdisc->cpu_qstats,
&qdisc->qstats, qlen);
+ sch->q.qlen += qlen;
} else {
sch->q.qlen += qdisc->q.qlen;
sch->bstats.bytes += qdisc->bstats.bytes;
@@ -242,7 +243,8 @@ static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
sch = dev_queue->qdisc_sleeping;
- if (gnet_stats_copy_basic(&sch->running, d, NULL, &sch->bstats) < 0 ||
+ if (gnet_stats_copy_basic(&sch->running, d, sch->cpu_bstats,
+ &sch->bstats) < 0 ||
gnet_stats_copy_queue(d, NULL, &sch->qstats, sch->q.qlen) < 0)
return -1;
return 0;
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index 0e9d761..008db8d 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -413,6 +413,7 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
__gnet_stats_copy_queue(&sch->qstats,
qdisc->cpu_qstats,
&qdisc->qstats, qlen);
+ sch->q.qlen += qlen;
} else {
sch->q.qlen += qdisc->q.qlen;
sch->bstats.bytes += qdisc->bstats.bytes;
@@ -435,7 +436,7 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
opt.offset[tc] = dev->tc_to_txq[tc].offset;
}
- if (nla_put(skb, TCA_OPTIONS, NLA_ALIGN(sizeof(opt)), &opt))
+ if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
goto nla_put_failure;
if ((priv->flags & TC_MQPRIO_F_MODE) &&
@@ -559,8 +560,8 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl);
sch = dev_queue->qdisc_sleeping;
- if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
- d, NULL, &sch->bstats) < 0 ||
+ if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d,
+ sch->cpu_bstats, &sch->bstats) < 0 ||
gnet_stats_copy_queue(d, NULL,
&sch->qstats, sch->q.qlen) < 0)
return -1;
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 1da7ea8..1df78e3 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -343,7 +343,7 @@ static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
cl_q = q->queues[cl - 1];
if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
- d, NULL, &cl_q->bstats) < 0 ||
+ d, cl_q->cpu_bstats, &cl_q->bstats) < 0 ||
gnet_stats_copy_queue(d, NULL, &cl_q->qstats, cl_q->q.qlen) < 0)
return -1;
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 86350fe..15f8f24 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -474,7 +474,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
* skb will be queued.
*/
if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
- struct Qdisc *rootq = qdisc_root(sch);
+ struct Qdisc *rootq = qdisc_root_bh(sch);
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
q->duplicate = 0;
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 222e53d..1cbbd8c 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -314,8 +314,14 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
bool any_qdisc_is_offloaded;
int err;
- if (new == NULL)
- new = &noop_qdisc;
+ if (!new) {
+ new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+ TC_H_MAKE(sch->handle, arg), extack);
+ if (!new)
+ new = &noop_qdisc;
+ else
+ qdisc_hash_add(new, true);
+ }
*old = qdisc_replace(sch, new, &q->queues[band]);
@@ -396,7 +402,7 @@ static int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
cl_q = q->queues[cl - 1];
if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
- d, NULL, &cl_q->bstats) < 0 ||
+ d, cl_q->cpu_bstats, &cl_q->bstats) < 0 ||
gnet_stats_copy_queue(d, NULL, &cl_q->qstats, cl_q->q.qlen) < 0)
return -1;
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index 7cbdad8..1aa95e7 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -22,7 +22,7 @@
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/random.h>
-#include <linux/jhash.h>
+#include <linux/siphash.h>
#include <net/ip.h>
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>
@@ -49,7 +49,7 @@ struct sfb_bucket {
* (Section 4.4 of SFB reference : moving hash functions)
*/
struct sfb_bins {
- u32 perturbation; /* jhash perturbation */
+ siphash_key_t perturbation; /* siphash key */
struct sfb_bucket bins[SFB_LEVELS][SFB_NUMBUCKETS];
};
@@ -221,7 +221,8 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_da
static void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q)
{
- q->bins[slot].perturbation = prandom_u32();
+ get_random_bytes(&q->bins[slot].perturbation,
+ sizeof(q->bins[slot].perturbation));
}
static void sfb_swap_slot(struct sfb_sched_data *q)
@@ -318,9 +319,9 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
/* If using external classifiers, get result and record it. */
if (!sfb_classify(skb, fl, &ret, &salt))
goto other_drop;
- sfbhash = jhash_1word(salt, q->bins[slot].perturbation);
+ sfbhash = siphash_1u32(salt, &q->bins[slot].perturbation);
} else {
- sfbhash = skb_get_hash_perturb(skb, q->bins[slot].perturbation);
+ sfbhash = skb_get_hash_perturb(skb, &q->bins[slot].perturbation);
}
@@ -356,7 +357,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
/* Inelastic flow */
if (q->double_buffering) {
sfbhash = skb_get_hash_perturb(skb,
- q->bins[slot].perturbation);
+ &q->bins[slot].perturbation);
if (!sfbhash)
sfbhash = 1;
sfb_skb_cb(skb)->hashes[slot] = sfbhash;
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 650f214..d483d6b 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -18,7 +18,7 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/skbuff.h>
-#include <linux/jhash.h>
+#include <linux/siphash.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <net/netlink.h>
@@ -121,7 +121,7 @@ struct sfq_sched_data {
u8 headdrop;
u8 maxdepth; /* limit of packets per flow */
- u32 perturbation;
+ siphash_key_t perturbation;
u8 cur_depth; /* depth of longest slot */
u8 flags;
unsigned short scaled_quantum; /* SFQ_ALLOT_SIZE(quantum) */
@@ -161,7 +161,7 @@ static inline struct sfq_head *sfq_dep_head(struct sfq_sched_data *q, sfq_index
static unsigned int sfq_hash(const struct sfq_sched_data *q,
const struct sk_buff *skb)
{
- return skb_get_hash_perturb(skb, q->perturbation) & (q->divisor - 1);
+ return skb_get_hash_perturb(skb, &q->perturbation) & (q->divisor - 1);
}
static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
@@ -611,9 +611,11 @@ static void sfq_perturbation(struct timer_list *t)
struct sfq_sched_data *q = from_timer(q, t, perturb_timer);
struct Qdisc *sch = q->sch;
spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
+ siphash_key_t nkey;
+ get_random_bytes(&nkey, sizeof(nkey));
spin_lock(root_lock);
- q->perturbation = prandom_u32();
+ q->perturbation = nkey;
if (!q->filter_list && q->tail)
sfq_rehash(sch);
spin_unlock(root_lock);
@@ -692,7 +694,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
del_timer(&q->perturb_timer);
if (q->perturb_period) {
mod_timer(&q->perturb_timer, jiffies + q->perturb_period);
- q->perturbation = prandom_u32();
+ get_random_bytes(&q->perturbation, sizeof(q->perturbation));
}
sch_tree_unlock(sch);
kfree(p);
@@ -749,7 +751,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt,
q->quantum = psched_mtu(qdisc_dev(sch));
q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum);
q->perturb_period = 0;
- q->perturbation = prandom_u32();
+ get_random_bytes(&q->perturbation, sizeof(q->perturbation));
if (opt) {
int err = sfq_change(sch, opt);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 914750b..f68ccd1 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -80,6 +80,7 @@ static struct sctp_association *sctp_association_init(
/* Discarding const is appropriate here. */
asoc->ep = (struct sctp_endpoint *)ep;
asoc->base.sk = (struct sock *)sk;
+ asoc->base.net = sock_net(sk);
sctp_endpoint_hold(asoc->ep);
sock_hold(asoc->base.sk);
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index ce80878..d2048de 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -191,6 +191,12 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
* the packet
*/
max_data = asoc->frag_point;
+ if (unlikely(!max_data)) {
+ max_data = sctp_min_frag_point(sctp_sk(asoc->base.sk),
+ sctp_datachk_len(&asoc->stream));
+ pr_warn_ratelimited("%s: asoc:%p frag_point is zero, forcing max_data to default minimum (%Zu)",
+ __func__, asoc, max_data);
+ }
/* If the the peer requested that we authenticate DATA chunks
* we need to account for bundling of the AUTH chunks along with
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index c99114e..8640ded 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -165,6 +165,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
/* Remember who we are attached to. */
ep->base.sk = sk;
+ ep->base.net = sock_net(sk);
sock_hold(ep->base.sk);
return ep;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 5c36a99..bfe2915 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -815,7 +815,7 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
if (!sctp_transport_hold(t))
return err;
- if (!net_eq(sock_net(t->asoc->base.sk), x->net))
+ if (!net_eq(t->asoc->base.net, x->net))
goto out;
if (x->lport != htons(t->asoc->base.bind_addr.port))
goto out;
@@ -830,7 +830,7 @@ static inline __u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
{
const struct sctp_transport *t = data;
const union sctp_addr *paddr = &t->ipaddr;
- const struct net *net = sock_net(t->asoc->base.sk);
+ const struct net *net = t->asoc->base.net;
__be16 lport = htons(t->asoc->base.bind_addr.port);
__u32 addr;
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 0860122..025f48e 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -399,25 +399,6 @@ enum sctp_xmit sctp_packet_append_chunk(struct sctp_packet *packet,
return retval;
}
-static void sctp_packet_release_owner(struct sk_buff *skb)
-{
- sk_free(skb->sk);
-}
-
-static void sctp_packet_set_owner_w(struct sk_buff *skb, struct sock *sk)
-{
- skb_orphan(skb);
- skb->sk = sk;
- skb->destructor = sctp_packet_release_owner;
-
- /*
- * The data chunks have already been accounted for in sctp_sendmsg(),
- * therefore only reserve a single byte to keep socket around until
- * the packet has been transmitted.
- */
- refcount_inc(&sk->sk_wmem_alloc);
-}
-
static void sctp_packet_gso_append(struct sk_buff *head, struct sk_buff *skb)
{
if (SCTP_OUTPUT_CB(head)->last == head)
@@ -429,6 +410,7 @@ static void sctp_packet_gso_append(struct sk_buff *head, struct sk_buff *skb)
head->truesize += skb->truesize;
head->data_len += skb->len;
head->len += skb->len;
+ refcount_add(skb->truesize, &head->sk->sk_wmem_alloc);
__skb_header_release(skb);
}
@@ -604,7 +586,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
if (!head)
goto out;
skb_reserve(head, packet->overhead + MAX_HEADER);
- sctp_packet_set_owner_w(head, sk);
+ skb_set_owner_w(head, sk);
/* set sctp header */
sh = skb_push(head, sizeof(struct sctphdr));
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 6d36f74..269b528 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -242,6 +242,7 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
sa->sin_port = sh->dest;
sa->sin_addr.s_addr = ip_hdr(skb)->daddr;
}
+ memset(sa->sin_zero, 0, sizeof(sa->sin_zero));
}
/* Initialize an sctp_addr from a socket. */
@@ -250,6 +251,7 @@ static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk)
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = 0;
addr->v4.sin_addr.s_addr = inet_sk(sk)->inet_rcv_saddr;
+ memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero));
}
/* Initialize sk->sk_rcv_saddr from sctp_addr. */
@@ -272,6 +274,7 @@ static void sctp_v4_from_addr_param(union sctp_addr *addr,
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
+ memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero));
}
/* Initialize an address parameter from a sctp_addr and return the length
@@ -296,6 +299,7 @@ static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct flowi4 *fl4,
saddr->v4.sin_family = AF_INET;
saddr->v4.sin_port = port;
saddr->v4.sin_addr.s_addr = fl4->saddr;
+ memset(saddr->v4.sin_zero, 0, sizeof(saddr->v4.sin_zero));
}
/* Compare two addresses exactly. */
@@ -318,6 +322,7 @@ static void sctp_v4_inaddr_any(union sctp_addr *addr, __be16 port)
addr->v4.sin_family = AF_INET;
addr->v4.sin_addr.s_addr = htonl(INADDR_ANY);
addr->v4.sin_port = port;
+ memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero));
}
/* Is this a wildcard address? */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index de8a82b..0234a64 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1373,8 +1373,10 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
/* Generate an INIT ACK chunk. */
new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC,
0);
- if (!new_obj)
- goto nomem;
+ if (!new_obj) {
+ error = -ENOMEM;
+ break;
+ }
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
@@ -1396,7 +1398,8 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
if (!new_obj) {
if (cmd->obj.chunk)
sctp_chunk_free(cmd->obj.chunk);
- goto nomem;
+ error = -ENOMEM;
+ break;
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
@@ -1443,8 +1446,10 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
/* Generate a SHUTDOWN chunk. */
new_obj = sctp_make_shutdown(asoc, chunk);
- if (!new_obj)
- goto nomem;
+ if (!new_obj) {
+ error = -ENOMEM;
+ break;
+ }
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
break;
@@ -1780,11 +1785,17 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
break;
}
- if (error)
+ if (error) {
+ cmd = sctp_next_cmd(commands);
+ while (cmd) {
+ if (cmd->verb == SCTP_CMD_REPLY)
+ sctp_chunk_free(cmd->obj.chunk);
+ cmd = sctp_next_cmd(commands);
+ }
break;
+ }
}
-out:
/* If this is in response to a received chunk, wait until
* we are done with the packet to open the queue so that we don't
* send multiple packets in response to a single request.
@@ -1799,7 +1810,4 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
sp->data_ready_signalled = 0;
return error;
-nomem:
- error = -ENOMEM;
- goto out;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 713a669..559f09a 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2175,8 +2175,10 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook(
/* Update socket peer label if first association. */
if (security_sctp_assoc_request((struct sctp_endpoint *)ep,
- chunk->skb))
+ chunk->skb)) {
+ sctp_association_free(new_asoc);
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+ }
/* Set temp so that it won't be added into hashtable */
new_asoc->temp = 1;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 227b050..95f9068 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -83,7 +83,7 @@
#include <net/sctp/stream_sched.h>
/* Forward declarations for internal helper functions. */
-static int sctp_writeable(struct sock *sk);
+static bool sctp_writeable(struct sock *sk);
static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
size_t msg_len);
@@ -119,25 +119,10 @@ static void sctp_enter_memory_pressure(struct sock *sk)
/* Get the sndbuf space available at the time on the association. */
static inline int sctp_wspace(struct sctp_association *asoc)
{
- int amt;
+ struct sock *sk = asoc->base.sk;
- if (asoc->ep->sndbuf_policy)
- amt = asoc->sndbuf_used;
- else
- amt = sk_wmem_alloc_get(asoc->base.sk);
-
- if (amt >= asoc->base.sk->sk_sndbuf) {
- if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK)
- amt = 0;
- else {
- amt = sk_stream_wspace(asoc->base.sk);
- if (amt < 0)
- amt = 0;
- }
- } else {
- amt = asoc->base.sk->sk_sndbuf - amt;
- }
- return amt;
+ return asoc->ep->sndbuf_policy ? sk->sk_sndbuf - asoc->sndbuf_used
+ : sk_stream_wspace(sk);
}
/* Increment the used sndbuf space count of the corresponding association by
@@ -1928,10 +1913,10 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
asoc->pmtu_pending = 0;
}
- if (sctp_wspace(asoc) < msg_len)
+ if (sctp_wspace(asoc) < (int)msg_len)
sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
- if (!sctp_wspace(asoc)) {
+ if (sctp_wspace(asoc) <= 0) {
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
if (err)
@@ -3343,8 +3328,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
__u16 datasize = asoc ? sctp_datachk_len(&asoc->stream) :
sizeof(struct sctp_data_chunk);
- min_len = sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT,
- datasize);
+ min_len = sctp_min_frag_point(sp, datasize);
max_len = SCTP_MAX_CHUNK_LEN - datasize;
if (val < min_len || val > max_len)
@@ -7939,7 +7923,7 @@ __poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
mask = 0;
/* Is there any exceptional events? */
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
if (sk->sk_shutdown & RCV_SHUTDOWN)
@@ -7948,7 +7932,7 @@ __poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
mask |= EPOLLHUP;
/* Is it readable? Reconsider this code with TCP-style support. */
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* The association is either gone or not ready. */
@@ -8334,7 +8318,7 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
if (sk_can_busy_loop(sk)) {
sk_busy_loop(sk, noblock);
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
continue;
}
@@ -8516,7 +8500,7 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
goto do_error;
if (signal_pending(current))
goto do_interrupted;
- if (msg_len <= sctp_wspace(asoc))
+ if ((int)msg_len <= sctp_wspace(asoc))
break;
/* Let another process have a go. Since we are going
@@ -8591,14 +8575,9 @@ void sctp_write_space(struct sock *sk)
* UDP-style sockets or TCP-style sockets, this code should work.
* - Daisy
*/
-static int sctp_writeable(struct sock *sk)
+static bool sctp_writeable(struct sock *sk)
{
- int amt = 0;
-
- amt = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
- if (amt < 0)
- amt = 0;
- return amt;
+ return sk->sk_sndbuf > sk->sk_wmem_queued;
}
/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
@@ -8777,7 +8756,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
newinet->inet_rcv_saddr = inet->inet_rcv_saddr;
newinet->inet_dport = htons(asoc->peer.port);
newinet->pmtudisc = inet->pmtudisc;
- newinet->inet_id = asoc->next_tsn ^ jiffies;
+ newinet->inet_id = prandom_u32();
newinet->uc_ttl = inet->uc_ttl;
newinet->mc_loop = 1;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 033696e..c0d55ed 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -207,7 +207,8 @@ void sctp_transport_reset_hb_timer(struct sctp_transport *transport)
/* When a data chunk is sent, reset the heartbeat interval. */
expires = jiffies + sctp_transport_timeout(transport);
- if (time_before(transport->hb_timer.expires, expires) &&
+ if ((time_before(transport->hb_timer.expires, expires) ||
+ !timer_pending(&transport->hb_timer)) &&
!mod_timer(&transport->hb_timer,
expires + prandom_u32_max(transport->rto)))
sctp_transport_hold(transport);
@@ -277,7 +278,7 @@ bool sctp_transport_update_pmtu(struct sctp_transport *t, u32 pmtu)
pf->af->from_sk(&addr, sk);
pf->to_sk_daddr(&t->ipaddr, sk);
- dst->ops->update_pmtu(dst, sk, NULL, pmtu);
+ dst->ops->update_pmtu(dst, sk, NULL, pmtu, true);
pf->to_sk_daddr(&addr, sk);
dst = sctp_transport_dst_check(t);
diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c
index ed5dcf0..333e435 100644
--- a/net/smc/smc_cdc.c
+++ b/net/smc/smc_cdc.c
@@ -96,6 +96,7 @@ int smc_cdc_msg_send(struct smc_connection *conn,
struct smc_wr_buf *wr_buf,
struct smc_cdc_tx_pend *pend)
{
+ union smc_host_cursor cfed;
struct smc_link *link;
int rc;
@@ -105,12 +106,10 @@ int smc_cdc_msg_send(struct smc_connection *conn,
conn->tx_cdc_seq++;
conn->local_tx_ctrl.seqno = conn->tx_cdc_seq;
- smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf,
- &conn->local_tx_ctrl, conn);
+ smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf, conn, &cfed);
rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend);
if (!rc)
- smc_curs_copy(&conn->rx_curs_confirmed,
- &conn->local_tx_ctrl.cons, conn);
+ smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn);
return rc;
}
diff --git a/net/smc/smc_cdc.h b/net/smc/smc_cdc.h
index 934df44..34d2e14 100644
--- a/net/smc/smc_cdc.h
+++ b/net/smc/smc_cdc.h
@@ -135,7 +135,9 @@ static inline void smc_curs_copy_net(union smc_cdc_cursor *tgt,
#endif
}
-/* calculate cursor difference between old and new, where old <= new */
+/* calculate cursor difference between old and new, where old <= new and
+ * difference cannot exceed size
+ */
static inline int smc_curs_diff(unsigned int size,
union smc_host_cursor *old,
union smc_host_cursor *new)
@@ -160,28 +162,51 @@ static inline int smc_curs_comp(unsigned int size,
return smc_curs_diff(size, old, new);
}
+/* calculate cursor difference between old and new, where old <= new and
+ * difference may exceed size
+ */
+static inline int smc_curs_diff_large(unsigned int size,
+ union smc_host_cursor *old,
+ union smc_host_cursor *new)
+{
+ if (old->wrap < new->wrap)
+ return min_t(int,
+ (size - old->count) + new->count +
+ (new->wrap - old->wrap - 1) * size,
+ size);
+
+ if (old->wrap > new->wrap) /* wrap has switched from 0xffff to 0x0000 */
+ return min_t(int,
+ (size - old->count) + new->count +
+ (new->wrap + 0xffff - old->wrap) * size,
+ size);
+
+ return max_t(int, 0, (new->count - old->count));
+}
+
static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer,
union smc_host_cursor *local,
+ union smc_host_cursor *save,
struct smc_connection *conn)
{
- union smc_host_cursor temp;
-
- smc_curs_copy(&temp, local, conn);
- peer->count = htonl(temp.count);
- peer->wrap = htons(temp.wrap);
+ smc_curs_copy(save, local, conn);
+ peer->count = htonl(save->count);
+ peer->wrap = htons(save->wrap);
/* peer->reserved = htons(0); must be ensured by caller */
}
static inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer,
- struct smc_host_cdc_msg *local,
- struct smc_connection *conn)
+ struct smc_connection *conn,
+ union smc_host_cursor *save)
{
+ struct smc_host_cdc_msg *local = &conn->local_tx_ctrl;
+
peer->common.type = local->common.type;
peer->len = local->len;
peer->seqno = htons(local->seqno);
peer->token = htonl(local->token);
- smc_host_cursor_to_cdc(&peer->prod, &local->prod, conn);
- smc_host_cursor_to_cdc(&peer->cons, &local->cons, conn);
+ smc_host_cursor_to_cdc(&peer->prod, &local->prod, save, conn);
+ smc_host_cursor_to_cdc(&peer->cons, &local->cons, save, conn);
peer->prod_flags = local->prod_flags;
peer->conn_state_flags = local->conn_state_flags;
}
diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
index 18daebc..2c9baf8 100644
--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -128,6 +128,8 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
{
struct smc_link_group *lgr = conn->lgr;
+ if (!lgr)
+ return;
write_lock_bh(&lgr->conns_lock);
if (conn->alert_token_local) {
__smc_lgr_unregister_conn(conn);
@@ -612,6 +614,8 @@ int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
local_contact = SMC_REUSE_CONTACT;
conn->lgr = lgr;
smc_lgr_register_conn(conn); /* add smc conn to lgr */
+ if (delayed_work_pending(&lgr->free_work))
+ cancel_delayed_work(&lgr->free_work);
write_unlock_bh(&lgr->conns_lock);
break;
}
diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c
index 28361ae..62885a2 100644
--- a/net/smc/smc_tx.c
+++ b/net/smc/smc_tx.c
@@ -163,12 +163,11 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
conn->local_tx_ctrl.prod_flags.urg_data_pending = 1;
if (!atomic_read(&conn->sndbuf_space) || conn->urg_tx_pend) {
+ if (send_done)
+ return send_done;
rc = smc_tx_wait(smc, msg->msg_flags);
- if (rc) {
- if (send_done)
- return send_done;
+ if (rc)
goto out_err;
- }
continue;
}
@@ -487,25 +486,23 @@ static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
struct smc_wr_buf *wr_buf;
int rc;
- spin_lock_bh(&conn->send_lock);
rc = smc_cdc_get_free_slot(conn, &wr_buf, &pend);
if (rc < 0) {
if (rc == -EBUSY) {
struct smc_sock *smc =
container_of(conn, struct smc_sock, conn);
- if (smc->sk.sk_err == ECONNABORTED) {
- rc = sock_error(&smc->sk);
- goto out_unlock;
- }
+ if (smc->sk.sk_err == ECONNABORTED)
+ return sock_error(&smc->sk);
rc = 0;
if (conn->alert_token_local) /* connection healthy */
mod_delayed_work(system_wq, &conn->tx_work,
SMC_TX_WORK_DELAY);
}
- goto out_unlock;
+ return rc;
}
+ spin_lock_bh(&conn->send_lock);
if (!conn->local_tx_ctrl.prod_flags.urg_data_present) {
rc = smc_tx_rdma_writes(conn);
if (rc) {
@@ -596,7 +593,8 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
if (to_confirm > conn->rmbe_update_limit) {
smc_curs_copy(&prod, &conn->local_rx_ctrl.prod, conn);
sender_free = conn->rmb_desc->len -
- smc_curs_diff(conn->rmb_desc->len, &prod, &cfed);
+ smc_curs_diff_large(conn->rmb_desc->len,
+ &cfed, &prod);
}
if (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c
index 3c458d2..c269475 100644
--- a/net/smc/smc_wr.c
+++ b/net/smc/smc_wr.c
@@ -215,12 +215,14 @@ int smc_wr_tx_put_slot(struct smc_link *link,
pend = container_of(wr_pend_priv, struct smc_wr_tx_pend, priv);
if (pend->idx < link->wr_tx_cnt) {
+ u32 idx = pend->idx;
+
/* clear the full struct smc_wr_tx_pend including .priv */
memset(&link->wr_tx_pends[pend->idx], 0,
sizeof(link->wr_tx_pends[pend->idx]));
memset(&link->wr_tx_bufs[pend->idx], 0,
sizeof(link->wr_tx_bufs[pend->idx]));
- test_and_clear_bit(pend->idx, link->wr_tx_mask);
+ test_and_clear_bit(idx, link->wr_tx_mask);
return 1;
}
diff --git a/net/socket.c b/net/socket.c
index 7a0ddf8..4a4c11d 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -877,7 +877,7 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
.msg_iocb = iocb};
ssize_t res;
- if (file->f_flags & O_NONBLOCK)
+ if (file->f_flags & O_NONBLOCK || (iocb->ki_flags & IOCB_NOWAIT))
msg.msg_flags = MSG_DONTWAIT;
if (iocb->ki_pos != 0)
@@ -902,7 +902,7 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (iocb->ki_pos != 0)
return -ESPIPE;
- if (file->f_flags & O_NONBLOCK)
+ if (file->f_flags & O_NONBLOCK || (iocb->ki_flags & IOCB_NOWAIT))
msg.msg_flags = MSG_DONTWAIT;
if (sock->type == SOCK_SEQPACKET)
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index eaad9bc..e1f0571 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -63,6 +63,7 @@
#include <linux/sunrpc/gss_krb5.h>
#include <linux/random.h>
#include <linux/crypto.h>
+#include <linux/atomic.h>
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 214440c..3a28e15 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -54,9 +54,6 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail)
h->last_refresh = now;
}
-static inline int cache_is_valid(struct cache_head *h);
-static void cache_fresh_locked(struct cache_head *head, time_t expiry,
- struct cache_detail *detail);
static void cache_fresh_unlocked(struct cache_head *head,
struct cache_detail *detail);
@@ -101,9 +98,6 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
if (cache_is_expired(detail, tmp)) {
hlist_del_init(&tmp->cache_list);
detail->entries --;
- if (cache_is_valid(tmp) == -EAGAIN)
- set_bit(CACHE_NEGATIVE, &tmp->flags);
- cache_fresh_locked(tmp, 0, detail);
freeme = tmp;
break;
}
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 3fe5d60..e280858 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -99,37 +99,64 @@ __rpc_add_timer(struct rpc_wait_queue *queue, struct rpc_task *task)
list_add(&task->u.tk_wait.timer_list, &queue->timer_list.list);
}
-static void rpc_rotate_queue_owner(struct rpc_wait_queue *queue)
-{
- struct list_head *q = &queue->tasks[queue->priority];
- struct rpc_task *task;
-
- if (!list_empty(q)) {
- task = list_first_entry(q, struct rpc_task, u.tk_wait.list);
- if (task->tk_owner == queue->owner)
- list_move_tail(&task->u.tk_wait.list, q);
- }
-}
-
static void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority)
{
if (queue->priority != priority) {
- /* Fairness: rotate the list when changing priority */
- rpc_rotate_queue_owner(queue);
queue->priority = priority;
+ queue->nr = 1U << priority;
}
}
-static void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid)
-{
- queue->owner = pid;
- queue->nr = RPC_BATCH_COUNT;
-}
-
static void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue)
{
rpc_set_waitqueue_priority(queue, queue->maxpriority);
- rpc_set_waitqueue_owner(queue, 0);
+}
+
+/*
+ * Add a request to a queue list
+ */
+static void
+__rpc_list_enqueue_task(struct list_head *q, struct rpc_task *task)
+{
+ struct rpc_task *t;
+
+ list_for_each_entry(t, q, u.tk_wait.list) {
+ if (t->tk_owner == task->tk_owner) {
+ list_add_tail(&task->u.tk_wait.links,
+ &t->u.tk_wait.links);
+ /* Cache the queue head in task->u.tk_wait.list */
+ task->u.tk_wait.list.next = q;
+ task->u.tk_wait.list.prev = NULL;
+ return;
+ }
+ }
+ INIT_LIST_HEAD(&task->u.tk_wait.links);
+ list_add_tail(&task->u.tk_wait.list, q);
+}
+
+/*
+ * Remove request from a queue list
+ */
+static void
+__rpc_list_dequeue_task(struct rpc_task *task)
+{
+ struct list_head *q;
+ struct rpc_task *t;
+
+ if (task->u.tk_wait.list.prev == NULL) {
+ list_del(&task->u.tk_wait.links);
+ return;
+ }
+ if (!list_empty(&task->u.tk_wait.links)) {
+ t = list_first_entry(&task->u.tk_wait.links,
+ struct rpc_task,
+ u.tk_wait.links);
+ /* Assume __rpc_list_enqueue_task() cached the queue head */
+ q = t->u.tk_wait.list.next;
+ list_add_tail(&t->u.tk_wait.list, q);
+ list_del(&task->u.tk_wait.links);
+ }
+ list_del(&task->u.tk_wait.list);
}
/*
@@ -139,22 +166,9 @@ static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue,
struct rpc_task *task,
unsigned char queue_priority)
{
- struct list_head *q;
- struct rpc_task *t;
-
- INIT_LIST_HEAD(&task->u.tk_wait.links);
if (unlikely(queue_priority > queue->maxpriority))
queue_priority = queue->maxpriority;
- if (queue_priority > queue->priority)
- rpc_set_waitqueue_priority(queue, queue_priority);
- q = &queue->tasks[queue_priority];
- list_for_each_entry(t, q, u.tk_wait.list) {
- if (t->tk_owner == task->tk_owner) {
- list_add_tail(&task->u.tk_wait.list, &t->u.tk_wait.links);
- return;
- }
- }
- list_add_tail(&task->u.tk_wait.list, q);
+ __rpc_list_enqueue_task(&queue->tasks[queue_priority], task);
}
/*
@@ -194,13 +208,7 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue,
*/
static void __rpc_remove_wait_queue_priority(struct rpc_task *task)
{
- struct rpc_task *t;
-
- if (!list_empty(&task->u.tk_wait.links)) {
- t = list_entry(task->u.tk_wait.links.next, struct rpc_task, u.tk_wait.list);
- list_move(&t->u.tk_wait.list, &task->u.tk_wait.list);
- list_splice_init(&task->u.tk_wait.links, &t->u.tk_wait.links);
- }
+ __rpc_list_dequeue_task(task);
}
/*
@@ -212,7 +220,8 @@ static void __rpc_remove_wait_queue(struct rpc_wait_queue *queue, struct rpc_tas
__rpc_disable_timer(queue, task);
if (RPC_IS_PRIORITY(queue))
__rpc_remove_wait_queue_priority(task);
- list_del(&task->u.tk_wait.list);
+ else
+ list_del(&task->u.tk_wait.list);
queue->qlen--;
dprintk("RPC: %5u removed from queue %p \"%s\"\n",
task->tk_pid, queue, rpc_qname(queue));
@@ -493,17 +502,9 @@ static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *q
* Service a batch of tasks from a single owner.
*/
q = &queue->tasks[queue->priority];
- if (!list_empty(q)) {
- task = list_entry(q->next, struct rpc_task, u.tk_wait.list);
- if (queue->owner == task->tk_owner) {
- if (--queue->nr)
- goto out;
- list_move_tail(&task->u.tk_wait.list, q);
- }
- /*
- * Check if we need to switch queues.
- */
- goto new_owner;
+ if (!list_empty(q) && --queue->nr) {
+ task = list_first_entry(q, struct rpc_task, u.tk_wait.list);
+ goto out;
}
/*
@@ -515,7 +516,7 @@ static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *q
else
q = q - 1;
if (!list_empty(q)) {
- task = list_entry(q->next, struct rpc_task, u.tk_wait.list);
+ task = list_first_entry(q, struct rpc_task, u.tk_wait.list);
goto new_queue;
}
} while (q != &queue->tasks[queue->priority]);
@@ -525,8 +526,6 @@ static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *q
new_queue:
rpc_set_waitqueue_priority(queue, (unsigned int)(q - &queue->tasks[0]));
-new_owner:
- rpc_set_waitqueue_owner(queue, task->tk_owner);
out:
return task;
}
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 3581168..5e7c13a 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -796,17 +796,11 @@ void xprt_connect(struct rpc_task *task)
static void xprt_connect_status(struct rpc_task *task)
{
- struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
-
- if (task->tk_status == 0) {
- xprt->stat.connect_count++;
- xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start;
+ switch (task->tk_status) {
+ case 0:
dprintk("RPC: %5u xprt_connect_status: connection established\n",
task->tk_pid);
- return;
- }
-
- switch (task->tk_status) {
+ break;
case -ECONNREFUSED:
case -ECONNRESET:
case -ECONNABORTED:
@@ -823,7 +817,7 @@ static void xprt_connect_status(struct rpc_task *task)
default:
dprintk("RPC: %5u xprt_connect_status: error %d connecting to "
"server %s\n", task->tk_pid, -task->tk_status,
- xprt->servername);
+ task->tk_rqstp->rq_xprt->servername);
task->tk_status = -EIO;
}
}
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index c8ae983..f2eaf26 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -1360,6 +1360,10 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
spin_unlock(&xprt->recv_lock);
req = rpcr_to_rdmar(rqst);
+ if (req->rl_reply) {
+ trace_xprtrdma_leaked_rep(rqst, req->rl_reply);
+ rpcrdma_recv_buffer_put(req->rl_reply);
+ }
req->rl_reply = rep;
rep->rr_rqst = rqst;
clear_bit(RPCRDMA_REQ_F_PENDING, &req->rl_flags);
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 98cbc7b0..f56f36b 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -242,8 +242,12 @@ rpcrdma_connect_worker(struct work_struct *work)
spin_lock_bh(&xprt->transport_lock);
if (ep->rep_connected > 0) {
- if (!xprt_test_and_set_connected(xprt))
+ if (!xprt_test_and_set_connected(xprt)) {
+ xprt->stat.connect_count++;
+ xprt->stat.connect_time += (long)jiffies -
+ xprt->stat.connect_start;
xprt_wake_pending_tasks(xprt, 0);
+ }
} else {
if (xprt_test_and_clear_connected(xprt))
xprt_wake_pending_tasks(xprt, -ENOTCONN);
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 7d8cce1..9dc059d 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -129,7 +129,7 @@ static struct ctl_table xs_tunables_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &xprt_min_resvport_limit,
- .extra2 = &xprt_max_resvport
+ .extra2 = &xprt_max_resvport_limit
},
{
.procname = "max_resvport",
@@ -137,7 +137,7 @@ static struct ctl_table xs_tunables_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &xprt_min_resvport,
+ .extra1 = &xprt_min_resvport_limit,
.extra2 = &xprt_max_resvport_limit
},
{
@@ -1611,6 +1611,9 @@ static void xs_tcp_state_change(struct sock *sk)
clear_bit(XPRT_SOCK_CONNECTING, &transport->sock_state);
xprt_clear_connecting(xprt);
+ xprt->stat.connect_count++;
+ xprt->stat.connect_time += (long)jiffies -
+ xprt->stat.connect_start;
xprt_wake_pending_tasks(xprt, -EAGAIN);
}
spin_unlock(&xprt->transport_lock);
@@ -1773,11 +1776,17 @@ static void xs_udp_timer(struct rpc_xprt *xprt, struct rpc_task *task)
spin_unlock_bh(&xprt->transport_lock);
}
-static unsigned short xs_get_random_port(void)
+static int xs_get_random_port(void)
{
- unsigned short range = xprt_max_resvport - xprt_min_resvport + 1;
- unsigned short rand = (unsigned short) prandom_u32() % range;
- return rand + xprt_min_resvport;
+ unsigned short min = xprt_min_resvport, max = xprt_max_resvport;
+ unsigned short range;
+ unsigned short rand;
+
+ if (max < min)
+ return -EADDRINUSE;
+ range = max - min + 1;
+ rand = (unsigned short) prandom_u32() % range;
+ return rand + min;
}
/**
@@ -1833,9 +1842,9 @@ static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock)
transport->srcport = xs_sock_getport(sock);
}
-static unsigned short xs_get_srcport(struct sock_xprt *transport)
+static int xs_get_srcport(struct sock_xprt *transport)
{
- unsigned short port = transport->srcport;
+ int port = transport->srcport;
if (port == 0 && transport->xprt.resvport)
port = xs_get_random_port();
@@ -1856,7 +1865,7 @@ static int xs_bind(struct sock_xprt *transport, struct socket *sock)
{
struct sockaddr_storage myaddr;
int err, nloop = 0;
- unsigned short port = xs_get_srcport(transport);
+ int port = xs_get_srcport(transport);
unsigned short last;
/*
@@ -1874,8 +1883,8 @@ static int xs_bind(struct sock_xprt *transport, struct socket *sock)
* transport->xprt.resvport == 1) xs_get_srcport above will
* ensure that port is non-zero and we will bind as needed.
*/
- if (port == 0)
- return 0;
+ if (port <= 0)
+ return port;
memcpy(&myaddr, &transport->srcaddr, transport->xprt.addrlen);
do {
@@ -2029,8 +2038,6 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt,
}
/* Tell the socket layer to start connecting... */
- xprt->stat.connect_count++;
- xprt->stat.connect_start = jiffies;
return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, 0);
}
@@ -2062,6 +2069,9 @@ static int xs_local_setup_socket(struct sock_xprt *transport)
case 0:
dprintk("RPC: xprt %p connected to %s\n",
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
+ xprt->stat.connect_count++;
+ xprt->stat.connect_time += (long)jiffies -
+ xprt->stat.connect_start;
xprt_set_connected(xprt);
case -ENOBUFS:
break;
@@ -2387,8 +2397,6 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
xs_set_memalloc(xprt);
/* Tell the socket layer to start connecting... */
- xprt->stat.connect_count++;
- xprt->stat.connect_start = jiffies;
set_bit(XPRT_SOCK_CONNECTING, &transport->sock_state);
ret = kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK);
switch (ret) {
@@ -3317,12 +3325,8 @@ static int param_set_uint_minmax(const char *val,
static int param_set_portnr(const char *val, const struct kernel_param *kp)
{
- if (kp->arg == &xprt_min_resvport)
- return param_set_uint_minmax(val, kp,
- RPC_MIN_RESVPORT,
- xprt_max_resvport);
return param_set_uint_minmax(val, kp,
- xprt_min_resvport,
+ RPC_MIN_RESVPORT,
RPC_MAX_RESVPORT);
}
diff --git a/net/tipc/core.c b/net/tipc/core.c
index eb0f701..49bb9fc 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -120,14 +120,6 @@ static int __init tipc_init(void)
sysctl_tipc_rmem[1] = RCVBUF_DEF;
sysctl_tipc_rmem[2] = RCVBUF_MAX;
- err = tipc_netlink_start();
- if (err)
- goto out_netlink;
-
- err = tipc_netlink_compat_start();
- if (err)
- goto out_netlink_compat;
-
err = tipc_register_sysctl();
if (err)
goto out_sysctl;
@@ -148,8 +140,21 @@ static int __init tipc_init(void)
if (err)
goto out_bearer;
+ err = tipc_netlink_start();
+ if (err)
+ goto out_netlink;
+
+ err = tipc_netlink_compat_start();
+ if (err)
+ goto out_netlink_compat;
+
pr_info("Started in single node mode\n");
return 0;
+
+out_netlink_compat:
+ tipc_netlink_stop();
+out_netlink:
+ tipc_bearer_cleanup();
out_bearer:
unregister_pernet_device(&tipc_topsrv_net_ops);
out_pernet_topsrv:
@@ -159,22 +164,18 @@ static int __init tipc_init(void)
out_pernet:
tipc_unregister_sysctl();
out_sysctl:
- tipc_netlink_compat_stop();
-out_netlink_compat:
- tipc_netlink_stop();
-out_netlink:
pr_err("Unable to start in single node mode\n");
return err;
}
static void __exit tipc_exit(void)
{
+ tipc_netlink_compat_stop();
+ tipc_netlink_stop();
tipc_bearer_cleanup();
unregister_pernet_device(&tipc_topsrv_net_ops);
tipc_socket_stop();
unregister_pernet_device(&tipc_net_ops);
- tipc_netlink_stop();
- tipc_netlink_compat_stop();
tipc_unregister_sysctl();
pr_info("Deactivated\n");
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 6344aca..0fbf8ea 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1114,7 +1114,7 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
default:
pr_warn("Dropping received illegal msg type\n");
kfree_skb(skb);
- return false;
+ return true;
};
}
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 318c541..29e6840 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -548,7 +548,7 @@ static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg,
if (len <= 0)
return -EINVAL;
- len = min_t(int, len, TIPC_MAX_BEARER_NAME);
+ len = min_t(int, len, TIPC_MAX_LINK_NAME);
if (!string_is_valid(name, len))
return -EINVAL;
@@ -830,7 +830,7 @@ static int tipc_nl_compat_link_reset_stats(struct tipc_nl_compat_cmd_doit *cmd,
if (len <= 0)
return -EINVAL;
- len = min_t(int, len, TIPC_MAX_BEARER_NAME);
+ len = min_t(int, len, TIPC_MAX_LINK_NAME);
if (!string_is_valid(name, len))
return -EINVAL;
@@ -983,6 +983,10 @@ static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock)
hdr = genlmsg_put(args, 0, 0, &tipc_genl_family, NLM_F_MULTI,
TIPC_NL_PUBL_GET);
+ if (!hdr) {
+ kfree_skb(args);
+ return -EMSGSIZE;
+ }
nest = nla_nest_start(args, TIPC_NLA_SOCK);
if (!nest) {
@@ -1030,8 +1034,11 @@ static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg,
u32 node;
struct nlattr *con[TIPC_NLA_CON_MAX + 1];
- nla_parse_nested(con, TIPC_NLA_CON_MAX,
- sock[TIPC_NLA_SOCK_CON], NULL, NULL);
+ err = nla_parse_nested(con, TIPC_NLA_CON_MAX,
+ sock[TIPC_NLA_SOCK_CON], NULL, NULL);
+
+ if (err)
+ return err;
node = nla_get_u32(con[TIPC_NLA_CON_NODE]);
tipc_tlv_sprintf(msg->rep, " connected to <%u.%u.%u:%u>",
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 6c91f12..5841d62 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -731,7 +731,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock,
/* fall thru' */
case TIPC_LISTEN:
case TIPC_CONNECTING:
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
revents |= EPOLLIN | EPOLLRDNORM;
break;
case TIPC_OPEN:
@@ -739,7 +739,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock,
revents |= EPOLLOUT;
if (!tipc_sk_type_connectionless(sk))
break;
- if (skb_queue_empty(&sk->sk_receive_queue))
+ if (skb_queue_empty_lockless(&sk->sk_receive_queue))
break;
revents |= EPOLLIN | EPOLLRDNORM;
break;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index f601933..d2d6ff0 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -225,6 +225,8 @@ static inline void unix_release_addr(struct unix_address *addr)
static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned int *hashp)
{
+ *hashp = 0;
+
if (len <= sizeof(short) || len > sizeof(*sunaddr))
return -EINVAL;
if (!sunaddr || sunaddr->sun_family != AF_UNIX)
@@ -2661,7 +2663,7 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa
mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
/* readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* Connection-based need to check for termination and startup */
@@ -2690,7 +2692,7 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock,
mask = 0;
/* exceptional events? */
- if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
@@ -2700,7 +2702,7 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock,
mask |= EPOLLHUP;
/* readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* Connection-based need to check for termination and startup */
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 2a4613b..62ec918 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -107,6 +107,7 @@
#include <linux/mutex.h>
#include <linux/net.h>
#include <linux/poll.h>
+#include <linux/random.h>
#include <linux/skbuff.h>
#include <linux/smp.h>
#include <linux/socket.h>
@@ -480,9 +481,13 @@ static void vsock_pending_work(struct work_struct *work)
static int __vsock_bind_stream(struct vsock_sock *vsk,
struct sockaddr_vm *addr)
{
- static u32 port = LAST_RESERVED_PORT + 1;
+ static u32 port = 0;
struct sockaddr_vm new_addr;
+ if (!port)
+ port = LAST_RESERVED_PORT + 1 +
+ prandom_u32_max(U32_MAX - LAST_RESERVED_PORT);
+
vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port);
if (addr->svm_port == VMADDR_PORT_ANY) {
@@ -873,7 +878,7 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
* the queue and write as long as the socket isn't shutdown for
* sending.
*/
- if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ if (!skb_queue_empty_lockless(&sk->sk_receive_queue) ||
(sk->sk_shutdown & RCV_SHUTDOWN)) {
mask |= EPOLLIN | EPOLLRDNORM;
}
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 3c199f7..52242a1 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -92,8 +92,17 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque)
struct virtio_vsock_pkt *pkt = opaque;
struct af_vsockmon_hdr *hdr;
struct sk_buff *skb;
+ size_t payload_len;
+ void *payload_buf;
- skb = alloc_skb(sizeof(*hdr) + sizeof(pkt->hdr) + pkt->len,
+ /* A packet could be split to fit the RX buffer, so we can retrieve
+ * the payload length from the header and the buffer pointer taking
+ * care of the offset in the original packet.
+ */
+ payload_len = le32_to_cpu(pkt->hdr.len);
+ payload_buf = pkt->buf + pkt->off;
+
+ skb = alloc_skb(sizeof(*hdr) + sizeof(pkt->hdr) + payload_len,
GFP_ATOMIC);
if (!skb)
return NULL;
@@ -133,8 +142,8 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque)
skb_put_data(skb, &pkt->hdr, sizeof(pkt->hdr));
- if (pkt->len) {
- skb_put_data(skb, pkt->buf, pkt->len);
+ if (payload_len) {
+ skb_put_data(skb, payload_buf, payload_len);
}
return skb;
@@ -871,9 +880,11 @@ virtio_transport_recv_connected(struct sock *sk,
if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
vsk->peer_shutdown |= SEND_SHUTDOWN;
if (vsk->peer_shutdown == SHUTDOWN_MASK &&
- vsock_stream_has_data(vsk) <= 0) {
- sock_set_flag(sk, SOCK_DONE);
- sk->sk_state = TCP_CLOSING;
+ vsock_stream_has_data(vsk) <= 0 &&
+ !sock_flag(sk, SOCK_DONE)) {
+ (void)virtio_transport_reset(vsk, NULL);
+
+ virtio_transport_do_close(vsk, true);
}
if (le32_to_cpu(pkt->hdr.flags))
sk->sk_state_change(sk);
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index fab1c17..af75941 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -45,6 +45,7 @@
@(set -e; \
allf=""; \
for f in $^ ; do \
+ test -f $$f || continue;\
# similar to hexdump -v -e '1/1 "0x%.2x," "\n"' \
thisf=$$(od -An -v -tx1 < $$f | \
sed -e 's/ /\n/g' | \
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 882d97b..550ac9d 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -41,6 +41,8 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
cfg80211_sched_dfs_chan_update(rdev);
}
+ schedule_work(&cfg80211_disconnect_work);
+
return err;
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 4dda927..5dd906b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -442,6 +442,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev);
bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz, u32 bw_khz);
+extern struct work_struct cfg80211_disconnect_work;
+
/**
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
* @wiphy: the wiphy to validate against
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e7e7ffc..05b1a9f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -286,7 +286,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_MESH_ID_LEN },
- [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
+ [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_BINARY,
+ .len = ETH_ALEN },
[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
@@ -1870,6 +1871,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
CMD(add_tx_ts, ADD_TX_TS);
CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
+ CMD(update_ft_ies, UPDATE_FT_IES);
}
#undef CMD
@@ -3419,7 +3421,7 @@ static void get_key_callback(void *c, struct key_params *params)
params->cipher)))
goto nla_put_failure;
- if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
+ if (nla_put_u8(cookie->msg, NL80211_KEY_IDX, cookie->idx))
goto nla_put_failure;
nla_nest_end(cookie->msg, key);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 42bd15c..3f2cde2 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -885,22 +885,36 @@ static bool valid_regdb(const u8 *data, unsigned int size)
return true;
}
-static void set_wmm_rule(struct ieee80211_reg_rule *rrule,
- struct fwdb_wmm_rule *wmm)
+static void set_wmm_rule(const struct fwdb_header *db,
+ const struct fwdb_country *country,
+ const struct fwdb_rule *rule,
+ struct ieee80211_reg_rule *rrule)
{
- struct ieee80211_wmm_rule *rule = &rrule->wmm_rule;
- unsigned int i;
+ struct ieee80211_wmm_rule *wmm_rule = &rrule->wmm_rule;
+ struct fwdb_wmm_rule *wmm;
+ unsigned int i, wmm_ptr;
+
+ wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
+ wmm = (void *)((u8 *)db + wmm_ptr);
+
+ if (!valid_wmm(wmm)) {
+ pr_err("Invalid regulatory WMM rule %u-%u in domain %c%c\n",
+ be32_to_cpu(rule->start), be32_to_cpu(rule->end),
+ country->alpha2[0], country->alpha2[1]);
+ return;
+ }
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- rule->client[i].cw_min =
+ wmm_rule->client[i].cw_min =
ecw2cw((wmm->client[i].ecw & 0xf0) >> 4);
- rule->client[i].cw_max = ecw2cw(wmm->client[i].ecw & 0x0f);
- rule->client[i].aifsn = wmm->client[i].aifsn;
- rule->client[i].cot = 1000 * be16_to_cpu(wmm->client[i].cot);
- rule->ap[i].cw_min = ecw2cw((wmm->ap[i].ecw & 0xf0) >> 4);
- rule->ap[i].cw_max = ecw2cw(wmm->ap[i].ecw & 0x0f);
- rule->ap[i].aifsn = wmm->ap[i].aifsn;
- rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
+ wmm_rule->client[i].cw_max = ecw2cw(wmm->client[i].ecw & 0x0f);
+ wmm_rule->client[i].aifsn = wmm->client[i].aifsn;
+ wmm_rule->client[i].cot =
+ 1000 * be16_to_cpu(wmm->client[i].cot);
+ wmm_rule->ap[i].cw_min = ecw2cw((wmm->ap[i].ecw & 0xf0) >> 4);
+ wmm_rule->ap[i].cw_max = ecw2cw(wmm->ap[i].ecw & 0x0f);
+ wmm_rule->ap[i].aifsn = wmm->ap[i].aifsn;
+ wmm_rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
}
rrule->has_wmm = true;
@@ -908,7 +922,7 @@ static void set_wmm_rule(struct ieee80211_reg_rule *rrule,
static int __regdb_query_wmm(const struct fwdb_header *db,
const struct fwdb_country *country, int freq,
- struct ieee80211_reg_rule *rule)
+ struct ieee80211_reg_rule *rrule)
{
unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
@@ -917,18 +931,14 @@ static int __regdb_query_wmm(const struct fwdb_header *db,
for (i = 0; i < coll->n_rules; i++) {
__be16 *rules_ptr = (void *)((u8 *)coll + ALIGN(coll->len, 2));
unsigned int rule_ptr = be16_to_cpu(rules_ptr[i]) << 2;
- struct fwdb_rule *rrule = (void *)((u8 *)db + rule_ptr);
- struct fwdb_wmm_rule *wmm;
- unsigned int wmm_ptr;
+ struct fwdb_rule *rule = (void *)((u8 *)db + rule_ptr);
- if (rrule->len < offsetofend(struct fwdb_rule, wmm_ptr))
+ if (rule->len < offsetofend(struct fwdb_rule, wmm_ptr))
continue;
- if (freq >= KHZ_TO_MHZ(be32_to_cpu(rrule->start)) &&
- freq <= KHZ_TO_MHZ(be32_to_cpu(rrule->end))) {
- wmm_ptr = be16_to_cpu(rrule->wmm_ptr) << 2;
- wmm = (void *)((u8 *)db + wmm_ptr);
- set_wmm_rule(rule, wmm);
+ if (freq >= KHZ_TO_MHZ(be32_to_cpu(rule->start)) &&
+ freq <= KHZ_TO_MHZ(be32_to_cpu(rule->end))) {
+ set_wmm_rule(db, country, rule, rrule);
return 0;
}
}
@@ -1010,12 +1020,8 @@ static int regdb_query_country(const struct fwdb_header *db,
if (rule->len >= offsetofend(struct fwdb_rule, cac_timeout))
rrule->dfs_cac_ms =
1000 * be16_to_cpu(rule->cac_timeout);
- if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) {
- u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
- struct fwdb_wmm_rule *wmm = (void *)((u8 *)db + wmm_ptr);
-
- set_wmm_rule(rrule, wmm);
- }
+ if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr))
+ set_wmm_rule(db, country, rule, rrule);
}
return reg_schedule_apply(regdom);
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index d536b07..07c2196 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -642,11 +642,15 @@ static bool cfg80211_is_all_idle(void)
* All devices must be idle as otherwise if you are actively
* scanning some new beacon hints could be learned and would
* count as new regulatory hints.
+ * Also if there is any other active beaconing interface we
+ * need not issue a disconnect hint and reset any info such
+ * as chan dfs state, etc.
*/
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
wdev_lock(wdev);
- if (wdev->conn || wdev->current_bss)
+ if (wdev->conn || wdev->current_bss ||
+ cfg80211_beaconing_iface_active(wdev))
is_all_idle = false;
wdev_unlock(wdev);
}
@@ -663,7 +667,7 @@ static void disconnect_work(struct work_struct *work)
rtnl_unlock();
}
-static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
/*
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index f7f53f9..20a5113 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -100,7 +100,7 @@ int x25_parse_address_block(struct sk_buff *skb,
}
len = *skb->data;
- needed = 1 + (len >> 4) + (len & 0x0f);
+ needed = 1 + ((len >> 4) + (len & 0x0f) + 1) / 2;
if (!pskb_may_pull(skb, needed)) {
/* packet is too short to hold the addresses it claims
@@ -288,7 +288,7 @@ static struct sock *x25_find_listener(struct x25_address *addr,
sk_for_each(s, &x25_list)
if ((!strcmp(addr->x25_addr,
x25_sk(s)->source_addr.x25_addr) ||
- !strcmp(addr->x25_addr,
+ !strcmp(x25_sk(s)->source_addr.x25_addr,
null_x25_address.x25_addr)) &&
s->sk_state == TCP_LISTEN) {
/*
@@ -685,11 +685,15 @@ static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out;
}
- len = strlen(addr->sx25_addr.x25_addr);
- for (i = 0; i < len; i++) {
- if (!isdigit(addr->sx25_addr.x25_addr[i])) {
- rc = -EINVAL;
- goto out;
+ /* check for the null_x25_address */
+ if (strcmp(addr->sx25_addr.x25_addr, null_x25_address.x25_addr)) {
+
+ len = strlen(addr->sx25_addr.x25_addr);
+ for (i = 0; i < len; i++) {
+ if (!isdigit(addr->sx25_addr.x25_addr[i])) {
+ rc = -EINVAL;
+ goto out;
+ }
}
}
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 8cab91c..d9117ab 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -32,14 +32,9 @@ void xdp_del_sk_umem(struct xdp_umem *umem, struct xdp_sock *xs)
{
unsigned long flags;
- if (xs->dev) {
- spin_lock_irqsave(&umem->xsk_list_lock, flags);
- list_del_rcu(&xs->list);
- spin_unlock_irqrestore(&umem->xsk_list_lock, flags);
-
- if (umem->zc)
- synchronize_net();
- }
+ spin_lock_irqsave(&umem->xsk_list_lock, flags);
+ list_del_rcu(&xs->list);
+ spin_unlock_irqrestore(&umem->xsk_list_lock, flags);
}
int xdp_umem_query(struct net_device *dev, u16 queue_id)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 6615040..ff15207 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -343,12 +343,18 @@ static int xsk_release(struct socket *sock)
local_bh_enable();
if (xs->dev) {
+ struct net_device *dev = xs->dev;
+
/* Wait for driver to stop using the xdp socket. */
- synchronize_net();
- dev_put(xs->dev);
+ xdp_del_sk_umem(xs->umem, xs);
xs->dev = NULL;
+ synchronize_net();
+ dev_put(dev);
}
+ xskq_destroy(xs->rx);
+ xskq_destroy(xs->tx);
+
sock_orphan(sk);
sock->sk = NULL;
@@ -707,9 +713,6 @@ static void xsk_destruct(struct sock *sk)
if (!sock_flag(sk, SOCK_DEAD))
return;
- xskq_destroy(xs->rx);
- xskq_destroy(xs->tx);
- xdp_del_sk_umem(xs->umem, xs);
xdp_put_umem(xs->umem);
sk_refcnt_debug_dec(sk);
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 790b514..82b0a99 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -131,7 +131,7 @@ struct sec_path *secpath_dup(struct sec_path *src)
sp->len = 0;
sp->olen = 0;
- memset(sp->ovec, 0, sizeof(sp->ovec[XFRM_MAX_OFFLOAD_DEPTH]));
+ memset(sp->ovec, 0, sizeof(sp->ovec));
if (src) {
int i;
@@ -246,6 +246,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
else
XFRM_INC_STATS(net,
LINUX_MIB_XFRMINSTATEINVALID);
+
+ if (encap_type == -1)
+ dev_put(skb->dev);
goto drop;
}
diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c
index 555ee2a..d6a3cdf 100644
--- a/net/xfrm/xfrm_interface.c
+++ b/net/xfrm/xfrm_interface.c
@@ -133,7 +133,7 @@ static void xfrmi_dev_free(struct net_device *dev)
free_percpu(dev->tstats);
}
-static int xfrmi_create2(struct net_device *dev)
+static int xfrmi_create(struct net_device *dev)
{
struct xfrm_if *xi = netdev_priv(dev);
struct net *net = dev_net(dev);
@@ -156,54 +156,7 @@ static int xfrmi_create2(struct net_device *dev)
return err;
}
-static struct xfrm_if *xfrmi_create(struct net *net, struct xfrm_if_parms *p)
-{
- struct net_device *dev;
- struct xfrm_if *xi;
- char name[IFNAMSIZ];
- int err;
-
- if (p->name[0]) {
- strlcpy(name, p->name, IFNAMSIZ);
- } else {
- err = -EINVAL;
- goto failed;
- }
-
- dev = alloc_netdev(sizeof(*xi), name, NET_NAME_UNKNOWN, xfrmi_dev_setup);
- if (!dev) {
- err = -EAGAIN;
- goto failed;
- }
-
- dev_net_set(dev, net);
-
- xi = netdev_priv(dev);
- xi->p = *p;
- xi->net = net;
- xi->dev = dev;
- xi->phydev = dev_get_by_index(net, p->link);
- if (!xi->phydev) {
- err = -ENODEV;
- goto failed_free;
- }
-
- err = xfrmi_create2(dev);
- if (err < 0)
- goto failed_dev_put;
-
- return xi;
-
-failed_dev_put:
- dev_put(xi->phydev);
-failed_free:
- free_netdev(dev);
-failed:
- return ERR_PTR(err);
-}
-
-static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p,
- int create)
+static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p)
{
struct xfrm_if __rcu **xip;
struct xfrm_if *xi;
@@ -211,17 +164,11 @@ static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p,
for (xip = &xfrmn->xfrmi[0];
(xi = rtnl_dereference(*xip)) != NULL;
- xip = &xi->next) {
- if (xi->p.if_id == p->if_id) {
- if (create)
- return ERR_PTR(-EEXIST);
-
+ xip = &xi->next)
+ if (xi->p.if_id == p->if_id)
return xi;
- }
- }
- if (!create)
- return ERR_PTR(-ENODEV);
- return xfrmi_create(net, p);
+
+ return NULL;
}
static void xfrmi_dev_uninit(struct net_device *dev)
@@ -230,7 +177,6 @@ static void xfrmi_dev_uninit(struct net_device *dev)
struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id);
xfrmi_unlink(xfrmn, xi);
- dev_put(xi->phydev);
dev_put(dev);
}
@@ -417,7 +363,7 @@ static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_err;
}
- fl.flowi_oif = xi->phydev->ifindex;
+ fl.flowi_oif = xi->p.link;
ret = xfrmi_xmit2(skb, dev, &fl);
if (ret < 0)
@@ -558,7 +504,7 @@ static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p)
static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p)
{
- struct net *net = dev_net(xi->dev);
+ struct net *net = xi->net;
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
int err;
@@ -606,7 +552,7 @@ static int xfrmi_get_iflink(const struct net_device *dev)
{
struct xfrm_if *xi = netdev_priv(dev);
- return xi->phydev->ifindex;
+ return xi->p.link;
}
@@ -632,12 +578,14 @@ static void xfrmi_dev_setup(struct net_device *dev)
dev->needs_free_netdev = true;
dev->priv_destructor = xfrmi_dev_free;
netif_keep_dst(dev);
+
+ eth_broadcast_addr(dev->broadcast);
}
static int xfrmi_dev_init(struct net_device *dev)
{
struct xfrm_if *xi = netdev_priv(dev);
- struct net_device *phydev = xi->phydev;
+ struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link);
int err;
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
@@ -652,13 +600,19 @@ static int xfrmi_dev_init(struct net_device *dev)
dev->features |= NETIF_F_LLTX;
- dev->needed_headroom = phydev->needed_headroom;
- dev->needed_tailroom = phydev->needed_tailroom;
+ if (phydev) {
+ dev->needed_headroom = phydev->needed_headroom;
+ dev->needed_tailroom = phydev->needed_tailroom;
- if (is_zero_ether_addr(dev->dev_addr))
- eth_hw_addr_inherit(dev, phydev);
- if (is_zero_ether_addr(dev->broadcast))
- memcpy(dev->broadcast, phydev->broadcast, dev->addr_len);
+ if (is_zero_ether_addr(dev->dev_addr))
+ eth_hw_addr_inherit(dev, phydev);
+ if (is_zero_ether_addr(dev->broadcast))
+ memcpy(dev->broadcast, phydev->broadcast,
+ dev->addr_len);
+ } else {
+ eth_hw_addr_random(dev);
+ eth_broadcast_addr(dev->broadcast);
+ }
return 0;
}
@@ -689,21 +643,28 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
struct netlink_ext_ack *extack)
{
struct net *net = dev_net(dev);
- struct xfrm_if_parms *p;
+ struct xfrm_if_parms p;
struct xfrm_if *xi;
+ int err;
- xi = netdev_priv(dev);
- p = &xi->p;
-
- xfrmi_netlink_parms(data, p);
+ xfrmi_netlink_parms(data, &p);
if (!tb[IFLA_IFNAME])
return -EINVAL;
- nla_strlcpy(p->name, tb[IFLA_IFNAME], IFNAMSIZ);
+ nla_strlcpy(p.name, tb[IFLA_IFNAME], IFNAMSIZ);
- xi = xfrmi_locate(net, p, 1);
- return PTR_ERR_OR_ZERO(xi);
+ xi = xfrmi_locate(net, &p);
+ if (xi)
+ return -EEXIST;
+
+ xi = netdev_priv(dev);
+ xi->p = p;
+ xi->net = net;
+ xi->dev = dev;
+
+ err = xfrmi_create(dev);
+ return err;
}
static void xfrmi_dellink(struct net_device *dev, struct list_head *head)
@@ -716,20 +677,19 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct xfrm_if *xi = netdev_priv(dev);
- struct net *net = dev_net(dev);
+ struct net *net = xi->net;
+ struct xfrm_if_parms p;
- xfrmi_netlink_parms(data, &xi->p);
-
- xi = xfrmi_locate(net, &xi->p, 0);
-
- if (IS_ERR_OR_NULL(xi)) {
+ xfrmi_netlink_parms(data, &p);
+ xi = xfrmi_locate(net, &p);
+ if (!xi) {
xi = netdev_priv(dev);
} else {
if (xi->dev != dev)
return -EEXIST;
}
- return xfrmi_update(xi, &xi->p);
+ return xfrmi_update(xi, &p);
}
static size_t xfrmi_get_size(const struct net_device *dev)
@@ -760,7 +720,7 @@ struct net *xfrmi_get_link_net(const struct net_device *dev)
{
struct xfrm_if *xi = netdev_priv(dev);
- return dev_net(xi->phydev);
+ return xi->net;
}
static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = {
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index f08ab5c..c1900e4 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -456,6 +456,8 @@ static void ___xfrm_state_destroy(struct xfrm_state *x)
x->type->destructor(x);
xfrm_put_type(x->type);
}
+ if (x->xfrag.page)
+ put_page(x->xfrag.page);
xfrm_dev_state_free(x);
security_xfrm_state_free(x);
xfrm_state_free(x);
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 36f9f41..75d4b48 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -172,6 +172,7 @@
KBUILD_HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/
KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/ -I$(srctree)/tools/include
KBUILD_HOSTCFLAGS += -I$(srctree)/tools/perf
+KBUILD_HOSTCFLAGS += -DHAVE_ATTR_TEST=0
HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable
HOSTCFLAGS_trace_helpers.o += -I$(srctree)/tools/lib/bpf/
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 5061a2e..176c04a 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -59,7 +59,9 @@ static int write_kprobe_events(const char *val)
{
int fd, ret, flags;
- if ((val != NULL) && (val[0] == '\0'))
+ if (val == NULL)
+ return -1;
+ else if (val[0] == '\0')
flags = O_WRONLY | O_TRUNC;
else
flags = O_WRONLY | O_APPEND;
diff --git a/samples/bpf/sockex2_kern.c b/samples/bpf/sockex2_kern.c
index f58acfc..f2f9dbc 100644
--- a/samples/bpf/sockex2_kern.c
+++ b/samples/bpf/sockex2_kern.c
@@ -14,7 +14,7 @@ struct vlan_hdr {
__be16 h_vlan_encapsulated_proto;
};
-struct bpf_flow_keys {
+struct flow_key_record {
__be32 src;
__be32 dst;
union {
@@ -59,7 +59,7 @@ static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
}
static inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
- struct bpf_flow_keys *flow)
+ struct flow_key_record *flow)
{
__u64 verlen;
@@ -83,7 +83,7 @@ static inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto
}
static inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
- struct bpf_flow_keys *flow)
+ struct flow_key_record *flow)
{
*ip_proto = load_byte(skb,
nhoff + offsetof(struct ipv6hdr, nexthdr));
@@ -96,7 +96,8 @@ static inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_pro
return nhoff;
}
-static inline bool flow_dissector(struct __sk_buff *skb, struct bpf_flow_keys *flow)
+static inline bool flow_dissector(struct __sk_buff *skb,
+ struct flow_key_record *flow)
{
__u64 nhoff = ETH_HLEN;
__u64 ip_proto;
@@ -198,7 +199,7 @@ struct bpf_map_def SEC("maps") hash_map = {
SEC("socket2")
int bpf_prog2(struct __sk_buff *skb)
{
- struct bpf_flow_keys flow = {};
+ struct flow_key_record flow = {};
struct pair *value;
u32 key;
diff --git a/samples/bpf/sockex3_kern.c b/samples/bpf/sockex3_kern.c
index 95907f8..c527b57 100644
--- a/samples/bpf/sockex3_kern.c
+++ b/samples/bpf/sockex3_kern.c
@@ -61,7 +61,7 @@ struct vlan_hdr {
__be16 h_vlan_encapsulated_proto;
};
-struct bpf_flow_keys {
+struct flow_key_record {
__be32 src;
__be32 dst;
union {
@@ -88,7 +88,7 @@ static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
}
struct globals {
- struct bpf_flow_keys flow;
+ struct flow_key_record flow;
};
struct bpf_map_def SEC("maps") percpu_map = {
@@ -114,14 +114,14 @@ struct pair {
struct bpf_map_def SEC("maps") hash_map = {
.type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(struct bpf_flow_keys),
+ .key_size = sizeof(struct flow_key_record),
.value_size = sizeof(struct pair),
.max_entries = 1024,
};
static void update_stats(struct __sk_buff *skb, struct globals *g)
{
- struct bpf_flow_keys key = g->flow;
+ struct flow_key_record key = g->flow;
struct pair *value;
value = bpf_map_lookup_elem(&hash_map, &key);
diff --git a/samples/bpf/sockex3_user.c b/samples/bpf/sockex3_user.c
index 5ba3ae9..9d02e04 100644
--- a/samples/bpf/sockex3_user.c
+++ b/samples/bpf/sockex3_user.c
@@ -13,7 +13,7 @@
#define PARSE_IP_PROG_FD (prog_fd[0])
#define PROG_ARRAY_FD (map_fd[0])
-struct bpf_flow_keys {
+struct flow_key_record {
__be32 src;
__be32 dst;
union {
@@ -64,7 +64,7 @@ int main(int argc, char **argv)
(void) f;
for (i = 0; i < 5; i++) {
- struct bpf_flow_keys key = {}, next_key;
+ struct flow_key_record key = {}, next_key;
struct pair value;
sleep(1);
diff --git a/samples/bpf/syscall_tp_kern.c b/samples/bpf/syscall_tp_kern.c
index 9149c52..8833aac 100644
--- a/samples/bpf/syscall_tp_kern.c
+++ b/samples/bpf/syscall_tp_kern.c
@@ -50,13 +50,27 @@ static __always_inline void count(void *map)
SEC("tracepoint/syscalls/sys_enter_open")
int trace_enter_open(struct syscalls_enter_open_args *ctx)
{
- count((void *)&enter_open_map);
+ count(&enter_open_map);
+ return 0;
+}
+
+SEC("tracepoint/syscalls/sys_enter_openat")
+int trace_enter_open_at(struct syscalls_enter_open_args *ctx)
+{
+ count(&enter_open_map);
return 0;
}
SEC("tracepoint/syscalls/sys_exit_open")
int trace_enter_exit(struct syscalls_exit_open_args *ctx)
{
- count((void *)&exit_open_map);
+ count(&exit_open_map);
+ return 0;
+}
+
+SEC("tracepoint/syscalls/sys_exit_openat")
+int trace_enter_exit_at(struct syscalls_exit_open_args *ctx)
+{
+ count(&exit_open_map);
return 0;
}
diff --git a/samples/bpf/trace_event_user.c b/samples/bpf/trace_event_user.c
index d08046ab8..d330224 100644
--- a/samples/bpf/trace_event_user.c
+++ b/samples/bpf/trace_event_user.c
@@ -35,9 +35,9 @@ static void print_ksym(__u64 addr)
return;
sym = ksym_search(addr);
printf("%s;", sym->name);
- if (!strcmp(sym->name, "sys_read"))
+ if (!strstr(sym->name, "sys_read"))
sys_read_seen = true;
- else if (!strcmp(sym->name, "sys_write"))
+ else if (!strstr(sym->name, "sys_write"))
sys_write_seen = true;
}
diff --git a/samples/mei/mei-amt-version.c b/samples/mei/mei-amt-version.c
index bb99889..32234481 100644
--- a/samples/mei/mei-amt-version.c
+++ b/samples/mei/mei-amt-version.c
@@ -370,7 +370,7 @@ static uint32_t amt_host_if_call(struct amt_host_if *acmd,
unsigned int expected_sz)
{
uint32_t in_buf_sz;
- uint32_t out_buf_sz;
+ ssize_t out_buf_sz;
ssize_t written;
uint32_t status;
struct amt_host_if_resp_header *msg_hdr;
diff --git a/samples/pktgen/functions.sh b/samples/pktgen/functions.sh
index f8bb3cd..7d92857 100644
--- a/samples/pktgen/functions.sh
+++ b/samples/pktgen/functions.sh
@@ -5,6 +5,8 @@
# Author: Jesper Dangaaard Brouer
# License: GPL
+set -o errexit
+
## -- General shell logging cmds --
function err() {
local exitcode=$1
@@ -58,6 +60,7 @@
function proc_cmd() {
local result
local proc_file=$1
+ local status=0
# after shift, the remaining args are contained in $@
shift
local proc_ctrl=${PROC_DIR}/$proc_file
@@ -73,13 +76,13 @@
echo "cmd: $@ > $proc_ctrl"
fi
# Quoting of "$@" is important for space expansion
- echo "$@" > "$proc_ctrl"
- local status=$?
+ echo "$@" > "$proc_ctrl" || status=$?
- result=$(grep "Result: OK:" $proc_ctrl)
- # Due to pgctrl, cannot use exit code $? from grep
- if [[ "$result" == "" ]]; then
- grep "Result:" $proc_ctrl >&2
+ if [[ "$proc_file" != "pgctrl" ]]; then
+ result=$(grep "Result: OK:" $proc_ctrl) || true
+ if [[ "$result" == "" ]]; then
+ grep "Result:" $proc_ctrl >&2
+ fi
fi
if (( $status != 0 )); then
err 5 "Write error($status) occurred cmd: \"$@ > $proc_ctrl\""
@@ -105,6 +108,8 @@
fi
}
+[[ $EUID -eq 0 ]] && trap 'pg_ctrl "reset"' EXIT
+
## -- General shell tricks --
function root_check_run_with_sudo() {
diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c
index 7abb79d..f6732aa 100644
--- a/samples/vfio-mdev/mtty.c
+++ b/samples/vfio-mdev/mtty.c
@@ -171,7 +171,7 @@ static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid)
return NULL;
}
-void dump_buffer(char *buf, uint32_t count)
+void dump_buffer(u8 *buf, uint32_t count)
{
#if defined(DEBUG)
int i;
@@ -250,7 +250,7 @@ static void mtty_create_config_space(struct mdev_state *mdev_state)
}
static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
- char *buf, u32 count)
+ u8 *buf, u32 count)
{
u32 cfg_addr, bar_mask, bar_index = 0;
@@ -304,7 +304,7 @@ static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
}
static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
- u16 offset, char *buf, u32 count)
+ u16 offset, u8 *buf, u32 count)
{
u8 data = *buf;
@@ -475,7 +475,7 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
}
static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state,
- u16 offset, char *buf, u32 count)
+ u16 offset, u8 *buf, u32 count)
{
/* Handle read requests by guest */
switch (offset) {
@@ -650,7 +650,7 @@ static void mdev_read_base(struct mdev_state *mdev_state)
}
}
-static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
+static ssize_t mdev_access(struct mdev_device *mdev, u8 *buf, size_t count,
loff_t pos, bool is_write)
{
struct mdev_state *mdev_state;
@@ -698,7 +698,7 @@ static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
#if defined(DEBUG_REGS)
pr_info("%s: BAR%d WR @0x%llx %s val:0x%02x dlab:%d\n",
__func__, index, offset, wr_reg[offset],
- (u8)*buf, mdev_state->s[index].dlab);
+ *buf, mdev_state->s[index].dlab);
#endif
handle_bar_write(index, mdev_state, offset, buf, count);
} else {
@@ -708,7 +708,7 @@ static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
#if defined(DEBUG_REGS)
pr_info("%s: BAR%d RD @0x%llx %s val:0x%02x dlab:%d\n",
__func__, index, offset, rd_reg[offset],
- (u8)*buf, mdev_state->s[index].dlab);
+ *buf, mdev_state->s[index].dlab);
#endif
}
break;
@@ -827,7 +827,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
if (count >= 4 && !(*ppos % 4)) {
u32 val;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, false);
if (ret <= 0)
goto read_err;
@@ -839,7 +839,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
} else if (count >= 2 && !(*ppos % 2)) {
u16 val;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, false);
if (ret <= 0)
goto read_err;
@@ -851,7 +851,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
} else {
u8 val;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, false);
if (ret <= 0)
goto read_err;
@@ -889,7 +889,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
if (copy_from_user(&val, buf, sizeof(val)))
goto write_err;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, true);
if (ret <= 0)
goto write_err;
@@ -901,7 +901,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
if (copy_from_user(&val, buf, sizeof(val)))
goto write_err;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, true);
if (ret <= 0)
goto write_err;
@@ -913,7 +913,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
if (copy_from_user(&val, buf, sizeof(val)))
goto write_err;
- ret = mdev_access(mdev, (char *)&val, sizeof(val),
+ ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
*ppos, true);
if (ret <= 0)
goto write_err;
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index f36b088..91cdaea 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -266,6 +266,7 @@
-Wno-alias_paths \
-Wno-graph_child_address \
-Wno-graph_port \
+ -Wno-simple_bus_reg \
-Wno-unique_unit_address \
-Wno-pci_device_reg
endif
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index 004b0ac..4644f1a 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -99,7 +99,8 @@
attrs[n]['name'].string(): attrs[n]['address']
for n in range(int(sect_attrs['nsections']))}
args = []
- for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
+ for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
+ ".text", ".text.hot", ".text.unlikely"]:
address = section_name_to_address.get(section_name)
if address:
args.append(" -s {name} {addr}".format(
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 31ed7f3..4b2711c 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -491,6 +491,8 @@ static void build_initial_tok_table(void)
table[pos] = table[i];
learn_symbol(table[pos].sym, table[pos].len);
pos++;
+ } else {
+ free(table[i].sym);
}
}
table_cnt = pos;
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index e1a39e9..7e38070 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -252,6 +252,13 @@ static int expr_eq(struct expr *e1, struct expr *e2)
{
int res, old_count;
+ /*
+ * A NULL expr is taken to be yes, but there's also a different way to
+ * represent yes. expr_is_yes() checks for either representation.
+ */
+ if (!e1 || !e2)
+ return expr_is_yes(e1) && expr_is_yes(e2);
+
if (e1->type != e2->type)
return 0;
switch (e1->type) {
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index b16c893e..f9ab348 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1168,6 +1168,15 @@ static const struct sectioncheck *section_mismatch(
* refsymname = *.constprop.*
*
* Pattern 6:
+ * Hide section mismatch warnings for ELF local symbols. The goal
+ * is to eliminate false positive modpost warnings caused by
+ * compiler-generated ELF local symbol names such as ".LANCHOR1".
+ * Autogenerated symbol names bypass modpost's "Pattern 2"
+ * whitelisting, which relies on pattern-matching against symbol
+ * names to work. (One situation where gcc can autogenerate ELF
+ * local symbols is when "-fsection-anchors" is used.)
+ *
+ * Pattern 7:
* With CONFIG_CFI_CLANG, clang appends .cfi to all indirectly called
* functions and creates a function stub with the original name. This
* stub is always placed in .text, even if the actual function with the
@@ -1216,6 +1225,10 @@ static int secref_whitelist(const struct sectioncheck *mismatch,
return 0;
/* Check for pattern 6 */
+ if (strstarts(fromsym, ".L"))
+ return 0;
+
+ /* Check for pattern 7 */
if (match(fromsec, text_sections) &&
match(tosec, init_exit_sections) &&
match(tosym, cfi_symbols))
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 71f3941..365b3c2 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -73,8 +73,16 @@
printf -- '-svn%s' "`git svn find-rev $head`"
fi
- # Check for uncommitted changes
- if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then
+ # Check for uncommitted changes.
+ # First, with git-status, but --no-optional-locks is only
+ # supported in git >= 2.14, so fall back to git-diff-index if
+ # it fails. Note that git-diff-index does not refresh the
+ # index, so it may give misleading results. See
+ # git-update-index(1), git-diff-index(1), and git-status(1).
+ if {
+ git --no-optional-locks status -uno --porcelain 2>/dev/null ||
+ git diff-index --name-only HEAD
+ } | grep -qvE '^(.. )?scripts/package'; then
printf '%s' -dirty
fi
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 40e3a09..0a57d10 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -361,6 +361,7 @@ static void aafs_remove(struct dentry *dentry)
simple_rmdir(dir, dentry);
else
simple_unlink(dir, dentry);
+ d_delete(dentry);
dput(dentry);
}
inode_unlock(dir);
@@ -592,7 +593,7 @@ static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
void __aa_bump_ns_revision(struct aa_ns *ns)
{
- ns->revision++;
+ WRITE_ONCE(ns->revision, ns->revision + 1);
wake_up_interruptible(&ns->wait);
}
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 11975ec..bdf9e4c 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -321,6 +321,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
if (!bprm || !profile->xattr_count)
return 0;
+ might_sleep();
/* transition from exec match to xattr set */
state = aa_dfa_null_transition(profile->xmatch, state);
@@ -365,10 +366,11 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
}
/**
- * __attach_match_ - find an attachment match
+ * find_attach - do attachment search for unconfined processes
* @bprm - binprm structure of transitioning task
- * @name - to match against (NOT NULL)
+ * @ns: the current namespace (NOT NULL)
* @head - profile list to walk (NOT NULL)
+ * @name - to match against (NOT NULL)
* @info - info message if there was an error (NOT NULL)
*
* Do a linear search on the profiles in the list. There is a matching
@@ -378,12 +380,11 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
*
* Requires: @head not be shared or have appropriate locks held
*
- * Returns: profile or NULL if no match found
+ * Returns: label or NULL if no match found
*/
-static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
- const char *name,
- struct list_head *head,
- const char **info)
+static struct aa_label *find_attach(const struct linux_binprm *bprm,
+ struct aa_ns *ns, struct list_head *head,
+ const char *name, const char **info)
{
int candidate_len = 0, candidate_xattrs = 0;
bool conflict = false;
@@ -392,6 +393,8 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
AA_BUG(!name);
AA_BUG(!head);
+ rcu_read_lock();
+restart:
list_for_each_entry_rcu(profile, head, base.list) {
if (profile->label.flags & FLAG_NULL &&
&profile->label == ns_unconfined(profile->ns))
@@ -417,16 +420,32 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
perm = dfa_user_allow(profile->xmatch, state);
/* any accepting state means a valid match. */
if (perm & MAY_EXEC) {
- int ret;
+ int ret = 0;
if (count < candidate_len)
continue;
- ret = aa_xattrs_match(bprm, profile, state);
- /* Fail matching if the xattrs don't match */
- if (ret < 0)
- continue;
+ if (bprm && profile->xattr_count) {
+ long rev = READ_ONCE(ns->revision);
+ if (!aa_get_profile_not0(profile))
+ goto restart;
+ rcu_read_unlock();
+ ret = aa_xattrs_match(bprm, profile,
+ state);
+ rcu_read_lock();
+ aa_put_profile(profile);
+ if (rev !=
+ READ_ONCE(ns->revision))
+ /* policy changed */
+ goto restart;
+ /*
+ * Fail matching if the xattrs don't
+ * match
+ */
+ if (ret < 0)
+ continue;
+ }
/*
* TODO: allow for more flexible best match
*
@@ -449,43 +468,28 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
candidate_xattrs = ret;
conflict = false;
}
- } else if (!strcmp(profile->base.name, name))
+ } else if (!strcmp(profile->base.name, name)) {
/*
* old exact non-re match, without conditionals such
* as xattrs. no more searching required
*/
- return profile;
+ candidate = profile;
+ goto out;
+ }
}
- if (conflict) {
- *info = "conflicting profile attachments";
+ if (!candidate || conflict) {
+ if (conflict)
+ *info = "conflicting profile attachments";
+ rcu_read_unlock();
return NULL;
}
- return candidate;
-}
-
-/**
- * find_attach - do attachment search for unconfined processes
- * @bprm - binprm structure of transitioning task
- * @ns: the current namespace (NOT NULL)
- * @list: list to search (NOT NULL)
- * @name: the executable name to match against (NOT NULL)
- * @info: info message if there was an error
- *
- * Returns: label or NULL if no match found
- */
-static struct aa_label *find_attach(const struct linux_binprm *bprm,
- struct aa_ns *ns, struct list_head *list,
- const char *name, const char **info)
-{
- struct aa_profile *profile;
-
- rcu_read_lock();
- profile = aa_get_profile(__attach_match(bprm, name, list, info));
+out:
+ candidate = aa_get_newest_profile(candidate);
rcu_read_unlock();
- return profile ? &profile->label : NULL;
+ return &candidate->label;
}
static const char *next_name(int xtype, const char *name)
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index ba11bdf..2469549 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -1462,11 +1462,13 @@ static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
/* helper macro for snprint routines */
#define update_for_len(total, len, size, str) \
do { \
+ size_t ulen = len; \
+ \
AA_BUG(len < 0); \
- total += len; \
- len = min(len, size); \
- size -= len; \
- str += len; \
+ total += ulen; \
+ ulen = min(ulen, size); \
+ size -= ulen; \
+ str += ulen; \
} while (0)
/**
@@ -1601,7 +1603,7 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
struct aa_ns *prev_ns = NULL;
struct label_it i;
int count = 0, total = 0;
- size_t len;
+ ssize_t len;
AA_BUG(!str && size != 0);
AA_BUG(!label);
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 1590e2d..3a4293c 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1126,8 +1126,8 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
if (!name) {
/* remove namespace - can only happen if fqname[0] == ':' */
mutex_lock_nested(&ns->parent->lock, ns->level);
- __aa_remove_ns(ns);
__aa_bump_ns_revision(ns);
+ __aa_remove_ns(ns);
mutex_unlock(&ns->parent->lock);
} else {
/* remove profile */
@@ -1139,9 +1139,9 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
goto fail_ns_lock;
}
name = profile->base.hname;
+ __aa_bump_ns_revision(ns);
__remove_profile(profile);
__aa_labelset_update_subtree(ns);
- __aa_bump_ns_revision(ns);
mutex_unlock(&ns->lock);
}
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index c7161f8..ef0f184 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
- netnode.o netport.o ibpkey.o exports.o \
+ netnode.o netport.o exports.o\
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
@@ -14,6 +14,8 @@
selinux-$(CONFIG_NETLABEL) += netlabel.o
+selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o
+
ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
$(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h
index b17a19e..e3c2b12 100644
--- a/security/selinux/include/ibpkey.h
+++ b/security/selinux/include/ibpkey.h
@@ -24,8 +24,20 @@
#ifndef _SELINUX_IB_PKEY_H
#define _SELINUX_IB_PKEY_H
+#ifdef CONFIG_SECURITY_INFINIBAND
void sel_ib_pkey_flush(void);
int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid);
+#else
+
+static inline void sel_ib_pkey_flush(void) { }
+
+static inline int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid)
+{
+ *sid = SECINITSID_UNLABELED;
+ return 0;
+}
+#endif
+
#endif
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 2045697..797d838 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -107,6 +107,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
convert(plugin, src_channels, dst_channels, frames);
return frames;
}
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 7915564..3788906 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -269,6 +269,8 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
data = (struct mulaw_priv *)plugin->extra_data;
data->func(plugin, src_channels, dst_channels, frames);
return frames;
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 71571d9..31cb2ac 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -111,7 +111,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- if (snd_BUG_ON(frames <= 0))
+ if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0))
return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -123,7 +123,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->prev) {
if (plugin->src_frames)
frames = plugin->src_frames(plugin, frames);
- if (snd_BUG_ON(frames <= 0))
+ if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0))
return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index c8171f5..72dea04 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -57,6 +57,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
return -ENXIO;
if (frames == 0)
return 0;
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
nsrcs = plugin->src_format.channels;
ndsts = plugin->dst_format.channels;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b47fd36..74516e4 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1812,11 +1812,14 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
unsigned long flags;
- if (PCM_RUNTIME_CHECK(substream))
+ if (snd_BUG_ON(!substream))
return;
- runtime = substream->runtime;
snd_pcm_stream_lock_irqsave(substream, flags);
+ if (PCM_RUNTIME_CHECK(substream))
+ goto _unlock;
+ runtime = substream->runtime;
+
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
@@ -1827,6 +1830,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
#endif
_end:
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ _unlock:
snd_pcm_stream_unlock_irqrestore(substream, flags);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index a0112b9..43504e6 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -754,6 +754,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
+ /* clear the buffer for avoiding possible kernel info leaks */
+ if (runtime->dma_area && !substream->ops->copy_user)
+ memset(runtime->dma_area, 0, runtime->dma_bytes);
+
snd_pcm_timer_resolution_change(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index 8ce1d0b..ce1f1e4 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -123,6 +123,7 @@ int __init snd_seq_system_client_init(void)
{
struct snd_seq_port_callback pcallbacks;
struct snd_seq_port_info *port;
+ int err;
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
@@ -144,7 +145,10 @@ int __init snd_seq_system_client_init(void)
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
/* register announcement port */
strcpy(port->name, "Announce");
@@ -154,16 +158,24 @@ int __init snd_seq_system_client_init(void)
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
announce_port = port->addr.port;
kfree(port);
return 0;
+
+ error_port:
+ snd_seq_system_client_done();
+ kfree(port);
+ return err;
}
/* unregister our internal client */
-void __exit snd_seq_system_client_done(void)
+void snd_seq_system_client_done(void)
{
int oldsysclient = sysclient;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index ac9a7e1..5a9e570 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -88,6 +88,9 @@ static LIST_HEAD(snd_timer_slave_list);
/* lock for slave active lists */
static DEFINE_SPINLOCK(slave_active_lock);
+#define MAX_SLAVE_INSTANCES 1000
+static int num_slaves;
+
static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
@@ -240,7 +243,8 @@ static int snd_timer_check_master(struct snd_timer_instance *master)
return 0;
}
-static int snd_timer_close_locked(struct snd_timer_instance *timeri);
+static int snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put);
/*
* open a timer instance
@@ -252,38 +256,42 @@ int snd_timer_open(struct snd_timer_instance **ti,
{
struct snd_timer *timer;
struct snd_timer_instance *timeri = NULL;
+ struct device *card_dev_to_put = NULL;
int err;
+ mutex_lock(®ister_mutex);
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
/* open a slave instance */
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
pr_debug("ALSA: timer: invalid slave class %i\n",
tid->dev_sclass);
- return -EINVAL;
+ err = -EINVAL;
+ goto unlock;
}
- mutex_lock(®ister_mutex);
+ if (num_slaves >= MAX_SLAVE_INSTANCES) {
+ err = -EBUSY;
+ goto unlock;
+ }
timeri = snd_timer_instance_new(owner, NULL);
if (!timeri) {
- mutex_unlock(®ister_mutex);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+ num_slaves++;
err = snd_timer_check_slave(timeri);
if (err < 0) {
- snd_timer_close_locked(timeri);
+ snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
- mutex_unlock(®ister_mutex);
- *ti = timeri;
- return err;
+ goto unlock;
}
/* open a master instance */
- mutex_lock(®ister_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -294,25 +302,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
}
#endif
if (!timer) {
- mutex_unlock(®ister_mutex);
- return -ENODEV;
+ err = -ENODEV;
+ goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
- timeri = list_entry(timer->open_list_head.next,
+ struct snd_timer_instance *t =
+ list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
- if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
- mutex_unlock(®ister_mutex);
- return -EBUSY;
+ if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+ err = -EBUSY;
+ goto unlock;
}
}
if (timer->num_instances >= timer->max_instances) {
- mutex_unlock(®ister_mutex);
- return -EBUSY;
+ err = -EBUSY;
+ goto unlock;
}
timeri = snd_timer_instance_new(owner, timer);
if (!timeri) {
- mutex_unlock(®ister_mutex);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unlock;
}
/* take a card refcount for safe disconnection */
if (timer->card)
@@ -321,16 +330,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open) {
- int err = timer->hw.open(timer);
+ err = timer->hw.open(timer);
if (err) {
kfree(timeri->owner);
kfree(timeri);
+ timeri = NULL;
if (timer->card)
- put_device(&timer->card->card_dev);
+ card_dev_to_put = &timer->card->card_dev;
module_put(timer->module);
- mutex_unlock(®ister_mutex);
- return err;
+ goto unlock;
}
}
@@ -338,10 +347,15 @@ int snd_timer_open(struct snd_timer_instance **ti,
timer->num_instances++;
err = snd_timer_check_master(timeri);
if (err < 0) {
- snd_timer_close_locked(timeri);
+ snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
+
+ unlock:
mutex_unlock(®ister_mutex);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
*ti = timeri;
return err;
}
@@ -351,12 +365,15 @@ EXPORT_SYMBOL(snd_timer_open);
* close a timer instance
* call this with register_mutex down.
*/
-static int snd_timer_close_locked(struct snd_timer_instance *timeri)
+static int snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put)
{
struct snd_timer *timer = NULL;
struct snd_timer_instance *slave, *tmp;
list_del(&timeri->open_list);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ num_slaves--;
/* force to stop the timer */
snd_timer_stop(timeri);
@@ -403,7 +420,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
- put_device(&timer->card->card_dev);
+ *card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
}
@@ -415,14 +432,18 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
*/
int snd_timer_close(struct snd_timer_instance *timeri)
{
+ struct device *card_dev_to_put = NULL;
int err;
if (snd_BUG_ON(!timeri))
return -ENXIO;
mutex_lock(®ister_mutex);
- err = snd_timer_close_locked(timeri);
+ err = snd_timer_close_locked(timeri, &card_dev_to_put);
mutex_unlock(®ister_mutex);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
return err;
}
EXPORT_SYMBOL(snd_timer_close);
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index 52b8b61..62d989e 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -28,6 +28,8 @@
#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
/* clock sources as returned from register of Saffire Pro 10 and 26 */
+#define SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK 0x000000ff
+#define SAFFIREPRO_CLOCK_SOURCE_DETECT_MASK 0x0000ff00
#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
#define SAFFIREPRO_CLOCK_SOURCE_SKIP 1 /* never used on hardware */
#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
@@ -190,6 +192,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
map = saffirepro_clk_maps[1];
/* In a case that this driver cannot handle the value of register. */
+ value &= SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK;
if (value >= SAFFIREPRO_CLOCK_SOURCE_COUNT || map[value] < 0) {
err = -EIO;
goto end;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 4d3034a..be2c056 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -253,8 +253,7 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
return err;
}
-static unsigned int
-map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+static int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
{
unsigned int sec, sections, ch, channels;
unsigned int pcm, midi, location;
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 3095747..0717ab9 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -640,7 +640,7 @@ static int isight_probe(struct fw_unit *unit,
if (!isight->audio_base) {
dev_err(&unit->device, "audio unit base not found\n");
err = -ENXIO;
- goto err_unit;
+ goto error;
}
fw_iso_resources_init(&isight->resources, unit);
@@ -669,12 +669,12 @@ static int isight_probe(struct fw_unit *unit,
dev_set_drvdata(&unit->device, isight);
return 0;
-
-err_unit:
- fw_unit_put(isight->unit);
- mutex_destroy(&isight->mutex);
error:
snd_card_free(card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+
return err;
}
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
index ab6830a..015b4ec 100644
--- a/sound/firewire/motu/motu-proc.c
+++ b/sound/firewire/motu/motu-proc.c
@@ -17,7 +17,7 @@ static const char *const clock_names[] = {
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
- [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PDIF on coaxial interface",
[SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
[SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
};
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 5bc4a1d..60cb00f 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -48,9 +48,11 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
}
if (enable)
- snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN);
else
- snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
@@ -68,9 +70,11 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
}
if (enable)
- snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, AZX_PPCTL_PIE);
else
- snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
@@ -194,7 +198,8 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
*/
int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link)
{
- snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
+ snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL,
+ AZX_MLCTL_SPA, AZX_MLCTL_SPA);
return check_hdac_link_power_active(link, true);
}
@@ -222,8 +227,8 @@ int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
int ret;
list_for_each_entry(hlink, &bus->hlink_list, list) {
- snd_hdac_updatel(hlink->ml_addr,
- AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
+ AZX_MLCTL_SPA, AZX_MLCTL_SPA);
ret = check_hdac_link_power_active(hlink, true);
if (ret < 0)
return ret;
@@ -243,7 +248,8 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
int ret;
list_for_each_entry(hlink, &bus->hlink_list, list) {
- snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
+ AZX_MLCTL_SPA, 0);
ret = check_hdac_link_power_active(hlink, false);
if (ret < 0)
return ret;
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index e858b6f..74244d8 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -443,8 +443,6 @@ static void azx_int_disable(struct hdac_bus *bus)
list_for_each_entry(azx_dev, &bus->stream_list, list)
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
- synchronize_irq(bus->irq);
-
/* disable SIE for all streams */
snd_hdac_chip_writeb(bus, INTCTL, 0);
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 2647309..8afa2f8 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -118,7 +118,7 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
struct cs8427 *chip = device->private_data;
char *hw_data = udata ?
chip->playback.hw_udata : chip->playback.hw_status;
- char data[32];
+ unsigned char data[32];
int err, idx;
if (!memcmp(hw_data, ndata, count))
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 70559e5..7d4e18c 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -293,7 +293,8 @@ static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
if (mpu_irq[dev] >= 0 &&
- pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) {
+ pnp_irq_valid(pdev, 0) &&
+ pnp_irq(pdev, 0) != (resource_size_t)-1) {
mpu_irq[dev] = pnp_irq(pdev, 0);
} else {
mpu_irq[dev] = -1; /* disable interrupt */
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 8db1890..c175b2c 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -42,6 +42,10 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ /* ignore unsol events during shutdown */
+ if (codec->bus->shutdown)
+ return;
+
if (codec->patch_ops.unsol_event)
codec->patch_ops.unsol_event(codec, ev);
}
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 8fcb421..fa261b2 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -883,7 +883,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
return -EAGAIN; /* give a chance to retry */
}
- dev_WARN(chip->card->dev,
+ dev_err(chip->card->dev,
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
bus->last_cmd[addr]);
chip->single_cmd = 1;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index bfc4508..d63fea5 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1455,9 +1455,9 @@ static int azx_free(struct azx *chip)
}
if (bus->chip_init) {
- azx_stop_chip(chip);
azx_clear_irq_pending(chip);
azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
}
if (bus->irq >= 0)
@@ -1492,8 +1492,11 @@ static int azx_free(struct azx *chip)
static int azx_dev_disconnect(struct snd_device *device)
{
struct azx *chip = device->device_data;
+ struct hdac_bus *bus = azx_bus(chip);
chip->bus.shutdown = 1;
+ cancel_work_sync(&bus->unsol_work);
+
return 0;
}
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 0436789..bc4edc5 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/io.h>
#include <linux/pci.h>
+#include <asm/io.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -1682,13 +1683,14 @@ struct scp_msg {
static void dspio_clear_response_queue(struct hda_codec *codec)
{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
unsigned int dummy = 0;
- int status = -1;
+ int status;
/* clear all from the response queue */
do {
status = dspio_read(codec, &dummy);
- } while (status == 0);
+ } while (status == 0 && time_before(jiffies, timeout));
}
static int dspio_get_response_data(struct hda_codec *codec)
@@ -4520,7 +4522,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
val = 0;
/* If Voice Focus on SBZ, set to two channel. */
- if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ)
+ if ((nid == VOICE_FOCUS) && (spec->use_pci_mmio)
&& (spec->cur_mic_type != REAR_LINE_IN)) {
if (spec->effects_switch[CRYSTAL_VOICE -
EFFECT_START_NID]) {
@@ -4539,7 +4541,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
* For SBZ noise reduction, there's an extra command
* to module ID 0x47. No clue why.
*/
- if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ)
+ if ((nid == NOISE_REDUCTION) && (spec->use_pci_mmio)
&& (spec->cur_mic_type != REAR_LINE_IN)) {
if (spec->effects_switch[CRYSTAL_VOICE -
EFFECT_START_NID]) {
@@ -5855,8 +5857,8 @@ static int ca0132_build_controls(struct hda_codec *codec)
*/
num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
for (i = 0; i < num_fx; i++) {
- /* SBZ and R3D break if Echo Cancellation is used. */
- if (spec->quirk == QUIRK_SBZ || spec->quirk == QUIRK_R3D) {
+ /* Desktop cards break if Echo Cancellation is used. */
+ if (spec->use_pci_mmio) {
if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID +
OUT_EFFECTS_COUNT))
continue;
@@ -6753,12 +6755,14 @@ static void ca0132_process_dsp_response(struct hda_codec *codec,
struct ca0132_spec *spec = codec->spec;
codec_dbg(codec, "ca0132_process_dsp_response\n");
+ snd_hda_power_up_pm(codec);
if (spec->wait_scp) {
if (dspio_get_response_data(codec) >= 0)
spec->wait_scp = 0;
}
dspio_clear_response_queue(codec);
+ snd_hda_power_down_pm(codec);
}
static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
@@ -6769,11 +6773,10 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
/* Delay enabling the HP amp, to let the mic-detection
* state machine run.
*/
- cancel_delayed_work_sync(&spec->unsol_hp_work);
- schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
tbl = snd_hda_jack_tbl_get(codec, cb->nid);
if (tbl)
tbl->block_report = 1;
+ schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
}
static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
@@ -7407,12 +7410,25 @@ static void ca0132_reboot_notify(struct hda_codec *codec)
codec->patch_ops.free(codec);
}
+#ifdef CONFIG_PM
+static int ca0132_suspend(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ return 0;
+}
+#endif
+
static const struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
.free = ca0132_free,
.unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = ca0132_suspend,
+#endif
.reboot_notify = ca0132_reboot_notify,
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ae8fde4..5500dd43 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -923,6 +923,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index dd46354..1f6a036 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -379,9 +379,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0215:
case 0x10ec0233:
case 0x10ec0235:
- case 0x10ec0236:
case 0x10ec0255:
- case 0x10ec0256:
case 0x10ec0257:
case 0x10ec0282:
case 0x10ec0283:
@@ -393,6 +391,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0300:
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
break;
+ case 0x10ec0236:
+ case 0x10ec0256:
+ alc_write_coef_idx(codec, 0x36, 0x5757);
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
case 0x10ec0275:
alc_update_coef_idx(codec, 0xe, 0, 1<<0);
break;
@@ -421,6 +424,9 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0672:
alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
break;
+ case 0x10ec0623:
+ alc_update_coef_idx(codec, 0x19, 1<<13, 0);
+ break;
case 0x10ec0668:
alc_update_coef_idx(codec, 0x7, 3<<13, 0);
break;
@@ -507,6 +513,7 @@ static void alc_shutup_pins(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
switch (codec->core.vendor_id) {
+ case 0x10ec0283:
case 0x10ec0286:
case 0x10ec0288:
case 0x10ec0298:
@@ -2908,6 +2915,7 @@ enum {
ALC269_TYPE_ALC225,
ALC269_TYPE_ALC294,
ALC269_TYPE_ALC300,
+ ALC269_TYPE_ALC623,
ALC269_TYPE_ALC700,
};
@@ -2943,6 +2951,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC225:
case ALC269_TYPE_ALC294:
case ALC269_TYPE_ALC300:
+ case ALC269_TYPE_ALC623:
case ALC269_TYPE_ALC700:
ssids = alc269_ssids;
break;
@@ -3458,7 +3467,9 @@ static void alc294_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- if (!spec->done_hp_init) {
+ /* required only at boot or S4 resume time */
+ if (!spec->done_hp_init ||
+ codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) {
alc294_hp_init(codec);
spec->done_hp_init = true;
}
@@ -5251,6 +5262,17 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
}
}
+static void alc256_fixup_dell_xps_13_headphone_noise2(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 0, HDA_AMP_VOLMASK, 1);
+ snd_hda_override_wcaps(codec, 0x1a, get_wcaps(codec, 0x1a) & ~AC_WCAP_IN_AMP);
+}
+
static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
@@ -5420,6 +5442,16 @@ static void alc295_fixup_disable_dac3(struct hda_codec *codec,
}
}
+/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */
+static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ hda_nid_t conn[1] = { 0x02 };
+ snd_hda_override_conn_list(codec, 0x17, 1, conn);
+ }
+}
+
/* Hook to update amp GPIO4 for automute */
static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
struct hda_jack_callback *jack)
@@ -5633,12 +5665,14 @@ enum {
ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
ALC275_FIXUP_DELL_XPS,
ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
+ ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2,
ALC293_FIXUP_LENOVO_SPK_NOISE,
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DISABLE_MIC_VREF,
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC295_FIXUP_DISABLE_DAC3,
+ ALC285_FIXUP_SPEAKER2_TO_DAC1,
ALC280_FIXUP_HP_HEADSET_MIC,
ALC221_FIXUP_HP_FRONT_MIC,
ALC292_FIXUP_TPT460,
@@ -5680,8 +5714,12 @@ enum {
ALC256_FIXUP_ASUS_HEADSET_MIC,
ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
ALC299_FIXUP_PREDATOR_SPK,
- ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC,
ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE,
+ ALC289_FIXUP_DELL_SPK2,
+ ALC289_FIXUP_DUAL_SPK,
+ ALC294_FIXUP_SPK2_TO_DAC1,
+ ALC294_FIXUP_ASUS_DUAL_SPK,
+
};
static const struct hda_fixup alc269_fixups[] = {
@@ -6350,6 +6388,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
+ [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_dell_xps_13_headphone_noise2,
+ .chained = true,
+ .chain_id = ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE
+ },
[ALC293_FIXUP_LENOVO_SPK_NOISE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_disable_aamix,
@@ -6412,6 +6456,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc295_fixup_disable_dac3,
},
+ [ALC285_FIXUP_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ },
[ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -6719,16 +6767,6 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
}
},
- [ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x14, 0x411111f0 }, /* disable confusing internal speaker */
- { 0x19, 0x04a11150 }, /* use as headset mic, without its own jack detect */
- { }
- },
- .chained = true,
- .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
- },
[ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -6739,6 +6777,35 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
},
+ [ALC289_FIXUP_DELL_SPK2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170130 }, /* bass spk */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [ALC289_FIXUP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK2
+ },
+ [ALC294_FIXUP_SPK2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ /* The GPIO must be pulled to initialize the AMP */
+ .v.func = alc_fixup_gpio4,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_SPK2_TO_DAC1
+ },
+
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6792,17 +6859,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
- SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2),
SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP),
- SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2),
SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE2),
SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
@@ -6811,6 +6878,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -6898,7 +6967,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
- SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC),
@@ -6970,6 +7039,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -6978,6 +7048,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
@@ -7003,6 +7075,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
+ SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
#if 0
@@ -7150,6 +7223,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"},
{.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"},
{.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"},
+ {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"},
{.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"},
{.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"},
{.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"},
@@ -7761,6 +7835,9 @@ static int patch_alc269(struct hda_codec *codec)
spec->codec_variant = ALC269_TYPE_ALC300;
spec->gen.mixer_nid = 0; /* no loopback on ALC300 */
break;
+ case 0x10ec0623:
+ spec->codec_variant = ALC269_TYPE_ALC623;
+ break;
case 0x10ec0700:
case 0x10ec0701:
case 0x10ec0703:
@@ -8879,6 +8956,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269),
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 046705b..d8168aa 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -77,6 +77,7 @@ enum {
STAC_DELL_M6_BOTH,
STAC_DELL_EQ,
STAC_ALIENWARE_M17X,
+ STAC_ELO_VUPOINT_15MX,
STAC_92HD89XX_HP_FRONT_JACK,
STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK,
STAC_92HD73XX_ASUS_MOBO,
@@ -1879,6 +1880,18 @@ static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
codec->no_jack_detect = 1;
}
+
+static void stac92hd73xx_disable_automute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.suppress_auto_mute = 1;
+}
+
static const struct hda_fixup stac92hd73xx_fixups[] = {
[STAC_92HD73XX_REF] = {
.type = HDA_FIXUP_FUNC,
@@ -1904,6 +1917,10 @@ static const struct hda_fixup stac92hd73xx_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = stac92hd73xx_fixup_alienware_m17x,
},
+ [STAC_ELO_VUPOINT_15MX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_disable_automute,
+ },
[STAC_92HD73XX_INTEL] = {
.type = HDA_FIXUP_PINS,
.v.pins = intel_dg45id_pin_configs,
@@ -1942,6 +1959,7 @@ static const struct hda_model_fixup stac92hd73xx_models[] = {
{ .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
{ .id = STAC_DELL_EQ, .name = "dell-eq" },
{ .id = STAC_ALIENWARE_M17X, .name = "alienware" },
+ { .id = STAC_ELO_VUPOINT_15MX, .name = "elo-vupoint-15mx" },
{ .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" },
{}
};
@@ -1991,6 +2009,8 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
"Alienware M17x", STAC_ALIENWARE_M17X),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490,
"Alienware M17x R3", STAC_DELL_EQ),
+ SND_PCI_QUIRK(0x1059, 0x1011,
+ "ELO VuPoint 15MX", STAC_ELO_VUPOINT_15MX),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1927,
"HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17,
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 057c2f3..41ea8e7 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -661,6 +661,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
unsigned long flags;
unsigned char mclk_change;
unsigned int i, old_rate;
+ bool call_set_rate = false;
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
return -EINVAL;
@@ -684,7 +685,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
* setting clock rate for internal clock mode */
old_rate = ice->get_rate(ice);
if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
+ call_set_rate = true;
else if (rate == ice->cur_rate) {
spin_unlock_irqrestore(&ice->reg_lock, flags);
return 0;
@@ -692,12 +693,14 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
}
ice->cur_rate = rate;
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (call_set_rate)
+ ice->set_rate(ice, rate);
/* setting master clock */
mclk_change = ice->set_mclk(ice, rate);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
-
if (mclk_change && ice->gpio.i2s_mclk_changed)
ice->gpio.i2s_mclk_changed(ice);
if (ice->gpio.set_pro_rate)
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 943a726..c846291 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1171,16 +1171,6 @@ static int snd_intel8x0m_create(struct snd_card *card,
}
port_inited:
- if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, chip)) {
- dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0m_free(chip);
- return -EBUSY;
- }
- chip->irq = pci->irq;
- pci_set_master(pci);
- synchronize_irq(chip->irq);
-
/* initialize offsets */
chip->bdbars_count = 2;
tbl = intel_regs;
@@ -1224,11 +1214,21 @@ static int snd_intel8x0m_create(struct snd_card *card,
chip->int_sta_reg = ICH_REG_GLOB_STA;
chip->int_sta_mask = int_sta_masks;
+ pci_set_master(pci);
+
if ((err = snd_intel8x0m_chip_init(chip, 1)) < 0) {
snd_intel8x0m_free(chip);
return err;
}
+ if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ snd_intel8x0m_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_intel8x0m_free(chip);
return err;
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 8e3275a..e53b54d 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -42,7 +42,7 @@
#include "../codecs/da7219.h"
#include "../codecs/da7219-aad.h"
-#define CZ_PLAT_CLK 25000000
+#define CZ_PLAT_CLK 48000000
#define DUAL_CHANNEL 2
static struct snd_soc_jack cz_jack;
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 0981966..be24731 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1410,6 +1410,12 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev,
if (ret)
return ret;
+ /* Filter out 44.1, 88.2 and 176.4Khz */
+ rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_176400);
+ if (!rates)
+ return -EINVAL;
+
sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
hdmi_dais[i].name = devm_kstrdup(&hdev->dev,
dai_name, GFP_KERNEL);
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index c3b28b2..89b6e18 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2121,10 +2121,8 @@ static void max98090_pll_det_disable_work(struct work_struct *work)
M98090_IULK_MASK, 0);
}
-static void max98090_pll_work(struct work_struct *work)
+static void max98090_pll_work(struct max98090_priv *max98090)
{
- struct max98090_priv *max98090 =
- container_of(work, struct max98090_priv, pll_work);
struct snd_soc_component *component = max98090->component;
if (!snd_soc_component_is_active(component))
@@ -2277,7 +2275,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
if (active & M98090_ULK_MASK) {
dev_dbg(component->dev, "M98090_ULK_MASK\n");
- schedule_work(&max98090->pll_work);
+ max98090_pll_work(max98090);
}
if (active & M98090_JDET_MASK) {
@@ -2440,7 +2438,6 @@ static int max98090_probe(struct snd_soc_component *component)
max98090_pll_det_enable_work);
INIT_WORK(&max98090->pll_det_disable_work,
max98090_pll_det_disable_work);
- INIT_WORK(&max98090->pll_work, max98090_pll_work);
/* Enable jack detection */
snd_soc_component_write(component, M98090_REG_JACK_DETECT,
@@ -2493,7 +2490,6 @@ static void max98090_remove(struct snd_soc_component *component)
cancel_delayed_work_sync(&max98090->jack_work);
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
cancel_work_sync(&max98090->pll_det_disable_work);
- cancel_work_sync(&max98090->pll_work);
max98090->component = NULL;
}
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index b1572a2..388d2f7 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1533,7 +1533,6 @@ struct max98090_priv {
struct delayed_work jack_work;
struct delayed_work pll_det_enable_work;
struct work_struct pll_det_disable_work;
- struct work_struct pll_work;
struct snd_soc_jack *jack;
unsigned int dai_fmt;
int tdm_slots;
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 4ea3287..e51143d 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -248,17 +248,6 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int max9867_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
-
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
- return 0;
-}
-
static int max9867_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_component *component = dai->component;
@@ -361,7 +350,6 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
static const struct snd_soc_dai_ops max9867_dai_ops = {
.set_fmt = max9867_dai_set_fmt,
.set_sysclk = max9867_set_dai_sysclk,
- .prepare = max9867_prepare,
.digital_mute = max9867_mute,
.hw_params = max9867_dai_hw_params,
};
@@ -392,27 +380,59 @@ static struct snd_soc_dai_driver max9867_dai[] = {
}
};
-#ifdef CONFIG_PM_SLEEP
-static int max9867_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int max9867_suspend(struct snd_soc_component *component)
{
- struct max9867_priv *max9867 = dev_get_drvdata(dev);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- /* Drop down to power saving mode when system is suspended */
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK);
return 0;
}
-static int max9867_resume(struct device *dev)
+static int max9867_resume(struct snd_soc_component *component)
{
- struct max9867_priv *max9867 = dev_get_drvdata(dev);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
- regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
- MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
return 0;
}
+#else
+#define max9867_suspend NULL
+#define max9867_resume NULL
#endif
+static int max9867_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int err;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ err = regcache_sync(max9867->regmap);
+ if (err)
+ return err;
+
+ err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_SHTDOWN, MAX9867_SHTDOWN);
+ if (err)
+ return err;
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_SHTDOWN, 0);
+ if (err)
+ return err;
+
+ regcache_mark_dirty(max9867->regmap);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_component_driver max9867_component = {
.controls = max9867_snd_controls,
.num_controls = ARRAY_SIZE(max9867_snd_controls),
@@ -420,6 +440,9 @@ static const struct snd_soc_component_driver max9867_component = {
.num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
.dapm_widgets = max9867_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
+ .suspend = max9867_suspend,
+ .resume = max9867_resume,
+ .set_bias_level = max9867_set_bias_level,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
@@ -518,15 +541,10 @@ static const struct of_device_id max9867_of_match[] = {
};
MODULE_DEVICE_TABLE(of, max9867_of_match);
-static const struct dev_pm_ops max9867_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
-};
-
static struct i2c_driver max9867_i2c_driver = {
.driver = {
.name = "max9867",
.of_match_table = of_match_ptr(max9867_of_match),
- .pm = &max9867_pm_ops,
},
.probe = max9867_i2c_probe,
.id_table = max9867_i2c_id,
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
index 55cd997..d917085 100644
--- a/sound/soc/codecs/max9867.h
+++ b/sound/soc/codecs/max9867.h
@@ -67,7 +67,7 @@
#define MAX9867_MICCONFIG 0x15
#define MAX9867_MODECONFIG 0x16
#define MAX9867_PWRMAN 0x17
-#define MAX9867_SHTDOWN_MASK (1<<7)
+#define MAX9867_SHTDOWN 0x80
#define MAX9867_REVISION 0xff
#define MAX9867_CACHEREGNUM 10
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index b7cf7cc..4f0497a 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -303,7 +303,7 @@ struct pm8916_wcd_analog_priv {
};
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
-static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" };
+static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
static const char *const hph_text[] = { "ZERO", "Switch", };
static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
@@ -318,7 +318,7 @@ static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
/* RDAC2 MUX */
static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
- CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text);
+ CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
static const struct snd_kcontrol_new spkr_switch[] = {
SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index e3c8cd1..4dd1a60 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -585,7 +585,7 @@ static int nau8540_calc_fll_param(unsigned int fll_in,
fvco_max = 0;
fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
fvco_max < fvco) {
fvco_max = fvco;
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index e3de1ff..439e402 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -24,8 +24,7 @@
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S24_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 1dc70f4..7e3b47ee 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3307,6 +3307,9 @@ static void rt5645_jack_detect_work(struct work_struct *work)
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
return;
+ case 4:
+ val = snd_soc_component_read32(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
+ break;
default: /* read rt5645 jd1_1 status */
val = snd_soc_component_read32(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000;
break;
@@ -3634,7 +3637,7 @@ static const struct rt5645_platform_data intel_braswell_platform_data = {
static const struct rt5645_platform_data buddy_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
- .jd_mode = 3,
+ .jd_mode = 4,
.level_trigger_irq = true,
};
@@ -3662,6 +3665,11 @@ static const struct rt5645_platform_data jd_mode3_platform_data = {
.jd_mode = 3,
};
+static const struct rt5645_platform_data lattepanda_board_platform_data = {
+ .jd_mode = 2,
+ .inv_jd1_1 = true
+};
+
static const struct dmi_system_id dmi_platform_data[] = {
{
.ident = "Chrome Buddy",
@@ -3759,6 +3767,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&intel_braswell_platform_data,
},
+ {
+ .ident = "LattePanda board",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
+ },
+ .driver_data = (void *)&lattepanda_board_platform_data,
+ },
{ }
};
@@ -4016,6 +4033,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
RT5645_JD1_MODE_1);
break;
case 3:
+ case 4:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 9b7a183..71b7b88 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -297,6 +297,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 6f5dac0..7a78bb0 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -68,6 +68,7 @@ struct rt5682_priv {
static const struct reg_sequence patch_list[] = {
{0x01c1, 0x1000},
+ {RT5682_DAC_ADC_DIG_VOL1, 0xa020},
};
static const struct reg_default rt5682_reg[] = {
@@ -982,6 +983,16 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ rt5682->hs_jack = hs_jack;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ return 0;
+ }
+
switch (rt5682->pdata.jd_src) {
case RT5682_JD1:
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2,
@@ -1019,8 +1030,6 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
break;
}
- rt5682->hs_jack = hs_jack;
-
return 0;
}
@@ -1449,6 +1458,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
RT5682_NG2_EN_MASK, RT5682_NG2_EN);
snd_soc_component_update_bits(component,
RT5682_DEPOP_1, 0x60, 0x60);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -1456,6 +1467,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
RT5682_DEPOP_1, 0x60, 0x0);
snd_soc_component_write(component,
RT5682_HP_CTRL_2, 0x0000);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000);
break;
default:
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 64a52d4..896412d 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1387,7 +1387,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component)
* Searching for a suitable index solving this formula:
* idx = 40 * log10(vag_val / lo_cagcntrl) + 15
*/
- vol_quot = (vag * 100) / lo_vag;
+ vol_quot = lo_vag ? (vag * 100) / lo_vag : 0;
lo_vol = 0;
for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
if (vol_quot >= vol_quot_table[i])
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index deff6516..0a3b746 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2413,6 +2413,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
err_reset:
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
@@ -2429,12 +2431,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index ba89d9d..b793701 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2620,6 +2620,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
@@ -2643,6 +2644,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 1965635..d14e851 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1902,6 +1902,7 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_BIAS_ENA, 0);
+ snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0);
regcache_cache_only(wm8904->regmap, true);
regcache_mark_dirty(wm8904->regmap);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index efd8910..dde015f 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2792,7 +2792,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
if (target % Fref == 0) {
fll_div->theta = 0;
- fll_div->lambda = 0;
+ fll_div->lambda = 1;
} else {
gcd_fll = gcd(target, fratio * Fref);
@@ -2862,7 +2862,7 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
return -EINVAL;
}
- if (fll_div.theta || fll_div.lambda)
+ if (fll_div.theta)
fll1 |= WM8962_FLL_FRAC;
/* Stop the FLL while we reconfigure */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 14f1b0c0..01acb8d 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -537,13 +537,10 @@ static SOC_ENUM_SINGLE_DECL(dac_osr,
static SOC_ENUM_SINGLE_DECL(adc_osr,
WM8994_OVERSAMPLING, 1, osr_text);
-static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+static const struct snd_kcontrol_new wm8994_common_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
- WM8994_AIF1_ADC2_RIGHT_VOLUME,
- 1, 119, 0, digital_tlv),
SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME,
WM8994_AIF2_ADC_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
@@ -560,8 +557,6 @@ SOC_ENUM("AIF2DACR Source", aif2dacr_src),
SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
- WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME,
WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
@@ -569,17 +564,12 @@ SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv),
SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv),
SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0),
-SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0),
WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2),
WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1),
WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0),
-WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
-WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
-WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
-
WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2),
WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1),
WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0),
@@ -598,9 +588,6 @@ SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0),
SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf),
SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0),
-SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
-SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
-
SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf),
SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0),
@@ -641,6 +628,24 @@ SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2,
8, 1, 0),
};
+/* Controls not available on WM1811 */
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+ WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+ WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
+SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
+};
+
static const struct snd_kcontrol_new wm8994_eq_controls[] = {
SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
eq_tlv),
@@ -4262,13 +4267,15 @@ static int wm8994_component_probe(struct snd_soc_component *component)
wm8994_handle_pdata(wm8994);
wm_hubs_add_analogue_controls(component);
- snd_soc_add_component_controls(component, wm8994_snd_controls,
- ARRAY_SIZE(wm8994_snd_controls));
+ snd_soc_add_component_controls(component, wm8994_common_snd_controls,
+ ARRAY_SIZE(wm8994_common_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
ARRAY_SIZE(wm8994_dapm_widgets));
switch (control->type) {
case WM8994:
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
ARRAY_SIZE(wm8994_specific_dapm_widgets));
if (control->revision < 4) {
@@ -4288,8 +4295,10 @@ static int wm8994_component_probe(struct snd_soc_component *component)
}
break;
case WM8958:
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
snd_soc_add_component_controls(component, wm8958_snd_controls,
- ARRAY_SIZE(wm8958_snd_controls));
+ ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
if (control->revision < 1) {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index ee85056..b114fc7 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1147,8 +1147,7 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
}
if (in) {
- if (in & WMFW_CTL_FLAG_READABLE)
- out |= rd;
+ out |= rd;
if (in & WMFW_CTL_FLAG_WRITEABLE)
out |= wr;
if (in & WMFW_CTL_FLAG_VOLATILE)
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index b6dc524..e58240e 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -414,10 +414,12 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
},
- .driver_data = (void *)(BYT_RT5640_IN1_MAP |
- BYT_RT5640_MCLK_EN |
- BYT_RT5640_SSP0_AIF1),
-
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{
.matches = {
@@ -675,13 +677,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_MCLK_EN),
},
{
+ /* Teclast X89 */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
},
.driver_data = (void *)(BYT_RT5640_IN3_MAP |
- BYT_RT5640_MCLK_EN |
- BYT_RT5640_SSP0_AIF1),
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_1P0 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{ /* Toshiba Satellite Click Mini L9W-B */
.matches = {
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index b8a03f5..f36e33a 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -423,6 +423,9 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
dmic_constraints);
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
}
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 9a2777b..4395bb7 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -563,10 +563,6 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
- err = clk_prepare_enable(priv->clk);
- if (err < 0)
- return err;
-
priv->extclk = devm_clk_get(&pdev->dev, "extclk");
if (IS_ERR(priv->extclk)) {
if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
@@ -582,6 +578,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
}
}
+ err = clk_prepare_enable(priv->clk);
+ if (err < 0)
+ return err;
+
/* Some sensible defaults - this reflects the powerup values */
priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
@@ -595,7 +595,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
}
- err = devm_snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
+ err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
soc_dai, 2);
if (err) {
dev_err(&pdev->dev, "snd_soc_register_component failed\n");
@@ -618,6 +618,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
{
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (!IS_ERR(priv->extclk))
clk_disable_unprepare(priv->extclk);
clk_disable_unprepare(priv->clk);
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index 3026255..0e4f65e 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -203,6 +203,8 @@ static int axg_fifo_pcm_open(struct snd_pcm_substream *ss)
ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0,
dev_name(dev), ss);
+ if (ret)
+ return ret;
/* Enable pclk to access registers and clock the fifo ip */
ret = clk_prepare_enable(fifo->pclk);
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 9db9a29..c1a7d37 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -319,10 +319,11 @@ static int q6asm_dai_open(struct snd_pcm_substream *substream)
prtd->audio_client = q6asm_audio_client_alloc(dev,
(q6asm_cb)event_handler, prtd, stream_id,
LEGACY_PCM_MODE);
- if (!prtd->audio_client) {
+ if (IS_ERR(prtd->audio_client)) {
pr_info("%s: Could not allocate memory\n", __func__);
+ ret = PTR_ERR(prtd->audio_client);
kfree(prtd);
- return -ENOMEM;
+ return ret;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 11399f8..b86f76c 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -677,7 +677,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
ret = rockchip_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
- return ret;
+ goto err_suspend;
}
return 0;
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index ce00fe2..d4bde48 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -604,6 +604,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct i2s_dai *i2s = to_info(dai);
+ struct i2s_dai *other = get_other_dai(i2s);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 mod, tmp = 0;
unsigned long flags;
@@ -661,7 +662,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
* CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
* clock configuration assigned in DT is not overwritten.
*/
- if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
+ if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL &&
+ other->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
@@ -699,6 +701,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = to_info(dai);
+ struct i2s_dai *other = get_other_dai(i2s);
u32 mod, mask = 0, val = 0;
struct clk *rclksrc;
unsigned long flags;
@@ -784,6 +787,9 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
i2s->frmclk = params_rate(params);
rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+ if (!rclksrc || IS_ERR(rclksrc))
+ rclksrc = other->clk_table[CLK_I2S_RCLK_SRC];
+
if (rclksrc && !IS_ERR(rclksrc))
i2s->rclk_srcrate = clk_get_rate(rclksrc);
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 15a3182..4e6ecdc 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -1344,6 +1344,18 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
};
int ret;
+ /*
+ * 1) Avoid duplicate register for DVC with MIX case
+ * 2) Allow duplicate register for MIX
+ * 3) re-register if card was rebinded
+ */
+ list_for_each_entry(kctrl, &card->controls, list) {
+ struct rsnd_kctrl_cfg *c = kctrl->private_data;
+
+ if (c == cfg)
+ return 0;
+ }
+
if (size > RSND_MAX_CHANNELS)
return -EINVAL;
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 2b16e0c..024ece46 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -40,11 +40,8 @@ struct rsnd_dvc {
struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */
struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */
struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
- u32 flags;
};
-#define KCTRL_INITIALIZED (1 << 0)
-
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
@@ -227,9 +224,6 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
int channels = rsnd_rdai_channels_get(rdai);
int ret;
- if (rsnd_flags_has(dvc, KCTRL_INITIALIZED))
- return 0;
-
/* Volume */
ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
@@ -285,8 +279,6 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- rsnd_flags_set(dvc, KCTRL_INITIALIZED);
-
return 0;
}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 8f7a0ab..f64c705 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -438,6 +438,7 @@ struct rsnd_dai_stream {
char name[RSND_DAI_NAME_SIZE];
struct snd_pcm_substream *substream;
struct rsnd_mod *mod[RSND_MOD_MAX];
+ struct rsnd_mod *dma;
struct rsnd_dai *rdai;
struct device *dmac_dev; /* for IPMMU */
u32 parent_ssi_status;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 9410e0a..33dc8d6 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -72,7 +72,6 @@
struct rsnd_ssi {
struct rsnd_mod mod;
- struct rsnd_mod *dma;
u32 flags;
u32 cr_own;
@@ -873,7 +872,6 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int ret;
/*
@@ -888,7 +886,7 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
return ret;
/* SSI probe might be called many times in MUX multi path */
- ret = rsnd_dma_attach(io, mod, &ssi->dma);
+ ret = rsnd_dma_attach(io, mod, &io->dma);
return ret;
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b9ad15f..7cdca89 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3524,7 +3524,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
break;
case snd_soc_dapm_pinctrl:
w->pinctrl = devm_pinctrl_get(dapm->dev);
- if (IS_ERR_OR_NULL(w->pinctrl)) {
+ if (IS_ERR(w->pinctrl)) {
ret = PTR_ERR(w->pinctrl);
if (ret == -EPROBE_DEFER)
return ERR_PTR(ret);
@@ -3694,7 +3694,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
struct snd_pcm_hw_params *params = NULL;
struct snd_pcm_runtime *runtime = NULL;
unsigned int fmt;
- int ret;
+ int ret = 0;
if (WARN_ON(!config) ||
WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index c7b990a..2a528e7 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -100,10 +100,9 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
unsigned int sync = 0;
int enable;
- trace_snd_soc_jack_report(jack, mask, status);
-
if (!jack)
return;
+ trace_snd_soc_jack_report(jack, mask, status);
dapm = &jack->card->dapm;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index dc0c00a..615844f 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1797,7 +1797,7 @@ static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
struct snd_soc_pcm_stream *stream)
{
runtime->hw.rate_min = stream->rate_min;
- runtime->hw.rate_max = stream->rate_max;
+ runtime->hw.rate_max = min_not_zero(stream->rate_max, UINT_MAX);
runtime->hw.channels_min = stream->channels_min;
runtime->hw.channels_max = stream->channels_max;
if (runtime->hw.formats)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 88a7e86..069f38f 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1890,6 +1890,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
int count = hdr->count;
int i;
bool abi_match;
+ int ret;
if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
return 0;
@@ -1926,7 +1927,12 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
}
/* create the FE DAIs and DAI links */
- soc_tplg_pcm_create(tplg, _pcm);
+ ret = soc_tplg_pcm_create(tplg, _pcm);
+ if (ret < 0) {
+ if (!abi_match)
+ kfree(_pcm);
+ return ret;
+ }
/* offset by version-specific struct size and
* real priv data size
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 6d0bf78..aa2b119 100644
--- a/sound/soc/stm/stm32_i2s.c
+++ b/sound/soc/stm/stm32_i2s.c
@@ -246,8 +246,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid)
return IRQ_NONE;
}
- regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
- I2S_IFCR_MASK, flags);
+ regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+ I2S_IFCR_MASK, flags);
if (flags & I2S_SR_OVR) {
dev_dbg(&pdev->dev, "Overrun\n");
@@ -276,7 +276,6 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
case STM32_I2S_CFG2_REG:
case STM32_I2S_IER_REG:
case STM32_I2S_SR_REG:
- case STM32_I2S_IFCR_REG:
case STM32_I2S_TXDR_REG:
case STM32_I2S_RXDR_REG:
case STM32_I2S_CGFR_REG:
@@ -488,7 +487,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai,
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
int format = params_width(params);
- u32 cfgr, cfgr_mask, cfg1, cfg1_mask;
+ u32 cfgr, cfgr_mask, cfg1;
unsigned int fthlv;
int ret;
@@ -501,7 +500,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai,
switch (format) {
case 16:
cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16);
- cfgr_mask = I2S_CGFR_DATLEN_MASK;
+ cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN;
break;
case 32:
cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) |
@@ -529,15 +528,11 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai,
if (ret < 0)
return ret;
- cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN;
- cfg1_mask = cfg1;
-
fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4;
- cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1);
- cfg1_mask |= I2S_CFG1_FTHVL_MASK;
+ cfg1 = I2S_CFG1_FTHVL_SET(fthlv - 1);
return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG,
- cfg1_mask, cfg1);
+ I2S_CFG1_FTHVL_MASK, cfg1);
}
static int stm32_i2s_startup(struct snd_pcm_substream *substream,
@@ -551,8 +546,8 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream,
i2s->refcount++;
spin_unlock(&i2s->lock_fd);
- return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
- I2S_IFCR_MASK, I2S_IFCR_MASK);
+ return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+ I2S_IFCR_MASK, I2S_IFCR_MASK);
}
static int stm32_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -589,6 +584,10 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
/* Enable i2s */
dev_dbg(cpu_dai->dev, "start I2S\n");
+ cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN;
+ regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG,
+ cfg1_mask, cfg1_mask);
+
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
I2S_CR1_SPE, I2S_CR1_SPE);
if (ret < 0) {
@@ -603,8 +602,8 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
- regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
- I2S_IFCR_MASK, I2S_IFCR_MASK);
+ regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+ I2S_IFCR_MASK, I2S_IFCR_MASK);
if (playback_flg) {
ier = I2S_IER_UDRIE;
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index f226542..540c4a0 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -112,16 +112,21 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
if (!sai_provider) {
dev_err(&sai_client->pdev->dev,
"SAI sync provider data not found\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_put_dev;
}
/* Configure sync client */
ret = stm32_sai_sync_conf_client(sai_client, synci);
if (ret < 0)
- return ret;
+ goto out_put_dev;
/* Configure sync provider */
- return stm32_sai_sync_conf_provider(sai_provider, synco);
+ ret = stm32_sai_sync_conf_provider(sai_provider, synco);
+
+out_put_dev:
+ put_device(&pdev->dev);
+ return ret;
}
static int stm32_sai_probe(struct platform_device *pdev)
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index 2fb2b914..6c2e69e 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -994,6 +994,16 @@ static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream,
return 0;
}
+/* No support of mmap in S/PDIF mode */
+static const struct snd_pcm_hardware stm32_sai_pcm_hw_spdif = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .buffer_bytes_max = 8 * PAGE_SIZE,
+ .period_bytes_min = 1024,
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 8,
+};
+
static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
.buffer_bytes_max = 8 * PAGE_SIZE,
@@ -1050,7 +1060,7 @@ static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
};
static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config_spdif = {
- .pcm_hardware = &stm32_sai_pcm_hw,
+ .pcm_hardware = &stm32_sai_pcm_hw_spdif,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.process = stm32_sai_pcm_process_spdif,
};
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c
index 45a4aa9..901457da 100644
--- a/sound/soc/tegra/tegra_sgtl5000.c
+++ b/sound/soc/tegra/tegra_sgtl5000.c
@@ -149,14 +149,14 @@ static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Property 'nvidia,i2s-controller' missing/invalid\n");
ret = -EINVAL;
- goto err;
+ goto err_put_codec_of_node;
}
tegra_sgtl5000_dai.platform_of_node = tegra_sgtl5000_dai.cpu_of_node;
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
if (ret)
- goto err;
+ goto err_put_cpu_of_node;
ret = snd_soc_register_card(card);
if (ret) {
@@ -169,6 +169,13 @@ static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
err_fini_utils:
tegra_asoc_utils_fini(&machine->util_data);
+err_put_cpu_of_node:
+ of_node_put(tegra_sgtl5000_dai.cpu_of_node);
+ tegra_sgtl5000_dai.cpu_of_node = NULL;
+ tegra_sgtl5000_dai.platform_of_node = NULL;
+err_put_codec_of_node:
+ of_node_put(tegra_sgtl5000_dai.codec_of_node);
+ tegra_sgtl5000_dai.codec_of_node = NULL;
err:
return ret;
}
@@ -183,6 +190,12 @@ static int tegra_sgtl5000_driver_remove(struct platform_device *pdev)
tegra_asoc_utils_fini(&machine->util_data);
+ of_node_put(tegra_sgtl5000_dai.cpu_of_node);
+ tegra_sgtl5000_dai.cpu_of_node = NULL;
+ tegra_sgtl5000_dai.platform_of_node = NULL;
+ of_node_put(tegra_sgtl5000_dai.codec_of_node);
+ tegra_sgtl5000_dai.codec_of_node = NULL;
+
return ret;
}
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 0414ddf2..c39d66d 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -16,7 +16,8 @@
power.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ validate.o
snd-usbmidi-lib-objs := midi.o
diff --git a/sound/usb/card.h b/sound/usb/card.h
index baa3887..1282799a 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -142,6 +142,7 @@ struct snd_usb_substream {
struct snd_usb_endpoint *sync_endpoint;
unsigned long flags;
bool need_setup_ep; /* (re)configure EP at prepare? */
+ bool need_setup_fmt; /* (re)configure fmt after resume? */
unsigned int speed; /* USB_SPEED_XXX */
u64 formats; /* format bitmasks (all or'ed) */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index db5e39d..e313498 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -52,39 +52,37 @@ static void *find_uac_clock_desc(struct usb_host_interface *iface, int id,
static bool validate_clock_source_v2(void *p, int id)
{
struct uac_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_source_v3(void *p, int id)
{
struct uac3_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v2(void *p, int id)
{
struct uac_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 7 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v3(void *p, int id)
{
struct uac3_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 11 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v2(void *p, int id)
{
struct uac_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v3(void *p, int id)
{
struct uac3_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
#define DEFINE_FIND_HELPER(name, obj, validator, type) \
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index d86be8b..aeb74cc 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -401,6 +401,9 @@ static void snd_complete_urb(struct urb *urb)
}
prepare_outbound_urb(ep, ctx);
+ /* can be stopped during prepare callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
} else {
retire_inbound_urb(ep, ctx);
/* can be stopped during retire callback */
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index d338bd0..f5b4c66 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -30,4 +30,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
}
+/* in validate.c */
+bool snd_usb_validate_audio_desc(void *p, int protocol);
+bool snd_usb_validate_midi_desc(void *p);
+
#endif /* __USBAUDIO_HELPER_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 0b902bd..606e316 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -755,13 +755,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
{
int mu_channels;
- if (desc->bLength < sizeof(*desc))
- return -EINVAL;
- if (!desc->bNrInPins)
- return -EINVAL;
- if (desc->bLength < sizeof(*desc) + desc->bNrInPins)
- return -EINVAL;
-
switch (state->mixer->protocol) {
case UAC_VERSION_1:
case UAC_VERSION_2:
@@ -780,18 +773,182 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
}
/*
+ * Parse Input Terminal Unit
+ */
+static int __check_input_term(struct mixer_build *state, int id,
+ struct usb_audio_term *term);
+
+static int parse_term_uac1_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_input_terminal_descriptor *d = p1;
+
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le16_to_cpu(d->wChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
+
+static int parse_term_uac2_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac2_input_terminal_descriptor *d = p1;
+ int err;
+
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
+
+static int parse_term_uac3_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_input_terminal_descriptor *d = p1;
+ int err;
+
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+
+ err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
+ if (err < 0)
+ return err;
+ term->channels = err;
+
+ /* REVISIT: UAC3 IT doesn't have channels cfg */
+ term->chconfig = 0;
+
+ term->name = le16_to_cpu(d->wTerminalDescrStr);
+ return 0;
+}
+
+static int parse_term_mixer_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_mixer_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
+
+ err = uac_mixer_unit_get_channels(state, d);
+ if (err <= 0)
+ return err;
+
+ term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
+ term->channels = err;
+ if (protocol != UAC_VERSION_3) {
+ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+ term->name = uac_mixer_unit_iMixer(d);
+ }
+ return 0;
+}
+
+static int parse_term_selector_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_selector_unit_descriptor *d = p1;
+ int err;
+
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
+ term->id = id;
+ if (state->mixer->protocol != UAC_VERSION_3)
+ term->name = uac_selector_unit_iSelector(d);
+ return 0;
+}
+
+static int parse_term_proc_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id, int vtype)
+{
+ struct uac_processing_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
+
+ if (d->bNrInPins) {
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ }
+
+ term->type = vtype << 16; /* virtual type */
+ term->id = id;
+
+ if (protocol == UAC_VERSION_3)
+ return 0;
+
+ if (!term->channels) {
+ term->channels = uac_processing_unit_bNrChannels(d);
+ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+ }
+ term->name = uac_processing_unit_iProcessing(d, protocol);
+ return 0;
+}
+
+static int parse_term_uac2_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_clock_source_descriptor *d = p1;
+
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = d->iClockSource;
+ return 0;
+}
+
+static int parse_term_uac3_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = le16_to_cpu(d->wClockSourceStr);
+ return 0;
+}
+
+#define PTYPE(a, b) ((a) << 8 | (b))
+
+/*
* parse the source unit recursively until it reaches to a terminal
* or a branched unit.
*/
static int __check_input_term(struct mixer_build *state, int id,
- struct usb_audio_term *term)
+ struct usb_audio_term *term)
{
int protocol = state->mixer->protocol;
- int err;
void *p1;
unsigned char *hdr;
- memset(term, 0, sizeof(*term));
for (;;) {
/* a loop in the terminal chain? */
if (test_and_set_bit(id, state->termbitmap))
@@ -800,202 +957,58 @@ static int __check_input_term(struct mixer_build *state, int id,
p1 = find_audio_control_unit(state, id);
if (!p1)
break;
+ if (!snd_usb_validate_audio_desc(p1, protocol))
+ break; /* bad descriptor */
hdr = p1;
term->id = id;
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL:
- if (protocol == UAC_VERSION_1) {
- struct uac_input_terminal_descriptor *d = p1;
+ switch (PTYPE(protocol, hdr[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): {
+ /* the header is the same for all versions */
+ struct uac_feature_unit_descriptor *d = p1;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le16_to_cpu(d->wChannelConfig);
- term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
- struct uac2_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
-
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- }
- return 0;
- case UAC_FEATURE_UNIT: {
- /* the header is the same for v1 and v2 */
- struct uac_feature_unit_descriptor *d = p1;
-
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
-
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
- term->channels = uac_mixer_unit_bNrChannels(d);
- term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
- term->name = uac_mixer_unit_iMixer(d);
- return 0;
- }
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
- return 0;
- }
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT */
- if (protocol == UAC_VERSION_1)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- else /* UAC_VERSION_2 */
- term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 */
- if (protocol == UAC_VERSION_1 && !term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
- else if (protocol == UAC_VERSION_2 && !term->type)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
-
- if (protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
- return 0;
- }
-
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- if (!term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
-
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
- term->name = uac_processing_unit_iProcessing(d, protocol);
- return 0;
- }
- case UAC2_CLOCK_SOURCE: {
- struct uac_clock_source_descriptor *d = p1;
-
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = d->iClockSource;
- return 0;
- }
- default:
- return -ENODEV;
- }
- } else { /* UAC_VERSION_3 */
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL: {
- struct uac3_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
-
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
-
- err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
- if (err < 0)
- return err;
- term->channels = err;
-
- /* REVISIT: UAC3 IT doesn't have channels cfg */
- term->chconfig = 0;
-
- term->name = le16_to_cpu(d->wTerminalDescrStr);
- return 0;
- }
- case UAC3_FEATURE_UNIT: {
- struct uac3_feature_unit_descriptor *d = p1;
-
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC3_CLOCK_SOURCE: {
- struct uac3_clock_source_descriptor *d = p1;
-
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = le16_to_cpu(d->wClockSourceStr);
- return 0;
- }
- case UAC3_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
-
- err = uac_mixer_unit_get_channels(state, d);
- if (err <= 0)
- return err;
-
- term->channels = err;
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
-
- return 0;
- }
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
-
- return 0;
- }
- case UAC3_PROCESSING_UNIT: {
- struct uac_processing_unit_descriptor *d = p1;
-
- if (!d->bNrInPins)
- return -EINVAL;
-
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
-
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
-
- return 0;
- }
- default:
- return -ENODEV;
- }
+ id = d->bSourceID;
+ break; /* continue to parse */
+ }
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ return parse_term_uac1_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ return parse_term_uac2_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_term_uac3_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_term_mixer_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_term_selector_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_PROCESSING_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_EFFECT_UNIT);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_EXTENSION_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ return parse_term_uac2_clock_source(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_term_uac3_clock_source(state, term, p1, id);
+ default:
+ return -ENODEV;
}
}
return -ENODEV;
@@ -1039,10 +1052,15 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
};
+static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval)
+{
+ kfree(cval);
+}
+
/* private_free callback */
void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
{
- kfree(kctl->private_data);
+ usb_mixer_elem_info_free(kctl->private_data);
kctl->private_data = NULL;
}
@@ -1226,7 +1244,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
if (cval->min + cval->res < cval->max) {
int last_valid_res = cval->res;
int saved, test, check;
- get_cur_mix_raw(cval, minchn, &saved);
+ if (get_cur_mix_raw(cval, minchn, &saved) < 0)
+ goto no_res_check;
for (;;) {
test = saved;
if (test < cval->max)
@@ -1246,6 +1265,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
}
+no_res_check:
cval->initialized = 1;
}
@@ -1567,7 +1587,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
ctl_info = get_feature_control_info(control);
if (!ctl_info) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
if (mixer->protocol == UAC_VERSION_1)
@@ -1600,7 +1620,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -1770,7 +1790,7 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
get_connector_control_name(mixer, term, is_input, kctl->id.name,
@@ -1791,13 +1811,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
if (state->mixer->protocol != UAC_VERSION_2)
return -EINVAL;
- if (hdr->bLength != sizeof(*hdr)) {
- usb_audio_dbg(state->chip,
- "Bogus clock source descriptor length of %d, ignoring.\n",
- hdr->bLength);
- return 0;
- }
-
/*
* The only property of this unit we are interested in is the
* clock source validity. If that isn't readable, just bail out.
@@ -1823,7 +1836,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
@@ -1856,62 +1869,20 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
__u8 *bmaControls;
if (state->mixer->protocol == UAC_VERSION_1) {
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = hdr->bControlSize;
- if (!csize) {
- usb_audio_dbg(state->chip,
- "unit %u: invalid bControlSize == 0\n",
- unitid);
- return -EINVAL;
- }
channels = (hdr->bLength - 7) / csize - 1;
bmaControls = hdr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 6) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 6 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else { /* UAC_VERSION_3 */
struct uac3_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (ftr->bLength - 7) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
}
/* parse the source unit */
@@ -2089,7 +2060,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2115,15 +2086,11 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
- if (d_v2->bLength < sizeof(*d_v2))
- return -EINVAL;
control = UAC2_TE_CONNECTOR;
term_id = d_v2->bTerminalID;
bmctls = le16_to_cpu(d_v2->bmControls);
} else if (state->mixer->protocol == UAC_VERSION_3) {
struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
- if (d_v3->bLength < sizeof(*d_v3))
- return -EINVAL;
control = UAC3_TE_INSERTION;
term_id = d_v3->bTerminalID;
bmctls = le32_to_cpu(d_v3->bmControls);
@@ -2385,18 +2352,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
const char *name = extension_unit ?
"Extension Unit" : "Processing Unit";
- if (desc->bLength < 13) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
num_ins = desc->bNrInPins;
- if (desc->bLength < 13 + num_ins ||
- desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
for (i = 0; i < num_ins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2487,7 +2443,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2625,7 +2581,7 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
if (kctl->private_data) {
struct usb_mixer_elem_info *cval = kctl->private_data;
num_ins = cval->max;
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
kctl->private_data = NULL;
}
if (kctl->private_value) {
@@ -2651,13 +2607,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
const struct usbmix_name_map *map;
char **namelist;
- if (desc->bLength < 5 || !desc->bNrInPins ||
- desc->bLength < 5 + desc->bNrInPins) {
- usb_audio_err(state->chip,
- "invalid SELECTOR UNIT descriptor %d\n", unitid);
- return -EINVAL;
- }
-
for (i = 0; i < desc->bNrInPins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2697,10 +2646,10 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
break;
}
- namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
+ namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
if (!namelist) {
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_cval;
}
#define MAX_ITEM_NAME_LEN 64
for (i = 0; i < desc->bNrInPins; i++) {
@@ -2708,11 +2657,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
len = 0;
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
if (!namelist[i]) {
- while (i--)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
len = check_mapped_selector_name(state, unitid, i, namelist[i],
MAX_ITEM_NAME_LEN);
@@ -2726,11 +2672,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- for (i = 0; i < desc->bNrInPins; i++)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
kctl->private_value = (unsigned long)namelist;
kctl->private_free = usb_mixer_selector_elem_free;
@@ -2776,6 +2719,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
cval->head.id, kctl->id.name, desc->bNrInPins);
return snd_usb_mixer_add_control(&cval->head, kctl);
+
+ error_name:
+ for (i = 0; i < desc->bNrInPins; i++)
+ kfree(namelist[i]);
+ kfree(namelist);
+ error_cval:
+ usb_mixer_elem_info_free(cval);
+ return err;
}
/*
@@ -2796,62 +2747,49 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- case UAC_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC2_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_processing_unit(state, unitid, p1);
- else
- return 0; /* FIXME - effect units not implemented yet */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_extension_unit(state, unitid, p1);
- else /* UAC_VERSION_2 */
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC2_EXTENSION_UNIT_V2:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
- } else { /* UAC_VERSION_3 */
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- case UAC3_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC3_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC3_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC3_EFFECT_UNIT:
- return 0; /* FIXME - effect units not implemented yet */
- case UAC3_PROCESSING_UNIT:
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC3_EXTENSION_UNIT:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
+ if (!snd_usb_validate_audio_desc(p1, protocol)) {
+ usb_audio_dbg(state->chip, "invalid unit %d\n", unitid);
+ return 0; /* skip invalid unit */
+ }
+
+ switch (PTYPE(protocol, p1[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_audio_input_terminal(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_clock_source_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_audio_selector_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT):
+ return parse_audio_feature_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_audio_processing_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_audio_extension_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return 0; /* FIXME - effect units not implemented yet */
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n",
+ unitid, p1[2]);
+ return -EINVAL;
}
}
@@ -3013,6 +2951,9 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
continue;
iface = usb_ifnum_to_if(dev, intf);
+ if (!iface)
+ continue;
+
num = iface->num_altsetting;
if (num < 2)
@@ -3166,11 +3107,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen,
p, UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (!snd_usb_validate_audio_desc(p, mixer->protocol))
+ continue; /* skip invalid descriptor */
+
if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3182,8 +3124,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
} else if (mixer->protocol == UAC_VERSION_2) {
struct uac2_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3209,8 +3149,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
} else { /* UAC_VERSION_3 */
struct uac3_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 5e67b99..1e28280 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -578,15 +578,15 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
if (WARN_ON(!iface))
return -EINVAL;
alts = usb_altnum_to_altsetting(iface, fmt->altsetting);
- altsd = get_iface_desc(alts);
- if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+ if (WARN_ON(!alts))
return -EINVAL;
+ altsd = get_iface_desc(alts);
- if (fmt == subs->cur_audiofmt)
+ if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt)
return 0;
/* close the old interface */
- if (subs->interface >= 0 && subs->interface != fmt->iface) {
+ if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) {
if (!subs->stream->chip->keep_iface) {
err = usb_set_interface_timeout(subs->dev,
subs->interface, 0, MAX_SETALT_TIMEOUT_MS);
@@ -601,6 +601,9 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->altset_idx = 0;
}
+ if (subs->need_setup_fmt)
+ subs->need_setup_fmt = false;
+
/* set interface */
if (iface->cur_altsetting != alts) {
err = snd_usb_select_mode_quirk(subs, fmt);
@@ -644,6 +647,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
return 0;
}
+static int snd_usb_pcm_change_state(struct snd_usb_substream *subs, int state);
+
int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
int datainterval, bool enable)
{
@@ -666,6 +671,11 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
}
snd_usb_autoresume(subs->stream->chip);
+
+ ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
+ if (ret < 0)
+ return ret;
+
if (datainterval != -EINVAL)
fmt = find_format_and_si(subs, datainterval);
else
@@ -1857,6 +1867,13 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
subs->data_endpoint->retire_data_urb = retire_playback_urb;
subs->running = 0;
return 0;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (subs->stream->chip->setup_fmt_after_resume_quirk) {
+ stop_endpoints(subs, true);
+ subs->need_setup_fmt = true;
+ return 0;
+ }
+ break;
}
return -EINVAL;
@@ -1889,6 +1906,13 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
subs->data_endpoint->retire_data_urb = retire_capture_urb;
subs->running = 1;
return 0;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (subs->stream->chip->setup_fmt_after_resume_quirk) {
+ stop_endpoints(subs, true);
+ subs->need_setup_fmt = true;
+ return 0;
+ }
+ break;
}
return -EINVAL;
diff --git a/sound/usb/power.c b/sound/usb/power.c
index bd303a1..606a2cb 100644
--- a/sound/usb/power.c
+++ b/sound/usb/power.c
@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p;
int i;
+ if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
+ continue;
for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 57c6209..65f9c4b 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3400,7 +3400,8 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.vendor_name = "Dell",
.product_name = "WD19 Dock",
.profile_name = "Dell-WD15-Dock",
- .ifnum = QUIRK_NO_INTERFACE
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_SETUP_FMT_AFTER_RESUME
}
},
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 60d0009..bc70212 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -259,6 +259,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd)
return -ENODEV;
+ if ((injd && !snd_usb_validate_midi_desc(injd)) ||
+ (outjd && !snd_usb_validate_midi_desc(outjd)))
+ return -ENODEV;
if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL)))
@@ -516,6 +519,16 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
return snd_usb_create_mixer(chip, quirk->ifnum, 0);
}
+
+static int setup_fmt_after_resume_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ chip->setup_fmt_after_resume_quirk = 1;
+ return 1; /* Continue with creating streams and mixer */
+}
+
/*
* audio-interface quirks
*
@@ -554,6 +567,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
[QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk,
[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
+ [QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -1343,7 +1357,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
struct usb_interface *iface;
/* Playback Designs */
- if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
+ USB_ID_PRODUCT(chip->usb_id) < 0x0110) {
switch (fp->altsetting) {
case 1:
fp->dsd_dop = true;
@@ -1360,37 +1375,19 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
/* XMOS based USB DACs */
switch (chip->usb_id) {
case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
- case USB_ID(0x20b1, 0x0002): /* Wyred 4 Sound DAC-2 DSD */
- case USB_ID(0x20b1, 0x2004): /* Matrix Audio X-SPDIF 2 */
- case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
- case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
- case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
- case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */
- case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
if (fp->altsetting == 2)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
- case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
- case USB_ID(0x152a, 0x85de): /* SMSL D1 DAC */
- case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
+ case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */
+ case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
- case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */
- case USB_ID(0x20b1, 0x2005): /* Denafrips Ares DAC */
- case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
- case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
- case USB_ID(0x20b1, 0x3021): /* Eastern El. MiniMax Tube DAC Supreme */
- case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
- case USB_ID(0x20b1, 0x302d): /* Unison Research Unico CD Due */
- case USB_ID(0x20b1, 0x307b): /* CH Precision C1 DAC */
- case USB_ID(0x20b1, 0x3086): /* Singxer F-1 converter board */
- case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
@@ -1445,9 +1442,13 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
* from XMOS/Thesycon
*/
switch (USB_ID_VENDOR(chip->usb_id)) {
- case 0x20b1: /* XMOS based devices */
case 0x152a: /* Thesycon devices */
+ case 0x20b1: /* XMOS based devices */
+ case 0x22d9: /* Oppo */
+ case 0x23ba: /* Playback Designs */
case 0x25ce: /* Mytek devices */
+ case 0x278b: /* Rotel? */
+ case 0x292b: /* Gustard/Ess based devices */
case 0x2ab6: /* T+A devices */
case 0x3842: /* EVGA */
case 0xc502: /* HiBy devices */
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index fa2cc4a..c414dff 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -642,16 +642,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/
static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id, bool uac23)
+ int terminal_id, int protocol)
{
struct uac2_input_terminal_descriptor *term = NULL;
- size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) :
- sizeof(struct uac_input_terminal_descriptor);
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) {
- if (term->bLength < minlen)
+ if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
@@ -662,7 +660,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+ int terminal_id, int protocol)
{
/* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;
@@ -670,8 +668,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) {
- if (term->bLength >= sizeof(*term) &&
- term->bTerminalID == terminal_id)
+ if (!snd_usb_validate_audio_desc(term, protocol))
+ continue;
+ if (term->bTerminalID == terminal_id)
return term;
}
@@ -746,7 +745,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- false);
+ protocol);
if (iterm) {
num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig);
@@ -782,7 +781,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ protocol);
if (input_term) {
clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels))
@@ -791,7 +790,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ protocol);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -1017,14 +1017,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ UAC_VERSION_3);
if (input_term) {
clock = input_term->bCSourceID;
goto found_clock;
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ UAC_VERSION_3);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 8bf7ea6..a0a39a3 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -44,7 +44,7 @@ struct snd_usb_audio {
wait_queue_head_t shutdown_wait;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
-
+ unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */
int num_interfaces;
int num_suspended_intf;
int sample_rate_read_error;
@@ -110,6 +110,7 @@ enum quirk_type {
QUIRK_AUDIO_EDIROL_UAXX,
QUIRK_AUDIO_ALIGN_TRANSFER,
QUIRK_AUDIO_STANDARD_MIXER,
+ QUIRK_SETUP_FMT_AFTER_RESUME,
QUIRK_TYPE_COUNT
};
diff --git a/sound/usb/validate.c b/sound/usb/validate.c
new file mode 100644
index 0000000..389e865
--- /dev/null
+++ b/sound/usb/validate.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Validation of USB-audio class descriptors
+//
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+#include <linux/usb/midi.h>
+#include "usbaudio.h"
+#include "helper.h"
+
+struct usb_desc_validator {
+ unsigned char protocol;
+ unsigned char type;
+ bool (*func)(const void *p, const struct usb_desc_validator *v);
+ size_t size;
+};
+
+#define UAC_VERSION_ALL (unsigned char)(-1)
+
+/* UAC1 only */
+static bool validate_uac1_header(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac1_ac_header_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bInCollection;
+}
+
+/* for mixer unit; covering all UACs */
+static bool validate_mixer_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_mixer_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d) || !d->bNrInPins)
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ /* We can't determine the bitmap size only from this unit descriptor,
+ * so just check with the remaining length.
+ * The actual bitmap is checked at mixer unit parser.
+ */
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 2 + 1; /* wChannelConfig, iChannelNames */
+ /* bmControls[n*m] */
+ len += 1; /* iMixer */
+ break;
+ case UAC_VERSION_2:
+ len += 4 + 1; /* bmChannelConfig, iChannelNames */
+ /* bmMixerControls[n*m] */
+ len += 1 + 1; /* bmControls, iMixer */
+ break;
+ case UAC_VERSION_3:
+ len += 2; /* wClusterDescrID */
+ /* bmMixerControls[n*m] */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+/* both for processing and extension units; covering all UACs */
+static bool validate_processing_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_processing_unit_descriptor *d = p;
+ const unsigned char *hdr = p;
+ size_t len, m;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ if (d->bLength < len)
+ return false;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ /* bNrChannels, wChannelConfig, iChannelNames */
+ len += 1 + 2 + 1;
+ if (d->bLength < len + 1) /* bControlSize */
+ return false;
+ m = hdr[len];
+ len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */
+ break;
+ case UAC_VERSION_2:
+ /* bNrChannels, bmChannelConfig, iChannelNames */
+ len += 1 + 4 + 1;
+ if (v->type == UAC2_PROCESSING_UNIT_V2)
+ len += 2; /* bmControls -- 2 bytes for PU */
+ else
+ len += 1; /* bmControls -- 1 byte for EU */
+ len += 1; /* iProcessing */
+ break;
+ case UAC_VERSION_3:
+ /* wProcessingDescrStr, bmControls */
+ len += 2 + 4;
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ if (v->type == UAC1_EXTENSION_UNIT)
+ return true; /* OK */
+ switch (d->wProcessType) {
+ case UAC_PROCESS_UP_DOWNMIX:
+ case UAC_PROCESS_DOLBY_PROLOGIC:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_2:
+ if (v->type == UAC2_EXTENSION_UNIT_V2)
+ return true; /* OK */
+ switch (d->wProcessType) {
+ case UAC2_PROCESS_UP_DOWNMIX:
+ case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 4; /* bNrModes, daModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_3:
+ if (v->type == UAC3_EXTENSION_UNIT) {
+ len += 2; /* wClusterDescrID */
+ break;
+ }
+ switch (d->wProcessType) {
+ case UAC3_PROCESS_UP_DOWNMIX:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */
+ break;
+ case UAC3_PROCESS_MULTI_FUNCTION:
+ len += 2 + 4; /* wClusterDescrID, bmAlgorighms */
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ return true;
+}
+
+/* both for selector and clock selector units; covering all UACs */
+static bool validate_selector_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_selector_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 1; /* iSelector */
+ break;
+ case UAC_VERSION_2:
+ len += 1 + 1; /* bmControls, iSelector */
+ break;
+ case UAC_VERSION_3:
+ len += 4 + 2; /* bmControls, wSelectorDescrStr */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+static bool validate_uac1_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d) || !d->bControlSize)
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + d->bControlSize + 1;
+}
+
+static bool validate_uac2_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac2_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + 4 + 1;
+}
+
+static bool validate_uac3_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac3_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + wFeatureDescrStr */
+ return d->bLength >= sizeof(*d) + 4 + 2;
+}
+
+static bool validate_midi_out_jack(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct usb_midi_out_jack_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bNrInputPins * 2;
+}
+
+#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) }
+#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) }
+
+static struct usb_desc_validator audio_validators[] = {
+ /* UAC1 */
+ FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header),
+ FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL,
+ struct uac_input_terminal_descriptor),
+ FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL,
+ struct uac1_output_terminal_descriptor),
+ FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit),
+ FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit),
+
+ /* UAC2 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor),
+ FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL,
+ struct uac2_input_terminal_descriptor),
+ FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL,
+ struct uac2_output_terminal_descriptor),
+ FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit),
+ /* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit),
+ FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE,
+ struct uac_clock_source_descriptor),
+ FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER,
+ struct uac_clock_multiplier_descriptor),
+ /* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */
+
+ /* UAC3 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor),
+ FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL,
+ struct uac3_input_terminal_descriptor),
+ FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL,
+ struct uac3_output_terminal_descriptor),
+ /* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit),
+ /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE,
+ struct uac3_clock_source_descriptor),
+ FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER,
+ struct uac3_clock_multiplier_descriptor),
+ /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */
+ /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */
+ { } /* terminator */
+};
+
+static struct usb_desc_validator midi_validators[] = {
+ FIXED(UAC_VERSION_ALL, USB_MS_HEADER,
+ struct usb_ms_header_descriptor),
+ FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK,
+ struct usb_midi_in_jack_descriptor),
+ FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK,
+ validate_midi_out_jack),
+ { } /* terminator */
+};
+
+
+/* Validate the given unit descriptor, return true if it's OK */
+static bool validate_desc(unsigned char *hdr, int protocol,
+ const struct usb_desc_validator *v)
+{
+ if (hdr[1] != USB_DT_CS_INTERFACE)
+ return true; /* don't care */
+
+ for (; v->type; v++) {
+ if (v->type == hdr[2] &&
+ (v->protocol == UAC_VERSION_ALL ||
+ v->protocol == protocol)) {
+ if (v->func)
+ return v->func(hdr, v);
+ /* check for the fixed size */
+ return hdr[0] >= v->size;
+ }
+ }
+
+ return true; /* not matching, skip validation */
+}
+
+bool snd_usb_validate_audio_desc(void *p, int protocol)
+{
+ return validate_desc(p, protocol, audio_validators);
+}
+
+bool snd_usb_validate_midi_desc(void *p)
+{
+ return validate_desc(p, UAC_VERSION_1, midi_validators);
+}
+
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 598066c..c2b6b21 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -143,7 +143,7 @@
local type
type=$(bpftool -jp map show $keyword $ref | \
command sed -n 's/.*"type": "\(.*\)",$/\1/p')
- printf $type
+ [[ -n $type ]] && printf $type
}
_bpftool_map_update_get_id()
diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
index 55bc512..e4e6e2b 100644
--- a/tools/bpf/bpftool/btf_dumper.c
+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -32,7 +32,7 @@ static void btf_dumper_ptr(const void *data, json_writer_t *jw,
}
static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
- const void *data)
+ __u8 bit_offset, const void *data)
{
int actual_type_id;
@@ -40,7 +40,7 @@ static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
if (actual_type_id < 0)
return actual_type_id;
- return btf_dumper_do_type(d, actual_type_id, 0, data);
+ return btf_dumper_do_type(d, actual_type_id, bit_offset, data);
}
static void btf_dumper_enum(const void *data, json_writer_t *jw)
@@ -237,7 +237,7 @@ static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
- return btf_dumper_modifier(d, type_id, data);
+ return btf_dumper_modifier(d, type_id, bit_offset, data);
default:
jsonw_printf(d->jw, "(unsupported-kind");
return -EINVAL;
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index be7aebf..158469f 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -130,16 +130,17 @@ static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
return 0;
}
-int open_obj_pinned(char *path)
+int open_obj_pinned(char *path, bool quiet)
{
int fd;
fd = bpf_obj_get(path);
if (fd < 0) {
- p_err("bpf obj get (%s): %s", path,
- errno == EACCES && !is_bpffs(dirname(path)) ?
- "directory not in bpf file system (bpffs)" :
- strerror(errno));
+ if (!quiet)
+ p_err("bpf obj get (%s): %s", path,
+ errno == EACCES && !is_bpffs(dirname(path)) ?
+ "directory not in bpf file system (bpffs)" :
+ strerror(errno));
return -1;
}
@@ -151,7 +152,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
enum bpf_obj_type type;
int fd;
- fd = open_obj_pinned(path);
+ fd = open_obj_pinned(path, false);
if (fd < 0)
return -1;
@@ -384,7 +385,7 @@ int build_pinned_obj_table(struct pinned_obj_table *tab,
while ((ftse = fts_read(fts))) {
if (!(ftse->fts_info & FTS_F))
continue;
- fd = open_obj_pinned(ftse->fts_path);
+ fd = open_obj_pinned(ftse->fts_path, true);
if (fd < 0)
continue;
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 238e734..057a227b 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -126,7 +126,7 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv,
int get_fd_type(int fd);
const char *get_fd_type_name(enum bpf_obj_type type);
char *get_fdinfo(int fd, const char *key);
-int open_obj_pinned(char *path);
+int open_obj_pinned(char *path, bool quiet);
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
int do_pin_fd(int fd, const char *name);
diff --git a/tools/gpio/Build b/tools/gpio/Build
index 620c193..4141f35 100644
--- a/tools/gpio/Build
+++ b/tools/gpio/Build
@@ -1,3 +1,4 @@
+gpio-utils-y += gpio-utils.o
lsgpio-y += lsgpio.o gpio-utils.o
gpio-hammer-y += gpio-hammer.o gpio-utils.o
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile
index 240eda0..6a73c06 100644
--- a/tools/gpio/Makefile
+++ b/tools/gpio/Makefile
@@ -3,7 +3,11 @@
bindir ?= /usr/bin
-ifeq ($(srctree),)
+# This will work when gpio is built in tools env. where srctree
+# isn't set and when invoked from selftests build, where srctree
+# is set to ".". building_out_of_srctree is undefined for in srctree
+# builds
+ifndef building_out_of_srctree
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
@@ -31,11 +35,15 @@
prepare: $(OUTPUT)include/linux/gpio.h
+GPIO_UTILS_IN := $(output)gpio-utils-in.o
+$(GPIO_UTILS_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=gpio-utils
+
#
# lsgpio
#
LSGPIO_IN := $(OUTPUT)lsgpio-in.o
-$(LSGPIO_IN): prepare FORCE
+$(LSGPIO_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
$(Q)$(MAKE) $(build)=lsgpio
$(OUTPUT)lsgpio: $(LSGPIO_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
@@ -44,7 +52,7 @@
# gpio-hammer
#
GPIO_HAMMER_IN := $(OUTPUT)gpio-hammer-in.o
-$(GPIO_HAMMER_IN): prepare FORCE
+$(GPIO_HAMMER_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
$(Q)$(MAKE) $(build)=gpio-hammer
$(OUTPUT)gpio-hammer: $(GPIO_HAMMER_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
@@ -53,7 +61,7 @@
# gpio-event-mon
#
GPIO_EVENT_MON_IN := $(OUTPUT)gpio-event-mon-in.o
-$(GPIO_EVENT_MON_IN): prepare FORCE
+$(GPIO_EVENT_MON_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
$(Q)$(MAKE) $(build)=gpio-event-mon
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index a350f97..249fa8d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -22,7 +22,9 @@
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
@@ -1071,16 +1073,22 @@ int bpf_map__reuse_fd(struct bpf_map *map, int fd)
return -errno;
new_fd = open("/", O_RDONLY | O_CLOEXEC);
- if (new_fd < 0)
+ if (new_fd < 0) {
+ err = -errno;
goto err_free_new_name;
+ }
new_fd = dup3(fd, new_fd, O_CLOEXEC);
- if (new_fd < 0)
+ if (new_fd < 0) {
+ err = -errno;
goto err_close_new_fd;
+ }
err = zclose(map->fd);
- if (err)
+ if (err) {
+ err = -errno;
goto err_close_new_fd;
+ }
free(map->name);
map->fd = new_fd;
@@ -1099,7 +1107,7 @@ int bpf_map__reuse_fd(struct bpf_map *map, int fd)
close(new_fd);
err_free_new_name:
free(new_name);
- return -errno;
+ return err;
}
static int
diff --git a/tools/lib/bpf/libbpf_errno.c b/tools/lib/bpf/libbpf_errno.c
index d9ba851..d2d1722 100644
--- a/tools/lib/bpf/libbpf_errno.c
+++ b/tools/lib/bpf/libbpf_errno.c
@@ -20,6 +20,7 @@
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
+#undef _GNU_SOURCE
#include <stdio.h>
#include <string.h>
diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile
index ed61fb3..5dbb0dd 100644
--- a/tools/lib/subcmd/Makefile
+++ b/tools/lib/subcmd/Makefile
@@ -20,9 +20,17 @@
LIBFILE = $(OUTPUT)libsubcmd.a
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
-CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -fPIC
-ifeq ($(CC_NO_CLANG), 0)
+ifeq ($(DEBUG),0)
+ ifeq ($(feature-fortify-source), 1)
+ CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
+ endif
+endif
+
+ifeq ($(DEBUG),1)
+ CFLAGS += -O0
+else ifeq ($(CC_NO_CLANG), 0)
CFLAGS += -O3
else
CFLAGS += -O6
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index bca0c9e..05f8a0f 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -115,6 +115,7 @@
LIB_TARGET = libtraceevent.a libtraceevent.so.$(EVENT_PARSE_VERSION)
LIB_INSTALL = libtraceevent.a libtraceevent.so*
+LIB_INSTALL := $(addprefix $(OUTPUT),$(LIB_INSTALL))
INCLUDES = -I. -I $(srctree)/tools/include $(CONFIG_INCLUDES)
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c
index e76154c..2700f1f 100644
--- a/tools/lib/traceevent/parse-filter.c
+++ b/tools/lib/traceevent/parse-filter.c
@@ -1475,8 +1475,10 @@ static int copy_filter_type(struct event_filter *filter,
if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) {
/* Add trivial event */
arg = allocate_arg();
- if (arg == NULL)
+ if (arg == NULL) {
+ free(str);
return -1;
+ }
arg->type = FILTER_ARG_BOOLEAN;
if (strcmp(str, "TRUE") == 0)
@@ -1485,8 +1487,11 @@ static int copy_filter_type(struct event_filter *filter,
arg->boolean.value = 0;
filter_type = add_filter_type(filter, event->id);
- if (filter_type == NULL)
+ if (filter_type == NULL) {
+ free(str);
+ free_arg(arg);
return -1;
+ }
filter_type->filter = arg;
diff --git a/tools/objtool/arch/x86/lib/x86-opcode-map.txt b/tools/objtool/arch/x86/lib/x86-opcode-map.txt
index e0b8593..0a0e911 100644
--- a/tools/objtool/arch/x86/lib/x86-opcode-map.txt
+++ b/tools/objtool/arch/x86/lib/x86-opcode-map.txt
@@ -333,7 +333,7 @@
06: CLTS
07: SYSRET (o64)
08: INVD
-09: WBINVD
+09: WBINVD | WBNOINVD (F3)
0a:
0b: UD2 (1B)
0c:
@@ -364,7 +364,7 @@
# a ModR/M byte.
1a: BNDCL Gv,Ev (F3) | BNDCU Gv,Ev (F2) | BNDMOV Gv,Ev (66) | BNDLDX Gv,Ev
1b: BNDCN Gv,Ev (F2) | BNDMOV Ev,Gv (66) | BNDMK Gv,Ev (F3) | BNDSTX Ev,Gv
-1c:
+1c: Grp20 (1A),(1C)
1d:
1e:
1f: NOP Ev
@@ -792,6 +792,8 @@
f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v)
f6: ADCX Gy,Ey (66) | ADOX Gy,Ey (F3) | MULX By,Gy,rDX,Ey (F2),(v)
f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v)
+f8: MOVDIR64B Gv,Mdqq (66) | ENQCMD Gv,Mdqq (F2) | ENQCMDS Gv,Mdqq (F3)
+f9: MOVDIRI My,Gy
EndTable
Table: 3-byte opcode 2 (0x0f 0x3a)
@@ -943,9 +945,9 @@
EndTable
GrpTable: Grp7
-0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B)
-1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001),(11B) | CLAC (010),(11B) | STAC (011),(11B)
-2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B)
+0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B) | PCONFIG (101),(11B) | ENCLV (000),(11B)
+1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001),(11B) | CLAC (010),(11B) | STAC (011),(11B) | ENCLS (111),(11B)
+2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B) | ENCLU (111),(11B)
3: LIDT Ms
4: SMSW Mw/Rv
5: rdpkru (110),(11B) | wrpkru (111),(11B)
@@ -1020,7 +1022,7 @@
3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
4: XSAVE | ptwrite Ey (F3),(11B)
5: XRSTOR | lfence (11B)
-6: XSAVEOPT | clwb (66) | mfence (11B)
+6: XSAVEOPT | clwb (66) | mfence (11B) | TPAUSE Rd (66),(11B) | UMONITOR Rv (F3),(11B) | UMWAIT Rd (F2),(11B)
7: clflush | clflushopt (66) | sfence (11B)
EndTable
@@ -1051,6 +1053,10 @@
6: vscatterpf1qps/d Wx (66),(ev)
EndTable
+GrpTable: Grp20
+0: cldemote Mb
+EndTable
+
# AMD's Prefetch Group
GrpTable: GrpP
0: PREFETCH
diff --git a/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk b/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk
index b02a36b..a42015b 100644
--- a/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk
+++ b/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk
@@ -69,7 +69,7 @@
lprefix1_expr = "\\((66|!F3)\\)"
lprefix2_expr = "\\(F3\\)"
- lprefix3_expr = "\\((F2|!F3|66\\&F2)\\)"
+ lprefix3_expr = "\\((F2|!F3|66&F2)\\)"
lprefix_expr = "\\((66|F2|F3)\\)"
max_lprefix = 4
@@ -257,7 +257,7 @@
return add_flags(imm, mod)
}
-/^[0-9a-f]+\:/ {
+/^[0-9a-f]+:/ {
if (NR == 1)
next
# get index
diff --git a/tools/pci/pcitest.c b/tools/pci/pcitest.c
index af146bb..4c5be77 100644
--- a/tools/pci/pcitest.c
+++ b/tools/pci/pcitest.c
@@ -23,7 +23,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
-#include <time.h>
#include <unistd.h>
#include <linux/pcitest.h>
@@ -50,15 +49,13 @@ struct pci_test {
static int run_test(struct pci_test *test)
{
- long ret;
+ int ret = -EINVAL;
int fd;
- struct timespec start, end;
- double time;
fd = open(test->device, O_RDWR);
if (fd < 0) {
perror("can't open PCI Endpoint Test device");
- return fd;
+ return -ENODEV;
}
if (test->barnum >= 0 && test->barnum <= 5) {
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c
index 0b24266..e46be9e 100644
--- a/tools/perf/arch/powerpc/util/header.c
+++ b/tools/perf/arch/powerpc/util/header.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
+#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,7 +32,7 @@ get_cpuid(char *buffer, size_t sz)
buffer[nb-1] = '\0';
return 0;
}
- return -1;
+ return ENOBUFS;
}
char *
diff --git a/tools/perf/arch/s390/util/header.c b/tools/perf/arch/s390/util/header.c
index 163b92f..cc72554 100644
--- a/tools/perf/arch/s390/util/header.c
+++ b/tools/perf/arch/s390/util/header.c
@@ -11,6 +11,7 @@
*/
#include <sys/types.h>
+#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
@@ -56,7 +57,7 @@ int get_cpuid(char *buffer, size_t sz)
sysinfo = fopen(SYSINFO, "r");
if (sysinfo == NULL)
- return -1;
+ return errno;
while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
if (!strncmp(line, SYSINFO_MANU, strlen(SYSINFO_MANU))) {
@@ -91,7 +92,7 @@ int get_cpuid(char *buffer, size_t sz)
/* Missing manufacturer, type or model information should not happen */
if (!manufacturer[0] || !type[0] || !model[0])
- return -1;
+ return EINVAL;
/*
* Scan /proc/service_levels and return the CPU-MF counter facility
@@ -135,14 +136,14 @@ int get_cpuid(char *buffer, size_t sz)
else
nbytes = snprintf(buffer, sz, "%s,%s,%s", manufacturer, type,
model);
- return (nbytes >= sz) ? -1 : 0;
+ return (nbytes >= sz) ? ENOBUFS : 0;
}
char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
{
char *buf = malloc(128);
- if (buf && get_cpuid(buf, 128) < 0)
+ if (buf && get_cpuid(buf, 128))
zfree(&buf);
return buf;
}
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
index fb0d71a..2a5daec6 100644
--- a/tools/perf/arch/x86/util/header.c
+++ b/tools/perf/arch/x86/util/header.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
+#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -56,7 +57,7 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt)
buffer[nb-1] = '\0';
return 0;
}
- return -1;
+ return ENOBUFS;
}
int
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 763c2ed..1452e51 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2626,6 +2626,7 @@ static int build_cl_output(char *cl_sort, bool no_source)
bool add_sym = false;
bool add_dso = false;
bool add_src = false;
+ int ret = 0;
if (!buf)
return -ENOMEM;
@@ -2644,7 +2645,8 @@ static int build_cl_output(char *cl_sort, bool no_source)
add_dso = true;
} else if (strcmp(tok, "offset")) {
pr_err("unrecognized sort token: %s\n", tok);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
}
@@ -2667,13 +2669,15 @@ static int build_cl_output(char *cl_sort, bool no_source)
add_sym ? "symbol," : "",
add_dso ? "dso," : "",
add_src ? "cl_srcline," : "",
- "node") < 0)
- return -ENOMEM;
+ "node") < 0) {
+ ret = -ENOMEM;
+ goto err;
+ }
c2c.show_src = add_src;
-
+err:
free(buf);
- return 0;
+ return ret;
}
static int setup_coalesce(const char *coalesce, bool no_source)
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index b63bca4..56dd5d1 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -686,6 +686,7 @@ static char *compact_gfp_flags(char *gfp_flags)
new = realloc(new_flags, len + strlen(cpt) + 2);
if (new == NULL) {
free(new_flags);
+ free(orig_flags);
return NULL;
}
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 2b1ef70..952e222 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -699,14 +699,15 @@ static int process_sample_event(struct perf_tool *tool,
static int cpu_isa_config(struct perf_kvm_stat *kvm)
{
- char buf[64], *cpuid;
+ char buf[128], *cpuid;
int err;
if (kvm->live) {
err = get_cpuid(buf, sizeof(buf));
if (err != 0) {
- pr_err("Failed to look up CPU type\n");
- return err;
+ pr_err("Failed to look up CPU type: %s\n",
+ str_error_r(err, buf, sizeof(buf)));
+ return -err;
}
cpuid = buf;
} else
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index b2188e6..2f94f7a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -383,6 +383,13 @@ static int report__setup_sample_type(struct report *rep)
PERF_SAMPLE_BRANCH_ANY))
rep->nonany_branch_mode = true;
+#ifndef HAVE_LIBUNWIND_SUPPORT
+ if (dwarf_callchain_users) {
+ ui__warning("Please install libunwind development packages "
+ "during the perf build.\n");
+ }
+#endif
+
return 0;
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 53c11fc..1200973 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -428,7 +428,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
"selected. Hence, no address to lookup the source line number.\n");
return -EINVAL;
}
- if (PRINT_FIELD(BRSTACKINSN) &&
+ if (PRINT_FIELD(BRSTACKINSN) && !allow_user_set &&
!(perf_evlist__combined_branch_type(session->evlist) &
PERF_SAMPLE_BRANCH_ANY)) {
pr_err("Display of branch stack assembler requested, but non all-branch filter set\n"
@@ -1021,7 +1021,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
continue;
insn = 0;
- for (off = 0;; off += ilen) {
+ for (off = 0; off < (unsigned)len; off += ilen) {
uint64_t ip = start + off;
printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp);
@@ -1029,6 +1029,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp);
break;
} else {
+ ilen = 0;
printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip,
dump_insn(&x, ip, buffer + off, len - off, &ilen));
if (ilen == 0)
@@ -1036,6 +1037,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
insn++;
}
}
+ if (off != end - start)
+ printed += fprintf(fp, "\tmismatch of LBR data and executable\n");
}
/*
@@ -1066,6 +1069,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
goto out;
}
for (off = 0; off <= end - start; off += ilen) {
+ ilen = 0;
printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", start + off,
dump_insn(&x, start + off, buffer + off, len - off, &ilen));
if (ilen == 0)
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 6b36b71..38b5888 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -446,12 +446,12 @@ static struct fixed {
const char *name;
const char *event;
} fixed[] = {
- { "inst_retired.any", "event=0xc0" },
- { "inst_retired.any_p", "event=0xc0" },
- { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" },
- { "cpu_clk_unhalted.thread", "event=0x3c" },
- { "cpu_clk_unhalted.core", "event=0x3c" },
- { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" },
+ { "inst_retired.any", "event=0xc0,period=2000003" },
+ { "inst_retired.any_p", "event=0xc0,period=2000003" },
+ { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03,period=2000003" },
+ { "cpu_clk_unhalted.thread", "event=0x3c,period=2000003" },
+ { "cpu_clk_unhalted.core", "event=0x3c,period=2000003" },
+ { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1,period=2000003" },
{ NULL, NULL},
};
@@ -754,6 +754,7 @@ static int process_mapfile(FILE *outfp, char *fpath)
char *line, *p;
int line_num;
char *tblname;
+ int ret = 0;
pr_info("%s: Processing mapfile %s\n", prog, fpath);
@@ -765,6 +766,7 @@ static int process_mapfile(FILE *outfp, char *fpath)
if (!mapfp) {
pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
fpath);
+ free(line);
return -1;
}
@@ -791,7 +793,8 @@ static int process_mapfile(FILE *outfp, char *fpath)
/* TODO Deal with lines longer than 16K */
pr_info("%s: Mapfile %s: line %d too long, aborting\n",
prog, fpath, line_num);
- return -1;
+ ret = -1;
+ goto out;
}
line[strlen(line)-1] = '\0';
@@ -821,7 +824,9 @@ static int process_mapfile(FILE *outfp, char *fpath)
out:
print_mapping_table_suffix(outfp);
- return 0;
+ fclose(mapfp);
+ free(line);
+ return ret;
}
/*
@@ -1118,6 +1123,7 @@ int main(int argc, char *argv[])
goto empty_map;
} else if (rc < 0) {
/* Make build fail */
+ fclose(eventsfp);
free_arch_std_events();
return 1;
} else if (rc) {
@@ -1130,6 +1136,7 @@ int main(int argc, char *argv[])
goto empty_map;
} else if (rc < 0) {
/* Make build fail */
+ fclose(eventsfp);
free_arch_std_events();
return 1;
} else if (rc) {
@@ -1147,6 +1154,8 @@ int main(int argc, char *argv[])
if (process_mapfile(eventsfp, mapfile)) {
pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
/* Make build fail */
+ fclose(eventsfp);
+ free_arch_std_events();
return 1;
}
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c
index 910e25e..6cf0065 100644
--- a/tools/perf/tests/bp_signal.c
+++ b/tools/perf/tests/bp_signal.c
@@ -48,14 +48,6 @@ asm (
"__test_function:\n"
"incq (%rdi)\n"
"ret\n");
-#elif defined (__aarch64__)
-extern void __test_function(volatile long *ptr);
-asm (
- ".globl __test_function\n"
- "__test_function:\n"
- "str x30, [x0]\n"
- "ret\n");
-
#else
static void __test_function(volatile long *ptr)
{
@@ -301,10 +293,15 @@ bool test__bp_signal_is_supported(void)
* stepping into the SIGIO handler and getting stuck on the
* breakpointed instruction.
*
+ * Since arm64 has the same issue with arm for the single-step
+ * handling, this case also gets suck on the breakpointed
+ * instruction.
+ *
* Just disable the test for these architectures until these
* issues are resolved.
*/
-#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__)
+#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) || \
+ defined(__aarch64__)
return false;
#else
return true;
diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c
index a693bcf..44c16fd 100644
--- a/tools/perf/tests/perf-hooks.c
+++ b/tools/perf/tests/perf-hooks.c
@@ -20,12 +20,11 @@ static void sigsegv_handler(int sig __maybe_unused)
static void the_hook(void *_hook_flags)
{
int *hook_flags = _hook_flags;
- int *p = NULL;
*hook_flags = 1234;
/* Generate a segfault, test perf_hooks__recover */
- *p = 0;
+ raise(SIGSEGV);
}
int test__perf_hooks(struct test *test __maybe_unused, int subtest __maybe_unused)
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c
index e92fa60..788b080 100644
--- a/tools/perf/tests/task-exit.c
+++ b/tools/perf/tests/task-exit.c
@@ -105,6 +105,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused
if (perf_evlist__mmap(evlist, 128) < 0) {
pr_debug("failed to mmap events: %d (%s)\n", errno,
str_error_r(errno, sbuf, sizeof(sbuf)));
+ err = -1;
goto out_delete_evlist;
}
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index daea1fd..6958d7e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1614,7 +1614,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
build_id_path = strdup(filename);
if (!build_id_path)
- return -1;
+ return ENOMEM;
/*
* old style build-id cache has name of XX/XXXXXXX.. while
@@ -1871,11 +1871,11 @@ int symbol__annotate(struct symbol *sym, struct map *map,
int err;
if (!arch_name)
- return -1;
+ return errno;
args.arch = arch = arch__find(arch_name);
if (arch == NULL)
- return -ENOTSUP;
+ return ENOTSUP;
if (parch)
*parch = arch;
@@ -2732,7 +2732,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *ev
notes->offsets = zalloc(size * sizeof(struct annotation_line *));
if (notes->offsets == NULL)
- return -1;
+ return ENOMEM;
if (perf_evsel__is_group_event(evsel))
nr_pcnt = evsel->nr_members;
@@ -2757,7 +2757,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *ev
out_free_offsets:
zfree(¬es->offsets);
- return -1;
+ return err;
}
#define ANNOTATION__CFG(n) \
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 7eb7de5..29e75c0 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -321,20 +321,50 @@ bool die_is_func_def(Dwarf_Die *dw_die)
}
/**
+ * die_entrypc - Returns entry PC (the lowest address) of a DIE
+ * @dw_die: a DIE
+ * @addr: where to store entry PC
+ *
+ * Since dwarf_entrypc() does not return entry PC if the DIE has only address
+ * range, we have to use this to retrieve the lowest address from the address
+ * range attribute.
+ */
+int die_entrypc(Dwarf_Die *dw_die, Dwarf_Addr *addr)
+{
+ Dwarf_Addr base, end;
+
+ if (!addr)
+ return -EINVAL;
+
+ if (dwarf_entrypc(dw_die, addr) == 0)
+ return 0;
+
+ return dwarf_ranges(dw_die, 0, &base, addr, &end) < 0 ? -ENOENT : 0;
+}
+
+/**
* die_is_func_instance - Ensure that this DIE is an instance of a subprogram
* @dw_die: a DIE
*
* Ensure that this DIE is an instance (which has an entry address).
- * This returns true if @dw_die is a function instance. If not, you need to
- * call die_walk_instances() to find actual instances.
+ * This returns true if @dw_die is a function instance. If not, the @dw_die
+ * must be a prototype. You can use die_walk_instances() to find actual
+ * instances.
**/
bool die_is_func_instance(Dwarf_Die *dw_die)
{
Dwarf_Addr tmp;
+ Dwarf_Attribute attr_mem;
+ int tag = dwarf_tag(dw_die);
- /* Actually gcc optimizes non-inline as like as inlined */
- return !dwarf_func_inline(dw_die) && dwarf_entrypc(dw_die, &tmp) == 0;
+ if (tag != DW_TAG_subprogram &&
+ tag != DW_TAG_inlined_subroutine)
+ return false;
+
+ return dwarf_entrypc(dw_die, &tmp) == 0 ||
+ dwarf_attr(dw_die, DW_AT_ranges, &attr_mem) != NULL;
}
+
/**
* die_get_data_member_location - Get the data-member offset
* @mb_die: a DIE of a member of a data structure
@@ -611,6 +641,9 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data)
Dwarf_Die *origin;
int tmp;
+ if (!die_is_func_instance(inst))
+ return DIE_FIND_CB_CONTINUE;
+
attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem);
if (attr == NULL)
return DIE_FIND_CB_CONTINUE;
@@ -682,15 +715,14 @@ static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
fname = die_get_call_file(in_die);
lineno = die_get_call_lineno(in_die);
- if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
+ if (fname && lineno > 0 && die_entrypc(in_die, &addr) == 0) {
lw->retval = lw->callback(fname, lineno, addr, lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_END;
}
+ if (!lw->recursive)
+ return DIE_FIND_CB_SIBLING;
}
- if (!lw->recursive)
- /* Don't need to search recursively */
- return DIE_FIND_CB_SIBLING;
if (addr) {
fname = dwarf_decl_file(in_die);
@@ -723,7 +755,7 @@ static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive,
/* Handle function declaration line */
fname = dwarf_decl_file(sp_die);
if (fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
- dwarf_entrypc(sp_die, &addr) == 0) {
+ die_entrypc(sp_die, &addr) == 0) {
lw.retval = callback(fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
@@ -737,6 +769,10 @@ static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
struct __line_walk_param *lw = data;
+ /*
+ * Since inlined function can include another inlined function in
+ * the same file, we need to walk in it recursively.
+ */
lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
@@ -761,11 +797,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
Dwarf_Lines *lines;
Dwarf_Line *line;
Dwarf_Addr addr;
- const char *fname, *decf = NULL;
+ const char *fname, *decf = NULL, *inf = NULL;
int lineno, ret = 0;
int decl = 0, inl;
Dwarf_Die die_mem, *cu_die;
size_t nlines, i;
+ bool flag;
/* Get the CU die */
if (dwarf_tag(rt_die) != DW_TAG_compile_unit) {
@@ -796,6 +833,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
"Possible error in debuginfo.\n");
continue;
}
+ /* Skip end-of-sequence */
+ if (dwarf_lineendsequence(line, &flag) != 0 || flag)
+ continue;
+ /* Skip Non statement line-info */
+ if (dwarf_linebeginstatement(line, &flag) != 0 || !flag)
+ continue;
/* Filter lines based on address */
if (rt_die != cu_die) {
/*
@@ -805,13 +848,21 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
*/
if (!dwarf_haspc(rt_die, addr))
continue;
+
if (die_find_inlinefunc(rt_die, addr, &die_mem)) {
+ /* Call-site check */
+ inf = die_get_call_file(&die_mem);
+ if ((inf && !strcmp(inf, decf)) &&
+ die_get_call_lineno(&die_mem) == lineno)
+ goto found;
+
dwarf_decl_line(&die_mem, &inl);
if (inl != decl ||
decf != dwarf_decl_file(&die_mem))
continue;
}
}
+found:
/* Get source line */
fname = dwarf_linesrc(line, NULL, NULL);
@@ -826,8 +877,9 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
*/
if (rt_die != cu_die)
/*
- * Don't need walk functions recursively, because nested
- * inlined functions don't have lines of the specified DIE.
+ * Don't need walk inlined functions recursively, because
+ * inner inlined functions don't have the lines of the
+ * specified function.
*/
ret = __die_walk_funclines(rt_die, false, callback, data);
else {
@@ -1002,7 +1054,7 @@ static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
bool first = true;
const char *name;
- ret = dwarf_entrypc(sp_die, &entry);
+ ret = die_entrypc(sp_die, &entry);
if (ret)
return ret;
@@ -1065,7 +1117,7 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
bool first = true;
const char *name;
- ret = dwarf_entrypc(sp_die, &entry);
+ ret = die_entrypc(sp_die, &entry);
if (ret)
return ret;
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 8ac53bf1..ee15fac4 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -41,6 +41,9 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
/* Get DW_AT_linkage_name (should be NULL for C binary) */
const char *die_get_linkage_name(Dwarf_Die *dw_die);
+/* Get the lowest PC in DIE (including range list) */
+int die_entrypc(Dwarf_Die *dw_die, Dwarf_Addr *addr);
+
/* Ensure that this DIE is a subprogram and definition (not declaration) */
bool die_is_func_def(Dwarf_Die *dw_die);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e1e94b4..918260b 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1508,7 +1508,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
return 0;
}
-static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
+static int64_t hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
{
struct hists *hists = a->hists;
struct perf_hpp_fmt *fmt;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 003b70d..21f867a 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2276,7 +2276,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
}
check_calls:
- if (callchain_param.order != ORDER_CALLEE) {
+ if (chain && callchain_param.order != ORDER_CALLEE) {
err = find_prev_cpumode(chain, thread, cursor, parent, root_al,
&cpumode, chain->nr - first_call);
if (err)
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 6a6929f..1117ab8 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "symbol.h"
+#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
@@ -751,6 +752,8 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
}
after->start = map->end;
+ after->pgoff += map->end - pos->start;
+ assert(pos->map_ip(pos, map->end) == after->map_ip(after, map->end));
__map_groups__insert(pos->groups, after);
if (verbose >= 2 && !use_browser)
map__fprintf(after, fp);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 1a7c76d..95043ca 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1282,8 +1282,15 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
if (get_config_terms(head_config, &config_terms))
return -ENOMEM;
- if (perf_pmu__config(pmu, &attr, head_config, parse_state->error))
+ if (perf_pmu__config(pmu, &attr, head_config, parse_state->error)) {
+ struct perf_evsel_config_term *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, &config_terms, list) {
+ list_del_init(&pos->list);
+ free(pos);
+ }
return -EINVAL;
+ }
evsel = __add_event(list, &parse_state->idx, &attr,
get_config_name(head_config), pmu,
@@ -1843,15 +1850,20 @@ int parse_events(struct perf_evlist *evlist, const char *str,
ret = parse_events__scanner(str, &parse_state, PE_START_EVENTS);
perf_pmu__parse_cleanup();
+
+ if (!ret && list_empty(&parse_state.list)) {
+ WARN_ONCE(true, "WARNING: event parser found nothing\n");
+ return -1;
+ }
+
+ /*
+ * Add list to the evlist even with errors to allow callers to clean up.
+ */
+ perf_evlist__splice_list_tail(evlist, &parse_state.list);
+
if (!ret) {
struct perf_evsel *last;
- if (list_empty(&parse_state.list)) {
- WARN_ONCE(true, "WARNING: event parser found nothing\n");
- return -1;
- }
-
- perf_evlist__splice_list_tail(evlist, &parse_state.list);
evlist->nr_groups += parse_state.nr_groups;
last = perf_evlist__last(evlist);
last->cmdline_group_boundary = true;
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index c9319f8..f732e3a 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -34,7 +34,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
static inline const char *perf_reg_name(int id __maybe_unused)
{
- return NULL;
+ return "unknown";
}
static inline int perf_reg_value(u64 *valp __maybe_unused,
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c37fbef..7ccabb8 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -764,6 +764,16 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
return 0;
}
+/* Return innermost DIE */
+static int find_inner_scope_cb(Dwarf_Die *fn_die, void *data)
+{
+ struct find_scope_param *fsp = data;
+
+ memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
+ fsp->found = true;
+ return 1;
+}
+
/* Find an appropriate scope fits to given conditions */
static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
{
@@ -775,8 +785,13 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
.die_mem = die_mem,
.found = false,
};
+ int ret;
- cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp);
+ ret = cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb,
+ &fsp);
+ if (!ret && !fsp.found)
+ cu_walk_functions_at(&pf->cu_die, pf->addr,
+ find_inner_scope_cb, &fsp);
return fsp.found ? die_mem : NULL;
}
@@ -950,7 +965,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
ret = find_probe_point_lazy(in_die, pf);
else {
/* Get probe address */
- if (dwarf_entrypc(in_die, &addr) != 0) {
+ if (die_entrypc(in_die, &addr) != 0) {
pr_warning("Failed to get entry address of %s.\n",
dwarf_diename(in_die));
return -ENOENT;
@@ -1002,7 +1017,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
param->retval = find_probe_point_by_line(pf);
} else if (die_is_func_instance(sp_die)) {
/* Instances always have the entry address */
- dwarf_entrypc(sp_die, &pf->addr);
+ die_entrypc(sp_die, &pf->addr);
/* But in some case the entry address is 0 */
if (pf->addr == 0) {
pr_debug("%s has no entry PC. Skipped\n",
@@ -1414,6 +1429,18 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
return DIE_FIND_CB_END;
}
+static bool available_var_finder_overlap(struct available_var_finder *af)
+{
+ int i;
+
+ for (i = 0; i < af->nvls; i++) {
+ if (af->pf.addr == af->vls[i].point.address)
+ return true;
+ }
+ return false;
+
+}
+
/* Add a found vars into available variables list */
static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
{
@@ -1424,6 +1451,14 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
Dwarf_Die die_mem;
int ret;
+ /*
+ * For some reason (e.g. different column assigned to same address),
+ * this callback can be called with the address which already passed.
+ * Ignore it first.
+ */
+ if (available_var_finder_overlap(af))
+ return 0;
+
/* Check number of tevs */
if (af->nvls == af->max_vls) {
pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
@@ -1567,7 +1602,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
/* Get function entry information */
func = basefunc = dwarf_diename(&spdie);
if (!func ||
- dwarf_entrypc(&spdie, &baseaddr) != 0 ||
+ die_entrypc(&spdie, &baseaddr) != 0 ||
dwarf_decl_line(&spdie, &baseline) != 0) {
lineno = 0;
goto post;
@@ -1584,7 +1619,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr,
&indie)) {
/* There is an inline function */
- if (dwarf_entrypc(&indie, &_addr) == 0 &&
+ if (die_entrypc(&indie, &_addr) == 0 &&
_addr == addr) {
/*
* addr is at an inline function entry.
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index 9005fbe..23092fd 100644
--- a/tools/perf/util/strbuf.c
+++ b/tools/perf/util/strbuf.c
@@ -109,7 +109,6 @@ static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
return ret;
}
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
- va_end(ap_saved);
if (len > strbuf_avail(sb)) {
pr_debug("this should not happen, your vsnprintf is broken");
va_end(ap_saved);
diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c
index db21317..2d9b94b 100644
--- a/tools/power/acpi/tools/acpidump/apmain.c
+++ b/tools/power/acpi/tools/acpidump/apmain.c
@@ -106,7 +106,7 @@ static int ap_insert_action(char *argument, u32 to_be_done)
current_action++;
if (current_action > AP_MAX_ACTIONS) {
- fprintf(stderr, "Too many table options (max %u)\n",
+ fprintf(stderr, "Too many table options (max %d)\n",
AP_MAX_ACTIONS);
return (-1);
}
diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
index f794d6b..3e4ff4a 100644
--- a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
@@ -40,7 +40,6 @@ static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
{
.name = "PC9",
.desc = N_("Processor Package C9"),
- .desc = N_("Processor Package C2"),
.id = PC9,
.range = RANGE_PACKAGE,
.get_count_percent = hsw_ext_get_count_percent,
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 71cf7e77..02d1238 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -1,6 +1,6 @@
/*
* turbostat -- show CPU frequency and C-state residency
- * on modern Intel turbo-capable processors.
+ * on modern Intel and AMD processors.
*
* Copyright (c) 2013 Intel Corporation.
* Len Brown <len.brown@intel.com>
@@ -71,6 +71,8 @@ unsigned int do_irtl_snb;
unsigned int do_irtl_hsw;
unsigned int units = 1000000; /* MHz etc */
unsigned int genuine_intel;
+unsigned int authentic_amd;
+unsigned int max_level, max_extended_level;
unsigned int has_invariant_tsc;
unsigned int do_nhm_platform_info;
unsigned int no_MSR_MISC_PWR_MGMT;
@@ -1667,30 +1669,51 @@ int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp)
void get_apic_id(struct thread_data *t)
{
- unsigned int eax, ebx, ecx, edx, max_level;
+ unsigned int eax, ebx, ecx, edx;
- eax = ebx = ecx = edx = 0;
+ if (DO_BIC(BIC_APIC)) {
+ eax = ebx = ecx = edx = 0;
+ __cpuid(1, eax, ebx, ecx, edx);
+
+ t->apic_id = (ebx >> 24) & 0xff;
+ }
+
+ if (!DO_BIC(BIC_X2APIC))
+ return;
+
+ if (authentic_amd) {
+ unsigned int topology_extensions;
+
+ if (max_extended_level < 0x8000001e)
+ return;
+
+ eax = ebx = ecx = edx = 0;
+ __cpuid(0x80000001, eax, ebx, ecx, edx);
+ topology_extensions = ecx & (1 << 22);
+
+ if (topology_extensions == 0)
+ return;
+
+ eax = ebx = ecx = edx = 0;
+ __cpuid(0x8000001e, eax, ebx, ecx, edx);
+
+ t->x2apic_id = eax;
+ return;
+ }
if (!genuine_intel)
return;
- __cpuid(0, max_level, ebx, ecx, edx);
-
- __cpuid(1, eax, ebx, ecx, edx);
- t->apic_id = (ebx >> 24) & 0xf;
-
if (max_level < 0xb)
return;
- if (!DO_BIC(BIC_X2APIC))
- return;
-
ecx = 0;
__cpuid(0xb, eax, ebx, ecx, edx);
t->x2apic_id = edx;
- if (debug && (t->apic_id != t->x2apic_id))
- fprintf(outf, "cpu%d: apic 0x%x x2apic 0x%x\n", t->cpu_id, t->apic_id, t->x2apic_id);
+ if (debug && (t->apic_id != (t->x2apic_id & 0xff)))
+ fprintf(outf, "cpu%d: BIOS BUG: apic 0x%x x2apic 0x%x\n",
+ t->cpu_id, t->apic_id, t->x2apic_id);
}
/*
@@ -1953,11 +1976,12 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
#define PCL_7S 11 /* PC7 Shrink */
#define PCL__8 12 /* PC8 */
#define PCL__9 13 /* PC9 */
-#define PCLUNL 14 /* Unlimited */
+#define PCL_10 14 /* PC10 */
+#define PCLUNL 15 /* Unlimited */
int pkg_cstate_limit = PCLUKN;
char *pkg_cstate_limit_strings[] = { "reserved", "unknown", "pc0", "pc1", "pc2",
- "pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "unlimited"};
+ "pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "pc10", "unlimited"};
int nhm_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int snb_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
@@ -1965,7 +1989,7 @@ int hsw_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S,
int slv_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7};
int amt_pkg_cstate_limits[16] = {PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int phi_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
-int bxt_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
+int glm_pkg_cstate_limits[16] = {PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int skx_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
@@ -3165,7 +3189,7 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
- pkg_cstate_limits = bxt_pkg_cstate_limits;
+ pkg_cstate_limits = glm_pkg_cstate_limits;
break;
default:
return 0;
@@ -4438,16 +4462,18 @@ void decode_c6_demotion_policy_msr(void)
void process_cpuid()
{
- unsigned int eax, ebx, ecx, edx, max_level, max_extended_level;
- unsigned int fms, family, model, stepping;
+ unsigned int eax, ebx, ecx, edx;
+ unsigned int fms, family, model, stepping, ecx_flags, edx_flags;
unsigned int has_turbo;
eax = ebx = ecx = edx = 0;
__cpuid(0, max_level, ebx, ecx, edx);
- if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)
+ if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69)
genuine_intel = 1;
+ else if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
+ authentic_amd = 1;
if (!quiet)
fprintf(outf, "CPUID(0): %.4s%.4s%.4s ",
@@ -4461,25 +4487,8 @@ void process_cpuid()
family += (fms >> 20) & 0xff;
if (family >= 6)
model += ((fms >> 16) & 0xf) << 4;
-
- if (!quiet) {
- fprintf(outf, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
- max_level, family, model, stepping, family, model, stepping);
- fprintf(outf, "CPUID(1): %s %s %s %s %s %s %s %s %s %s\n",
- ecx & (1 << 0) ? "SSE3" : "-",
- ecx & (1 << 3) ? "MONITOR" : "-",
- ecx & (1 << 6) ? "SMX" : "-",
- ecx & (1 << 7) ? "EIST" : "-",
- ecx & (1 << 8) ? "TM2" : "-",
- edx & (1 << 4) ? "TSC" : "-",
- edx & (1 << 5) ? "MSR" : "-",
- edx & (1 << 22) ? "ACPI-TM" : "-",
- edx & (1 << 28) ? "HT" : "-",
- edx & (1 << 29) ? "TM" : "-");
- }
-
- if (!(edx & (1 << 5)))
- errx(1, "CPUID: no MSR");
+ ecx_flags = ecx;
+ edx_flags = edx;
/*
* check max extended function levels of CPUID.
@@ -4489,6 +4498,25 @@ void process_cpuid()
ebx = ecx = edx = 0;
__cpuid(0x80000000, max_extended_level, ebx, ecx, edx);
+ if (!quiet) {
+ fprintf(outf, "0x%x CPUID levels; 0x%x xlevels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
+ max_level, max_extended_level, family, model, stepping, family, model, stepping);
+ fprintf(outf, "CPUID(1): %s %s %s %s %s %s %s %s %s %s\n",
+ ecx_flags & (1 << 0) ? "SSE3" : "-",
+ ecx_flags & (1 << 3) ? "MONITOR" : "-",
+ ecx_flags & (1 << 6) ? "SMX" : "-",
+ ecx_flags & (1 << 7) ? "EIST" : "-",
+ ecx_flags & (1 << 8) ? "TM2" : "-",
+ edx_flags & (1 << 4) ? "TSC" : "-",
+ edx_flags & (1 << 5) ? "MSR" : "-",
+ edx_flags & (1 << 22) ? "ACPI-TM" : "-",
+ edx_flags & (1 << 28) ? "HT" : "-",
+ edx_flags & (1 << 29) ? "TM" : "-");
+ }
+
+ if (!(edx_flags & (1 << 5)))
+ errx(1, "CPUID: no MSR");
+
if (max_extended_level >= 0x80000007) {
/*
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c
index 321ba92..235eef7 100644
--- a/tools/testing/radix-tree/idr-test.c
+++ b/tools/testing/radix-tree/idr-test.c
@@ -227,6 +227,57 @@ void idr_u32_test(int base)
idr_u32_test1(&idr, 0xffffffff);
}
+static inline void *idr_mk_value(unsigned long v)
+{
+ BUG_ON((long)v < 0);
+ return (void *)((v & 1) | 2 | (v << 1));
+}
+
+DEFINE_IDR(find_idr);
+
+static void *idr_throbber(void *arg)
+{
+ time_t start = time(NULL);
+ int id = *(int *)arg;
+
+ rcu_register_thread();
+ do {
+ idr_alloc(&find_idr, idr_mk_value(id), id, id + 1, GFP_KERNEL);
+ idr_remove(&find_idr, id);
+ } while (time(NULL) < start + 10);
+ rcu_unregister_thread();
+
+ return NULL;
+}
+
+void idr_find_test_1(int anchor_id, int throbber_id)
+{
+ pthread_t throbber;
+ time_t start = time(NULL);
+
+ pthread_create(&throbber, NULL, idr_throbber, &throbber_id);
+
+ BUG_ON(idr_alloc(&find_idr, idr_mk_value(anchor_id), anchor_id,
+ anchor_id + 1, GFP_KERNEL) != anchor_id);
+
+ do {
+ int id = 0;
+ void *entry = idr_get_next(&find_idr, &id);
+ BUG_ON(entry != idr_mk_value(id));
+ } while (time(NULL) < start + 11);
+
+ pthread_join(throbber, NULL);
+
+ idr_remove(&find_idr, anchor_id);
+ BUG_ON(!idr_is_empty(&find_idr));
+}
+
+void idr_find_test(void)
+{
+ idr_find_test_1(100000, 0);
+ idr_find_test_1(0, 100000);
+}
+
void idr_checks(void)
{
unsigned long i;
@@ -307,6 +358,7 @@ void idr_checks(void)
idr_u32_test(4);
idr_u32_test(1);
idr_u32_test(0);
+ idr_find_test();
}
#define module_init(x)
diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
index cf16948..6af24f9 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.c
+++ b/tools/testing/selftests/bpf/cgroup_helpers.c
@@ -44,7 +44,7 @@
*/
int setup_cgroup_environment(void)
{
- char cgroup_workdir[PATH_MAX + 1];
+ char cgroup_workdir[PATH_MAX - 24];
format_cgroup_path(cgroup_workdir, "");
diff --git a/tools/testing/selftests/bpf/test_libbpf.sh b/tools/testing/selftests/bpf/test_libbpf.sh
index 8b1bc96..2989b2e 100755
--- a/tools/testing/selftests/bpf/test_libbpf.sh
+++ b/tools/testing/selftests/bpf/test_libbpf.sh
@@ -6,7 +6,7 @@
# Determine selftest success via shell exit code
exit_handler()
{
- if (( $? == 0 )); then
+ if [ $? -eq 0 ]; then
echo "selftests: $TESTNAME [PASS]";
else
echo "$TESTNAME: failed at file $LAST_LOADED" 1>&2
diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c
index 0c7d9e5..a7fc91b 100644
--- a/tools/testing/selftests/bpf/test_sockmap.c
+++ b/tools/testing/selftests/bpf/test_sockmap.c
@@ -248,6 +248,10 @@ static int msg_loop_sendpage(int fd, int iov_length, int cnt,
int i, fp;
file = fopen(".sendpage_tst.tmp", "w+");
+ if (!file) {
+ perror("create file for sendpage");
+ return 1;
+ }
for (i = 0; i < iov_length * cnt; i++, k++)
fwrite(&k, sizeof(char), 1, file);
fflush(file);
@@ -255,6 +259,11 @@ static int msg_loop_sendpage(int fd, int iov_length, int cnt,
fclose(file);
fp = open(".sendpage_tst.tmp", O_RDONLY);
+ if (fp < 0) {
+ perror("reopen file for sendpage");
+ return 1;
+ }
+
clock_gettime(CLOCK_MONOTONIC, &s->start);
for (i = 0; i < cnt; i++) {
int sent = sendfile(fd, fp, NULL, iov_length);
diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c
index cf156b3..82922f1 100644
--- a/tools/testing/selftests/bpf/trace_helpers.c
+++ b/tools/testing/selftests/bpf/trace_helpers.c
@@ -41,6 +41,7 @@ int load_kallsyms(void)
syms[i].name = strdup(func);
i++;
}
+ fclose(f);
sym_cnt = i;
qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
return 0;
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
index d026ff4..92ffb3b 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
@@ -78,8 +78,11 @@
echo "r ${PROBEFUNC} \$retval" > kprobe_events
! echo "p ${PROBEFUNC} \$retval" > kprobe_events
+# $comm was introduced in 4.8, older kernels reject it.
+if grep -A1 "fetcharg:" README | grep -q '\$comm' ; then
: "Comm access"
test_goodarg "\$comm"
+fi
: "Indirect memory access"
test_goodarg "+0(${GOODREG})" "-0(${GOODREG})" "+10(\$stack)" \
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc
index ce361b9..da298f1 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc
@@ -25,9 +25,9 @@
test $N -eq 256 && break
done
-L=`wc -l kprobe_events`
-if [ $L -ne $N ]; then
- echo "The number of kprobes events ($L) is not $N"
+L=`cat kprobe_events | wc -l`
+if [ $L -ne 256 ]; then
+ echo "The number of kprobes events ($L) is not 256"
exit_fail
fi
diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c
index 0c2cdc1..a9c4b5e 100644
--- a/tools/testing/selftests/kvm/dirty_log_test.c
+++ b/tools/testing/selftests/kvm/dirty_log_test.c
@@ -31,9 +31,9 @@
/* How many pages to dirty for each guest loop */
#define TEST_PAGES_PER_LOOP 1024
/* How many host loops to run (one KVM_GET_DIRTY_LOG for each loop) */
-#define TEST_HOST_LOOP_N 32
+#define TEST_HOST_LOOP_N 32UL
/* Interval for each host loop (ms) */
-#define TEST_HOST_LOOP_INTERVAL 10
+#define TEST_HOST_LOOP_INTERVAL 10UL
/*
* Guest variables. We use these variables to share data between host
diff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c
index cd01144..d306677 100644
--- a/tools/testing/selftests/kvm/lib/assert.c
+++ b/tools/testing/selftests/kvm/lib/assert.c
@@ -56,7 +56,7 @@ static void test_dump_stack(void)
#pragma GCC diagnostic pop
}
-static pid_t gettid(void)
+static pid_t _gettid(void)
{
return syscall(SYS_gettid);
}
@@ -73,7 +73,7 @@ test_assert(bool exp, const char *exp_str,
fprintf(stderr, "==== Test Assertion Failure ====\n"
" %s:%u: %s\n"
" pid=%d tid=%d - %s\n",
- file, line, exp_str, getpid(), gettid(),
+ file, line, exp_str, getpid(), _gettid(),
strerror(errno));
test_dump_stack();
if (fmt) {
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index 2f190aa..c0885fb 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -1301,6 +1301,27 @@
fi
log_test $rc 0 "Prefix route with metric on link up"
+ # explicitly check for metric changes on edge scenarios
+ run_cmd "$IP addr flush dev dummy2"
+ run_cmd "$IP addr add dev dummy2 172.16.104.0/24 metric 259"
+ run_cmd "$IP addr change dev dummy2 172.16.104.0/24 metric 260"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.0 metric 260"
+ rc=$?
+ fi
+ log_test $rc 0 "Modify metric of .0/24 address"
+
+ run_cmd "$IP addr flush dev dummy2"
+ run_cmd "$IP addr add dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 260"
+ run_cmd "$IP addr change dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 261"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ check_route "172.16.104.2 dev dummy2 proto kernel scope link src 172.16.104.1 metric 261"
+ rc=$?
+ fi
+ log_test $rc 0 "Modify metric of address with peer route"
+
$IP li del dummy1
$IP li del dummy2
cleanup
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
index ca53b53..08bac6c 100644
--- a/tools/testing/selftests/net/forwarding/lib.sh
+++ b/tools/testing/selftests/net/forwarding/lib.sh
@@ -251,7 +251,7 @@
{
local dev=$1; shift
- while lldptool -t -i $dev -V APP -c app | grep -q pending; do
+ while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
echo "$dev: waiting for lldpad to push pending APP updates"
sleep 5
done
diff --git a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
index fef88eb..fa6a88c 100755
--- a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
+++ b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
@@ -36,7 +36,7 @@
{
ip -6 route del 2001:db8:1::/64 vrf v$h2
ip -4 route del 192.0.2.0/28 vrf v$h2
- simple_if_fini $h2 192.0.2.130/28
+ simple_if_fini $h2 192.0.2.130/28 2001:db8:2::2/64
}
router_create()
diff --git a/tools/testing/selftests/net/reuseport_dualstack.c b/tools/testing/selftests/net/reuseport_dualstack.c
index fe3230c..fb7a59e 100644
--- a/tools/testing/selftests/net/reuseport_dualstack.c
+++ b/tools/testing/selftests/net/reuseport_dualstack.c
@@ -129,7 +129,7 @@ static void test(int *rcv_fds, int count, int proto)
{
struct epoll_event ev;
int epfd, i, test_fd;
- uint16_t test_family;
+ int test_family;
socklen_t len;
epfd = epoll_create(1);
@@ -146,6 +146,7 @@ static void test(int *rcv_fds, int count, int proto)
send_from_v4(proto);
test_fd = receive_once(epfd, proto);
+ len = sizeof(test_family);
if (getsockopt(test_fd, SOL_SOCKET, SO_DOMAIN, &test_family, &len))
error(1, errno, "failed to read socket domain");
if (test_family != AF_INET)
diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh
index e101af5..ff665de7 100755
--- a/tools/testing/selftests/net/rtnetlink.sh
+++ b/tools/testing/selftests/net/rtnetlink.sh
@@ -234,6 +234,26 @@
echo "PASS: route get"
}
+kci_test_addrlft()
+{
+ for i in $(seq 10 100) ;do
+ lft=$(((RANDOM%3) + 1))
+ ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1))
+ check_err $?
+ done
+
+ sleep 5
+
+ ip addr show dev "$devdummy" | grep "10.23.11."
+ if [ $? -eq 0 ]; then
+ echo "FAIL: preferred_lft addresses remaining"
+ check_err 1
+ return
+ fi
+
+ echo "PASS: preferred_lft addresses have expired"
+}
+
kci_test_addrlabel()
{
ret=0
@@ -965,6 +985,7 @@
kci_test_polrouting
kci_test_route_get
+ kci_test_addrlft
kci_test_tc
kci_test_gre
kci_test_gretap
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 8fdfeaf..7549d39 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -288,7 +288,7 @@ TEST_F(tls, splice_from_pipe)
ASSERT_GE(pipe(p), 0);
EXPECT_GE(write(p[1], mem_send, send_len), 0);
EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), 0);
- EXPECT_GE(recv(self->cfd, mem_recv, send_len, 0), 0);
+ EXPECT_EQ(recv(self->cfd, mem_recv, send_len, MSG_WAITALL), send_len);
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
}
@@ -322,13 +322,13 @@ TEST_F(tls, send_and_splice)
ASSERT_GE(pipe(p), 0);
EXPECT_EQ(send(self->fd, test_str, send_len2, 0), send_len2);
- EXPECT_NE(recv(self->cfd, buf, send_len2, 0), -1);
+ EXPECT_EQ(recv(self->cfd, buf, send_len2, MSG_WAITALL), send_len2);
EXPECT_EQ(memcmp(test_str, buf, send_len2), 0);
EXPECT_GE(write(p[1], mem_send, send_len), send_len);
EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), send_len);
- EXPECT_GE(recv(self->cfd, mem_recv, send_len, 0), 0);
+ EXPECT_EQ(recv(self->cfd, mem_recv, send_len, MSG_WAITALL), send_len);
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
}
@@ -516,17 +516,17 @@ TEST_F(tls, recv_peek_multiple_records)
len = strlen(test_str_second) + 1;
EXPECT_EQ(send(self->fd, test_str_second, len, 0), len);
- len = sizeof(buf);
+ len = strlen(test_str_first);
memset(buf, 0, len);
- EXPECT_NE(recv(self->cfd, buf, len, MSG_PEEK), -1);
+ EXPECT_EQ(recv(self->cfd, buf, len, MSG_PEEK | MSG_WAITALL), len);
/* MSG_PEEK can only peek into the current record. */
- len = strlen(test_str_first) + 1;
+ len = strlen(test_str_first);
EXPECT_EQ(memcmp(test_str_first, buf, len), 0);
- len = sizeof(buf);
+ len = strlen(test_str) + 1;
memset(buf, 0, len);
- EXPECT_NE(recv(self->cfd, buf, len, 0), -1);
+ EXPECT_EQ(recv(self->cfd, buf, len, MSG_WAITALL), len);
/* Non-MSG_PEEK will advance strparser (and therefore record)
* however.
@@ -543,9 +543,9 @@ TEST_F(tls, recv_peek_multiple_records)
len = strlen(test_str_second) + 1;
EXPECT_EQ(send(self->fd, test_str_second, len, 0), len);
- len = sizeof(buf);
+ len = strlen(test_str) + 1;
memset(buf, 0, len);
- EXPECT_NE(recv(self->cfd, buf, len, MSG_PEEK), -1);
+ EXPECT_EQ(recv(self->cfd, buf, len, MSG_PEEK | MSG_WAITALL), len);
len = strlen(test_str) + 1;
EXPECT_EQ(memcmp(test_str, buf, len), 0);
diff --git a/tools/testing/selftests/powerpc/cache_shape/Makefile b/tools/testing/selftests/powerpc/cache_shape/Makefile
index ede4d3d..689f6c8 100644
--- a/tools/testing/selftests/powerpc/cache_shape/Makefile
+++ b/tools/testing/selftests/powerpc/cache_shape/Makefile
@@ -1,12 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-TEST_PROGS := cache_shape
-
-all: $(TEST_PROGS)
-
-$(TEST_PROGS): ../harness.c ../utils.c
+TEST_GEN_PROGS := cache_shape
top_srcdir = ../../../../..
include ../../lib.mk
-clean:
- rm -f $(TEST_PROGS) *.o
+$(TEST_GEN_PROGS): ../harness.c ../utils.c
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile
index 33ced6e..8696282 100644
--- a/tools/testing/selftests/powerpc/mm/Makefile
+++ b/tools/testing/selftests/powerpc/mm/Makefile
@@ -3,6 +3,7 @@
$(MAKE) -C ../
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors
+TEST_GEN_PROGS_EXTENDED := tlbie_test
TEST_GEN_FILES := tempfile
top_srcdir = ../../../../..
@@ -15,3 +16,4 @@
$(OUTPUT)/tempfile:
dd if=/dev/zero of=$@ bs=64k count=1
+$(OUTPUT)/tlbie_test: LDLIBS += -lpthread
diff --git a/tools/testing/selftests/powerpc/mm/tlbie_test.c b/tools/testing/selftests/powerpc/mm/tlbie_test.c
new file mode 100644
index 0000000..f85a093
--- /dev/null
+++ b/tools/testing/selftests/powerpc/mm/tlbie_test.c
@@ -0,0 +1,734 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019, Nick Piggin, Gautham R. Shenoy, Aneesh Kumar K.V, IBM Corp.
+ */
+
+/*
+ *
+ * Test tlbie/mtpidr race. We have 4 threads doing flush/load/compare/store
+ * sequence in a loop. The same threads also rung a context switch task
+ * that does sched_yield() in loop.
+ *
+ * The snapshot thread mark the mmap area PROT_READ in between, make a copy
+ * and copy it back to the original area. This helps us to detect if any
+ * store continued to happen after we marked the memory PROT_READ.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <linux/futex.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <time.h>
+#include <stdarg.h>
+#include <sched.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/prctl.h>
+
+static inline void dcbf(volatile unsigned int *addr)
+{
+ __asm__ __volatile__ ("dcbf %y0; sync" : : "Z"(*(unsigned char *)addr) : "memory");
+}
+
+static void err_msg(char *msg)
+{
+
+ time_t now;
+ time(&now);
+ printf("=================================\n");
+ printf(" Error: %s\n", msg);
+ printf(" %s", ctime(&now));
+ printf("=================================\n");
+ exit(1);
+}
+
+static char *map1;
+static char *map2;
+static pid_t rim_process_pid;
+
+/*
+ * A "rim-sequence" is defined to be the sequence of the following
+ * operations performed on a memory word:
+ * 1) FLUSH the contents of that word.
+ * 2) LOAD the contents of that word.
+ * 3) COMPARE the contents of that word with the content that was
+ * previously stored at that word
+ * 4) STORE new content into that word.
+ *
+ * The threads in this test that perform the rim-sequence are termed
+ * as rim_threads.
+ */
+
+/*
+ * A "corruption" is defined to be the failed COMPARE operation in a
+ * rim-sequence.
+ *
+ * A rim_thread that detects a corruption informs about it to all the
+ * other rim_threads, and the mem_snapshot thread.
+ */
+static volatile unsigned int corruption_found;
+
+/*
+ * This defines the maximum number of rim_threads in this test.
+ *
+ * The THREAD_ID_BITS denote the number of bits required
+ * to represent the thread_ids [0..MAX_THREADS - 1].
+ * We are being a bit paranoid here and set it to 8 bits,
+ * though 6 bits suffice.
+ *
+ */
+#define MAX_THREADS 64
+#define THREAD_ID_BITS 8
+#define THREAD_ID_MASK ((1 << THREAD_ID_BITS) - 1)
+static unsigned int rim_thread_ids[MAX_THREADS];
+static pthread_t rim_threads[MAX_THREADS];
+
+
+/*
+ * Each rim_thread works on an exclusive "chunk" of size
+ * RIM_CHUNK_SIZE.
+ *
+ * The ith rim_thread works on the ith chunk.
+ *
+ * The ith chunk begins at
+ * map1 + (i * RIM_CHUNK_SIZE)
+ */
+#define RIM_CHUNK_SIZE 1024
+#define BITS_PER_BYTE 8
+#define WORD_SIZE (sizeof(unsigned int))
+#define WORD_BITS (WORD_SIZE * BITS_PER_BYTE)
+#define WORDS_PER_CHUNK (RIM_CHUNK_SIZE/WORD_SIZE)
+
+static inline char *compute_chunk_start_addr(unsigned int thread_id)
+{
+ char *chunk_start;
+
+ chunk_start = (char *)((unsigned long)map1 +
+ (thread_id * RIM_CHUNK_SIZE));
+
+ return chunk_start;
+}
+
+/*
+ * The "word-offset" of a word-aligned address inside a chunk, is
+ * defined to be the number of words that precede the address in that
+ * chunk.
+ *
+ * WORD_OFFSET_BITS denote the number of bits required to represent
+ * the word-offsets of all the word-aligned addresses of a chunk.
+ */
+#define WORD_OFFSET_BITS (__builtin_ctz(WORDS_PER_CHUNK))
+#define WORD_OFFSET_MASK ((1 << WORD_OFFSET_BITS) - 1)
+
+static inline unsigned int compute_word_offset(char *start, unsigned int *addr)
+{
+ unsigned int delta_bytes, ret;
+ delta_bytes = (unsigned long)addr - (unsigned long)start;
+
+ ret = delta_bytes/WORD_SIZE;
+
+ return ret;
+}
+
+/*
+ * A "sweep" is defined to be the sequential execution of the
+ * rim-sequence by a rim_thread on its chunk one word at a time,
+ * starting from the first word of its chunk and ending with the last
+ * word of its chunk.
+ *
+ * Each sweep of a rim_thread is uniquely identified by a sweep_id.
+ * SWEEP_ID_BITS denote the number of bits required to represent
+ * the sweep_ids of rim_threads.
+ *
+ * As to why SWEEP_ID_BITS are computed as a function of THREAD_ID_BITS,
+ * WORD_OFFSET_BITS, and WORD_BITS, see the "store-pattern" below.
+ */
+#define SWEEP_ID_BITS (WORD_BITS - (THREAD_ID_BITS + WORD_OFFSET_BITS))
+#define SWEEP_ID_MASK ((1 << SWEEP_ID_BITS) - 1)
+
+/*
+ * A "store-pattern" is the word-pattern that is stored into a word
+ * location in the 4)STORE step of the rim-sequence.
+ *
+ * In the store-pattern, we shall encode:
+ *
+ * - The thread-id of the rim_thread performing the store
+ * (The most significant THREAD_ID_BITS)
+ *
+ * - The word-offset of the address into which the store is being
+ * performed (The next WORD_OFFSET_BITS)
+ *
+ * - The sweep_id of the current sweep in which the store is
+ * being performed. (The lower SWEEP_ID_BITS)
+ *
+ * Store Pattern: 32 bits
+ * |------------------|--------------------|---------------------------------|
+ * | Thread id | Word offset | sweep_id |
+ * |------------------|--------------------|---------------------------------|
+ * THREAD_ID_BITS WORD_OFFSET_BITS SWEEP_ID_BITS
+ *
+ * In the store pattern, the (Thread-id + Word-offset) uniquely identify the
+ * address to which the store is being performed i.e,
+ * address == map1 +
+ * (Thread-id * RIM_CHUNK_SIZE) + (Word-offset * WORD_SIZE)
+ *
+ * And the sweep_id in the store pattern identifies the time when the
+ * store was performed by the rim_thread.
+ *
+ * We shall use this property in the 3)COMPARE step of the
+ * rim-sequence.
+ */
+#define SWEEP_ID_SHIFT 0
+#define WORD_OFFSET_SHIFT (SWEEP_ID_BITS)
+#define THREAD_ID_SHIFT (WORD_OFFSET_BITS + SWEEP_ID_BITS)
+
+/*
+ * Compute the store pattern for a given thread with id @tid, at
+ * location @addr in the sweep identified by @sweep_id
+ */
+static inline unsigned int compute_store_pattern(unsigned int tid,
+ unsigned int *addr,
+ unsigned int sweep_id)
+{
+ unsigned int ret = 0;
+ char *start = compute_chunk_start_addr(tid);
+ unsigned int word_offset = compute_word_offset(start, addr);
+
+ ret += (tid & THREAD_ID_MASK) << THREAD_ID_SHIFT;
+ ret += (word_offset & WORD_OFFSET_MASK) << WORD_OFFSET_SHIFT;
+ ret += (sweep_id & SWEEP_ID_MASK) << SWEEP_ID_SHIFT;
+ return ret;
+}
+
+/* Extract the thread-id from the given store-pattern */
+static inline unsigned int extract_tid(unsigned int pattern)
+{
+ unsigned int ret;
+
+ ret = (pattern >> THREAD_ID_SHIFT) & THREAD_ID_MASK;
+ return ret;
+}
+
+/* Extract the word-offset from the given store-pattern */
+static inline unsigned int extract_word_offset(unsigned int pattern)
+{
+ unsigned int ret;
+
+ ret = (pattern >> WORD_OFFSET_SHIFT) & WORD_OFFSET_MASK;
+
+ return ret;
+}
+
+/* Extract the sweep-id from the given store-pattern */
+static inline unsigned int extract_sweep_id(unsigned int pattern)
+
+{
+ unsigned int ret;
+
+ ret = (pattern >> SWEEP_ID_SHIFT) & SWEEP_ID_MASK;
+
+ return ret;
+}
+
+/************************************************************
+ * *
+ * Logging the output of the verification *
+ * *
+ ************************************************************/
+#define LOGDIR_NAME_SIZE 100
+static char logdir[LOGDIR_NAME_SIZE];
+
+static FILE *fp[MAX_THREADS];
+static const char logfilename[] ="Thread-%02d-Chunk";
+
+static inline void start_verification_log(unsigned int tid,
+ unsigned int *addr,
+ unsigned int cur_sweep_id,
+ unsigned int prev_sweep_id)
+{
+ FILE *f;
+ char logfile[30];
+ char path[LOGDIR_NAME_SIZE + 30];
+ char separator[2] = "/";
+ char *chunk_start = compute_chunk_start_addr(tid);
+ unsigned int size = RIM_CHUNK_SIZE;
+
+ sprintf(logfile, logfilename, tid);
+ strcpy(path, logdir);
+ strcat(path, separator);
+ strcat(path, logfile);
+ f = fopen(path, "w");
+
+ if (!f) {
+ err_msg("Unable to create logfile\n");
+ }
+
+ fp[tid] = f;
+
+ fprintf(f, "----------------------------------------------------------\n");
+ fprintf(f, "PID = %d\n", rim_process_pid);
+ fprintf(f, "Thread id = %02d\n", tid);
+ fprintf(f, "Chunk Start Addr = 0x%016lx\n", (unsigned long)chunk_start);
+ fprintf(f, "Chunk Size = %d\n", size);
+ fprintf(f, "Next Store Addr = 0x%016lx\n", (unsigned long)addr);
+ fprintf(f, "Current sweep-id = 0x%08x\n", cur_sweep_id);
+ fprintf(f, "Previous sweep-id = 0x%08x\n", prev_sweep_id);
+ fprintf(f, "----------------------------------------------------------\n");
+}
+
+static inline void log_anamoly(unsigned int tid, unsigned int *addr,
+ unsigned int expected, unsigned int observed)
+{
+ FILE *f = fp[tid];
+
+ fprintf(f, "Thread %02d: Addr 0x%lx: Expected 0x%x, Observed 0x%x\n",
+ tid, (unsigned long)addr, expected, observed);
+ fprintf(f, "Thread %02d: Expected Thread id = %02d\n", tid, extract_tid(expected));
+ fprintf(f, "Thread %02d: Observed Thread id = %02d\n", tid, extract_tid(observed));
+ fprintf(f, "Thread %02d: Expected Word offset = %03d\n", tid, extract_word_offset(expected));
+ fprintf(f, "Thread %02d: Observed Word offset = %03d\n", tid, extract_word_offset(observed));
+ fprintf(f, "Thread %02d: Expected sweep-id = 0x%x\n", tid, extract_sweep_id(expected));
+ fprintf(f, "Thread %02d: Observed sweep-id = 0x%x\n", tid, extract_sweep_id(observed));
+ fprintf(f, "----------------------------------------------------------\n");
+}
+
+static inline void end_verification_log(unsigned int tid, unsigned nr_anamolies)
+{
+ FILE *f = fp[tid];
+ char logfile[30];
+ char path[LOGDIR_NAME_SIZE + 30];
+ char separator[] = "/";
+
+ fclose(f);
+
+ if (nr_anamolies == 0) {
+ remove(path);
+ return;
+ }
+
+ sprintf(logfile, logfilename, tid);
+ strcpy(path, logdir);
+ strcat(path, separator);
+ strcat(path, logfile);
+
+ printf("Thread %02d chunk has %d corrupted words. For details check %s\n",
+ tid, nr_anamolies, path);
+}
+
+/*
+ * When a COMPARE step of a rim-sequence fails, the rim_thread informs
+ * everyone else via the shared_memory pointed to by
+ * corruption_found variable. On seeing this, every thread verifies the
+ * content of its chunk as follows.
+ *
+ * Suppose a thread identified with @tid was about to store (but not
+ * yet stored) to @next_store_addr in its current sweep identified
+ * @cur_sweep_id. Let @prev_sweep_id indicate the previous sweep_id.
+ *
+ * This implies that for all the addresses @addr < @next_store_addr,
+ * Thread @tid has already performed a store as part of its current
+ * sweep. Hence we expect the content of such @addr to be:
+ * |-------------------------------------------------|
+ * | tid | word_offset(addr) | cur_sweep_id |
+ * |-------------------------------------------------|
+ *
+ * Since Thread @tid is yet to perform stores on address
+ * @next_store_addr and above, we expect the content of such an
+ * address @addr to be:
+ * |-------------------------------------------------|
+ * | tid | word_offset(addr) | prev_sweep_id |
+ * |-------------------------------------------------|
+ *
+ * The verifier function @verify_chunk does this verification and logs
+ * any anamolies that it finds.
+ */
+static void verify_chunk(unsigned int tid, unsigned int *next_store_addr,
+ unsigned int cur_sweep_id,
+ unsigned int prev_sweep_id)
+{
+ unsigned int *iter_ptr;
+ unsigned int size = RIM_CHUNK_SIZE;
+ unsigned int expected;
+ unsigned int observed;
+ char *chunk_start = compute_chunk_start_addr(tid);
+
+ int nr_anamolies = 0;
+
+ start_verification_log(tid, next_store_addr,
+ cur_sweep_id, prev_sweep_id);
+
+ for (iter_ptr = (unsigned int *)chunk_start;
+ (unsigned long)iter_ptr < (unsigned long)chunk_start + size;
+ iter_ptr++) {
+ unsigned int expected_sweep_id;
+
+ if (iter_ptr < next_store_addr) {
+ expected_sweep_id = cur_sweep_id;
+ } else {
+ expected_sweep_id = prev_sweep_id;
+ }
+
+ expected = compute_store_pattern(tid, iter_ptr, expected_sweep_id);
+
+ dcbf((volatile unsigned int*)iter_ptr); //Flush before reading
+ observed = *iter_ptr;
+
+ if (observed != expected) {
+ nr_anamolies++;
+ log_anamoly(tid, iter_ptr, expected, observed);
+ }
+ }
+
+ end_verification_log(tid, nr_anamolies);
+}
+
+static void set_pthread_cpu(pthread_t th, int cpu)
+{
+ cpu_set_t run_cpu_mask;
+ struct sched_param param;
+
+ CPU_ZERO(&run_cpu_mask);
+ CPU_SET(cpu, &run_cpu_mask);
+ pthread_setaffinity_np(th, sizeof(cpu_set_t), &run_cpu_mask);
+
+ param.sched_priority = 1;
+ if (0 && sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
+ /* haven't reproduced with this setting, it kills random preemption which may be a factor */
+ fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
+ }
+}
+
+static void set_mycpu(int cpu)
+{
+ cpu_set_t run_cpu_mask;
+ struct sched_param param;
+
+ CPU_ZERO(&run_cpu_mask);
+ CPU_SET(cpu, &run_cpu_mask);
+ sched_setaffinity(0, sizeof(cpu_set_t), &run_cpu_mask);
+
+ param.sched_priority = 1;
+ if (0 && sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
+ fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
+ }
+}
+
+static volatile int segv_wait;
+
+static void segv_handler(int signo, siginfo_t *info, void *extra)
+{
+ while (segv_wait) {
+ sched_yield();
+ }
+
+}
+
+static void set_segv_handler(void)
+{
+ struct sigaction sa;
+
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = segv_handler;
+
+ if (sigaction(SIGSEGV, &sa, NULL) == -1) {
+ perror("sigaction");
+ exit(EXIT_FAILURE);
+ }
+}
+
+int timeout = 0;
+/*
+ * This function is executed by every rim_thread.
+ *
+ * This function performs sweeps over the exclusive chunks of the
+ * rim_threads executing the rim-sequence one word at a time.
+ */
+static void *rim_fn(void *arg)
+{
+ unsigned int tid = *((unsigned int *)arg);
+
+ int size = RIM_CHUNK_SIZE;
+ char *chunk_start = compute_chunk_start_addr(tid);
+
+ unsigned int prev_sweep_id;
+ unsigned int cur_sweep_id = 0;
+
+ /* word access */
+ unsigned int pattern = cur_sweep_id;
+ unsigned int *pattern_ptr = &pattern;
+ unsigned int *w_ptr, read_data;
+
+ set_segv_handler();
+
+ /*
+ * Let us initialize the chunk:
+ *
+ * Each word-aligned address addr in the chunk,
+ * is initialized to :
+ * |-------------------------------------------------|
+ * | tid | word_offset(addr) | 0 |
+ * |-------------------------------------------------|
+ */
+ for (w_ptr = (unsigned int *)chunk_start;
+ (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
+ w_ptr++) {
+
+ *pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
+ *w_ptr = *pattern_ptr;
+ }
+
+ while (!corruption_found && !timeout) {
+ prev_sweep_id = cur_sweep_id;
+ cur_sweep_id = cur_sweep_id + 1;
+
+ for (w_ptr = (unsigned int *)chunk_start;
+ (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
+ w_ptr++) {
+ unsigned int old_pattern;
+
+ /*
+ * Compute the pattern that we would have
+ * stored at this location in the previous
+ * sweep.
+ */
+ old_pattern = compute_store_pattern(tid, w_ptr, prev_sweep_id);
+
+ /*
+ * FLUSH:Ensure that we flush the contents of
+ * the cache before loading
+ */
+ dcbf((volatile unsigned int*)w_ptr); //Flush
+
+ /* LOAD: Read the value */
+ read_data = *w_ptr; //Load
+
+ /*
+ * COMPARE: Is it the same as what we had stored
+ * in the previous sweep ? It better be!
+ */
+ if (read_data != old_pattern) {
+ /* No it isn't! Tell everyone */
+ corruption_found = 1;
+ }
+
+ /*
+ * Before performing a store, let us check if
+ * any rim_thread has found a corruption.
+ */
+ if (corruption_found || timeout) {
+ /*
+ * Yes. Someone (including us!) has found
+ * a corruption :(
+ *
+ * Let us verify that our chunk is
+ * correct.
+ */
+ /* But first, let us allow the dust to settle down! */
+ verify_chunk(tid, w_ptr, cur_sweep_id, prev_sweep_id);
+
+ return 0;
+ }
+
+ /*
+ * Compute the new pattern that we are going
+ * to write to this location
+ */
+ *pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
+
+ /*
+ * STORE: Now let us write this pattern into
+ * the location
+ */
+ *w_ptr = *pattern_ptr;
+ }
+ }
+
+ return NULL;
+}
+
+
+static unsigned long start_cpu = 0;
+static unsigned long nrthreads = 4;
+
+static pthread_t mem_snapshot_thread;
+
+static void *mem_snapshot_fn(void *arg)
+{
+ int page_size = getpagesize();
+ size_t size = page_size;
+ void *tmp = malloc(size);
+
+ while (!corruption_found && !timeout) {
+ /* Stop memory migration once corruption is found */
+ segv_wait = 1;
+
+ mprotect(map1, size, PROT_READ);
+
+ /*
+ * Load from the working alias (map1). Loading from map2
+ * also fails.
+ */
+ memcpy(tmp, map1, size);
+
+ /*
+ * Stores must go via map2 which has write permissions, but
+ * the corrupted data tends to be seen in the snapshot buffer,
+ * so corruption does not appear to be introduced at the
+ * copy-back via map2 alias here.
+ */
+ memcpy(map2, tmp, size);
+ /*
+ * Before releasing other threads, must ensure the copy
+ * back to
+ */
+ asm volatile("sync" ::: "memory");
+ mprotect(map1, size, PROT_READ|PROT_WRITE);
+ asm volatile("sync" ::: "memory");
+ segv_wait = 0;
+
+ usleep(1); /* This value makes a big difference */
+ }
+
+ return 0;
+}
+
+void alrm_sighandler(int sig)
+{
+ timeout = 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ int page_size = getpagesize();
+ time_t now;
+ int i, dir_error;
+ pthread_attr_t attr;
+ key_t shm_key = (key_t) getpid();
+ int shmid, run_time = 20 * 60;
+ struct sigaction sa_alrm;
+
+ snprintf(logdir, LOGDIR_NAME_SIZE,
+ "/tmp/logdir-%u", (unsigned int)getpid());
+ while ((c = getopt(argc, argv, "r:hn:l:t:")) != -1) {
+ switch(c) {
+ case 'r':
+ start_cpu = strtoul(optarg, NULL, 10);
+ break;
+ case 'h':
+ printf("%s [-r <start_cpu>] [-n <nrthreads>] [-l <logdir>] [-t <timeout>]\n", argv[0]);
+ exit(0);
+ break;
+ case 'n':
+ nrthreads = strtoul(optarg, NULL, 10);
+ break;
+ case 'l':
+ strncpy(logdir, optarg, LOGDIR_NAME_SIZE - 1);
+ break;
+ case 't':
+ run_time = strtoul(optarg, NULL, 10);
+ break;
+ default:
+ printf("invalid option\n");
+ exit(0);
+ break;
+ }
+ }
+
+ if (nrthreads > MAX_THREADS)
+ nrthreads = MAX_THREADS;
+
+ shmid = shmget(shm_key, page_size, IPC_CREAT|0666);
+ if (shmid < 0) {
+ err_msg("Failed shmget\n");
+ }
+
+ map1 = shmat(shmid, NULL, 0);
+ if (map1 == (void *) -1) {
+ err_msg("Failed shmat");
+ }
+
+ map2 = shmat(shmid, NULL, 0);
+ if (map2 == (void *) -1) {
+ err_msg("Failed shmat");
+ }
+
+ dir_error = mkdir(logdir, 0755);
+
+ if (dir_error) {
+ err_msg("Failed mkdir");
+ }
+
+ printf("start_cpu list:%lu\n", start_cpu);
+ printf("number of worker threads:%lu + 1 snapshot thread\n", nrthreads);
+ printf("Allocated address:0x%016lx + secondary map:0x%016lx\n", (unsigned long)map1, (unsigned long)map2);
+ printf("logdir at : %s\n", logdir);
+ printf("Timeout: %d seconds\n", run_time);
+
+ time(&now);
+ printf("=================================\n");
+ printf(" Starting Test\n");
+ printf(" %s", ctime(&now));
+ printf("=================================\n");
+
+ for (i = 0; i < nrthreads; i++) {
+ if (1 && !fork()) {
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+ set_mycpu(start_cpu + i);
+ for (;;)
+ sched_yield();
+ exit(0);
+ }
+ }
+
+
+ sa_alrm.sa_handler = &alrm_sighandler;
+ sigemptyset(&sa_alrm.sa_mask);
+ sa_alrm.sa_flags = 0;
+
+ if (sigaction(SIGALRM, &sa_alrm, 0) == -1) {
+ err_msg("Failed signal handler registration\n");
+ }
+
+ alarm(run_time);
+
+ pthread_attr_init(&attr);
+ for (i = 0; i < nrthreads; i++) {
+ rim_thread_ids[i] = i;
+ pthread_create(&rim_threads[i], &attr, rim_fn, &rim_thread_ids[i]);
+ set_pthread_cpu(rim_threads[i], start_cpu + i);
+ }
+
+ pthread_create(&mem_snapshot_thread, &attr, mem_snapshot_fn, map1);
+ set_pthread_cpu(mem_snapshot_thread, start_cpu + i);
+
+
+ pthread_join(mem_snapshot_thread, NULL);
+ for (i = 0; i < nrthreads; i++) {
+ pthread_join(rim_threads[i], NULL);
+ }
+
+ if (!timeout) {
+ time(&now);
+ printf("=================================\n");
+ printf(" Data Corruption Detected\n");
+ printf(" %s", ctime(&now));
+ printf(" See logfiles in %s\n", logdir);
+ printf("=================================\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 923d531..9f94234 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
+TEST_GEN_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \
perf-hwbreak
@@ -7,14 +7,9 @@
top_srcdir = ../../../../..
include ../../lib.mk
-all: $(TEST_PROGS)
-
CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
-ptrace-pkey core-pkey: child.h
-ptrace-pkey core-pkey: LDLIBS += -pthread
+$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: child.h
+$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: LDLIBS += -pthread
-$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
-
-clean:
- rm -f $(TEST_PROGS) *.o
+$(TEST_GEN_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
index e23e2e1..d5c64fe 100644
--- a/tools/testing/selftests/powerpc/ptrace/core-pkey.c
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -352,10 +352,7 @@ static int write_core_pattern(const char *core_pattern)
FILE *f;
f = fopen(core_pattern_file, "w");
- if (!f) {
- perror("Error writing to core_pattern file");
- return TEST_FAIL;
- }
+ SKIP_IF_MSG(!f, "Try with root privileges");
ret = fwrite(core_pattern, 1, len, f);
fclose(f);
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
index 0b4ebcc..ca29faf 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
@@ -31,7 +31,7 @@ void gpr(void)
ASM_LOAD_GPR_IMMED(gpr_1)
ASM_LOAD_FPR_SINGLE_PRECISION(flt_1)
:
- : [gpr_1]"i"(GPR_1), [flt_1] "r" (&a)
+ : [gpr_1]"i"(GPR_1), [flt_1] "b" (&a)
: "memory", "r6", "r7", "r8", "r9", "r10",
"r11", "r12", "r13", "r14", "r15", "r16", "r17",
"r18", "r19", "r20", "r21", "r22", "r23", "r24",
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
index 59206b9..a08a915 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
@@ -59,8 +59,8 @@ void tm_gpr(void)
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
: [gpr_1]"i"(GPR_1), [gpr_2]"i"(GPR_2),
- [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "r" (&a),
- [flt_2] "r" (&b), [cptr1] "r" (&cptr[1])
+ [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "b" (&a),
+ [flt_2] "b" (&b), [cptr1] "b" (&cptr[1])
: "memory", "r7", "r8", "r9", "r10",
"r11", "r12", "r13", "r14", "r15", "r16",
"r17", "r18", "r19", "r20", "r21", "r22",
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
index b3c061d..f471747 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
@@ -72,7 +72,7 @@ void tm_spd_tar(void)
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
- : [val] "r" (cptr[1]), [sprn_dscr]"i"(SPRN_DSCR),
+ : [sprn_dscr]"i"(SPRN_DSCR),
[sprn_tar]"i"(SPRN_TAR), [sprn_ppr]"i"(SPRN_PPR),
[sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1),
[dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2),
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
index 277dade..18a685b 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
@@ -77,8 +77,7 @@ void tm_spd_vsx(void)
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
- : [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt),
- [sprn_texasr] "i" (SPRN_TEXASR)
+ : [sprn_texasr] "i" (SPRN_TEXASR)
: "memory", "r0", "r1", "r3", "r4",
"r7", "r8", "r9", "r10", "r11"
);
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
index 51427a2..ba04999 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
@@ -74,7 +74,7 @@ void tm_spr(void)
"3: ;"
: [tfhar] "=r" (tfhar), [res] "=r" (result),
- [texasr] "=r" (texasr), [cptr1] "=r" (cptr1)
+ [texasr] "=r" (texasr), [cptr1] "=b" (cptr1)
: [sprn_texasr] "i" (SPRN_TEXASR)
: "memory", "r0", "r8", "r31"
);
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
index 48b462f..f70023b 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
@@ -65,7 +65,7 @@ void tm_tar(void)
: [sprn_dscr]"i"(SPRN_DSCR), [sprn_tar]"i"(SPRN_TAR),
[sprn_ppr]"i"(SPRN_PPR), [sprn_texasr]"i"(SPRN_TEXASR),
[tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2),
- [dscr_2]"i"(DSCR_2), [cptr1] "r" (&cptr[1])
+ [dscr_2]"i"(DSCR_2), [cptr1] "b" (&cptr[1])
: "memory", "r0", "r1", "r3", "r4", "r5", "r6"
);
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
index 17c23ca..dfba800 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
@@ -65,8 +65,7 @@ void tm_vsx(void)
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
- : [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt),
- [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "r" (&cptr[1])
+ : [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "b" (&cptr[1])
: "memory", "r0", "r1", "r3", "r4",
"r7", "r8", "r9", "r10", "r11"
);
diff --git a/tools/testing/selftests/powerpc/signal/Makefile b/tools/testing/selftests/powerpc/signal/Makefile
index 1fca25c..209a958 100644
--- a/tools/testing/selftests/powerpc/signal/Makefile
+++ b/tools/testing/selftests/powerpc/signal/Makefile
@@ -1,15 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
-TEST_PROGS := signal signal_tm
-
-all: $(TEST_PROGS)
-
-$(TEST_PROGS): ../harness.c ../utils.c signal.S
+TEST_GEN_PROGS := signal signal_tm
CFLAGS += -maltivec
-signal_tm: CFLAGS += -mhtm
+$(OUTPUT)/signal_tm: CFLAGS += -mhtm
top_srcdir = ../../../../..
include ../../lib.mk
-clean:
- rm -f $(TEST_PROGS) *.o
+$(TEST_GEN_PROGS): ../harness.c ../utils.c signal.S
diff --git a/tools/testing/selftests/powerpc/switch_endian/Makefile b/tools/testing/selftests/powerpc/switch_endian/Makefile
index fcd2dcb..bdc081a 100644
--- a/tools/testing/selftests/powerpc/switch_endian/Makefile
+++ b/tools/testing/selftests/powerpc/switch_endian/Makefile
@@ -8,6 +8,7 @@
top_srcdir = ../../../../..
include ../../lib.mk
+$(OUTPUT)/switch_endian_test: ASFLAGS += -I $(OUTPUT)
$(OUTPUT)/switch_endian_test: $(OUTPUT)/check-reversed.S
$(OUTPUT)/check-reversed.o: $(OUTPUT)/check.o
diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
index 156c8e7..09894f4 100644
--- a/tools/testing/selftests/powerpc/tm/tm-unavailable.c
+++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
@@ -236,7 +236,8 @@ void *tm_una_ping(void *input)
}
/* Check if we were not expecting a failure and a it occurred. */
- if (!expecting_failure() && is_failure(cr_)) {
+ if (!expecting_failure() && is_failure(cr_) &&
+ !failure_is_reschedule()) {
printf("\n\tUnexpected transaction failure 0x%02lx\n\t",
failure_code());
return (void *) -1;
@@ -244,9 +245,11 @@ void *tm_una_ping(void *input)
/*
* Check if TM failed due to the cause we were expecting. 0xda is a
- * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause.
+ * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause, unless
+ * it was caused by a reschedule.
*/
- if (is_failure(cr_) && !failure_is_unavailable()) {
+ if (is_failure(cr_) && !failure_is_unavailable() &&
+ !failure_is_reschedule()) {
printf("\n\tUnexpected failure cause 0x%02lx\n\t",
failure_code());
return (void *) -1;
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h
index df420424..5518b1d 100644
--- a/tools/testing/selftests/powerpc/tm/tm.h
+++ b/tools/testing/selftests/powerpc/tm/tm.h
@@ -52,6 +52,15 @@ static inline bool failure_is_unavailable(void)
return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV;
}
+static inline bool failure_is_reschedule(void)
+{
+ if ((failure_code() & TM_CAUSE_RESCHED) == TM_CAUSE_RESCHED ||
+ (failure_code() & TM_CAUSE_KVM_RESCHED) == TM_CAUSE_KVM_RESCHED)
+ return true;
+
+ return false;
+}
+
static inline bool failure_is_nesting(void)
{
return (__builtin_get_texasru() & 0x400000);
diff --git a/tools/testing/selftests/proc/fd-001-lookup.c b/tools/testing/selftests/proc/fd-001-lookup.c
index a2010df..60d7948 100644
--- a/tools/testing/selftests/proc/fd-001-lookup.c
+++ b/tools/testing/selftests/proc/fd-001-lookup.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Test /proc/*/fd lookup.
-#define _GNU_SOURCE
+
#undef NDEBUG
#include <assert.h>
#include <dirent.h>
diff --git a/tools/testing/selftests/proc/fd-003-kthread.c b/tools/testing/selftests/proc/fd-003-kthread.c
index 1d659d5..dc591f9 100644
--- a/tools/testing/selftests/proc/fd-003-kthread.c
+++ b/tools/testing/selftests/proc/fd-003-kthread.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Test that /proc/$KERNEL_THREAD/fd/ is empty.
-#define _GNU_SOURCE
+
#undef NDEBUG
#include <sys/syscall.h>
#include <assert.h>
diff --git a/tools/testing/selftests/rseq/param_test.c b/tools/testing/selftests/rseq/param_test.c
index eec2663..e8a657a 100644
--- a/tools/testing/selftests/rseq/param_test.c
+++ b/tools/testing/selftests/rseq/param_test.c
@@ -15,7 +15,7 @@
#include <errno.h>
#include <stddef.h>
-static inline pid_t gettid(void)
+static inline pid_t rseq_gettid(void)
{
return syscall(__NR_gettid);
}
@@ -373,11 +373,12 @@ void *test_percpu_spinlock_thread(void *arg)
rseq_percpu_unlock(&data->lock, cpu);
#ifndef BENCHMARK
if (i != 0 && !(i % (reps / 10)))
- printf_verbose("tid %d: count %lld\n", (int) gettid(), i);
+ printf_verbose("tid %d: count %lld\n",
+ (int) rseq_gettid(), i);
#endif
}
printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
- (int) gettid(), nr_abort, signals_delivered);
+ (int) rseq_gettid(), nr_abort, signals_delivered);
if (!opt_disable_rseq && thread_data->reg &&
rseq_unregister_current_thread())
abort();
@@ -454,11 +455,12 @@ void *test_percpu_inc_thread(void *arg)
} while (rseq_unlikely(ret));
#ifndef BENCHMARK
if (i != 0 && !(i % (reps / 10)))
- printf_verbose("tid %d: count %lld\n", (int) gettid(), i);
+ printf_verbose("tid %d: count %lld\n",
+ (int) rseq_gettid(), i);
#endif
}
printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
- (int) gettid(), nr_abort, signals_delivered);
+ (int) rseq_gettid(), nr_abort, signals_delivered);
if (!opt_disable_rseq && thread_data->reg &&
rseq_unregister_current_thread())
abort();
@@ -605,7 +607,7 @@ void *test_percpu_list_thread(void *arg)
}
printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
- (int) gettid(), nr_abort, signals_delivered);
+ (int) rseq_gettid(), nr_abort, signals_delivered);
if (!opt_disable_rseq && rseq_unregister_current_thread())
abort();
@@ -796,7 +798,7 @@ void *test_percpu_buffer_thread(void *arg)
}
printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
- (int) gettid(), nr_abort, signals_delivered);
+ (int) rseq_gettid(), nr_abort, signals_delivered);
if (!opt_disable_rseq && rseq_unregister_current_thread())
abort();
@@ -1011,7 +1013,7 @@ void *test_percpu_memcpy_buffer_thread(void *arg)
}
printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
- (int) gettid(), nr_abort, signals_delivered);
+ (int) rseq_gettid(), nr_abort, signals_delivered);
if (!opt_disable_rseq && rseq_unregister_current_thread())
abort();
diff --git a/tools/testing/selftests/tc-testing/bpf/Makefile b/tools/testing/selftests/tc-testing/bpf/Makefile
new file mode 100644
index 0000000..dc92eb2
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/bpf/Makefile
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0
+
+APIDIR := ../../../../include/uapi
+TEST_GEN_FILES = action.o
+
+top_srcdir = ../../../../..
+include ../../lib.mk
+
+CLANG ?= clang
+LLC ?= llc
+PROBE := $(shell $(LLC) -march=bpf -mcpu=probe -filetype=null /dev/null 2>&1)
+
+ifeq ($(PROBE),)
+ CPU ?= probe
+else
+ CPU ?= generic
+endif
+
+CLANG_SYS_INCLUDES := $(shell $(CLANG) -v -E - </dev/null 2>&1 \
+ | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
+
+CLANG_FLAGS = -I. -I$(APIDIR) \
+ $(CLANG_SYS_INCLUDES) \
+ -Wno-compare-distinct-pointer-types
+
+$(OUTPUT)/%.o: %.c
+ $(CLANG) $(CLANG_FLAGS) \
+ -O2 -target bpf -emit-llvm -c $< -o - | \
+ $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
diff --git a/tools/testing/selftests/tc-testing/bpf/action.c b/tools/testing/selftests/tc-testing/bpf/action.c
new file mode 100644
index 0000000..c32b99b
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/bpf/action.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Copyright (c) 2018 Davide Caratti, Red Hat inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+
+__attribute__((section("action-ok"),used)) int action_ok(struct __sk_buff *s)
+{
+ return TC_ACT_OK;
+}
+
+__attribute__((section("action-ko"),used)) int action_ko(struct __sk_buff *s)
+{
+ s->data = 0x0;
+ return TC_ACT_OK;
+}
+
+char _license[] __attribute__((section("license"),used)) = "GPL";
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
index 6f289a49e..1a9b282d 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
@@ -55,7 +55,7 @@
"bpf"
],
"setup": [
- "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { return 2; }' | clang -O2 -x c -c - -target bpf -o _b.o",
+ "make -C bpf",
[
"$TC action flush action bpf",
0,
@@ -63,14 +63,14 @@
255
]
],
- "cmdUnderTest": "$TC action add action bpf object-file _b.o index 667",
+ "cmdUnderTest": "$TC action add action bpf object-file $EBPFDIR/action.o section action-ok index 667",
"expExitCode": "0",
"verifyCmd": "$TC action get action bpf index 667",
- "matchPattern": "action order [0-9]*: bpf _b.o:\\[action\\] id [0-9]* tag 3b185187f1855c4c( jited)? default-action pipe.*index 667 ref",
+ "matchPattern": "action order [0-9]*: bpf action.o:\\[action-ok\\] id [0-9]* tag [0-9a-f]{16}( jited)? default-action pipe.*index 667 ref",
"matchCount": "1",
"teardown": [
"$TC action flush action bpf",
- "rm -f _b.o"
+ "make -C bpf clean"
]
},
{
@@ -81,7 +81,7 @@
"bpf"
],
"setup": [
- "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { s->data = 0x0; return 2; }' | clang -O2 -x c -c - -target bpf -o _c.o",
+ "make -C bpf",
[
"$TC action flush action bpf",
0,
@@ -89,10 +89,10 @@
255
]
],
- "cmdUnderTest": "$TC action add action bpf object-file _c.o index 667",
+ "cmdUnderTest": "$TC action add action bpf object-file $EBPFDIR/action.o section action-ko index 667",
"expExitCode": "255",
"verifyCmd": "$TC action get action bpf index 667",
- "matchPattern": "action order [0-9]*: bpf _c.o:\\[action\\] id [0-9].*index 667 ref",
+ "matchPattern": "action order [0-9]*: bpf action.o:\\[action-ko\\] id [0-9].*index 667 ref",
"matchCount": "0",
"teardown": [
[
@@ -101,7 +101,7 @@
1,
255
],
- "rm -f _c.o"
+ "make -C bpf clean"
]
},
{
diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py
index a023d0d..d651bc1 100644
--- a/tools/testing/selftests/tc-testing/tdc_config.py
+++ b/tools/testing/selftests/tc-testing/tdc_config.py
@@ -16,7 +16,9 @@
'DEV2': '',
'BATCH_FILE': './batch.txt',
# Name of the namespace to use
- 'NS': 'tcut'
+ 'NS': 'tcut',
+ # Directory containing eBPF test programs
+ 'EBPFDIR': './bpf'
}
diff --git a/tools/testing/selftests/vm/gup_benchmark.c b/tools/testing/selftests/vm/gup_benchmark.c
index 9601bc2..17da711 100644
--- a/tools/testing/selftests/vm/gup_benchmark.c
+++ b/tools/testing/selftests/vm/gup_benchmark.c
@@ -51,6 +51,7 @@ int main(int argc, char **argv)
break;
case 'w':
write = 1;
+ break;
default:
return -1;
}
diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c
index 6e29087..f1c6e025 100644
--- a/tools/testing/selftests/watchdog/watchdog-test.c
+++ b/tools/testing/selftests/watchdog/watchdog-test.c
@@ -89,7 +89,13 @@ int main(int argc, char *argv[])
fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
- printf("Watchdog device not enabled.\n");
+ if (errno == ENOENT)
+ printf("Watchdog device not enabled.\n");
+ else if (errno == EACCES)
+ printf("Run watchdog as root.\n");
+ else
+ printf("Watchdog device open failed %s\n",
+ strerror(errno));
exit(-1);
}
@@ -103,7 +109,7 @@ int main(int argc, char *argv[])
printf("Last boot is caused by: %s.\n", (flags != 0) ?
"Watchdog" : "Power-On-Reset");
else
- printf("WDIOC_GETBOOTSTATUS errno '%s'\n", strerror(errno));
+ printf("WDIOC_GETBOOTSTATUS error '%s'\n", strerror(errno));
break;
case 'd':
flags = WDIOS_DISABLECARD;
@@ -111,7 +117,7 @@ int main(int argc, char *argv[])
if (!ret)
printf("Watchdog card disabled.\n");
else
- printf("WDIOS_DISABLECARD errno '%s'\n", strerror(errno));
+ printf("WDIOS_DISABLECARD error '%s'\n", strerror(errno));
break;
case 'e':
flags = WDIOS_ENABLECARD;
@@ -119,7 +125,7 @@ int main(int argc, char *argv[])
if (!ret)
printf("Watchdog card enabled.\n");
else
- printf("WDIOS_ENABLECARD errno '%s'\n", strerror(errno));
+ printf("WDIOS_ENABLECARD error '%s'\n", strerror(errno));
break;
case 'p':
ping_rate = strtoul(optarg, NULL, 0);
@@ -133,7 +139,7 @@ int main(int argc, char *argv[])
if (!ret)
printf("Watchdog timeout set to %u seconds.\n", flags);
else
- printf("WDIOC_SETTIMEOUT errno '%s'\n", strerror(errno));
+ printf("WDIOC_SETTIMEOUT error '%s'\n", strerror(errno));
break;
default:
usage(argv[0]);
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index ec3a0b7..67ae6c1 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -81,7 +81,7 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
FILE *fd = NULL;
struct udev_device *plat;
const char *speed;
- int ret = 0;
+ size_t ret;
plat = udev_device_get_parent(sdev);
path = udev_device_get_syspath(plat);
@@ -91,8 +91,10 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
if (!fd)
return -1;
ret = fread((char *) &descr, sizeof(descr), 1, fd);
- if (ret < 0)
+ if (ret != 1) {
+ err("Cannot read vudc device descr file: %s", strerror(errno));
goto err;
+ }
fclose(fd);
copy_descr_attr(dev, &descr, bDeviceClass);
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c
index dc93fad..b0f7489 100644
--- a/tools/usb/usbip/libsrc/usbip_host_common.c
+++ b/tools/usb/usbip/libsrc/usbip_host_common.c
@@ -43,7 +43,7 @@ static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
int size;
int fd;
int length;
- char status;
+ char status[2] = { 0 };
int value = 0;
size = snprintf(status_attr_path, sizeof(status_attr_path),
@@ -61,15 +61,15 @@ static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
return -1;
}
- length = read(fd, &status, 1);
+ length = read(fd, status, 1);
if (length < 0) {
err("error reading attribute %s", status_attr_path);
close(fd);
return -1;
}
- value = atoi(&status);
-
+ value = atoi(status);
+ close(fd);
return value;
}
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c
index 37908a8..1ff3a6c 100644
--- a/tools/vm/page-types.c
+++ b/tools/vm/page-types.c
@@ -701,7 +701,7 @@ static void walk_pfn(unsigned long voffset,
if (kpagecgroup_read(cgi, index, pages) != pages)
fatal("kpagecgroup returned fewer pages than expected");
- if (kpagecount_read(cnt, index, batch) != pages)
+ if (kpagecount_read(cnt, index, pages) != pages)
fatal("kpagecount returned fewer pages than expected");
for (i = 0; i < pages; i++)
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index d982650..3de40a9 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -382,10 +382,16 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
* We might get preempted before the vCPU actually runs, but
* over-invalidation doesn't affect correctness.
*/
- if (*last_ran != vcpu->vcpu_id) {
+ if (*last_ran != -1 && *last_ran != vcpu->vcpu_id) {
kvm_call_hyp(__kvm_tlb_flush_local_vmid, vcpu);
- *last_ran = vcpu->vcpu_id;
+
+ /*
+ * 'last_ran' and this vcpu may share an ASID and hit the
+ * conditions for Cortex-A77 erratum 1542418.
+ */
+ kvm_workaround_1542418_vmid_rollover();
}
+ *last_ran = vcpu->vcpu_id;
vcpu->cpu = cpu;
vcpu->arch.host_cpu_context = this_cpu_ptr(&kvm_host_cpu_state);
@@ -470,15 +476,16 @@ bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
return vcpu_mode_priv(vcpu);
}
-/* Just ensure a guest exit from a particular CPU */
-static void exit_vm_noop(void *info)
+static void exit_vmid_rollover(void *info)
{
+ kvm_workaround_1542418_vmid_rollover();
}
-void force_vm_exit(const cpumask_t *mask)
+static void force_vmid_rollover_exit(const cpumask_t *mask)
{
preempt_disable();
- smp_call_function_many(mask, exit_vm_noop, NULL, true);
+ smp_call_function_many(mask, exit_vmid_rollover, NULL, true);
+ kvm_workaround_1542418_vmid_rollover();
preempt_enable();
}
@@ -536,10 +543,10 @@ static void update_vttbr(struct kvm *kvm)
/*
* On SMP we know no other CPUs can use this CPU's or each
- * other's VMID after force_vm_exit returns since the
+ * other's VMID after force_vmid_rollover_exit returns since the
* kvm_vmid_lock blocks them from reentry to the guest.
*/
- force_vm_exit(cpu_all_mask);
+ force_vmid_rollover_exit(cpu_all_mask);
/*
* Now broadcast TLB + ICACHE invalidation over the inner
* shareable domain to make sure all data structures are
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index 1344557..bf330b4 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -412,7 +412,8 @@ static void stage2_flush_memslot(struct kvm *kvm,
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
do {
next = stage2_pgd_addr_end(addr, end);
- stage2_flush_puds(kvm, pgd, addr, next);
+ if (!stage2_pgd_none(*pgd))
+ stage2_flush_puds(kvm, pgd, addr, next);
} while (pgd++, addr = next, addr != end);
}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 8b958ed..62a3a2f 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -375,8 +375,8 @@ int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
int vgic_v3_save_pending_tables(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- int last_byte_offset = -1;
struct vgic_irq *irq;
+ gpa_t last_ptr = ~(gpa_t)0;
int ret;
u8 val;
@@ -396,11 +396,11 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
bit_nr = irq->intid % BITS_PER_BYTE;
ptr = pendbase + byte_offset;
- if (byte_offset != last_byte_offset) {
+ if (ptr != last_ptr) {
ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
if (ret)
return ret;
- last_byte_offset = byte_offset;
+ last_ptr = ptr;
}
stored = val & (1U << bit_nr);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 4a584a5..9502b1a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -51,6 +51,7 @@
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/bsearch.h>
+#include <linux/kthread.h>
#include <asm/processor.h>
#include <asm/io.h>
@@ -92,7 +93,7 @@ EXPORT_SYMBOL_GPL(halt_poll_ns_shrink);
* kvm->lock --> kvm->slots_lock --> kvm->irq_lock
*/
-DEFINE_SPINLOCK(kvm_lock);
+DEFINE_MUTEX(kvm_lock);
static DEFINE_RAW_SPINLOCK(kvm_count_lock);
LIST_HEAD(vm_list);
@@ -146,10 +147,30 @@ __weak int kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
return 0;
}
+bool kvm_is_zone_device_pfn(kvm_pfn_t pfn)
+{
+ /*
+ * The metadata used by is_zone_device_page() to determine whether or
+ * not a page is ZONE_DEVICE is guaranteed to be valid if and only if
+ * the device has been pinned, e.g. by get_user_pages(). WARN if the
+ * page_count() is zero to help detect bad usage of this helper.
+ */
+ if (!pfn_valid(pfn) || WARN_ON_ONCE(!page_count(pfn_to_page(pfn))))
+ return false;
+
+ return is_zone_device_page(pfn_to_page(pfn));
+}
+
bool kvm_is_reserved_pfn(kvm_pfn_t pfn)
{
+ /*
+ * ZONE_DEVICE pages currently set PG_reserved, but from a refcounting
+ * perspective they are "normal" pages, albeit with slightly different
+ * usage rules.
+ */
if (pfn_valid(pfn))
- return PageReserved(pfn_to_page(pfn));
+ return PageReserved(pfn_to_page(pfn)) &&
+ !kvm_is_zone_device_pfn(pfn);
return true;
}
@@ -616,13 +637,31 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd)
stat_data->kvm = kvm;
stat_data->offset = p->offset;
+ stat_data->mode = p->mode ? p->mode : 0644;
kvm->debugfs_stat_data[p - debugfs_entries] = stat_data;
- debugfs_create_file(p->name, 0644, kvm->debugfs_dentry,
+ debugfs_create_file(p->name, stat_data->mode, kvm->debugfs_dentry,
stat_data, stat_fops_per_vm[p->kind]);
}
return 0;
}
+/*
+ * Called after the VM is otherwise initialized, but just before adding it to
+ * the vm_list.
+ */
+int __weak kvm_arch_post_init_vm(struct kvm *kvm)
+{
+ return 0;
+}
+
+/*
+ * Called just after removing the VM from the vm_list, but before doing any
+ * other destruction.
+ */
+void __weak kvm_arch_pre_destroy_vm(struct kvm *kvm)
+{
+}
+
static struct kvm *kvm_create_vm(unsigned long type)
{
int r, i;
@@ -677,22 +716,31 @@ static struct kvm *kvm_create_vm(unsigned long type)
rcu_assign_pointer(kvm->buses[i],
kzalloc(sizeof(struct kvm_io_bus), GFP_KERNEL));
if (!kvm->buses[i])
- goto out_err;
+ goto out_err_no_mmu_notifier;
}
r = kvm_init_mmu_notifier(kvm);
if (r)
+ goto out_err_no_mmu_notifier;
+
+ r = kvm_arch_post_init_vm(kvm);
+ if (r)
goto out_err;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_add(&kvm->vm_list, &vm_list);
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
preempt_notifier_inc();
return kvm;
out_err:
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
+ if (kvm->mmu_notifier.ops)
+ mmu_notifier_unregister(&kvm->mmu_notifier, current->mm);
+#endif
+out_err_no_mmu_notifier:
cleanup_srcu_struct(&kvm->irq_srcu);
out_err_no_irq_srcu:
cleanup_srcu_struct(&kvm->srcu);
@@ -732,9 +780,11 @@ static void kvm_destroy_vm(struct kvm *kvm)
kvm_uevent_notify_change(KVM_EVENT_DESTROY_VM, kvm);
kvm_destroy_vm_debugfs(kvm);
kvm_arch_sync_events(kvm);
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_del(&kvm->vm_list);
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
+ kvm_arch_pre_destroy_vm(kvm);
+
kvm_free_irq_routing(kvm);
for (i = 0; i < KVM_NR_BUSES; i++) {
struct kvm_io_bus *bus = kvm_get_bus(kvm, i);
@@ -1697,7 +1747,7 @@ EXPORT_SYMBOL_GPL(kvm_release_pfn_dirty);
void kvm_set_pfn_dirty(kvm_pfn_t pfn)
{
- if (!kvm_is_reserved_pfn(pfn)) {
+ if (!kvm_is_reserved_pfn(pfn) && !kvm_is_zone_device_pfn(pfn)) {
struct page *page = pfn_to_page(pfn);
if (!PageReserved(page))
@@ -1708,7 +1758,7 @@ EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty);
void kvm_set_pfn_accessed(kvm_pfn_t pfn)
{
- if (!kvm_is_reserved_pfn(pfn))
+ if (!kvm_is_reserved_pfn(pfn) && !kvm_is_zone_device_pfn(pfn))
mark_page_accessed(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
@@ -3714,7 +3764,9 @@ static int kvm_debugfs_open(struct inode *inode, struct file *file,
if (!refcount_inc_not_zero(&stat_data->kvm->users_count))
return -ENOENT;
- if (simple_attr_open(inode, file, get, set, fmt)) {
+ if (simple_attr_open(inode, file, get,
+ stat_data->mode & S_IWUGO ? set : NULL,
+ fmt)) {
kvm_put_kvm(stat_data->kvm);
return -ENOMEM;
}
@@ -3828,13 +3880,13 @@ static int vm_stat_get(void *_offset, u64 *val)
u64 tmp_val;
*val = 0;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
stat_tmp.kvm = kvm;
vm_stat_get_per_vm((void *)&stat_tmp, &tmp_val);
*val += tmp_val;
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
return 0;
}
@@ -3847,12 +3899,12 @@ static int vm_stat_clear(void *_offset, u64 val)
if (val)
return -EINVAL;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
stat_tmp.kvm = kvm;
vm_stat_clear_per_vm((void *)&stat_tmp, 0);
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
return 0;
}
@@ -3867,13 +3919,13 @@ static int vcpu_stat_get(void *_offset, u64 *val)
u64 tmp_val;
*val = 0;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
stat_tmp.kvm = kvm;
vcpu_stat_get_per_vm((void *)&stat_tmp, &tmp_val);
*val += tmp_val;
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
return 0;
}
@@ -3886,12 +3938,12 @@ static int vcpu_stat_clear(void *_offset, u64 val)
if (val)
return -EINVAL;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
stat_tmp.kvm = kvm;
vcpu_stat_clear_per_vm((void *)&stat_tmp, 0);
}
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
return 0;
}
@@ -3912,7 +3964,7 @@ static void kvm_uevent_notify_change(unsigned int type, struct kvm *kvm)
if (!kvm_dev.this_device || !kvm)
return;
- spin_lock(&kvm_lock);
+ mutex_lock(&kvm_lock);
if (type == KVM_EVENT_CREATE_VM) {
kvm_createvm_count++;
kvm_active_vms++;
@@ -3921,7 +3973,7 @@ static void kvm_uevent_notify_change(unsigned int type, struct kvm *kvm)
}
created = kvm_createvm_count;
active = kvm_active_vms;
- spin_unlock(&kvm_lock);
+ mutex_unlock(&kvm_lock);
env = kzalloc(sizeof(*env), GFP_KERNEL);
if (!env)
@@ -3938,7 +3990,7 @@ static void kvm_uevent_notify_change(unsigned int type, struct kvm *kvm)
}
add_uevent_var(env, "PID=%d", kvm->userspace_pid);
- if (kvm->debugfs_dentry) {
+ if (!IS_ERR_OR_NULL(kvm->debugfs_dentry)) {
char *tmp, *p = kmalloc(PATH_MAX, GFP_KERNEL);
if (p) {
@@ -3962,7 +4014,8 @@ static void kvm_init_debug(void)
kvm_debugfs_num_entries = 0;
for (p = debugfs_entries; p->name; ++p, kvm_debugfs_num_entries++) {
- debugfs_create_file(p->name, 0644, kvm_debugfs_dir,
+ int mode = p->mode ? p->mode : 0644;
+ debugfs_create_file(p->name, mode, kvm_debugfs_dir,
(void *)(long)p->offset,
stat_fops[p->kind]);
}
@@ -4138,3 +4191,86 @@ void kvm_exit(void)
kvm_vfio_ops_exit();
}
EXPORT_SYMBOL_GPL(kvm_exit);
+
+struct kvm_vm_worker_thread_context {
+ struct kvm *kvm;
+ struct task_struct *parent;
+ struct completion init_done;
+ kvm_vm_thread_fn_t thread_fn;
+ uintptr_t data;
+ int err;
+};
+
+static int kvm_vm_worker_thread(void *context)
+{
+ /*
+ * The init_context is allocated on the stack of the parent thread, so
+ * we have to locally copy anything that is needed beyond initialization
+ */
+ struct kvm_vm_worker_thread_context *init_context = context;
+ struct kvm *kvm = init_context->kvm;
+ kvm_vm_thread_fn_t thread_fn = init_context->thread_fn;
+ uintptr_t data = init_context->data;
+ int err;
+
+ err = kthread_park(current);
+ /* kthread_park(current) is never supposed to return an error */
+ WARN_ON(err != 0);
+ if (err)
+ goto init_complete;
+
+ err = cgroup_attach_task_all(init_context->parent, current);
+ if (err) {
+ kvm_err("%s: cgroup_attach_task_all failed with err %d\n",
+ __func__, err);
+ goto init_complete;
+ }
+
+ set_user_nice(current, task_nice(init_context->parent));
+
+init_complete:
+ init_context->err = err;
+ complete(&init_context->init_done);
+ init_context = NULL;
+
+ if (err)
+ return err;
+
+ /* Wait to be woken up by the spawner before proceeding. */
+ kthread_parkme();
+
+ if (!kthread_should_stop())
+ err = thread_fn(kvm, data);
+
+ return err;
+}
+
+int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn,
+ uintptr_t data, const char *name,
+ struct task_struct **thread_ptr)
+{
+ struct kvm_vm_worker_thread_context init_context = {};
+ struct task_struct *thread;
+
+ *thread_ptr = NULL;
+ init_context.kvm = kvm;
+ init_context.parent = current;
+ init_context.thread_fn = thread_fn;
+ init_context.data = data;
+ init_completion(&init_context.init_done);
+
+ thread = kthread_run(kvm_vm_worker_thread, &init_context,
+ "%s-%d", name, task_pid_nr(current));
+ if (IS_ERR(thread))
+ return PTR_ERR(thread);
+
+ /* kthread_run is never supposed to return NULL */
+ WARN_ON(thread == NULL);
+
+ wait_for_completion(&init_context.init_done);
+
+ if (!init_context.err)
+ *thread_ptr = thread;
+
+ return init_context.err;
+}