diff --git a/Android.bp b/Android.bp index 75677378cb449447098ae6ae03bab41f14e1187e..1fd921a0e88c7d47ae0d6aa22c29ed501c0aeaa8 100644 --- a/Android.bp +++ b/Android.bp @@ -6,12 +6,12 @@ cc_binary_host { }, } -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 @@ cc_prebuilt_binary { 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", -} - -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", -} +// 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 = "" -cc_library_headers { - name: "qseecom-kernel-headers", - generated_headers: ["qseecom-kernel-includes"], - export_generated_headers: ["qseecom-kernel-includes"], -} +build = ["gen_headers_arm.bp", "gen_headers_arm64.bp"] 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 8718d4ad227b6a5eff1507d3ee427d81ef6a066e..b492fb6057c9be1acdf16f5a000c826cff669e37 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -478,6 +478,8 @@ What: /sys/devices/system/cpu/vulnerabilities /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 Description: Information about CPU vulnerabilities diff --git a/Documentation/admin-guide/hw-vuln/index.rst b/Documentation/admin-guide/hw-vuln/index.rst index 49311f3da6f24bced65bc1217fa3e67a6695c6cc..0795e3c2643f2946c3f1a58418bc6d28c376ec06 100644 --- a/Documentation/admin-guide/hw-vuln/index.rst +++ b/Documentation/admin-guide/hw-vuln/index.rst @@ -12,3 +12,5 @@ are configurable at compile, boot or run time. 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 e3a796c0d3a24d5e7e91b694103e47ae3a48ecbb..2d19c9f4c1fec13752884021be630bd0423708c5 100644 --- a/Documentation/admin-guide/hw-vuln/mds.rst +++ b/Documentation/admin-guide/hw-vuln/mds.rst @@ -265,8 +265,11 @@ time with the option "mds=". The valid arguments for this option are: ============ ============================================================= -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 0000000000000000000000000000000000000000..ba9988d8bce500af602492ee571b35d24b9678b3 --- /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 0000000000000000000000000000000000000000..af6865b822d21d18946016c1458a4dc3567050e9 --- /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 `. + + +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 4834753e0f4a5a6c3c4ecdeeefe5aaf59de6448e..94b67a27c7326555ad05a64108f826a83db6e982 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -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/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 52c7e85721fcf1367ed1483502a035d9b57deaeb..c371b45820922ae934cf6dcbcc5f857eb361b0b9 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -428,6 +428,7 @@ SwapPss: 0 kB 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 @@ replaced by copy-on-write) part of the underlying shmem object out on swap. "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 c8656dd029a910251bc9f6ffec70781d3a093f90..958fff945304488d60c0558347ad78198366138b 100644 --- a/Documentation/hid/uhid.txt +++ b/Documentation/hid/uhid.txt @@ -160,7 +160,7 @@ them but you should handle them according to your needs. 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 f6b1873f68abc695d0e10ddac6e94c4b4042bda8..de583fbbfe4238e782352d0534a3030fe459a9a8 100644 --- a/Documentation/scheduler/sched-bwc.txt +++ b/Documentation/scheduler/sched-bwc.txt @@ -90,6 +90,51 @@ There are two ways in which a group may become throttled: 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 1bb8bcaf8497703f7cdd61538ca1374f0e8ac622..635cd6eaf71495e081de44774e489d622323fcf4 100644 --- a/Documentation/virtual/kvm/locking.txt +++ b/Documentation/virtual/kvm/locking.txt @@ -15,8 +15,6 @@ The acquisition orders for mutexes are as follows: 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 @@ which time it will be set using the Dirty tracking mechanism described above. ------------ 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 ef389dcf1b1d3cc6a01d334fff858c21c696c931..0780d55c5aa82835600973f16e51f3195d374259 100644 --- a/Documentation/x86/index.rst +++ b/Documentation/x86/index.rst @@ -6,3 +6,4 @@ x86 architecture specifics :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 0000000000000000000000000000000000000000..583ddc185ba220276cd3316b7bc1c9bbe115c206 --- /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 `. + +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 37589dda522eef92b6210373a1826dfd974e6275..d57f0ffc2b9334bd11b2adcc7354de123c33bd2c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 4 PATCHLEVEL = 19 -SUBLEVEL = 81 +SUBLEVEL = 91 EXTRAVERSION = NAME = "People's Front" @@ -926,6 +926,12 @@ KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init) # 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 @@ else # KBUILD_EXTMOD # We are always building modules KBUILD_MODULES := 1 -PHONY += crmodverdir -crmodverdir: - $(cmd_crmodverdir) PHONY += $(objtree)/Module.symvers $(objtree)/Module.symvers: @@ -1614,7 +1617,7 @@ $(objtree)/Module.symvers: 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 @@ help: # Dummies... PHONY += prepare scripts -prepare: ; +prepare: + $(cmd_crmodverdir) scripts: ; endif # KBUILD_EXTMOD @@ -1783,17 +1787,14 @@ endif # 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 db681cf4959c879803b17b71b9da8ebb54264e4a..2ad77fb43639cd89fe2a00033790d565a2ff1935 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 8aec462d90fbe8f0aa88847272d02004a863f2db..30f66b12354183a708abe7fe816c8a3c3aa799f9 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 f2701c13a66b209571ff89b71ac6c93cabb9835d..cf9619d4efb4f86d68cb2417558fe3327c55c408 100644 --- a/arch/arc/mm/cache.c +++ b/arch/arc/mm/cache.c @@ -1144,6 +1144,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 @@ -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 8c47ddbd98de5687a6f8a033a77d1111b7cf0684..1b88246150baa0f527f5063184bfc444371f285f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -25,6 +25,7 @@ config ARM 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) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 908022d60dfb5abcdb5b1a0c478119329d4160f1..86d6b04232618a1c0d5c26bc41daa39132148e85 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -1094,14 +1094,21 @@ choice 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 @@ config DEBUG_OMAP2PLUS_UART 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 @@ config DEBUG_UART_PHYS 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 @@ config DEBUG_UART_VIRT 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 @@ config DEBUG_UART_8250_WORD 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 diff --git a/arch/arm/boot/compressed/libfdt_env.h b/arch/arm/boot/compressed/libfdt_env.h index 07437816e0986876079aa64e9ae6450c2d45d763..b36c0289a308ea22e9dd6ebb1458aef28da74c21 100644 --- a/arch/arm/boot/compressed/libfdt_env.h +++ b/arch/arm/boot/compressed/libfdt_env.h @@ -6,6 +6,8 @@ #include #include +#define INT_MAX ((int)(~0U>>1)) + 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 325daae40278a11fca64fa96d74fd64e991da546..21bc1173fa6b9f6c1ea5aa107535a1e9d191b416 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 20bbb899b3b76eb5041c9ae5e89d808897eadbf6..cc59e42c91342e8e716ed62c5dbde08c48ffe583 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"; -}; -&cpsw_emac0 { - phy_id = <&davinci_mdio>, <0>; - phy-mode = "rgmii-txid"; + ethphy0: ethernet-phy@0 { + reg = <0>; + }; }; -&cpsw_emac1 { - phy_id = <&davinci_mdio>, <1>; +&cpsw_emac0 { + 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 4d969013f99a6180a1e841de8e736e61c6cf3b7e..d9e92671055bdeeadecccf5cb3890ab470f004f8 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 1ad530a39a957228e6e334ea9b04515789edec19..f56798efddff3a963a05bb21a20421349d6a2d4b 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 cf1e4f747242f4f646ab631e0e9e2e145575a041..09e58fb810d95db0e54932013862d027c813aa90 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/am57xx-cl-som-am57x.dts b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts index 203266f884807e3a2cced5c35c56e6d92fc69bf9..52ae8eef60fc328bbc1b0e1117f9d86959335ee7 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 a917cf8825ca8b43bc3cd02a5395d6fbe77a2721..0e4c7c4c8c0930c81a6e8f1925a9cf0343a4b76e 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 f935b72d3d96458a99f1a101d98ec7d620f60c4e..83e0fbc4a1a10cf4e4b9418b9646d7f06aaea181 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 36203288de4267d6b41d01375027da7d099b473e..2f6aa24a0b67c707068bba9fb7902525ff1158c0 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 10868ba3277f52d1eef2be37df09b101187d9fcf..916a97734f84cc7b72d7087d09e429020c8c957c 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 7c6ad2afb0947afc85ea34ff5d470df9bfbd3ef9..1b0d0680c8b6207ddb9717dfd320e8069c6d6d65 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 b23a983f95a5379f9224d3dd602a1e2ee0df094d..69f6b9d2e7e7de67ad887ff844bd6d315a87e7a6 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 87fdc146ff525af94c8db648f8f46b8c3c1084d4..d107459fc0f89417f7d7adde30d0155da86dc929 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 bb86f17ed5ed1ba476056c434039ebf142f2bc6b..21876da7c44250c752c28983e894fa2f536352cc 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 4b9176dc5d029ff025f400b434e89868da7b1191..df0f0cc575c181006936cba2a78f03a61d3f5e9a 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 3b1baa8605a77e8f724724550e5ec123df608732..2214bfe7aa205f624c7ad485ae88f0d3ef8a15fa 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 4b7c762d5f2236678f769808e60233ba6b66aa3a..7d554b9ab27fd73762f46578a396e0cc77cb1037 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 1be9889a2b3a1eb7c40a7c74287910b4f4c616ed..430277291e025fb17c31cebf49173ffaadab55ea 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 d2b865f6029322e296133e97e7df4aa3b5127c3c..07d1b571e6017b27a29b7acb7d5e72499baba1c4 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 a29fc0494076244576166cc8bf40c123296fac9e..a57f2d435dcae8324c7001942098e55e0aabe267 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 71df3adfc7ca1b5684b39947588f6831de90111d..ec1f17ab6753b64211e0030b6725a0099d2c5590 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 1ee25a475be87ff452287d1adfda8e1644a71a27..d16db1fa7e15c69dba0060dfc647211e440f607d 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 = ; + reg = <0x800000 0x0f800000>; }; }; }; diff --git a/arch/arm/boot/dts/bcm-hr2.dtsi b/arch/arm/boot/dts/bcm-hr2.dtsi index 3084a7c957339f0edc2fef97d203b08635c96790..e4d49731287f693ae94c39239b5d51bac1bfc46c 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 09ba8504632284532e3b17c6d1531e2d732fadc4..2b219addeb4496e18fd5948113bd654db51eaae7 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/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts index 580e3cbcfbf7cf8fe83a6479a147d763bafa46b3..3e1584e787aec8c59e02cf0d4e05a6d240dffc0a 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 4a0a5115b298436dc76180bc74f1d1372d3163a5..250ad0535e8cc642429dc45a2c9401f987bebf41 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 2cb45ddd2ae3b9affc4c96b68465dc44ebfa2fe9..7ce24b282d421a585b2b264f219d0c541576492c 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 620b50c19ead93b65ef43794ec3f50d732a0a2db..7c22cbf6f3d41f1d2e1e82538f23899de60be426 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 = ; samsung,pin-val = <1>; }; + + s2mps14_irq: s2mps14-irq { + samsung,pins = "gpx3-5"; + samsung,pin-pud = ; + }; }; &rtc { diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index 94efca78c42ff27727b254ad2900baad4c2a6858..5892a9f7622faba0b7f4b27a372d6be64af0f3e1 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 = ; clocks = <&cmu CLK_USBOTG>; diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts index 7a8a5c55701a894359c748562d156c9c52109ff5..4ab1f1c66c27f407faadc897e332007c6ed80239 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 = ; + }; +}; + &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 6ff6dea29d4490f13bdf457b1ee7ecfb96929cfa..d31a68672bfacb3a2f6575c26790db05b5498d6c 100644 --- a/arch/arm/boot/dts/exynos5250-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos5250-pinctrl.dtsi @@ -225,6 +225,12 @@ samsung,pin-drv = ; }; + i2c2_gpio_bus: i2c2-gpio-bus { + samsung,pins = "gpa0-6", "gpa0-7"; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + uart2_data: uart2-data { samsung,pins = "gpa1-0", "gpa1-1"; samsung,pin-function = ; @@ -593,6 +599,11 @@ samsung,pin-pud = ; samsung,pin-drv = ; }; + + hdmi_hpd: hdmi-hpd { + samsung,pins = "gpx3-7"; + samsung,pin-pud = ; + }; }; &pinctrl_1 { diff --git a/arch/arm/boot/dts/exynos5250-snow-rev5.dts b/arch/arm/boot/dts/exynos5250-snow-rev5.dts index 0348b1c49a691d373792d2433cf26463803c7b70..7cbfc6f1f4b8fde1c52d9ad3203cfe4504c2aa42 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 25bdc9d97a4dfde6ffa6a3edd9e06d4902f16ae3..9eb48cabcca450878f829e15562effaa476bf777 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 27214e6ebe4f42f1ce7152042b7f315eb7472cf6..d476ba0f07b6be90788cf075c074708395d4ee50 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 7989631b39ccf506333bb9db6f157ab3e1327483..4398f2d1fe88171a989f2b928b78570f7169de12 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 3787cf3763c41f50e4edc474aeba9de49503c6bc..e9e4a8a02600b913c50779810e32d089ada29c3d 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 a1d81badb5c8ad5b801915be447ee095c90d8ca7..119b19ba53b6d8d66c9683f1ffdef3571c0a2af7 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 11515c0cb195c414580de3e2f2e1170b263d1d21..ee4b1b106b1ae955554f3f75bcd96b64bc93fb68 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 3edc7b5550d88d67d21132231dba61d9bcdc1fcc..2b6e77029de4dfcc936543b6248f6bf98fd6de85 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 ad2ae25b7b4dbeb5fb714dee5af9182166d51457..aca27aa2d44bd3f5f36875b92e0b0c120c20a547 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 e9351774c61999ce25e7eede7928b270292f1b65..109f51603d45ee0db851dc59ec336885fab22698 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 67de7863ad795718d03cabd590e070a70d508f7b..fa22fd9b24129ca85d0bad2aa53439fd155f1774 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 95c7b918f6d6093145fd6b26f15021bdf8217327..aab029349420dd1a0975bdb359f48be23846d9c0 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 9616e500b9961657c8c21f8293a8e91e1f5a1e77..2b5df8dfd3ff3b84802e2ca23ec169bdb86e574e 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 71bfd2b15609ae5a1b48b9b309b421f88abbd058..aaaa987d8eff9edd4b746698ca3156dbd6de4887 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 e316fe08837a3e551e8320ed0d08c8d71fdd07ff..e4d7da267532deafecebdd19c28613ea683e92de 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 5cb6967866c0af7bf1c782686de695971def117b..f37e9a75a3ca7b3553ed1294906e47df4da6cf50 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 a5626b46ac4e11baf20eaf3d02bd3f1b32c78486..f8544a9e46330fd756395eaff80f7290dca4af44 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 85c15ee63272775f2b0025ef2175f64e2b4c36d3..8c8ad80de461400eae6d5f60473410d387197dd1 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 3eddd805a793a3614c9f4fdd14483c93c9081a76..f635d5c5029c4443f845f231f357f1630303324d 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 9c455dcbe6ebf9b3c30d3d05d9a1e76291bf0f8d..c85f9d01768a105b48fb38cae6d077c62bf16845 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 f9a882d99132918b5f0bc364eec2f093d717c678..35123b7cb6b3ed87e72679e8dcdfd4dde532e9ac 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 cbad7c88c58cc60f59a3dfac35aabe8e6e8ddee0..b0b4f7c00246d7769939f1cca739302d714bea8a 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 ec466b4bfd4107d61e7f754bc4879ed94ca17fb0..0935e1400e5d29f661bc6fecaade83d1c1af0aaa 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 753d88df1627405953c75116599c7235bd39095b..39e75b997bdc8157d42346ec32b0c7a789bb7f97 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 6ee4ff8e4e8f0f470a77a3878c4eac392efebbe6..9eb960cc02cc514f8abccaf579280fc18fe9694a 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 db52ddccabc3338a87490e4c02d21bb2a1d8e01e..d17abdfb6330c1f6ea919f5d224c782e9df8267e 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 ca1419ca303c3d5994a73b5bdddc9e69428dac55..2fc64d2c7c88e53a2ec1368c38c98b65d64063a1 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 ba39d938f2891a3485e950cc3491578910a1b306..5f8a47a9fcd4071f7ce697111d6e4170ab9c1b19 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 df613e88fd2c19622aee24020d215c4add0341e5..ddce0a844758b3a5836c3d240f0f13bff6a68f4a 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 1c50b785cad473afc0d4ef28c266664ac8a63ba9..b36b97b655dda346be60c0aa3bfa0892a9256c2c 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 682a99783ee69b24485315576f3852e278ac3081..a25da415cb02ec8a1595bdbf3138f3e5de05d740 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 ab522c2da6df6687176bfbad84a9a767177c16cb..9e9e92acceb27ea9f3e0bd233cf69b07f85372e0 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 79d80036f74de643f3c0e210bcb363fd239a9526..1eddf2908b3f2962d46d32245d618f243cb794ef 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 ba60b0cb3cc13ba08c9c414d7b8c317cc936ad7e..99191466a80852fc9e92386c34d4f9ea15337cbf 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 5761a66e8a0d3736f9c4776690174883bdb2dd14..82d8df097ef1fbb6296640fe1631bd90c604db92 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 f8902a338e49a40b27814230d0228fd675e62649..2e3125391bc49a78e3a3be67fa567cd3c9705672 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 39eb067904c3ded221952cc1770f397d90cd019d..4344632f794002e054c86f40121d7059347f70e0 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 6e80254c4562a95a9adf78eb4d9c22c5da91d37a..9235fd45a824ec1b552a8a6f7aacb30b91b11330 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 26cf08549df401e61325ceb017fea2a7848cefa8..f5b2d768fe47f45f27f0f193f93cc1722eed6bdc 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 e6ebac8f43e4fbfe64a18d8254ebe80d374ebf7a..ad90d66ccca6cbcdb1425bf721bf43c794412284 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 ef2abc0978439df5a18f770e932b37de94d043e5..81f60c96a2e410f2dddce0557aa2c6229db0f8b9 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 117bd002dd1d11bb942ec811ed9dbad4339dd5fa..7d5a48250f86732d1bc0393eda1b0f89f55ced90 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 cf70ebc4399a21bf5b4074aca4eea99b71c6e250..c875e23ee45fb936e025abbac515e30d6bc0eae7 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 ce45f08e30514b4a61f1c336b0964071bc40aaa6..db2e5bce9b6a1dad7eb3b58520868ddf1099fcc0 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 50dde84b72ed762ea87e4f21ed5aa260a548bd1b..f00dda334976abf53e37b367ab76938bdc06de4c 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 462071c9ddd7399a1234347d2f5db8b69e8a1292..09071ca11c6cfa1d0ef5adbed75289847d00caab 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 a72b8981fc3bd800d047596ca8732160a003861b..c77d58f06c94924e86a1edc99c6b090f53313fc4 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 54cf3e67069a9c7771d24b93e47a9368f6a6053f..4ab135906949f274ccfa592fa7aabaaa5ab8cc81 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 f6268d0ded2966fc173674e147e40f6d54937c8c..ee6263d1c2d3ddf4020a769a662fbb2abe726699 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 f83a8c62ea531d144d2f7a51d5d02503714a42f1..d595034f3f1bfee6258a31d84681b00d647df025 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 b6b0818343c4e5dd4a9c60a41da5656f39fefd1a..8accbe16b7584c0a266ca721f5a041be6a89f9d6 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 9f11f1fcc3e6caac35c38c5b81038b3a1a152078..9d086a3b5ffc0ca0ebbe4de55b919794ee3967c8 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 7fff3717cf7c09ae29d130756b3f33856d7ef819..315d0e7615f335efd3e7e120ce2360b149de7acf 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 679b4482ab13aca4bcaa0506f73b3c3a04233134..f7a48e4622e1bc85a7984312173ad3f3abd7d69d 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 404e602e67813f05c043f6414601937c23a4573b..408da704c459b5d4d7d5e9ef90fdadda17b63162 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 2fa88c6f188201ccd62258e09c1c7da7acf61ee3..55d1872aa81a8aa7f67aab41987dc629d0e18187 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 c8e115564ba2c85df585ee772d923f52230a2481..0c2406ac8a638dd2cadd919238315e6f96ae3179 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 adb5cc7d8ce2fc3feb189a525ca9061d59b5e56e..832b5c5d7441a64a79e685f4f51ff4f83e6a39a2 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 841a27f3198ff88b4f58e9fd3286ce080ad3117c..48aede543612b76b892fa3f8356287ededdd3f26 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 f8f31872fa144d83b29c227a921125c482ebc4bf..91f809ed1370a6220ac881b6aec386b80dc0dde3 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 252175b592475743b77a765607534f9cffbe803c..2bc51623a8060d08042c3e8cf601ec65e937e35e 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 40ccdf43dffc56109dedb9cbca391c2a7ca2c711..db0feb9b9f5d7ef3dfce9181566287e14d6a1b75 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 42bfc8f8f7f6b56eae4071ee48ef11a797d324d3..5c7a2bb9141cba474375434f1f6fa84bd7a047e5 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 c84c877f09d499fff1665d7005c3845207c18e79..13dfe2afaba563ce5648a78d6756a0300370133e 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 7b62e6fb47ebe7b09ead1c698082c1b18114195f..ae0728df542e930b8ab3abfaf2185d9a8875f799 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 32a07232c0345ae3a275e50dac3d095fe9acd017..8180211265592f868d80203dcc737001fdcee6c0 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>; }; diff --git a/arch/arm/boot/dts/imx6ul-geam.dts b/arch/arm/boot/dts/imx6ul-geam.dts index d81d20f8fc8ddae18938210eddd846d354fea800..85cfad080f15c4c791bae9ffb7334aa01a2ac7f2 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 cd99285511544cc7862dd27d1da71ecf28de1a0c..1cb52744f58adeebc476571f6e211cc25b633d21 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 8f775f6974d1ceb01b426b1cd80edfad5f707b80..8d6893210842bed98518f7fd862f1bcb2d23c863 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 a031bee311df4e7134c5715f2f4754291a372f9b..cf7faf4b9c47ef76f5c537bb1e6e894e4122b4fe 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 0c09420f995125d49bba630931185a179a40020d..797262d2f27fdbdd1f3098282ebafd5a8615cf2b 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 02b5ba42cd5911cdc95f61987d0641aed6e37039..bb6dbfd5546b4b178cc31626b6d66ef42417e107 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 336cdead3da54cd137eeaa45897685b489ebb5a0..50834a43e5fb210200341c47effb987f9ff7d9e6 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; diff --git a/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi index 10ab4697950f51e001a8c483e09e9c8cd0240c27..fb213bec465435a80d1ab44f71592cba1aee71ba 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 183193e8580dd12a99f1d14d365fceff892e0853..038d8c90f6dfeafd52cba37b3d5c076bbc67320e 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 cd1776a7015ac0dc6d10630eb45bc93f816b0aae..796ed35d4ac9ae194cce80414f1eb09fd718ba02 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 584418f517a88228053a65c63a90758d891f3817..62d5e9a4a7818cf6648e5378d32d20eeaf6fed54 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 04d24ee17b142f498fe781070f729cf8ad44490c..898f4b8d7421fbe50f6c58c00cdfd644c25a2f12 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 d9f8fb69511b6bd98e6aba5c047b833c1d276e77..e2e327f437e3569058378c8279d5be4faa22b587 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 177d21fdeb288d3e458176ddf0d537a746f0d2ba..6b4acea1ef7955d4c07a68e7b3ea24014e05dcb8 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 f27b3849d3ff3ed91d7e2d504206c4c8a7d8045b..934a019f341e420dabb11785a6cb92c449819cb2 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 c9b3c60b0eb22fe3ac4f8ab05f1a17209e96ee5d..317f1bcc56e2a56cc64f90e57392babc33fc299a 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 fe8344cee8641debe183a206e8db0afb78fa8a20..1fb1ec5d3d70717f08026f47e42d9bfee0c81ca8 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 fa390da636de761fabe196c9c16e2b41b26ba41d..97d5c711eb0ca7dd6cd886ea2276a35ddade285c 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 a7f697b0290fffb8d0f480b00ae48cc5741183df..7eaf96b425bed4dc48fd647a598a0ce3923c9906 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 = ; - 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 = ; - 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 = ; - 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 = ; - 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 738b44cf2b0bbdd5eb398af5fff2d6eb60095b53..1c833105d6c5495f6ec2116d888f82b44dce4371 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 7d2302e8706c969f5ba4da112b206f54baa5cf44..9354da4efe0939e6de254f7ca9133bb02ea79a21 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 4981741377f3add795d403e8afa3588593d916c7..ed0d6fb20122a4e3c50ac17685cf0617467ea6d5 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 d77dcf890cfc82b5c05c88149694f8687e14072c..7162e0ca05b0a5d477fd1369f127e8bef67e8413 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 5b3e5c50c72f71baf5f3bae5a2a6c5f6e83a635f..4293047a4b76baf9d9235b390cf59e3e8de02165 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 47e5b63339d18fbd6d67f1382558bfc182faffba..e95deed6a7973ddff8b4a96a91dc078c70e23576 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 f1d6de8b3c193eee0d88b0465f33de4e122ba266..000bf16de6517df2ec0e3713429ca6efde32e285 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 84635eeb99cd46ae4d7240dfffbe551cb20a6af8..7f57af2f10acb6f02d42fff742d8be754101a042 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 ac830b9177763d32a70b8807e02f35cb039efc17..0c39a2340030b566137d3b7c665ec40dc6f8068a 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 ded5fcf084eb786d5c3ef521ec1ffbf535915b3f..1f91646b895162c7771174797ce50bb3194f9220 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 90c98f95b2b3ac8b572655a18e04eaa2f4c95801..a51081de7144862e6acaab83362806bb65ce181a 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 6f5bd027b71753c8508acc93a56f211a95cd12be..7b4ec2c4004206b819b2901ffc81df52f6b60f3f 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 6eb26b837446c7d51c3f3e7a1bf86ad3ad9f3ea1..5059ecac44787cd6711b869b03cdb92ab0da5be8 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 8b8db9d8e9126889c4240e523418f8bc3d1a47d7..61a06f6add3ca52a9d7c4851080c53d0a5d18ac9 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 ebd93df5d07a8a90218c9bc92e062e962e27db3d..b6c9b85951ea695a2b24f6a68e5b039ee7d556fa 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 95d59be97213e0392505c4e7afd67d2f99e5e4a6..8494b578717090a6401090ce3c59b25f17f7dab1 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 747f750f675d96dd351d8ad5a02213dae10fb6bf..ccbecad9c5c7c45c67d6fab36daa38a4806c3c97 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 a520b4c14ea9f5023b11672c17cb438f256f9067..0a0e837dc79cb7b8c44a438eaf4da9b9fa879e76 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 3a8f0edc3af99dea946c13417946e5a9c0456245..53009dbd3615893b638285e76304e9e0293593a0 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 54d056b01bb514ce84420ec9e918fa67b88c2081..8328ad589e2bac0281b3973cbbe6f49cdda86546 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 6b997bc016ee8a9e989a38ce476bb1c4f62d9f26..03919714645ae56d22c8e9e59a6836beea8f74f8 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 = ; 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 50312e752e2fae3dc42c6a0c6b45b78b0db7dcf1..7b9508e83d46c54df2d2b424a63f3ae9db1cdc87 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 0925bdca438feedaa8ee956f8109fcca75dbbe1f..52a757f47bf08f57f4d2895b5735d03d74ea73f9 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 = ; 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 = ; 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 ce22db01fbbaafb4689cc736cc357f8ec7f7f335..e6580aa0cea3573fd3c7b52e560f4a076f6e58cb 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 f02036e5de015a95feec05c47ead06e91cb3daee..fefdf8238bbe900226f338c60753e0f68919258e 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 991ac6feedd5beb6123f7a12f98c8799d89f9565..25b6a99dd87a22c64519187e3cd10555330706ed 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 = ; 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 = ; 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 67f57200d9a06ba1a2dde2b123aca62a1571230e..d560fc4051c5f7a355a0896f0e5d0e76e41acbeb 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 45fd2b302dda1d1c3c995149f2386c1d1db97af9..4a2890618f6fcf8d914eef78c4a027e1f70b3ac1 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 = ; + }; + }; + 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 50325489c0ced4d21306801d2fabaf1e0261a97f..32e1ab33666294e048ecb1314c89c10d013c9dfd 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 ed8f6ca52c5bc979777aa54a6d2e011593c9f92f..a9f053dfdc06885a6a4fcff445e74834ac62aad7 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 = ; + interrupts = ; }; timer { @@ -541,7 +541,7 @@ compatible = "rockchip,gpio-bank"; reg = <0x20030000 0x100>; interrupts = ; - clocks = <&xin24m>; + clocks = <&cru PCLK_GPIO0_PMU>; gpio-controller; #gpio-cells = <2>; @@ -554,7 +554,7 @@ compatible = "rockchip,gpio-bank"; reg = <0x10310000 0x100>; interrupts = ; - clocks = <&xin24m>; + clocks = <&cru PCLK_GPIO1>; gpio-controller; #gpio-cells = <2>; @@ -567,7 +567,7 @@ compatible = "rockchip,gpio-bank"; reg = <0x10320000 0x100>; interrupts = ; - clocks = <&xin24m>; + clocks = <&cru PCLK_GPIO2>; gpio-controller; #gpio-cells = <2>; @@ -580,7 +580,7 @@ compatible = "rockchip,gpio-bank"; reg = <0x10330000 0x100>; interrupts = ; - 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 0e159c884f972240914d319aeeccdccdecdf2a93..1aeac33b0d341bea18c0dddf46d6feca5f1c8b5d 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 a9a5689dc462db0b3f6abe56be1dea2dd6aedbbd..3bf6c450a26e58f2f0902356b19d3c587dfba181 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 b280e6494193885aba2ca44e9ad3eb9902a2bb57..31b01a998b2ed7e4f0e7c7b1ab1cda05595d39c7 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 2310a4e97768c222ca19fe8f125c5aede36be8d1..986767735e24925a5f2ba7b6b738c0ce255d0c6d 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 = ; @@ -892,7 +892,7 @@ power-domains = <&pm_domains DOMAIN_VAPE>; }; - ssp@80003000 { + spi@80003000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x80003000 0x1000>; interrupts = ; diff --git a/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi b/arch/arm/boot/dts/ste-href-family-pinctrl.dtsi index 5c5cea232743d111f154cd0faa0517b44f6a70ab..1ec193b0c5065b794838705702a99bd48c95586c 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 3f14b4df69b4e4d1ab27bd821eaeeb8d819ef40f..94eeb7f1c947863956561ce5e507bcbc75574bbb 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 b0b94d05309855f4816a05295cbeb19251bc89e5..603890461ae0f33a2ff4e4ab4f3c7f8a2fb8a761 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 62ecb6a2fa39e14aa99daf35782d7b2071852e93..1bd1aba3322f111c67cf672019bb3aac9599d6cd 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 372bc2ea6b92192422368bf8413fe62849321d08..063ee8ac5dcbd12d763a9d32a62f3185d819ae50 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 / { 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 185541a5b69fb58127136284f86341845b963af3..c50c36baba758f4364aac78e7f973c2c0b1a65b0 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 221acd10f6c8418ccacbdd996e9aa579de4ccf7f..2f0d966f39ad8313b507a5e537ee5edd177e1412 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 b97a0f2f20b97634b239bf01f3878b5b7d7665a8..d82a604f3d9c76b7332bfa172e3c1afeff338cbd 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 3d62a895072071c57ca69a531190d729492a5044..5d46bb0139fadce8055e7e1223455b78d96a18bd 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 316cb8b2945b114224d2c75ffa65a3d6a07f3300..a66d9f92f58f5dee4d3746cdf4f0b0dcaf481413 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 8acbaab14fe5179a5649d9b39cb35ae1ea143f3c..d2a2eb8b3f2624a4ac974db1a19c7c120dc1f4be 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 debc0bf22ea3b063192e458140bcab68ad10e6a5..76924fa42bbc33b152bb2a87bba0035239414649 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 = , , , diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 9c52712af24111daeb7e94a8be2ebabcc94c55c9..355619dce79944ad66f52e68579810448ae52bb9 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 = , ; }; @@ -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 1537ce148cc1918a92db88c5352840ade011b9c2..49547a43cc90ad0e865266350bcb2608cab62cfc 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 30540dc8e0c5fd5c5657ca7ad725769e316bd185..bdda0d99128e56d1519d2047685c5a8965a40fb5 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 f0096074a46786cf36c6a824aa6a7ce8bba824ac..9233ba30a857c7b2f77da960da00aab19b0eaff2 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 0dbdb29a8fff9b1132b7011f725adc3e35b0a93b..ee7ce3752581b59160e0c6f62315cfbd27ebdb41 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 880096c7e2523aee4174a9ce7fce4d853cf8e882..5e8a95af89b8c3539ff39f902059bdd256a0df43 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 387fc2aa546d660ad52a9373862a95d2610c053c..333df90e8037cd511b5159dc7587e1102ed67a56 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 443b083c6adc9a1c9f605554fcada283e8d90872..92fcb756a08a9b605d5f63365f93fe61df1434fe 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 35859d8f3267fd2a1d6fa7e716e97094895f3530..bf97f6244c233f802393133d50456735d18766e7 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 25591d6883ef2feb1fa89e28360bdeb14a048d13..d9532fb1ef65071936c047a25274b2b299c30dce 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 = ; diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index fc6131315c47ffe695a4db6cbf0f7e38a8b89221..4b1530ebe4272887f33c85e00669e4781618848e 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 = ; - 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 ef245291924f076d73aec01d5ad0f68db0e92589..4f9b4a889febe80ae8e9a1a5becd253f9caeefc4 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 = ; + linux,code = ; wakeup-source; }; }; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 15b73bd377f0408b948f80b36a4afaff82f14688..80854f7de765c571a628a8f5c1846a47a377079f 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 2f807d40c1b792bd35c662bcd243588b7fa91d0b..f810bbf8212bda115ae5e3f587d2f7b980da682b 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 = ; nvidia,tristate = ; }; 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 = ; + interrupts = ; spi-max-frequency = <10000000>; }; }; @@ -674,7 +674,7 @@ reg = <0>; clocks = <&clk16m>; interrupt-parent = <&gpio>; - interrupts = ; + interrupts = ; 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 16e1f387aa6db65aad52d814bd4e663522f81048..a0c550e26738f4e48b87380400f61b32893d756c 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 = ; + /* CAN_INT */ + interrupts = ; spi-max-frequency = <10000000>; }; spidev0: spi@1 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index a6781f6533105e7e27a7bb1e9da8f066cc1814a8..5a04ddefb71f62013d2ce5991fb56beaf47b184e 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 5f61d36090270131ed6c8f91a2f93f3ee92ef5e3..6f4f60ba5429c8dedcd3a9863c5558a5e0b8199d 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 cc5a3dc2b4a08dc3ceca92112bc6e0206bfd21d1..27cd6cb52f1ba33607db41ce80c6fb502a55837f 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 0e1bfdd3421ff04f31266051546b23f5b9e3cfd0..0dd352289a45e58150f75896a590873f66773174 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 651913f1afa2a06647addfdb6ae415b567031f66..4ae2c85df3a0078111f1b4aa9a24b695265cee16 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 2879d58afa418f98b81d60e7b5eabdefb12a4aa3..36fc3dd5dd04af4380f07dae0bd2c6eac510601c 100644 --- a/arch/arm/configs/vendor/bengal-perf_defconfig +++ b/arch/arm/configs/vendor/bengal-perf_defconfig @@ -19,8 +19,6 @@ 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 @@ -67,7 +65,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y 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 @@ -257,6 +254,7 @@ 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 @@ -264,11 +262,6 @@ 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 @@ -312,6 +305,7 @@ CONFIG_PPTP=y CONFIG_PPPOL2TP=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_RTL8152=y CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_CLD_LL_CORE=y CONFIG_CNSS_GENL=y @@ -342,6 +336,7 @@ CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m CONFIG_I2C_CHARDEV=y CONFIG_I2C_QCOM_GENI=y CONFIG_SPI=y @@ -407,7 +402,6 @@ CONFIG_VIDEO_VIM2M=y CONFIG_VIDEO_VICODEC=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 @@ -417,6 +411,7 @@ CONFIG_LOGO=y 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 @@ -432,15 +427,10 @@ 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 @@ -453,7 +443,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -467,11 +456,10 @@ CONFIG_USB_CONFIGFS_F_QDSS=y 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 @@ -511,17 +499,14 @@ CONFIG_HWSPINLOCK=y 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 @@ -558,8 +543,10 @@ CONFIG_QCOM_SMP2P_SLEEPSTATE=y CONFIG_MSM_CDSP_LOADER=y CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y +CONFIG_QTI_RPM_STATS_LOG=y CONFIG_QTEE_SHM_BRIDGE=y CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_QCOM_CDSP_RM=y CONFIG_QCOM_QHEE_ENABLE_MEM_PROTECTION=y @@ -588,9 +575,7 @@ 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 @@ -605,11 +590,7 @@ 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 diff --git a/arch/arm/configs/vendor/bengal_defconfig b/arch/arm/configs/vendor/bengal_defconfig index 25c79459ba59623d7cdac3aecab5f911d7a40995..4f3bbeb0d62e759562478859990f493147cb7ced 100644 --- a/arch/arm/configs/vendor/bengal_defconfig +++ b/arch/arm/configs/vendor/bengal_defconfig @@ -18,8 +18,6 @@ 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 @@ -66,7 +64,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y 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 +256,7 @@ 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 @@ -266,11 +264,6 @@ 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 @@ -315,6 +308,7 @@ CONFIG_PPTP=y CONFIG_PPPOL2TP=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_RTL8152=y CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_CLD_LL_CORE=y CONFIG_CNSS_GENL=y @@ -328,6 +322,8 @@ CONFIG_JOYSTICK_XPAD=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 @@ -344,6 +340,7 @@ CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m CONFIG_I2C_CHARDEV=y CONFIG_I2C_QCOM_GENI=y CONFIG_SPI=y @@ -409,7 +406,6 @@ CONFIG_VIDEO_VIM2M=y CONFIG_VIDEO_VICODEC=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 @@ -419,6 +415,7 @@ CONFIG_LOGO=y 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 @@ -434,15 +431,10 @@ 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 @@ -455,7 +447,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -469,10 +460,10 @@ CONFIG_USB_CONFIGFS_F_QDSS=y 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 @@ -515,19 +506,16 @@ CONFIG_HWSPINLOCK=y 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 @@ -568,9 +556,11 @@ CONFIG_QCOM_SMP2P_SLEEPSTATE=y CONFIG_MSM_CDSP_LOADER=y CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y +CONFIG_QTI_RPM_STATS_LOG=y # CONFIG_MSM_JTAGV8 is not set CONFIG_QTEE_SHM_BRIDGE=y CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_QCOM_CDSP_RM=y CONFIG_QCOM_QHEE_ENABLE_MEM_PROTECTION=y @@ -600,12 +590,11 @@ 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 +CONFIG_F2FS_CHECK_FS=y CONFIG_FS_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -617,11 +606,7 @@ 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 @@ -648,6 +633,7 @@ CONFIG_CRYPTO_DEV_QCOM_ICE=y CONFIG_XZ_DEC=y CONFIG_PRINTK_TIME=y CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS=y CONFIG_DEBUG_MODULE_LOAD_INFO=y CONFIG_DEBUG_INFO=y CONFIG_FRAME_WARN=2048 @@ -667,7 +653,6 @@ CONFIG_DEBUG_OBJECTS_TIMERS=y CONFIG_DEBUG_OBJECTS_WORK=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y -CONFIG_SLUB_DEBUG_ON=y CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y diff --git a/arch/arm/crypto/crc32-ce-glue.c b/arch/arm/crypto/crc32-ce-glue.c index 96e62ec105d061864e516eacefcbd55e983bfedd..cd9e93b46c2ddf21d1722ce228729e61d2253ed3 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/uaccess.h b/arch/arm/include/asm/uaccess.h index c136eef8f690be60bba14f6a871dff286a5942f0..6390a40f16e733e061b1d3abfc01a92a46384a8b 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 746565a876dcdd362522d1e546c8404faacfbfe2..0465d65d23de5786ef5df32738d0830ca492353a 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -296,16 +296,15 @@ __sys_trace: 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 997b02302c3145f5ac380ae18823eba50d916ac7..9328f2010bc199198612120bde11a7a0c8d2c729 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -72,7 +72,7 @@ ENDPROC(__vet_atags) * 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 @@ __mmap_switched_data: #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 cab89479d15ef29cbeb2e4c3643f5dfd0da0ab76..326a97aa3ea0cfaf390d06a267cbcdc280d18164 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -205,6 +205,8 @@ M_CLASS(streq r3, [r12, #PMSAv8_MAIR1]) 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/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index a4fb0f8b8f84a96544977bf0c31b49d6e93c714a..300cc556d5d1eda1a56661b51a92a8947aeb74e5 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1072,8 +1072,6 @@ static int armv7pmu_set_event_filter(struct hw_perf_event *event, { unsigned long config_base = 0; - if (attr->exclude_idle) - return -EPERM; if (attr->exclude_user) config_base |= ARMV7_EXCLUDE_USER; if (attr->exclude_kernel) @@ -1108,7 +1106,7 @@ static void armv7pmu_reset(void *info) } /* Initialize & Reset PMNC: C and P bits */ - armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C); + armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_P | ARMV7_PMNC_C); } static int armv7_a8_map_event(struct perf_event *event) @@ -1190,11 +1188,68 @@ static void armv7_read_num_pmnc_events(void *info) *nb_cnt += 1; } -static int armv7_probe_num_events(struct arm_pmu *arm_pmu) +static void armv7_pmu_idle_update(struct arm_pmu *cpu_pmu) { - return smp_call_function_any(&arm_pmu->supported_cpus, + struct pmu_hw_events *hw_events; + struct perf_event *event; + int idx; + + if (!cpu_pmu) + return; + + hw_events = this_cpu_ptr(cpu_pmu->hw_events); + if (!hw_events) + return; + + for (idx = 0; idx < cpu_pmu->num_events; ++idx) { + event = hw_events->events[idx]; + + if (!event || !event->attr.exclude_idle || + event->state != PERF_EVENT_STATE_ACTIVE) + continue; + + cpu_pmu->pmu.read(event); + } +} + +struct armv7_pmu_idle_nb { + struct arm_pmu *cpu_pmu; + struct notifier_block perf_cpu_idle_nb; +}; + +static int armv7_pmu_idle_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct armv7_pmu_idle_nb *pmu_idle_nb = container_of(nb, + struct armv7_pmu_idle_nb, perf_cpu_idle_nb); + + if (action == IDLE_START) + armv7_pmu_idle_update(pmu_idle_nb->cpu_pmu); + + return NOTIFY_OK; +} + +static int armv7_probe_pmu(struct arm_pmu *arm_pmu) +{ + int ret; + struct armv7_pmu_idle_nb *pmu_idle_nb; + + pmu_idle_nb = devm_kzalloc(&arm_pmu->plat_device->dev, + sizeof(*pmu_idle_nb), GFP_KERNEL); + if (!pmu_idle_nb) + return -ENOMEM; + + ret = smp_call_function_any(&arm_pmu->supported_cpus, armv7_read_num_pmnc_events, &arm_pmu->num_events, 1); + if (ret) + return ret; + + pmu_idle_nb->cpu_pmu = arm_pmu; + pmu_idle_nb->perf_cpu_idle_nb.notifier_call = armv7_pmu_idle_notifier; + idle_notifier_register(&pmu_idle_nb->perf_cpu_idle_nb); + + return 0; } static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) @@ -1206,7 +1261,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv1_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) @@ -1218,7 +1273,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv1_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) @@ -1230,7 +1285,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv1_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) @@ -1243,7 +1298,20 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv2_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); +} + +static int armv8_pmuv3_pmu_init(struct arm_pmu *cpu_pmu) +{ + armv7pmu_init(cpu_pmu); + cpu_pmu->name = "armv7_pmuv3"; + cpu_pmu->map_event = armv7_a7_map_event; + cpu_pmu->set_event_filter = armv7pmu_set_event_filter; + cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = + &armv7_pmuv2_events_attr_group; + cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = + &armv7_pmu_format_attr_group; + return armv7_probe_pmu(cpu_pmu); } static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) @@ -1256,7 +1324,7 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv2_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) @@ -1269,7 +1337,7 @@ static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) &armv7_pmuv2_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv7_pmu_format_attr_group; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu) @@ -1667,7 +1735,7 @@ static int krait_pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->disable = krait_pmu_disable_event; cpu_pmu->get_event_idx = krait_pmu_get_event_idx; cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } /* @@ -1991,7 +2059,7 @@ static int scorpion_pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->disable = scorpion_pmu_disable_event; cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx; cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) @@ -2004,7 +2072,7 @@ static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->disable = scorpion_pmu_disable_event; cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx; cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx; - return armv7_probe_num_events(cpu_pmu); + return armv7_probe_pmu(cpu_pmu); } static const struct of_device_id armv7_pmu_of_device_ids[] = { @@ -2018,6 +2086,7 @@ static const struct of_device_id armv7_pmu_of_device_ids[] = { {.compatible = "qcom,krait-pmu", .data = krait_pmu_init}, {.compatible = "qcom,scorpion-pmu", .data = scorpion_pmu_init}, {.compatible = "qcom,scorpion-mp-pmu", .data = scorpion_mp_pmu_init}, + {.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_pmu_init}, {}, }; diff --git a/arch/arm/kernel/psci_smp.c b/arch/arm/kernel/psci_smp.c index cb3fcaeb2233eb1f9bee78bc1bfff9848b1b56dc..4066ac908244beae5003de6d1f9096a58ec82b98 100644 --- a/arch/arm/kernel/psci_smp.c +++ b/arch/arm/kernel/psci_smp.c @@ -112,6 +112,11 @@ int psci_cpu_kill(unsigned int cpu) return 0; } +bool psci_cpu_can_disable(unsigned int cpu) +{ + return true; +} + #endif bool __init psci_smp_available(void) @@ -126,5 +131,6 @@ const struct smp_operations psci_smp_ops __initconst = { .cpu_disable = psci_cpu_disable, .cpu_die = psci_cpu_die, .cpu_kill = psci_cpu_kill, + .cpu_can_disable = psci_cpu_can_disable, #endif }; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index a172ff55393bbbdf76b4afd487c0e2e1a5d5a690..d20a4e44d8ee4d487779952e33c7d09a3e737dfb 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/lib/getuser.S b/arch/arm/lib/getuser.S index 746e7801dcdf70fed9e339c2d6800b3f275c49b7..b2e4bc3a635e22002fd90eaf4d802b528f6dab32 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -42,6 +42,12 @@ _ASM_NOKPROBE(__get_user_1) 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 @@ rb .req r0 #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 @@ _ASM_NOKPROBE(__get_user_bad8) .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 38d660d3705f4f259c5299d2cc8c1126f0a1dbb4..515eeaa9975c6cbf75b8aba01d85df7deb063b25 100644 --- a/arch/arm/lib/putuser.S +++ b/arch/arm/lib/putuser.S @@ -41,16 +41,13 @@ ENDPROC(__put_user_1) 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 @@ ENTRY(__put_user_2) 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 @@ ENDPROC(__put_user_bad) .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 0921e2c10edfd35a348957b3d658422d0533593f..e2e4df3d11e53dfc38d8456340efc6d109c417a6 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 42665914166a318e50fd741c60cea0a7c6e9a8a0..83ca89a353002a9a02955bc8e1410c8b55edc461 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 b08e407d8d96f4f7a61d107ee877a64571a8acaf..529f4b5bbd3a7addfbe7492d3b83d516b96fd567 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 ef835d82cdb95ecb7f43d36a80f1dc7c9870a5b6..5783062224c393254c82ee45807e90c486ed95bb 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 e8ccf51c6f292959c4f373d7a802c19ddf278c09..ec0235899de20e63eb4d351775a1675817aafb6a 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -25,7 +25,7 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y) 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 52de382fc8047148f272dd57d7dd01354ec33801..7e49dfda3d2f4491645abfbd9af4830c132d7dca 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 77867778d4ec700844fefe14146303a9f35f17fb..5429d86c7190d805ea003729837997751a772eaa 100644 --- a/arch/arm/mach-omap1/include/mach/usb.h +++ b/arch/arm/mach-omap1/include/mach/usb.h @@ -11,7 +11,7 @@ #include -#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 68ba5f472f6badd30ccc1408361ce5ff20c54a3a..859c71c4e93244c050979916f83eca19f754a2a1 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 7f02743edbe4c7880bb12feccb4d6a1683ca276d..dae726228770421bea84353400dda87e390fcb0e 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/Makefile.boot b/arch/arm/mach-qcom/Makefile.boot new file mode 100644 index 0000000000000000000000000000000000000000..4c0a039a5027241fb8f973c4c9823f0009e00cc1 --- /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-sunxi/mc_smp.c b/arch/arm/mach-sunxi/mc_smp.c index b4037b603897d62e15eeafb099c4ef1163328d29..ff173e67eed2173eec8007be24985f45b32a5bd6 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 805f306fa6f707f055878a31f00a2f412a89f9c5..e31f167a8199443689a78f97de477d6f929db2c6 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -56,16 +56,16 @@ ENTRY(tegra_resume) 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/mm/alignment.c b/arch/arm/mm/alignment.c index bd2c739d80839bac052d4af850afed32c246358c..84a6bbaf8cb200c46963569fa3d92fca65cdc992 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 420b0fd6bfc7a07b21f1a8b01f51d18a808c965d..7c416695aeb91fcffab5272d6d4109f9ed0bf5db 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1950,7 +1950,10 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, for_each_sg(sg, s, nents, i) { s->dma_address = iova + current_offset; - s->dma_length = total_length - current_offset; + if (i == 0) + s->dma_length = total_length; + else + s->dma_length = 0; current_offset += s->length; } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 047ee14affcc3244ccb6952af67627c8a60f3267..9bef174498183eeb3bd80b14385c2d88d3424a2c 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-v7m.S b/arch/arm/mm/proc-v7m.S index 92e84181933ad96fec9bcb488e148b2c7874310d..9c2978c128d97cf24ba3f5aff70dc14179ae17b3 100644 --- a/arch/arm/mm/proc-v7m.S +++ b/arch/arm/mm/proc-v7m.S @@ -135,13 +135,11 @@ __v7m_setup_cont: 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/Makefile b/arch/arm64/Makefile index aed19411a20de8fb2b8ee160c1f19102f811944e..d06b9f6d901f7db7eb8a1f2cc5950af2d6e35504 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -176,6 +176,7 @@ archclean: $(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 @@ archclean: 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 98dbff19f5cccd6db3711cbe611f2fe3f0f8128f..5caba225b4f78760b4d8d316ae706daab0fa44e5 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 3f531393eaee9a8cbae54ad5e1f5fd0e1b3aafbb..b3f186434f363f36834e0d3bccd1d671d2ba0fe5 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 1221764f5719cfe19a97b63c440ff8465e2b5dec..667016815cf32081d44efb139f5f1c8e94bbefd6 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 24f1aac366d64355f5b6b37bb8e263bcce7f2e2d..d5b6e8159a335a0fde372e68f84e2101fc448560 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 c21f2331add60255d0cb97b49319a3e30c22d354..285cb7143b96c9311606663885b722a523d70af5 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 7c661753bfaf4fade78932fd7de9754c28ddaddd..faa017d4cd56b3960b33b0469590e9876c71c613 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 125f4deb52fe9e0c08f866050788a941ca710b11..b664e7af74eb3a99953796816ed2e5525b169832 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 c518130e5ce730e8267456cee4a6062d4527b772..3c34f14fa508602a7f3678d328e3ecdb7d47847c 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 = ; 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 cbe99bd4e06d2c43934a23848bbe788ee69e0390..8cd50b75171de6f2d64f9732d36e4b280510b48f 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 54954b314a452b7929aa0ce30a031927c2832b92..00f7be6d83f7c3dc64b8dbf0df9c33c444edc73e 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", diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi index 98cbba6809caa17e2fa4f6b630bf8b02e26f32ab..1ade7e486828c2db082a121e856456e5562d3445 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 d32cf384637022b8ee36287283be31c1091273ee..864ef0111b01ac93025edacdf0ffd048e73bcb07 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 f63bceb88caafa249d84de963c3daa034fb842b7..b4dfb9afdef86926d8fec69bf034cdb630ccde8a 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 c87a80e9bcc6a80bc0f8a59c43a32d6485facafe..8f0bb3c44bd6d05a11e6dea2ed390f0c88bdc9cc 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 1a406a76c86a2ae7ae465c192b08739009e9d891..ea854f689fda89fe8c19526ce99c7724e41c0bd5 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 = ; @@ -650,7 +650,7 @@ status = "disabled"; }; - ssp1: ssp@66190000 { + ssp1: spi@66190000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x66190000 0x1000>; interrupts = ; diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi b/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi index bc299c3d90683b02e168b9c06cbfd0b26c66ee2f..a9b92e52d50e8a1d4175ece1de5dc2050c34eea6 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 8a3a770e8f2ce62bb99fc9fd74461073cfa7780a..56789ccf94545f39cde28c34f9dba8af495322a7 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 e283480bfc7e5d50701b7d62bde1b9b3e53ee5a5..ff714fcbac68d816287d85cf7d5535042222c144 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 = ; @@ -533,7 +532,7 @@ status = "disabled"; }; - ssp1: ssp@190000 { + ssp1: spi@190000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x00190000 0x1000>; interrupts = ; diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi index 2131f12364cb23906047495bbfae29e5084c3420..6e20415b061e419e95baeebf4b98b04581dcb4e6 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 75ad724c487ec863c3e409a09cb6cb70fddb5f66..31b1a606cb664435dff6fda2fe3979ebe0410e4b 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 68ac78c4564dc74cdfd0f40697c1b599bee72add..5da732f82fa0cb0fc844b73bfdd20d347a786628 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 7881e3d81a9aba6134fc66a7d35e6d02daa4009a..b9c0f2de8f12c4ed9be5f25d56a62634c216a221 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 440e111651d53d9bba85763f0a04cde2796022b3..a59b48203688a47db12732f019255800b9880def 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 ef83786b8b905d57852ad6637bd2d4dfb5683c43..de6af453a6e168520e2b875521114d5b10ea0444 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi @@ -202,7 +202,7 @@ interrupts = ; }; - 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 8cb78dd9967281d06c1cd10c4a55b0bebe337e6e..ebe0cd4bf2b7e2f8539741d61ec14def4565562e 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 860c8fb10795011f6e6f9894f0ad5e8d4e9444d4..4bde7b6f2b113ccd541c68c9e2c0dee6efe5f568 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 = ; clocks = <&clk_bus>; clock-names = "apb_pclk"; }; - spi1: ssp@fe900000 { + spi1: spi@fe900000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x0 0xfe900000 0x1000>; interrupts = ; diff --git a/arch/arm64/boot/dts/lg/lg1313.dtsi b/arch/arm64/boot/dts/lg/lg1313.dtsi index 1887af654a7db96685581b9f6953f8906073da11..16ced1ff1ad36754977dd41c5a1d39c8d8f81866 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 = ; clocks = <&clk_bus>; clock-names = "apb_pclk"; }; - spi1: ssp@fe900000 { + spi1: spi@fe900000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x0 0xfe900000 0x1000>; interrupts = ; diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index a4dfcd19b9e88965187cd24e23116103b0f7e652..9fc14bb9a0affc7dea710afa5bae74b90a264adb 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 = ; #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 = ; #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 = ; #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 = ; #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 = ; #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 = ; #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 = ; #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 = ; #address-cells = <1>; diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi index 7398ae8856dc0ecf425d8eaac7f753c1855800c5..ccaa555180dc0f03fe3b73d318d00902bc8ee599 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 9d5a0e6b2ca4f9b69413e84f8513a7c2f31a5d8f..68af663757d0b3870f53906d7fa9711d73bcb2b9 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 f60f08ba1a6f96dde54582ccdc708584bab60441..f1dfd17413b9efb6aa19b78a46dbc75f0b86ba04 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 = ; @@ -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 a8e8f2669d4c53ae7492dc489107d3fbac30eebe..1b8f19ee257f0fff48634dbd9585a5dc130c0faf 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 7d3d866a006352ac196b52002c9c9ce8899f5208..3b90f816dfefcbd8cbddc513ed5a534504643197 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 8ce4a79d9360ffe0ba32499b6f877eb174e22d71..1e6a71066c163fd7bd2493b286e83a0596942930 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 36b60791c156d2ace112850571418d0410f81bd7..6062cc8250b110d988a12b80b27257f24fe125af 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 = + ; + }; + }; + 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 2409344df4fa2f50ee9e91feddc483e0a4a10279..2e3917171b17f9a557869454cbb746f6bf8296f9 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_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 cede1fa0983c9321511649251cff09c1cdaa4e63..ded364d208351f959262df4e57339c7189fcfea7 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 9c09baca7dd78ce9283e535d90bcbb12c1c8da29..306ad2157c9882b5ce4a5a9c5bc24e5d25d75c4b 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 8954c8c6f5475451535ea61f55c29a00c0623bdb..14062b4535dd71b29d56a0361b556c2c46a2a1a6 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 25dd57485323507888ebfc66cebdcfc858f5a3a3..d3b8e1a9c07616f76e41976351b28332efc96e0d 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 259f21b0c0014690ded2d21a417d0cffb2d2b271..28dee4dad82c236c6e2b5fd974b71f707a15a0f2 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 a61b3cc6f4c95ecf8396b1ff253fa1f794f10dd0..47b5989035e4e2d3e1069a7776b06bbbe6c88fc4 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 29ce23422acf221e0725e93949b99c79f975ccf7..a516c0e01429a82c1ddb21c2d214484e4604bf72 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/vendor/bengal-perf_defconfig b/arch/arm64/configs/vendor/bengal-perf_defconfig index 1ab315e28fc5bd4fdb6e1abe10d0cecfccbf376a..57d212e628e3289169b8d8d8997a1979ae06ad08 100644 --- a/arch/arm64/configs/vendor/bengal-perf_defconfig +++ b/arch/arm64/configs/vendor/bengal-perf_defconfig @@ -11,6 +11,7 @@ CONFIG_TASKSTATS=y 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 @@ -32,6 +33,8 @@ CONFIG_NAMESPACES=y 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 +46,7 @@ CONFIG_EMBEDDED=y CONFIG_SLAB_FREELIST_RANDOM=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_PROFILING=y +CONFIG_HOTPLUG_SIZE_BITS=29 CONFIG_ARCH_QCOM=y CONFIG_ARCH_BENGAL=y CONFIG_PCI=y @@ -83,6 +87,7 @@ CONFIG_CRYPTO_GHASH_ARM64_CE=y 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 @@ -98,6 +103,10 @@ CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_BFQ_GROUP_IOSCHED=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y +CONFIG_MEMORY_HOTPLUG_MOVABLE_NODE=y +CONFIG_MEMORY_HOTREMOVE=y CONFIG_CMA=y CONFIG_CMA_DEBUGFS=y CONFIG_ZSMALLOC=y @@ -252,6 +261,7 @@ CONFIG_BT=y 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_FW_LOADER_USER_HELPER=y @@ -301,6 +311,7 @@ CONFIG_PPTP=y CONFIG_PPPOL2TP=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_RTL8152=y CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_CLD_LL_CORE=y CONFIG_CNSS_GENL=y @@ -309,7 +320,10 @@ CONFIG_KEYBOARD_GPIO=y # 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 @@ -321,6 +335,8 @@ CONFIG_SERIAL_MSM_GENI=y 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 @@ -384,7 +400,6 @@ CONFIG_VIDEO_VIM2M=y CONFIG_VIDEO_VICODEC=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_LOGO=y @@ -393,6 +408,7 @@ CONFIG_LOGO=y 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 @@ -403,18 +419,14 @@ CONFIG_HID_MAGICMOUSE=y 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 @@ -426,7 +438,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -440,11 +451,10 @@ CONFIG_USB_CONFIGFS_F_QDSS=y 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 @@ -484,18 +494,18 @@ CONFIG_HWSPINLOCK=y 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_MSM_RPM_SMD=y CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_MEM_OFFLINE=y +CONFIG_OVERRIDE_MEMORY_LIMIT=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_MDT_LOADER=y CONFIG_QPNP_PBS=y @@ -531,9 +541,11 @@ CONFIG_MSM_CDSP_LOADER=y CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y +CONFIG_QTI_L2_REUSE=y CONFIG_QTI_RPM_STATS_LOG=y CONFIG_QTEE_SHM_BRIDGE=y CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_QCOM_CDSP_RM=y CONFIG_QCOM_QHEE_ENABLE_MEM_PROTECTION=y @@ -579,8 +591,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y @@ -603,7 +613,6 @@ CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y 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 @@ -612,15 +621,16 @@ CONFIG_PAGE_OWNER=y 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 CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_CTI_SAVE_DISABLE=y CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y -CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_CORESIGHT_DUMMY=y CONFIG_CORESIGHT_REMOTE_ETM=y diff --git a/arch/arm64/configs/vendor/bengal_defconfig b/arch/arm64/configs/vendor/bengal_defconfig index 9edbc5f8ab8b4ff47b3d30956228fe825596769f..3fd94125d840dd0ab77196ddfbc78f6d4f8071c1 100644 --- a/arch/arm64/configs/vendor/bengal_defconfig +++ b/arch/arm64/configs/vendor/bengal_defconfig @@ -10,6 +10,7 @@ CONFIG_TASKSTATS=y 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 @@ -33,6 +34,8 @@ CONFIG_NAMESPACES=y 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 +47,7 @@ CONFIG_EMBEDDED=y CONFIG_SLAB_FREELIST_RANDOM=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_PROFILING=y +CONFIG_HOTPLUG_SIZE_BITS=29 CONFIG_ARCH_QCOM=y CONFIG_ARCH_BENGAL=y CONFIG_ARCH_SCUBA=y @@ -87,6 +91,7 @@ CONFIG_CRYPTO_GHASH_ARM64_CE=y 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 @@ -102,6 +107,10 @@ CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_BFQ_GROUP_IOSCHED=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y +CONFIG_MEMORY_HOTPLUG_MOVABLE_NODE=y +CONFIG_MEMORY_HOTREMOVE=y CONFIG_CLEANCACHE=y CONFIG_CMA=y CONFIG_CMA_DEBUG=y @@ -260,6 +269,7 @@ CONFIG_BT=y 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_FW_LOADER_USER_HELPER=y @@ -311,6 +321,7 @@ CONFIG_PPTP=y CONFIG_PPPOL2TP=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_RTL8152=y CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_CLD_LL_CORE=y CONFIG_CNSS_GENL=y @@ -319,7 +330,10 @@ CONFIG_KEYBOARD_GPIO=y # 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 @@ -333,6 +347,8 @@ CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING=y 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 @@ -397,7 +413,6 @@ CONFIG_VIDEO_VIM2M=y CONFIG_VIDEO_VICODEC=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_LOGO=y @@ -406,6 +421,7 @@ CONFIG_LOGO=y 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 @@ -416,18 +432,14 @@ CONFIG_HID_MAGICMOUSE=y 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 @@ -439,7 +451,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -453,10 +464,10 @@ CONFIG_USB_CONFIGFS_F_QDSS=y 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 @@ -502,20 +513,20 @@ CONFIG_HWSPINLOCK=y 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_MSM_RPM_SMD=y CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_MEM_OFFLINE=y +CONFIG_OVERRIDE_MEMORY_LIMIT=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_MDT_LOADER=y CONFIG_QPNP_PBS=y @@ -555,9 +566,11 @@ CONFIG_MSM_CDSP_LOADER=y CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y +CONFIG_QTI_L2_REUSE=y CONFIG_QTI_RPM_STATS_LOG=y CONFIG_QTEE_SHM_BRIDGE=y CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_QCOM_CDSP_RM=y CONFIG_QCOM_QHEE_ENABLE_MEM_PROTECTION=y @@ -593,6 +606,7 @@ 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 @@ -604,8 +618,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y @@ -628,9 +640,9 @@ CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y 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 CONFIG_DEBUG_MODULE_LOAD_INFO=y CONFIG_DEBUG_INFO=y CONFIG_PAGE_OWNER=y diff --git a/arch/arm64/configs/vendor/kona-perf_defconfig b/arch/arm64/configs/vendor/kona-perf_defconfig index baced85ded35260bdc841da5cc47d961b8fbebb5..e14ff9acbdd36db8713a7db578d7d0323e03f942 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 @@ -297,6 +296,7 @@ CONFIG_MD=y 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 @@ -470,7 +470,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -544,7 +543,6 @@ 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_SMEM=y @@ -667,6 +665,8 @@ CONFIG_HARDENED_USERCOPY=y 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 504b11e3a07122920fe14d00712768cad2eebf6f..8e736d2d0ee478aa9f02e701a1a0c2a4a7ee69dc 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 @@ -268,6 +267,8 @@ CONFIG_BT=y 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 @@ -308,6 +309,7 @@ CONFIG_MD=y 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 @@ -483,7 +485,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -565,7 +566,6 @@ CONFIG_ARM_SMMU=y 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 @@ -698,6 +698,8 @@ CONFIG_HARDENED_USERCOPY_PAGESPAN=y 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 12744b0e6bbe900b47226c2e99119e74fcfd66b2..9442cf74fadd7afbcdd50937273785739ec17aaf 100644 --- a/arch/arm64/configs/vendor/lito-perf_defconfig +++ b/arch/arm64/configs/vendor/lito-perf_defconfig @@ -294,6 +294,7 @@ CONFIG_MD=y 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 @@ -367,6 +368,7 @@ CONFIG_QPNP_SMB5=y 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 +389,7 @@ CONFIG_QTI_AOP_REG_COOLING_DEVICE=y 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 @@ -417,7 +420,6 @@ CONFIG_VIDEO_VIM2M=m CONFIG_VIDEO_VICODEC=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_QCOM_SPMI_WLED=y @@ -461,7 +463,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -526,6 +527,12 @@ CONFIG_SM_DISPCC_LITO=y CONFIG_SM_GPUCC_LITO=y 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 CONFIG_MAILBOX=y @@ -534,7 +541,6 @@ 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_SMEM=y @@ -547,6 +553,7 @@ CONFIG_QCOM_RUN_QUEUE_STATS=y 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 e373833594ffc81a1a0d5e9a1795d09ce9b34475..284a8690d8ce00f7271537a96280539ce02d7748 100644 --- a/arch/arm64/configs/vendor/lito_defconfig +++ b/arch/arm64/configs/vendor/lito_defconfig @@ -301,6 +301,7 @@ CONFIG_MD=y 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 @@ -375,6 +376,7 @@ CONFIG_QPNP_SMB5=y 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 +397,7 @@ CONFIG_QTI_AOP_REG_COOLING_DEVICE=y 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 @@ -425,7 +428,6 @@ CONFIG_VIDEO_VIM2M=y CONFIG_VIDEO_VICODEC=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_QCOM_SPMI_WLED=y @@ -469,7 +471,6 @@ CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y @@ -540,6 +541,12 @@ CONFIG_SM_DISPCC_LITO=y CONFIG_SM_GPUCC_LITO=y 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 CONFIG_MAILBOX=y @@ -550,7 +557,6 @@ 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_SMEM=y @@ -564,6 +570,7 @@ CONFIG_QCOM_RUN_QUEUE_STATS=y 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/cputype.h b/arch/arm64/include/asm/cputype.h index 0811e7cc5a6bb6bf54f41842c203d5715e403465..1e04fdbb6539c5a50eabcf95d5c91d54bc4aaf61 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 @@ -83,6 +84,7 @@ #define ARM_CPU_PART_KRYO5S 0x805 #define ARM_CPU_PART_KRYO2XX_GOLD 0x800 #define ARM_CPU_PART_KRYO2XX_SILVER 0x801 +#define ARM_CPU_PART_CORTEX_A77 0xD0D #define APM_CPU_PART_POTENZA 0x000 @@ -100,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) @@ -110,6 +114,7 @@ #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) #define MIDR_KRYO4G MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO4G) #define MIDR_KRYO5S MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO5S) +#define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) @@ -120,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/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h index 78b942c1bea437c5a0e9124b86afd4c48503d999..e060c2746f36ac0c433b1a2b5debfbec3d68c800 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,8 +91,9 @@ #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) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index f2aa655908213a5c022ca9559ee94b973ded7e6a..6a431774ead60971095b097e1ac3cde4f2753119 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -303,23 +303,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/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index 92be1d12d59080d06472e05e2f292f812a38d224..39dc98dd78ebf9fbf081f550e8e3f1eaeb221d76 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/cpufeature.c b/arch/arm64/kernel/cpufeature.c index d6fb0be5f5e97effc1dce5fdc3ec07a4df33062d..c6324c2af4387b6d75e4349e0e24c38333348ef1 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 7eff8afa035fdbca60383a946558ca06befde450..b6618391be8c6d931029fbbe40c346755e119b59 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 77ca59598c8b1cec86059e56eec4c9f635d716ca..06058fba5f86c7af9a784288dc976ceb397cab86 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -703,6 +703,7 @@ secondary_startup: /* * 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 @@ ENTRY(__enable_mmu) 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/smp.c b/arch/arm64/kernel/smp.c index 583c4fa66f827f595e507b361d7b41d31d5f1a7b..81d22c8162a564ef0f01077bbbab072d4381658c 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 c65a8237cf7ee3f117af422aed2d0f5e325690f7..4aded5b86b0080c5a3c4c80c9e7036f054f3f8b8 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/lib/clear_user.S b/arch/arm64/lib/clear_user.S index 21ba0b29621b2fab087aa8adaeb15e0d589966ac..4374020c824a0addf16c65f18fb5740d2cdaec9c 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -57,5 +57,6 @@ ENDPROC(__arch_clear_user) .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 20305d485046754c7c59c48ef28c77dfc54f2ca0..96b22c0fa34328053c8838a870c162e65a9f8312 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -75,5 +75,6 @@ ENDPROC(__arch_copy_from_user) .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 54b75deb1d1606c2d5c317b35ee2f5bbc78144ba..e56c705f1f2361ba4e52274ee506bab201a48e4d 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -77,5 +77,6 @@ ENDPROC(__arch_copy_in_user) .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 fda6172d6b88819bcec2a69439fa202c57119f40..6b99b939c50f2a5fd79ce1fcacfc5e31e5b1293c 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -74,5 +74,6 @@ ENDPROC(__arch_copy_to_user) .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/init.c b/arch/arm64/mm/init.c index e99c8b60b743bdc3238f410801266f1b0c53cbd6..16a3d42c8caa7b5abe3e74d786d14c145364938c 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -330,6 +330,7 @@ static void __init update_memory_limit(void) phys_addr_t end_addr, addr_aligned, offset; int len; const __be32 *prop; + char *status; phys_addr_t min_ddr_sz = 0, offline_sz = 0; int t_len = (2 * dt_root_size_cells) * sizeof(__be32); @@ -349,6 +350,12 @@ static void __init update_memory_limit(void) return; } + status = (char *)fdt_getprop(initial_boot_params, node, "status", NULL); + if (status && !strcmp(status, "disabled")) { + pr_info("mem-offline device is disabled\n"); + return; + } + prop = of_get_flat_dt_prop(node, "offline-sizes", &len); if (prop) { if (len % t_len != 0) { diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 85026ee88a9e8035f8c65883676ac67e31ca1251..70861832f9891b6dd4584dcb0c00518e2769e787 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1366,6 +1366,14 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys) return NULL; memblock_reserve(dt_phys, size); + + /* + * memblock_dbg is not up because of parse_early_param get called after + * setup_machine_fd. To capture fdt reserved info below pr_info is + * added. + */ + pr_info("memblock_reserve: 0x%x %pS\n", size - 1, (void *) _RET_IP_); + return dt_virt; } diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c index 146c04ceaa514bace2f7aaa49936ca5ca8865741..54529b4ed513022a7a69c77b6d65165934278070 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 b29c3b241e1bb590eba500918e39e4ce9bc59796..10708287706451e4d7801a1ad334e7523b3cc259 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 4f3ab5707265204f309b31695cb3a60a4f7a8d1c..548bac6c60f8ccd18216e58f99197034c313b41a 100644 --- a/arch/microblaze/Makefile +++ b/arch/microblaze/Makefile @@ -83,19 +83,21 @@ archclean: 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.
- ELF image with $(arch)/boot/dts/
.dts linked in' - echo ' - stripped elf with fdt blob' - echo ' simpleImage.
.unstrip - full ELF image with fdt blob' - echo ' *_defconfig - Select default config from arch/microblaze/configs' - echo '' + echo ' simpleImage.
- Create the following images with
.dtb linked in' + echo ' simpleImage.
: raw image' + echo ' simpleImage.
.ub : raw image with U-Boot header' + echo ' simpleImage.
.unstrip: ELF (identical to vmlinux)' + echo ' simpleImage.
.strip : stripped ELF' echo ' Targets with
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
with the' diff --git a/arch/microblaze/boot/Makefile b/arch/microblaze/boot/Makefile index 600e5a198bd2aa509ecf577081f3fa4ce2d82657..cff570a719461ffd34ffec2e93d0ddaea0776de6 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) + +$(obj)/simpleImage.$(DTB).ub: $(obj)/simpleImage.$(DTB) FORCE $(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).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 201caf226b47b4b841ea4267ac0d6fd430475de6..a830a9701e501a8c73b1db9dec07660a3ac5e007 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -806,6 +806,7 @@ config SIBYTE_LITTLESUR 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 1a8a07e7a5633d0c92056989ab1dfddbc834615e..46eddbec8d9fdec090ee273e1e5ba3cd573ba612 100644 --- a/arch/mips/bcm47xx/workarounds.c +++ b/arch/mips/bcm47xx/workarounds.c @@ -5,9 +5,8 @@ #include #include -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 7019e2967009e98e6f6191c1e86969d4663a957f..bbbf8057565b236e69561439f8326106204ff82b 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 a2af38cf28a701b7cd3f3d2f1c8f5da3d391ba82..64574e74cb236a3124c4f2d0b9c279ce036309aa 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 8241fc6aa17d8668c21373febf27960e8b495e57..3839feba68f2043c99ad4ed46a24698e7ec5780f 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 807cadaf554e2e0e3d5665c95c8d2cb19ce1d705..5ba181e87d2c1ff2dab04190f700c7db9bcbf8af 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 8772617b64cefec0835523fe107a2febdaa778ab..80112f2298b68cce9ba80e994167eaec0c435098 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 bf6a8afd7ad2783dff63cb55954a2e34d8ca6e79..581a6a3c66e405c6ea23cdfefc45690a2fe43209 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 89e9fb7976fe61e3671100f7d2dae00b3d59bb5f..520ca166cbed572f8c75d0a333740616aa2d3adc 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 493a3cc7c39ad5a412d6d460061b740260dfff9c..cfdbe66575f4d82012d6350bfad2a1c624639e71 100644 --- a/arch/mips/include/asm/kexec.h +++ b/arch/mips/include/asm/kexec.h @@ -12,11 +12,11 @@ #include /* 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 5f47f76ed510a53dd760bedf911d3d1414d8a4de..20eb9c46a75ab18dbb2fec8b0a67d7dfb1af7300 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/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c index 159e83add4bb3e6b43f105b521761eb9cb80491b..5ec546b5eed1c0c6a46b6bfbc5dadf0188667a42 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/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index f6d9182ef82a9cd2aa528d2de40f6e4634116119..70a1ab66d252c15f4c305d5901dee16b669f9320 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 8e84fc385b946c391b932777d49bb82d05e25598..19b2841219adfeeab42d37edcfb5c8ccd87d68d1 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 63a1a5ef5219f47bcd9797e543298056294da22f..87683583f2064ad011a6befc19cc6bbdf50ba52c 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 0c826ad6e994cce359474229acf08ff0d0330b78..ee6159d2ed22ec4954fe7def5eea7f2e6099bcc6 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -240,7 +240,7 @@ handler: ;\ * 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 9fc6b60140f007bea1442f60727a22aee24776c9..31ed257ff06188eec745477154aa74d17910ef37 100644 --- a/arch/openrisc/kernel/head.S +++ b/arch/openrisc/kernel/head.S @@ -1728,7 +1728,7 @@ _string_nl: /* * .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/powerpc/Makefile b/arch/powerpc/Makefile index c4c03992ee828c5805fd24cffdf6fadaa0724714..dfcb698ec8f3bfd2606db35ee950fceb51803ca4 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -145,7 +145,14 @@ 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 25e3184f11f78c0f9a58633fa9fb63b626574bf9..7d5ddf53750ce7f0f6974f240604cbb9322d5188 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -32,8 +32,8 @@ else 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 538e42b1120d861b28385bdf6468ba4c0a1e0b2c..b5861fa3836c112e55d9da0712d2841bc276caad 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 2a0c8b1bf147959bd23b53d844acc19a242dcdbc..2abc8e83b95e901de1c340e19faa3d709bb9e217 100644 --- a/arch/powerpc/boot/libfdt_env.h +++ b/arch/powerpc/boot/libfdt_env.h @@ -5,6 +5,8 @@ #include #include +#define INT_MAX ((int)(~0U>>1)) + #include "of.h" typedef unsigned long uintptr_t; diff --git a/arch/powerpc/boot/opal.c b/arch/powerpc/boot/opal.c index 0272570d02de15ba70363809c987ca7efd97dd99..dfb199ef5b949d066ecedb2ce3a950711b425f25 100644 --- a/arch/powerpc/boot/opal.c +++ b/arch/powerpc/boot/opal.c @@ -13,8 +13,6 @@ #include #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 e398173ae67d14256437b07a3357b7cc2f8d01f7..d0609c116e4fd2acf65bb49445b1c16b37a22790 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 6a6804c2e1b08656d67f9955d14fee6d2823c09d..59b35b93eadec88a1dd377a0e863a4b0aef2d292 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 ce242b9ea8c67d1546db351ade84d6b994f35b06..7c1d8e74b25d4f18ca31fdfa1dd773ab47a0e542 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/reg.h b/arch/powerpc/include/asm/reg.h index 640a4d818772a4afe0e07ba1018b62fadaa7ee10..af99716615122c259e94f6d4e0a5e0fa7d430e3d 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 759597bf0fd867bd6d4c151acb8acce7f7f3ff6b..ccf44c135389a111023a244498f9fab60a8583f5 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 d89beaba26ff95d2ab0ed48cdaf1ba7fc8f3bd73..8b957aabb826d3b55674cab70e3fc096869f8ec0 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/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 1ca9e37f7cc998c11eaeceab9ad6215431ae5ebb..38a25ff8afb7642f687ed78775d3132787d1744e 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 bbc06bd72b1f2497ef0a1120e631d4243f3f432a..4333b9a473dc47aa02ea9b80d7b474c7f112354f 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 eac18790d1b158e801b15da73780ab11e3796ebf..d450280e5c29c1b5e1260d55fd6a98ac1be55fda 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 7c3738d890e8b21d93581c09ce46620afe88cef9..50400f213bbf2a57ea60ba3330cf3beee5fd9da4 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 2da01340c84c3f3ade55cecb0486b33730856d04..1eab54bc6ee9385c8de90ac0b9cde6b7d6da1302 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/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index f3b8e04eca9c3283e0e2c64ea8e2354a22b20ce8..c6f41907f0d716fca6298715f7bf845c1f5411ac 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 110eba400de7cc07cb1c3a9942f2bec64c4d5ad1..af1f3d5f9a0f71c08a1e38d296615acf502560d7 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 1b238ecc553e2fc4d591daa3e6b995da981c2c27..210d239a9395077f63ec42148b3ce44a35fd6780 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 7a46e0e57a3647e70e64d40aaf28f985d222cc0a..58b50967b3e5903e8903bc68d8dbb5255a620823 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -533,6 +533,7 @@ flush_count_cache: /* Save LR into r9 */ mflr r9 + // Flush the link stack .rept 64 bl .+4 .endr @@ -542,6 +543,11 @@ flush_count_cache: .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 90af86f143a911365ecaf53b64f5d2b7fb653abd..e1dab9b1e447814b3a1c93c95f1d04be9c95c1be 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1123,7 +1123,7 @@ TRAMP_REAL_BEGIN(hmi_exception_early) 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 19b4c628f3beced4211da41d48c528085254fee9..f0dc680e659af7029868b88cc36a002f3dd73126 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/misc_64.S b/arch/powerpc/kernel/misc_64.S index 1bf6aaefd26a295553b063dc04c672fa95d3eb20..facc02964ab3155b9c400f825dd38f9018de4014 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S @@ -87,7 +87,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) 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 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) 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 909c9407e392a454222c7b8f7376e70a5d890e66..02b69a68139cc2d85eafc27d5e17ad089c5fdd67 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 c4d7078e5295fa6eec81f1913783191a162aac06..8e88f78e57dbae9c5d11b6eb033cc72f7ac56c34 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 9e41a9de432355414cdb9d6ce0057fecd5393369..95d1264ba7952d0f2a638b4fdd452201459b8ec3 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 70568ccbd9fd5eae17014473aa415d9b472b7d86..a5c5940d970abf34cd77c5584ac22323b71313f6 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()) @@ -201,11 +205,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 +378,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 +434,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 70f145e0248776d6692439d60e4fe6f10097abc0..8487ad6864621726f8ea00132cb521eda0a778bf 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -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 3745113fcc652d8ca3e66692aaab7d87f7ea9338..2a7eb5452aba79fc4d2b2c709642b0e71cfc6130 100644 --- a/arch/powerpc/kernel/vdso32/datapage.S +++ b/arch/powerpc/kernel/vdso32/datapage.S @@ -37,6 +37,7 @@ data_page_branch: 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 75cff3f336b3a433d6b3b16ed03198736ebadb2d..49eecd28aef1e49c90a8a9237ffebdc91963eac9 100644 --- a/arch/powerpc/kernel/vdso32/gettimeofday.S +++ b/arch/powerpc/kernel/vdso32/gettimeofday.S @@ -139,6 +139,7 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) */ 99: li r0,__NR_clock_gettime + .cfi_restore lr sc blr .cfi_endproc @@ -159,12 +160,15 @@ V_FUNCTION_BEGIN(__kernel_clock_getres) 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 69c5af2b3c96cfd46a85a2c250a9727e585fcabf..228a4a2383d69e52d5dd4a2d3f779004ce5d6ef1 100644 --- a/arch/powerpc/kernel/vdso64/cacheflush.S +++ b/arch/powerpc/kernel/vdso64/cacheflush.S @@ -39,7 +39,7 @@ V_FUNCTION_BEGIN(__kernel_sync_dicache) 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 @@ V_FUNCTION_BEGIN(__kernel_sync_dicache) 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 abf17feffe4048af18382a075b01553f7e9be00e..bf966869151169b6b101675b4822609375058019 100644 --- a/arch/powerpc/kernel/vdso64/datapage.S +++ b/arch/powerpc/kernel/vdso64/datapage.S @@ -37,6 +37,7 @@ data_page_branch: 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 afbad2ac31472c6f28667da0cfdb1c03993dab2c..020e90d1079ddc2c9c0b1f40e76e37e5f3abdbb2 100644 --- a/arch/powerpc/kernel/vdso64/gettimeofday.S +++ b/arch/powerpc/kernel/vdso64/gettimeofday.S @@ -169,6 +169,7 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) */ 99: li r0,__NR_clock_gettime + .cfi_restore lr sc blr .cfi_endproc @@ -189,12 +190,15 @@ V_FUNCTION_BEGIN(__kernel_clock_getres) 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 281f074581a3b02c0b51261315799cb9a609dde0..cc05f346e04219d0c737948c463d890f55bf6deb 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 07a8004c3c237520954136dfd5409a80a692dfe9..65486c3d029b58b6eca6769a439c4663f188f544 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 eb8b11515a7ffe8439c26995aa6eb4d9183fde59..d258ed4ef77c338a7e7052e259e134071f269cef 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 7c68d834c94a75dcfbc97a18fa0bcaa9fec63417..02ab86be9dedd4adf79a66eafe3261fce1c6ecc2 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_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index f1878e13dd5691b11743364d57e6f75a953fe8ec..7fe3077a1ef642465c9b2e6724087948790c9780 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -1559,6 +1560,10 @@ mc_cont: 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 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) 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 365526ee29b88e1d421a49cd734605b5996038b5..6e0ff8b600ced85f19dff12e8204ad03b2096a0b 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 0c13561d8b807d4695dc5ba0973b93c9daabb311..42a48c5f7b7f5910490a0ff4ad7aed41b89e9ffd 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/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c index 3ea4c1f107d7ef99ad5699d16b6cc24dfc41d453..69caeb5bccb213f836b353989c31e81e0303a788 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 bea6c544e38f94c1ede88190013775b1b8413ebb..06783270a12428fdb73b42bfd5f693be2903c367 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 9f574e59d178618af5ba5056f13241f0776f9852..2f162c6e52d4f1632e53e2cee263b3b007e8b838 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 0cddae4263f9699d5eddb6635cc630e85b6febd2..1749f15fc0705714996d07d3b102f88e4258f7c3 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 279a51bf94d052ff100fe1054c05584375c3d61d..7e3ab477f67fef4cf326b1a44015d2cc73733a45 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -949,6 +949,19 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) goto out_addrs; } + /* + * 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 diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c index 6a2f65d3d088cf964a4c61d3e6e64891354eed05..053b8e9aa9e756124d3e25ed1cc5c4595a68b2ad 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 0028f4b9490dba671b0e87e72fdf6fb0781f90a0..e5a621699a6d84cb1c24e145d0f9c49c34f3cf4a 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 d75c9816a5c92ad4211d71568e90848ab7fc998a..2b6589fe812dde8746f847e129211802408f4408 100644 --- a/arch/powerpc/platforms/83xx/misc.c +++ b/arch/powerpc/platforms/83xx/misc.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -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 3c1beae29f2d838ddc3871a0f78608b5068fb125..9dd5b8909178bdc4f736bf975c94c454d36d8ee5 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 a29fdf8a2e56ed5ab70d663f2bb40e04fe821e72..dd3cc4632b9ae6732d79e774fda50ae0c2171813 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 326ca6288bb12969a8941b471c1cc3bf3a76f02d..ee63749a2d47eaee50cb45bb0da47e9b8ccaab4e 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 13aef2323bbca5638889afa54d8f2fcfe7b98636..db230a35609bfef59f97fdee53aed43cce843f9c 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 db09c7022635bca8fce8fb6225554a3d76d8d200..fdd9577d17980d1a36ce351cd037d1d697b42d09 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 */ @@ -176,6 +181,27 @@ static void pnv_smp_cpu_kill_self(void) if (cpu_has_feature(CPU_FTR_ARCH_207S)) 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 @@ -201,6 +227,7 @@ static void pnv_smp_cpu_kill_self(void) 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 cdbfc5cfd6f38ee85620288c14d2288808572e1e..f5387ad822798125402d76a530a6d7cc2c1733f5 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/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index e3010b14aea510a713dee19725b31c8b8e84275d..c5ffcadab7302f65b9737e81a710a5bcf7b3f787 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 18014cdeb590aa40eb61ff78ae8e273cb2f3233a..ef6595153642e3af4b5f52b300cbb3dd7d6b6d1f 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 f99cd31b6fd1a96ed5a6886ba650412f6fe4d6df..7f86bc3eaadecc2e2496b400e790dd9fbcbe7b27 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/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index ea602f7f97ce1df5386f02808438cdb3272dd6d5..49e3a88b6a0c1474656c3c7020ea5b3d7e8c4691 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -48,6 +48,7 @@ #include #include #include +#include #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/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index 0b24b10312213e60d7aff0d1575cb075dab1f79e..3c939b9de488ec21c60ee9d32ef2eb6a16a5ccd8 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 575db3b06a6b84acdcb7625dc3b1e2eaa688e882..e3ebf64693929f64da6248495a0297e980bcc1c6 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/xmon/Makefile b/arch/powerpc/xmon/Makefile index 9d7d8e6d705c4fa07289bc06e0e4f81f48f553bb..365e711bebabbb4b8209fffadcaf6ce7fb63d4af 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 @@ UBSAN_SANITIZE := n 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 bb5db7bfd8539edaaf68af6c6e67e6c5df6bf8de..f0fa22e7d36c78b0011e4a7932266a9609f825bb 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/mm/ioremap.c b/arch/riscv/mm/ioremap.c index 70ef2724cdf61e5b2001f0ec6243b7f5e9c6bfaa..bd2f2db557cc54f182794284bb0d9d317cb2b36a 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 9e6668ee93de83122fbfc1c3ade9ab9b0519d87c..f6a9b0c2035530fef55f9cf579d63a396c4598a9 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 b375c6c5ae7b190ff242df88bf83d7963cf0cef0..9b3d821e5b46ecd56d66e80ef2006ad879695afb 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 a8418e1379eb7ee08c92acd034eae000cb19c695..bcfb6371086f2319f6901d2cc52a1d8c44fd0a1a 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 09b61d0e491f6882253cdded41fcd05bc40d17de..8d04e6f3f79649d460376f09217c9e8fe211a850 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/pgtable.h b/arch/s390/include/asm/pgtable.h index de05466ce50c596e6f9c7b75f9665dca9eda4e93..0a326da1562fc80598ccc2f71ad33e40f5532f9c 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/uaccess.h b/arch/s390/include/asm/uaccess.h index 5332f628c1edc8e9f6d4ba5f53f6e729a14df707..40194f8c772a038f9b50282adc788ccf27550e12 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 b205c0ff0b22047d4dcc051780f29ca68c5ba574..762fc45376ffdba56ebcbfefcecada3941468fdd 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -23,6 +23,8 @@ KCOV_INSTRUMENT_early_nobss.o := n 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/idle.c b/arch/s390/kernel/idle.c index b9d8fe45737aa2e529cb9eec244349e121803858..8f8456816d83e890a3f3150ccec184f21810a509 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 44404836e9d1152f9f7416259d54018e6c4a5643..df92c2af99b69556042b57b850168780bd8e5156 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -2045,14 +2045,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 +2064,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 da02f4087d61fdb01c8583e1213cb53e030d82d1..df2413f26a8f2397a6cdb9970e8ad9042a916adb 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)); @@ -810,6 +813,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(); diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index 3031cc6dd0ab48de8ebf3797a2bc748995d67c49..7ab7d256d1eb7ed9622e9d04c43d47f9d0bf13b5 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 04dd3e2c3bd9b4b1ee9f20f53c358cb817ae6551..e76309fbbcb3b6e23af21350f98f2b555502b978 100644 --- a/arch/s390/kernel/vdso32/Makefile +++ b/arch/s390/kernel/vdso32/Makefile @@ -28,9 +28,10 @@ obj-y += vdso32_wrapper.o 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 a9418bf975db5a32db1c88b07d59f71454ce550b..ada5c11a16e5adb20cfcd7f9908296eb1ac6cbb9 100644 --- a/arch/s390/kernel/vdso32/clock_gettime.S +++ b/arch/s390/kernel/vdso32/clock_gettime.S @@ -10,6 +10,7 @@ #include #include #include +#include .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 @@ __kernel_clock_gettime: 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 @@ __kernel_clock_gettime: 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 3c0db0fa6ad90304929e7263ea2ca07bbe077eca..b23063fbc892cd91b1e08fabc52a52b26f968e98 100644 --- a/arch/s390/kernel/vdso32/gettimeofday.S +++ b/arch/s390/kernel/vdso32/gettimeofday.S @@ -10,6 +10,7 @@ #include #include #include +#include .text .align 4 @@ -19,7 +20,7 @@ __kernel_gettimeofday: 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 ddebc26cd9494156040d893a8cf623bfb7c510d6..f849ac61c5da02ee8b764bc3c01fc44c16137e04 100644 --- a/arch/s390/kernel/vdso64/Makefile +++ b/arch/s390/kernel/vdso64/Makefile @@ -28,9 +28,10 @@ obj-y += vdso64_wrapper.o 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 fac3ab5ec83a9c3a73f9201b5e094309dda3a1a9..9d2ee79b90f250afeedaeb22e6646ef55bfce056 100644 --- a/arch/s390/kernel/vdso64/clock_gettime.S +++ b/arch/s390/kernel/vdso64/clock_gettime.S @@ -10,6 +10,7 @@ #include #include #include +#include .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 @@ __kernel_clock_gettime: 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 @@ __kernel_clock_gettime: 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 @@ __kernel_clock_gettime: 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 6e1f0b421695ac5c4b4bee16adee3690bf89b705..aebe10dc7c99a13498edd6ffddf99d822cc37d23 100644 --- a/arch/s390/kernel/vdso64/gettimeofday.S +++ b/arch/s390/kernel/vdso64/gettimeofday.S @@ -10,6 +10,7 @@ #include #include #include +#include .text .align 4 @@ -19,7 +20,7 @@ __kernel_gettimeofday: 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 fac1d4eaa426868882e8082bc840b3861d33a7cf..db3196aebaa1c82b8bc47ccc7727c374b3da8226 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 57ab40188d4bddab071505f1d5a204a82dca3ce5..5418d10dc2a819b030d01c985a5e8129d5b1e3ce 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -9,5 +9,9 @@ lib-$(CONFIG_SMP) += spinlock.o 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 510a18299196f3b797be51fd22b2bbfbe5cd003f..a51c892f14f3ee5bd393ed74003c96fd80268979 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 2809d11c7a283b825dc28b1ce9bde1cc4af28b09..9b5b866d8adf1025b2f0fff05fdd938ce4af727f 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/sparc/include/asm/cmpxchg_64.h b/arch/sparc/include/asm/cmpxchg_64.h index f71ef3729888f2b14d764d2b39e3ad9741fe230e..316faa0130bab987e0818a33f3654fe9a50be0b0 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 b162c23ae8c2305eea1ce1f8b6bb2a16aadcca24..7a836d21ff0c8cd4002febc01eb0b9e6efcf18e0 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 05df5f0430535f307495662610f2c06dda92d0b5..3c5a1c620f0f7759aa1af38c889078ddb77da2b2 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 222785af550b46736676808b6e00d8d8cef9a286..ec4da4dc98f12bf006465aa19d9febc4d4b9ca15 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,61 +1473,82 @@ 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; - } - build_prologue(&ctx); - build_epilogue(&ctx); + for (i = 0; i < prog->len; i++) + ctx.offset[i] = i * (12 * 4); - /* Now we know the actual image size. */ - image_size = sizeof(u32) * ctx.idx; - header = bpf_jit_binary_alloc(image_size, &image_ptr, - sizeof(u32), jit_fill_hole); - if (header == NULL) { - prog = orig_prog; - goto out_off; - } - - ctx.image = (u32 *)image_ptr; -skip_init_ctx: - for (pass = 1; pass < 3; pass++) { + prev_image_size = ~0U; + for (pass = 1; pass < 40; pass++) { ctx.idx = 0; build_prologue(&ctx); - if (build_body(&ctx)) { - bpf_jit_binary_free(header); prog = orig_prog; goto out_off; } - 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), + 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(); + } + + /* Now we know the actual image size. */ + image_size = sizeof(u32) * ctx.idx; + header = bpf_jit_binary_alloc(image_size, &image_ptr, + sizeof(u32), jit_fill_hole); + if (header == NULL) { + prog = orig_prog; + goto out_off; + } + + ctx.image = (u32 *)image_ptr; +skip_init_ctx: + ctx.idx = 0; + + build_prologue(&ctx); + + if (build_body(&ctx)) { + bpf_jit_binary_free(header); + prog = orig_prog; + goto out_off; + } + + build_epilogue(&ctx); + + 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 2014597605ea9cd24ff881370ce063ddb57992e0..85726eeec34512cde5fa0dd66a4485a9b64af311 100644 --- a/arch/um/Kconfig.debug +++ b/arch/um/Kconfig.debug @@ -16,6 +16,7 @@ config GPROF 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 8d80b27502e6ae4feb235d01801a67be5ca8cce4..7e524efed58484c394beefd2d6d3651ee0eeb5fa 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 4d6a78e31089f6c12f94bddbf9a53f85bf8d0401..00c4c2735a5f7069f092de80bd73865e73510657 100644 --- a/arch/um/drivers/vector_user.c +++ b/arch/um/drivers/vector_user.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "vector_user.h" #define ID_GRE 0 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f85253f01cdb02bd42f9418c6e25a44956be75f9..d9ba53381dd5b67baaa8cab4fd2a796ba238a209 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1904,6 +1904,51 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS 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 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/events/amd/core.c b/arch/x86/events/amd/core.c index 27ade3cb6482ce417e0798d531f1c9ad3cf3c17c..defb536aebce2144db706672e62d3e5ee82fdbe3 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #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 80c6d84cad67ba116c3e1881d4989f761a5adfc8..07bf5517d9d8bc981fbee0d71996362fd9cf0e4c 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/intel/uncore.c b/arch/x86/events/intel/uncore.c index 2690135bf83f0025f19f16e84d2557b6cf6357a5..7098b9b05d566048abab8938c32c17c2ad2bb0e0 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 42fa3974c421c01ec79ccb9809bf023689919519..40e040ec31b505ec1a8cc050942f3210575dcf39 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 3fb85515528624fbbeb588c1db156c7bbd91bd2f..8a9cff1f129dc57efb0be3d4a8b5719f8eaa436d 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -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 759f0a1766124513cf015cf2f2a547710477b9f8..8c13b99b9507b1bee83c5f7599c0bab00004b21c 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/intel-family.h b/arch/x86/include/asm/intel-family.h index 5d0b72f2814029d8cb3b970e51f3e2c78056d9ef..2a8e5f78e23c4bb3f32cd9c0104d9a90f939fa99 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 f327236f0fa7108454296a2af00ccc8e99fe571c..5125fca472bb0b44dc8baab1a00e719096e0d8e3 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 0d3f5cf3ff3eae84e05d723bd76fdc44492d83dc..155be8adb934e4e2f70db6c7c9e3aa84c4f7dde0 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 a1d22e4428f63732330c4d68b146a58228dd63b2..0f4feee6d082eddca270ccf2f6a526c3b9fbf523 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 28cb2b31527a3c5fe5e45f29fff540acef65c73c..09c7466c4880691f88caaf91717f1e91e7180215 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 /** - * 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 b54f25697beb00b81102a085e43c2b1720d88a8c..efb44bd3a7140be1df3e24398028c650ccfc0739 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 6de1fd3d009744277c0be1593daaa380e1f0fedb..ee696efec99fd213697695c1351e656dd19dd438 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h @@ -236,24 +236,52 @@ static inline int regs_within_kernel_stack(struct pt_regs *regs, (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); } +/** + * 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/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index dfdd1caf0d55db514bacc3f3b881fc88b14f36b9..1ca76ca944badad782d95dc592f277e3028a4657 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/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 347137e80bf5ace65a2688a78897f6f704e96800..320769b4807b509aa49006fcfe52892f42348b73 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -28,7 +28,7 @@ obj-y += cpuid-deps.o 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 ee7d17611ead47f87774400982503b65553f209a..2d23a448e72de1d344d9fd455901d6aeae7e5819 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -32,11 +32,15 @@ #include #include +#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]); } @@ -266,6 +283,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 @@ -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 b33fdfa0ff49e6f9e70754051216eee0d08eed16..a6458ab499c213800168e7e7dced4edecebc7c17 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_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_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 | 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 7b229afa0a37a6a5bd6c910eaaa2e515fb562e82..236582c90d3f547a8b3295c64bd5d78534699528 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 fa61c870ada94e6fc01426242d3836766f8d76be..1d9b8aaea06c8c9c7d14b0c30d51ded3bac83d7d 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 fc3c07fe7df58a22c01c8c1180d0b394bde8b59a..a5287b18a63f567c6903f37e2de4d0c7fde68367 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 abb71ac704433cea9f2eca68b8f3ed60566c22da..b99a04da70f618d1668d66390a9bb749e364c798 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 627e5c809b33dc8174121689d0d8cccec723e83e..2052e1e6a11c903f22e78205ae234a05b78cc665 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 912d53939f4f4ef6160196cf38718eeb5f0c10ae..a999a58ca33180ea3374b6ee570cd2426aea67f9 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 2013699a5c54a64231217512a5bbde9005d83e9c..a2d7e6646cce8034a632a78eb1095f53edcd42c3 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; @@ -1020,6 +1101,41 @@ bool rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_domain *d, return false; } +/** + * 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 * @@ -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 ff1c00b695aeda4960eaad08dc42826bdf44df8b..1ceccc4a5472ce90d6245e781f9dd1b9af2e73b7 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 fee118b3b69fd7b2f0ded5706b630502870b96ea..87ed8462a5c72e68c7bc6c21a3e08d13e5486602 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -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 9f915a8791cc7495fdfea1824de0bbd27d4b2db2..5bdfe52b2c9d9737a345ecc30642d025dc5e8854 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -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/tsx.c b/arch/x86/kernel/cpu/tsx.c new file mode 100644 index 0000000000000000000000000000000000000000..3e20d322bc98b636cde2747394d26fcec8783a0c --- /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 + */ + +#include + +#include + +#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 e83a057564d1b56e82e3bd52229101d402d27d93..173e915e11d5430922deadb70be8ffa6bb7dc483 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 516ec7586a5fbd9ce820b93f5d66a0816ab6d449..8d4d5064531060e85161cc39e9fe6c83b63a25a6 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -40,6 +40,7 @@ #include #include #include +#include #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 9119859ba78714d882a09fe49b263d2e085a5329..420aa7d3a2e6b7d450f5c8e4cd6c227a01dee13c 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 b810102a9cfac9b4354a6339b3fb18b50921dad5..48c24d0e9e75c746ae6c46e520b9aac9b3c8f8de 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 88940261fb5379841c7e516f2c78aa912baddb57..eddf91a0e363eab11933ed8e2b3bd01e956be982 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,30 @@ #include #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 -#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; - break; - } + 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); - drop_large_spte(vcpu, iterator.sptep); - if (!is_shadow_present_pte(*iterator.sptep)) { - u64 base_addr = iterator.addr; + base_gfn = gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1); + if (it.level == level) + break; - 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); + 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); - 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 65892288bf510fbf56916137cc194c6175df7d50..f7b2de7b6382fe28d9e5926d2e7ce491f77a04d6 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 c73bf4e4988cb5c84065dd324b50b93cbe12c2e1..918b0d5bf2724c66953addcc6b0bfd5b776c6c45 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 14ffd973df54e6d1c122b14d6f15f33cea931ed6..adf42dc8d38b05bab3722558fbb740eb931c98f4 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); - validate_direct_spte(vcpu, it.sptep, direct_access); - drop_large_spte(vcpu, 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); - if (is_shadow_present_pte(*it.sptep)) - continue; + base_gfn = gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1); + if (it.level == hlevel) + break; + + validate_direct_spte(vcpu, it.sptep, direct_access); - direct_gfn = gw->gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1); + drop_large_spte(vcpu, it.sptep); - 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 ac2cc2ed7a85f8b22c996ad71e9baf418dbdbbf7..7657dcd72134bd607fdd38ad62faf6e45db4241b 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 6f7b3acdab263b6ef26ac70d55e02c11d6bf95b3..fa2abed1a14dabd6a781bd7b184f0a05981ebd9b 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 6ae8a013af31ac36b7fcd57768cd3e5b85c9cd7d..353f63f3b262673571dc0e94e46a8426c81b2962 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); @@ -1130,6 +1132,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. @@ -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 3a91ea760f073974a382d090cbe74ca4067a817b..608e5f8c5d0a56c34680babca4a041799a024bd8 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/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c index c05a818224bb06481343dc133406625b9169fb0a..abcb8d00b01486f431fc9fa591f6e6260c8e1153 100644 --- a/arch/x86/mm/dump_pagetables.c +++ b/arch/x86/mm/dump_pagetables.c @@ -19,7 +19,9 @@ #include #include #include +#include +#include #include /* @@ -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 1bcb7242ad79a099880e055cb378df1ee695e9d9..72e6fa1a913c4888d47b47c04ce9e937c6f34ff5 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/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c index 8f6cc71e08482b05ddb53efce7ee3b818b1061f2..24d573bc550d9885537a19ab8582cc95a988f6ce 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,78 +742,22 @@ 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)); - /* 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)); - - /* < 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)); + /* 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)); - /* 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)); + /* if ecx >= 32, mov dreg_lo into dreg_hi and clear dreg_lo */ - /* >= 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)); + /* cmp ecx,32 */ + EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); + /* skip the next two instructions (4 bytes) when < 32 */ + EMIT2(IA32_JB, 4); - /* >= 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 */ @@ -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,78 +795,22 @@ 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)); - /* 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)); - - /* < 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 */ + /* 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)); - /* 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)); + /* if ecx >= 32, mov dreg_hi to dreg_lo and set/clear dreg_hi depending on sign */ - /* 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)); + /* cmp ecx,32 */ + EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); + /* skip the next two instructions (5 bytes) when < 32 */ + EMIT2(IA32_JB, 5); - /* >= 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 */ - 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 */ + /* sar dreg_hi,31 */ 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 */ @@ -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)); - /* 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)); - - /* < 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)); + /* 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)); - /* 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)); + /* if ecx >= 32, mov dreg_hi to dreg_lo and clear 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)); - - /* >= 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)); + /* cmp ecx,32 */ + EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); + /* skip the next two instructions (4 bytes) when < 32 */ + EMIT2(IA32_JB, 4); - /* >= 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 bd372e896557190b7a17a97d74f38fe7a0107f81..e723559c386a14212b48987fc034d2c2fda2dece 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -588,6 +588,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] * @@ -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 9061babfbc83d73b59a1cddb7b954353e3f53c2d..335a62e74a2e9ad8d1751bd1aef1bc556d2ce172 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/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index c9986041a5e124dbd325d857a364d637ec19ec87..6c3ec193a24657470f00769fe02cb53103ebe57f 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 b02a36b2c14fb20522dc16d3f4093cbe36e2e740..a42015b305f406ee2b48a40df81daeee628f1832 100644 --- a/arch/x86/tools/gen-insn-attr-x86.awk +++ b/arch/x86/tools/gen-insn-attr-x86.awk @@ -69,7 +69,7 @@ BEGIN { 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 @@ function convert_operands(count,opnd, i,j,imm,mod) 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 c6c7c9b7b5c1926bc06d50878b8af9ddc1fead59..2483ff345bbcde95aa7d6aa0f09e712e242b2a52 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 3a6feed76dfc167736984315390e9b8b0cb81e6c..a93d8a7cef26c2ed2bbabbf2a135ee35ecd5113e 100644 --- a/arch/x86/xen/xen-asm_64.S +++ b/arch/x86/xen/xen-asm_64.S @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -24,6 +25,7 @@ ENTRY(xen_\name) 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 59153d0aa8908dd415c975c87b9140650afa2686..b43f03620843055b4333dbcf117f4759ce8ff034 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 64f4dcff2c8646382e9043cd2460476690805c21..ef5a07b85367270430820bd9dd82259b2ca7d756 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; - - return rq; + if (!(bfqd->busy_queues > 1 && bfq_class_idle(bfqq))) + goto 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 d5e9e60cb1a5f5bd1341efcd2e3647804e251556..a41e9884f2dd2e70b4095c1edde8ef399652c31a 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 527524134693005624d508b6720f8dfeefbcc47b..a06547fe6f6b461eb9c148fe630e123520184b1a 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 8f877eea12817fcfe5ab214356053608e8765716..dca51ca0d2fa6080193c36061a940f98280ce35b 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-merge.c b/block/blk-merge.c index 74d3cb320f8e0bbaf5c49a441084e3d5a484d353..8d6147733c12318cf025476a5c3d0cf68f05c704 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 0b7297a43ccd25748c8b088b351b812fe346dc55..5006a0d0099010413fbffb2a644e5c8b7a93d36f 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 bab47a17b96f4b2dc786d81b4a7d4c88435b821c..8286640d4d663fe51a207421f5a4190ee2d9bfb8 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/crypto/af_alg.c b/crypto/af_alg.c index ec78a04eb136e8bfd31e9ce3ab2d1b0a7ed29be1..ed643ce12278962a3336cf09922064cd99139d5d 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 3cca814348a261e586e8701f1d7e9d77df276d6f..f847c181a39c8f6cadad7a403b0a9a8b75e24585 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 adcce310f646256a16620ef96e886bb3210e41e9..ad739255951f01da1bf42ce68904a070084f97e2 100644 --- a/crypto/ecc.c +++ b/crypto/ecc.c @@ -906,36 +906,50 @@ 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]); } -int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits, - const u64 *private_key, unsigned int private_key_len) +static int __ecc_is_key_valid(const struct ecc_curve *curve, + const u64 *private_key, unsigned int ndigits) { - int nbytes; - const struct ecc_curve *curve = ecc_get_curve(curve_id); + u64 one[ECC_MAX_DIGITS] = { 1, }; + u64 res[ECC_MAX_DIGITS]; if (!private_key) return -EINVAL; - nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT; - - if (private_key_len != nbytes) + if (curve->g.ndigits != ndigits) return -EINVAL; - if (vli_is_zero(private_key, ndigits)) + /* Make sure the private key is in the range [2, n-3]. */ + if (vli_cmp(one, private_key, ndigits) != -1) return -EINVAL; - - /* Make sure the private key is in the range [1, n-1]. */ - if (vli_cmp(curve->n, private_key, ndigits) != 1) + 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, + const u64 *private_key, unsigned int private_key_len) +{ + int nbytes; + const struct ecc_curve *curve = ecc_get_curve(curve_id); + + nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT; + + if (private_key_len != nbytes) + return -EINVAL; + + return __ecc_is_key_valid(curve, private_key, ndigits); +} + /* * ECC private keys are generated using the method of extra random bits, * equivalent to that described in FIPS 186-4, Appendix B.4.1. @@ -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 9893dbfc1af4524fdea9a0f2d6aa1b225534cde7..812476e4682138225fd46fd2745062d5e1d55105 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 828e3b0eaf7fe2c7335d2571d695aedb2e9c5df5..8cc4cf8316fa6c33e3384809210bfd517e8e498a 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 c651e206d79608f38c5cb52538a1c26bca86d02f..30ccd94f87d247d16903f9fb3952514d423662ce 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -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 6b0d3ef7309cb710a63b38848ad35b770fed3c1a..2ccfbb61ca89987d67d68da320251ba291a6982b 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 298180bf7e3c16d73ac3d71d2d4d208633ba44f5..bfcc68b9f708d12ea75bf94c076a3b7be285ada9 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 0f28a38a43ea1dadaeafbbefa478637deb1de90d..99b0da89910989f8ef53caefd2e64333036a49a0 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 70c2bd169f66923f3937f675c2254226af767cfe..49decca4e08ffeabbc0fc4f421a4ad88015884fe 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 39284deedd885f3c925de34bcf9c183e47a2bf70..17df5dacd43cf2c1687c4b0d0cf02c414f1f4706 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 091415b14fbf1109e14e292c233f53f04dadf1e9..3b3a25d9f0e6d25717b3a0b6e43d4f79e525ce17 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 f008ba7c9cedc952852ca1550b0c58a7fece1529..73177b8a07bd10caea0c8a64f06333aa242ab3f8 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -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 d2e29a19890d14db1a9e6fd85f207d558c6a3c6e..92a14686108677c3fe64a96bfbd105f587f10972 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/device_pm.c b/drivers/acpi/device_pm.c index 1806260938e89aebee6d0f125cce9b44448c8c4f..e0927c5fd282188a4ca3d04a874401c0b86c3a8c 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 ed73f6fb0779b9b80ddc5cf5833c08d4c863a248..2261703650125985f9cd0b1900006ca0a5d883a3 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 7433035ded95543e13f67fdc23a42667dcdd6be3..e465e720eab206305aeb89d68ccc11530d19d657 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 7a3431018e0ab4ce96dc055abf31444e498b04bb..5008ead4609a46edafe73b9481b9912c71efc464 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 e1b6231cfa1c5b642e3b0ee78e81ff1c01278bb3..1dcc48b9d33c9c42d5c9a57e57934a97368cbd32 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/android/binder.c b/drivers/android/binder.c index 71e777d1ef9dcf9ee2803244e30b2172caa178ec..c35d9494f40cb7fc134cc0b53bafd0142ab876ee 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 27bd92181bd4d4543d26e4f1c295b18fb6731302..deb37977cab9c12210e1afcced3768c843fed9fe 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 39b181d6bd0d8cf2cbcd9dde1cf89b373ecae6a4..99698d7fe585a8215e0f93a36b9c2bf6a6c903b8 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -121,7 +121,8 @@ config SATA_AHCI_PLATFORM 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_mvebu.c b/drivers/ata/ahci_mvebu.c index f9cb51be38ebfd099940c1836d34e6933275ec54..a54214291481ff04bf24d4a830a7eebd6a718b01 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 46f0bd75eff7984f9709d431aaa874fa951c3724..cf1e0e18a7a98af51d14d6bf4389f8c730751627 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/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c index 0a550190955ad26915842aff139bd9e09588257b..cc6d06c1b2c70a8e50c3f6384004f10d25edeba1 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/atm/zatm.c b/drivers/atm/zatm.c index e89146ddede693a400b0d7019f14ca92aa4dff8e..d5c76b50d3575b753cb8e9f69de6031e145494f4 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 8946dfee4768e8a82fb35f994b4e9ff2b88c245a..e8d676fad0c95600f426d36796c0ffac270ae98d 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 392932f1d34c229bed130520cc68729501473bf4..16de1482a446f7b75dadb9d79f370bb3ac22171a 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 3fa026c3b888517dbcc271f347ba27e0658704ac..b964fe1f6828529ceee71fe28cbcad3ee3360a38 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 dff82a3c2caa90162076a56c7911554d6fb2d09f..e9be1f56929af334202df5f06fb34e7111081100 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #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 bf5be0bfaf7730bfd275a20ca3e26ea46671de2e..52c292d0908a277f61b3d9b9f2282d2dec6ca2aa 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 3aaf6af3ec23d7d54d5f45deb97ede64bec7d3db..2158e130744e00d8584421020eab88b7fa26cc65 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;drivecode == 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 15a9ffce901255efd9591f5d5cdf3c2e349b91c8..19d3598a0e3f52b13f0cf1e1739ef8b20c49f5d9 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 71450317315040ba54e3aca6764d5f941dcee67a..29f16ac7b19ecbc52ba2dae0863138b88068b791 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 8ebe99bde397c1d5e95181c469459f771019ae69..2bd488bca3eb7e834edb712cda01ebeb847a7669 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 0813c654c89387e36e0174a453028e8c5b75f26b..b452359b6aae84f40e5602d16ae51859a42338c0 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 ea58301d0895c96212f2fdc38e39e9859d9c627d..f87371e55e682ac4deaa4ce69fb44765ceb8460b 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/nbd.c b/drivers/block/nbd.c index bc2fa4e85f0cac87a7949a3b472ff2f35504dced..996b1ef5f07666d06848c6d28302acab624ef9c2 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; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index f2c631ce793cc8a342b44381592824bf902282f7..14056dc450642941a12d142ce899ea9c627a5d8e 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 87b9e7fbf0621af826e2b67cc9e518a60a5c6724..27323fa23997da763430533bfa799890483eeda4 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/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index b805afa6645a60dca6b15ae378b832482b8d00c4..5cb7f102c32eafe22a4282bd11ce2211ccb1b02f 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 8a0cdca5954c93064936b3362c69e571cc153e97..612941f3377cb533aeeac368912c0b7660618dee 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -35,6 +35,7 @@ #define BT_PWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg) #define BT_PWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg) + static const struct of_device_id bt_power_match_table[] = { { .compatible = "qca,ar3002" }, { .compatible = "qca,qca6174" }, @@ -662,49 +663,69 @@ static int bt_power_populate_dt_pinfo(struct platform_device *pdev) rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_core, "qca,bt-vdd-core"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-core not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_io, "qca,bt-vdd-io"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-io not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_xtal, "qca,bt-vdd-xtal"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-xtal not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_pa, "qca,bt-vdd-pa"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-pa not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_ldo, "qca,bt-vdd-ldo"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-ldo not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_chip_pwd, "qca,bt-chip-pwd"); + if (rc < 0) + BT_PWR_ERR("bt-chip-pwd not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_aon, "qca,bt-vdd-aon"); + if (rc < 0) + BT_PWR_ERR("bt-vdd-aon not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_dig, "qca,bt-vdd-dig"); - + if (rc < 0) + BT_PWR_ERR("bt-vdd-dig not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_rfa1, "qca,bt-vdd-rfa1"); - + if (rc < 0) + BT_PWR_ERR("bt-vdd-rfa1 not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_rfa2, "qca,bt-vdd-rfa2"); - + if (rc < 0) + BT_PWR_ERR("bt-vdd-rfa2 not provided in device tree"); rc = bt_dt_parse_vreg_info(&pdev->dev, &bt_power_pdata->bt_vdd_asd, "qca,bt-vdd-asd"); - + if (rc < 0) + BT_PWR_ERR("bt-vdd-asd not provided in device tree"); rc = bt_dt_parse_clk_info(&pdev->dev, &bt_power_pdata->bt_chip_clk); + if (rc < 0) + BT_PWR_ERR("clock not provided in device tree"); } bt_power_pdata->bt_power_setup = bluetooth_power; diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index a69536dd286df1a834ae1c7b58a0c5319742b22f..bf886e69050dde70706f32dd77b16a79f25e90bb 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 @@ -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 26d9c1d4daa3cf2cc3b87f773732115916a55020..88d14849865f7f4d941c1095189943a7e9bfe093 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 60d1419590babb9f275e460756250b90b9234ab7..3951f7b23840447030eaabe617d125b97c25495e 100644 --- a/drivers/bluetooth/btrsi.c +++ b/drivers/bluetooth/btrsi.c @@ -21,8 +21,9 @@ #include #include -#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/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index aa6b7ed9fdf12552c3bdbe4fe95e24ae4589bf4f..59e5fc5eec8f878f5f02f5bcd7b41776b2ce039d 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 66fe1e6dc631feaf401b9f9c900262f570e7a68d..27829273f3c913fe53adef14ebd3ccb89b9d0fd7 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 aa2543b3c286968fece4acee97953eb2c1fe0ba8..46e20444ba19bdbe85163eddac4c6d93b4cbab3c 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 4bf961de59170895d3b9580199d983425d6d9339..8dafdd5868ec181b296df02fd62e193411330d43 100644 --- a/drivers/bus/mhi/controllers/mhi_arch_qcom.c +++ b/drivers/bus/mhi/controllers/mhi_arch_qcom.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 #include @@ -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; @@ -333,45 +331,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) +void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl) { - 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; - struct mhi_device *boot_dev; - /* 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 disable boot log channel */ - if (mhi_cntrl->ee == MHI_EE_AMSS) { - boot_dev = arch_info->boot_dev; - 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); - } -} + struct mhi_device *boot_dev = arch_info->boot_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; + ipc_log_string(arch_info->boot_ipc_log, + HLOG "Device entered mission mode\n"); - /* start a boot monitor if not in crashed state */ - if (!mhi_dev->mdm_state) - arch_info->cookie = async_schedule(mhi_boot_monitor, mhi_cntrl); + /* disable boot logger channel */ + if (boot_dev) + mhi_unprepare_from_transfer(boot_dev); - return 0; + 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, @@ -606,7 +580,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 diff --git a/drivers/bus/mhi/controllers/mhi_qcom.c b/drivers/bus/mhi/controllers/mhi_qcom.c index 1801155b7a7d51f35f3e0e65bf2b7531b441a019..78959a9d54133b8b1e8106bae8faa8502d78f17b 100644 --- a/drivers/bus/mhi/controllers/mhi_qcom.c +++ b/drivers/bus/mhi/controllers/mhi_qcom.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 #include @@ -418,15 +418,15 @@ static int mhi_force_suspend(struct mhi_controller *mhi_cntrl) msleep(delayms); } - if (ret) + if (ret) { + MHI_ERR("Force suspend ret with %d\n", ret); goto exit_force_suspend; + } mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND; ret = mhi_arch_link_suspend(mhi_cntrl); exit_force_suspend: - MHI_LOG("Force suspend ret with %d\n", ret); - mutex_unlock(&mhi_cntrl->pm_mutex); return ret; @@ -576,10 +576,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 */ @@ -634,9 +630,12 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl, */ pm_runtime_get(dev); ret = mhi_force_suspend(mhi_cntrl); - if (!ret) + if (!ret) { + MHI_LOG("Attempt resume after forced suspend\n"); mhi_runtime_resume(dev); + } pm_runtime_put(dev); + mhi_arch_mission_mode_enter(mhi_cntrl); break; default: MHI_ERR("Unhandled cb:0x%x\n", reason); diff --git a/drivers/bus/mhi/controllers/mhi_qcom.h b/drivers/bus/mhi/controllers/mhi_qcom.h index f9c1d8a000d136f8c3bf0d95f31ea59671a16b44..1383bb02cb8b8634d0f5b57a655cb5ece31ce011 100644 --- a/drivers/bus/mhi/controllers/mhi_qcom.h +++ b/drivers/bus/mhi/controllers/mhi_qcom.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_QCOM_ #define _MHI_QCOM_ @@ -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; @@ -71,7 +70,7 @@ void mhi_reg_write_work(struct work_struct *w); #ifdef CONFIG_ARCH_QCOM -int mhi_arch_power_up(struct mhi_controller *mhi_cntrl); +void mhi_arch_mission_mode_enter(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); @@ -98,9 +97,8 @@ 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) { - return 0; } #endif diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index b4e568cf239c66d7db78b1bf41714d60855b196e..8707efc35ffbb6c8d57a38dba922dbd3c80cfb64 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 #include @@ -1018,7 +1018,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; @@ -1101,13 +1101,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 +1393,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 +1414,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) @@ -1516,6 +1522,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); diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index e4c27fd250f28c92d95d99f39d5c0e148066fe6b..7321b0fcd66daaa4eeeda7ec2db020166f9f2a55 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 @@ -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 { @@ -706,6 +706,9 @@ struct mhi_chan { struct completion completion; rwlock_t lock; struct list_head node; + + /* stats */ + u64 mode_change; }; struct tsync_node { @@ -769,8 +772,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); @@ -946,22 +949,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 e943dd7e94d68d3c62b76367909829fd9b7368ef..ecee75555b02a4619b0b3f698dbeb2a443c766df 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.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 #include @@ -333,6 +333,29 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, smp_wmb(); } +static void mhi_recycle_fwd_ev_ring_element(struct mhi_controller *mhi_cntrl, + struct mhi_ring *ring) +{ + dma_addr_t ctxt_wp; + + /* update the WP */ + ring->wp += ring->el_size; + if (ring->wp >= (ring->base + ring->len)) + ring->wp = ring->base; + + /* update the context WP based on the RP to support fast forwarding */ + ctxt_wp = ring->iommu_base + (ring->wp - ring->base); + *ring->ctxt_wp = ctxt_wp; + + /* update the RP */ + ring->rp += ring->el_size; + if (ring->rp >= (ring->base + ring->len)) + ring->rp = ring->base; + + /* visible to other cores */ + smp_wmb(); +} + static bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl, struct mhi_ring *ring) { @@ -537,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); @@ -967,6 +990,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; @@ -1025,7 +1049,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; @@ -1055,12 +1080,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 @@ -1074,14 +1095,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; @@ -1200,6 +1234,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); @@ -1542,7 +1580,7 @@ int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, ev_ring->wp = dev_rp - 1; if (ev_ring->wp < ev_ring->base) ev_ring->wp = ev_ring->base + ev_ring->len - ev_ring->el_size; - mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); + mhi_recycle_fwd_ev_ring_element(mhi_cntrl, ev_ring); read_lock_bh(&mhi_cntrl->pm_lock); if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) @@ -1734,7 +1772,7 @@ irqreturn_t mhi_intvec_handlr(int irq_number, void *dev) 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; } @@ -2161,6 +2199,9 @@ int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d) int i; + if (!mhi_cntrl->mhi_ctxt) + return -ENODEV; + seq_printf(m, "[%llu ns]:\n", sched_clock()); er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt; @@ -2194,6 +2235,9 @@ int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d) struct mhi_chan_ctxt *chan_ctxt; int i; + if (!mhi_cntrl->mhi_ctxt) + return -ENODEV; + seq_printf(m, "[%llu ns]:\n", sched_clock()); mhi_chan = mhi_cntrl->mhi_chan; @@ -2212,12 +2256,14 @@ 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; } } diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 5a2cbd8477dfed76bdba4de7a3589a7b69e8ecfc..bfd15abd5f92088e7c203f58f4f626c8a9333d95 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.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 #include @@ -15,6 +15,8 @@ #include #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 @@ -525,6 +527,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) mhi_timesync_log(mhi_cntrl); + mhi_special_events_pending(mhi_cntrl); + MHI_LOG("Adding new devices\n"); /* add supported devices */ @@ -643,7 +647,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); + flush_work(&mhi_cntrl->special_work); mhi_cntrl->force_m3_done = false; @@ -785,18 +789,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 +809,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 +821,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); } @@ -1171,6 +1176,9 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) write_unlock_irq(&mhi_cntrl->pm_lock); MHI_LOG("Wait for M3 completion\n"); + /* finish reg writes before D3 cold */ + mhi_force_reg_write(mhi_cntrl); + ret = wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->dev_state == MHI_STATE_M3 || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), @@ -1285,6 +1293,9 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) mhi_cntrl->M3_FAST++; write_unlock_irq(&mhi_cntrl->pm_lock); + /* finish reg writes before DRV hand-off to avoid noc err */ + mhi_force_reg_write(mhi_cntrl); + /* now safe to check ctrl event ring */ tasklet_enable(&mhi_cntrl->mhi_event->task); mhi_msi_handlr(0, mhi_cntrl->mhi_event); @@ -1374,11 +1385,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; } @@ -1447,8 +1458,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), diff --git a/drivers/bus/mhi/devices/mhi_netdev.c b/drivers/bus/mhi/devices/mhi_netdev.c index 275e4e2be93ff864ca49e7749eb44dacd76a19c3..cd26e5558abf67ee61318bbb0e1f080216ef03f8 100644 --- a/drivers/bus/mhi/devices/mhi_netdev.c +++ b/drivers/bus/mhi/devices/mhi_netdev.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 #include @@ -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 { @@ -411,6 +406,8 @@ static void mhi_netdev_free_pool(struct mhi_netdev *mhi_netdev) __free_pages(mhi_buf->page, mhi_netdev->order); mhi_netdev->bg_pool_size--; } + + kfree(mhi_netdev->bg_pool); } static int mhi_netdev_alloc_thread(void *data) @@ -449,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, @@ -488,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; } @@ -498,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); @@ -835,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 @@ -943,6 +945,7 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev) unregister_netdev(mhi_netdev->ndev); netif_napi_del(mhi_netdev->napi); free_netdev(mhi_netdev->ndev); + mhi_netdev->ndev = NULL; if (!IS_ERR_OR_NULL(mhi_netdev->dentry)) debugfs_remove_recursive(mhi_netdev->dentry); @@ -975,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; @@ -1082,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); } @@ -1095,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/ti-sysc.c b/drivers/bus/ti-sysc.c index e95b26319cd91076695cacf94737902afe1d255b..b6f63e7620214a7baf02fc491ea7fe118204958f 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 27a82a559ab9439deac2b7b243633014e575ae7b..933268b8d6a548954256a89d18240b3b04718963 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; diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 4385e26475d294fd9b6151bb50280e7eff0d6575..171dff1700708e5ccc57186053b0233e8fe7c507 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 #include @@ -1846,6 +1846,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 && @@ -1859,10 +1861,37 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (rpra && rpra[i].buf.len && ctx->overps[oix]->mstart) { 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) { + 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 +1968,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 +1997,50 @@ 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); + if (ctx->overps[i]->mstart) { + if (map && map->buf) { + if ((buf_page_size(ctx->overps[i]->mend - + ctx->overps[i]->mstart)) == map->size) { + 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[i].buf.pv), - (char *)uint64_to_ptr(rpra[i].buf.pv - + rpra[i].buf.len)); + 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, @@ -2290,10 +2297,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, goto bail; } - 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_debugfs.c b/drivers/char/diag/diag_debugfs.c index fd40fce0cdf7f7d089a4f6ec1552fdc7635e1885..a6b3740e0b4acb2ee7c1f56e9ff4504901f53af8 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. */ #ifdef CONFIG_DEBUG_FS @@ -480,7 +480,6 @@ static ssize_t diag_dbgfs_read_socketinfo(struct file *file, char __user *ubuf, int i = 0; int j = 0; unsigned int buf_size; - unsigned int bytes_remaining = 0; unsigned int bytes_written = 0; unsigned int bytes_in_buffer = 0; struct diag_socket_info *info = NULL; @@ -499,78 +498,75 @@ static ssize_t diag_dbgfs_read_socketinfo(struct file *file, char __user *ubuf, } buf_size = ksize(buf); - bytes_remaining = buf_size; - for (i = 0; i < NUM_TYPES; i++) { - for (j = 0; j < NUM_PERIPHERALS; j++) { - switch (i) { - case TYPE_DATA: - info = &socket_data[j]; - break; - case TYPE_CNTL: - info = &socket_cntl[j]; - break; - case TYPE_DCI: - info = &socket_dci[j]; - break; - case TYPE_CMD: - info = &socket_cmd[j]; - break; - case TYPE_DCI_CMD: - info = &socket_dci_cmd[j]; - break; - default: - return -EINVAL; - } - fwd_ctxt = (struct diagfwd_info *)(info->fwd_ctxt); - - bytes_written = scnprintf(buf+bytes_in_buffer, - bytes_remaining, - "name\t\t:\t%s\n" - "hdl\t\t:\t%pK\n" - "inited\t\t:\t%d\n" - "opened\t\t:\t%d\n" - "diag_state\t:\t%d\n" - "buf_1 busy\t:\t%d\n" - "buf_2 busy\t:\t%d\n" - "flow ctrl count\t:\t%d\n" - "data_ready\t:\t%d\n" - "init pending\t:\t%d\n" - "read pending\t:\t%d\n" - "bytes read\t:\t%lu\n" - "bytes written\t:\t%lu\n" - "fwd inited\t:\t%d\n" - "fwd opened\t:\t%d\n" - "fwd ch_open\t:\t%d\n\n", - info->name, - info->hdl, - info->inited, - atomic_read(&info->opened), - atomic_read(&info->diag_state), - (fwd_ctxt && fwd_ctxt->buf_1) ? - atomic_read(&fwd_ctxt->buf_1->in_busy) : -1, - (fwd_ctxt && fwd_ctxt->buf_2) ? - atomic_read(&fwd_ctxt->buf_2->in_busy) : -1, - atomic_read(&info->flow_cnt), - info->data_ready, - work_pending(&info->init_work), - work_pending(&info->read_work), - (fwd_ctxt) ? fwd_ctxt->read_bytes : 0, - (fwd_ctxt) ? fwd_ctxt->write_bytes : 0, - (fwd_ctxt) ? fwd_ctxt->inited : -1, - (fwd_ctxt) ? - atomic_read(&fwd_ctxt->opened) : -1, - (fwd_ctxt) ? fwd_ctxt->ch_open : -1); - bytes_in_buffer += bytes_written; + i = diag_dbgfs_socketinfo_index; + for (j = 0; j < NUM_PERIPHERALS; j++) { + switch (i) { + case TYPE_DATA: + info = &socket_data[j]; + break; + case TYPE_CNTL: + info = &socket_cntl[j]; + break; + case TYPE_DCI: + info = &socket_dci[j]; + break; + case TYPE_CMD: + info = &socket_cmd[j]; + break; + case TYPE_DCI_CMD: + info = &socket_dci_cmd[j]; + break; + default: + kfree(buf); + return -EINVAL; + } + fwd_ctxt = (struct diagfwd_info *)(info->fwd_ctxt); + + bytes_written = scnprintf(buf+bytes_in_buffer, + buf_size, + "name\t\t:\t%s\n" + "hdl\t\t:\t%pK\n" + "inited\t\t:\t%d\n" + "opened\t\t:\t%d\n" + "diag_state\t:\t%d\n" + "buf_1 busy\t:\t%d\n" + "buf_2 busy\t:\t%d\n" + "flow ctrl count\t:\t%d\n" + "data_ready\t:\t%d\n" + "init pending\t:\t%d\n" + "read pending\t:\t%d\n" + "bytes read\t:\t%lu\n" + "bytes written\t:\t%lu\n" + "fwd inited\t:\t%d\n" + "fwd opened\t:\t%d\n" + "fwd ch_open\t:\t%d\n\n", + info->name, + info->hdl, + info->inited, + atomic_read(&info->opened), + atomic_read(&info->diag_state), + (fwd_ctxt && fwd_ctxt->buf_1) ? + atomic_read(&fwd_ctxt->buf_1->in_busy) : -1, + (fwd_ctxt && fwd_ctxt->buf_2) ? + atomic_read(&fwd_ctxt->buf_2->in_busy) : -1, + atomic_read(&info->flow_cnt), + info->data_ready, + work_pending(&info->init_work), + work_pending(&info->read_work), + (fwd_ctxt) ? fwd_ctxt->read_bytes : 0, + (fwd_ctxt) ? fwd_ctxt->write_bytes : 0, + (fwd_ctxt) ? fwd_ctxt->inited : -1, + (fwd_ctxt) ? + atomic_read(&fwd_ctxt->opened) : -1, + (fwd_ctxt) ? fwd_ctxt->ch_open : -1); - /* Check if there is room to add another table entry */ - bytes_remaining = buf_size - bytes_in_buffer; + bytes_in_buffer += bytes_written; - if (bytes_remaining < bytes_written) - break; - } } - diag_dbgfs_socketinfo_index = i+1; + + diag_dbgfs_socketinfo_index += 1; + *ppos = 0; ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer); @@ -586,7 +582,6 @@ static ssize_t diag_dbgfs_read_rpmsginfo(struct file *file, char __user *ubuf, int i = 0; int j = 0; unsigned int buf_size; - unsigned int bytes_remaining = 0; unsigned int bytes_written = 0; unsigned int bytes_in_buffer = 0; struct diag_rpmsg_info *info = NULL; @@ -603,77 +598,71 @@ static ssize_t diag_dbgfs_read_rpmsginfo(struct file *file, char __user *ubuf, return -ENOMEM; buf_size = ksize(buf); - bytes_remaining = buf_size; - for (i = 0; i < NUM_TYPES; i++) { - for (j = 0; j < NUM_PERIPHERALS; j++) { - switch (i) { - case TYPE_DATA: - info = &rpmsg_data[j]; - break; - case TYPE_CNTL: - info = &rpmsg_cntl[j]; - break; - case TYPE_DCI: - info = &rpmsg_dci[j]; - break; - case TYPE_CMD: - info = &rpmsg_cmd[j]; - break; - case TYPE_DCI_CMD: - info = &rpmsg_dci_cmd[j]; - break; - default: - return -EINVAL; - } - - fwd_ctxt = (struct diagfwd_info *)(info->fwd_ctxt); - - bytes_written = scnprintf(buf+bytes_in_buffer, - bytes_remaining, - "name\t\t:\t%s:\t%s\n" - "hdl\t\t:\t%pK\n" - "inited\t\t:\t%d\n" - "opened\t\t:\t%d\n" - "diag_state\t:\t%d\n" - "buf_1 busy\t:\t%d\n" - "buf_2 busy\t:\t%d\n" - "open pending\t:\t%d\n" - "close pending\t:\t%d\n" - "read pending\t:\t%d\n" - "bytes read\t:\t%lu\n" - "bytes written\t:\t%lu\n" - "fwd inited\t:\t%d\n" - "fwd opened\t:\t%d\n" - "fwd ch_open\t:\t%d\n\n", - info->edge, - info->name, - info->hdl, - info->inited, - atomic_read(&info->opened), - atomic_read(&info->diag_state), - (fwd_ctxt && fwd_ctxt->buf_1) ? - atomic_read(&fwd_ctxt->buf_1->in_busy) : -1, - (fwd_ctxt && fwd_ctxt->buf_2) ? - atomic_read(&fwd_ctxt->buf_2->in_busy) : -1, - work_pending(&info->open_work), - work_pending(&info->close_work), - work_pending(&info->read_work), - (fwd_ctxt) ? fwd_ctxt->read_bytes : 0, - (fwd_ctxt) ? fwd_ctxt->write_bytes : 0, - (fwd_ctxt) ? fwd_ctxt->inited : -1, - (fwd_ctxt) ? - atomic_read(&fwd_ctxt->opened) : -1, - (fwd_ctxt) ? fwd_ctxt->ch_open : -1); - bytes_in_buffer += bytes_written; + i = diag_dbgfs_rpmsginfo_index; + for (j = 0; j < NUM_PERIPHERALS; j++) { + switch (i) { + case TYPE_DATA: + info = &rpmsg_data[j]; + break; + case TYPE_CNTL: + info = &rpmsg_cntl[j]; + break; + case TYPE_DCI: + info = &rpmsg_dci[j]; + break; + case TYPE_CMD: + info = &rpmsg_cmd[j]; + break; + case TYPE_DCI_CMD: + info = &rpmsg_dci_cmd[j]; + break; + default: + kfree(buf); + return -EINVAL; + } - /* Check if there is room to add another table entry */ - bytes_remaining = buf_size - bytes_in_buffer; + fwd_ctxt = (struct diagfwd_info *)(info->fwd_ctxt); + bytes_written = scnprintf(buf+bytes_in_buffer, + buf_size, + "name\t\t:\t%s:\t%s\n" + "hdl\t\t:\t%pK\n" + "inited\t\t:\t%d\n" + "opened\t\t:\t%d\n" + "diag_state\t:\t%d\n" + "buf_1 busy\t:\t%d\n" + "buf_2 busy\t:\t%d\n" + "open pending\t:\t%d\n" + "close pending\t:\t%d\n" + "read pending\t:\t%d\n" + "bytes read\t:\t%lu\n" + "bytes written\t:\t%lu\n" + "fwd inited\t:\t%d\n" + "fwd opened\t:\t%d\n" + "fwd ch_open\t:\t%d\n\n", + info->edge, + info->name, + info->hdl, + info->inited, + atomic_read(&info->opened), + atomic_read(&info->diag_state), + (fwd_ctxt && fwd_ctxt->buf_1) ? + atomic_read(&fwd_ctxt->buf_1->in_busy) : -1, + (fwd_ctxt && fwd_ctxt->buf_2) ? + atomic_read(&fwd_ctxt->buf_2->in_busy) : -1, + work_pending(&info->open_work), + work_pending(&info->close_work), + work_pending(&info->read_work), + (fwd_ctxt) ? fwd_ctxt->read_bytes : 0, + (fwd_ctxt) ? fwd_ctxt->write_bytes : 0, + (fwd_ctxt) ? fwd_ctxt->inited : -1, + (fwd_ctxt) ? + atomic_read(&fwd_ctxt->opened) : -1, + (fwd_ctxt) ? fwd_ctxt->ch_open : -1); - if (bytes_remaining < bytes_written) - break; - } + bytes_in_buffer += bytes_written; } - diag_dbgfs_rpmsginfo_index = i+1; + diag_dbgfs_rpmsginfo_index += 1; + *ppos = 0; ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer); diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 968772aecf61aff3751bdc3e09e0f2850ae36a55..088e449174d650c62504423d6d09e00132bdfaf9 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.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 @@ -1367,7 +1367,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_peripheral(DIAG_LOCAL_PROC, APPS_DATA); ret_val = diag_save_user_msg_mask(info); - if (ret_val) + if (ret_val < 0) pr_err("diag: unable to save msg mask to update userspace clients err:%d\n", ret_val); mutex_unlock(&driver->md_session_lock); @@ -2520,8 +2520,7 @@ static int __diag_mask_init(struct diag_mask_info *mask_info, int mask_len, return -ENOMEM; } kmemleak_not_leak(mask_info->update_buf); - mask_info->update_buf_client = kzalloc(MAX_USERSPACE_BUF_SIZ, - GFP_KERNEL); + mask_info->update_buf_client = vzalloc(MAX_USERSPACE_BUF_SIZ); if (!mask_info->update_buf_client) { kfree(mask_info->update_buf); mask_info->update_buf = NULL; @@ -2529,7 +2528,6 @@ static int __diag_mask_init(struct diag_mask_info *mask_info, int mask_len, mask_info->ptr = NULL; return -ENOMEM; } - kmemleak_not_leak(mask_info->update_buf_client); mask_info->update_buf_client_len = 0; } return 0; @@ -2545,7 +2543,7 @@ static void __diag_mask_exit(struct diag_mask_info *mask_info) mask_info->ptr = NULL; kfree(mask_info->update_buf); mask_info->update_buf = NULL; - kfree(mask_info->update_buf_client); + vfree(mask_info->update_buf_client); mask_info->update_buf_client = NULL; mutex_unlock(&mask_info->lock); } @@ -2895,7 +2893,7 @@ static void diag_msg_mask_exit(void) ms_ptr = ms_ptr->next; } msg_mask.ms_ptr = NULL; - kfree(msg_mask.update_buf_client); + vfree(msg_mask.update_buf_client); msg_mask.update_buf_client = NULL; mutex_unlock(&driver->msg_mask_lock); } @@ -2991,7 +2989,7 @@ static void diag_log_mask_exit(void) ms_ptr = ms_ptr->next; } log_mask.ms_ptr = NULL; - kfree(log_mask.update_buf_client); + vfree(log_mask.update_buf_client); log_mask.update_buf_client = NULL; } @@ -3088,6 +3086,7 @@ static void diag_event_mask_exit(void) kfree(event_mask.ptr); kfree(event_mask.update_buf); + vfree(event_mask.update_buf_client); ms_ptr = (struct diag_multisim_masks *)(event_mask.ms_ptr); while (ms_ptr) { kfree(ms_ptr->sub_ptr); diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index aa77fb78ad0be1200a4308fe530ebd9a338b7fa3..6177f2b34b4b46b0cdcb11c95cb384f1956e2f19 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.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 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -155,6 +156,9 @@ void diag_md_close_device(int id) * When we close the Memory device mode, make sure we flush the * internal buffers in the table so that there are no stale * entries. + * + * Give Write_done notifications to buffers with packets + * indicated valid length. */ spin_lock_irqsave(&ch->lock, flags); for (j = 0; j < ch->num_tbl_entries; j++) { @@ -173,6 +177,35 @@ void diag_md_close_device(int id) diag_ws_reset(DIAG_WS_MUX); } + +void diag_md_clear_tbl_entries(int id) +{ + int j; + unsigned long flags; + struct diag_md_info *ch = NULL; + struct diag_buf_tbl_t *entry = NULL; + + ch = &diag_md[id]; + if (!ch || !ch->md_info_inited) + return; + + /* + * When we close the Memory device mode, make sure we flush the + * internal buffers in the table so that there are no stale + * entries. + */ + spin_lock_irqsave(&ch->lock, flags); + for (j = 0; j < ch->num_tbl_entries; j++) { + entry = &ch->tbl[j]; + entry->buf = NULL; + entry->len = 0; + entry->ctx = 0; + } + spin_unlock_irqrestore(&ch->lock, flags); + + diag_ws_reset(DIAG_WS_MUX); +} + int diag_md_write(int id, unsigned char *buf, int len, int ctx) { int i, peripheral, pid = 0; @@ -298,29 +331,37 @@ 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) continue; for (j = 0; j < ch->num_tbl_entries && !err; j++) { + spin_lock_irqsave(&ch->lock, flags); entry = &ch->tbl[j]; - if (entry->len <= 0 || entry->buf == NULL) + if (entry->len <= 0 || entry->buf == NULL) { + spin_unlock_irqrestore(&ch->lock, flags); continue; - + } peripheral = diag_md_get_peripheral(entry->ctx); - if (peripheral < 0) + if (peripheral < 0) { + spin_unlock_irqrestore(&ch->lock, flags); goto drop_data; + } + spin_unlock_irqrestore(&ch->lock, flags); session_info = diag_md_session_get_peripheral(i, peripheral); 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) { @@ -349,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 @@ -395,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/diag_memorydevice.h b/drivers/char/diag/diag_memorydevice.h index e8d4a51ef1fd602f2543b22c9965757990470eac..f7bc0f278f6be369fbb86a7ace600022ac1b9668 100644 --- a/drivers/char/diag/diag_memorydevice.h +++ b/drivers/char/diag/diag_memorydevice.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2014-2015, 2017-2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017-2018, 2020 The Linux Foundation. All rights reserved. */ #ifndef DIAG_MEMORYDEVICE_H @@ -33,6 +33,7 @@ void diag_md_open_all(void); void diag_md_close_all(void); void diag_md_open_device(int id); void diag_md_close_device(int id); +void diag_md_clear_tbl_entries(int id); int diag_md_register(int id, int ctx, struct diag_mux_ops *ops); int diag_md_close_peripheral(int id, uint8_t peripheral); int diag_md_write(int id, unsigned char *buf, int len, int ctx); diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c index 6606c5e414ac362ed4aa95260c44bc5c9cb438c0..9c24e5a7180ddf1414f416a87af9148a48ce7e44 100644 --- a/drivers/char/diag/diag_mux.c +++ b/drivers/char/diag/diag_mux.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 @@ -33,6 +33,7 @@ static struct diag_logger_ops usb_log_ops = { .queue_read = diag_usb_queue_read, .open_device = diag_usb_connect_device, .close_device = diag_usb_disconnect_device, + .clear_tbl_entries = NULL, .write = diag_usb_write, .close_peripheral = NULL }; @@ -42,6 +43,7 @@ static struct diag_logger_ops md_log_ops = { .close = diag_md_close_all, .open_device = diag_md_open_device, .close_device = diag_md_close_device, + .clear_tbl_entries = diag_md_clear_tbl_entries, .queue_read = NULL, .write = diag_md_write, .close_peripheral = diag_md_close_peripheral, @@ -215,6 +217,20 @@ int diag_mux_close_peripheral(int proc, uint8_t peripheral) return 0; } +void diag_mux_close_device(int proc) +{ + struct diag_logger_t *logger = NULL; + + if (!diag_mux) + return; + + logger = diag_mux->logger[proc]; + + if (logger && logger->log_ops && logger->log_ops->clear_tbl_entries) + logger->log_ops->clear_tbl_entries(proc); + +} + int diag_mux_switch_logging(int proc, int *req_mode, int *peripheral_mask) { unsigned int new_mask = 0; diff --git a/drivers/char/diag/diag_mux.h b/drivers/char/diag/diag_mux.h index 12886423e9072a2bb9e2d0ae9448c953c15c3dfb..378d4b9617f7ffbaa36decbc04970106071276df 100644 --- a/drivers/char/diag/diag_mux.h +++ b/drivers/char/diag/diag_mux.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. */ #ifndef DIAG_MUX_H #define DIAG_MUX_H @@ -43,6 +43,7 @@ struct diag_logger_ops { void (*close)(void); void (*open_device)(int id); void (*close_device)(int id); + void (*clear_tbl_entries)(int id); int (*queue_read)(int id); int (*write)(int id, unsigned char *buf, int len, int ctx); int (*close_peripheral)(int id, uint8_t peripheral); @@ -62,6 +63,7 @@ int diag_mux_register(int proc, int ctx, struct diag_mux_ops *ops); int diag_mux_queue_read(int proc); int diag_mux_write(int proc, unsigned char *buf, int len, int ctx); int diag_mux_close_peripheral(int proc, uint8_t peripheral); +void diag_mux_close_device(int proc); int diag_mux_open_all(struct diag_logger_t *logger); int diag_mux_close_all(void); int diag_mux_switch_logging(int proc, int *new_mode, int *peripheral_mask); diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c index 772456e012149ed0061ccd7883f63bd50ea7d2a4..a1ae3ab953d10fe64efce3854420d086176b87c2 100644 --- a/drivers/char/diag/diag_usb.c +++ b/drivers/char/diag/diag_usb.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 @@ -232,15 +232,23 @@ static void usb_event_work_fn(struct work_struct *work) struct diag_usb_info *ch = container_of(work, struct diag_usb_info, event_work); struct diag_usb_event_q *entry = NULL; + unsigned long flags; if (!ch) return; + spin_lock_irqsave(&ch->event_lock, flags); entry = list_first_entry(&(ch->event_q), struct diag_usb_event_q, link); - if (!entry) + if (!entry) { + spin_unlock_irqrestore(&ch->event_lock, flags); return; + } switch (entry->data) { case USB_DIAG_CONNECT: + + diag_usb_event_remove(entry); + spin_unlock_irqrestore(&ch->event_lock, flags); + wait_event_interruptible(ch->wait_q, ch->enabled > 0); ch->max_size = usb_diag_request_size(ch->hdl); atomic_set(&ch->connected, 1); @@ -252,10 +260,14 @@ static void usb_event_work_fn(struct work_struct *work) usb_connect(ch); break; case USB_DIAG_DISCONNECT: + + diag_usb_event_remove(entry); + spin_unlock_irqrestore(&ch->event_lock, flags); + atomic_set(&ch->connected, 0); DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag: USB channel %s: Cleared connected(%d) status\n", - ch->name, atomic_read(&ch->connected)); + "diag: USB channel %s: Cleared connected(%d) status\n", + ch->name, atomic_read(&ch->connected)); if (!atomic_read(&ch->connected) && driver->usb_connected && @@ -264,8 +276,11 @@ static void usb_event_work_fn(struct work_struct *work) usb_disconnect(ch); break; + default: + spin_unlock_irqrestore(&ch->event_lock, flags); + break; } - diag_usb_event_remove(entry); + if (!list_empty(&ch->event_q)) queue_work(ch->usb_wq, &(ch->event_work)); @@ -363,8 +378,7 @@ static void diag_usb_write_done(struct diag_usb_info *ch, spin_unlock_irqrestore(&ch->write_lock, flags); return; } - DIAG_LOG(DIAG_DEBUG_MUX, "full write_done, ctxt: %d\n", - ctxt); + DIAG_LOG(DIAG_DEBUG_MUX, "full write_done\n"); list_del(&entry->track); ctxt = entry->ctxt; buf = entry->buf; @@ -398,7 +412,9 @@ static void diag_usb_notifier(void *priv, unsigned int event, DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: USB channel %s: Received Connect event\n", usb_info->name); + spin_lock_irqsave(&usb_info->event_lock, flags); diag_usb_event_add(usb_info, USB_DIAG_CONNECT); + spin_unlock_irqrestore(&usb_info->event_lock, flags); queue_work(usb_info->usb_wq, &usb_info->event_work); break; @@ -406,7 +422,9 @@ static void diag_usb_notifier(void *priv, unsigned int event, DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: USB channel %s: Received Disconnect event\n", usb_info->name); + spin_lock_irqsave(&usb_info->event_lock, flags); diag_usb_event_add(usb_info, USB_DIAG_DISCONNECT); + spin_unlock_irqrestore(&usb_info->event_lock, flags); queue_work(usb_info->usb_wq, &usb_info->event_work); break; @@ -680,6 +698,7 @@ int diag_usb_register(int id, int ctxt, struct diag_mux_ops *ops) ch->ctxt = ctxt; spin_lock_init(&ch->lock); spin_lock_init(&ch->write_lock); + spin_lock_init(&ch->event_lock); ch->read_buf = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL); if (!ch->read_buf) goto err; diff --git a/drivers/char/diag/diag_usb.h b/drivers/char/diag/diag_usb.h index 35242bb6e8b0689d46086155e0dc4baebeacf291..c074186fb4e598cf9eae3287e51fd5edf6595cf7 100644 --- a/drivers/char/diag/diag_usb.h +++ b/drivers/char/diag/diag_usb.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. */ #ifndef DIAGUSB_H @@ -58,6 +58,7 @@ struct diag_usb_info { unsigned long write_cnt; spinlock_t lock; spinlock_t write_lock; + spinlock_t event_lock; struct usb_diag_ch *hdl; struct diag_mux_ops *ops; unsigned char *read_buf; diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 110e916449eab29d267d685c8dee85278eab0e1b..0516429deebaded34f7446a37c9bdf5bd6a6738d 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 22377365bd7c8045ca0dbe5be7813cdfba427a3f..771c91db3fd8bad6caef1019ece73ff0efe900ff 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 @@ -14,6 +14,7 @@ #include #include #include +#include #include #ifdef CONFIG_DIAG_OVER_USB #include @@ -3085,7 +3086,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 +3117,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 +3176,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 +3243,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 +3261,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); diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c index 4ef4776ca9b16a9160b30f73e3e043905b9c059d..d4af0e6eab8c23b6a05b0ee0332074ea1372d258 100644 --- a/drivers/char/diag/diagfwd_bridge.c +++ b/drivers/char/diag/diagfwd_bridge.c @@ -1,5 +1,5 @@ // 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 @@ -205,6 +205,12 @@ int diag_remote_dev_open(int id) void diag_remote_dev_close(int id) { + + if (id < 0 || id >= NUM_REMOTE_DEV) + return; + + diag_mux_close_device(BRIDGE_TO_MUX(id)); + if (bridge_info[id].type == DIAG_DATA_TYPE) diag_notify_md_client(BRIDGE_TO_MUX(id), 0, DIAG_STATUS_CLOSED); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index e9b6ac61fb7f64953e988c130d53f518ca3e7ab0..49882524263421bdefea2ec69cd304eaf98a02c9 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/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 042860d97b1560a1943b9c28e57d48c1e77536b8..37b338a76ba4670a583260555573f27937fac1ae 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 e2c143861b1e5aa7e4ee7899477750acb7fff358..28dbd5529188a5a316337d733daa4f3a5d0dd818 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 3fb297b5fb17685eb1fc1b2a2f51db9b21119159..84c17f936c09c44c5f2a6883dd5ae3a6e1240aa3 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1365,7 +1365,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); diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c index 638f4ab88f445a51840a73203f739946873941e0..75583612ab10551367cd74d727a0cbda958d0429 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 f54ca6869ed2c3f62d3c2d31daea3c5044642b9f..022e03634ce2ac0a987bace4eeaff5f8f6d45fed 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 8c4dd1a3bb6ab562136e32bad330fd1da2fdb1c5..767740d758dd6f916a99719ad447a1fb806db763 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 1ae77b41050abb8404046261718cf6e8a6386da5..51faafd310a2e41c9ed2a42a823ca4fe5f31a59f 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 3acf4fd4e5a5397f4b6fa440ce46ee24bf7e6036..ef381caf5f43eaa06be81a1486a672e1f36681a5 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 c55f6aeb4227a43ab34f67048a3f6a23835bc7e8..b353a5e5f8b1ceb1becc50247de0b2cb9edae643 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 a84c5573cabeae1ede83c2e597e8d310bc98d307..ed344eb717cc440dd1c5cbbd2c4daccba3e7691e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/ 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 da7bafcfbe706cef631e86f3df8dc86536f5900c..b3eaf654fac980ed01cee5e467ad089cb561f066 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 113152425a95dc102d1affc016706a6866aeefd8..ea23002be4de15c7265ee209b0683261ec7d6903 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 2f97a843d6d6bcbd7a6fe7f90849dabfbbe54db6..90988e7a5b47fb28e1cd07734135bcff63c0e513 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 ab6ecefc49ad85424a02dda7b3d3342d76598884..43ba2a8b03faf1bceeddbd2870deb4936b2fb451 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-stm32mp1.c b/drivers/clk/clk-stm32mp1.c index a907555b2a3d8f5c7882e5be1e080eed5fda1767..bf3b6a4c78d0c06390fffe3f8b2c49f8afba9387 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/clk.c b/drivers/clk/clk.c index 80ca7b19ff5d810735670955f0a04ed87ee1f9a6..be04521586bcfd59b746e906162578f467731edd 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2010-2011 Canonical Ltd * Copyright (C) 2011-2012 Linaro Ltd - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-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 as @@ -3503,45 +3503,6 @@ do { \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) -/* - * clock_debug_print_enabled_debug_suspend() - Print names of enabled clocks - * during suspend. - */ -static void clock_debug_print_enabled_debug_suspend(struct seq_file *s) -{ - struct clk_core *core; - int cnt = 0; - - if (!mutex_trylock(&clk_debug_lock)) - return; - - clock_debug_output(s, 0, "Enabled clocks:\n"); - - hlist_for_each_entry(core, &clk_debug_list, debug_node) { - if (!core->prepare_count) - continue; - - if (core->vdd_class) - clock_debug_output(s, 0, " %s:%u:%u [%ld, %d]", - core->name, core->prepare_count, - core->enable_count, core->rate, - clk_find_vdd_level(core, core->rate)); - - else - clock_debug_output(s, 0, " %s:%u:%u [%ld]", - core->name, core->prepare_count, - core->enable_count, core->rate); - cnt++; - } - - mutex_unlock(&clk_debug_lock); - - if (cnt) - clock_debug_output(s, 0, "Enabled clock count: %d\n", cnt); - else - clock_debug_output(s, 0, "No clocks enabled.\n"); -} - static int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) { char *start = ""; @@ -3552,7 +3513,7 @@ static int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) clk = c->hw->clk; - clock_debug_output(s, 0, "\t"); + clock_debug_output(s, 0, " "); do { if (clk->core->vdd_class) @@ -3584,9 +3545,10 @@ static void clock_debug_print_enabled_clocks(struct seq_file *s) struct clk_core *core; int cnt = 0; - clock_debug_output(s, 0, "Enabled clocks:\n"); + if (!mutex_trylock(&clk_debug_lock)) + return; - mutex_lock(&clk_debug_lock); + clock_debug_output(s, 0, "Enabled clocks:\n"); hlist_for_each_entry(core, &clk_debug_list, debug_node) cnt += clock_debug_print_clock(core, s); @@ -3911,7 +3873,7 @@ void clock_debug_print_enabled(void) if (likely(!debug_suspend)) return; - clock_debug_print_enabled_debug_suspend(NULL); + clock_debug_print_enabled_clocks(NULL); } EXPORT_SYMBOL_GPL(clock_debug_print_enabled); diff --git a/drivers/clk/imgtec/clk-boston.c b/drivers/clk/imgtec/clk-boston.c index f5d54a64d33c53117e6e637e4007e50877f51624..dddda45127a809288dc8c4c5bf4c7ba4acbdc624 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 7e9f0176578a6d09e2170105943d0c6505602f69..b04927d06cd1033924483ff33318d1e82f439f98 100644 --- a/drivers/clk/keystone/Kconfig +++ b/drivers/clk/keystone/Kconfig @@ -7,7 +7,7 @@ config COMMON_CLK_KEYSTONE 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 16e56772d280ba29d56ae18ec90e626a77c28906..6c7eaa21e662b8343e6fc5d6269fbd6fb3460215 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 92f7e32770c6a9905c79b2a84f2075c0dcd2be05..a8aecef1ba89ac1589fb248ffa120e24d8de7e6a 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 4d4f6d842c31ce2fc1ee0fafcf117d152ad7b198..b039909e03cf855e53874f6d871744b1dc715377 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 9d79ff857d83ef915fc8e4bb3ef04c8d56f7dee0..e90af556ff90face1ba236755efc9880dbfaaa8a 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 0fc75c39595708f0d5c6296fcbeea4e3d61f5731..d083b860f08333ad1caf8082664efdeaf1e0099d 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/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 1def6863ee76cd6373ef294ba083225f856ff890..0d5ed47a93fb084b0feeeeccdf459531071cd40e 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -442,6 +442,33 @@ config SM_DEBUGCC_BENGAL BENGAL devices. Say Y if you want to support the clock measurement functionality. +config SDM_CAMCC_LAGOON + tristate "LAGOON Camera Clock Controller" + select SDM_GCC_LAGOON + help + Support for the camera clock controller on Qualcomm Technologies, Inc. + LAGOON devices. + 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 @@ -452,6 +479,24 @@ config SDM_GCC_LAGOON Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, UFS, SD/eMMC, etc. +config SDM_GPUCC_LAGOON + tristate "LAGOON Graphics Clock Controller" + select SDM_GCC_LAGOON + help + Support for the graphics clock controller on Qualcomm Technologies, Inc. + LAGOON devices. + 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 3dc03c5d46b7e062a813fe9ef3b65e6d352b5914..0168829ad32f3c3ca3283edce729b2a056916291 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -49,9 +49,14 @@ obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o 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-lagoon.c b/drivers/clk/qcom/camcc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..7763e482d96ebcd7e97457b2082ab3ea1ed7a4a1 --- /dev/null +++ b/drivers/clk/qcom/camcc-lagoon.c @@ -0,0 +1,2066 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "common.h" +#include "vdd-level-lito.h" + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_mx, VDD_NUM, 1, vdd_corner); + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL1_OUT_MAIN, + P_CAM_CC_PLL2_OUT_EARLY, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_MAIN, + P_CORE_BI_PLL_TEST_SE, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_0[] = { + "bi_tcxo", + "cam_cc_pll0_out_even", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_EVEN, 3 }, + { P_CAM_CC_PLL2_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_1[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll1_out_even", + "cam_cc_pll2_out_main", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_2[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll3", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EARLY, 3 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_3[] = { + "bi_tcxo", + "cam_cc_pll2_out_early", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_EVEN, 3 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_4[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll1_out_even", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_EVEN, 3 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_5[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll1_out_even", + "cam_cc_pll3", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL2_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_6[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll2_out_main", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_MAIN, 2 }, + { P_CAM_CC_PLL2_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_7[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll1", + "cam_cc_pll2_out_main", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_MAIN, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_8[] = { + "bi_tcxo", + "cam_cc_pll0", + "cam_cc_pll1", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_9[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_9[] = { + "bi_tcxo", + "cam_cc_pll2_out_main", + "core_bi_pll_test_se", +}; + +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +/* 600MHz configuration */ +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x1F, + .alpha = 0x4000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002067, + .test_ctl_val = 0x40000000, + .test_ctl_hi_val = 0x00000000, + .user_ctl_val = 0x00000101, + .user_ctl_hi_val = 0x00004805, +}; + +static struct clk_alpha_pll cam_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 = "cam_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 const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll0_out_even", + .parent_names = (const char *[]){ "cam_cc_pll0" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +/* 808MHz configuration */ +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x2A, + .alpha = 0x1555, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002067, + .test_ctl_val = 0x40000000, + .test_ctl_hi_val = 0x00000000, + .user_ctl_val = 0x00000101, + .user_ctl_hi_val = 0x00004805, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .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 = "cam_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}, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll1_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll1_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll1_out_even", + .parent_names = (const char *[]){ "cam_cc_pll1" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +/* 1920MHz configuration */ +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x64, + .alpha = 0x0, + .post_div_val = 0x3 << 8, + .post_div_mask = 0x3 << 8, + .aux_output_mask = BIT(1), + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), + .config_ctl_val = 0x20000800, + .config_ctl_hi_val = 0x400003d2, + .test_ctl_val = 0x04000400, + .test_ctl_hi_val = 0x00004000, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_AGERA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_agera_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_MIN] = 1200000000, + [VDD_LOWER] = 1800000000, + [VDD_LOW] = 2400000000, + [VDD_NOMINAL] = 3000000000, + [VDD_HIGH] = 3300000000}, + }, + }, +}; + +static struct clk_fixed_factor cam_cc_pll2_out_early = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2_out_early", + .parent_names = (const char *[]){ "cam_cc_pll2" }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll2_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll2_out_main = { + .offset = 0x2000, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll2_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll2_out_main), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_AGERA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2_out_main", + .parent_names = (const char *[]){ "cam_cc_pll2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_ops, + }, +}; + +/* 384MHz configuration */ +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x14, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002067, + .test_ctl_val = 0x40000000, + .test_ctl_hi_val = 0x00000000, + .user_ctl_val = 0x00000001, + .user_ctl_hi_val = 0x00004805, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .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 = "cam_cc_pll3", + .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 const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x6010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 200000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0xf004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_0_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 37500000, + [VDD_LOW] = 50000000, + [VDD_NOMINAL] = 100000000}, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0x10004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_1_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 37500000, + [VDD_LOW] = 50000000, + [VDD_NOMINAL] = 100000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(150000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_MAIN, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x9064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_cphy_rx_clk_src", + .parent_names = cam_cc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000, + [VDD_LOW] = 384000000, + [VDD_LOW_L1] = 400000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x5004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi0phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000}, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x5028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi1phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000}, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x504c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi2phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000}, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x5070, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi3phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x603c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_fast_ahb_clk_src", + .parent_names = cam_cc_parent_names_4, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 100000000, + [VDD_LOW] = 200000000, + [VDD_LOW_L1] = 300000000, + [VDD_NOMINAL] = 404000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(240000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_MAIN, 1, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0xe014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_clk_src", + .parent_names = cam_cc_parent_names_5, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 384000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 600000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(240000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x9010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_clk_src", + .parent_names = cam_cc_parent_names_1, + .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] = 320000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_0_csid_clk_src = { + .cmd_rcgr = 0x903c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_csid_clk_src", + .parent_names = cam_cc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000, + [VDD_LOW] = 384000000, + [VDD_LOW_L1] = 400000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_clk_src = { + .cmd_rcgr = 0xa010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_clk_src", + .parent_names = cam_cc_parent_names_1, + .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] = 320000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_csid_clk_src = { + .cmd_rcgr = 0xa034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_csid_clk_src", + .parent_names = cam_cc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000, + [VDD_LOW] = 384000000, + [VDD_LOW_L1] = 400000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_2_clk_src = { + .cmd_rcgr = 0xb00c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 320000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_2_csid_clk_src = { + .cmd_rcgr = 0xb030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_csid_clk_src", + .parent_names = cam_cc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000, + [VDD_LOW] = 384000000, + [VDD_LOW_L1] = 400000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_lite_clk_src[] = { + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 1.5, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0xc004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_6, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_clk_src", + .parent_names = cam_cc_parent_names_6, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 320000000, + [VDD_LOW] = 400000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0xc024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_names = cam_cc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 300000000, + [VDD_LOW] = 384000000, + [VDD_LOW_L1] = 400000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_0_clk_src[] = { + F(240000000, P_CAM_CC_PLL2_OUT_MAIN, 2, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_MAIN, 2, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_MAIN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_0_clk_src = { + .cmd_rcgr = 0x7010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_7, + .freq_tbl = ftbl_cam_cc_ipe_0_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_clk_src", + .parent_names = cam_cc_parent_names_7, + .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] = 240000000, + [VDD_LOW] = 320000000, + [VDD_LOW_L1] = 404000000, + [VDD_NOMINAL] = 538666667, + [VDD_HIGH] = 600000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_jpeg_clk_src[] = { + F(66666667, P_CAM_CC_PLL0_OUT_MAIN, 9, 0, 0), + F(133333333, P_CAM_CC_PLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0xd004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_jpeg_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_jpeg_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 200000000, + [VDD_LOW] = 404000000, + [VDD_LOW_L1] = 480000000, + [VDD_NOMINAL] = 600000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = { + F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(269333333, P_CAM_CC_PLL1_OUT_MAIN, 3, 0, 0), + F(323200000, P_CAM_CC_PLL1_OUT_MAIN, 2.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_lrme_clk_src = { + .cmd_rcgr = 0x11004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_8, + .freq_tbl = ftbl_cam_cc_lrme_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_lrme_clk_src", + .parent_names = cam_cc_parent_names_8, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 200000000, + [VDD_LOW] = 269333333, + [VDD_LOW_L1] = 323200000, + [VDD_NOMINAL] = 404000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_CAM_CC_PLL2_OUT_EARLY, 1, 1, 50), + F(24000000, P_CAM_CC_PLL2_OUT_EARLY, 10, 1, 4), + F(64000000, P_CAM_CC_PLL2_OUT_EARLY, 15, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk0_clk_src", + .parent_names = cam_cc_parent_names_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 64000000}, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x4024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk1_clk_src", + .parent_names = cam_cc_parent_names_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 64000000}, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x4044, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk2_clk_src", + .parent_names = cam_cc_parent_names_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 64000000}, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x4064, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk3_clk_src", + .parent_names = cam_cc_parent_names_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 64000000}, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x4084, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk4_clk_src", + .parent_names = cam_cc_parent_names_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_mx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 64000000}, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(80000000, P_CAM_CC_PLL2_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x6058, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_9, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_slow_ahb_clk_src", + .parent_names = cam_cc_parent_names_9, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOWER] = 80000000}, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x6070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6070, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_areg_clk = { + .halt_reg = 0x6054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_areg_clk", + .parent_names = (const char *[]){ + "cam_cc_fast_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_axi_clk = { + .halt_reg = 0x6038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x6028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_clk", + .parent_names = (const char *[]){ + "cam_cc_bps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_clk = { + .halt_reg = 0x13004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_camnoc_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0xf01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf01c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_0_clk", + .parent_names = (const char *[]){ + "cam_cc_cci_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0x1001c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1001c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_1_clk", + .parent_names = (const char *[]){ + "cam_cc_cci_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0x14010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x14010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_core_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0x12004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_cpas_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x501c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi0phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi0phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x5040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi1phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi1phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x5064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi2phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi2phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x5088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi3phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi3phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x5020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy0_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x5044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy1_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x5068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5068, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy2_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x508c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x508c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy3_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0xe02c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xe02c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_clk", + .parent_names = (const char *[]){ + "cam_cc_icp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ts_clk = { + .halt_reg = 0xe00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xe00c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_ts_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_axi_clk = { + .halt_reg = 0x9080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x9028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_cphy_rx_clk = { + .halt_reg = 0x907c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x907c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_csid_clk = { + .halt_reg = 0x9054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x9038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_dsp_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_axi_clk = { + .halt_reg = 0xa058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_clk = { + .halt_reg = 0xa028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_cphy_rx_clk = { + .halt_reg = 0xa054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_csid_clk = { + .halt_reg = 0xa04c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa04c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_dsp_clk = { + .halt_reg = 0xa030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_dsp_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_axi_clk = { + .halt_reg = 0xb054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_clk = { + .halt_reg = 0xb024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_cphy_rx_clk = { + .halt_reg = 0xb050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_csid_clk = { + .halt_reg = 0xb048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_2_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_dsp_clk = { + .halt_reg = 0xb02c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb02c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_2_dsp_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0xc01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc01c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_lite_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0xc044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0xc03c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc03c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_lite_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_ahb_clk = { + .halt_reg = 0x7040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_areg_clk = { + .halt_reg = 0x703c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x703c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_areg_clk", + .parent_names = (const char *[]){ + "cam_cc_fast_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_axi_clk = { + .halt_reg = 0x7038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_clk = { + .halt_reg = 0x7028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_clk", + .parent_names = (const char *[]){ + "cam_cc_ipe_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0xd01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xd01c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_jpeg_clk", + .parent_names = (const char *[]){ + "cam_cc_jpeg_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_lrme_clk = { + .halt_reg = 0x1101c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1101c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_lrme_clk", + .parent_names = (const char *[]){ + "cam_cc_lrme_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x401c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x401c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk0_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x403c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x403c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk1_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x405c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x405c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk2_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x407c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x407c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk3_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x409c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x409c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk4_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk4_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_soc_ahb_clk = { + .halt_reg = 0x1400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1400c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_soc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sys_tmr_clk = { + .halt_reg = 0xe034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xe034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_sys_tmr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +struct clk_hw *cam_cc_lagoon_hws[] = { + [CAM_CC_PLL2_OUT_EARLY] = &cam_cc_pll2_out_early.hw, +}; + +static struct clk_regmap *cam_cc_lagoon_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr, + [CAM_CC_BPS_AXI_CLK] = &cam_cc_bps_axi_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_CAMNOC_AXI_CLK] = &cam_cc_camnoc_axi_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_ICP_TS_CLK] = &cam_cc_icp_ts_clk.clkr, + [CAM_CC_IFE_0_AXI_CLK] = &cam_cc_ife_0_axi_clk.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_CPHY_RX_CLK] = &cam_cc_ife_0_cphy_rx_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK] = &cam_cc_ife_0_csid_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK_SRC] = &cam_cc_ife_0_csid_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_1_AXI_CLK] = &cam_cc_ife_1_axi_clk.clkr, + [CAM_CC_IFE_1_CLK] = &cam_cc_ife_1_clk.clkr, + [CAM_CC_IFE_1_CLK_SRC] = &cam_cc_ife_1_clk_src.clkr, + [CAM_CC_IFE_1_CPHY_RX_CLK] = &cam_cc_ife_1_cphy_rx_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK] = &cam_cc_ife_1_csid_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK_SRC] = &cam_cc_ife_1_csid_clk_src.clkr, + [CAM_CC_IFE_1_DSP_CLK] = &cam_cc_ife_1_dsp_clk.clkr, + [CAM_CC_IFE_2_AXI_CLK] = &cam_cc_ife_2_axi_clk.clkr, + [CAM_CC_IFE_2_CLK] = &cam_cc_ife_2_clk.clkr, + [CAM_CC_IFE_2_CLK_SRC] = &cam_cc_ife_2_clk_src.clkr, + [CAM_CC_IFE_2_CPHY_RX_CLK] = &cam_cc_ife_2_cphy_rx_clk.clkr, + [CAM_CC_IFE_2_CSID_CLK] = &cam_cc_ife_2_csid_clk.clkr, + [CAM_CC_IFE_2_CSID_CLK_SRC] = &cam_cc_ife_2_csid_clk_src.clkr, + [CAM_CC_IFE_2_DSP_CLK] = &cam_cc_ife_2_dsp_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_0_AHB_CLK] = &cam_cc_ipe_0_ahb_clk.clkr, + [CAM_CC_IPE_0_AREG_CLK] = &cam_cc_ipe_0_areg_clk.clkr, + [CAM_CC_IPE_0_AXI_CLK] = &cam_cc_ipe_0_axi_clk.clkr, + [CAM_CC_IPE_0_CLK] = &cam_cc_ipe_0_clk.clkr, + [CAM_CC_IPE_0_CLK_SRC] = &cam_cc_ipe_0_clk_src.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_LRME_CLK] = &cam_cc_lrme_clk.clkr, + [CAM_CC_LRME_CLK_SRC] = &cam_cc_lrme_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL2_OUT_MAIN] = &cam_cc_pll2_out_main.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_SOC_AHB_CLK] = &cam_cc_soc_ahb_clk.clkr, + [CAM_CC_SYS_TMR_CLK] = &cam_cc_sys_tmr_clk.clkr, +}; + +static const struct regmap_config cam_cc_lagoon_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x15700, + .fast_io = true, +}; + +static const struct qcom_cc_desc cam_cc_lagoon_desc = { + .config = &cam_cc_lagoon_regmap_config, + .hwclks = cam_cc_lagoon_hws, + .num_hwclks = ARRAY_SIZE(cam_cc_lagoon_hws), + .clks = cam_cc_lagoon_clocks, + .num_clks = ARRAY_SIZE(cam_cc_lagoon_clocks), +}; + +static const struct of_device_id cam_cc_lagoon_match_table[] = { + { .compatible = "qcom,lagoon-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_lagoon_match_table); + +static int cam_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]); + } + + vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx"); + if (IS_ERR(vdd_mx.regulator[0])) { + if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "Unable to get vdd_mx regulator\n"); + return PTR_ERR(vdd_mx.regulator[0]); + } + + regmap = qcom_cc_map(pdev, &cam_cc_lagoon_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_fabia_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); + clk_fabia_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); + clk_agera_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); + clk_fabia_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); + + ret = qcom_cc_really_probe(pdev, &cam_cc_lagoon_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register CAM CC clocks\n"); + return ret; + } + + dev_info(&pdev->dev, "Registered CAM CC clocks\n"); + + return 0; +} + +static struct platform_driver cam_cc_lagoon_driver = { + .probe = cam_cc_lagoon_probe, + .driver = { + .name = "lagoon-camcc", + .of_match_table = cam_cc_lagoon_match_table, + }, +}; + +static int __init cam_cc_lagoon_init(void) +{ + return platform_driver_register(&cam_cc_lagoon_driver); +} +subsys_initcall(cam_cc_lagoon_init); + +static void __exit cam_cc_lagoon_exit(void) +{ + platform_driver_unregister(&cam_cc_lagoon_driver); +} +module_exit(cam_cc_lagoon_exit); + +MODULE_DESCRIPTION("QTI CAM_CC LAGOON Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 201685cc573d32d5b53dcb993fa02250d5613cdf..b3ef447d213de4686a4c10872b94c8fe1811fb48 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 @@ -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-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 4b76012eb63b5a0353a4597165350a1651c20bed..bb9e9abb64c505ede52e071ab88f1b72ebd91719 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -311,6 +311,24 @@ static const struct clk_rpmh_desc clk_rpmh_lito = { .num_clks = ARRAY_SIZE(lito_rpmh_clocks), }; +DEFINE_CLK_RPMH_ARC(lagoon, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 4); +DEFINE_CLK_RPMH_VRM(lagoon, ln_bb_clk2, ln_bb_clk2_ao, "lnbclkg2", 4); +DEFINE_CLK_RPMH_VRM(lagoon, ln_bb_clk3, ln_bb_clk3_ao, "lnbclkg3", 4); + +static struct clk_hw *lagoon_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &lagoon_bi_tcxo.hw, + [RPMH_CXO_CLK_A] = &lagoon_bi_tcxo_ao.hw, + [RPMH_LN_BB_CLK2] = &lagoon_ln_bb_clk2.hw, + [RPMH_LN_BB_CLK2_A] = &lagoon_ln_bb_clk2_ao.hw, + [RPMH_LN_BB_CLK3] = &lagoon_ln_bb_clk3.hw, + [RPMH_LN_BB_CLK3_A] = &lagoon_ln_bb_clk3_ao.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_lagoon = { + .clks = lagoon_rpmh_clocks, + .num_clks = ARRAY_SIZE(lagoon_rpmh_clocks), +}; + static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -389,6 +407,7 @@ static const struct of_device_id clk_rpmh_match_table[] = { { .compatible = "qcom,sdm845-rpmh-clk", .data = &clk_rpmh_sdm845}, { .compatible = "qcom,kona-rpmh-clk", .data = &clk_rpmh_kona}, { .compatible = "qcom,lito-rpmh-clk", .data = &clk_rpmh_lito}, + { .compatible = "qcom,lagoon-rpmh-clk", .data = &clk_rpmh_lagoon}, { } }; MODULE_DEVICE_TABLE(of, clk_rpmh_match_table); diff --git a/drivers/clk/qcom/debugcc-bengal.c b/drivers/clk/qcom/debugcc-bengal.c index 0cd08b5d0ce1a6042b2dc48ca8c5e82445293a6f..bf3a92f8ed582965e9620257e91e2ce6b2377cdf 100644 --- a/drivers/clk/qcom/debugcc-bengal.c +++ b/drivers/clk/qcom/debugcc-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__ @@ -211,6 +211,7 @@ static const char *const gcc_debug_mux_parent_names[] = { "gpu_cc_debug_mux", "mc_cc_debug_mux", "measure_only_cnoc_clk", + "measure_only_ipa_2x_clk", "measure_only_snoc_clk", }; @@ -313,8 +314,9 @@ static int gcc_debug_mux_sels[] = { 0x13D, /* gcc_video_venus_ctl_clk */ 0x3E, /* gcc_video_xo_clk */ 0xE7, /* gpu_cc_debug_mux */ - 0x9E, /* mc_cc_debug_mux */ + 0x9E, /* mc_cc_debug_mux */ 0x1A, /* measure_only_cnoc_clk */ + 0xC6, /* measure_only_ipa_2x_clk */ 0x7, /* measure_only_snoc_clk */ }; @@ -423,6 +425,14 @@ static struct clk_dummy measure_only_cnoc_clk = { }, }; +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){ @@ -449,6 +459,7 @@ static struct clk_dummy pwrcl_clk = { struct clk_hw *debugcc_bengal_hws[] = { &measure_only_cnoc_clk.hw, + &measure_only_ipa_2x_clk.hw, &measure_only_mccc_clk.hw, &measure_only_snoc_clk.hw, &perfcl_clk.hw, diff --git a/drivers/clk/qcom/debugcc-lagoon.c b/drivers/clk/qcom/debugcc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..2d2138ba9eecd8d1e1f674cac266be006c1d4a75 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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-lagoon.c b/drivers/clk/qcom/dispcc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..593a8df485767659d2cfdc9acf63d50f43efa014 --- /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 +#include +#include +#include +#include +#include +#include + +#include + +#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/gcc-bengal.c b/drivers/clk/qcom/gcc-bengal.c index 304b14628f2f98665ca4e3af7adfb29b0c4f7a66..124605fd4f7487a76882f09beea8d211944570ae 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__ @@ -754,6 +754,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 +3767,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 ca54500ae53948e181792de5b0145d6456626ad0..f0ae8b617e408f4a8511f63f8be1bc7b422828d4 100644 --- a/drivers/clk/qcom/gcc-lagoon.c +++ b/drivers/clk/qcom/gcc-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__ @@ -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), { } @@ -775,6 +788,7 @@ static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { F(9600000, P_BI_TCXO, 2, 0, 0), F(19200000, P_BI_TCXO, 1, 0, 0), F(25000000, P_GPLL0_OUT_ODD, 8, 0, 0), + F(50000000, P_GPLL0_OUT_ODD, 4, 0, 0), F(100000000, P_GPLL0_OUT_ODD, 2, 0, 0), F(202000000, P_GPLL7_OUT_MAIN, 4, 0, 0), { } @@ -1628,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, @@ -2642,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 cd937ce6aaaf63e0d853b36b6320cda17ec9022f..4e23973b6cd161354193e61cd6bf6b65446f0d89 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-lagoon.c b/drivers/clk/qcom/gpucc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..8c7e09b753477dde8ad15a603e5d6dc014a63a25 --- /dev/null +++ b/drivers/clk/qcom/gpucc-lagoon.c @@ -0,0 +1,559 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "common.h" +#include "clk-regmap-mux.h" +#include "vdd-level-lito.h" + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_gx, VDD_GX_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_mx, VDD_NUM, 1, vdd_gx_corner); + +#define CX_GMU_CBCR_SLEEP_MASK 0xF +#define CX_GMU_CBCR_SLEEP_SHIFT 4 +#define CX_GMU_CBCR_WAKE_MASK 0xF +#define CX_GMU_CBCR_WAKE_SHIFT 8 + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_EVEN, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL0_OUT_ODD, + P_GPU_CC_PLL1_OUT_EVEN, + P_GPU_CC_PLL1_OUT_MAIN, + P_GPU_CC_PLL1_OUT_ODD, + P_CRC_DIV, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpu_cc_parent_names_0[] = { + "bi_tcxo", + "gpu_cc_pll0", + "gpu_cc_pll1", + "gcc_gpu_gpll0_clk", + "gcc_gpu_gpll0_div_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map gpu_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CRC_DIV, 1 }, + { P_GPU_CC_PLL0_OUT_ODD, 2 }, + { P_GPU_CC_PLL1_OUT_EVEN, 3 }, + { P_GPU_CC_PLL1_OUT_ODD, 4 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpu_cc_parent_names_1[] = { + "bi_tcxo", + "crc_div", + "gpu_cc_pll0", + "gpu_cc_pll1", + "gpu_cc_pll1", + "gcc_gpu_gpll0_clk", + "core_bi_pll_test_se", +}; + +static const struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +/* 506MHz Configuration*/ +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x1A, + .cal_l = 0x3F, + .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, +}; + +static struct clk_alpha_pll gpu_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 = "gpu_cc_pll0", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_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 struct clk_fixed_factor crc_div = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "crc_div", + .parent_names = (const char *[]){ "gpu_cc_pll0" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +/* 514MHz Configuration*/ +static const struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x1A, + .cal_l = 0x3F, + .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, +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .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 = "gpu_cc_pll1", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_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 freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gmu_clk_src", + .parent_names = gpu_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_LOWER] = 200000000}, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src[] = { + F(253000000, P_CRC_DIV, 1, 0, 0), + F(355000000, P_CRC_DIV, 1, 0, 0), + F(430000000, P_CRC_DIV, 1, 0, 0), + 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), + { } +}; + +static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = { + .cmd_rcgr = 0x101c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_gx_gfx3d_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gfx3d_clk_src", + .parent_names = gpu_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_gx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_GX_NUM]) { + [VDD_GX_LOWER] = 253000000, + [VDD_GX_LOW] = 355000000, + [VDD_GX_LOW_L1] = 430000000, + [VDD_GX_NOMINAL] = 565000000, + [VDD_GX_NOMINAL_L1] = 650000000, + [VDD_GX_HIGH] = 800000000, + [VDD_GX_HIGH_L1] = 850000000}, + }, +}; + +static struct clk_branch gpu_cc_acd_ahb_clk = { + .halt_reg = 0x1168, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1168, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_acd_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_acd_cxo_clk = { + .halt_reg = 0x1164, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1164, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_acd_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x1078, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_ahb_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_crc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_clk = { + .halt_reg = 0x10a4, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10a4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gfx3d_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_slv_clk = { + .halt_reg = 0x10a8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10a8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gfx3d_slv_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gmu_clk", + .parent_names = (const char *[]){ + "gpu_cc_gmu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_cxo_clk = { + .halt_reg = 0x1060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1060, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gfx3d_clk = { + .halt_reg = 0x1054, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x1054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gfx3d_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gmu_clk", + .parent_names = (const char *[]){ + "gpu_cc_gmu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_vsense_clk = { + .halt_reg = 0x1058, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_vsense_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gpu_cc_lagoon_clocks[] = { + [GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr, + [GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr, + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_GFX3D_CLK] = &gpu_cc_cx_gfx3d_clk.clkr, + [GPU_CC_CX_GFX3D_SLV_CLK] = &gpu_cc_cx_gfx3d_slv_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_CXO_CLK] = &gpu_cc_gx_cxo_clk.clkr, + [GPU_CC_GX_GFX3D_CLK] = &gpu_cc_gx_gfx3d_clk.clkr, + [GPU_CC_GX_GFX3D_CLK_SRC] = &gpu_cc_gx_gfx3d_clk_src.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_GX_VSENSE_CLK] = &gpu_cc_gx_vsense_clk.clkr, + [GPU_CC_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL1] = &gpu_cc_pll1.clkr, +}; + +static const struct regmap_config gpu_cc_lagoon_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8008, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpu_cc_lagoon_desc = { + .config = &gpu_cc_lagoon_regmap_config, + .clks = gpu_cc_lagoon_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_lagoon_clocks), +}; + +static const struct of_device_id gpu_cc_lagoon_match_table[] = { + { .compatible = "qcom,lagoon-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_lagoon_match_table); + +static int gpu_cc_lagoon_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + unsigned int value, mask; + int ret; + + regmap = qcom_cc_map(pdev, &gpu_cc_lagoon_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + 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]); + } + + vdd_gx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_gx"); + if (IS_ERR(vdd_gx.regulator[0])) { + if (!(PTR_ERR(vdd_gx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "Unable to get vdd_gx regulator\n"); + return PTR_ERR(vdd_gx.regulator[0]); + } + + vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx"); + if (IS_ERR(vdd_mx.regulator[0])) { + if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "Unable to get vdd_mx regulator\n"); + return PTR_ERR(vdd_mx.regulator[0]); + } + + /* Register clock fixed factor for CRC divide. */ + ret = devm_clk_hw_register(&pdev->dev, &crc_div.hw); + if (ret) { + dev_err(&pdev->dev, "Failed to register hardware clock\n"); + return ret; + } + + clk_fabia_pll_configure(&gpu_cc_pll0, regmap, &gpu_cc_pll0_config); + clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + ret = qcom_cc_really_probe(pdev, &gpu_cc_lagoon_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GPU CC clocks\n"); + return ret; + } + + /* Recommended WAKEUP/SLEEP settings for the gpu_cc_cx_gmu_clk */ + mask = CX_GMU_CBCR_WAKE_MASK << CX_GMU_CBCR_WAKE_SHIFT; + mask |= CX_GMU_CBCR_SLEEP_MASK << CX_GMU_CBCR_SLEEP_SHIFT; + 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); + + dev_info(&pdev->dev, "Registered GPU CC clocks\n"); + return 0; +} + +static struct platform_driver gpu_cc_lagoon_driver = { + .probe = gpu_cc_lagoon_probe, + .driver = { + .name = "lagoon-gpucc", + .of_match_table = gpu_cc_lagoon_match_table, + }, +}; + +static int __init gpu_cc_lagoon_init(void) +{ + return platform_driver_register(&gpu_cc_lagoon_driver); +} +core_initcall(gpu_cc_lagoon_init); + +static void __exit gpu_cc_lagoon_exit(void) +{ + platform_driver_unregister(&gpu_cc_lagoon_driver); +} +module_exit(gpu_cc_lagoon_exit); + +MODULE_DESCRIPTION("QTI GPU_CC LAGOON Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/npucc-lagoon.c b/drivers/clk/qcom/npucc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..1281e7d8e6192beae8bc76848dff45a4ce7ac1a6 --- /dev/null +++ b/drivers/clk/qcom/npucc-lagoon.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#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, +}; + +/* 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, + .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, + .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, + .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/vdd-level-lito.h b/drivers/clk/qcom/vdd-level-lito.h index b968de1b90a4a6f471a73618e4ac2f759fc87d12..abc7231342a6647dce44b6141b07cef6b50db6c0 100644 --- a/drivers/clk/qcom/vdd-level-lito.h +++ b/drivers/clk/qcom/vdd-level-lito.h @@ -32,4 +32,29 @@ static int vdd_corner[] = { [VDD_HIGH_L1] = RPMH_REGULATOR_LEVEL_TURBO_L1, }; +enum vdd_gx_levels { + VDD_GX_NONE, + VDD_GX_MIN, /* MIN SVS */ + VDD_GX_LOWER, /* SVS2 */ + VDD_GX_LOW, /* SVS */ + VDD_GX_LOW_L1, /* SVSL1 */ + VDD_GX_NOMINAL, /* NOM */ + VDD_GX_NOMINAL_L1, /* NOM1 */ + VDD_GX_HIGH, /* TURBO */ + VDD_GX_HIGH_L1, /* TURBO1 */ + VDD_GX_NUM, +}; + +static int vdd_gx_corner[] = { + [VDD_GX_NONE] = 0, + [VDD_GX_MIN] = RPMH_REGULATOR_LEVEL_MIN_SVS, + [VDD_GX_LOWER] = RPMH_REGULATOR_LEVEL_LOW_SVS, + [VDD_GX_LOW] = RPMH_REGULATOR_LEVEL_SVS, + [VDD_GX_LOW_L1] = RPMH_REGULATOR_LEVEL_SVS_L1, + [VDD_GX_NOMINAL] = RPMH_REGULATOR_LEVEL_NOM, + [VDD_GX_NOMINAL_L1] = RPMH_REGULATOR_LEVEL_NOM_L1, + [VDD_GX_HIGH] = RPMH_REGULATOR_LEVEL_TURBO, + [VDD_GX_HIGH_L1] = RPMH_REGULATOR_LEVEL_TURBO_L1, +}; + #endif diff --git a/drivers/clk/qcom/videocc-lagoon.c b/drivers/clk/qcom/videocc-lagoon.c index 5ddb669faeff636d9ce797152955f432afcd4bde..453223b4aaba06955103ce6e0b3afe8e55036744 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 9e14f1486fbb93ccb887c9b85cae0a61757741f3..81569767025cc4f23458c18848d536c8c671e3a6 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 ea4cafbe6e851aca89c24f79b4912b1a2278d774..9e16931e6f28aa7de918ea65a143fbd3e53893b4 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 628b63b85d3f09c5cfdf37dea4b565953e41264a..9ace7d39cd1b53bde1b204fe80a80d9bcfaa318f 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 69fb3afc970fb5d94aebfa7825e71443304c6955..2ca7e2be2f09e50e8b63ad3642fc78775017256a 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 ecbae8acd05b8c62b0048e8d5f07e39e86fc241c..f2f13b603ae9b811d4a8e52464c58c71ff153b1e 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 d2c99d8916b83e48c1b23d6c49dd98f43f81f2db..a5fddebbe530532be49c3c4dc7e3cfdf6d6deea0 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 d4b6b517fe1b44689df28853cf894baa3799153d..bd38c6aa389706c92261cd72f4f0c8fd8906ba67 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 d4f77c4eb277a50a67ebf914d6e4f62574560ff3..d5af937212992e2308abc1e912e681e4af255f74 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 162de44df099bff5be3a70e4fd5b010e1fc584f5..302596dc79a2c1283963615c1d82063bfa67dc5a 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -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 ee9c12cf3f08c38d6c1757a646c043b25f7dd90e..dec4a130390a3d46e33543465e9a9dd40d374515 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 0f7a0ffd3f706322e5049a41c0ef00dffde8de6e..d425b47cef1797e9aef965f17dd90250baa7e5c8 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 77ed0b0ba6819d94317e12f31ac25896143e2a77..61e3ba12773eac5b40d0bae5b19395d6d0fc1914 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 8936ef87652c093aade64063c9aa710d8064fbc7..c14bf782b2b33a91869dca97c4cc4ac2fcc8a2b8 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 012714d94b429bcca04b277aed39983a140b08d0..004b411b640b3f5449089cd52f17e66d2c1824a5 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 cc857d4d4a86e9985876b439ecd21f43579446f3..68551effb5ca2f3956080953226289ea9886f78f 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 080bfa24863ee46284c9427b8937655e97d21aee..7264e9731034829e39f4b8649cd40f59f51988fb 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 14881547043130d1e686055387a6276e49fd11f9..beb672a215b6cca9dbc8f1c58dd7bf1bbc57864b 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 dfaa5aad06927bed680c3799d2c986769ff984db..2c2564acad227a9626b9b9c333b3eb9157b2db5f 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/sh_cmt.c b/drivers/clocksource/sh_cmt.c index bbbf37c471a3919f7fb177f0439cd15d020d8d08..cec90a4c79b34b270a552a4db42e2b3540112cf4 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 cf93f6419b5142e397747be406138dacf3278a5c..fadff7915dd9cb8df40493a1db52db8a06e3af16 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c @@ -21,7 +21,7 @@ #include /* - * 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 eb10321f85178bce0bcf2af255e1070e92bf6253..8e7894a026acebb61ba401dc8410951484dcc0de 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/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 136c9c2dec4c77b39890609b6bad12dc471b4d70..5c02ee161e9b3aa92dbd4a6994f916704d2d00aa 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(). diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index bf6519cf64bc2e040b6e004f1771e47e5691c7e0..5fff39dae625777b25663f06c5183289ef4cd8c2 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.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 117960f494350ccd50e2049b1effb38e4a0d11d8..8c1fd83646df1a8025a46004106f52dd3869a019 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.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 @@ -104,13 +104,25 @@ static ssize_t dcvsh_freq_limit_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%lu\n", c->dcvsh_freq_limit); } -static unsigned long limits_mitigation_notify(struct cpufreq_qcom *c) +static unsigned long limits_mitigation_notify(struct cpufreq_qcom *c, + bool limit) { + struct cpufreq_policy *policy; + u32 cpu; unsigned long freq; - freq = readl_relaxed(c->reg_bases[REG_DOMAIN_STATE]) & + if (limit) { + freq = readl_relaxed(c->reg_bases[REG_DOMAIN_STATE]) & GENMASK(7, 0); - freq = DIV_ROUND_CLOSEST_ULL(freq * c->xo_rate, 1000); + freq = DIV_ROUND_CLOSEST_ULL(freq * c->xo_rate, 1000); + } else { + cpu = cpumask_first(&c->related_cpus); + policy = cpufreq_cpu_get_raw(cpu); + if (!policy) + freq = U32_MAX; + else + freq = policy->cpuinfo.max_freq; + } sched_update_cpu_freq_min_max(&c->related_cpus, 0, freq); trace_dcvsh_freq(cpumask_first(&c->related_cpus), freq); @@ -130,7 +142,7 @@ static void limits_dcvsh_poll(struct work_struct *work) cpu = cpumask_first(&c->related_cpus); - freq_limit = limits_mitigation_notify(c); + freq_limit = limits_mitigation_notify(c, true); dcvsh_freq = qcom_cpufreq_hw_get(cpu); @@ -138,6 +150,9 @@ static void limits_dcvsh_poll(struct work_struct *work) mod_delayed_work(system_highpri_wq, &c->freq_poll_work, msecs_to_jiffies(LIMITS_POLLING_DELAY_MS)); } else { + /* Update scheduler for throttle removal */ + limits_mitigation_notify(c, false); + regval = readl_relaxed(c->reg_bases[REG_INTR_CLR]); regval |= GT_IRQ_STATUS; writel_relaxed(regval, c->reg_bases[REG_INTR_CLR]); @@ -163,7 +178,7 @@ static irqreturn_t dcvsh_handle_isr(int irq, void *data) if (c->is_irq_enabled) { c->is_irq_enabled = false; disable_irq_nosync(c->dcvsh_irq); - limits_mitigation_notify(c); + limits_mitigation_notify(c, true); mod_delayed_work(system_highpri_wq, &c->freq_poll_work, msecs_to_jiffies(LIMITS_POLLING_DELAY_MS)); @@ -364,7 +379,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, struct cpufreq_qcom *c) { - struct device *dev = &pdev->dev; + struct device *dev = &pdev->dev, *cpu_dev; void __iomem *base_freq, *base_volt; u32 data, src, lval, i, core_count, prev_cc, prev_freq, cur_freq, volt; u32 vc; @@ -417,8 +432,12 @@ static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, prev_freq = cur_freq; cur_freq *= 1000; - for_each_cpu(cpu, &c->related_cpus) - dev_pm_opp_add(get_cpu_device(cpu), cur_freq, volt); + for_each_cpu(cpu, &c->related_cpus) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + continue; + dev_pm_opp_add(cpu_dev, cur_freq, volt); + } } c->table[i].frequency = CPUFREQ_TABLE_END; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index dc32f34e68d9379dceee6f04795fe81473a8f9ca..01acd88c419311a21b95108aae509c9d93a0367e 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -62,24 +62,23 @@ 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; - } + } + 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 470ff59a651b35f6972f608d96cf436b6ee9014e..f21fcaddf1f4b8a8d01b237f4044e382901ab1ba 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 01f4f31e4866769c885fd2535ed23889500b23a7..6f21e1304d2f36ac63206016c84a426bcb1177ea 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.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. * Copyright (C) 2006-2007 Adam Belay * Copyright (C) 2009 Intel Corporation */ @@ -1371,7 +1371,7 @@ static bool psci_enter_sleep(struct lpm_cpu *cpu, int idx, bool from_idle) if (cpu->bias) biastimer_start(cpu->bias); stop_critical_timings(); - wfi(); + cpu_do_idle(); start_critical_timings(); return true; } @@ -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 3f86d23c592ec0cdce4b6e8019a02256f4146d78..36ff5a1d9422602be64d9ea19b527814c54dd8ba 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 6386e1784fe4198b385c063a93e4f5c3e2ca0ad4..68d5ea818b6c014c1bbe9d49c3f458eafc927df5 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 801aeab5ab1e6898c8eb172a3a95a1daf989584b..f662914d87b8269322611343bdad0cf073fc075d 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -493,6 +493,29 @@ static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd) 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 @@ -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); diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c index cd464637b0cb6499563a4afe0ce702401eb43b1d..49c0097fa4749989306cbfb172e90ea1aa8b15ed 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 67155cb21636917456941c346457b300116d61f1..a83588d6ba72c9ee2628fb1eb9c9372f95ad143d 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 a091ae57f9024f723adb1820b71bc80d030bd199..45985b955d2c899c1478ce43d2d279c6590d9e0a 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 7725b6ee14efb2ecc89d9c0822aa19903284d3f5..fcb6747ed29ea39d6d8fff8e0d16c2cec8078798 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 0997e166ea57755c4d0178702995cce489b06d7e..8b749c721c8712c319e65841928221b2e1e2fadb 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 afebbd87c4aa1d22ca179f558552cb2f410fcc0a..1587f4ac6821e8fbe9a7b59a9fa03695e865b502 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 f59b044ebd25528864d055c04b90f67b83248eac..2bf084afe9b5857c123af84e1a57504ec89ce569 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 e01c46387df8d8823f4a9434ea0db3b9992583c4..519086730791b9ab53898e7f0464f0f3742cc552 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 56bd28174f5251c11c8996a959fc7e96160ee6ac..b926098f70ffdb213bbfb1267cf6e473cfd880b4 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 faa282074e5aa4a611b9ee137fc393d3fecc4a3c..b7216935236f03fc1892f9ec3e4b8bff7a48f07e 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 590d7352837e50e0a37ee6f84af4a9cff2dcc6db..641b11077f479df05544e35dacc482ffe56e79ca 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/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 97d57c3d9d7b9e7fabbe5bb0b96a1d0cbb3a74ec..92d42ac129f8950cb462f15a7dc8c1c30a07a83b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -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 */ } @@ -539,10 +584,6 @@ static void devfreq_dev_release(struct device *dev) 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); @@ -653,9 +694,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 +711,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 +742,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 +1060,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; @@ -1171,7 +1216,6 @@ 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) @@ -1184,10 +1228,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, 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,7 +1267,6 @@ 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) @@ -1226,10 +1279,20 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, 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 +1347,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/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 35dd06479867fad9636db14fc58747ee60dbf198..91d620994123e47729048f50aeafdd0688c39c30 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 dacf3f42426de9e54a2c255248e422e313096acc..a4f95574eb9adc9cb4030c81e4f142e34b340642 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -143,7 +143,7 @@ config DMA_JZ4740 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 db5b8fe1dd4ab4e4aa9f1c6abe381521ee71057d..7db66f974041e9eb859f7879f38354320503a742 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 da74fd74636b41f14b40b17abc1022ab23de3593..cee78f9c4794c387ec51b8f1a55bd8d2e20bc0fc 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 987899610b46113449add169ae60657e06e15fb7..edff93aacad36b23891c793ab48faaa9f61d12dc 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 0f389e008ce64ac0c55f71ce44549d464c0e4c1e..055d83b6cb68af4bbf6798abf09c7ff749607224 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 f62dd0944908d2015032859c20a2580a67fcf189..c299ff181bb68bcbc9ee53bd7bec96ad357ab059 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 09e7dfdbb7907df6217b65fb6b2ff3a96520e2ec..646c9c960c071a40b9dad231c5298f44106aa2f4 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 21a5708985bc2430712278d93d0bddaee7b827de..0fec3c554fe35427cc9760734a7ec224256d74d0 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 489c8fa4d2e247fa5989391ed2d4a2979e67a06d..4451ccfaf7c9292ae461c38cbfae0423ce7ffb32 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/qcom/gpi.c b/drivers/dma/qcom/gpi.c index 0637793355401d820aaf876bae286f4dee2e0ea1..d9eeab1d0397687f94e81349966bc84479b7deaf 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -1461,6 +1461,7 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan, (ch_ring->el_size * imed_event->tre_index); struct msm_gpi_dma_async_tx_cb_param *tx_cb_param; unsigned long flags; + u32 chid; /* * If channel not active don't process event but let @@ -1513,8 +1514,13 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan, /* make sure rp updates are immediately visible to all cores */ smp_wmb(); - if (imed_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) - return; + chid = imed_event->chid; + if (imed_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) { + if (chid == GPI_RX_CHAN) + goto gpi_free_desc; + else + return; + } tx_cb_param = vd->tx.callback_param; if (vd->tx.callback && tx_cb_param) { @@ -1532,6 +1538,7 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan, vd->tx.callback(tx_cb_param); } +gpi_free_desc: spin_lock_irqsave(&gpii_chan->vc.lock, flags); list_del(&vd->node); spin_unlock_irqrestore(&gpii_chan->vc.lock, flags); @@ -1550,6 +1557,7 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan, struct msm_gpi_dma_async_tx_cb_param *tx_cb_param; struct gpi_desc *gpi_desc; unsigned long flags; + u32 chid; /* only process events on active channel */ if (unlikely(gpii_chan->pm_state != ACTIVE_STATE)) { @@ -1594,8 +1602,13 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan, /* update must be visible to other cores */ smp_wmb(); - if (compl_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) - return; + chid = compl_event->chid; + if (compl_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) { + if (chid == GPI_RX_CHAN) + goto gpi_free_desc; + else + return; + } tx_cb_param = vd->tx.callback_param; if (vd->tx.callback && tx_cb_param) { @@ -1609,6 +1622,7 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan, vd->tx.callback(tx_cb_param); } +gpi_free_desc: spin_lock_irqsave(&gpii_chan->vc.lock, flags); list_del(&vd->node); spin_unlock_irqrestore(&gpii_chan->vc.lock, flags); diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 041ce864097e49804f6b9f8dcdd4cd3cf344cb6a..80ff95f75199f0ce30f733ebba6a727a7921a31b 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 1ed1c7efa28853249b9002477f323e20d89223bd..9e8ce56a83d8a65e4e9410ddbbea32edd38388ab 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 379e8d534e615cc23d4d3a0281ff9498fedd7e00..4903a408fc146eae9c5b3ac00a6e00e87046b4d6 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 e507ec36c0d3dfa107ccba439551390b9ace1add..f8fa99402f12bc13220bbfe834ebe096ea185d1a 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 395c698edb4d7f1d762497f96c0d9f55c2a29130..fc0f9c8766a87c35c17b7e42e95d1ca2db7fd20a 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 c1244231259518d08dda4427c977fac160aee76b..8aec137b4fcaaa8a3d55afd1fc863f168b083d1a 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) @@ -2112,8 +2115,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 +2134,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/i3200_edac.c b/drivers/edac/i3200_edac.c index d92d56cee10170df7600d3812dd1b1c1ad698abe..299b441647cd5c62740403c7476e79bf35983e13 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 f1d19504a028181b181969274110ae82d77a980c..4a3300c2da333311d29ca0dacab86882215b0156 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 72cea3cb86224bac58b2a4c914086dff8803b24d..53074ad361e58c179fe205bc5cf0b7876ad9fa16 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 4ba92f1dd0f744e29ab52c73ea8328bfd45abd80..dd209e0dd9abb2ca72c0c2b45a5548088852d5c9 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 c009d94f40c52982546fd8354c0b613d0b30237c..34be60fe68922100bac7f2ae4a7dc1b50c8ad419 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/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index c90b81e66798b2957926166cbc367a5ae4ba457c..5301be93d38f20e9efed428bba28c33b147b26e0 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2015, 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -516,7 +516,6 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys) } else if (mdm_drv->pon_state == PON_RETRY) { esoc_mdm_log( "Boot failed. Doing cleanup and attempting to retry\n"); - pon_trial++; mdm_subsys_retry_powerup_cleanup(esoc_clink, 0); } else if (mdm_drv->pon_state == PON_SUCCESS) { break; diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 5e1dd2772278147cf1d7ab51333540a13eb52df3..bdb67878179ed7970f428fce6de5269231c3c541 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 9f30f4929b72999be05a896fc22340b1a50bb1c0..7a767b66dd8656fee5dfa72c709016f030fece19 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/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 9dff33ea6416f66879ea4de32e8dba6a22e37c01..204390297f4bd4e249a6f3987e992ff1eb2b7d56 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 92f843eaf1e0156cf56d7839cec8d766bff6e86f..7a30952b463d58329edd05980409b40b6b856c74 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 e4119eb34986cb5219b648d1b900a23c8686329b..30fc04e284312e9f19c165994dad1170966304b8 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 c8024a39171ba3321adfeb1c2efddecc2b1ae226..3c8ae7cc35de3cf1b10cb452adbd8f0282b39a27 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 cfa033b05aed5e568b2510e2a7dace5162d13327..62f3401a1f01e90d9ddceabbe462416471d67614 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 27f2092b9882aef307763e214eb74c69f4db2af5..b53d5cc9c9f6c57ebae04f6a43e42fb814fc27d9 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 1ea71640fdc21dd6b8b96c93d1383babc6e89a33..c64c7da738297453ee05f173c1a6c0d4c0fadbe9 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 fb8af5cb7c9bffef3f4446baf2e94045685e6f3e..ccefa84f730576874cfa5f78751088612cafc632 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -45,6 +45,7 @@ #include #include #include +#include MODULE_AUTHOR("Abhay Salunke "); 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 4045098ddb860ee4a32528246f6add85ccc8b0b2..116989cf3d4571e447291d9909743cdfa1850935 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/google/gsmi.c b/drivers/firmware/google/gsmi.c index c8f169bf2e27dc0264785f402759a3d15f307ef0..62337be07afcb092c5178681cb0ed382be691a0c 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 77d239073658315c804053be06170164300105de..d90718aee126f91dcbeeb6fb9508c10669bb3337 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 diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 688525dd4aee599548c52502168b3f04f62388fb..367e727a8f93ed24aaea8469c3af42dcc768df41 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 a200a217461191b796105682e599582bb736683e..44eb99807e337c20b6e72c7ec712593ea85629dd 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #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(*header), 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); + kfree(data); return ret; } diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 538bce4b5b4271b5d830623840c5e71aa7527017..78254ed93206b19f648863390ca4a389e290dee9 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-pca953x.c b/drivers/gpio/gpio-pca953x.c index e0657fc72d31f10847c7e7e52d5736fe81e1e353..0232c25a158645f588d2d9826ef1e2a36311f605 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 d6d36d537e3736955881944cbf2aae53326841bf..b77ea16ffa031e4cbe291d8f5d6463338eaed536 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 87c18a544513768ce03aa38464a77125e6b1c867..7f3da34c78746b7549d8d394337e408e1c2a9768 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 cf2604e635999dcace0818728af94771961f7fb2..8edbb3f0c1013bdabab88b20f9acc7facea60963 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 bbc58021f8347a5191c2a733c7519231b7d4172e..ae3d8b27b907e0bd040c096e737a442dfb61dcf5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2548,19 +2548,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 +2590,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 b80243d3972e490fbd0d97804e978269b5cd7372..ce7f18c5ccb26e861ef625aed1434ef0941e17ab 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 f823d4baf044db4c715a0fe8a19fbf4b1540c4b1..cf582cc46d53e05b4e5ba1064ce0ca046368afe4 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 b0e14a3d54efd44e86580f816f41f05f16368b08..b14ce112703f06d6367ca9e3f6b99d87e91025d1 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/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c index 8def0d9fa0ff0586343a13d08a9bea59c1a2da32..46c9cb47a96e58b156a9162a984c1eccbd50a2fe 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_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index d440b28ee43fb94ce99ae8dadfae321fb80625a2..6896d69b8c240a6632ad3eb9afd67cea0a292508 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 c7c505095402df3d5f8291c8592ae391e8ec65fb..6bf032e81e39fd9610e6a612e5e81b397634da39 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 fb86c24394ff463f3ee565125d72e27434889038..ce459ea4ec3ad1858ba8a6bc9e7f209a9c509a64 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 04440064b9b7baaeaec4a9d19d48a33ed2b5dbdb..e5b3ba73e6617af5e8c2a7c037e124240de53149 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/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1c1fee0e19492a4eeb4796ece3c23e3526efd692..5f601e8c6f9ddd8ecaa75be0328bcd5c7f4d83c6 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 @@ -4174,33 +4230,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) { @@ -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/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index 3b378936f57559fbfe80a30a6b14ee00aa9f7136..a9b15001416a729bca83439647cdd52ecf2b9965 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 95478db9998b51a410b927d654990967c74c5fdc..e4b9eb1f6b6021b0563341aff68ae4eab3c75f98 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; - break; + goto err; } - if (!check_cmd(engine, desc, cmd, length, is_master)) { + 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 (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 a4b4ab7b9f8ef323592b9e8f162de009c21673b3..b0d76a7a0946fdb5c8e9ca32ec6b7be5cc2422a9 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 d6c25bea438220b3dfc270f56440d606385d5fc2..db2e9af49ae6f65d9acd334f6941c79fcc93d93f 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 9372877100420ba7fc9c7129b273b763144e266c..c7d05ac7af3cb1a5e7c2164c444cca089283705f 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 b10770cfccd24bedd80a7fd67ac06d78dde695c1..7a0e6dbbad2e4817132cd3b7c3bdfd87914bbcdc 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 b116e4942c10d13eb7e9771a4f50e737e47d185a..834d3951d8a9b9d4344b48476e59ee3291759e39 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 679bbae5294533b5aa09ea10b03dfe682ff1558a..f08c54740cbee6e93c4c1030c7a34addec9268c4 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 87411a5aba7796f409afed81ad2040497a9e61b9..d4c6aa7fbac8d57084c052100e77243658a50a14 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 2c9b284036d10217a013e146eba8704123006d65..961abb6ea18e9a13cd048c48df1a5609858d1a31 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 d6c8f8fdfda5f106776e0a148e034e10e64ccbb7..b7fda69342fcb0e71fca62339d4d50639c5805fd 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 4e070afb2738b741700ffe510951159334a9fcb0..a6f4f32dd71ce483fa583ed75d1ec1bbdf9975c6 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 50d56498de7792c015176a229ad31b74a5379180..b1154d80356428ecb9b9b591935846fdfdae8c91 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 425df814de75d2557e4e52505d4f2aa134237adf..8d731eb1de69cb1177a6564d324e4a5d56d9ee75 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 f5ffa6d31e82c3d19a4ceb8e1b0e78956d0eae3d..eaf1a161bc96c279a8949c46a1ed314b617a2e56 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 f7945bae3b4a9e74b7400b463c95984b93cabc59..e1760140e3c26ab4c0f9f324e8e5a2e91134224d 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 9acb9dfaf57e6ca4777b7546e533c97cbf10dda2..9cde79a7335c825de1f59c059ad13ac2d50eb30a 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 74cc204b07e80e13f42943da7767648661c3bf75..2d9b7b5fb49c867a4ff9ee271ff98b59addb3670 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 d756436c1fcd3293f40db2d9efda018603c496e4..989465e5f9d91ac00824cb9cd60a38fa55a2dfd1 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/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 2445e75cf7ea6664854325a6383fe0e32e2bdd57..d00f45eed03cac4655b8bd6bea72781b32365403 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; - - drm_kms_helper_poll_disable(dev); - - console_lock(); - qxl_fbdev_set_suspend(qdev, 1); - console_unlock(); + int ret; - /* 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 7d39ed63e5be755e7d3de41899b46663086bf52b..b24401f21e934cbc02ef730c46ce9409a78f182a 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 c22321cc5a4158fd9fc628e1c0ac2195247aed55..c2b506c707a28dc45f8b0c790fa4056e7d82d130 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 0a785ef0ab660f41c2a204fb766068c93dcfa044..db2d8b84e137be8182b32cf6c0df1a78498eeec6 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_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 8c31c9ab06f8b2bf81a163022efbbc560ee7517d..fda1ae12069a7fae1aff579bbc90af283a992eed 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/host1x/hw/hw_host1x06_uclass.h b/drivers/gpu/host1x/hw/hw_host1x06_uclass.h index 4457486c72b05e0dd5f2b3ddade15276e5fa98c7..e599e15bf999aa44a70d0c796956739b02adc126 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/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c index 2f8db9d625514928006c6b31adeccbe312514ef3..4a28f3fbb0a28c55630be20edbb8c2df4de74645 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 ac783b48e07f01e46788369708225e55590552f8..266c0e1fe2a0618ee309fdd90abf8db13177257e 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -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. */ #define ANY_ID (~0) @@ -26,7 +26,6 @@ static const struct adreno_a3xx_core adreno_gpu_core_a306 = { DEFINE_ADRENO_REV(ADRENO_REV_A306, 3, 0, 6, 0), .features = ADRENO_SOFT_FAULT_DETECT, .gpudev = &adreno_a3xx_gpudev, - .gmem_base = 0, .gmem_size = SZ_128K, .busy_mask = 0x7ffffffe, .bus_width = 0, @@ -48,7 +47,6 @@ static const struct adreno_a3xx_core adreno_gpu_core_a306a = { DEFINE_ADRENO_REV(ADRENO_REV_A306A, 3, 0, 6, 0x20), .features = ADRENO_SOFT_FAULT_DETECT, .gpudev = &adreno_a3xx_gpudev, - .gmem_base = 0, .gmem_size = SZ_128K, .busy_mask = 0x7ffffffe, .bus_width = 16, @@ -68,7 +66,6 @@ static const struct adreno_a3xx_core adreno_gpu_core_a304 = { DEFINE_ADRENO_REV(ADRENO_REV_A304, 3, 0, 4, 0), .features = ADRENO_SOFT_FAULT_DETECT, .gpudev = &adreno_a3xx_gpudev, - .gmem_base = 0, .gmem_size = (SZ_64K + SZ_32K), .busy_mask = 0x7ffffffe, .bus_width = 0, @@ -192,7 +189,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a530v2 = { ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_1M, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -217,7 +213,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a530v3 = { ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_1M, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -282,7 +277,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a505 = { DEFINE_ADRENO_REV(ADRENO_REV_A505, 5, 0, 5, ANY_ID), .features = ADRENO_PREEMPTION | ADRENO_64BIT, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_128K + SZ_8K), .busy_mask = 0xfffffffe, .bus_width = 16, @@ -301,7 +295,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a506 = { .features = ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_128K + SZ_8K), .busy_mask = 0xfffffffe, .bus_width = 16, @@ -378,7 +371,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a510 = { .base = { DEFINE_ADRENO_REV(ADRENO_REV_A510, 5, 1, 0, ANY_ID), .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_256K, .busy_mask = 0xfffffffe, .bus_width = 16, @@ -503,7 +495,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a540v2 = { ADRENO_CONTENT_PROTECTION | ADRENO_GPMU | ADRENO_SPTP_PC, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_1M, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -585,7 +576,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a512 = { .features = ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_256K + SZ_16K), .busy_mask = 0xfffffffe, .bus_width = 32, @@ -603,7 +593,6 @@ static const struct adreno_a5xx_core adreno_gpu_core_a508 = { .features = ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION, .gpudev = &adreno_a5xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_128K + SZ_8K), .busy_mask = 0xfffffffe, .bus_width = 32, @@ -733,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 }, @@ -777,7 +766,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a630v2 = { ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IOCOHERENT | ADRENO_PREEMPTION, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_1M, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -797,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}, @@ -835,7 +823,7 @@ static const struct adreno_reglist a615_hwcg_regs[] = { {A6XX_RBBM_CLOCK_DELAY_UCHE, 0x00000002}, {A6XX_RBBM_CLOCK_CNTL_RB0, 0x22222222}, {A6XX_RBBM_CLOCK_CNTL2_RB0, 0x00002222}, - {A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002220}, + {A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002020}, {A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00002220}, {A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00002220}, {A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00002220}, @@ -864,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}, }; @@ -876,7 +864,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a615 = { ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC | ADRENO_IOCOHERENT, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_512K, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -903,7 +890,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a618 = { ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC | ADRENO_IOCOHERENT, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_512K, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -923,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}, @@ -1031,7 +1044,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a620 = { ADRENO_IFPC | ADRENO_PREEMPTION | ADRENO_ACD | ADRENO_APRIV, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0, .gmem_size = SZ_512K, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1121,7 +1133,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a640 = { ADRENO_CONTENT_PROTECTION | ADRENO_IOCOHERENT | ADRENO_IFPC | ADRENO_PREEMPTION, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_1M, //Verified 1MB .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1201,7 +1212,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a650 = { ADRENO_IOCOHERENT | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC | ADRENO_APRIV, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0, .gmem_size = SZ_1M + SZ_128K, /* verified 1152kB */ .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1232,7 +1242,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a650v2 = { ADRENO_IFPC | ADRENO_PREEMPTION | ADRENO_ACD | ADRENO_LM | ADRENO_APRIV, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0, .gmem_size = SZ_1M + SZ_128K, /* verified 1152kB */ .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1260,7 +1269,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a680 = { DEFINE_ADRENO_REV(ADRENO_REV_A680, 6, 8, 0, ANY_ID), .features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_GPMU, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_2M, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1337,7 +1345,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a612 = { ADRENO_IOCOHERENT | ADRENO_PREEMPTION | ADRENO_GPMU | ADRENO_IFPC, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_128K + SZ_4K), .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1362,7 +1369,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a616 = { ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC | ADRENO_IOCOHERENT, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = SZ_512K, .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1388,7 +1394,6 @@ static const struct adreno_a6xx_core adreno_gpu_core_a610 = { .features = ADRENO_64BIT | ADRENO_CONTENT_PROTECTION | ADRENO_PREEMPTION, .gpudev = &adreno_a6xx_gpudev, - .gmem_base = 0x100000, .gmem_size = (SZ_128K + SZ_4K), .busy_mask = 0xfffffffe, .bus_width = 32, @@ -1426,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 18d78f832a1c024c34ea9277670d2d3216773fe1..58c93b4c396ba8d32288fc100cb2373ecc3012bd 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 #include @@ -829,6 +829,17 @@ static int adreno_identify_gpu(struct adreno_device *adreno_dev) return -ENODEV; } + /* + * Some GPUs needs UCHE GMEM base address to be minimum 0x100000 + * and 1MB aligned. Configure UCHE GMEM base based on GMEM size + * and align it one 1MB. This needs to be done based on GMEM size + * because setting it to minimum value 0x100000 will result in RB + * and UCHE GMEM range overlap for GPUs with GMEM size >1MB. + */ + if (!adreno_is_a650_family(adreno_dev)) + adreno_dev->uche_gmem_base = + ALIGN(adreno_dev->gpucore->gmem_size, SZ_1M); + /* * Initialize uninitialzed gpu registers, only needs to be done once * Make all offsets that are not initialized to ADRENO_REG_UNUSED @@ -1339,15 +1350,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, @@ -2478,7 +2489,7 @@ static int adreno_prop_device_info(struct kgsl_device *device, .device_id = device->id + 1, .chip_id = adreno_dev->chipid, .mmu_enabled = MMU_FEATURE(&device->mmu, KGSL_MMU_PAGED), - .gmem_gpubaseaddr = adreno_dev->gpucore->gmem_base, + .gmem_gpubaseaddr = 0, .gmem_sizebytes = adreno_dev->gpucore->gmem_size, }; @@ -2552,9 +2563,9 @@ static int adreno_prop_uche_gmem_addr(struct kgsl_device *device, struct kgsl_device_getproperty *param) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - u64 vaddr = adreno_dev->gpucore->gmem_base; - return copy_prop(param, &vaddr, sizeof(vaddr)); + return copy_prop(param, &adreno_dev->uche_gmem_base, + sizeof(adreno_dev->uche_gmem_base)); } static int adreno_prop_ucode_version(struct kgsl_device *device, @@ -3304,12 +3315,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 @@ -3333,17 +3347,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 87d7e80b52bec39aa7e4a5b5a64c5490dc71f686..3edab23fa4ac6fcc466c2d3d6bd42044d657986b 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, @@ -352,7 +353,6 @@ struct adreno_reglist { * @patchid: Match for the patch revision of the GPU * @features: Common adreno features supported by this core * @gpudev: Pointer to the GPU family specific functions for this core - * @gmem_base: Base address of binning memory (GMEM/OCMEM) * @gmem_size: Amount of binning memory (GMEM/OCMEM) to reserve for the core * @busy_mask: mask to check if GPU is busy in RBBM_STATUS * @bus_width: Bytes transferred in 1 cycle @@ -362,7 +362,6 @@ struct adreno_gpu_core { unsigned int core, major, minor, patchid; unsigned long features; struct adreno_gpudev *gpudev; - unsigned long gmem_base; size_t gmem_size; unsigned int busy_mask; u32 bus_width; @@ -379,6 +378,7 @@ enum gpu_coresight_sources { * @dev: Reference to struct kgsl_device * @priv: Holds the private flags specific to the adreno_device * @chipid: Chip ID specific to the GPU + * @uche_gmem_base: Base address of GMEM for UCHE access * @cx_misc_len: Length of the CX MISC register block * @cx_misc_virt: Pointer where the CX MISC block is mapped * @rscc_base: Base physical address of the RSCC @@ -463,6 +463,7 @@ struct adreno_device { struct kgsl_device dev; /* Must be first field in this struct */ unsigned long priv; unsigned int chipid; + u64 uche_gmem_base; unsigned long cx_dbgc_base; unsigned int cx_dbgc_len; void __iomem *cx_dbgc_virt; @@ -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_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 911ee41df89b5b83b8a3ab24911c6dd9d48f8c48..b13bc05b3e4c930229c96fd21c6986ba0861c72c 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -1508,10 +1508,10 @@ static void a5xx_start(struct adreno_device *adreno_dev) /* Program the GMEM VA range for the UCHE path */ kgsl_regwrite(device, A5XX_UCHE_GMEM_RANGE_MIN_LO, - adreno_dev->gpucore->gmem_base); + adreno_dev->uche_gmem_base); kgsl_regwrite(device, A5XX_UCHE_GMEM_RANGE_MIN_HI, 0x0); kgsl_regwrite(device, A5XX_UCHE_GMEM_RANGE_MAX_LO, - adreno_dev->gpucore->gmem_base + + adreno_dev->uche_gmem_base + adreno_dev->gpucore->gmem_size - 1); kgsl_regwrite(device, A5XX_UCHE_GMEM_RANGE_MAX_HI, 0x0); diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 69ca42f4eab19baa54c7618f345a99e9717ca506..63e48e6b82207048a7b99cd4f03ffcffd46192e5 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -429,16 +429,16 @@ static void a6xx_start(struct adreno_device *adreno_dev) kgsl_regwrite(device, A6XX_UCHE_WRITE_THRU_BASE_HI, 0x0001ffff); /* - * Some A6xx targets no longer use a programmed GMEM base address - * so only write the registers if a non zero address is given - * in the GPU list + * Some A6xx targets no longer use a programmed UCHE GMEM base + * address so only write the registers if this address is + * non zero. */ - if (adreno_dev->gpucore->gmem_base) { + if (adreno_dev->uche_gmem_base) { kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MIN_LO, - adreno_dev->gpucore->gmem_base); + adreno_dev->uche_gmem_base); kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MIN_HI, 0x0); kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MAX_LO, - adreno_dev->gpucore->gmem_base + + adreno_dev->uche_gmem_base + adreno_dev->gpucore->gmem_size - 1); kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MAX_HI, 0x0); } diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c index af1381852751638351b533a7876e28017dfd2a98..0db1b5871504bb120f8fa27e9ba78f20e678f7f4 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 */ @@ -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); diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c index d0be2708ae3b5ba02fcbbe4bcd93d350c92a3f22..e466de948ac8538a9e8cedc06955c3a73d39fe6e 100644 --- a/drivers/gpu/msm/adreno_compat.c +++ b/drivers/gpu/msm/adreno_compat.c @@ -28,8 +28,7 @@ int adreno_getproperty_compat(struct kgsl_device *device, devinfo.chip_id = adreno_dev->chipid; devinfo.mmu_enabled = MMU_FEATURE(&device->mmu, KGSL_MMU_PAGED); - devinfo.gmem_gpubaseaddr = - adreno_dev->gpucore->gmem_base; + devinfo.gmem_gpubaseaddr = 0; devinfo.gmem_sizebytes = adreno_dev->gpucore->gmem_size; diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index 2fe694f67638c867e07b207425f299be12fcf0d0..b0342c703905355e407a0da257d24f4754c4cb80 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 @@ -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 8fe0ccb7fc52c05837e74b7301c058e22adcc817..4125053ef61f79d8a14d4dc346a423df4a1dfa82 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 @@ -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 435f3254ab9a0e4cb6aa2a80358a4933f90f249a..ea0cce68ebc1b9e6751c15838de2e83672e8932f 100644 --- a/drivers/gpu/msm/kgsl_gmu.c +++ b/drivers/gpu/msm/kgsl_gmu.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 @@ -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); } @@ -1339,14 +1340,17 @@ static int gmu_probe(struct kgsl_device *device, struct device_node *node) device->gmu_core.reg_len = gmu->reg_len; /* Initialize HFI and GMU interrupts */ - hfi->hfi_interrupt_num = kgsl_request_irq(gmu->pdev, "kgsl_hfi_irq", - hfi_irq_handler, device); - - gmu->gmu_interrupt_num = kgsl_request_irq(gmu->pdev, "kgsl_gmu_irq", - gmu_irq_handler, device); + ret = kgsl_request_irq(gmu->pdev, "kgsl_hfi_irq", + hfi_irq_handler, device); + if (ret < 0) + goto error; + hfi->hfi_interrupt_num = ret; - if (hfi->hfi_interrupt_num < 0 || gmu->gmu_interrupt_num < 0) + ret = kgsl_request_irq(gmu->pdev, "kgsl_gmu_irq", + gmu_irq_handler, device); + if (ret < 0) goto error; + gmu->gmu_interrupt_num = ret; /* Don't enable GMU interrupts until GMU started */ /* We cannot use irq_disable because it writes registers */ diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 61c6eb0e9be4ab8ce0388fa72a3d96ebf3055aca..f6f633bba8ae2e4814db9e550dc36161e8099573 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1034,7 +1034,7 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu); pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { - pt->compat_va_start = KGSL_IOMMU_SVM_BASE32; + pt->compat_va_start = KGSL_IOMMU_SVM_BASE32(mmu); pt->compat_va_end = KGSL_IOMMU_SECURE_BASE(mmu); pt->va_start = KGSL_IOMMU_VA_BASE64; pt->va_end = KGSL_IOMMU_VA_END64; @@ -1043,7 +1043,7 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, if (pagetable->name != KGSL_MMU_GLOBAL_PT && pagetable->name != KGSL_MMU_SECURE_PT) { if (kgsl_is_compat_task()) { - pt->svm_start = KGSL_IOMMU_SVM_BASE32; + pt->svm_start = KGSL_IOMMU_SVM_BASE32(mmu); pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu); } else { pt->svm_start = KGSL_IOMMU_SVM_BASE64; @@ -1063,13 +1063,13 @@ static void setup_32bit_pagetable(struct kgsl_mmu *mmu, pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu); pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { - pt->va_start = KGSL_IOMMU_SVM_BASE32; + pt->va_start = KGSL_IOMMU_SVM_BASE32(mmu); pt->va_end = KGSL_IOMMU_SECURE_BASE(mmu); pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; } } else { - pt->va_start = KGSL_IOMMU_SVM_BASE32; + pt->va_start = KGSL_IOMMU_SVM_BASE32(mmu); pt->va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu); pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; @@ -1077,7 +1077,7 @@ static void setup_32bit_pagetable(struct kgsl_mmu *mmu, if (pagetable->name != KGSL_MMU_GLOBAL_PT && pagetable->name != KGSL_MMU_SECURE_PT) { - pt->svm_start = KGSL_IOMMU_SVM_BASE32; + pt->svm_start = KGSL_IOMMU_SVM_BASE32(mmu); pt->svm_end = KGSL_IOMMU_SVM_END32; } } diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index 5e8bb24fc0c3621d50c9f488949603f71620d17a..32fe93dcf78362b3bf57ab551516de0b221ab823 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -24,7 +24,9 @@ #define KGSL_IOMMU_SECURE_BASE(_mmu) \ (KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) - KGSL_IOMMU_SECURE_SIZE) -#define KGSL_IOMMU_SVM_BASE32 0x300000 +#define KGSL_IOMMU_SVM_BASE32(__mmu) \ + (ADRENO_DEVICE(KGSL_MMU_DEVICE(__mmu))->uche_gmem_base + \ + ADRENO_DEVICE(KGSL_MMU_DEVICE(__mmu))->gpucore->gmem_size) #define KGSL_IOMMU_SVM_END32 (0xC0000000 - SZ_16M) #define KGSL_IOMMU_VA_BASE64 0x500000000ULL diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index d5a7f5ea47fafeca01403c2dd89906f32fdaf212..5970692bf891c11e3110e1744902601c42b18f9c 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 @@ -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_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 99bb67bc2511a6f94b795359219fd1b8f519947c..efc78790168823aaa2980eb51366fa33a74651a1 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 @@ -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/hid/hid-axff.c b/drivers/hid/hid-axff.c index a594e478a1e218abd629b69f8ca9b19b9335e753..843aed4dec80a8378f42f24d71f56ed0c604e0b5 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 4545cd1ba730d5e304c7eaf01daa240ec84dd986..b0c8fae7f903d0f45a31b84c1705c4a3da8f19ff 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -211,17 +211,38 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) return 0; /* we know nothing about this usage 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; } @@ -543,6 +555,41 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) return 0; } +/* + * 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) { @@ -761,6 +810,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 +1014,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 +1068,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 +1099,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 818ea7d935333046adc5036d141ffa6cf6be5c40..309969b8dc2ecfb5353d5fbb5799c02ee087f0be 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 d82d75bb11f78b591483233bc294aaf3a5939a72..80f9a02dfa69b212b523a9e19a724a14c9edf3a2 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 2d8cead3adcaadf68e30ea9a4f37c7fc21452d50..5a02c50443cb70057c6c6ae0a0c9fc10f6564781 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 6bf4da7ad63a51f3b9aa6713552c96be6042bba2..8cb63ea9977d6807a10e86b568157d8f0f2f9762 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -120,6 +120,10 @@ static int hammer_input_configured(struct hid_device *hdev, 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, diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index edc0f64bb584806f080c1b6f682c53a20acc8466..c68486ee203c73bb5e3195afad10971257a9bf74 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 704049e62d58ac9a9cd1e7c8bb4a3e1c222eb42d..4d1496f60071fdeb195ebb03f60c50c0013df55a 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); - - if (!buffer) - return; + 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, 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 f5da7f332138ff200a5330cc6415aaf27805139c..efd28614ab79ca53a532a81d10667d9493be926c 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 @@ -713,6 +715,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 d988b92b20c828cfa57179aa5f0a029b12b0532a..01bed2f6862ee5a313d58cf82cc67c5366f135c4 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 0e3fb1a7e42174dd1dfb953e7209fc14ab50ed7a..6909d9c2fc67a692a4b580b9749876658371c234 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 8c2da183d3bc71354d82b635b5234a70ca106850..acf739fc40603dcc9fdd4e44668231b760f931c5 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 4b26928cb2b658676d614d8fef5dfe216f37332c..ef80c592b88a3ef7b62f06d1737a3a8c71b16b27 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 e1394af0ae7ba06701ad106896fb9391c417d6c0..1871cdcd1e0a8c5d93273b4dd58174efcaabdb14 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 e642cfaf303b49f8ef46b67daf314d46c863f6e3..034c883e57fa270dba16d39cc567ee78df119e73 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -1867,8 +1867,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 +1877,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 40b1cb1a62b05baf4530a4826a22390084650a04..4865e6774a9cc95db9824e447e8ce778b18f7b00 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -174,11 +174,9 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5), - HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, - { HID_USB_DEVICE(USB_VENDOR_ID_QVR32A, USB_DEVICE_ID_QVR32A), - HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, - { USB_VENDOR_ID_NREAL, USB_DEVICE_ID_NREAL, HID_QUIRK_HIDINPUT_FORCE }, + { HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_QVR32A, USB_DEVICE_ID_QVR32A), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_NREAL, USB_DEVICE_ID_NREAL), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { 0 } }; @@ -726,6 +724,7 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_QVR) { HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5) }, { HID_USB_DEVICE(USB_VENDOR_ID_QVR32A, USB_DEVICE_ID_QVR32A) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NREAL, USB_DEVICE_ID_NREAL) }, #endif { } }; diff --git a/drivers/hid/hid-qvr.c b/drivers/hid/hid-qvr.c index 6df6010c89835e01600b9ae40b70fe9e714ce553..ab347d17668c8ea8b9e4dc913b2efec5234d5f84 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 @@ -63,6 +63,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 +71,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 +112,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) { @@ -255,10 +261,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 +287,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 +304,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-sony.c b/drivers/hid/hid-sony.c index 09f2c617b09fd0fda62ae9793274b3e397a31278..d05c387a588edf6627b232e8a88a408928155be7 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 dc4128bfe2ca95d3553c1141f03cbd618d367e5c..8dae0f9b819e011d6695462fea7e88e85cd16669 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,13 +531,15 @@ 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); + mutex_unlock(&steam->mutex); + + if (!client_opened) ret = steam_input_register(steam); - } else { + else ret = 0; - } - mutex_unlock(&steam->mutex); 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 30b8c3256c9917e88f6dcff081ee7a9306bc00f9..efe8c2a0261ef0c5b275a26df21fb62c88b4c0f3 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 a29756c6ca02d064faee371143e750b19094f26c..4e7e01be99b13fcd28edc814f9d0a7425a74c1c0 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 3cde7c1b9c33cd673858d5089ed097e724fb12b0..2f940c1de6169724b6f629787496b8bf4d9f9268 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -50,6 +50,7 @@ #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) /* flags */ #define I2C_HID_STARTED 0 @@ -177,6 +178,10 @@ 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 }, { 0, 0 } }; @@ -501,6 +506,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); diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c index cac262a912c1248747d2814fa9e3b3d3512f8c76..10af8585c820d253b71301f23efae5db3b40fc14 100644 --- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c +++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c @@ -322,6 +322,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 = { @@ -330,6 +349,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 = { @@ -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 cd23903ddcf194e581902102ebcd56f70459ac9a..e918d78e541c0d072ea4fe89cdfec2eb9628da4c 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 b9b917d2d50db3fedaa17ce8f6fcb3a05e2f38c3..c41dbb167c91ba23f3159ba1a629b118e26a0abd 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 3c37c3cbf6f10013b928e18c1ef245f9eac3d295..9c0900c35b236b71b9b89ab5d33a922f2f15c7b9 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 1df037e7f0b4212f5b6432d8ee6183681f2167fc..77bb46948eea83251f98b9b8061f0f26f1a0fd0c 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 fdb0f832fadefe33aff3642406ae7fc380657078..5e515533e9cdbccdb73860eb92517a572c86f7dd 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 8e923e70e5945c366ad3a16049a35d680c908641..12bc9fa2111178cd59e36cf81ebc9e4c70af89b6 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 e6b49500c52aedc8751afd7114ed2283e505ea56..8c9555313fc3db87be75921a24280bdf1f95e26d 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 bb15d7816a294f0fd3c9b99d4bc04195e374698e..2cef0c37ff6fe0b6c9eb9147316252d79f416b83 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 eba692cddbdee721ece32e4b943d1a244575e991..559101a1c1367a69b2d5402fcac24ff67c5f4de5 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 b998f9fbed41e3fd854625f1a399936efd84835b..979b579bc118fe690d0d82f59841eb9e1ea9497a 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 7f01fad0d3e34f27982264d583fa34d909ffd2eb..65de80bd63d8cd90fd085911099752220af59d49 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 ed3f2c026effe2634724e3fc15e221cbe0fde91a..50e7363b27518748bdfcf815bb033162cef1a0a6 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -29,4 +29,4 @@ obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o 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 0000000000000000000000000000000000000000..5cbd10f0c0097761c5ea93351c6d5473f6acef0c --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..793840ea8354cd4fdde327699ca8383b56221454 --- /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-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index f6d0571ab9dd59e10066f6143d6206f69b022dc7..d31f1d8758b243253d985ea36859dbebee6d26e0 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 44451fa388ac72b4eeb4ecfd4ad94313bd6870b0..284fbb36608ee933b20aafa259e1d6f33c2d6ce4 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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 a1213c0535ae38da36d3770f4d4c3817f5e8eefd..4dd59ed3d564d685cd354716cb0becd64fc77ed1 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 9e894c93dc07a0a08072470967eaa6b550551e36..3f4b0b421c8b987dbbfd645b7a96a5585de72b06 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -29,6 +29,7 @@ #include #include #include +#include #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 06870c1393e2514ea85598015a23922cc5bf2309..a0891f55d1f99fdd705f98dee6f144c25737a7a1 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 @@ -15,9 +15,11 @@ #include #include #include +#include #include #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) @@ -132,7 +134,7 @@ static ssize_t enable_tgu_store(struct device *dev, /* program the TGU Group data for the desired use case*/ - for (i = 0; i <= drvdata->grp_refcnt; i++) + for (i = 0; i < drvdata->grp_refcnt; i++) tgu_writel(drvdata, drvdata->grp_data[i].value, drvdata->grp_data[i].grpaddr); @@ -143,21 +145,21 @@ static ssize_t enable_tgu_store(struct device *dev, CONDITION_DECODE_STEP(i, j)); } /* program the TGU Condition Decode for the desired use case*/ - for (i = 0; i <= drvdata->cond_refcnt; i++) + for (i = 0; i < drvdata->cond_refcnt; i++) tgu_writel(drvdata, drvdata->condition_data[i].value, drvdata->condition_data[i].condaddr); /* program the TGU Condition Select for the desired use case*/ - for (i = 0; i <= drvdata->select_refcnt; i++) + for (i = 0; i < drvdata->select_refcnt; i++) tgu_writel(drvdata, drvdata->select_data[i].value, drvdata->select_data[i].selectaddr); /* Timer and Counter Check */ - for (i = 0; i <= drvdata->timer_refcnt; i++) + for (i = 0; i < drvdata->timer_refcnt; i++) tgu_writel(drvdata, drvdata->timer_data[i].value, drvdata->timer_data[i].timeraddr); - for (i = 0; i <= drvdata->counter_refcnt; i++) + for (i = 0; i < drvdata->counter_refcnt; i++) tgu_writel(drvdata, drvdata->counter_data[i].value, drvdata->counter_data[i].counteraddr); @@ -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 a20de48ec3f5dd0550ed8dcdde6c2c0335981381..594d9877b81c47b2feaa8a9593d6e179a2cd994f 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 a7adfd7fdad5d491289f1b3cb3c6d67bb873cc7e..37139efd63287c715d8781a761ea1049ba0841a5 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,8 @@ 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)) { spin_unlock_irqrestore(&drvdata->spinlock, flags); if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) { @@ -1402,10 +1420,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 +1431,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: @@ -1607,8 +1625,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 +1636,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 +1655,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 +1671,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 +1681,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 0f93cfedbda41c2dcf05ea7bbaf51c124400a5d5..b3cdae32e0583687ceea27c0c7edc6c2202b2cc9 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 f99efc16a3cb638fa3d8ca3fd3dc9e5acbc9d11e..fdbd088efd4fb67be08a312a51f10ffff56ae807 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 fc6b7f8b62fb888238b83178212a16f9478564c8..a8e7502916433cf8c5d3ae792e7bbe3b9fb6639a 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 968319f4e5f101e5f1371d7701bc1493630e3d0e..24ab3cb426a7582c1f223f71a472664621dd4eba 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -175,16 +175,36 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x02a6), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Comet Lake PCH */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x06a6), + .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, + }, { 0 }, }; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8f803812ea244a023e13769447082e1f00e80437..ee6dd1b84fac82ca40a902971da6074cc3748e0e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -433,12 +433,13 @@ config I2C_BCM_KONA 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 a19fbff168617bb314acead75ab8a8196f84a853..d9401b519106920655bb0469e83868a6f258f9c4 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_high) + clk_high--; - if (clk_low) - clk_low--; + 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 7bd409eaf0acd9fa1a625ba995575d703b4cea93..d4b72e4ffd71f7b97e172f51ea95a96b0d29f8fe 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 1e57f58fcb00151e9ef274e25e3adf22f2c197b8..2bb4d20ead32b8895afc77024c1a01bfd05006ea 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,10 +502,18 @@ 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, + + 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)) + 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); @@ -515,10 +525,18 @@ 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, + + 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)) + 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); @@ -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, + + 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)) + if (dma_mapping_error(i2c->dev, wpaddr)) { + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false); + return -ENOMEM; - rpaddr = dma_map_single(i2c->dev, (msgs + 1)->buf, + } + + 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 2ac86096ddd950e2342eab0873462fedefe67853..cd9c65f3d404ff92f0eef87d4d99236b370e82cc 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 c86c3ae1318f200696f909f7563c8a114ee30995..e09cd0775ae91c60e052b22a748d870dfe037f7a 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 ac9c9486b834cc78cb71ab7be34dfcc4b68f1daa..f4e3613f9361b1e3b53d80ecd7012b63323fc863 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 ef13b6ce9d8debe8578a7af6589d102237517842..47d196c026ba6296120ce9b4f1bc7164c76fdc57 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 bc26ec822e2685d59d43ae21bdd7389b0b3b291f..dd0687e36a47ba4d37313ac749614eca305b429a 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 48281c1b30c6d56892cac6ad7388386aad586476..b8f9e020d80e6a1049ff0328788073b628f6777c 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 32affd3fa8bd1ffe462c5f66abd6b9e40ba6a36f..559c3b1284d735f743753eac9658f75cbcf63169 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 0f01cdba9d2c61517300f669af789547d06c90ad..14d4884996968bbae11d68c6d3e6f7f2535e63be 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 383c802eb5b86a3d4865d28cecde8fba5319fdff..cb8c98a440109d12154bc56e5fff8e03b8d07998 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/max9611.c b/drivers/iio/adc/max9611.c index 49c1956e6a6742f6189e3ce7dcb19cd9af4fb039..0884435eec68d2d3b477ace6d72e208407472d58 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -289,7 +289,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; } } diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 5dd104cf0939b7ca12adaf5737b79915ddb3a7a7..6e0ef9bb2497f93c42ee6107a23533908ed10f74 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 2accf6fa26a8f9fab8c161d4266a30245b791329..a71075f3a3dab862759d0a71c1dd2ac682f781cd 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 @@ -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; @@ -498,7 +515,23 @@ static int adc_configure(struct adc_chip *adc, if (!adc->poll_eoc) reinit_completion(&adc->complete); - ret = adc_write(adc, ADC_USR_DIG_PARAM, buf, ADC5_MULTI_TRANSFER); + ret = adc_write(adc, ADC_USR_DIG_PARAM, buf, 1); + if (ret) + return ret; + + ret = adc_write(adc, ADC_USR_FAST_AVG_CTL, &buf[1], 1); + if (ret) + return ret; + + ret = adc_write(adc, ADC_USR_CH_SEL_CTL, &buf[2], 1); + if (ret) + return ret; + + ret = adc_write(adc, ADC_USR_DELAY_CTL, &buf[3], 1); + if (ret) + return ret; + + ret = adc_write(adc, ADC_USR_EN_CTL1, &buf[4], 1); if (ret) return ret; @@ -513,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, @@ -541,14 +617,19 @@ static int adc_do_conversion(struct adc_chip *adc, if (ret < 0) goto unlock; - if ((chan->type == IIO_VOLTAGE) || (chan->type == IIO_TEMP)) + if ((chan->type == IIO_VOLTAGE) || (chan->type == IIO_TEMP)) { ret = adc_read_voltage_data(adc, data_volt); + if (ret) + goto unlock; + } else if (chan->type == IIO_POWER) { ret = adc_read_voltage_data(adc, data_volt); if (ret) goto unlock; ret = adc_read_current_data(adc, data_cur); + if (ret) + goto unlock; } ret = adc_post_configure_usb_in_read(adc, prop); @@ -558,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; @@ -580,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) @@ -651,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; @@ -725,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, - SCALE_HW_CALIB_CUR) - [ADC_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("ext_isense", 1, + [ADC_PARALLEL_ISENSE] = ADC_CHAN_VOLT("parallel_isense", 1, SCALE_HW_CALIB_CUR) - [ADC_PARALLEL_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("parallel_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_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, @@ -745,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) @@ -768,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) { @@ -783,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; @@ -790,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; @@ -851,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; @@ -886,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, @@ -900,10 +1153,18 @@ static const struct of_device_id adc_match_table[] = { .compatible = "qcom,spmi-adc5", .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, + }, { } }; @@ -941,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; @@ -1030,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); @@ -1047,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); @@ -1072,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 114dab743ee61c21cbeed5a2ac57089d1c559901..49cdbf114b7895ac64ba62bb3c4025e6f2caf74a 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,44 @@ 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, 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 +1328,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 e77c92faa073dcaff715674e2271578877a35693..d3db4abaf3f2e53f731fa23489287227de353b40 100644 --- a/drivers/iio/adc/qcom-vadc-common.h +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -83,6 +83,13 @@ #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 -60 +#define DIE_TEMP_ADC7_SCALE_2 20 +#define DIE_TEMP_ADC7_MAX 160 + /** * struct vadc_map_pt - Map the graph representation for ADC channel * @x: Represent the ADC digitized code. @@ -144,9 +151,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 +181,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 c52d20f7ca2ed46694ab1ee938ebaebacc7f660e..0409dcf5b04796643806cb164010171d497dcd42 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/mcp4922.c b/drivers/iio/dac/mcp4922.c index bf9aa3fc0534e6786c5f5fe605cfd43b9a383690..b5190d1dae8e33a15e148ff78471fb09dd4b90d1 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 066e05f920810c74afdac0b535046af455c67388..ff6666ac5d68b7493564535b7c3139d6db258dd4 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 a27fe208f3aed80a949f50e98be96dc1f1a8f92e..ea24701beb3ea8f985846effcb79a0a1c46f3153 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 76643c5571aa87286bef0eeee0a557f04326ac6b..e59d0438de7326be74b3c225eb2247fbf6bdde0f 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 5483b2ea754dd37c624fbe513b5bf721266cf88b..d2fe9dbddda74e5937cc767649d555458b8d553f 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -13,8 +13,8 @@ config INV_MPU6050_I2C 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 @@ config INV_MPU6050_SPI 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 d80ef468508a19763ccbe0a70be5397158440c77..6b560d99f38518d8a7d43e21027a6c5cf4aac718 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 dd758e3d403da9c8a14dc41d4da2b0accc7e1779..e46eb4ddea210bc2265122264989dffccda00678 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 e69a59659dbcf9036c24abe96b8a4ef314a89143..220eba58cfbb6d43d8f85c28bf237caca60dd62a 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 548e042f7b5bde8b508675b23f2070a78bef14a9..0e54f2d54bd70cc75815dbf2f7c2c7a33eaad2a2 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 227f50afff22f9c5329aeca129364e8770088dfb..a112c3f45f748fc930714c64bb422fb9743d1d07 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/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 09c7b9c095b07f4aaf24a15b89a02ab2d29ceb98..0428a0dfcbd4a99ea34710c12f1cb7802a126901 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 6257be21cbeddfd932a5cb116bcbbaf07825bbe2..1f373ba573b6dceb78a7e41bc33f0b4b470296f8 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); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 6d8ac51a39cc05600e6ea97b50ed62b13319e778..6a585c3e2192364e3a0f970a8ca29b3b5e3ba103 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 74aa3e651bc3c61e2c89a41b1117820d5a8de3e1..218411282069b2a8056304a226e6a9dcaa46259d 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 5df8e548cc146066879d6470a8482412d4f17ab3..4a14de2d8c716476fdf1e3fb10fd471629e4faee 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 96f76896488da6c0414411fea440520e75bf2843..802942adea8e85d60356854cb49cda90d330134e 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 22bd9784fa2eacec2826c59f35ba7170ac375f8e..589b0d4677d52dcefe03cebdc5eb60d9de145b8d 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 6637df77d236554a176709b7d18423387dbc07d0..8b3b5fdc19bbbbbcbf46b9ecbaf2ed3b2c5309fb 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 4097f3fa25c5f71e1660bb3d4d5074b99d3331ca..09e7d3dd30553bdd038a1fe268e6faba2208ea13 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 3be6405d9855ecdf86ae4e6e94677baa474027e4..4dcc92d11609728b1bc3136068ffb6730ff04693 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 6d304279409427910f03a620dae31080aa49823b..1fd8798d91a737b1b34786ce90fe7fad5f01a148 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 347fe18b1a41c0990c753aa29f2efaaea0cd119b..a9e3a11bea54af54f0de1dcdae62be93b581194d 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 6aa5a8a242ffddb2cd9494932076a995dbb78310..6b89353611333169363c37c2bc80eec92f4e3220 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 f208a25d0e4f5d8e387d630938a39e9bb0dba567..1669548e91dcf5e86a5ed936ca3a5ddf58ffeef4 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 6c967dde58e702c228835a84c3b1ea19abc1ea64..a8dd12e525f8126d65d93023d8bbac9f52723efb 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 d648a4167832c61cd02a071b0de11139cb8c3dc9..291c12f588b58b23e9e1b006ea5a0d862cd66df1 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 cbff746d9e9deddc5f7140ebed61376afbfb0eaa..684a298e150370af34024410ffdee906073c2b3c 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 1ad38c8c1ef97e2250f231b10f56c6d16f42abd6..4e7b3c027901bd21fec925ef029cb471a1cc887c 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 c3c96c5869ed454680b619caef88a3fd369d5579..718dcdef946eeecd852a903cb89de1a7ae642c6b 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 fddb5fdf92de86b94afaa63f877698a5cb97ed9e..21c2100b2ea98ddf8cae4e3f43f581fc079620d4 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 9a24fd0ee3e78c5b7574b8ec7cc99b56ea32a729..ebfb0998bcedba7ee7505a41664573c3d5d598fc 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 e8850d59e7804caa45dd5e2cd77b140c7bfd7047..a94444db3045a38f6172e7c57e1330f9d98eda86 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 a442b29e76119ed5dd7e2ef366f3265b945a0783..7021444f18b46d0bd539586e6bfd45582cab038e 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 14aa308befef913d6c62d6c21c345f7c45ccbc87..2c3e600db9ce77e05cd5dfd1682d16d0a9b38c76 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 41a538d23b802456c81504e2a4adc96f1c73e2db..c68596d4e8037e818daec2dba7e5ab35a0716035 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 2fa4fb17f6d3c64b4c7bfbc94a7ea562eec96ea5..af24698ff22627c78b26e510484327e906e7dd69 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 #include #include #include @@ -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 423818a7d3330d2bd8598c40e672312e81a69f3e..771eb6bd0785482beb9b9ae031de94c6cb1b93ea 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 db4aa13ebae0c693138bdb51832961e2a1d492ba..d1de3285fd8856ddf255e44e82f6c556c67aad84 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/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index e219093d2764536a58c7c362a08167361ed0e93a..d2da28d613f2cc41397de8f2a9d7af99510aaa54 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 c05eae93170eb738e37fa968eede41207782e4b1..f4ffdc588ea07056b7ee2ffbf9013b7112fd1b13 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: diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 941d1df54631afb0e283a83e75e18fec9e734f46..6a060c84598feb61a75d45b7d50e9e20c7fd351e 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 77b1f3fd086ad754564627d4f465b155767ef2f3..ef0f710587ad849e134f2a48e63d943f1937753a 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 f3e80dec13344b03ac8b4d93e6486976f060d814..af7f2083d4d1a3b554b66273449751227522a8f2 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 a0af6d424aeda58b54c58146f9761e782713e89b..d1680d3b5825076ddd47ab2df24329cbe5e5662d 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 505fa36487629ce003b1ac78a2414efbe18e4195..93b16237b76774c986580792b7160cd7a9c85c3e 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/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c index d0723d4aef5c980e69d2a1218cd7ac0e8062f9b3..7424e88b0d9184d14fa5cd3ac9b0f53bb07fcf16 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 ca2638d8f35ef182d9feb4bb8e6b4362d17e37bd..d831f3e61ae8ff6b15f5c734098a702746f1f34f 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 b65d10b0a87591e8d1049d95806b2208cc2120fd..f4cb5cf26006f00316e76299ce60fe452760216b 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 83311dd07019b81bb65fbd4aa9810dbc6f352f8e..ea089cb091ade455029956a12dada273ac2d1092 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 6aeb7a165e46919c1c3651d72776fd05f03173b1..ea4542a9d69e68523558455c138412f9b6341684 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_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index fa98a52796470a5e1fefed778e3f82c1aa94cfe8..f7dd8de79941567d3f9c166bdd11c50515c6ee88 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) diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c index 0d6c04ba7fc36c922e3f6733d4093e9a8cd9753c..c41a5fee81f711e183a43630150bff2a60da4708 100644 --- a/drivers/infiniband/sw/rxe/rxe_srq.c +++ b/drivers/infiniband/sw/rxe/rxe_srq.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #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 3b731c7682e5bcadf874e2b0f475ca92591beee8..6a75f96b90962db0806613b7e31eba6d66d5a986 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 78dd36daac00ec29889844f75330ef66dac583ac..d8cb5bbe6eb5859a9205fd7b05610024706bf9d8 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/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 2f6388596f886c262222ec8feefefca23b83b1db..96af06cfe0afd9386235007a762eff92197b42a4 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 267da8215e08fe71b9d4920e1accf851d7723538..31cd361416ac9e1fc21d2d103c0743369eb34251 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 bc6a44a16445c17422970faa9e9432e77c121ac4..03ee53adaacd2e1bcd1637dec54c06cf093eed13 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 2743ed4656e4a25387da3b3b40ec1f0b862d990a..1cd23bf3236c2e577317b68c69cc86b2ab832cc2 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 28b473f6cbb631e53d3268467a118ffabf6a9b6f..092096ee06b96c0cf6abe64e0fbc6d0fc624e906 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/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index 577d57b63f45c45fd1e53c9fe6907420f430fb67..d4a5d1ef34e1f7694b44bce844d588eddcfe150e 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.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 @@ -682,6 +682,8 @@ int qpnp_pon_system_pwr_off(enum pon_power_off_type type) } } +out: + spin_unlock_irqrestore(&spon_list_slock, flags); /* Set ship mode here if it has been requested */ if (!!pon_ship_mode_en) { batt_psy = power_supply_get_by_name("battery"); @@ -694,8 +696,6 @@ int qpnp_pon_system_pwr_off(enum pon_power_off_type type) dev_err(sys_reset_dev->dev, "Failed to set ship mode\n"); } } -out: - spin_unlock_irqrestore(&spon_list_slock, flags); return rc; } diff --git a/drivers/input/misc/qti-haptics.c b/drivers/input/misc/qti-haptics.c index 81606e111533cbc2985c0ea30fd2826a6cba35a7..6b89a10f8dc99690a64a3cee0dc87434ec63229a 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 @@ -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 06cebde2422ea7589928351da4b117edcd0239e9..e8d1134943c4fb155e3246d2c06d236fdabe7a26 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 93901ebd122a504e7e96c35a17c100ae1ea607e3..c8e07ea2422b264253d233b4d91486f0c5300043 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 5c7f489157792bf32da34e982b715824ec17eaff..9066f2b70ff0e49c9be1ae9568c6120f099046b0 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 3991d2943660c56d189fba623efbe014ee0d586b..099dde68e332dc2a1448232543bdecd6de1b38e2 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 a6f515bcab2228a8783f10dbf10fae30462fd852..539a47425fcd964367466179997b0e6faa93671b 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 b6ccf39c6a7bb46bc67bf086952a799e27f4f930..4b2466cf2fb1c9181ce6038750a9586f10ee983a 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 49d8d53e50b7bd105ceeca5150dda23f2f3771d7..96f9b5397367fca2388b3186296611ae7287dec9 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 0b8a25c58d02e65865aabfceca26444c93b7aa4a..654252361653df1cb61d1ffd105d5ed4aa7fca71 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/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 727c3232517cdbded6488e353e30b203c2110650..c84ee739a8d50cd4e811dac9b5c4aae9db728172 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 0000000000000000000000000000000000000000..aeb312bc92c1f116cfbe61d4a0b502de2d559739 --- /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 0000000000000000000000000000000000000000..d77fef873842dcb18069d3cb3a31659ec8a49b98 --- /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 0000000000000000000000000000000000000000..67415c4d9f3cec08620b6d88c5cbfdc41dc75f56 --- /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 0000000000000000000000000000000000000000..d98222ce067a349459c2ed0e8fff0d6bdfeacf37 --- /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 0000000000000000000000000000000000000000..f374d99ab50ec9c357e726823aa9341f57eb3444 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -0,0 +1,1830 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM) +#include +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#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[3 + 1] = {NULL, NULL, NULL, NULL}; + int count; + int ret; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + ret = of_property_read_string_array(dt->parent, prop, + (const char **)&active_tp, count); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + if (!of_device_compatible_match(dt, active_tp)) { + pr_err(" %s:no match compatible: %s, %s %s\n", + __func__, active_tp[0]); + return -ENODEV; + } + + return 0; +} + +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 0000000000000000000000000000000000000000..7603a64b9d0296c69af265fe5ccecbcbc89cb36f --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..295f91d2f816c1f2dd01fd94a65c1f7a7f2dec2f --- /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 0000000000000000000000000000000000000000..b9ae3f1246f9ae3fa129265e78809421bfc15690 --- /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 +#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 0000000000000000000000000000000000000000..4727744b6b599f0b1ca1ec9d66fe91553733e6ba --- /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 0000000000000000000000000000000000000000..e6ab5113b628ab29f7b4c941b8494757ec0fc48d --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c @@ -0,0 +1,2012 @@ +/* + * + * 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[] = { +#include FTS_UPGRADE_FW_FILE +}; + +u8 fw_file2[] = { +#include FTS_UPGRADE_FW2_FILE +}; + +u8 fw_file3[] = { +#include FTS_UPGRADE_FW3_FILE +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +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 0000000000000000000000000000000000000000..398787e8a6903619cbabacffc1db257aff36aad5 --- /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 0000000000000000000000000000000000000000..43deb4a87200cf6ec5aba4f14ba0238f93a8c963 --- /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 0000000000000000000000000000000000000000..a7b0b5b61141524e6120040878f3aba3a4c884e9 --- /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 0000000000000000000000000000000000000000..92fe990967608e865ee577f452fa554ec5a5467a --- /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 0000000000000000000000000000000000000000..f4e74af5535c29fb5e833e3c4aeb361616457fc3 --- /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 0000000000000000000000000000000000000000..0f626408a9bf4158c61db877936e9d2c90cbcc16 --- /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 f2d9c2c4188558a38cc8458e4561d4ead7d7a5f3..b20ba65992735253bf71ebce5a10ab2ea1b14d40 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -127,6 +127,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 = { diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index e5c3b066bd2a19747143c7e7cdf9f184fe92dd2b..06f0eb04a8fd4b2d331d084b885516de9e3bc30d 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 d5dfa4053bbf2359c94bef8748cab1cda367aaf4..b71673911aac303bbc61e34c1375582c6e7dbddf 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 78c687df4fe673f37db1579fc8d4f1344d6caece..b217f5b6580f4d4ce443577883e11fa66031c439 100644 --- a/drivers/input/touchscreen/synaptics_tcm/Kconfig +++ b/drivers/input/touchscreen/synaptics_tcm/Kconfig @@ -89,7 +89,7 @@ config TOUCHSCREEN_SYNAPTICS_TCM_TESTING 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_REFLASH 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 858fa15f285bac6bc1dd58a72265a03ea97ac220..76efa405599b36b9ae7945a825bf5e84a44b0045 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 01d5f080ca24ff2e92a821291a7b0b563d7db2dc..91c2fe654fbd2f7185c95d906e1915da2f0d4155 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -367,6 +367,14 @@ config SPAPR_TCE_IOMMU 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 @@ config ARM_SMMU 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 @@ menuconfig IOMMU_DEBUG 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 2d4b320043dc141bc43ef2d6eee8cb2593d87e43..5a59ed1024ee88b30ccb170893d7bbcbb193f9c1 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -34,4 +34,5 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o +obj-$(CONFIG_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 1f2ed44de243865d48b07a2a8d06b008a65cb837..9991386fb70000f02e4ab0b345ab5c74fcfed2f3 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 c235f79b7a200c90f8b8d4325a61ce4373de8d86..5120ce4fdce326b6b59185d66e7ba8cb146bf4d5 100644 --- a/drivers/iommu/amd_iommu_quirks.c +++ b/drivers/iommu/amd_iommu_quirks.c @@ -73,6 +73,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", diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index ec180b2f4f417ddafc03141fbcd5a342c90c626d..52e26864742169111f822a5ccbe0b765df0e9f61 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 fbdbdaeb9ee2dcb694cb1968863b21c861272483..a168ce56d5a6fe470f005873a6a93920ce646a6c 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 #include @@ -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,7 +2226,7 @@ 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; @@ -2325,6 +2334,8 @@ 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); out_unlock: mutex_unlock(&smmu_domain->init_mutex); return ret; @@ -2448,6 +2459,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); } @@ -3036,10 +3048,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)) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index aeccbf3b0eafdc9ddd0b072fbe959cc510e33b7e..5f2cba70ebef84d2897367f47abd6c069dcbc0aa 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/iommu-debug.c b/drivers/iommu/iommu-debug.c index dee31adeab2a2cde6d70f2fb6cb86fa0bbdbc825..c90712ac5352e4ce45da9797d4a4db6bd9994967 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 @@ -1448,9 +1386,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 +1722,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 +1775,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 +2292,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 d21a7e0ae118e6bfd2b1fc98dc55bde6a3d9f24a..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..15426405246fae60d498839503060b856e633cc7 --- /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 +#include +#include + +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 0000000000000000000000000000000000000000..3afe1872a201252674afd0c31fa4316e71bac453 --- /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 ab8047cedce156d1762149c376b76812a5869fe1..436d0470fda8221800cae165bae6a5a4cd0e7e83 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -35,8 +35,6 @@ #include #include -#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/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e7549a2b1482bef4628de7724dc2de5aaac0fde1..050d6e040128d3bc3d8a4b9d9e6ef88950000ae6 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-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c index 13063339b416dac51bf84e77662aa28d70080be7..a2a3acd744911014cfe46389151b68d03b54504c 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 21786a44236877ee35f24bbc04c2c1838f507cb2..c67fd2fb333c13677796c5efa35e2cfd43a396d6 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 eade36dafa3408f192b618cd1c52323235ed2d34..4c239f18240db8bc2944ab80eae983508de3b902 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 12d9e5f4beb1f81c5aa5e5af81bc9aca61c21668..58635b5f296f0cd10284ea01ba94b9c71c613082 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/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 136f86a1627d18cf396990ca1a4122d17578d0af..d5e774d8302158179110d40d5b6aa10ddc68f804 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 80710c62ac29313073399cc16b5c87d01cc81759..8dce31dbf2cbcf0c037d0688aa239652c37c5753 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 537e98f2b24a2d67b4b23b8c4b9a135672d27848..88b632787abd65dda14ea979ad914640bcc2c451 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 8d2ed510c04b31d4a2ba7439840cc3fddfa6aa50..bdc86ee4c77935fa4f68be4bca2dbd567ed6a961 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 da7f4fc1a51d17b0824c661d4cb473cf5f4165c4..a0f61eb853c55155e6f4f2df37f7bb2c53a1381c 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/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 58bfafc34bc46978121d81c63662677392499f20..129b3656c453a68eb295b859baac6a26eac86a65 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/msm_qmp.c b/drivers/mailbox/msm_qmp.c index 96384d420455c8e88a5e2663001b3853a04e056e..726f65677b5b13f98dd4a6ed947cb690c2c1b8d6 100644 --- a/drivers/mailbox/msm_qmp.c +++ b/drivers/mailbox/msm_qmp.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 @@ -377,6 +377,7 @@ static int qmp_send_data(struct mbox_chan *chan, void *data) struct qmp_pkt *pkt = (struct qmp_pkt *)data; void __iomem *addr; unsigned long flags; + u32 size; int i; if (!mbox || !data || mbox->local_state != CHANNEL_CONNECTED) @@ -398,13 +399,15 @@ static int qmp_send_data(struct mbox_chan *chan, void *data) memcpy32_toio(addr + sizeof(pkt->size), pkt->data, pkt->size); iowrite32(pkt->size, addr); + /* readback to ensure write reflects in msgram */ + size = ioread32(addr); mbox->tx_sent = true; for (i = 0; i < mbox->ctrl.num_chans; i++) { if (chan == &mbox->ctrl.chans[i]) mbox->idx_in_flight = i; } QMP_INFO(mdev->ilc, "Copied buffer to msgram sz:%d i:%d\n", - pkt->size, mbox->idx_in_flight); + size, mbox->idx_in_flight); send_irq(mdev); qmp_schedule_tx_timeout(mbox); spin_unlock_irqrestore(&mbox->tx_lock, flags); diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index ca1f993c0de306ac0352307f966266bb2d03ae7e..e31322225e93934d6382abdba527c62093c9e032 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/debug.c b/drivers/md/bcache/debug.c index 06da66b2488ae8f5371fcfeb92c7b336d24e78ca..8c53d874ada4aa92526d328dd93203c400564254 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 2321643974dab5e7f1d23666405aab008c0cc440..14d381cc6d7473148a89e3deb7d8435f8263226a 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; @@ -1004,6 +1009,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 +1491,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); diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 5bb81e564ce8827869b12f5da6f6fbf6a2ac5d90..3e8d1f1b562f8be02f99ac43b382ec4e5333980d 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 ba5395fd386d562ac070158fa03666e9197e5ce7..b5fc3c6c7178e00212cb5d78d9f2acb3e4e8ab69 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 b86d2439ffc76fea17d07aca9b2df8839f074b42..2fcf62fb2844f467326c653ae20f59caf4b9d8b8 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 481e54ded9dc7a3e0651a0115f823187c328f3c4..def4f6ec290bef28fb295f077b12c8b193ec3aea 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 b78a8a4d061caf0b6f396b66e4a7f9a5478a5323..23de59a692c515ea318391b597bba033f3d34e95 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 36805b12661e173be5d2a845626f82baaf8de690..d3f28a9e3fd950f432f55c81387adda025dce574 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -19,7 +19,6 @@ #include #include #include -#include #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 5f1f80d424dd370f306e15033e12bb77de72f3b7..503c4265ecbe106eaca30abbd7bdc4463952afc2 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 7e8d7fc99410dff89b1fbd7380dbed5bb97c4a27..c2c17149d9687b3a595ab64577b59c429cea81ea 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 9470b8f77a337bb22e639ea8e45defc12cb59cb2..84ac671acd2e170196c97ebdd14ffc884720850e 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 3dd668f6940512903b9b76d6d642fd43a850349f..971a2da43cbfc31818dff01a17952eb8a528e0a2 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,31 +566,51 @@ 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. */ @@ -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 93a64529f21902968a705c6ea88a292ad6161d24..2662746ba8b9dadf29e6b1e8dcdafbe84f42b1d8 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-linear.c b/drivers/md/md-linear.c index d45c697c0ebe7cede53a25618e30a4398390202f..9a60231b844be161299ec8f53150519c645c2b75 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 881487de1e25af5e994c8770e255b74b3864f513..80c35bfd11b8dd83440fcec1ef4846423b3ef6ca 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 a8fbaa384e9ae5174327f57daafb24616f92c5f8..62f214d43e15a359b668910db4cc9a3856be3432 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 4f89463e0b01e6144c4327f0a86cc7259c065ac8..cce62bbc2bcba620735d1f1d3cb2c34d0e2d79a0 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 21ea537bd55e9984f7cfe5b908a3d6bcad9038e9..eff04fa23dfad46d7d43dee24cde0a1fd90f2f68 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 3cafbfd655f5df859c066af0ecd3cc6e84b83496..2e7140507f93007d1a852b48c78dbe9ab9681ab2 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 6929d110d80488e72bce1d8830289cbce904e434..6800dcd50a11b4626e626a3d271304054ef6b565 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 diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 25e97de36717ad84509a6376b134644dc2d41312..02c5e390f89f32ca26095dd015d4b09f01843c59 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 4a5aad26ded75db670a25efbf0897c1d03d7e519..01021382131bcf30f3f238ccd0f67b64972a207c 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 4a15d53f659ecf8496bc28c38a02780b66f30b17..dd6f3e6ce00628abb8ffe37b7c3fc5c6bec83441 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1437,6 +1437,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 0496d93b2b8fa289455ab0be94f48b1b390269c9..8f987bc0dd883096c29851602d03ca2c62411a05 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 24f80f214297f294b7e0f22cf3b2fb3feec9ae2d..32159486d3cac9c54e995362f8a4332abbf4969c 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 6ca88daa0ecd79dad07c16bf7686c78089f15e13..65c3024c5f76f1588f6ab5f4108ca06e8109afc7 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 469be87a3761feb5b775e4cf26aeac2600987f83..556e13c911a62842724096afcfd410c340f4d344 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 65f83741277e1365cdf723a1221df693562da55e..1cf46c401664d5dfade36847ebe5910589c9ad0e 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 91fae01d052bf5568cd646d6bf521062182a43ca..3dc2100470a1cdf5ecb5c2c1115c921d10be09b8 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 8ba3920b6e2f470e2a3b514fa2e1ea2ccd3e7ff6..5477ba326d68108afba9a5741180c194d0ecc5dc 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 a66f6201f53c71e73306ff3d1942d47ba08d6fef..afd66d243403b31adfa75c6e1123f99bce0ffad2 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/ov2680.c b/drivers/media/i2c/ov2680.c index f753a1c333ef9b7f10963fca1f1f7b7817900997..d8798fb714ba8a6d3cbf44769ecd61c440aff2a0 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 385c1886a9470a1ad4153b61493501803be5ee5c..98a1f2e312b58249069de19f7b3c78dc4bf4e85d 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 a3bbef682fb8eacd35929701e10c3fffd154cc61..2023df14f8282e00fff07e7f4cc30581bca73edf 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 7b7c74d7737075c3e2dd5402342afd08edf40cc7..53dd30d96e691308288515fffe7f6913bbb1042d 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 9a80decd93d3c0ed5e011734dc8ea5c4211085a2..5d107c53364d64249374431d7c1a236ead06e4b4 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/ov772x.c b/drivers/media/i2c/ov772x.c index 7158c31d8403be27483c6e1afa011ba8dc1912b2..4eae5f2f7d3183d22cca5c42377b935d8aaf4a78 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 8a6a7a5929aa35ce32a3be4fb98b9ab9f973177f..7804013934ab5f314623779109362c012a777a6d 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/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 0c389a3fb4e5f0fb9bdaaa75128e0a177898ba10..e64f9093cd6d394aaa9d455eb526f66e696a6504 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/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 44936d6d7c3967e28c9d1dcbaa722cf7e380c39e..1380474519f2bf045cae322ee0fa84414289397d 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 8001d3e9134e440836fdf39b5b3624fd75b5ebe7..db2a7ad1e523195b97c07c0c473a5cd279c80f2c 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/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index d89e14524d427faa94a04f2ae9402d621788270f..1fd0782576705b08e4078ffc3e47d2b246f78b4f 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 4b0220f40b4253d0739553e51185fd9dc9d23484..fccc771d23a51797f8b6158d6993d6c29ebead4d 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -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 2469ca1dc5985d98b6322b3f91d7a378af8b535d..8df02c32781ee2ba0c836474cf749c2236990d2a 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -16,6 +16,7 @@ #define __CODA_H__ #include +#include #include #include #include @@ -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 f924e76e2fbf869d859c6cea46c6f8945b368a02..340f8218f54d307cd582ecf645ca2228dc0cf067 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 b0eb3d899eb44bfcca33eac2b50194eeb2b70838..6f82693524331e13a5aec76ca4003590c0fb7a4f 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.c b/drivers/media/platform/msm/cvp/cvp_hfi.c index d1e10c07c54ad3b993f73f055f1e8967de52bdc1..25d71f6dde4aad74686dfe8bd80849c44fdc6a16 100644 --- a/drivers/media/platform/msm/cvp/cvp_hfi.c +++ b/drivers/media/platform/msm/cvp/cvp_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 @@ -1423,9 +1423,9 @@ static int iris_hfi_flush_debug_queue(void *dev) return -EINVAL; } - cvp_dump_csr(device); mutex_lock(&device->lock); + cvp_dump_csr(device); if (!device->power_enabled) { dprintk(CVP_WARN, "%s: iris power off\n", __func__); rc = -EINVAL; @@ -2349,34 +2349,6 @@ static int iris_hfi_core_release(void *dev) return rc; } -static int __get_q_size(struct iris_hfi_device *dev, unsigned int q_index) -{ - struct cvp_hfi_queue_header *queue; - struct cvp_iface_q_info *q_info; - u32 write_ptr, read_ptr; - - if (q_index >= CVP_IFACEQ_NUMQ) { - dprintk(CVP_ERR, "Invalid q index: %d\n", q_index); - return -ENOENT; - } - - q_info = &dev->iface_queues[q_index]; - if (!q_info) { - dprintk(CVP_ERR, "cannot read shared Q's\n"); - return -ENOENT; - } - - queue = (struct cvp_hfi_queue_header *)q_info->q_hdr; - if (!queue) { - dprintk(CVP_ERR, "queue not present\n"); - return -ENOENT; - } - - write_ptr = (u32)queue->qhdr_write_idx; - read_ptr = (u32)queue->qhdr_read_idx; - return read_ptr - write_ptr; -} - static void __core_clear_interrupt(struct iris_hfi_device *device) { u32 intr_status = 0, mask = 0; @@ -3300,8 +3272,7 @@ static int __response_handler(struct iris_hfi_device *device) *session_id = session->session_id; } - if (packet_count >= cvp_max_packets && - __get_q_size(device, CVP_IFACEQ_MSGQ_IDX)) { + if (packet_count >= cvp_max_packets) { dprintk(CVP_WARN, "Too many packets in message queue!\n"); break; diff --git a/drivers/media/platform/msm/cvp/cvp_hfi_helper.h b/drivers/media/platform/msm/cvp/cvp_hfi_helper.h index 84ea89b7f92fd6daa615f233fecefe6bd8a2a95a..f8fe274ed70da32bd93f54d6303ce2a0db6f8ecd 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 + #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 fc4b075e37346b6aba704699606b391c0e5d5744..e6ef20647100638540df08b05d53ed3ba75dcb60 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 @@ -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 c984d88a4791ff5f6a8989cc9369a8027fd0b2b7..e2a3cb6b17036c0888733dd1f85834b6d75cbd24 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 #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 @@ -1623,7 +1634,7 @@ static void aggregate_power_request(struct msm_cvp_core *core, static int adjust_bw_freqs(void) { struct msm_cvp_core *core; - struct iris_hfi_device *hdev; + struct cvp_hfi_device *hdev; struct bus_info *bus; struct clock_set *clocks; struct clock_info *cl; @@ -1631,12 +1642,12 @@ static int adjust_bw_freqs(void) unsigned int tbl_size; unsigned int cvp_min_rate, cvp_max_rate, max_bw, min_bw; struct cvp_power_level rt_pwr = {0}, nrt_pwr = {0}; - unsigned long tmp, core_sum, op_core_sum, bw_sum; + unsigned long core_sum, op_core_sum, bw_sum; int i, rc = 0; core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); - hdev = core->device->hfi_device_data; + hdev = core->device; clocks = &core->resources.clock_set; cl = &clocks->clock_tbl[clocks->count - 1]; tbl = core->resources.allowed_clks_tbl; @@ -1691,22 +1702,24 @@ static int adjust_bw_freqs(void) return -EINVAL; } - tmp = core->curr_freq; - core->curr_freq = core_sum; - rc = msm_cvp_set_clocks(core); + mutex_unlock(&core->lock); + rc = msm_bus_scale_update_bw(bus->client, bw_sum, 0); if (rc) { + dprintk(CVP_ERR, "Failed voting bus %s to ab %u\n", + bus->name, bw_sum); + goto exit; + } + + rc = call_hfi_op(hdev, scale_clocks, hdev->hfi_device_data, core_sum); + if (rc) dprintk(CVP_ERR, "Failed to set clock rate %u %s: %d %s\n", core_sum, cl->name, rc, __func__); - core->curr_freq = tmp; - return rc; - } - hdev->clk_freq = core->curr_freq; - rc = msm_bus_scale_update_bw(bus->client, - bw_sum, 0); - if (rc) - dprintk(CVP_ERR, "Failed voting bus %s to ab %u\n", - bus->name, bw_sum); + +exit: + mutex_lock(&core->lock); + if (!rc) + core->curr_freq = core_sum; return rc; } @@ -1743,6 +1756,7 @@ static int msm_cvp_request_power(struct msm_cvp_inst *inst, inst->cur_cmd_type = CVP_KMD_REQUEST_POWER; core = inst->core; + mutex_lock(&core->power_lock); mutex_lock(&core->lock); memcpy(&inst->power, power, sizeof(*power)); @@ -1763,6 +1777,7 @@ static int msm_cvp_request_power(struct msm_cvp_inst *inst, } mutex_unlock(&core->lock); + mutex_unlock(&core->power_lock); inst->cur_cmd_type = 0; cvp_put_inst(s); @@ -1787,9 +1802,11 @@ static int msm_cvp_update_power(struct msm_cvp_inst *inst) inst->cur_cmd_type = CVP_KMD_UPDATE_POWER; core = inst->core; + mutex_lock(&core->power_lock); mutex_lock(&core->lock); rc = adjust_bw_freqs(); mutex_unlock(&core->lock); + mutex_unlock(&core->power_lock); inst->cur_cmd_type = 0; cvp_put_inst(s); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_internal.h b/drivers/media/platform/msm/cvp/msm_cvp_internal.h index 9a5d136ea1670f28002ac588a7453082db6c4cd0..0a27a827be3e9bc7085bc0499eaabe4a699f5733 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; @@ -298,7 +284,7 @@ struct cvp_session_event { struct msm_cvp_core { struct list_head list; - struct mutex lock; + struct mutex lock, power_lock; int id; dev_t dev_num; struct cdev cdev; diff --git a/drivers/media/platform/msm/cvp/msm_v4l2_cvp.c b/drivers/media/platform/msm/cvp/msm_v4l2_cvp.c index 8c37858d19f8e422a09105196c9838dd5c92da16..b9391f520dc02fdf2a32535599ac28526b20e5a3 100644 --- a/drivers/media/platform/msm/cvp/msm_v4l2_cvp.c +++ b/drivers/media/platform/msm/cvp/msm_v4l2_cvp.c @@ -124,6 +124,7 @@ static int msm_cvp_initialize_core(struct platform_device *pdev, INIT_LIST_HEAD(&core->instances); mutex_init(&core->lock); + mutex_init(&core->power_lock); core->state = CVP_CORE_UNINIT; for (i = SYS_MSG_INDEX(SYS_MSG_START); @@ -487,6 +488,7 @@ static int msm_cvp_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &msm_cvp_core_attr_group); dev_set_drvdata(&pdev->dev, NULL); mutex_destroy(&core->lock); + mutex_destroy(&core->power_lock); kfree(core); return rc; } 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 b58333b4d119d19648f28fc2f76a9eea1929edac..d4614dd765afbaac4bd6fe93065a4f0cb1f5e1d9 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 @@ -17,6 +17,7 @@ #include #include #include +#include #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) || (sgt->nents != 1)) { + MPQ_DVB_ERR_PRINT("Not a contiguous 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; + + 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; - desc->virt_base = dma_alloc_coherent(&mpq_demux->pdev->dev, - size, &desc->phys_base, - GFP_KERNEL); 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) + 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; + + 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; - desc->virt_base = dma_alloc_coherent(&mpq_demux->pdev->dev, - SDMX_METADATA_BUFFER_SIZE, - &desc->phys_base, - GFP_KERNEL); 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_host_ipc.c b/drivers/media/platform/msm/npu/npu_host_ipc.c index 573f272e09cdb18972f5fc3ee3b27edecd4ef3d3..0d2816fa4036ea196f463764220dd219b36faf16 100644 --- a/drivers/media/platform/msm/npu/npu_host_ipc.c +++ b/drivers/media/platform/msm/npu/npu_host_ipc.c @@ -217,7 +217,7 @@ static int ipc_queue_read(struct npu_device *npu_dev, */ queue.qhdr_rx_req = 1; *is_tx_req_set = 0; - status = -EPERM; + status = -EIO; goto exit; } @@ -232,12 +232,8 @@ static int ipc_queue_read(struct npu_device *npu_dev, target_que, packet_size); - if (packet_size == 0) { - status = -EPERM; - goto exit; - } - - if (packet_size > NPU_IPC_BUF_LENGTH) { + if ((packet_size == 0) || + (packet_size > NPU_IPC_BUF_LENGTH)) { NPU_ERR("Invalid packet size %d\n", packet_size); status = -EINVAL; goto exit; @@ -313,7 +309,7 @@ static int ipc_queue_write(struct npu_device *npu_dev, packet_size = (*(uint32_t *)packet); if (packet_size == 0) { /* assign failed status and return */ - status = -EPERM; + status = -EINVAL; goto exit; } diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c index 1b666e1f73afdc194570c7ad581bce05f10e0b89..43c801403f525561156110d71eb9231fa84d3c9e 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. */ /* ------------------------------------------------------------------------- @@ -154,11 +154,10 @@ static int load_fw_nolock(struct npu_device *npu_dev, bool enable) } /* Keep reading ctrl status until NPU is ready */ - if (wait_for_status_ready(npu_dev, REG_NPU_FW_CTRL_STATUS, - FW_CTRL_STATUS_MAIN_THREAD_READY_VAL, false)) { - ret = -EPERM; + ret = wait_for_status_ready(npu_dev, REG_NPU_FW_CTRL_STATUS, + FW_CTRL_STATUS_MAIN_THREAD_READY_VAL, false); + if (ret) goto load_fw_fail; - } npu_host_ipc_post_init(npu_dev); NPU_DBG("firmware init complete\n"); @@ -425,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) { @@ -527,7 +526,7 @@ static int npu_notify_fw_pwr_state(struct npu_device *npu_dev, reg_val = REGR(npu_dev, REG_NPU_FW_CTRL_STATUS); if (reg_val & FW_CTRL_STATUS_PWR_NOTIFY_ERR_VAL) { NPU_ERR("NOTIfY_PWR failed\n"); - ret = -EPERM; + ret = -EIO; } } } @@ -691,7 +690,7 @@ int npu_host_init(struct npu_device *npu_dev) host_ctx->wq_pri = alloc_workqueue("npu_ipc_wq", WQ_HIGHPRI | WQ_UNBOUND, 0); if (!host_ctx->wq || !host_ctx->wq_pri) { - ret = -EPERM; + ret = -ENOMEM; goto fail; } else { INIT_WORK(&host_ctx->ipc_irq_work, npu_ipc_irq_work); @@ -918,10 +917,10 @@ static int host_error_hdlr(struct npu_device *npu_dev, bool force) host_ctx->err_irq_sts = 0; /* Keep reading ctrl status until NPU is ready */ - if (wait_for_status_ready(npu_dev, REG_NPU_FW_CTRL_STATUS, - FW_CTRL_STATUS_MAIN_THREAD_READY_VAL, false)) { + ret = wait_for_status_ready(npu_dev, REG_NPU_FW_CTRL_STATUS, + FW_CTRL_STATUS_MAIN_THREAD_READY_VAL, false); + if (ret) { NPU_ERR("wait for fw status ready timedout\n"); - ret = -EPERM; goto fw_start_done; } diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index b6e9e93bde7a8ad635a32eab35fc4658610d571a..406ac673ad84cb9e46cd715065fb3f6a8d6c7625 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/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index dfbbbf0f746f93e41d9bf39c69ddc066b8f7a4a1..e3972dbf4c9a3f56ea7b8a56d8d7c75bcf6a6e19 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 41249d1443fa18dc1b1d4667ef81f36c3509fa86..420897241248c8455a2acf9e2c120a8e9bf8df22 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 e1085e3ab3cc6f2905eb9cd5e6fc31aa20bc23bb..485fa3fa8b49a7852bb5fd81631137e846eba56f 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/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index 66b64096f5de0e8cc33306af98692d9b4e7b2691..40c4eef71c34c554d0f7dfac4cb601515dc22395 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 1d9c028e52cba8e4c2554912dd1313b7331e8d61..18d0b5641789436536b07ce838890bf968201a94 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/vicodec/vicodec-codec.c b/drivers/media/platform/vicodec/vicodec-codec.c index 2d047646f6147e74f04f9cf6ebcd877ac40e554a..d854b2344f12b1c68f299c25df095a6c95d067ac 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 204aa6f554e4dfbd8813949f83c83bd522467c06..fa8435ac2b1ae549654b77c112ea7d0c5e86f501 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 27db8835c2410b792d45be6e484c02821b5c9212..8548fa93bcf65c7766f2af4457b87e938e516091 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 9e0d70e9f119ca4bc077f40c0782e79ba5911051..3f0ffd4915cd2156bcdc234aecd3912317d57088 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 2a92e5aac9ed346edf1adb01a336381d0e7b4c97..ac17883a054fef861a74188edff68728bf10b18e 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 488590594150e03d40b3d80ec9424d8662c9e383..c5f466a7331291d8145e3c60e310382e1fc30c12 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 cfb7cb4d37a875b0715d1b4020a662c0338625eb..e1794f8689d473349084403c00a72a80d025dce3 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 2e273f4dfc2951b53fee1c2261a5e2309bef32ed..c58ae489f39cb7a53bc9f9bd1f7e1111e660c22c 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 50248e2176a0894bcec2dccde366250e28affedf..0f909500a0b801767dc1e1856aa12694ccd42772 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 b9c0f695d002bdedbd6dbcc3a2df71367697f575..8d86f618ec776bb893c6b298e1d232b0cca076af 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 3738ff2f7b850052565d3b131d559fad048103d7..f6e4157095cc0081d013e20b238698c0c3183716 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 11aa94f189cb0f2fd2776e3dc23220942c549f9f..500026974dee0528eb87a67511fc15b822035267 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/rc/imon.c b/drivers/media/rc/imon.c index f23a220352f7f970ddfd9a54470d6d7886aab27c..6b10363fb6f09801a204af581084b144f90398e4 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 68487ce9f79b618e213aaba9dcb0a5ae069baf15..d96aed1343e4208afef093e04383e549265657ab 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 */ #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 11ce5101e19f6b71ed703ea9cd8010e7ae1b6967..c43730977f53c0bd7caa28fa58a1ce67018ce082 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 e3f63299f85c0906a31db16528f7af468b836e68..07e3322bb1827cd75568cd2f0e6dd9088e03298e 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 a8f3169e30b31b278a45665805c565627134cdeb..ac4fddfd0a43f5893f61cce4040b6e742b3a135f 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -537,6 +537,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 f7fcd733a2ca8de24360a30fc5e08a6d80902589..963739fa86718f296d5dba4afacc73593fa5b441 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/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 5b51ed7d6243fe8572b126e45fba6cee83587f3e..5400ec99986fc8ad240e44101dc1576c43c59883 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 350635826aaed39898519ba4844327ab9d4695eb..6e3bdd71e273ba06cbce8a4d0fb52867c3a569bc 100644 --- a/drivers/media/usb/pulse8-cec/pulse8-cec.c +++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c @@ -585,7 +585,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/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 6e3f234e790b849b5cd0c222556659f420c0f130..e33fa78ef98dda3b36c77640e3e8f6fd8a156d94 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 f29d1bef029367f241dbbb1125199ee2d99f2525..cce29b604f4ac70568a7c306db53f4c1e6afa50e 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 c3ddbf6c202a23593bcca171e37e37d306d2e519..063e229ead5ef7b8dfee8ceaae32018babfceac7 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -390,6 +390,39 @@ static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) return NULL; } +/* ------------------------------------------------------------------------ + * 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 e9ea261b38ba86baeb568e12aaaebab2b5f92347..dddf0f5372c7a558860774948e304b624225c827 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/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index c215287e80cf3bbff8d452f53bf097fba39148dd..1c6a7c16e0c17d25a34f37c920fa189e51f5ffe3 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -21,6 +21,7 @@ #include #include #include +#include /* GPIO descriptor enum */ #include #include #include diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 47d6d40f41cd52fe47cb52462d6672a94e9597b6..a4403a57ddc89631ba934058b2c95688a7e47aff 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 15bc052704a6d8ace919278db045c4ba189df156..9ca1f8c015de9466d955a446f7c1cf39e8998bcc 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 = { @@ -472,6 +483,16 @@ static int bxtwc_probe(struct platform_device *pdev) return ret; } + 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, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 3f554c44752183a917f6229a4bc26ec81a042e00..d1495d76bf2c3d5983903d91d458f217e45f1b95 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 234febfe6398bf962285a6dd77a798c7286c75bc..d0bf50e3568d7e770835092430e33175a4475601 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 fe8d335a4d74dabcd2b012b9462a68aa0378d720..4660ad90ef556e52b57fd28bda959f2104937ba4 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/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c index ef83a9078646fb5177f2a1da4b28eb03fdae4327..d2ed3b9728b7cbf96562200e489daf6745a3df4e 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 b83a373e3a8dd3ce3682aa5249236d9b234565c4..08f4a512afad204b4798b8b6b7ee8ccff5167db1 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/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index f68435df76d48bc8b128ddc6b3a866227fd15fba..22301bba8c495b8dbf34d87d8ccbeee59b6d8123 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 eb4d90b7d99e16ed41455d63744e2ebab6e46dd9..8b01257783dd861569beb62db90a38878eed03ee 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 c383322ec2ba0a7d0ae90b22055da16410b49515..bb2e1387b119788f752710ce697a4077f29caa23 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 + * - + * 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 f85aa3f4042d90167bdd4f8f564879ff69bbe7b2..9c40424200224225592227b9d7fb1d4aae8a084c 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 28cdd87851cbaf254a86997aa2750b015ec4ab77..41a10e392839ec713ce13f1bb9e93b973d05a81b 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 cac3bcc308a7ebfc99bc40522389587060b251f7..7bb929f05d85227b1f6b16f5d037ac58ad3e2200 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/wigig_sensing.c b/drivers/misc/wigig_sensing.c index 61372a1ed9248cc849a9bf6e39ba747ad86b6036..7db1f3b6ab9a8364c21c00b5c4462d7f66c336f8 100644 --- a/drivers/misc/wigig_sensing.c +++ b/drivers/misc/wigig_sensing.c @@ -434,6 +434,7 @@ static int wigig_sensing_change_state(struct wigig_sensing_ctx *ctx, { enum wigig_sensing_stm_e curr_state; bool transition_allowed = true; + int rc = 0; if (!state) { pr_err("state is NULL\n"); @@ -449,42 +450,54 @@ static int wigig_sensing_change_state(struct wigig_sensing_ctx *ctx, /* Moving to SYS_ASSEERT state is always allowed */ if (new_state == WIGIG_SENSING_STATE_SYS_ASSERT) - transition_allowed = true; + goto skip; + /* * Moving from INITIALIZED state is allowed only to READY_STOPPED state + * and only when spi_ready is set */ else if (curr_state == WIGIG_SENSING_STATE_INITIALIZED && - new_state != WIGIG_SENSING_STATE_READY_STOPPED) + (new_state != WIGIG_SENSING_STATE_READY_STOPPED || + !ctx->stm.spi_ready)) { transition_allowed = false; + rc = -EFAULT; + } /* * Moving to GET_PARAMS state is allowed only from READY_STOPPED state */ else if (curr_state != WIGIG_SENSING_STATE_READY_STOPPED && - new_state == WIGIG_SENSING_STATE_GET_PARAMS) + new_state == WIGIG_SENSING_STATE_GET_PARAMS) { transition_allowed = false; + rc = -EFAULT; + } /* * Moving from GET_PARAMS state is allowed only to READY_STOPPED state */ else if (curr_state == WIGIG_SENSING_STATE_GET_PARAMS && - new_state != WIGIG_SENSING_STATE_READY_STOPPED) + new_state != WIGIG_SENSING_STATE_READY_STOPPED) { transition_allowed = false; + rc = -EFAULT; + } /* * Moving from SYS_ASSERT state is allowed only to READY_STOPPED state */ else if (curr_state == WIGIG_SENSING_STATE_SYS_ASSERT && - new_state != WIGIG_SENSING_STATE_READY_STOPPED) + new_state != WIGIG_SENSING_STATE_READY_STOPPED) { transition_allowed = false; + rc = -ENODEV; + } +skip: if (transition_allowed) { pr_info("state transition (%d) --> (%d)\n", curr_state, new_state); state->state = new_state; } else { - pr_info("state transition rejected (%d) xx> (%d)\n", - curr_state, new_state); + pr_err("state transition rejected (%d) xx> (%d)\n", + curr_state, new_state); } - return 0; + return rc; } static int wigig_sensing_ioc_set_auto_recovery(struct wigig_sensing_ctx *ctx) @@ -503,39 +516,41 @@ static int wigig_sensing_ioc_get_mode(struct wigig_sensing_ctx *ctx) } static int wigig_sensing_ioc_change_mode(struct wigig_sensing_ctx *ctx, - struct wigig_sensing_change_mode req) + struct wigig_sensing_change_mode *req) { struct wigig_sensing_stm sim_state; int rc; u32 ch; + if (req == NULL) + return -EINVAL; + pr_info("mode = %d, channel = %d, has_channel = %d\n", - req.mode, req.channel, req.has_channel); + req->mode, req->channel, req->has_channel); if (!ctx) return -EINVAL; /* Save the request for later use */ - ctx->stm.mode_request = req.mode; + ctx->stm.mode_request = req->mode; /* Simulate a state change */ - ctx->stm.state_request = convert_mode_to_state(req.mode); + ctx->stm.state_request = convert_mode_to_state(req->mode); sim_state = ctx->stm; rc = wigig_sensing_change_state(ctx, &sim_state, ctx->stm.state_request); - if (rc || sim_state.state != ctx->stm.state_request) { + if (rc) { pr_err("State change not allowed\n"); - rc = -EFAULT; goto End; } /* Send command to FW */ + mutex_lock(&ctx->dri_lock); ctx->stm.change_mode_in_progress = true; - ch = req.has_channel ? req.channel : 0; + ch = req->has_channel ? req->channel : 0; ctx->stm.channel_request = ch; ctx->stm.burst_size_ready = false; /* Change mode command must not be called during DRI processing */ - mutex_lock(&ctx->dri_lock); - rc = wigig_sensing_send_change_mode_command(ctx, req.mode, ch); + rc = wigig_sensing_send_change_mode_command(ctx, req->mode, ch); mutex_unlock(&ctx->dri_lock); if (rc) { pr_err("wigig_sensing_send_change_mode_command() failed, err %d\n", @@ -553,24 +568,31 @@ static int wigig_sensing_ioc_change_mode(struct wigig_sensing_ctx *ctx, pr_err("wait_event_interruptible_timeout() interrupted by a signal (%d)\n", rc); goto End; - } - if (rc == 0) { + } else if (rc == 0) { /* Timeout, FW did not respond in time */ pr_err("wait_event_interruptible_timeout() timed out\n"); rc = -ETIME; goto End; + } else { + /* rc > 0, this is fine, set rc to 0 */ + rc = 0; } if (ctx->stm.state != ctx->stm.state_request) { - pr_err("wigig_sensing_change_state() failed\n"); - rc = -EFAULT; + pr_err("%s() failed\n", __func__); + if (ctx->stm.state == WIGIG_SENSING_STATE_SYS_ASSERT) + rc = -ENODEV; + else + rc = -EFAULT; } End: ctx->stm.state_request = WIGIG_SENSING_STATE_MIN; ctx->stm.channel_request = 0; ctx->stm.mode_request = WIGIG_SENSING_MODE_STOP; - return (rc == 0) ? ctx->stm.burst_size : rc; + req->burst_size = ctx->stm.burst_size; + + return rc; } static int wigig_sensing_ioc_clear_data(struct wigig_sensing_ctx *ctx) @@ -667,9 +689,8 @@ static ssize_t wigig_sensing_read(struct file *filp, char __user *buf, struct cir_data *d = &ctx->cir_data; /* Driver not ready to send data */ - if ((!ctx) || - (!ctx->spi_dev) || - (!d->b.buf)) + if (!ctx || !ctx->spi_dev || !d->b.buf || + ctx->stm.state == WIGIG_SENSING_STATE_SYS_ASSERT) return -ENODEV; if (ctx->stm.change_mode_in_progress) @@ -794,7 +815,11 @@ static long wigig_sensing_ioctl(struct file *file, unsigned int cmd, if (copy_from_user(&req, (void *)arg, sizeof(req))) return -EFAULT; - rc = wigig_sensing_ioc_change_mode(ctx, req); + rc = wigig_sensing_ioc_change_mode(ctx, &req); + + if (copy_to_user((void *)arg, &req, sizeof(req))) + return -EFAULT; + break; } case WIGIG_SENSING_IOCTL_CLEAR_DATA: @@ -867,35 +892,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); @@ -909,6 +934,7 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx) } ctx->stm.burst_size = burst_size; + pr_info_ratelimited("burst_size = %u\n", burst_size); if (ctx->stm.state >= WIGIG_SENSING_STATE_SYS_ASSERT || ctx->stm.state < WIGIG_SENSING_STATE_READY_STOPPED) { pr_err("Received burst_size in an unexpected state (%d)\n", @@ -918,15 +944,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; @@ -934,22 +962,20 @@ 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 */ rc = wigig_sensing_change_state(ctx, &ctx->stm, ctx->stm.state_request); - if (rc || ctx->stm.state != ctx->stm.state_request) { + if (rc) { pr_err("wigig_sensing_change_state() failed\n"); - rc = -EFAULT; goto End; } @@ -967,100 +993,58 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx) return rc; } -static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx, - u16 fill_level, u32 burst_size) +static int wigig_sensing_chip_data_ready_internal(struct wigig_sensing_ctx *ctx, + u16 fill_level, u32 *offset) { int rc = 0; - enum wigig_sensing_stm_e stm_state = ctx->stm.state; struct spi_fifo *spi_fifo = &ctx->spi_fifo; struct cir_data *d = &ctx->cir_data; struct circ_buf local; u32 bytes_to_read; - u32 idx = 0; - u32 spi_transaction_size; u32 available_space_to_end; - u32 orig_head; - - if (stm_state == WIGIG_SENSING_STATE_INITIALIZED || - stm_state == WIGIG_SENSING_STATE_READY_STOPPED || - stm_state == WIGIG_SENSING_STATE_SYS_ASSERT) { - pr_err("Received data ready interrupt in an unexpected stm_state, disregarding\n"); - return 0; - } - - if (!ctx->cir_data.b.buf) - return -EFAULT; /* - * Read data from FIFO over SPI - * Disregard the lowest 2 bits of fill_level as SPI can read in 32 bit - * units. Additionally, HW reports the fill_level as one more than - * there is actually (1 when the FIFO is empty) + * Make sure that fill_level is in 32 bit units */ - if (fill_level == 0 || fill_level == 1) { - pr_err("Wrong fill_level received\n"); - return -EFAULT; - } - fill_level = (fill_level - 1) & ~0x3; - - if (fill_level > burst_size) { - pr_err("FIFO fill level too large, fill_level = %u, burst_size = %u\n", - fill_level, burst_size); - return -EFAULT; - } - - /* - * In case there is not enough space in the buffer, discard an old - * burst - */ - if (circ_space(&d->b, d->size_bytes) < fill_level) { - mutex_lock(&d->lock); - if (circ_space(&d->b, d->size_bytes) < fill_level) { - pr_debug("Buffer full, dropping burst\n"); - d->b.tail = (d->b.tail + burst_size) & - (d->size_bytes - 1); - ctx->dropped_bursts++; - } - mutex_unlock(&d->lock); - } + fill_level = fill_level & ~0x3; - spi_transaction_size = - calc_spi_transaction_size(fill_level, SPI_MAX_TRANSACTION_SIZE); local = d->b; - orig_head = local.head; + local.head = (local.head + *offset) & (d->size_bytes - 1); mutex_lock(&ctx->spi_lock); while (fill_level > 0) { if (ctx->stm.change_mode_in_progress) { - local.head = orig_head; + rc = -EFAULT; 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, idx=%u, available_space_to_end = %u\n", - fill_level, bytes_to_read, idx, + pr_debug("fill_level=%u, bytes_to_read=%u, offset=%u, available_space_to_end = %u\n", + fill_level, bytes_to_read, *offset, available_space_to_end); /* Determine transaction type */ if (available_space_to_end >= bytes_to_read) { rc = spis_block_read_mem(ctx->spi_dev, - spi_fifo->base_addr + idx, + spi_fifo->base_addr + *offset, &d->b.buf[local.head], bytes_to_read, - ctx->last_read_length - ); + ctx->last_read_length); + if (rc) + break; } else { /* * There is not enough place in the CIR buffer, copy to * a temporay buffer and then split */ rc = spis_block_read_mem(ctx->spi_dev, - spi_fifo->base_addr + idx, + spi_fifo->base_addr + *offset, ctx->temp_buffer, bytes_to_read, - ctx->last_read_length - ); + ctx->last_read_length); + if (rc) + break; memcpy(&d->b.buf[local.head], ctx->temp_buffer, available_space_to_end); memcpy(&d->b.buf[0], @@ -1069,14 +1053,91 @@ static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx, } fill_level -= bytes_to_read; - idx += bytes_to_read; + *offset += bytes_to_read; local.head = (local.head + bytes_to_read) & (d->size_bytes - 1); } mutex_unlock(&ctx->spi_lock); + return rc; +} + +static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx, + u16 fill_level, + u32 burst_size) +{ + int rc = 0; + u32 read_bytes = 0; + enum wigig_sensing_stm_e stm_state = ctx->stm.state; + struct cir_data *d = &ctx->cir_data; + union user_rgf_spi_status spi_status; + + if (stm_state == WIGIG_SENSING_STATE_INITIALIZED || + stm_state == WIGIG_SENSING_STATE_READY_STOPPED || + stm_state == WIGIG_SENSING_STATE_SYS_ASSERT) { + pr_err("Received data ready interrupt in an unexpected state, disregarding\n"); + return 0; + } + + if (!ctx->cir_data.b.buf) + return -EFAULT; + + /* + * In case there is not enough space in the buffer, discard an old + * burst + */ + if (circ_space(&d->b, d->size_bytes) < burst_size) { + mutex_lock(&d->lock); + if (circ_space(&d->b, d->size_bytes) < burst_size) { + pr_debug("Buffer full, dropping burst\n"); + d->b.tail = (d->b.tail + burst_size) & + (d->size_bytes - 1); + ctx->dropped_bursts++; + } + mutex_unlock(&d->lock); + } + + while (read_bytes < burst_size) { + 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) + break; + + /* Read fill_level again */ + pr_debug("Reading RGF_USER_SPI_SPI_MBOX_FILL_STATUS register\n"); + 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); + if (rc) { + pr_err("Fail to read RGF_USER_SPI_SPI_MBOX_FILL_STATUS, err %d\n", + rc); + return rc; + } + fill_level = spi_status.b.fill_level; + } + /* Increment destination rd_ptr */ mutex_lock(&d->lock); - d->b.head = local.head; + d->b.head = (d->b.head + read_bytes) & (d->size_bytes - 1); pr_debug("head=%u, tail=%u\n", d->b.head, d->b.tail); mutex_unlock(&d->lock); @@ -1164,6 +1225,7 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) u32 sanity_reg = 0; union user_rgf_spi_mbox_inb additional_inb_command; u8 num_retries = 0; + bool dont_deassert = false; mutex_lock(&ctx->dri_lock); @@ -1193,9 +1255,11 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) } ctx->stm.spi_malfunction = false; - if (ctx->stm.state == WIGIG_SENSING_STATE_INITIALIZED) + if (ctx->stm.state == WIGIG_SENSING_STATE_INITIALIZED) { wigig_sensing_change_state(ctx, &ctx->stm, WIGIG_SENSING_STATE_READY_STOPPED); + ctx->stm.spi_ready = true; + } } pr_debug("Reading SANITY register\n"); @@ -1232,20 +1296,26 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) goto bail_out; } + if (spi_status.b.int_dont_deassert) { + dont_deassert = true; + spi_status.v &= ~INT_DONT_DEASSERT; + } if (spi_status.b.int_sysassert) { + enum wigig_sensing_stm_e old_state = ctx->stm.state; + pr_info_ratelimited("SYSASSERT INTERRUPT\n"); ctx->stm.fw_is_ready = false; - rc = wigig_sensing_change_state(ctx, &ctx->stm, - WIGIG_SENSING_STATE_SYS_ASSERT); - if (rc != 0 || - ctx->stm.state != WIGIG_SENSING_STATE_SYS_ASSERT) - pr_err("State change to WIGIG_SENSING_SYS_ASSERT failed\n"); + wigig_sensing_change_state(ctx, &ctx->stm, + WIGIG_SENSING_STATE_SYS_ASSERT); /* Send asynchronous RESET event to application */ - wigig_sensing_send_event(ctx, WIGIG_SENSING_EVENT_RESET); + if (old_state != WIGIG_SENSING_STATE_READY_STOPPED) + wigig_sensing_send_event(ctx, + WIGIG_SENSING_EVENT_RESET); ctx->stm.spi_malfunction = true; + ctx->stm.spi_ready = false; memset(&ctx->inb_cmd, 0, sizeof(ctx->inb_cmd)); spi_status.v &= ~INT_SYSASSERT; goto deassert_and_bail_out; @@ -1267,11 +1337,11 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) if (spi_status.b.int_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_status.v &= ~INT_DATA_READY; } if (spi_status.b.int_deep_sleep_exit || @@ -1307,10 +1377,12 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) deassert_and_bail_out: /* Notify FW we are done with interrupt handling */ - rc = wigig_sensing_deassert_dri(ctx, additional_inb_command); - if (rc) - pr_err("wigig_sensing_deassert_dri() failed, rc=%d\n", - rc); + if (!dont_deassert || additional_inb_command.b.mode != 0) { + rc = wigig_sensing_deassert_dri(ctx, additional_inb_command); + if (rc) + pr_err("wigig_sensing_deassert_dri() failed, rc=%d\n", + rc); + } bail_out: mutex_unlock(&ctx->dri_lock); @@ -1424,10 +1496,11 @@ static int wigig_sensing_remove(struct spi_device *spi) .mode = WIGIG_SENSING_MODE_STOP, .has_channel = false, .channel = 0, + .burst_size = 0, }; /* Make sure that FW is in STOP mode */ - wigig_sensing_ioc_change_mode(ctx, req); + wigig_sensing_ioc_change_mode(ctx, &req); device_destroy(ctx->class, ctx->wigig_sensing_dev); unregister_chrdev_region(ctx->wigig_sensing_dev, 1); diff --git a/drivers/misc/wigig_sensing.h b/drivers/misc/wigig_sensing.h index 1389c3f77aacc3cf64d242f89871f1e3c3025a8e..da55cee44f57da74f61f8b26100f221677ace247 100644 --- a/drivers/misc/wigig_sensing.h +++ b/drivers/misc/wigig_sensing.h @@ -41,6 +41,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 + \ @@ -51,6 +53,7 @@ #define INT_FW_READY BIT(24) #define INT_DATA_READY BIT(25) #define INT_FIFO_READY BIT(26) +#define INT_DONT_DEASSERT BIT(27) #define INT_SYSASSERT BIT(29) #define INT_DEEP_SLEEP_EXIT BIT(30) union user_rgf_spi_status { @@ -67,7 +70,7 @@ union user_rgf_spi_status { u8 int_fw_ready:1; /* FW MBOX ready */ u8 int_data_ready:1; /* data available on FIFO */ u8 int_fifo_ready:1; /* FIFO status update */ - u8 reserved2:1; + u8 int_dont_deassert:1; /* Don't deassert DRI */ u8 reserved3:1; u8 int_sysassert:1; /* SYSASSERT occurred */ u8 int_deep_sleep_exit:1; @@ -145,6 +148,7 @@ struct wigig_sensing_stm { bool auto_recovery; bool fw_is_ready; bool spi_malfunction; + bool spi_ready; bool waiting_for_deep_sleep_exit; bool waiting_for_deep_sleep_exit_first_pass; bool burst_size_ready; @@ -189,6 +193,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; diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index f1953c92c500ff4dfb9925493a571962980b838f..6ef9a949ced78299062a0fa1eb9247466326f436 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 f8d60830be3340460df4ebd2d104284ccff3224a..cc893823226108b4abc361a2540812505af4a814 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2702,14 +2702,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"); @@ -2724,7 +2724,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", @@ -2749,7 +2749,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/queue.c b/drivers/mmc/core/queue.c index 13ed3ac8c18ade65c2729ff233e5a4adf5dfae55..de6dac1841608cf842450efa57abd3ca25957f9d 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -360,6 +360,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 +374,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 ddd98cdd33bcdd8eaed178711b52639d7e64d8fc..72f34a58928ca6bd27fec902384d4a091e6b9ecf 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -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 eb1a65cb878f0391b3df5056d4c594c8e47e12d2..fa6268c0f12321f8a6f0c5f22074b31af93c70c3 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 517591d219e933fc233c636adcbff268f4bb621e..613d37ab08d20c84c2b37cd08ed6683e6311d0aa 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 f171cce5197de6d6d84f523623e5de90ef20100c..673f6a9616cd951451783567b97c767596684f95 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -390,7 +390,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 +634,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 +682,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 +761,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 +772,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 +787,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 +1056,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 +1068,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); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index b23c57e07f369985653c7e0e53967a796c2f1b5c..0135693afa158420587976796dde940f0872fd65 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 ca0b43973769c9f80b4771914b5b74016b54bf84..382172fb3da8f55886a27874dbcffd585e4aaf95 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 d458ad24f6085552ea44365907cff3346acf17af..4a222eacc712658c8a1e6c72c12b38f0eb106f38 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-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. * * drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform * driver source file @@ -866,7 +866,8 @@ static int msm_init_cm_dll(struct sdhci_host *host, msm_host_offset->CORE_DLL_CONFIG); /* For hs400es mode, no need to wait for core dll lock */ - if (!(msm_host->enhanced_strobe && + if (msm_host->mmc->card + && !(msm_host->enhanced_strobe && mmc_card_strobe(msm_host->mmc->card))) { wait_cnt = 50; /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index e284102c16e97aa690e65198ab3390ecb1cc030c..1ebcf0eb781ea6cae8dc9cf4ae12b53cfdf03b96 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/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 7d13ca9ea5347cd47abea398dec56000214cf0e8..35630ccbe9e5df2d5649eac0f9aebaae19997b69 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) { diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 270d3c9580c51195ccb6b05e3719e98fb1836031..c4a1d04b8c800d1b090e99dc9975025d4b61009c 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_exec_op(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; - op.addr.val += op.data.nbytes; - remaining -= op.data.nbytes; - op.data.buf.out += op.data.nbytes; - } + ret = spi_mem_exec_op(flash->spimem, &op); + if (ret) + return ret; - return len; + return op.data.nbytes; } /* diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 986f81d2f93e3d8cdceed38250dbdc8d48046663..47ad0766affa9b234caafeb32f14905897da0fff 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 4129535b8e46f34e8891c3d86f0c6fb5160f6095..ece605d78c215670af7334fff835fb095e155bd0 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 9887bda317cd9daabe4de8515587e5a11803a431..b31c868019adada77b86e028974203f09251739e 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 0bbb23b014f1b8c804ec4aecb7f478849f8ee3b2..10c53364aa70c1849b0768ee330293759919c97e 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 32e95af486a2026f644825487c3b4a643cd566f5..ea022712edee846f530c90f49d9b2e07c24b199d 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 555a74e15269da45f2b4685dcd149cfd88f47438..9d3997840889b2b3139e54ecbd0729cf507f74ae 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 24f59d0066afdd77d586f29b944f566e0038d98c..7e7729df782780728826f1bd113bd1199d5a050b 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -30,6 +30,7 @@ #include #include #include +#include #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 9c90695a885fe19e9d689e20d142ba1585ce7ad3..7a84a8f05b46d14abfbc87f4b4bc18127934b5d9 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 880e75f63a19b8f18e8148487300f3b556179b0e..07d8750313fd6fe44659367061489b0a273d3f8a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -23,7 +23,6 @@ #include #include #include -#include /* 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 bb8866e05ff76cc2be9bb1d24f2f5eaadb3799bc..1e7273263c4ba797ebdfcdd411169405e8052142 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 1f0b7ee38df5627acf1d0f675fc5d85abb7aea67..5b5f4d25a3e126e44ab3584d27a46f87e00b0836 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 0806c7a81c0f7fc887a83f8610d832e88f249f70..04cedd3a2bf6634c5d1f05ef3d27ba302d12935f 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 f028277fb1cedb86ac4837e78341705c82f60b1b..2e183425facd855860e0f4d6d182954f6a9850e1 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 d2a726654ff1182e961f22ff365dc461996b7204..c120c8761fcd29e07c0d68a17bdaf6fdca9250ad 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 e9e9ecbcedcc384aee39a7b3e08f9d23cffd39aa..0b8f0c46268dae932896b7438e15fe8205b9016f 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 619bf1498a662a85891887c885fdab12d10113e4..0652caad57ec1cb8e8c27c04242fe7e2996b329b 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -197,9 +197,9 @@ config VXLAN 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 0d2392c4b625a195c9727adab45c6bc933b5c794..9b8143dca5123315e39d40173fce51c258e018ac 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 @@ -2263,8 +2264,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 +2665,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 +2698,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 +2730,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 +2754,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 +2768,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 +2801,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 +2814,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 +2834,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 +2887,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 +4034,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 606b7d8ffe138f880bfc830eff10b84ba6a01cfe..24c6015f6c92bcb4bc3e604d78562010d49dc4fd 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 8acdc7fa4792f24438cb0cd84d73b44f0c46f7d8..d5567a7c1c6d4652a04f8135b381387e3a2e5304 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 bd127ce3aba245e3bc100538da2aebdcf2b0287e..49b853f53f59511a24f24129a5aaf421f5a3ef10 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 6f265d2e647ba81ee39ffa1d31f59f3eb1618ca5..bfe13c6627bed6af9b5b0618b4fa4e468859fd82 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 727691dd08fbf9c1d43c20162f9de3621812e560..5f7e97d54733c1064583703cd5b90c1f2b0f95d5 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 aa97dbc797b6be339e911a9c34d55778040fdad4..cf0769ad39cdba93c24316190e54db8071f2ef1d 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 de8d9dceb123619102107a7d5ca6e6f9feb60697..0b0dd3f096dc6ad8807809d1b89ca626544e0170 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 17c21ad3b95eed15fb72342ff7d3ccedb7a2096f..3a39f51a9e2448790a6840b0dda76c339c98e85c 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/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c index 8d8c2086424d09b2c93a377be756dead26dc7979..1b0afeaf1a3c22ee3911635f0e2d2a464b51ed7f 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 13238a72a33862a7c2acfc966c03f93d206d82be..215cd74800df4b6e625cd41ed3627bb178f46267 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 43b0fa2b99322e33a0ea0fb81529a4c6844b0050..afc8d978124ef6fed024c3a7f5e31db1b989810a 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 c9fd83e8d9477d5599614cceca2315784301789e..43350f56e223d47d2a6363251b2ecfa0fc441d13 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 27861c417c9404c9c3df841daac95015978f3a69..3e44164736079d467644b63780002bbade28f933 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 3df23487487f7821a0cf545d3d0a97aba1f149d5..b01c6da4dd814595b46eaad8e024537efc19f14e 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/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index ad534b90ef21b6a269f57472fe5beee70f6fd917..2d3a2cb026d264751a79f1de3960e8427edf2759 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 2fa2caf7a7466c8d48deebb2a24894f4c794576e..02a4187d81bd037e6bcb9abe3e430d406a2a9b62 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 703e6bdaf0e1fe1ee1b4dd0dba860da1d53155b9..43b00e8bcdcd7dab1049671580b8d76f8167f060 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/port.c b/drivers/net/dsa/mv88e6xxx/port.c index fdeddbfa829da009ef00807236561ce9f5f73df6..2f16a310c110eb3682edd2e8a2410b1facea77a6 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 95b59f5eb39310aa6742ee8db5afd4c75857d2ea..cbb64a7683e287d724203cda74a186297e85d6b3 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 99b30353541ab7851cef3e5a9cdd10448f274a91..9e87d7b8360f59575b824ee734298409fcffbcb0 100644 --- a/drivers/net/ethernet/amazon/Kconfig +++ b/drivers/net/ethernet/amazon/Kconfig @@ -17,7 +17,7 @@ if NET_VENDOR_AMAZON 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/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c index 01d132c02ff9039e31bcc9c4e10b1cbaeb315150..265039c57023fc40d9c605b9cbdd49b5270cfdd3 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 c5b81268c2849491ba22d26181f4c1b8332526c9..d3d44e07afbc05b71ac1c160ba859c0b95f065be 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 00332a1ea84b9e770febc38dd69af42e3d5bba90..9f23703dd509f84981596313a1dbb066efa73479 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 77b1db2677309e639f52c75ecbc96e9e451a7bbb..da7e3d4f41661bf911f767e525ab50b6bb8e9818 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 19f89d9b1781f9b63e032efee0e5a3278153019e..9d489982682336a7cef13186f20557a4a7d7daeb 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 24f1053b8785e65b3346b0e2c8fb2acbccbbad29..d96a84a62d78dff9625ce78d15779a05df8b510c 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 91eb8910b1c992b1b7876f05a26753a5cf79c100..90a0e1d0d62219c00b807cddd0d276f532556eab 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 4f34808f1e0648b229250a0c38029f475ec221e1..8cc34b0bedc3a2f388ac778fe50b573f24f4b5ac 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 10ec5dc88e2438d49a675add9349a0a8ed9cded4..5502ec5f0f6993502cd8d4e880032a9606da5c34 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 b3bf64b48b93d7544c14779a973b2feff6025e92..41f239928c157f121b74f086c35c86457f2a3aba 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 e2ecdb1c5a5c4d3fb3ab481ff080300480e57741..a715fa317b1c822781b4a0422033816b5684e7e3 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 9939ccaeb125b3338db95e4badd440abba82f561..096ec18e8f15a63ad696d433ab8d9274c96e5688 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; + + 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; - aq_hw_write_reg(self, 0x00000208U, a); - - 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 b875590efcbddbeb5983f2da99f9785d4298734c..505c8a2abd9ca4ec41d28518d7963797483852b3 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 e37943760a58b2a88b33de295e73dfbac71fc5bf..6300d94c9ff070ccc47cd33d0a27a758f571dc11 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 0f65768026072ae7ded390fef283269f180f6e24..a1df2ebab07f021524586ee8cec66da3fbb6aa92 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 9dc6da039a6d90ac4137a70e94b2c3213c2a4741..3164aad29bcf879aa0841a912d6bbeaa757caad6 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 897302adc38ec16869609cccf6ddc8ee9941a3c7..50f8a377596e10bcb682f37fde9bd36b18701900 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_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 68c62e32e882046c96f7a66d35c2a1ba78da6ede..af57568c922ebe8edf3481aa9dd6f3dc283f1ed2 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); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index c54a74de7b088196cafad6912867173b7698f7f1..2f61175f5655a2d19d4ed6daf9c1ff01f6e2f737 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 cf2d4a6583d5526be80b5a4556ce31aee001bb65..f9e253b705ece65d61ff4b37f8207c39f99d2cd4 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 790c684f08abcab21980cd9a8479f27cf516d464..b178c2e9dc2319b7c5ae77b61d7c2e4746f0e64c 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 da9b8768999634521ee795e87ebdd322de09f9d5..2240c23b0a4c9396923481b9d2713135a9cf2aff 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; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index fd587bed32ebd1f1c222708ae9052a481c73cb4b..b7d75011cede58a13bf8e7188c7f8cd6b43e8735 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); + 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) { - priv->dev->phydev->link = !!(status & UMAC_IRQ_LINK_UP); + 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 0d527fa5de610c26586ccc33dae468e2e43d2289..a5049d637791d404e07329778d373c9dff24cf18 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 ef4a0c326736dc2518216b3aee4108e5ecf5133c..7e3f9642ba6c56d04b8b0d1968a15da4edf19418 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 9bbaad9f3d6326cdd113e48faa0d0e091bcde04c..efb44d5ab02156282f477fde76e8ca3bf4675f1b 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 74eeb3a985bf1e140fa1b7b623a7d1016ac7716b..c2eb188547942fa58c3b41d9b416b7f3062cf796 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 a6dc47edc4cf6431066850770928640670e20bb1..8f912de44defcab99c4fc261249b79415f6b8c1f 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 6fb13fa73b271c9d05f49ad5a04ea71f9a12c987..304e4b9436276c780e1e2f690f2c31d4d321166e 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 b77835724dc84d037c88bcb9ef7153db8f1f6e48..d83773bc0dd7f6352c8a010fdc6720e1d9d41a32 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 c99b59fe4c8fb0f1555d166048d2a0dfc1329b76..a1bda1683ebfc7a60d9c7d52092ab6179f4b46bd 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 f878a552fef3b36fcf98ee822d6e7a635401b92b..d0ed6c4f9e1a2922aee49e9e36256a3268671072 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 2327062e8af6b7af4b5cc146a88234ec60e89bb4..aecd0d36d6349869e8703f48b62f2e94fd18397c 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 3deb3c07681fd6a2b1d76f9c5a578522f4136de9..1d9ab7f4a2fef2d6d0a09f34d22f43b4db564034 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 bb43ddb7539e719d0cbff780e5ddf17c756dbe05..0957e735cdc4d12baa2d94793b436205a3f6080a 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 e337da6ba2a4c16973f4728c9fd9564da162942c..8ae28f82aafdc2226d2b26f24ef48a0809b32f4c 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 b34f0f077a3107662ffb2799e09a40e05ec5848a..838692948c0b22ea5447b07195d29cdbf76a60fc 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 02040b99c78a0561da461c1497a26bfb92ce2f40..484ee829009036883efde95754d419930f7e096c 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_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c index dba8a0c1eda3a5a235952cb878f381d60f4037fa..3d834d40e81e897b8aa698ac0cee28a4e1753798 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 5fe5d16dee724cdca0366e99f06c6ef9b9a17d4b..8350c0c9b89d1878ca6ff81218a70841ce9f37d6 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 ff84791a0ff853bf571aeb89014ec92d66eb6275..972dc7bd721d9a7412e48c429a75eb376a1b5486 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 13dfdfca49fc7816b8fb64c55d372b19370e4a7f..edc1d19c9c02e32a2eccfad7571183a4e5221c10 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 dfd1ad0b1cb9433b33402c214df3f07046395557..4af78de0e077a4fd4fc810439580adf4bf13fa41 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -2530,6 +2530,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 ed6c76d20b45b2a38ccf87e63487e77a756812a3..e4fc38cbe8535134feabf677be3dfdfe0cbf7f1f 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 9015bd911bee91aa8c7fd71ec027232fa42e7358..084f24daf2b5a8854dcbb9002314f658f0ceaf26 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 d7915cd68dc14756ea8e4d6edce5ea23d4dbad87..462bb8c4f80c93a21be4497cd978bf3304e98408 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 4cf80de4c471c425905da873c79ba3b5635da378..296ae1e4c322c8bb58cf19ca87cd420135c4b01a 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 6d7269d87a850bb4da693b19d5598bc81d18788a..b90bab72efdb3b3d4370bc5616ff0e8609b85cfd 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 2c2976a2dda6b9f1055af98c19cbc9d17bf8279b..7c548ed535da5babe24c342243c82d737f257034 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 f27f9bae1a4ac02811590636f55d74dcb0568225..c97c4edfa31bcf2a73ab7465c241cc4d768a368f 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 1e2b53a934fb9bb76dc2574e0d0756bcedf8a319..a5bf02ae4bc5c15e808caac7199de871eaad88e8 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 a91d49dd92ea6c32a308ea61d0e1a02785985aa1..e8936ae46add909429241d869c6b22044abbe58f 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; @@ -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 c5727003af8c1438f9e4824b16f8774b8a383878..471805ea363b6b5eda58800b7ef0e5417e1d5530 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 c7fa97a7e1f4d4b07dd6b00f7c5c7bffca4a0356..b758b3e793370ff9724cebc8620ff821e4b73a30 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 08a750fb60c49d397c61845130e153fe1e3b0b3e..c8cbbe5d55494ab7b0cf3f55f1115fa4a0589d10 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 1c70f9aa0aa766e6da6433e8f29bf2e50c3b4791..7f8cf809e02b5e4a36b43481677304b220dea120 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 0594a6c3dccda161a9ecc3ac38e55eb456e16f49..f9259e568fa052b2f4755c2786d4937f5d78a8da 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); } + 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 67befff0bfc508e04100a28ff8f374ca4844b73c..5e1a7ab06c63bcd41d156a7467ff093554a9ed2a 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 0ccfa6a845353618f9404259d7d837fdd7dec45e..0788e78747d9412ddca9bcaffabe0837171577d6 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)) @@ -1611,9 +1604,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 +1624,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 +2129,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 +2149,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 +2258,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 +2371,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 +2552,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 +2582,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 +2597,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 +2851,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 +2881,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 +3437,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 +3508,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 +3537,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 +3573,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 +3591,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 +3623,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 +3654,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 +3662,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 cb450d7ec8c1665a9ae690a3e3a9b78843976cf4..94d7446811d5d9ad83816ccc8adc027b58a2e34b 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 6a3c6b02a77cd5ddb33b5de31dc58cd2442bf114..0c34ea122358024554c91a4ed18b4f705c45ab73 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 68026a5ad7e772a437da29038c678fc5ce5b6b88..09a4d87429cea2a7089c04cc05b4bf11cac03c81 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 821d4c2f84bd3e7415a3a83c9de566fcea9ac491..d7520686509fd01c2ac6dc89ed0f4e34afe7ed9c 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 92f19384e25857ccef5d52f35329951250449057..a75d7c826fc2b8d229531cfafe005de98927f362 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 89ca69fa2b97b2ea28647192db6747049dab18f2..f8cc8d1f0b2097fe46de56db5ab9f7e9e1b1b7fa 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,15 +3728,36 @@ 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_tqp_enable(struct hclge_dev *hdev, int tqp_id, + int stream_id, bool enable) +{ + struct hclge_desc desc; + struct hclge_cfg_com_tqp_queue_cmd *req = + (struct hclge_cfg_com_tqp_queue_cmd *)desc.data; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_COM_TQP_QUEUE, false); + req->tqp_id = cpu_to_le16(tqp_id & HCLGE_RING_ID_MASK); + req->stream_id = cpu_to_le16(stream_id); + req->enable |= enable << HCLGE_TQP_ENABLE_B; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "Tqp enable fail, status =%d.\n", ret); + 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 ret; + int i, ret; switch (loop_mode) { case HNAE3_MAC_INTER_LOOP_MAC: @@ -3750,27 +3773,13 @@ static int hclge_set_loopback(struct hnae3_handle *handle, break; } - return ret; -} - -static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id, - int stream_id, bool enable) -{ - struct hclge_desc desc; - struct hclge_cfg_com_tqp_queue_cmd *req = - (struct hclge_cfg_com_tqp_queue_cmd *)desc.data; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_COM_TQP_QUEUE, false); - req->tqp_id = cpu_to_le16(tqp_id & HCLGE_RING_ID_MASK); - req->stream_id = cpu_to_le16(stream_id); - req->enable |= enable << HCLGE_TQP_ENABLE_B; + for (i = 0; i < vport->alloc_tqps; i++) { + ret = hclge_tqp_enable(hdev, i, 0, en); + if (ret) + return ret; + } - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, - "Tqp enable fail, status =%d.\n", ret); - return ret; + return 0; } static void hclge_reset_tqp_stats(struct hnae3_handle *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 1528fb3fa6be6d4da6afcba7bdbbf5e1a7608171..260b1e77969080678cb4afcfeccef8c885d0856f 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 398971a062f475202887ca5cbccccae653052bde..03491e8ebb7305d75e6c185e91f419e4ad6243dd 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 11e9259ca0407b255672e85e87f25eee554a9103..3180ae45288951cf7b6425458dd95249d4ecfb41 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 fb471fe2c4946692e1c36b31bf92ec325812a372..d8c0cc8e04c9dde553cb6b4a8df7a0f3cf7ee490 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 5570fb5dc2eb40079a86b4f32ce5779c80f29153..67db19709deaa89ac6bc71ba67ca9ff02753dcd9 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 4a8f82938ed5b87c8da6b09e88e08d387c652f0c..2352046971a4e7131ef6747cee08c27ab844beaf 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 4c0f7eda1166c5df202c3b9a71cc2e43516531fb..06b24a92ed7d4438a4740ac7f4b0dcd27bf951c8 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 506f78322d741e43682358bb5b74ea7481865c07..e8ee69d4e4d34898f4cb490c32b3d0f2370bd710 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 129f4e9f38dac01f424ea7337002add11a2751e5..a96f501813ff7fab86ddb766e9bb8825fb25dd1b 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 8fa14736449bcf34a9b44803c7467fba1ff7fc0e..8a1916443235a46eb388c550f8b883c310c0ac5d 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 27d5f27163d2cd04f8583f9defd888c3ee1ee8bc..78b44d7876386a87fe3dffa426cbf98cdd19a6b9 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 2569a168334cbc6785f9e2909f5a68ac6450c9d6..903b0a902cb959dbb564ec6793b5de21316446de 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 e707d717012faa997a127687ce45d54b27b9e3eb..618032612f52d8c365050d7a9c89d78f6caea06f 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 5ff6caa83948c2c14a6b85070ddaba54e0c50c3a..a6b0f605a7d8b64edd4dc39ebb346bc7bc3a29c5 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 autoneg \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 055562c930fb0813a88e3e6fad7b6e66f3a63ae6..1a66373184d6205690676bcb8792c2d5c8144e22 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -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; @@ -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 35f2866b38c6b921044d851f3898e342e048813f..1199f0502d6d5169fa211beb15f3b86331428582 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 d86f3fa7aa6a4f90b13b013fd97969c430fbc6e6..6a677fd540d649cc8d0f950224a37fe5696d5b64 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 fef6d892ed4cfe5ae293aa2fe01ef38a257c47bf..f50c19b833686b638f94f7c02673685682a92734 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 565677de5ba376184d5acbe32644f13207d2767e..94dabc9d89f731f436c90ed7bf56eebd56297e63 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -153,6 +153,32 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) NULL, 0); } +/** + * 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 a0614f472658ac5305c6aff1e589d99dbdd1634a..328d293bc3ff522560c1b164532a4703b43aceb5 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 661beea6af795cd72abf3e609347c89b21d9902d..f8d00263d90198bc0824ba3703cb7a0d41f4f2aa 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 9a5519130af13bd83c91e6347170ce44f65d4d3c..6455b6952ec8e4e44c20d895dcba07b95e236794 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 e783976c401d82d22b9da236df05dc28e37edf16..89f18fe18fe36e75179a4bcf9111faba8687781c 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; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 875f97aba6e0ddefa52315a349befb69fd398883..00c833cd2b3ae3bed0784399b19d1e5d5853c39a 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 295a8cd87fc16565148bf6cbb7713c02a71454f9..3274c543283c688ef2ecdb018dd247453a16fce9 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 9a95c4ffd7d795b03eebea76f6953e0255c2eea2..d2dae913d81e0bac21f3b978b1cfdacf8785d0f1 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 6b7ec2ae5ad6798818a9d5947e8071aba410ffff..1bfc59dff51f7eaf61a5226f06b253079595ca8b 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,14 +504,19 @@ 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_DROP_PACKET: + 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->qgrp_size << ICE_SINGLE_ACT_Q_REGION_S) & + 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; - case ICE_DROP_PACKET: - act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP; - 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 0c95c8f83432cb0793260b368b17a2511cc3a4d6..1d84fedf1f649b921d15c238e981f323ecea59cc 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 ba11b589883312fd5c18d0ab6a2577ee7d1f02ce..5ca9d684429d13138379327effcd288e5b0aa73c 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 ab76a5f77cd0e82f85665fcc7092bc7fde3e21cd..36db874f3c9288c1d92b3e1fd0c1c1e12a644b97 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 9f4d700e09df33cb5d3e17576859a563f9c6c52c..29ced6b74d364632113e9674f8d005257e548411 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 85280765d793de5239bd88807ef52e468852b0aa..b45a6e2ed8d15327ff2f384366f1f39fdb76fdeb 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); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 28762314353f915c1b80bcc3a737b007d6a178de..4313bbb2396f4df801ffc099fa385f9b3e51e425 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 67b9e81b7c0246435c26680e06939ef2e061bfd7..46911b67b0398b53798aeb455c48b861c2416e15 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 9b608d23ff7eeb4f39f5a636105632c6852d1ca6..a50977ce40766a9575608637173226f1d412e5f9 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) { @@ -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 3a9730612a704d318b5ab2297a31822775dad1a4..ff2fea0f8b75181eba81fd62d08194537116391e 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 94c59939a8cff373ae1157818d342e29057ee4ad..e639a365ac2d4333a667c1be4b69c6b459c0df3b 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 0d7fd3f043cf0bae5c2c89644ffb60db0a3aa364..5868ec11db1af907547bd9dbc1f3f229cea650f6 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 6a046030e8734a8542ff8ea67560f980d9ffb26c..4afe56a6eedfbedc09c5b1826692a0184369a726 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 676428a576621a36b30d077d054ab8986dca5507..a4c1ed65f620c035c1f26340ed29c4f5b9bc5708 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 4ab0d030b54486f67096a190471a747b1ac18c56..28d56e44ed9d81c308cbeb6967cf34dab451a713 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 10d72c83714db78efae06948bc74d30dd4a95f4c..a383276eb816aa3edbcae378a824542748f30bfd 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 df49dc143c47133c68ef5682e945d282576f6bdc..9cbc4173973e9327ee8f0135a7b216ac86edcd48 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 4382ef85488c5b6e936424dc902fd590d8030294..5fb088b54e665e65629fec6a43950939a4a64b2f 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 #include #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 0b03d65474e93535620854aac6f03d0aadbd3aa7..73dce92c41c44a248379c0618a8cf393aebc17a6 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 55ccd90beeb0d262b08b3a89354709cc7cdc1715..7366033cd31cf88323d2246c11e007d1300f027d 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 8ca1d1949d930d46d0cf7c386383e060640ff792..d8d0b6bd5c5ae192b1a78c21f9b9359a5e1b5325 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 c079f85593d6086bfdbf5e7c985e7cd34ecfdcdc..82a53317285d0d181f1d4baffc85563920986be1 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 32070e5d993d856d0c560e9adde78b3e86d928bf..ba62fbce23a2100ce4144e5b59ad2dba68c0325a 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 231ed508c240a45a191f3887dd2e7da975f2f04c..5fac00ea62457abfea909085fa77ebea9fb03073 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 4ca07bfb6b14f75760928e5df47ce911f8639f10..f33707ce8b6b0f26d9ace1d1f20e915e29f49913 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 2cf89126fb23ba13c7f47f59be40e23e638d11fe..d765e7a69d6b18dcff6acc30c4005b2023aadf44 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/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index ee126bcf7c350936e519e3fbd5322d47ac076e03..e498ee95bacab35c2f3363f1152b6a419fe2a282 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_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2ab9cf25a08ae19788d28ffddaa8698ba2213152..76960d3adfc032120141427b42ee0f3eff3f56b9 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 a4f237f815d1add44a6b0a5bfe9cb8218dc8d736..8d556eb37b7aa42a36c1f57579193bdef9d39da9 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 bd51e057e915063e401c957c6f606b28fee2084a..b881f5d4a7f9efd7769c99eea86105453635f4fc 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 0e9719fbc624382ff9ee63be06698e4515c8a922..35f8c9ef204d91cd4c17591d84ebab597cff33b4 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 732ba21d3369dcfec7307755889bb562e6e8f809..a29a6a618110e0a2f63b3f3edaa14cc1881e43b1 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 616bec30dfa3fe4b31a1295ef2239a57ac2cc36d..3d8c6f38e76b9e997f3c5a8c4f3f807f13a96ab0 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 dbd00982fd2b698bfa5bedc1572583890e75fe63..2134045e14c36a88c51717b7f4b36f07bbaa5c83 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 1ccd6371a15b5c3c0a142909d63b4e0796418fd4..6140e4650b71cc249d82f84fefc1fda1efe26b56 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 a6e9248669e141d4d19f0536eed52adf6ef78d90..db7e186dae56d4c0dda0d3ba59c5408867ef7371 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 c6d29fdbb880f1964847e674dc512d2f0311f0b7..d288c7eebacd8af046f23ef3fd3c68a28c34f998 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 a60e1c8d470a08734e1a5c02816fd112d2ec0a70..32e786a3952b1327a83fe310a98b72438e011d77 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 015de1e0addd6ee6f1f0b4067e3375d600404879..2847509a183d043a19cf26ef909df382b13f26a5 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 637687b766ff09637b45d563c0f30422a209f835..049a83b40e46925ddc31906bcdfecddf58d5dae7 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 58c7eb9d8e1b85893ea33c7de48426e5f555bdc5..938ace333af10ca38da2c253d7911fbe1a29e764 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; - - 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)); + if (!p_ptt) { + *p_media_type = MEDIA_UNSPECIFIED; + return -EINVAL; + } - 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 85e6b3989e7a913c7157f27ff27469e6cbf1f3aa..80a6b5d1ff3386b35b9e816e5b7b30b7427cb8e7 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 6ab3fb008139d5378f0ad6b9482d7f16dfe0cb2a..5dda547772c1363e9373f2d81ef72c6644936b85 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_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index f3d9c40c41159fd1eec1a3aa57dd2d50636dbb42..630b13a9c3d55afe0b8a41b65e6260ac50cf84c9 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"); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c index 4b76c69fe86d2aaa8609456277b767f1993bffe4..834208e55f7b8a7d1e2ac535096e64b263505178 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 016e209c4eadf8db58788875961b2961a5502dd0..d3487fccfd6db6c509c6a744fbf1903451283eaf 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_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 1b55d9ce4e8631d91e95b92233956a94dea2d8ca..122d0789958ee7a7c53db5680980d5ce6d1b16dc 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -131,6 +131,10 @@ struct rmnet_coal_stats { u64 coal_trans_invalid; struct rmnet_coal_close_stats close; u64 coal_veid[RMNET_MAX_VEID]; + u64 coal_tcp; + u64 coal_tcp_bytes; + u64 coal_udp; + u64 coal_udp_bytes; }; struct rmnet_priv_stats { diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c index 751a50ac779a888b55d09be6904ef21c9a33074d..ad54489304a5ba6a082a86c330a3adbae3e9880d 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c @@ -805,11 +805,17 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, th = (struct tcphdr *)((u8 *)iph + coal_desc->ip_len); coal_desc->trans_len = th->doff * 4; + priv->stats.coal.coal_tcp++; + priv->stats.coal.coal_tcp_bytes += + skb_frag_size(&coal_desc->frag); } else if (coal_desc->trans_proto == IPPROTO_UDP) { struct udphdr *uh; uh = (struct udphdr *)((u8 *)iph + coal_desc->ip_len); coal_desc->trans_len = sizeof(*uh); + priv->stats.coal.coal_udp++; + priv->stats.coal.coal_udp_bytes += + skb_frag_size(&coal_desc->frag); if (coal_desc->ip_proto == 4 && !uh->check) zero_csum = true; } else { diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 3488cf4a64a43eddda0beba2e10d235303e2d645..740ddada2af0b4ccad8c53b16e9ae5c5e160b42b 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/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 6186e6561b82b2c7d34670c9b18c10ced2087edb..813c412a4a0c93026d362b46165187cc5c12ca6a 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -223,6 +223,10 @@ static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { "Coalescing packets over VEID1", "Coalescing packets over VEID2", "Coalescing packets over VEID3", + "Coalescing TCP frames", + "Coalescing TCP bytes", + "Coalescing UDP frames", + "Coalescing UDP bytes", "Uplink priority packets", }; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 0c8b7146637e1537c621108ea40ea5b852c27b67..4ab87fe845427672a346f3d5aa38b447b3d9a968 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 9b6bf557a2f5ffde5fe405f17e3307014f0c6a05..e04af9546e52632f9e26320f8ad28169b769951a 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 5462d2e8a1b71aff17837ed13dceb49d4fccc398..faaf74073a1201abc144b32035cc376550930d72 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 7eeac3d6cfe898a9a4ef6df9378d8c6d29383ce1..1f971d31ec302c95b8a01a2b1b889383c4cf03d7 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 f21661532ed395565552ef4e95694a453d7358b8..cc8fbf398c0d7ad4bd5f91eec028f10f6f148612 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 b1b53f6c452f5267f8af304bf42bf68b0238703a..8355dfbb8ec3c71d01d6da5d6baad00829edf13a 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 b944828f9ea3ddfba9849ba0b1c6e66243cd8b78..8d6cff8bd16229655e84b65c9651838efa26b78b 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 f0afb88d7bc2b02de3dc1054ec2ec5803f452a35..ce4bfecc26c7aadfada60b1c2dd7b628c89a9075 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 d2caeb9edc044ce516d2be05a6ff00cbfd82052f..28d582c18afb9f5b9846428747fc5568c672e5e0 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 f27d67a4d3045a0164733675d813ebbf7c0a81db..09d25b87cf7c0ee09be03e785c76bc4a09bb7170 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-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 79c91526f3ecce6470a73d0c52fde876508852e2..fea286e14addcf2255b59926e2275d9fe6c2a2a6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -1199,7 +1199,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 d07520fb969e687aa6ee5c384c7e23ecf3a8e38a..62ccbd47c1db2b60319051718a51fe159d2e7a54 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c @@ -59,7 +59,9 @@ static int sun7i_gmac_init(struct platform_device *pdev, void *priv) 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/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index d42f47f6c632fe8618348d40fc609bfed5deef4a..644e42c181ee6030dee0222479daf314a290a1f5 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 f047b27971564ec06f59810a8092c5d068503edb..720b7ac77f3b3c08e428faaf16ba4cbae1b76275 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 7fe0d5e3392218ebea76ff524142a857e6b5e003..1468fa0a54e9b755884f808a3cd772b610a62b84 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 12539b357a78402dfc80a4a654761051a2fa6409..590172818b922f069a82765ff5042cd852093c27 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 d8f4c3f281505810620a3c2f03a22bb657a8cb2f..baa3088b475c758f3cbcf17bdf7cb0e222ba2caf 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 1ea0b016580a40a904b0da23cc49f7c2a159e75e..2b808d2482d60e740176b0bb9007cc8bef747e82 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 1afed85550c0a8cfd812c7b18a0b7f9788e77a3c..0b7a3eb06a651410802519ee37e0228c7be95d5b 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/cpts.c b/drivers/net/ethernet/ti/cpts.c index b96b93c686bf15581b89c05d347a2179ae9883cc..d7543811dfae2dfe528ba6f7f25394cde10b88e7 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 88d74aef218a2b7480a1e84f61ea9f8f63ad3e49..75237c81c63d65dda6951137bcd69ce43b5d65cd 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 003d0452d9cb157b1b7b2418341d2b9099342ceb..fbbf9b54b173bfc3d14c61253e83e51d3f4088db 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 d925b8203996691f1c380d70ff28de9f9c0b0a49..23417266b7ecc24aca2a0e562cd17985399c9cc3 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 9146068979d2c65160092fbd929ede4c0160acfe..03afc4d8c3ec1585b4d1e92fee653eff63414a83 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 60abc9250f56a7eac7025447626799e3b046c8f5..2241f98970926f6f64467b0e5fc7994dc07f9ee1 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 66b30ebd45ee8fcea48636ed19ba5db2a54e9c09..28764268a44f87d61bc9d6b1f8976fd6fb3fbecc 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 42f1f518dad6939300905f8b6218f4f31fabd212..c77c81eb7ab3be52c79fcfdae92cf598ecf2c3d4 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 d3eae123904575aba430883ab7a0ba4d28b8b07c..61a9843346ad7aa89a175686e51b95c3a2e3a407 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -1252,8 +1252,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 +1279,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 +1301,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/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 6f6c0dbd91fc84bb31cbb4411a0d1b665ac512dc..b7a71c203aa372fcbe5df39c953c24b203314448 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, diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 0dc92d2faa64dbeb9cefe3f9627c72e4e30f16d7..10a8ef2d025a16ad855f0978d96536c8ee44b7ff 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 6372cdc4a510976c536c3ee87829564cd8433cb1..78789dfbe29e4b219016827b938e789dc16bb82d 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -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 5a749dc25bec485359dd4b92d26e06404b7dc312..beeb7eb76ca32d0469a8edc16639d6ff02442f1b 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 b12023bc2cab5feb15ceedbe2fc357dfcf37627e..df8d49ad48c38ad9aaf5767878d96342f96d9b04 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 b2b6307d64a4de95d5ef29ba4d79dcbc12b5e1f1..acaf072bb4b0f3d2cdf807ecef963bcadbbd0d02 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 e4bf9e7d7583844451e9af8a575ede463cbdaa5a..eeadfde159401220d3fbbdfecd91776f6853a7b9 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) @@ -294,6 +302,35 @@ static int dp83867_config_init(struct phy_device *phydev) } } + 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; + } + /* Enable Interrupt output INT_OE in CFG3 register */ if (phy_interrupt_is_valid(phydev)) { val = phy_read(phydev, DP83867_CFG3); diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 8d370667fa1b3e5ada10b299ee35e35294bca798..df75efa96a7d95cecd6e4678f5dd5dfbaa9f4095 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -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 84ca9ff40ae0b0bedb3758f4618e6652db845eb4..36647b70b9a36a09e8ee52fc5dab4d19cd3680ec 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/slip/slip.c b/drivers/net/slip/slip.c index b008266e91eab6cfc8fb8e009f1940ef97211e55..77207f936871e510afceaee29926eb610450a16a 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/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 501576f538546392381471da43f0d2897df243bd..914cac55a7ae702b5a3a4dc7178effdac1d0eedb 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 85fba64c3fcf7b528197fa39f2a2c84a4b3ec1bd..c3cf9ae6d1df4a79a7793ec876025fdac4e6e6ed 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 f53e3e4e25f37666e3c7cea639ae6b2741645360..1f57a6a2b8a259fcebab2a51595abf559612a6f9 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 267978e23b937be032c7576abaed120f86bf05ad..457a06aa58250a2d6c377ea2be6128b7a8e2914b 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -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); @@ -3785,10 +3788,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 +3808,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 6f517e67302082a86d31455c1426ff16711abdd1..b55fd76348f9f8479158b808dc5a79708515c0e1 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 a291e5f2daef629935bfc015cd3c795ff1415d13..91d47a714afdf63829904faf9a5f0ea955207d2a 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 35f39f23d88144195b8f007035f207d38b48c1fd..8f8c9ede88c26cd6e870d2e12826263050cd32a6 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 9f895083bc0aad15a2cadfca2fba43d3406f12a8..7f5ee6bb4430063d45a6e83ecbfe95fd5969ba1f 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 0b1ec44acbf9f2d3cdde3ee6b248f8c5060c0649..d8a56df3933f05d6e149a03100d273bf92c2eed0 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; @@ -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 5f0366a125e2605b8c78f54c11004cd5ea37a533..0212f576a838ca2b4e798dc001345a449fa9ef80 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 b94759daeaccf68c84d71b9938627bb6a2b1c793..da2d179430ca5a39e20239f0a7c6215730c6dde3 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 c9bd0e2b5db7ed5a062fbb0a5d1abbdffc4e50ec..be90c9e9e5bc133697e6cf47f65b7d4ae6b48d0e 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 5210cffb53440bfc249403051eb5e4458ab17125..436eac342b62276aa7e39eaefa6a29521ef4f2f4 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 9feea02e7d3730c9350c6c6cfda722acb2512c19..5c9fc4070fd24490b2201aef2734f79b59ae7dbc 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/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 977f79ebb4fd5911bd8910d415e1ba6d492359ff..fac58c3c576a2054a4ef65a2b8d3657896a9037f 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 1419f9d1505fe2e9291eebdc81a1ac1e6e1176d2..613ca74f1b2867a7143e25812a9104cd4ff51ed2 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 #include #include #include @@ -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) @@ -4685,6 +4686,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 +8372,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 +8559,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 af2cf55c4c1e631ea075e5baa5742651b77435c1..2a503aacf0c645b6477d10fc452c355b5f996637 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 fa1843a7e0fdaaec3e74a96d9a8b359a921c9746..e2d78f77edb70cc681e83418228e5eb7bd1619be 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/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index f09a4ad2e9de71c25973ae553b94d0086606bfe0..f9c79e21ab22abfe044b9566f4425f05214a85f3 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 9f31b9a108507ebb94d7eda7bd6c0dee01d0342e..aefc92d2c09b930babb9d82af8342fffc34d48fd 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 36220258e3c7e686af0ffb5a2ab82869b8be5b01..e341cfb3fcc26845bb579ee1f4dbfa9088b1ae4f 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 e121187f371ff5e023b5efda39af6847b5a2c47a..d7c626d9594e153f8777427a21c24fb34fc48d2c 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 4defb7a0330f430c181c5873fee7376401e92611..53b66e9434c99845dbbaa8b31aaa0e74e0a67fc8 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 f019a20e5a1f8b01996d89f9bd8f7d85a1ae0b5a..983e1abbd9e43d26178f18c15c63b109642f33eb 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 440e16e641e4a6b774b0d6b715f1b059c5727198..f75eb068e6cfc4345d0746a4ba930f95d11f1a31 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/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1049773378f274e2f8c4ccf1050cb58f50858366..74f98bbaea889bae3b078c4ae7b3c968b74e7f9e 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 ce50d8f5835e03cf22cbb7f19c2bb1bc0b61296c..95544ce05acf9d35f7dc64b8f6dd5fb7c1e32b25 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/main.c b/drivers/net/wireless/ath/wil6210/main.c index 78920dd906a104ef25591183ee1df602692dd3b2..1e359ab173a0f6400abe5950b2186c4e51fb9993 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/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c7c520f327f2b89f4cd10eed64cb41a02a282865..bbdc6000afb9bf58b3c727338e3d2e959c2b644d 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 27893af63ebc33cbc3bf11f554c701314a54d521..8510d207ee87d1f5b2f65dfae112a19201d6ba5e 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/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 8347da632a5b0de85f9a68d9eedfd0436d2da915..4c5a3995dc352282e3243bd8a3ba608c55e17bf2 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 3e9c4f2f5dd12673e8c96e6dcacbea50cf7bc45e..456a1bf008b3d62242b386f9547dc1899e555b34 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 0e8b34d2d85cb1b3dbc0ab9716e93c78628b132c..39f0d0218088236f20cb64eec06875836c1a4f96 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 53e4962ceb8ae8199eec6894283a28ef3cc549dc..abaed2fa2defd39841109135529d848968e136c0 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 7faed831f07d5f59e5a485c4cb9e0af4b0f72338..34b031154da938a4de2cb56bb272165a760c0f3b 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 ecc89e718b9c146865b6e17917e3fb51023614aa..6188275b17e5aee8df963ee5c848a4d5d4921687 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 c4d135cff04ad2f7883c783fb96244bbbabfa370..9f76b880814e8201744a83256f8a89af8e4f0ba7 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 04dd7a936593826928cf11559b3b362d0ed43383..5512c7f73fce89ec231f42357d7ee257c3a6f7d7 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/Kconfig b/drivers/net/wireless/cnss2/Kconfig index ee0d0bd865cd12354ee4c98ab5c8bd16b9362f55..8fd35fb9f7beafdc8d9d9753a463a2f3cafabce5 100644 --- a/drivers/net/wireless/cnss2/Kconfig +++ b/drivers/net/wireless/cnss2/Kconfig @@ -21,6 +21,7 @@ config CNSS2_DEBUG config CNSS2_QMI bool "CNSS2 Platform Driver QMI support" + select CNSS_QMI_SVC depends on CNSS2 help CNSS2 platform driver uses QMI framework to communicate with WLAN @@ -82,4 +83,4 @@ config CNSS_QCA6490 This enables the changes from WLAN host driver that are specific to CNSS QCA6490 chipset. These changes are needed to support the new hardware architecture - for CNSS QCA6490 chipset. \ No newline at end of file + for CNSS QCA6490 chipset. diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index 023cefce939972af41ccc85c54569efea907c210..c031c5c90bd05a56514ddda45325b2c705873ce1 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -2,10 +2,11 @@ obj-$(CONFIG_CNSS2) += cnss2.o +ccflags-y += -I$(srctree)/drivers/net/wireless/cnss_utils/ cnss2-y := main.o cnss2-y += bus.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o -cnss2-$(CONFIG_CNSS2_DEBUG) += genl.o -cnss2-$(CONFIG_CNSS2_QMI) += qmi.o wlan_firmware_service_v01.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.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 33ca30a9ac06f01f9f84a17cb5b0f70d2c14efa1..d38ba5d315ba7f02170f272d969afea556b78418 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 1c3fb3a108e36bc83d5225e743f28c922bb01162..d52c5e5322f78caba255912b1eeb4c7023858e74 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 #include @@ -665,6 +665,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 +685,18 @@ 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); 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 +1962,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 +2022,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 +2078,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 +2173,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 +2254,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 b21fcbb3b3ef3e194bfffcb67ee03b6512cc680d..ef8939d91ae905f31eab5c7ed36a0ace091e9ac1 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 077e660912f095c4da6c66a0920762b50bfbaf23..840250a2da810b63a1708c734818da08888857aa 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.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 #include @@ -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; } @@ -638,6 +644,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 +660,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)) @@ -1078,6 +1091,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) { @@ -1618,6 +1687,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 +1722,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 +1736,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 +1763,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) @@ -2491,6 +2567,9 @@ int cnss_pci_pm_request_resume(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return 0; + status = dev->power.runtime_status; if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", @@ -2509,6 +2588,9 @@ int cnss_pci_pm_runtime_resume(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return 0; + status = dev->power.runtime_status; if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", @@ -2527,6 +2609,9 @@ int cnss_pci_pm_runtime_get(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return 0; + status = dev->power.runtime_status; if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", @@ -2545,6 +2630,9 @@ int cnss_pci_pm_runtime_get_sync(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return 0; + status = dev->power.runtime_status; if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", @@ -2558,6 +2646,9 @@ void cnss_pci_pm_runtime_get_noresume(struct cnss_pci_data *pci_priv) if (!pci_priv) return; + if (!pm_runtime_enabled(&pci_priv->pci_dev->dev)) + return; + pm_runtime_get_noresume(&pci_priv->pci_dev->dev); } @@ -2570,6 +2661,9 @@ int cnss_pci_pm_runtime_put_autosuspend(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return 0; + if (atomic_read(&dev->power.usage_count) == 0) { cnss_pr_dbg("Ignore excessive runtime PM put operation\n"); return -EINVAL; @@ -2587,6 +2681,9 @@ void cnss_pci_pm_runtime_put_noidle(struct cnss_pci_data *pci_priv) dev = &pci_priv->pci_dev->dev; + if (!pm_runtime_enabled(dev)) + return; + if (atomic_read(&dev->power.usage_count) == 0) { cnss_pr_dbg("Ignore excessive runtime PM put operation\n"); return; @@ -4062,6 +4159,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, @@ -4076,11 +4174,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", @@ -4181,6 +4278,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) { @@ -4188,11 +4286,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 5e9179d7999b34bae3fa64866c0180eaedfb9b4a..fb109fd98e9a2503c55824118d90c0b5e943a611 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -1,5 +1,5 @@ // 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 #include @@ -404,9 +404,11 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) resp->fw_version_info.fw_build_timestamp, QMI_WLFW_MAX_TIMESTAMP_LEN + 1); } - if (resp->fw_build_id_valid) + if (resp->fw_build_id_valid) { + resp->fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN] = '\0'; strlcpy(plat_priv->fw_build_id, resp->fw_build_id, QMI_WLFW_MAX_BUILD_ID_LEN + 1); + } if (resp->voltage_mv_valid) { plat_priv->cpr_info.voltage = resp->voltage_mv; cnss_pr_dbg("Voltage for CPR: %dmV\n", diff --git a/drivers/net/wireless/cnss2/reg.h b/drivers/net/wireless/cnss2/reg.h index 4052de4b78c5cd87ef600a428a6b323ec0d72dc8..6e7b70960625728b98874ec5383774a8b8c8a6e2 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/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig index 51d3ab45155086655ce865c0b55003c9be3fd829..2450fdd313b4049f46f5e22a44cc165230f1a578 100644 --- a/drivers/net/wireless/cnss_utils/Kconfig +++ b/drivers/net/wireless/cnss_utils/Kconfig @@ -5,4 +5,12 @@ config CNSS_UTILS help Add CNSS utilities support for the WLAN driver module. This feature enable wlan driver to use CNSS utilities APIs to set - and get wlan related information. \ No newline at end of file + and get wlan related information. + +config CNSS_QMI_SVC + bool "CNSS QMI SVC support" + help + Add CNSS QMI SVC support for the WLAN driver module. + This feature enable wlan driver to use CNSS QMI service APIs to set + and get wlan related information. + diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile index aa007fd5050833fefab5f08f4e2ca5fbcf6db201..1b7088904d2ec51369ef646098d7d117abf9a06a 100644 --- a/drivers/net/wireless/cnss_utils/Makefile +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o +obj-$(CONFIG_CNSS_QMI_SVC) += wlan_firmware_service_v01.o diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c similarity index 95% rename from drivers/net/wireless/cnss2/wlan_firmware_service_v01.c rename to drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c index 682a20b8895043a392419465562f1390a4fc266b..0964ffd3d4058eca3524a3cbc1f39a7045a0c9f7 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c @@ -1532,8 +1532,8 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_download_ind_msg_v01, - total_size_valid), + wlfw_initiate_cal_download_ind_msg_v01, + total_size_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1542,8 +1542,8 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_download_ind_msg_v01, - total_size), + wlfw_initiate_cal_download_ind_msg_v01, + total_size), }, { .data_type = QMI_OPT_FLAG, @@ -1552,8 +1552,8 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_initiate_cal_download_ind_msg_v01, - cal_data_location_valid), + wlfw_initiate_cal_download_ind_msg_v01, + cal_data_location_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1562,8 +1562,8 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_initiate_cal_download_ind_msg_v01, - cal_data_location), + wlfw_initiate_cal_download_ind_msg_v01, + cal_data_location), }, { .data_type = QMI_EOTI, @@ -1753,8 +1753,8 @@ struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_update_ind_msg_v01, - cal_data_location_valid), + wlfw_initiate_cal_update_ind_msg_v01, + cal_data_location_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1763,8 +1763,8 @@ struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_update_ind_msg_v01, - cal_data_location), + wlfw_initiate_cal_update_ind_msg_v01, + cal_data_location), }, { .data_type = QMI_EOTI, @@ -2787,8 +2787,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_dynamic_feature_mask_req_msg_v01, - mask_valid), + wlfw_dynamic_feature_mask_req_msg_v01, + mask_valid), }, { .data_type = QMI_UNSIGNED_8_BYTE, @@ -2797,8 +2797,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_dynamic_feature_mask_req_msg_v01, - mask), + wlfw_dynamic_feature_mask_req_msg_v01, + mask), }, { .data_type = QMI_EOTI, @@ -2815,8 +2815,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_dynamic_feature_mask_resp_msg_v01, - resp), + wlfw_dynamic_feature_mask_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -2826,8 +2826,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_dynamic_feature_mask_resp_msg_v01, - prev_mask_valid), + wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask_valid), }, { .data_type = QMI_UNSIGNED_8_BYTE, @@ -2836,8 +2836,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_dynamic_feature_mask_resp_msg_v01, - prev_mask), + wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask), }, { .data_type = QMI_OPT_FLAG, @@ -2846,8 +2846,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_dynamic_feature_mask_resp_msg_v01, - curr_mask_valid), + wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask_valid), }, { .data_type = QMI_UNSIGNED_8_BYTE, @@ -2856,8 +2856,8 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_dynamic_feature_mask_resp_msg_v01, - curr_mask), + wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask), }, { .data_type = QMI_EOTI, @@ -2943,8 +2943,8 @@ struct qmi_elem_info wlfw_qdss_trace_req_mem_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_qdss_trace_req_mem_ind_msg_v01, - mem_seg_len), + wlfw_qdss_trace_req_mem_ind_msg_v01, + mem_seg_len), }, { .data_type = QMI_STRUCT, @@ -2953,8 +2953,8 @@ struct qmi_elem_info wlfw_qdss_trace_req_mem_ind_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_qdss_trace_req_mem_ind_msg_v01, - mem_seg), + wlfw_qdss_trace_req_mem_ind_msg_v01, + mem_seg), .ei_array = wlfw_mem_seg_s_v01_ei, }, { @@ -2972,8 +2972,8 @@ struct qmi_elem_info wlfw_qdss_trace_mem_info_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_qdss_trace_mem_info_req_msg_v01, - mem_seg_len), + wlfw_qdss_trace_mem_info_req_msg_v01, + mem_seg_len), }, { .data_type = QMI_STRUCT, @@ -2982,8 +2982,8 @@ struct qmi_elem_info wlfw_qdss_trace_mem_info_req_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_qdss_trace_mem_info_req_msg_v01, - mem_seg), + wlfw_qdss_trace_mem_info_req_msg_v01, + mem_seg), .ei_array = wlfw_mem_seg_resp_s_v01_ei, }, { @@ -3001,8 +3001,8 @@ struct qmi_elem_info wlfw_qdss_trace_mem_info_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_qdss_trace_mem_info_resp_msg_v01, - resp), + wlfw_qdss_trace_mem_info_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -3040,8 +3040,8 @@ struct qmi_elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_save_ind_msg_v01, - mem_seg_valid), + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg_valid), }, { .data_type = QMI_DATA_LEN, @@ -3050,8 +3050,8 @@ struct qmi_elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_save_ind_msg_v01, - mem_seg_len), + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg_len), }, { .data_type = QMI_STRUCT, @@ -3060,8 +3060,8 @@ struct qmi_elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_save_ind_msg_v01, - mem_seg), + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg), .ei_array = wlfw_mem_seg_resp_s_v01_ei, }, { @@ -3071,8 +3071,8 @@ struct qmi_elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_qdss_trace_save_ind_msg_v01, - file_name_valid), + wlfw_qdss_trace_save_ind_msg_v01, + file_name_valid), }, { .data_type = QMI_STRING, @@ -3226,8 +3226,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - total_size_valid), + wlfw_qdss_trace_config_download_req_msg_v01, + total_size_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -3236,8 +3236,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - total_size), + wlfw_qdss_trace_config_download_req_msg_v01, + total_size), }, { .data_type = QMI_OPT_FLAG, @@ -3246,8 +3246,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - seg_id_valid), + wlfw_qdss_trace_config_download_req_msg_v01, + seg_id_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -3256,8 +3256,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x11, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - seg_id), + wlfw_qdss_trace_config_download_req_msg_v01, + seg_id), }, { .data_type = QMI_OPT_FLAG, @@ -3266,8 +3266,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x12, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - data_valid), + wlfw_qdss_trace_config_download_req_msg_v01, + data_valid), }, { .data_type = QMI_DATA_LEN, @@ -3276,8 +3276,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x12, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - data_len), + wlfw_qdss_trace_config_download_req_msg_v01, + data_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, @@ -3286,8 +3286,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x12, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - data), + wlfw_qdss_trace_config_download_req_msg_v01, + data), }, { .data_type = QMI_OPT_FLAG, @@ -3296,8 +3296,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x13, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - end_valid), + wlfw_qdss_trace_config_download_req_msg_v01, + end_valid), }, { .data_type = QMI_UNSIGNED_1_BYTE, @@ -3306,8 +3306,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x13, .offset = offsetof(struct - wlfw_qdss_trace_config_download_req_msg_v01, - end), + wlfw_qdss_trace_config_download_req_msg_v01, + end), }, { .data_type = QMI_EOTI, @@ -3324,8 +3324,8 @@ struct qmi_elem_info wlfw_qdss_trace_config_download_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_qdss_trace_config_download_resp_msg_v01, - resp), + wlfw_qdss_trace_config_download_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -3343,8 +3343,8 @@ struct qmi_elem_info wlfw_qdss_trace_mode_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_mode_req_msg_v01, - mode_valid), + wlfw_qdss_trace_mode_req_msg_v01, + mode_valid), }, { .data_type = QMI_SIGNED_4_BYTE_ENUM, @@ -3410,8 +3410,8 @@ struct qmi_elem_info wlfw_qdss_trace_free_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_free_ind_msg_v01, - mem_seg_valid), + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg_valid), }, { .data_type = QMI_DATA_LEN, @@ -3420,8 +3420,8 @@ struct qmi_elem_info wlfw_qdss_trace_free_ind_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_free_ind_msg_v01, - mem_seg_len), + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg_len), }, { .data_type = QMI_STRUCT, @@ -3430,8 +3430,8 @@ struct qmi_elem_info wlfw_qdss_trace_free_ind_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_qdss_trace_free_ind_msg_v01, - mem_seg), + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg), .ei_array = wlfw_mem_seg_resp_s_v01_ei, }, { @@ -3501,8 +3501,8 @@ struct qmi_elem_info wlfw_antenna_switch_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_antenna_switch_resp_msg_v01, - resp), + wlfw_antenna_switch_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -3512,8 +3512,8 @@ struct qmi_elem_info wlfw_antenna_switch_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_antenna_switch_resp_msg_v01, - antenna_valid), + wlfw_antenna_switch_resp_msg_v01, + antenna_valid), }, { .data_type = QMI_UNSIGNED_8_BYTE, @@ -3522,8 +3522,8 @@ struct qmi_elem_info wlfw_antenna_switch_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_antenna_switch_resp_msg_v01, - antenna), + wlfw_antenna_switch_resp_msg_v01, + antenna), }, { .data_type = QMI_EOTI, @@ -3540,8 +3540,8 @@ struct qmi_elem_info wlfw_antenna_grant_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_antenna_grant_req_msg_v01, - grant_valid), + wlfw_antenna_grant_req_msg_v01, + grant_valid), }, { .data_type = QMI_UNSIGNED_8_BYTE, @@ -3550,8 +3550,8 @@ struct qmi_elem_info wlfw_antenna_grant_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_antenna_grant_req_msg_v01, - grant), + wlfw_antenna_grant_req_msg_v01, + grant), }, { .data_type = QMI_EOTI, @@ -3568,8 +3568,8 @@ struct qmi_elem_info wlfw_antenna_grant_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_antenna_grant_resp_msg_v01, - resp), + wlfw_antenna_grant_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -3587,8 +3587,8 @@ struct qmi_elem_info wlfw_wfc_call_status_req_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_wfc_call_status_req_msg_v01, - wfc_call_status_len), + wlfw_wfc_call_status_req_msg_v01, + wfc_call_status_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, @@ -3597,8 +3597,8 @@ struct qmi_elem_info wlfw_wfc_call_status_req_msg_v01_ei[] = { .array_type = VAR_LEN_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct - wlfw_wfc_call_status_req_msg_v01, - wfc_call_status), + wlfw_wfc_call_status_req_msg_v01, + wfc_call_status), }, { .data_type = QMI_EOTI, @@ -3615,8 +3615,8 @@ struct qmi_elem_info wlfw_wfc_call_status_resp_msg_v01_ei[] = { .array_type = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct - wlfw_wfc_call_status_resp_msg_v01, - resp), + wlfw_wfc_call_status_resp_msg_v01, + resp), .ei_array = qmi_response_type_v01_ei, }, { @@ -3766,3 +3766,66 @@ struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; + +struct qmi_elem_info wlfw_device_info_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info wlfw_device_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_device_info_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_device_info_resp_msg_v01, + bar_addr_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_device_info_resp_msg_v01, + bar_addr), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_device_info_resp_msg_v01, + bar_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_device_info_resp_msg_v01, + bar_size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h similarity index 98% rename from drivers/net/wireless/cnss2/wlan_firmware_service_v01.h rename to drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h index 0e18d8000c3f36eeb5fa20fa72698c2b83e94ddf..a398d2f77c83f2b817a604d4831eec72a3d26b77 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h @@ -10,6 +10,7 @@ #define WLFW_SERVICE_VERS_V01 0x01 #define QMI_WLFW_WFC_CALL_STATUS_REQ_V01 0x0049 +#define QMI_WLFW_DEVICE_INFO_RESP_V01 0x004C #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 #define QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01 0x0044 @@ -73,6 +74,7 @@ #define QMI_WLFW_SHUTDOWN_REQ_V01 0x0043 #define QMI_WLFW_VBATT_REQ_V01 0x0032 #define QMI_WLFW_ANTENNA_SWITCH_RESP_V01 0x0047 +#define QMI_WLFW_DEVICE_INFO_REQ_V01 0x004C #define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033 #define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 #define QMI_WLFW_VBATT_RESP_V01 0x0032 @@ -984,4 +986,22 @@ struct wlfw_respond_get_info_ind_msg_v01 { #define WLFW_RESPOND_GET_INFO_IND_MSG_V01_MAX_MSG_LEN 6164 extern struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[]; +struct wlfw_device_info_req_msg_v01 { + char placeholder; +}; + +#define WLFW_DEVICE_INFO_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct qmi_elem_info wlfw_device_info_req_msg_v01_ei[]; + +struct wlfw_device_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 bar_addr_valid; + u64 bar_addr; + u8 bar_size_valid; + u32 bar_size; +}; + +#define WLFW_DEVICE_INFO_RESP_MSG_V01_MAX_MSG_LEN 25 +extern struct qmi_elem_info wlfw_device_info_resp_msg_v01_ei[]; + #endif diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index b4347806a59ed31f1e50d361094ee728af1736d5..d7335fabd929454eb404b136eb096a87ea7eb412 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/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 030482b357a3c3038bcf456f2c782b04747fa70f..06dd4e81b73745c3bfb6eebff4f0178658d7f61c 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 2f599353c8856b4c9604e4953722771c7fef4837..2ba1401e5c0d526c3336b9090d9c911b9b9b131d 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 514b86123d3d366fd368e9d49f7d10222f77bae5..80853f6cbd6d225aa71f16bba67254d770455f5e 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 a31a42e673c46fffec58ac95acbda96a442d803c..3443cbdbab4ae2a49bf8d58cefde92a0a79fbfd8 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 a4c96215933ba589d2bbbc272728c901c80a4214..a59bab8345f4ea83f4eb659299c1f2706921548f 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 8be50ed12300f5b16df34be38c9ad7d7ca5b41eb..c59dd47cf15d332ce7b39ac15f04d91df03322c6 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 421a869633a32d861919dc80f11f23500f398ace..2e512f6e9ebcde4af31c7f6cf25710d40bd47871 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 279dd7b7a3fb921c615dcab4fe769f926f7e896d..0b8cf7f3af93304116152bf9a6b9a5273507226d 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 79bdae994822844bcc07bb8b63b617f59982d042..868cb1195a74b23e7dbf5729e464cd7fe7fa21d9 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 9cb9f0544c9b172aecbb0d842516bfae1d1aab43..9808d954dca2942d566ecde8a292cf4b6432ab3a 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/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index b3fd20502abb3c604352fbc9d5aa84256f40a4f6..d90d58309bf0e8d4a27124fd5c47112daab39f42 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 0f357e8c4f94007b48661aff5cea0087296d5401..476c44db0e64b1c5a0325a1e24bbd4aed6d42be0 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/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 18db1ed92d9b09741e0fec60e66389bd5af18dea..e850aa504b6084c94c55ad7d37ee79e6da309701 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 5615ce55cef56a271609d353c8986d92bb62f134..449e3d32811a67d7e9fbc8a5ab389aece7423b32 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 6a5349401aa999aec7280af0c7d2376e4255e3db..00712205c05f2e9df1127842e1a71abde5d9e4bd 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 0982bd99b1c3cfd03aa04ed57210c0aa414b1635..844a1009484f6b297752d7e35b3178f8335069f5 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 00f9566bcc2136ab588beb05fada23eeea2b60f1..e9d67ba3e56dd8ad66b6ce48d0c7b170f689bb16 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 1d144985ea589d7a285c04279a70e5c09b570647..80a1a50f5da51034982215162326ce3f73d5c58c 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 7d319b6863feb2e14e8702907603c3f13b43de53..4f5571123f70aff8c31790f597244dbea16a4e0e 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 b99f33ff912306f5638bb95f9578df88b82aadc4..7b1dff92b7094ff10f8e04fb59d4ea54ee07b87c 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 42fdb7970cfdcb6608934ddf25c72087628199dd..b73582ec03a085fa2840a1e6259e64931ef6bfb3 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/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 47ec5293c045df4d6c11faeda4a183e36baf632d..7b74ef71bef1d9f09ca8515146038e246da3fa8b 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 cce70252fd96b5b57791f89f5ca902ce5b00bb0b..cbe4493b32664cba530fc97d744e560dc1934224 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 48e154e1865df321e12f4f160d51085d599d8721..0dd592ea6e8332a26829483613c047ad5d77f5aa 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 20cee5c397fb6f4d72dc6378401ee9bcf2af6708..e48b47f425540a7319d7d64ce3360b5bbaca807b 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/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index ed27147efcb37b9d8318b2aaac2d8620ac7adbd6..dd02bbd9544e7c75ae77c8b46acf7aac941887b1 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 843d65bba1811ee2f903b1498febd26ee21a7687..74e50566db1f2711d3c35a3bc31c0f9bdb767b2c 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/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 433c6a16870b6b5ef92b782dd13a3cf95f7a520e..d445acc4786b75ce562477f9579dbd9a1fed0485 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 ade4a2029a24a7e56ee12f85c2c13eec65b7fa82..1b5abd4816ed79eb7585db17bf98fa6145c04a38 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 14e8c575f6c3ec83ec208c53b53890fd37b5f0d0..924c761f34fd9fbb856e58dc19a38864bc539d2a 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 751b49c28ae53f0fbe255ac6d6a3216e1a4eef1a..c45d05d5aab1d207fb319d6bc43da72e36a2a134 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 324b2a4b8b67cf3bec9c4006a37063f2bad75a31..54a9e1dfaf7a403a2a15e597177edb847d75c1e2 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 e66f047ea4481b2b4a13b670528360625e010a97..26cfda24ce0854c15ce3c535004449e22c2893cd 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 9fd6ab4cbb949e617ee64527a9e2fa6a12f6c3fd..ca68dd184489be9e29362ec878c2fee7f08efc94 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 36afb166fa3ffd29f24414e462961bca03545a6e..c0ca0df84ed8b86aa029c5edbe554f53888fe4cd 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 20447fdce4c33770553e017331f6f70df6b8c883..227e5ebfe3dc2c1bd0f146bb88387d1d4554b02e 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 4aa332f4646b1b79396b55ca6c3c2d2b552debdf..ff8a46c9595e1bf1d2d6bded95c40425ab81ce51 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 ae9e773005339b5ef22f23d5593297ffcbe04a7a..734844b34c2667fc77a5667906ecf5acea2eea93 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 19abbc4e23e068498118b562833197c053e999c4..08928d5e252d7a83944658ffd1bbb6540bcd52bc 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 a1e338a1f055a7ea906c5fc8ecfd979db84efe03..ecb5c41c8ed7618467d6a4bbdc47fc50182330aa 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 99d37e3efba634e0e649349259eeaf5b5a4a72b9..c5ae4ea9a47a9202a944ffc6c0b4daf997b56c1d 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 9a1d15b3ce4535540184d34a5600be131ba329dd..518caaaf8a98765647b81cbc4f099f29d7a1534b 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 c2d5b495c179a1021dd4cd4221c0032f3a99e34a..c089540116fa72e6952c5a2670aaf32cb99a53e8 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_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 505ab1b055ff43cecd95dabf6634bfbc4f7c6dad..2b4fcdf4ec5bb96f1efaaf6b1ef0e12ae3d8e9b7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5691,6 +5691,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 b026e80940a4dc6fa57d790ca9787b59a57d7c06..6fbf8845a2ab6d03220df3d3735ab2113dc06fcc 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/rtl8192de/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c index 85cedd083d2b89cd8870485e45b6ac285fa1e2cb..75bfa9dfef4aa2d5f90eab8331807216b7652afc 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 80123fd9722198afd5ce68d816ac0283626f2fce..ee5ff7255090eef986b93753da38c90e0c9a2e64 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 d5ba2bace79bb480bd9285b8cf0d146b01c82c24..2b0c0308d2817ba0951527d50e1534a5f22cfa4f 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 d7b023cf74001db24145a5454950074e0ff97100..76f12247184aa4ddd94d229f7ad2815cb2120a0c 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 f7f776539438942e50a2f2e2b3dcbd8601818f27..3d026e518c02572cdb1f82978046124d7962812a 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/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 1095df7d957350f20b218bd32c8dadb30d3d9153..1a3a5235cfb8d4ae0c86a7f6959431f886efc126 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 dbe78d8491effa32a3356d8a4cd238de5ae79d88..7f34ec077ee57000e49fc094042ceb834c680ff2 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 27b6b141cb71f3be8c3f64d6fd41387b9c70bbe8..4cafc31b98b7c6f095d0d3f92134d03d812f5b83 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 a3bc8f0e56ab3e82ba868cb65ffbda84e690c4dd..159dc9ed0bad9389568b9d04b71547913c744aba 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 ba695e392c3b7b1c4d85c9e877d1234c23502caf..0df745cad601a03b64ee1f7113fd293058984ee1 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 5d823e965883b0f5f23db5ab39afc9f96a128267..fcb57d64d97e60d37dbc30364a4fc5d222f4d1a4 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 bb43cebda9dcd39ec3aa3a5a729758f987354882..60ae382f50da9887ad900a14807835490eb6bee3 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 e803fdfa918977f6703785253f06a16402a80387..f37069b53b20030ea99d7c5094d45950cef71520 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 6aa57322727916bd5bc1c8e5ab13f286f8fd1b1d..2ad263f708da7ab68b12c9767058df9505501013 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/nvme/host/core.c b/drivers/nvme/host/core.c index 5d0f99bcc987f4fb2350a9aac5b086717e271585..b2d9bd564960aba4a6c2c3c5a7bb57ec4e608c2f 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -551,9 +551,19 @@ 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; + range = kmalloc_array(segments, sizeof(*range), + 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 +578,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; } @@ -653,8 +666,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 +3569,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 +3611,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 +3652,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 +3676,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/lightnvm.c b/drivers/nvme/host/lightnvm.c index 6fe5923c95d4aa1f553c76890e84ffd243b6dcdf..a69553e75f38e0f4eecdee734d6e72c0f8327819 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 892ef52122329c3c8068ea87936dea8cffa9810f..838ee58d80cda8c5404e394a00ff7ea195b55a42 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 2653e1f4196d508fdf7c11c4d8306fbdf1373f35..cc4273f1198943867e6dc0388af18dcd3f0fff1e 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 a64a8bca0d5b9dcfe9056f9207568f2a3dd1e0b7..124f41157173ea0c4bc21f90702e877c0b4a6ae5 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 5251689a1d9ac2e5a5852c724f7f54d2ec80801c..291f4121f516ad887f7f842761e92d3ab486196b 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) { diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c index 81a9dc5290a8744b3f022aec8338098986b23967..39d972e2595f0dc764f2a5ac37d589422068c139 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 6313dbad9d6b796d0f08398f9029cd3104229be7..67196c3928b0113874cef26d31fa8bb11a2580f8 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/of/base.c b/drivers/of/base.c index 3f21ea6a90dcba1e0bf2432308fdfc3a64988613..f0dbb7ad88cf68019581399178c7c59d2f49080a 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/fdt.c b/drivers/of/fdt.c index 3d150678ade3d089e88ce4cbee586a13456a173e..9c73c848879a657eae1666da521579c5d169d2c1 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -79,6 +79,69 @@ void of_fdt_limit_memory(int limit) } } +/** + * of_fdt_get_ddrhbb - Return the highest bank bit of ddr on the current device + * + * On match, returns a non-zero positive value which matches the highest bank + * bit. + * Otherwise returns -ENOENT. + */ +int of_fdt_get_ddrhbb(int channel, int rank) +{ + int memory; + int len; + int ret; + /* Single spaces reserved for channel(0-9), rank(0-9) */ + char pname[] = "ddr_device_hbb_ch _rank "; + fdt32_t *prop = NULL; + + memory = fdt_path_offset(initial_boot_params, "/memory"); + if (memory > 0) { + snprintf(pname, sizeof(pname), + "ddr_device_hbb_ch%d_rank%d", channel, rank); + prop = fdt_getprop_w(initial_boot_params, memory, + pname, &len); + } + + if (!prop || len != sizeof(u32)) + return -ENOENT; + + ret = fdt32_to_cpu(*prop); + + return ret; +} + +/** + * of_fdt_get_ddrrank - Return the rank of ddr on the current device + * + * On match, returns a non-zero positive value which matches the ddr rank. + * Otherwise returns -ENOENT. + */ +int of_fdt_get_ddrrank(int channel) +{ + int memory; + int len; + int ret; + /* Single space reserved for channel(0-9) */ + char pname[] = "ddr_device_rank_ch "; + fdt32_t *prop = NULL; + + memory = fdt_path_offset(initial_boot_params, "/memory"); + if (memory > 0) { + snprintf(pname, sizeof(pname), + "ddr_device_rank_ch%d", channel); + prop = fdt_getprop_w(initial_boot_params, memory, + pname, &len); + } + + if (!prop || len != sizeof(u32)) + return -ENOENT; + + ret = fdt32_to_cpu(*prop); + + return ret; +} + /** * of_fdt_get_ddrtype - Return the type of ddr (4/5) on the current device * diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 2edb59039b5f5786527c203f8c61121f9511ab29..514528b3566ffcb787a5e790be0c122844fa029f 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 b98f2514df4b35b42c38bc4b2b7abef697b8ff75..5728490474f6bd2d628a23ec491fe2cf106645b2 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 25cf397b8f6b67f964646d4b310e5daf8b76b10e..4ea024d908ee22d6eae398c2f6ef8f6c59c12431 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 7f42314da6ae325b1099280e1d05278e72d839d6..808571f7f6ef9b97d44280878bd67acd411584ef 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 0595d66b579318489250e1a3c2b5552c4121cb10..31eb950ea3a7a031ffff52003217b6c0a5c5c06e 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/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 5e199e7d2d4fd12ea3d29d1153f01a033ce1c9b2..765357b87ff695624e9feff0c4b47953fcf0ecb9 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 c7e5f9d8e17f16313afbf42dc2a2d08bb07ea1f7..13e80d36fe370a51c57eaefb633c3f6ac5636108 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -626,6 +626,7 @@ struct msm_pcie_drv_info { u16 seq; u16 reply_seq; u32 timeout_ms; /* IPC command timeout */ + u32 l1ss_timeout_us; struct completion completion; }; @@ -5646,7 +5647,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 +5655,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 +5676,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; @@ -6997,11 +6997,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 +7026,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 +7141,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 976eaa9a9f2662e71444d35f20d7d384a765eaab..58e48735285337292d9ae0c1b78f5654ed6ed6b5 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 6692654798d44dc00e0319eb9b2692aefcff6178..c3a088910f48d119f67918066be0be39302ed04f 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 c5ff6ca65eab26aaee471ef505950d79d02c408a..1bfbceb9f445867c18fcc896538340fcb1980b29 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 9b9c677ad3a0b648b7bf9e154ac86fd6e52e9582..333ab6092f174d8463b54b169a5a5c834c49a05c 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 65eaa6b61868585d3258f17d178f326eaf1bf4ad..ab36e5ca1aca39ddf643f22d777cf009fe7bd633 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 12afa7fdf77e9569d78f517a77b01de129151937..c94c135254479848fe31fa16dc783eb23d90fad7 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 811cf83f956de4db38856abdc79712a19494c1ef..ef60718070728c227ffd954f2d3777292e5ffd53 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 ec48c9433ae507cd5e61157adbc0c50551a9f1ee..518c46f8e63b7348a81eae21b8c1a8fb0b08adaf 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 9c397fa8704c65974da2ffc7af4c1bcbac78d3ef..c71964e29b0135537ad6815aa8bc2cfc49cac004 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 a938abdb41ceeb575dc62af740d78d278104280c..c3e3f5358b3ba123127f23816d7aa51686f095ed 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/msi.c b/drivers/pci/msi.c index af24ed50a2452008e32dc837834682a506739fd9..23a363fd4c59cf91e4a098dae390a8d0fd626022 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 54a8b23bb7231aac51d267de56356e36e2b3a6aa..de65154196acf4930349b0b61e48288b44f91eb8 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 2baf1f82f89333e22cdfd2d24087f884c1ab5fb3..c9f51fc24563c9594dc17ee38c93d946fd862682 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -35,6 +35,8 @@ #include #include "pci.h" +DEFINE_MUTEX(pci_slot_mutex); + const char *pci_power_names[] = { "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown", }; @@ -5191,6 +5193,41 @@ static int pci_bus_reset(struct pci_bus *bus, int probe) return ret; } +/** + * 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 ab25752f00d96ad3b8e3fb4036a4106a64003a86..e9ede82ee2c259b52ee60fbd59ff92214e24e689 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 83180edd6ed470d5fa57f08833e8178d91a18d92..1563e22600ecab220306d7d616b093b9ef9b7355 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 1908dd2978d3c4fff2b4010be91ed2911a83abcf..118b5bcae42eab4448cfbb562ea38013bb92985f 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 708fd3a0d6466063e825383983206ced863adf4a..2c3b5bd59b18f7edbc1c3dcd180388b43b9bb06a 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 e85c5a8206c43d2b84d1456fe8fc0b4716e2ba38..54d593d10396ff9716c76a0b01eecb5a632f984c 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 d59afa42fc14ba3702b26acb704dc9726442360d..2498b2d340095e53cf2191bdf992ab866139614a 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 eef22dc29140cd104318de4f332ab1dbb3e88034..23a5a0c2c3fe9021252475e97eef03789d7d0d5d 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 06be52912dcdb201347737dbefb44e6aaa2db910..20a57a48ae1e4f7058a43b8c2b084253803dafba 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 e634229ece8957607b881e4f1e93c6b7028c85fe..a32897f83ee517312a0d48a8e9449ea68c15687d 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/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index 8786a9674471de5a892238b7ad063a9aa7b68356..aa917a61071db97bfbc84de84386a6aa4b196d21 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -60,7 +60,8 @@ config PHY_NS2_USB_DRD 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 986224fca9e913ca521695376bdb818cbec28612..5a180f71d8d4d1ae9843d8747794de75468764cc 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/Makefile b/drivers/phy/qualcomm/Makefile index 9ee110cadcaa2f0d41857fe59146ae80b5026eeb..7e3ea97f47ea7a7cca5ecc3971df211807a55995 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qrbtc-sdm845.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v4.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v4-lito.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3-660.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3.o obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.c new file mode 100644 index 0000000000000000000000000000000000000000..96fe6d43559819d0b54ec7fd8fa54bc72fb2622d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2013-2016, 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 + * 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. + * + */ + +#include "phy-qcom-ufs-qmp-v3.h" + +#define UFS_PHY_NAME "ufs_phy_qmp_v3" + +static +int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B, bool is_g4) +{ + /* + * Writing PHY calibration in this order: + * 1. Write Rate-A calibration first (1-lane mode). + * 2. Write 2nd lane configuration if needed. + * 3. Write Rate-B calibration overrides + */ + ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_A, + ARRAY_SIZE(phy_cal_table_rate_A)); + if (ufs_qcom_phy->lanes_per_direction == 2) + ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_2nd_lane, + ARRAY_SIZE(phy_cal_table_2nd_lane)); + if (is_rate_B) + ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_B, + ARRAY_SIZE(phy_cal_table_rate_B)); + /* flush buffered writes */ + mb(); + + return 0; +} + +static int ufs_qcom_phy_qmp_v3_init(struct phy *generic_phy) +{ + struct ufs_qcom_phy_qmp_v3 *phy = phy_get_drvdata(generic_phy); + struct ufs_qcom_phy *phy_common = &phy->common_cfg; + int err; + + err = ufs_qcom_phy_init_clks(phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n", + __func__, err); + goto out; + } + + err = ufs_qcom_phy_init_vregulators(phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n", + __func__, err); + goto out; + } + +out: + return err; +} + +static int ufs_qcom_phy_qmp_v3_exit(struct phy *generic_phy) +{ + return 0; +} + +static +void ufs_qcom_phy_qmp_v3_power_control(struct ufs_qcom_phy *phy, + bool power_ctrl) +{ + if (!power_ctrl) { + /* apply analog power collapse */ + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Make sure that PHY knows its analog rail is going to be + * powered OFF. + */ + mb(); + } else { + /* bring PHY out of analog power collapse */ + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + } +} + +static inline +void ufs_qcom_phy_qmp_v3_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +{ + /* + * v3 PHY does not have TX_LANE_ENABLE register. + * Implement this function so as not to propagate error to caller. + */ +} + +static +void ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl) +{ + u32 temp; + + temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE); + + if (ctrl) /* enable RX LineCfg */ + temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT; + else /* disable RX LineCfg */ + temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT; + + writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE); + /* make sure that RX LineCfg config applied before we return */ + mb(); +} + +static inline void ufs_qcom_phy_qmp_v3_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); + tmp &= ~MASK_SERDES_START; + tmp |= (1 << OFFSET_SERDES_START); + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qmp_v3_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, + val, (val & MASK_PCS_READY), 10, 1000000); + if (err) { + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + goto out; + } + +out: + return err; +} + +static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy) +{ + ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE, + "PHY QSERDES COM Registers "); + ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE, + "PHY Registers "); + ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE, + "PHY RX0 Registers "); + ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE, + "PHY TX0 Registers "); + ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE, + "PHY RX1 Registers "); + ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE, + "PHY TX1 Registers "); +} + +struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = { + .init = ufs_qcom_phy_qmp_v3_init, + .exit = ufs_qcom_phy_qmp_v3_exit, + .power_on = ufs_qcom_phy_power_on, + .power_off = ufs_qcom_phy_power_off, + .owner = THIS_MODULE, +}; + +struct ufs_qcom_phy_specific_ops phy_v3_ops = { + .calibrate_phy = ufs_qcom_phy_qmp_v3_phy_calibrate, + .start_serdes = ufs_qcom_phy_qmp_v3_start_serdes, + .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v3_is_pcs_ready, + .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_set_tx_lane_enable, + .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg, + .power_control = ufs_qcom_phy_qmp_v3_power_control, + .dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump, +}; + +static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qmp_v3 *phy; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg, + &ufs_qcom_phy_qmp_v3_phy_ops, &phy_v3_ops); + + if (!generic_phy) { + dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", + __func__); + err = -EIO; + goto out; + } + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy->common_cfg.name, UFS_PHY_NAME, + sizeof(phy->common_cfg.name)); + +out: + return err; +} + +static const struct of_device_id ufs_qcom_phy_qmp_v3_of_match[] = { + {.compatible = "qcom,ufs-phy-qmp-v3"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_of_match); + +static struct platform_driver ufs_qcom_phy_qmp_v3_driver = { + .probe = ufs_qcom_phy_qmp_v3_probe, + .driver = { + .of_match_table = ufs_qcom_phy_qmp_v3_of_match, + .name = "ufs_qcom_phy_qmp_v3", + }, +}; + +module_platform_driver(ufs_qcom_phy_qmp_v3_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.h new file mode 100644 index 0000000000000000000000000000000000000000..34ff463bbab2518f7ea141096607bbe76262d741 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v3.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013-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 + * 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. + * + */ + +#ifndef UFS_QCOM_PHY_QMP_V3_H_ +#define UFS_QCOM_PHY_QMP_V3_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ +#define COM_BASE 0x000 +#define COM_SIZE 0x18C +#define PHY_BASE 0xC00 +#define PHY_SIZE 0x1DC +#define TX_BASE(n) (0x400 + (0x400 * n)) +#define TX_SIZE 0x128 +#define RX_BASE(n) (0x600 + (0x400 * n)) +#define RX_SIZE 0x1FC +#define COM_OFF(x) (COM_BASE + x) +#define PHY_OFF(x) (PHY_BASE + x) +#define TX_OFF(n, x) (TX_BASE(n) + x) +#define RX_OFF(n, x) (RX_BASE(n) + x) + +/* UFS PHY QSERDES COM registers */ +#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00) +#define QSERDES_COM_ATB_SEL2 COM_OFF(0x04) +#define QSERDES_COM_FREQ_UPDATE COM_OFF(0x08) +#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) +#define QSERDES_COM_SSC_EN_CENTER COM_OFF(0x10) +#define QSERDES_COM_SSC_ADJ_PER1 COM_OFF(0x14) +#define QSERDES_COM_SSC_ADJ_PER2 COM_OFF(0x18) +#define QSERDES_COM_SSC_PER1 COM_OFF(0x1C) +#define QSERDES_COM_SSC_PER2 COM_OFF(0x20) +#define QSERDES_COM_SSC_STEP_SIZE1 COM_OFF(0x24) +#define QSERDES_COM_SSC_STEP_SIZE2 COM_OFF(0x28) +#define QSERDES_COM_POST_DIV COM_OFF(0x2C) +#define QSERDES_COM_POST_DIV_MUX COM_OFF(0x30) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) +#define QSERDES_COM_CLK_ENABLE1 COM_OFF(0x38) +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +#define QSERDES_COM_SYSCLK_BUF_ENABLE COM_OFF(0x40) +#define QSERDES_COM_PLL_EN COM_OFF(0x44) +#define QSERDES_COM_PLL_IVCO COM_OFF(0x48) + +/* UFS PHY registers */ +#define UFS_PHY_PHY_START PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB PHY_OFF(0x08) +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB PHY_OFF(0x0C) +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x2C) +#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x30) +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x34) +#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x38) +#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x130) +#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x134) +#define UFS_PHY_RX_MIN_HIBERN8_TIME PHY_OFF(0x138) +#define UFS_PHY_RX_SIGDET_CTRL1 PHY_OFF(0x13C) +#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x140) +#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x14C) +#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x160) +#define UFS_PHY_TX_MID_TERM_CTRL1 PHY_OFF(0x1BC) +#define UFS_PHY_MULTI_LANE_CTRL1 PHY_OFF(0x1C4) +#define QSERDES_COM_CMN_IETRIM COM_OFF(0x4C) +#define QSERDES_COM_CMN_IPTRIM COM_OFF(0x50) +#define QSERDES_COM_EP_CLOCK_DETECT_CTR COM_OFF(0x54) +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS COM_OFF(0x58) +#define QSERDES_COM_CLK_EP_DIV COM_OFF(0x5C) +#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x60) +#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x64) +#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x68) +#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x6C) +#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x70) +#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x74) +#define QSERDES_COM_PLL_CNTRL COM_OFF(0x78) +#define SERDES_COM_BIAS_EN_CTRL_BY_PSM COM_OFF(0x7C) +#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0x80) +#define QSERDES_COM_CML_SYSCLK_SEL COM_OFF(0x84) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x88) +#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x8C) +#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0x90) +#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0x94) +#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x98) +#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x9C) +#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0xA0) +#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0xA4) +#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0xA8) +#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0xAC) +#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xB0) +#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xB4) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xB8) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xBC) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xC0) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xC4) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xC8) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xCC) +#define QSERDES_COM_INTEGLOOP_INITVAL COM_OFF(0xD0) +#define QSERDES_COM_INTEGLOOP_EN COM_OFF(0xD4) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0xD8) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0xDC) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0xE0) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0xE4) +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL COM_OFF(0xE8) +#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0xEC) +#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0xF0) +#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0xF4) +#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0xF8) +#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0xFC) +#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x100) +#define QSERDES_COM_VCO_TUNE_INITVAL1 COM_OFF(0x104) +#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x108) +#define QSERDES_COM_VCO_TUNE_MINVAL1 COM_OFF(0x10C) +#define QSERDES_COM_VCO_TUNE_MINVAL2 COM_OFF(0x110) +#define QSERDES_COM_VCO_TUNE_MAXVAL1 COM_OFF(0x114) +#define QSERDES_COM_VCO_TUNE_MAXVAL2 COM_OFF(0x118) +#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x11C) +#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x120) +#define QSERDES_COM_CMN_STATUS COM_OFF(0x124) +#define QSERDES_COM_RESET_SM_STATUS COM_OFF(0x128) +#define QSERDES_COM_RESTRIM_CODE_STATUS COM_OFF(0x12C) +#define QSERDES_COM_PLLCAL_CODE1_STATUS COM_OFF(0x130) +#define QSERDES_COM_PLLCAL_CODE2_STATUS COM_OFF(0x134) +#define QSERDES_COM_CLK_SELECT COM_OFF(0x138) +#define QSERDES_COM_HSCLK_SEL COM_OFF(0x13C) +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS COM_OFF(0x140) +#define QSERDES_COM_PLL_ANALOG COM_OFF(0x144) +#define QSERDES_COM_CORECLK_DIV_MODE0 COM_OFF(0x148) +#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x14C) +#define QSERDES_COM_SW_RESET COM_OFF(0x150) +#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x154) +#define QSERDES_COM_C_READY_STATUS COM_OFF(0x158) +#define QSERDES_COM_CMN_CONFIG COM_OFF(0x15C) +#define QSERDES_COM_CMN_RATE_OVERRIDE COM_OFF(0x160) +#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x164) +#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x168) +#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x16C) +#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x170) +#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x174) +#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x178) +#define QSERDES_COM_CMN_MISC1 COM_OFF(0x17C) +#define QSERDES_COM_CMN_MISC2 COM_OFF(0x180) +#define QSERDES_COM_CMN_MODE COM_OFF(0x184) +#define QSERDES_COM_CMN_VREG_SEL COM_OFF(0x188) + +/* UFS PHY TX registers */ +#define QSERDES_TX0_RES_CODE_LANE_OFFSET_TX TX_OFF(0, 0x44) +#define QSERDES_TX0_RES_CODE_LANE_OFFSET_RX TX_OFF(0, 0x48) +#define QSERDES_TX0_TRANSCEIVER_BIAS_EN TX_OFF(0, 0x5C) +#define QSERDES_TX0_LANE_MODE_1 TX_OFF(0, 0x8C) +#define QSERDES_TX0_LANE_MODE_2 TX_OFF(0, 0x90) +#define QSERDES_TX0_LANE_MODE_3 TX_OFF(0, 0x94) + +#define QSERDES_TX1_RES_CODE_LANE_OFFSET_TX TX_OFF(1, 0x44) +#define QSERDES_TX1_RES_CODE_LANE_OFFSET_RX TX_OFF(1, 0x48) +#define QSERDES_TX1_LANE_MODE_1 TX_OFF(1, 0x8C) + + +/* UFS PHY RX registers */ +#define QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF RX_OFF(0, 0x24) +#define QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0, 0x28) +#define QSERDES_RX0_UCDR_SVS_SO_GAIN RX_OFF(0, 0x2C) +#define QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x30) +#define QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(0, 0x34) +#define QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW RX_OFF(0, 0x3C) +#define QSERDES_RX0_UCDR_PI_CONTROLS RX_OFF(0, 0x44) +#define QSERDES_RX0_RX_TERM_BW RX_OFF(0, 0x7C) +#define QSERDES_RX0_RX_EQ_GAIN2_LSB RX_OFF(0, 0xC8) +#define QSERDES_RX0_RX_EQ_GAIN2_MSB RX_OFF(0, 0xCC) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL1 RX_OFF(0, 0xD0) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD4) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(0, 0xD8) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(0, 0xDC) +#define QSERDES_RX0_SIGDET_CNTRL RX_OFF(0, 0x104) +#define QSERDES_RX0_SIGDET_LVL RX_OFF(0, 0x108) +#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x10C) +#define QSERDES_RX0_RX_INTERFACE_MODE RX_OFF(0, 0x11C) +#define QSERDES_RX0_RX_MODE_00 RX_OFF(0, 0x164) +#define QSERDES_RX0_UCDR_FO_GAIN RX_OFF(0, 0x08) + +#define QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF RX_OFF(1, 0x24) +#define QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(1, 0x28) +#define QSERDES_RX1_UCDR_SVS_SO_GAIN RX_OFF(1, 0x2C) +#define QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN RX_OFF(1, 0x30) +#define QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(1, 0x34) +#define QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW RX_OFF(1, 0x3C) +#define QSERDES_RX1_UCDR_PI_CONTROLS RX_OFF(1, 0x44) +#define QSERDES_RX1_RX_TERM_BW RX_OFF(1, 0x7C) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(1, 0xD4) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(1, 0xD8) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(1, 0xDC) +#define QSERDES_RX1_SIGDET_CNTRL RX_OFF(1, 0x104) +#define QSERDES_RX1_SIGDET_LVL RX_OFF(1, 0x108) +#define QSERDES_RX1_SIGDET_DEGLITCH_CNTRL RX_OFF(1, 0x10C) +#define QSERDES_RX1_RX_INTERFACE_MODE RX_OFF(1, 0x11C) +#define QSERDES_RX1_RX_MODE_00 RX_OFF(1, 0x164) +#define QSERDES_RX1_UCDR_FO_GAIN RX_OFF(1, 0x8) + +#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) + +/* + * This structure represents the v3 specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qmp_v3 { + struct ufs_qcom_phy common_cfg; +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD5), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x07), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0xDA), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xC1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_LANE_MODE_1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_RES_CODE_LANE_OFFSET_TX, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_RES_CODE_LANE_OFFSET_RX, 0x07), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00, 0x59), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FO_GAIN, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_MID_TERM_CTRL1, 0x43), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL, 0x12), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL1, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0xFF),/* 13 us */ +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_2nd_lane[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4, 0x1B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_RES_CODE_LANE_OFFSET_TX, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_RES_CODE_LANE_OFFSET_RX, 0x07), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00, 0x59), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FO_GAIN, 0x0C), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +#endif diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 6fb2b696959053f78f6581d55d841900fd0a4e67..50cdf2032f1b6f493bd22f200edd4a8e3d1e4c32 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -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 a44680d64f9b479dc62b30520a901fd569af135e..c267afb68f077cd5817059c9a5125cc61996d1d4 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 08925d24180b08f976137a60b6e0533b4340e636..1bd3c10ce189394ead8d74faa0b543471b79fce3 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 4b5cf0e0f16e2dbfcea320414f1aa8c9e8a56179..951090faa6a9194d53bc35e8972161b784aa417e 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 c4f4d904e4a61e4c0d622a08fcb85e1f5e63b6a7..618e04407ac854c9bd2b03f0da34ebd00124b7e8 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/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 227646eb817c8c82f2dbdea0cd592f04b1367f8d..f16baf9b869623e8fd2eae7e9031d90056aa86e6 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 1ea3438ea67e925aa82b6e57e503efb72689fde3..89ff2795a8b5521ce52ddc12bac4972d8e75a404 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 3aac640596ad6e06a3fb359bfa8c3019be019def..d76ac6b4b40dfc576d13e4cbf059d051184677d1 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-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index ef7ab208b951eeb307b73191f373b88ecd8462a3..9e2f3738bf3ecdec3314be32863b4fcc89b46739 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 50f0ec42c63723528576b4498df84fa147428250..fad0e132ead84b187e8d9294f98d29b0068f577d 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 fa7d998e1d5a86ba1354c6b976acfb32b2beba01..3535f984186152838ed1d8609ee55f30c3831eba 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 @@ -1872,6 +1893,13 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = { /* Conflict with parallel and NAND flash */ .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, @@ -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 628817c40e3bbc79cc1098368a23fc2d75e587e5..a5accffbc8c9115dbcf81e8d8fb35a8ec700cf4b 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 190f17e4bbdafd287b9dd8d9cdeee4011f80c6c5..1d3b88e6ab862e0d468af104881478f7df7db061 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 93f8bd04e7fe69ab92bb48c2715aa24371d315c3..ae74b260b014bddd2700b4edca00f44c99d3e84a 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 a0daf27042bd0f95555f68ee01722ec0c1260ab1..90fd37e8207bfbabd39b53e45379719c311bad8a 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 42e0d7d30029fbeb7422c575dbe5a1d79f64f30d..cfe36b44889d88eaee48909379eb9a16af6afe22 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 41ee33ec270cb1a62d0140f3d7fe0747d6d53b76..c5cab7cd02b23d7d49ca2db707a9c3dcbc130af6 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 f49ea3d92aa1e3eab716721cf2f5a12ea45a1ccd..e87ee43efa163340e066ad98e7fa92c011984d65 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 7e824e4d20f44e59856f3679b9f466f0b857088d..9bd0a3de101dd61f0808e257e864988494ef5bde 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 c399f0932af5e26eb3d9af531bc91975abd9d18c..f97f8179f2b1b591d7f55f940c6e4cf4e723b60c 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 698c7d8c9a0864e2ad820b5422516a3a5c968462..c05217edcb0e080dea207c411b9868256c416e9f 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 b81c807ac54d52ed2719d83bf883043a81e133c3..d2fcf7f7b966829d78e329327c85029f05dbb805 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 8070765311dbf7a488fe5edd9d3d7ce92e2f331a..e1c34e19222ee7bcf12d3a98f348b9d749b312fe 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 6502e676d368617927c9cf55be7fea2ea31697e8..33232041ee86dd3ea34afef3f7f98850f43ceba8 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh7734.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh7734.c @@ -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 a9bec6e6fdd18820a2847fe02bc98730fe223da2..14dfbbd6c1c37220e751d77e8a123728ef34234d 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 26ebedc1f6d31da3780fafb0d2cfd6d1bab6b7fb..61aaaf58c59932a5ec45020ef9bf4d97b872240b 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 cc6bf434a4c8a52325d7c9d1e403f6e7f9154e19..ed8f0746bd8fc3705de10acad92cb3d61c1a18fa 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 @@ -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; @@ -4249,6 +4267,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 3b6c648c803513d58b624248dcf27d9a7c370754..da435048a9f59895df799aaf23dcc71baf357d36 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 7cbf21cfe22f836f21ce5836697cbbfd5311ef2a..d9a860ba48d0241e1bfbcac7012266b8f872597a 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 @@ -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_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index 192bd26b6f0fe15520fed2f7a42484fdc9f70afe..f00e82a6cea4d4db7602b8b5678a426f605750aa 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.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 @@ -1590,6 +1590,17 @@ static int ipa3_usb_xdci_connect_internal( return result; } + /* Start MHIP UL channel before starting USB UL channel + * DL channel will be started when voting for PCIe -> LPM Exit. + */ + if (ipa3_is_mhip_offload_enabled()) { + result = ipa_mpm_mhip_xdci_pipe_enable(params->teth_prot); + if (result) { + IPA_USB_ERR("failed to enable MHIP UL channel\n"); + goto connect_fail; + } + } + if (params->teth_prot != IPA_USB_DIAG) { /* Start UL channel */ result = ipa3_xdci_start(params->usb_to_ipa_clnt_hdl, @@ -1610,20 +1621,11 @@ static int ipa3_usb_xdci_connect_internal( goto connect_dl_fail; } - /* MHIP pipe enablement */ - if (ipa3_is_mhip_offload_enabled()) { - result = ipa_mpm_mhip_xdci_pipe_enable(params->teth_prot); - if (result) { - IPA_USB_ERR("failed to enable MHIP channel\n"); - goto connect_teth_prot_fail; - } - } - /* Connect tethering protocol */ result = ipa3_usb_connect_teth_prot(params->teth_prot); if (result) { IPA_USB_ERR("failed to connect teth protocol\n"); - goto connect_mhip_prot_fail; + goto connect_teth_prot_fail; } if (!ipa3_usb_set_state(IPA_USB_CONNECTED, false, ttype)) { @@ -1637,9 +1639,6 @@ static int ipa3_usb_xdci_connect_internal( state_change_connected_fail: ipa3_usb_disconnect_teth_prot(params->teth_prot); -connect_mhip_prot_fail: - if (ipa3_is_mhip_offload_enabled()) - ipa_mpm_mhip_xdci_pipe_disable(params->teth_prot); connect_teth_prot_fail: ipa3_xdci_disconnect(params->ipa_to_usb_clnt_hdl, false, -1); ipa3_reset_gsi_channel(params->ipa_to_usb_clnt_hdl); @@ -1651,8 +1650,12 @@ static int ipa3_usb_xdci_connect_internal( ipa3_reset_gsi_event_ring(params->usb_to_ipa_clnt_hdl); } connect_ul_fail: - ipa_pm_deactivate_sync( + if (ipa3_is_mhip_offload_enabled()) + ipa_mpm_mhip_xdci_pipe_disable(params->teth_prot); +connect_fail: + ipa_pm_deactivate_sync( ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl); + return result; } diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c index 0c8bb243f23f0577587724177fa755d59893750c..16de3d9f6befff827ebb7769df4403aa6f087403 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 @@ -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 e7e82e02924fb24723d4dec6c5e772d6b30f24f4..91a582ada708626f6bd396ca3ebbb8098297c309 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.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 @@ -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); @@ -1519,6 +1523,23 @@ static int ipa3_ioctl_fnr_counter_set(unsigned long arg) return 0; } +static int proc_sram_info_rqst( + unsigned long arg) +{ + struct ipa_nat_in_sram_info sram_info = { 0 }; + + if (ipa3_nat_get_sram_info(&sram_info)) + return -EFAULT; + + if (copy_to_user( + (void __user *) arg, + &sram_info, + sizeof(struct ipa_nat_in_sram_info))) + return -EFAULT; + + return 0; +} + static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int retval = 0; @@ -1616,6 +1637,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } + if (ipa3_nat_init_cmd(&nat_init)) { retval = -EFAULT; break; @@ -1681,6 +1703,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } + if (ipa3_del_nat_table(&table_del)) { retval = -EFAULT; break; @@ -1693,6 +1716,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } + if (ipa3_del_ipv6ct_table(&table_del)) { retval = -EFAULT; break; @@ -2732,6 +2756,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) IPA_CLIENT_MAX, fst_switch.to_wigig); break; + + case IPA_IOC_GET_NAT_IN_SRAM_INFO: + retval = proc_sram_info_rqst(arg); + break; + + case IPA_IOC_APP_CLOCK_VOTE: + retval = ipa3_app_clk_vote( + (enum ipa_app_clock_vote_type) arg); + break; + default: IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -ENOTTY; @@ -3839,22 +3873,23 @@ int _ipa_init_sram_v3(void) if (ipa_get_hw_type() >= IPA_HW_v4_5) { ipa3_sram_set_canary(ipa_sram_mmio, IPA_MEM_PART(nat_tbl_ofst) - 12); - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(nat_tbl_ofst) - 8); - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(nat_tbl_ofst) - 4); - ipa3_sram_set_canary(ipa_sram_mmio, IPA_MEM_PART(nat_tbl_ofst)); } if (ipa_get_hw_type() >= IPA_HW_v4_0) { - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(pdn_config_ofst) - 4); - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(pdn_config_ofst)); - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(stats_quota_ofst) - 4); - ipa3_sram_set_canary(ipa_sram_mmio, - IPA_MEM_PART(stats_quota_ofst)); + if (ipa_get_hw_type() < IPA_HW_v4_5) { + ipa3_sram_set_canary(ipa_sram_mmio, + IPA_MEM_PART(pdn_config_ofst) - 4); + ipa3_sram_set_canary(ipa_sram_mmio, + IPA_MEM_PART(pdn_config_ofst)); + ipa3_sram_set_canary(ipa_sram_mmio, + IPA_MEM_PART(stats_quota_q6_ofst) - 4); + ipa3_sram_set_canary(ipa_sram_mmio, + IPA_MEM_PART(stats_quota_q6_ofst)); + } else { + ipa3_sram_set_canary(ipa_sram_mmio, + IPA_MEM_PART(stats_quota_q6_ofst) - 12); + } } + if (ipa_get_hw_type() <= IPA_HW_v3_5 || ipa_get_hw_type() >= IPA_HW_v4_5) { ipa3_sram_set_canary(ipa_sram_mmio, @@ -4652,6 +4687,12 @@ long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case IPA_IOC_MDFY_RT_RULE32: cmd = IPA_IOC_MDFY_RT_RULE; break; + case IPA_IOC_GET_NAT_IN_SRAM_INFO32: + cmd = IPA_IOC_GET_NAT_IN_SRAM_INFO; + break; + case IPA_IOC_APP_CLOCK_VOTE32: + cmd = IPA_IOC_APP_CLOCK_VOTE; + break; case IPA_IOC_COMMIT_HDR: case IPA_IOC_RESET_HDR: case IPA_IOC_COMMIT_RT: @@ -4790,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; @@ -4797,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); } /** @@ -4976,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 @@ -5092,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 @@ -5887,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; @@ -6902,7 +6975,10 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, ipa3_lan_poll, NAPI_WEIGHT); } + mutex_init(&ipa3_ctx->app_clock_vote.mutex); + return 0; + fail_cdev_add: fail_gsi_pre_fw_load_init: ipa3_dma_shutdown(); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index f773bfb2e23f726e1db46cb38f5b537799080046..a1e7e4acc6946eefacbffb81abd42a49222223d9 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 @@ -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 68a88cad8c4ee3fef0f47114cf7adc3c8d117a7b..b57aaf18c5fd24a261a88a6469f7335350d541df 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.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. */ #ifdef CONFIG_DEBUG_FS @@ -1145,6 +1145,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 +1161,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++) { @@ -1634,7 +1636,8 @@ static ssize_t ipa3_read_msg(struct file *file, char __user *ubuf, } static void ipa3_read_table( - char *table_addr, u32 table_size, + char *table_addr, + u32 table_size, u32 *total_num_entries, u32 *rule_id, enum ipahal_nat_type nat_type) @@ -1648,46 +1651,51 @@ static void ipa3_read_table( char *buff; size_t buff_size = 2 * IPA_MAX_ENTRY_STRING_LEN; - IPADBG("\n"); + IPADBG("In\n"); + if (table_addr == NULL) { pr_err("NULL NAT table\n"); - return; + goto bail; } result = ipahal_nat_entry_size(nat_type, &entry_size); + if (result) { IPAERR("Failed to retrieve size of %s entry\n", ipahal_nat_type_str(nat_type)); - return; + goto bail; } buff = kzalloc(buff_size, GFP_KERNEL); + if (!buff) { IPAERR("Out of memory\n"); - return; + goto bail; } for (i = 0, entry = table_addr; i < table_size; ++i, ++id, entry += entry_size) { + result = ipahal_nat_is_entry_zeroed(nat_type, entry, &entry_zeroed); + if (result) { - IPAERR( - "Failed to determine whether the %s entry is definitely zero\n" - , ipahal_nat_type_str(nat_type)); - goto bail; + IPAERR("Undefined if %s entry is zero\n", + ipahal_nat_type_str(nat_type)); + goto free_buf; } + if (entry_zeroed) continue; result = ipahal_nat_is_entry_valid(nat_type, entry, &entry_valid); + if (result) { - IPAERR( - "Failed to determine whether the %s entry is valid\n" - , ipahal_nat_type_str(nat_type)); - goto bail; + IPAERR("Undefined if %s entry is valid\n", + ipahal_nat_type_str(nat_type)); + goto free_buf; } if (entry_valid) { @@ -1698,7 +1706,9 @@ static void ipa3_read_table( ipahal_nat_stringify_entry(nat_type, entry, buff, buff_size); + pr_err("%s\n", buff); + memset(buff, 0, buff_size); } @@ -1706,52 +1716,150 @@ static void ipa3_read_table( pr_err("\n"); else pr_err("\tEmpty\n\n"); - IPADBG("return\n"); -bail: + +free_buf: kfree(buff); *rule_id = id; *total_num_entries += num_entries; + +bail: + IPADBG("Out\n"); } static void ipa3_start_read_memory_device( struct ipa3_nat_ipv6ct_common_mem *dev, enum ipahal_nat_type nat_type, - u32 *num_entries) + u32 *num_ddr_ent_ptr, + u32 *num_sram_ent_ptr) { u32 rule_id = 0; - IPADBG("\n"); + if (dev->is_ipv6ct_mem) { + + IPADBG("In: v6\n"); - pr_err("%s_Table_Size=%d\n", - dev->name, dev->table_entries + 1); + pr_err("%s_Table_Size=%d\n", + dev->name, dev->table_entries + 1); - pr_err("%s_Expansion_Table_Size=%d\n", - dev->name, dev->expn_table_entries); + pr_err("%s_Expansion_Table_Size=%d\n", + dev->name, dev->expn_table_entries); - if (!dev->is_sys_mem) - pr_err("Not supported for local(shared) memory\n"); + pr_err("\n%s Base Table:\n", dev->name); - pr_err("\n%s Base Table:\n", dev->name); - ipa3_read_table(dev->base_table_addr, dev->table_entries + 1, - num_entries, &rule_id, nat_type); + if (dev->base_table_addr) + ipa3_read_table( + dev->base_table_addr, + dev->table_entries + 1, + num_ddr_ent_ptr, + &rule_id, + nat_type); - pr_err("%s Expansion Table:\n", dev->name); - ipa3_read_table( - dev->expansion_table_addr, dev->expn_table_entries, - num_entries, - &rule_id, - nat_type); + pr_err("%s Expansion Table:\n", dev->name); + + if (dev->expansion_table_addr) + ipa3_read_table( + dev->expansion_table_addr, + dev->expn_table_entries, + num_ddr_ent_ptr, + &rule_id, + nat_type); + } + + if (dev->is_nat_mem) { + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa3_nat_mem_loc_data *mld_ptr = NULL; + u32 *num_ent_ptr; + const char *type_ptr; + + IPADBG("In: v4\n"); + + if (nm_ptr->active_table == IPA_NAT_MEM_IN_DDR && + nm_ptr->ddr_in_use) { + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_DDR]; + num_ent_ptr = num_ddr_ent_ptr; + type_ptr = "DDR based table"; + } - IPADBG("return\n"); + if (nm_ptr->active_table == IPA_NAT_MEM_IN_SRAM && + nm_ptr->sram_in_use) { + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_SRAM]; + num_ent_ptr = num_sram_ent_ptr; + type_ptr = "SRAM based table"; + } + + if (mld_ptr) { + pr_err("(%s) %s_Table_Size=%d\n", + type_ptr, + dev->name, + mld_ptr->table_entries + 1); + + pr_err("(%s) %s_Expansion_Table_Size=%d\n", + type_ptr, + dev->name, + mld_ptr->expn_table_entries); + + pr_err("\n(%s) %s_Base Table:\n", + type_ptr, + dev->name); + + if (mld_ptr->base_table_addr) + ipa3_read_table( + mld_ptr->base_table_addr, + mld_ptr->table_entries + 1, + num_ent_ptr, + &rule_id, + nat_type); + + pr_err("(%s) %s_Expansion Table:\n", + type_ptr, + dev->name); + + if (mld_ptr->expansion_table_addr) + ipa3_read_table( + mld_ptr->expansion_table_addr, + mld_ptr->expn_table_entries, + num_ent_ptr, + &rule_id, + nat_type); + } + } + + IPADBG("Out\n"); } static void ipa3_finish_read_memory_device( struct ipa3_nat_ipv6ct_common_mem *dev, - u32 num_entries) + u32 num_ddr_entries, + u32 num_sram_entries) { - IPADBG("\n"); - pr_err("Overall number %s entries: %d\n\n", dev->name, num_entries); - IPADBG("return\n"); + IPADBG("In\n"); + + if (dev->is_ipv6ct_mem) { + pr_err("Overall number %s entries: %u\n\n", + dev->name, + num_ddr_entries); + } else { + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + + if (num_ddr_entries) + pr_err("%s: Overall number of DDR entries: %u\n\n", + dev->name, + num_ddr_entries); + + if (num_sram_entries) + pr_err("%s: Overall number of SRAM entries: %u\n\n", + dev->name, + num_sram_entries); + + pr_err("%s: Driver focus changes to DDR(%u) to SRAM(%u)\n", + dev->name, + nm_ptr->switch2ddr_cnt, + nm_ptr->switch2sram_cnt); + } + + IPADBG("Out\n"); } static void ipa3_read_pdn_table(void) @@ -1764,72 +1872,104 @@ static void ipa3_read_pdn_table(void) char *buff; size_t buff_size = 128; - IPADBG("\n"); + IPADBG("In\n"); - result = ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size); - if (result) { - IPAERR("Failed to retrieve size of PDN entry"); - return; - } + if (ipa3_ctx->nat_mem.pdn_mem.base) { - buff = kzalloc(buff_size, GFP_KERNEL); - if (!buff) { - IPAERR("Out of memory\n"); - return; - } + result = ipahal_nat_entry_size( + IPAHAL_NAT_IPV4_PDN, &pdn_entry_size); - for (i = 0, pdn_entry = ipa3_ctx->nat_mem.pdn_mem.base; - i < IPA_MAX_PDN_NUM; - ++i, pdn_entry += pdn_entry_size) { - result = ipahal_nat_is_entry_zeroed(IPAHAL_NAT_IPV4_PDN, - pdn_entry, &entry_zeroed); if (result) { - IPAERR( - "Failed to determine whether the PDN entry is definitely zero\n"); + IPAERR("Failed to retrieve size of PDN entry"); goto bail; } - if (entry_zeroed) - continue; - result = ipahal_nat_is_entry_valid(IPAHAL_NAT_IPV4_PDN, - pdn_entry, &entry_valid); - if (result) { - IPAERR( - "Failed to determine whether the PDN entry is valid\n"); + buff = kzalloc(buff_size, GFP_KERNEL); + if (!buff) { + IPAERR("Out of memory\n"); goto bail; } - if (entry_valid) - pr_err("PDN %d:\n", i); - else - pr_err("PDN %d - Invalid:\n", i); - ipahal_nat_stringify_entry(IPAHAL_NAT_IPV4_PDN, + for (i = 0, pdn_entry = ipa3_ctx->nat_mem.pdn_mem.base; + i < IPA_MAX_PDN_NUM; + ++i, pdn_entry += pdn_entry_size) { + + result = ipahal_nat_is_entry_zeroed( + IPAHAL_NAT_IPV4_PDN, + pdn_entry, &entry_zeroed); + + if (result) { + IPAERR("ipahal_nat_is_entry_zeroed() fail\n"); + goto free; + } + + if (entry_zeroed) + continue; + + result = ipahal_nat_is_entry_valid( + IPAHAL_NAT_IPV4_PDN, + pdn_entry, &entry_valid); + + if (result) { + IPAERR( + "Failed to determine whether the PDN entry is valid\n"); + goto free; + } + + ipahal_nat_stringify_entry( + IPAHAL_NAT_IPV4_PDN, pdn_entry, buff, buff_size); - pr_err("%s\n", buff); - memset(buff, 0, buff_size); + + if (entry_valid) + pr_err("PDN %d: %s\n", i, buff); + else + pr_err("PDN %d - Invalid: %s\n", i, buff); + + memset(buff, 0, buff_size); + } + pr_err("\n"); +free: + kfree(buff); } - pr_err("\n"); bail: - kfree(buff); - IPADBG("return\n"); + IPADBG("Out\n"); } -static ssize_t ipa3_read_nat4(struct file *file, - char __user *ubuf, size_t count, - loff_t *ppos) +static ssize_t ipa3_read_nat4( + struct file *file, + char __user *ubuf, + size_t count, + loff_t *ppos) { - u32 rule_id = 0, num_entries = 0, index_num_entries = 0; + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa3_nat_mem_loc_data *mld_ptr = NULL; + + u32 rule_id = 0; + + u32 *num_ents_ptr; + u32 num_ddr_ents = 0; + u32 num_sram_ents = 0; + + u32 *num_index_ents_ptr; + u32 num_ddr_index_ents = 0; + u32 num_sram_index_ents = 0; + + const char *type_ptr; + + bool any_table_active = (nm_ptr->ddr_in_use || nm_ptr->sram_in_use); pr_err("IPA3 NAT stats\n"); - if (!ipa3_ctx->nat_mem.dev.is_dev_init) { + + if (!dev->is_dev_init) { pr_err("NAT hasn't been initialized or not supported\n"); goto ret; } - mutex_lock(&ipa3_ctx->nat_mem.dev.lock); + mutex_lock(&dev->lock); - if (!ipa3_ctx->nat_mem.dev.is_hw_init) { - pr_err("NAT H/W hasn't been initialized\n"); + if (!dev->is_hw_init || !any_table_active) { + pr_err("NAT H/W and/or S/W not initialized\n"); goto bail; } @@ -1837,71 +1977,117 @@ static ssize_t ipa3_read_nat4(struct file *file, ipa3_read_pdn_table(); } else { pr_err("NAT Table IP Address=%pI4h\n\n", - &ipa3_ctx->nat_mem.public_ip_addr); + &ipa3_ctx->nat_mem.public_ip_addr); } - ipa3_start_read_memory_device(&ipa3_ctx->nat_mem.dev, - IPAHAL_NAT_IPV4, &num_entries); + ipa3_start_read_memory_device( + dev, + IPAHAL_NAT_IPV4, + &num_ddr_ents, + &num_sram_ents); - /* Print Index tables */ - pr_err("ipaNatTable Index Table:\n"); - ipa3_read_table( - ipa3_ctx->nat_mem.index_table_addr, - ipa3_ctx->nat_mem.dev.table_entries + 1, - &index_num_entries, - &rule_id, - IPAHAL_NAT_IPV4_INDEX); + if (nm_ptr->active_table == IPA_NAT_MEM_IN_DDR && + nm_ptr->ddr_in_use) { - pr_err("ipaNatTable Expansion Index Table:\n"); - ipa3_read_table( - ipa3_ctx->nat_mem.index_table_expansion_addr, - ipa3_ctx->nat_mem.dev.expn_table_entries, - &index_num_entries, - &rule_id, - IPAHAL_NAT_IPV4_INDEX); + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_DDR]; + num_ents_ptr = &num_ddr_ents; + num_index_ents_ptr = &num_ddr_index_ents; + type_ptr = "DDR based table"; + } + + if (nm_ptr->active_table == IPA_NAT_MEM_IN_SRAM && + nm_ptr->sram_in_use) { - if (num_entries != index_num_entries) - IPAERR( - "The NAT table number of entries %d is different from index table number of entries %d\n", - num_entries, index_num_entries); + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_SRAM]; + num_ents_ptr = &num_sram_ents; + num_index_ents_ptr = &num_sram_index_ents; + type_ptr = "SRAM based table"; + } - ipa3_finish_read_memory_device(&ipa3_ctx->nat_mem.dev, num_entries); + if (mld_ptr) { + /* Print Index tables */ + pr_err("(%s) ipaNatTable Index Table:\n", type_ptr); + + ipa3_read_table( + mld_ptr->index_table_addr, + mld_ptr->table_entries + 1, + num_index_ents_ptr, + &rule_id, + IPAHAL_NAT_IPV4_INDEX); + + pr_err("(%s) ipaNatTable Expansion Index Table:\n", type_ptr); + + ipa3_read_table( + mld_ptr->index_table_expansion_addr, + mld_ptr->expn_table_entries, + num_index_ents_ptr, + &rule_id, + IPAHAL_NAT_IPV4_INDEX); + + if (*num_ents_ptr != *num_index_ents_ptr) + IPAERR( + "(%s) Base Table vs Index Table entry count differs (%u vs %u)\n", + type_ptr, *num_ents_ptr, *num_index_ents_ptr); + } + + ipa3_finish_read_memory_device( + dev, + num_ddr_ents, + num_sram_ents); - IPADBG("return\n"); bail: - mutex_unlock(&ipa3_ctx->nat_mem.dev.lock); + mutex_unlock(&dev->lock); + ret: + IPADBG("Out\n"); + return 0; } -static ssize_t ipa3_read_ipv6ct(struct file *file, - char __user *ubuf, size_t count, +static ssize_t ipa3_read_ipv6ct( + struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) { - u32 num_entries = 0; + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->ipv6ct_mem.dev; + + u32 num_ddr_ents, num_sram_ents; + + num_ddr_ents = num_sram_ents = 0; + + IPADBG("In\n"); pr_err("\n"); - if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) { + if (!dev->is_dev_init) { pr_err("IPv6 Conntrack not initialized or not supported\n"); - return 0; + goto bail; } - mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock); - - if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) { + if (!dev->is_hw_init) { pr_err("IPv6 connection tracking H/W hasn't been initialized\n"); goto bail; } - ipa3_start_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev, - IPAHAL_NAT_IPV6CT, &num_entries); - ipa3_finish_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev, - num_entries); + mutex_lock(&dev->lock); + + ipa3_start_read_memory_device( + dev, + IPAHAL_NAT_IPV6CT, + &num_ddr_ents, + &num_sram_ents); + + ipa3_finish_read_memory_device( + dev, + num_ddr_ents, + num_sram_ents); + + mutex_unlock(&dev->lock); - IPADBG("return\n"); bail: - mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock); + IPADBG("Out\n"); + return 0; } @@ -2227,6 +2413,22 @@ static ssize_t ipa3_read_usb_gsi_stats(struct file *file, return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt); } +static ssize_t ipa3_read_app_clk_vote( + struct file *file, + char __user *ubuf, + size_t count, + loff_t *ppos) +{ + int cnt = + scnprintf( + dbg_buff, + IPA_MAX_MSG_LEN, + "%u\n", + ipa3_ctx->app_clock_vote.cnt); + + return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt); +} + static void ipa_dump_status(struct ipahal_pkt_status *status) { IPA_DUMP_STATUS_FIELD(status_opcode); @@ -2504,7 +2706,11 @@ static const struct ipa3_debugfs_file debugfs_files[] = { "usb_gsi_stats", IPA_READ_ONLY_MODE, NULL, { .read = ipa3_read_usb_gsi_stats, } - } + }, { + "app_clk_vote_cnt", IPA_READ_ONLY_MODE, NULL, { + .read = ipa3_read_app_clk_vote, + } + }, }; void ipa3_debugfs_init(void) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 82a0e54217c0069ba21fdaf95a764bc5a7170065..dea05608cd6ea8706bcb7241bdc281a1fcf34283 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 @@ -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); @@ -3389,10 +3387,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 +3411,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 */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c index 5e03e1cad255744b7c2b764d366adf57cf3b81e4..43fb44687ff156fba6ebca77197c4703bfe158c4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.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 @@ -271,6 +271,27 @@ static void ipa_close_coal_frame(struct ipahal_imm_cmd_pyld **coal_cmd_pyld) ®_write_coal_close, false); } +static bool ipa_validate_quota_stats_sram_size(u32 needed_len) +{ + u32 sram_size; + + /* Starting IPA4.5 Quota stats is split between Q6 and AP */ + + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_5) { + IPAERR("Not supported ipa_ver=%d\n", ipa3_ctx->ipa_hw_type); + return false; + } + + sram_size = IPA_MEM_PART(stats_quota_ap_size); + if (needed_len > sram_size) { + IPAERR("SRAM partition too small: %u needed %u\n", + sram_size, needed_len); + return false; + } + + return true; +} + int ipa_init_quota_stats(u32 pipe_bitmask) { struct ipahal_stats_init_pyld *pyld; @@ -301,9 +322,7 @@ int ipa_init_quota_stats(u32 pipe_bitmask) return -EPERM; } - if (pyld->len > IPA_MEM_PART(stats_quota_size)) { - IPAERR("SRAM partition too small: %d needed %d\n", - IPA_MEM_PART(stats_quota_size), pyld->len); + if (!ipa_validate_quota_stats_sram_size(pyld->len)) { ret = -EPERM; goto destroy_init_pyld; } @@ -356,7 +375,7 @@ int ipa_init_quota_stats(u32 pipe_bitmask) quota_base.offset = ipahal_get_reg_n_ofst(IPA_STAT_QUOTA_BASE_n, ipa3_ctx->ee); quota_base.value = ipa3_ctx->smem_restricted_bytes + - IPA_MEM_PART(stats_quota_ofst); + IPA_MEM_PART(stats_quota_ap_ofst); quota_base.value_mask = ~0; quota_base_pyld = ipahal_construct_imm_cmd(IPA_IMM_CMD_REGISTER_WRITE, "a_base, false); @@ -377,7 +396,7 @@ int ipa_init_quota_stats(u32 pipe_bitmask) cmd.size = pyld->len; cmd.system_addr = dma_address; cmd.local_addr = ipa3_ctx->smem_restricted_bytes + - IPA_MEM_PART(stats_quota_ofst); + IPA_MEM_PART(stats_quota_ap_ofst); cmd_pyld = ipahal_construct_imm_cmd( IPA_IMM_CMD_DMA_SHARED_MEM, &cmd, false); if (!cmd_pyld) { @@ -476,7 +495,7 @@ int ipa_get_quota_stats(struct ipa_quota_stats_all *out) cmd.size = mem.size; cmd.system_addr = mem.phys_base; cmd.local_addr = ipa3_ctx->smem_restricted_bytes + - IPA_MEM_PART(stats_quota_ofst) + offset.offset; + IPA_MEM_PART(stats_quota_ap_ofst) + offset.offset; cmd_pyld[num_cmd] = ipahal_construct_imm_cmd( IPA_IMM_CMD_DMA_SHARED_MEM, &cmd, false); if (!cmd_pyld[num_cmd]) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index a6bdbcb576a775712f02bec27d33397da005db38..fde70018093a8ac676cb6bb23c785d6ab91138c0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef _IPA3_I_H_ @@ -140,12 +140,20 @@ #define WLAN3_CONS_RX_EP 17 #define WLAN4_CONS_RX_EP 18 -#define IPA_RAM_NAT_OFST 0 -#define IPA_RAM_NAT_SIZE 0 +#define IPA_RAM_NAT_OFST \ + IPA_MEM_PART(nat_tbl_ofst) +#define IPA_RAM_NAT_SIZE \ + IPA_MEM_PART(nat_tbl_size) #define IPA_RAM_IPV6CT_OFST 0 #define IPA_RAM_IPV6CT_SIZE 0 #define IPA_MEM_CANARY_VAL 0xdeadbeef +#define IS_IPV6CT_MEM_DEV(d) \ + (((void *) (d) == (void *) &ipa3_ctx->ipv6ct_mem)) + +#define IS_NAT_MEM_DEV(d) \ + (((void *) (d) == (void *) &ipa3_ctx->nat_mem)) + #define IPA_STATS #ifdef IPA_STATS @@ -236,6 +244,8 @@ enum { # define __cpuc_flush_dcache_area __flush_dcache_area #endif +#define IPA_APP_VOTE_MAX 500 + #define IPA_SMP2P_OUT_CLK_RSP_CMPLT_IDX 0 #define IPA_SMP2P_OUT_CLK_VOTE_IDX 1 #define IPA_SMP2P_SMEM_STATE_MASK 3 @@ -424,6 +434,12 @@ enum { #define IPA_IOC_MDFY_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \ IPA_IOCTL_MDFY_RT_RULE, \ compat_uptr_t) +#define IPA_IOC_GET_NAT_IN_SRAM_INFO32 _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_GET_NAT_IN_SRAM_INFO, \ + compat_uptr_t) +#define IPA_IOC_APP_CLOCK_VOTE32 _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_APP_CLOCK_VOTE, \ + compat_uptr_t) #endif /* #ifdef CONFIG_COMPAT */ #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311 @@ -1152,75 +1168,131 @@ struct ipa3_nat_ipv6ct_tmp_mem { /** * struct ipa3_nat_ipv6ct_common_mem - IPA NAT/IPv6CT memory device + * @name: the device name + * @lock: memory mutex * @class: pointer to the struct class * @dev: the dev_t of the device * @cdev: cdev of the device * @dev_num: device number + * @is_nat_mem: is the memory for v4 nat + * @is_ipv6ct_mem: is the memory for v6 nat + * @is_dev_init: flag indicating if device is initialized + * @is_hw_init: flag indicating if the corresponding HW is initialized + * @is_mapped: flag indicating if memory is mapped + * @phys_mem_size: the physical size in the shared memory + * @phys_mem_ofst: the offset in the shared memory + * @table_alloc_size: size (bytes) of table * @vaddr: the virtual address in the system memory * @dma_handle: the system memory DMA handle - * @phys_mem_size: the physical size in the shared memory - * @smem_offset: the offset in the shared memory - * @size: memory size - * @is_mapped: flag indicating if memory is mapped - * @is_sys_mem: flag indicating if memory is sys memory - * @is_mem_allocated: flag indicating if the memory is allocated - * @is_hw_init: flag indicating if the corresponding HW is initialized - * @is_dev_init: flag indicating if device is initialized - * @lock: memory mutex * @base_address: table virtual address * @base_table_addr: base table address * @expansion_table_addr: expansion table address * @table_entries: num of entries in the base table * @expn_table_entries: num of entries in the expansion table * @tmp_mem: temporary memory used to always provide HW with a legal memory - * @name: the device name */ struct ipa3_nat_ipv6ct_common_mem { - struct class *class; + char name[IPA_DEV_NAME_MAX_LEN]; + struct mutex lock; + struct class *class; struct device *dev; - struct cdev cdev; - dev_t dev_num; + struct cdev cdev; + dev_t dev_num; - /* system memory */ - void *vaddr; - dma_addr_t dma_handle; + bool is_nat_mem; + bool is_ipv6ct_mem; - /* shared memory */ - u32 phys_mem_size; - u32 smem_offset; + bool is_dev_init; + bool is_hw_init; + bool is_mapped; + + u32 phys_mem_size; + u32 phys_mem_ofst; + size_t table_alloc_size; + + void *vaddr; + dma_addr_t dma_handle; + void *base_address; + char *base_table_addr; + char *expansion_table_addr; + u32 table_entries; + u32 expn_table_entries; - size_t size; - bool is_mapped; - bool is_sys_mem; - bool is_mem_allocated; - bool is_hw_init; - bool is_dev_init; - struct mutex lock; - void *base_address; - char *base_table_addr; - char *expansion_table_addr; - u32 table_entries; - u32 expn_table_entries; struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem; - char name[IPA_DEV_NAME_MAX_LEN]; +}; + +/** + * struct ipa3_nat_mem_loc_data - memory specific info per table memory type + * @is_mapped: has the memory been mapped? + * @io_vaddr: the virtual address in the sram memory + * @vaddr: the virtual address in the system memory + * @dma_handle: the system memory DMA handle + * @phys_addr: physical sram memory location + * @table_alloc_size: size (bytes) of table + * @table_entries: number of entries in table + * @expn_table_entries: number of entries in expansion table + * @base_address: same as vaddr above + * @base_table_addr: base table address + * @expansion_table_addr: base table's expansion table address + * @index_table_addr: index table address + * @index_table_expansion_addr: index table's expansion table address + */ +struct ipa3_nat_mem_loc_data { + bool is_mapped; + + void __iomem *io_vaddr; + + void *vaddr; + dma_addr_t dma_handle; + + unsigned long phys_addr; + + size_t table_alloc_size; + + u32 table_entries; + u32 expn_table_entries; + + void *base_address; + + char *base_table_addr; + char *expansion_table_addr; + + char *index_table_addr; + char *index_table_expansion_addr; }; /** * struct ipa3_nat_mem - IPA NAT memory description * @dev: the memory device structure - * @index_table_addr: index table address - * @index_table_expansion_addr: index expansion table address * @public_ip_addr: ip address of nat table * @pdn_mem: pdn config table SW cache memory structure * @is_tmp_mem_allocated: indicate if tmp mem has been allocated + * @last_alloc_loc: last memory type allocated + * @active_table: which table memory type is currently active + * @switch2ddr_cnt: how many times we've switched focust to ddr + * @switch2sram_cnt: how many times we've switched focust to sram + * @ddr_in_use: is there table in ddr + * @sram_in_use: is there table in sram + * @mem_loc: memory specific info per table memory type */ struct ipa3_nat_mem { - struct ipa3_nat_ipv6ct_common_mem dev; - char *index_table_addr; - char *index_table_expansion_addr; - u32 public_ip_addr; - struct ipa_mem_buffer pdn_mem; - bool is_tmp_mem_allocated; + struct ipa3_nat_ipv6ct_common_mem dev; /* this item must be first */ + + u32 public_ip_addr; + struct ipa_mem_buffer pdn_mem; + + bool is_tmp_mem_allocated; + + enum ipa3_nat_mem_in last_alloc_loc; + + enum ipa3_nat_mem_in active_table; + u32 switch2ddr_cnt; + u32 switch2sram_cnt; + + bool ddr_in_use; + bool sram_in_use; + + struct ipa3_nat_mem_loc_data mem_loc[IPA_NAT_MEM_IN_MAX]; }; /** @@ -1228,7 +1300,7 @@ struct ipa3_nat_mem { * @dev: the memory device structure */ struct ipa3_ipv6ct_mem { - struct ipa3_nat_ipv6ct_common_mem dev; + struct ipa3_nat_ipv6ct_common_mem dev; /* this item must be first */ }; /** @@ -1286,6 +1358,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]; }; @@ -1663,12 +1736,16 @@ struct ipa3_pc_mbox_data { struct mbox_chan *mbox; }; +struct ipa3_app_clock_vote { + struct mutex mutex; + u32 cnt; +}; + /** * struct ipa3_context - IPA context * @cdev: cdev context * @ep: list of all end points - * @skip_ep_cfg_shadow: state to update filter table correctly across - power-save + * @skip_ep_cfg_shadow: state to update filter table correctly across power-save * @ep_flt_bitmap: End-points supporting filtering bitmap * @ep_flt_num: End-points supporting filtering number * @resume_on_connect: resume ep on ipa connect @@ -1754,6 +1831,7 @@ struct ipa3_pc_mbox_data { * @flt_rt_counters: the counters usage info for flt rt stats * @wdi3_ctx: IPA wdi3 context * @gsi_info: channel/protocol info for GSI offloading uC stats + * @app_vote: holds userspace application clock vote count * IPA context - holds all relevant info about IPA driver and its state * @lan_rx_napi_enable: flag if NAPI is enabled on the LAN dp * @lan_ndev: dummy netdev for LAN rx NAPI @@ -1931,6 +2009,7 @@ struct ipa3_context { struct net_device lan_ndev; struct napi_struct napi_lan_rx; struct ipahal_imm_cmd_pyld *coal_cmd_pyld; + struct ipa3_app_clock_vote app_clock_vote; }; struct ipa3_plat_drv_res { @@ -2066,10 +2145,6 @@ struct ipa3_plat_drv_res { * +-------------------------+ * | NAT TABLE (IPA4.5) | * +-------------------------+ - * | NAT IDX TABLE (IPA4.5) | - * +-------------------------+ - * | NAT EXP TABLE (IPA4.5) | - * +-------------------------+ * | CANARY (IPA4.5) | * +-------------------------+ * | CANARY (IPA4.5) | @@ -2149,10 +2224,6 @@ struct ipa3_mem_partition { u32 apps_hdr_proc_ctx_size_ddr; u32 nat_tbl_ofst; u32 nat_tbl_size; - u32 nat_index_tbl_ofst; - u32 nat_index_tbl_size; - u32 nat_exp_tbl_ofst; - u32 nat_exp_tbl_size; u32 modem_comp_decomp_ofst; u32 modem_comp_decomp_size; u32 modem_ofst; @@ -2180,8 +2251,10 @@ struct ipa3_mem_partition { u32 uc_descriptor_ram_size; u32 pdn_config_ofst; u32 pdn_config_size; - u32 stats_quota_ofst; - u32 stats_quota_size; + u32 stats_quota_q6_ofst; + u32 stats_quota_q6_size; + u32 stats_quota_ap_ofst; + u32 stats_quota_ap_size; u32 stats_tethering_ofst; u32 stats_tethering_size; u32 stats_fnr_ofst; @@ -2285,6 +2358,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, @@ -2440,6 +2515,8 @@ int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del); int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del); int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn); +int ipa3_nat_get_sram_info(struct ipa_nat_in_sram_info *info_ptr); +int ipa3_app_clk_vote(enum ipa_app_clock_vote_type vote_type); /* * Messaging diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index a9816c6d4096e82f7090950e42818586f3aea2f5..1c47b93dc11e0ab9ff31728fb3d10c5e296c793e 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 7be84f2ff70517afc92a55228153b084baafd976..d6c668a8ec7f104ae2cbad6a28f29134d5677d91 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.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 @@ -74,6 +74,7 @@ #define IPA_MHIP_HOLB_TMO 31 /* value to match granularity on ipa HW 4.5 */ #define IPA_MPM_FLOW_CTRL_ADD 1 #define IPA_MPM_FLOW_CTRL_DELETE 0 +#define IPA_MPM_NUM_OF_INIT_CMD_DESC 2 enum mhip_re_type { MHIP_RE_XFER = 0x2, @@ -479,12 +480,56 @@ static void ipa_mpm_gsi_chan_err_cb(struct gsi_chan_err_notify *err_data) static int ipa_mpm_set_dma_mode(enum ipa_client_type src_pipe, enum ipa_client_type dst_pipe, bool reset) { - int result = 0; + struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_MPM_NUM_OF_INIT_CMD_DESC]; + struct ipahal_imm_cmd_register_write reg_write_coal_close; + struct ipahal_reg_valmask valmask; + struct ipa3_desc desc[IPA_MPM_NUM_OF_INIT_CMD_DESC]; + int i, num_cmd = 0, result = 0; struct ipa_ep_cfg ep_cfg = { { 0 } }; IPA_MPM_FUNC_ENTRY(); IPA_MPM_DBG("DMA from %d to %d reset=%d\n", src_pipe, dst_pipe, reset); + memset(desc, 0, sizeof(desc)); + memset(cmd_pyld, 0, sizeof(cmd_pyld)); + + /* First step is to clear IPA Pipeline before changing DMA mode */ + if (ipa3_get_ep_mapping(src_pipe) != IPA_EP_NOT_ALLOCATED) { + i = ipa3_get_ep_mapping(src_pipe); + reg_write_coal_close.skip_pipeline_clear = false; + reg_write_coal_close.pipeline_clear_options = IPAHAL_HPS_CLEAR; + reg_write_coal_close.offset = ipahal_get_reg_ofst( + IPA_AGGR_FORCE_CLOSE); + ipahal_get_aggr_force_close_valmask(i, &valmask); + reg_write_coal_close.value = valmask.val; + reg_write_coal_close.value_mask = valmask.mask; + cmd_pyld[num_cmd] = ipahal_construct_imm_cmd( + IPA_IMM_CMD_REGISTER_WRITE, + ®_write_coal_close, false); + + if (!cmd_pyld[num_cmd]) { + IPA_MPM_ERR("failed to construct coal close IC\n"); + result = -ENOMEM; + goto destroy_imm_cmd; + } + ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]); + ++num_cmd; + } + /* NO-OP IC for ensuring that IPA pipeline is empty */ + cmd_pyld[num_cmd] = + ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false); + if (!cmd_pyld[num_cmd]) { + IPA_MPM_ERR("failed to construct NOP imm cmd\n"); + result = -ENOMEM; + goto destroy_imm_cmd; + } + + result = ipa3_send_cmd(num_cmd, desc); + if (result) { + IPAERR("fail to send Reset Pipeline immediate command\n"); + goto destroy_imm_cmd; + } + /* Reset to basic if reset = 1, otherwise set to DMA */ if (reset) ep_cfg.mode.mode = IPA_BASIC; @@ -496,6 +541,10 @@ static int ipa_mpm_set_dma_mode(enum ipa_client_type src_pipe, result = ipa_cfg_ep(ipa_get_ep_mapping(src_pipe), &ep_cfg); IPA_MPM_FUNC_EXIT(); +destroy_imm_cmd: + for (i = 0; i < num_cmd; ++i) + ipahal_destroy_imm_cmd(cmd_pyld[i]); + return result; } @@ -1031,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) ? @@ -2144,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_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c index 9631182053e0677c98c8c9a52535d85aa7696119..461d77c8c84a0c02557c34955525972ca5534c0e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c @@ -20,9 +20,10 @@ # include "ipa_emulation_stubs.h" #endif -#define IPA_NAT_PHYS_MEM_OFFSET 0 -#define IPA_IPV6CT_PHYS_MEM_OFFSET 0 +#define IPA_NAT_PHYS_MEM_OFFSET IPA_MEM_PART(nat_tbl_ofst) #define IPA_NAT_PHYS_MEM_SIZE IPA_RAM_NAT_SIZE + +#define IPA_IPV6CT_PHYS_MEM_OFFSET 0 #define IPA_IPV6CT_PHYS_MEM_SIZE IPA_RAM_IPV6CT_SIZE #define IPA_NAT_IPV6CT_TEMP_MEM_SIZE 128 @@ -38,6 +39,9 @@ #define IPA_TABLE_MAX_ENTRIES 8192 #define MAX_ALLOC_NAT_SIZE(size) (IPA_TABLE_MAX_ENTRIES * size) +#define IPA_VALID_TBL_INDEX(ti) \ + ((ti) == 0) + enum ipa_nat_ipv6ct_table_type { IPA_NAT_BASE_TBL = 0, IPA_NAT_EXPN_TBL = 1, @@ -47,6 +51,8 @@ enum ipa_nat_ipv6ct_table_type { IPA_IPV6CT_EXPN_TBL = 5 }; +static bool sram_compatible; + static int ipa3_nat_ipv6ct_vma_fault_remap(struct vm_fault *vmf) { vmf->page = NULL; @@ -60,6 +66,42 @@ static const struct vm_operations_struct ipa3_nat_ipv6ct_remap_vm_ops = { .fault = ipa3_nat_ipv6ct_vma_fault_remap, }; + +static inline const char *ipa3_nat_mem_in_as_str( + enum ipa3_nat_mem_in nmi) +{ + switch (nmi) { + case IPA_NAT_MEM_IN_DDR: + return "IPA_NAT_MEM_IN_DDR"; + case IPA_NAT_MEM_IN_SRAM: + return "IPA_NAT_MEM_IN_SRAM"; + default: + break; + } + return "INVALID_MEM_TYPE"; +} + +static inline char *ipa_ioc_v4_nat_init_as_str( + struct ipa_ioc_v4_nat_init *ptr, + char *buf, + uint32_t buf_sz) +{ + if (ptr && buf && buf_sz) { + snprintf( + buf, buf_sz, + "V4 NAT INIT: tbl_index(0x%02X) ipv4_rules_offset(0x%08X) expn_rules_offset(0x%08X) index_offset(0x%08X) index_expn_offset(0x%08X) table_entries(0x%04X) expn_table_entries(0x%04X) ip_addr(0x%08X)", + ptr->tbl_index, + ptr->ipv4_rules_offset, + ptr->expn_rules_offset, + ptr->index_offset, + ptr->index_expn_offset, + ptr->table_entries, + ptr->expn_table_entries, + ptr->ip_addr); + } + return buf; +} + static int ipa3_nat_ipv6ct_open(struct inode *inode, struct file *filp) { struct ipa3_nat_ipv6ct_common_mem *dev; @@ -73,90 +115,132 @@ static int ipa3_nat_ipv6ct_open(struct inode *inode, struct file *filp) return 0; } -static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma) +static int ipa3_nat_ipv6ct_mmap( + struct file *filp, + struct vm_area_struct *vma) { struct ipa3_nat_ipv6ct_common_mem *dev = (struct ipa3_nat_ipv6ct_common_mem *)filp->private_data; unsigned long vsize = vma->vm_end - vma->vm_start; - unsigned long phys_addr; - int result = 0; struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP); - IPADBG("\n"); + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa3_nat_mem_loc_data *mld_ptr; + enum ipa3_nat_mem_in nmi; + + int result = 0; + + nmi = nm_ptr->last_alloc_loc; + + IPADBG("In\n"); + + if (!IPA_VALID_NAT_MEM_IN(nmi)) { + IPAERR_RL("Bad ipa3_nat_mem_in type\n"); + result = -EPERM; + goto bail; + } + + mld_ptr = &nm_ptr->mem_loc[nmi]; if (!dev->is_dev_init) { - IPAERR("attempt to mmap %s before dev init\n", dev->name); - return -EPERM; + IPAERR("Attempt to mmap %s before dev init\n", + dev->name); + result = -EPERM; + goto bail; } mutex_lock(&dev->lock); - if (!dev->is_mem_allocated) { - IPAERR_RL("attempt to mmap %s before the memory allocation\n", - dev->name); + + if (!mld_ptr->vaddr) { + IPAERR_RL("Attempt to mmap %s before the memory allocation\n", + dev->name); result = -EPERM; - goto bail; + goto unlock; } - if (dev->is_sys_mem) { - if (dev->is_mapped) { - IPAERR("%s already mapped, only 1 mapping supported\n", - dev->name); - result = -EINVAL; - goto bail; - } - } else { - if ((dev->phys_mem_size == 0) || (vsize > dev->phys_mem_size)) { - IPAERR_RL("wrong parameters to %s mapping\n", - dev->name); + if (mld_ptr->is_mapped) { + IPAERR("%s already mapped, only 1 mapping supported\n", + dev->name); + result = -EINVAL; + goto unlock; + } + + if (nmi == IPA_NAT_MEM_IN_SRAM) { + if (dev->phys_mem_size == 0 || dev->phys_mem_size > vsize) { + IPAERR_RL("%s err vsize(0x%X) phys_mem_size(0x%X)\n", + dev->name, vsize, dev->phys_mem_size); result = -EINVAL; - goto bail; + goto unlock; } } - /* check if smmu enable & dma_coherent mode */ - if (!cb->valid || - !is_device_dma_coherent(cb->dev)) { + + /* + * Check if no smmu or non dma coherent + */ + if (!cb->valid || !is_device_dma_coherent(cb->dev)) { + + IPADBG("Either smmu valid=%u and/or DMA coherent=%u false\n", + cb->valid, is_device_dma_coherent(cb->dev)); + vma->vm_page_prot = - pgprot_noncached(vma->vm_page_prot); - IPADBG("App smmu enable in DMA mode\n"); + pgprot_noncached(vma->vm_page_prot); } - if (dev->is_sys_mem) { - IPADBG("Mapping system memory\n"); - IPADBG("map sz=0x%zx\n", dev->size); + mld_ptr->base_address = NULL; + + IPADBG("Mapping %s\n", ipa3_nat_mem_in_as_str(nmi)); + + if (nmi == IPA_NAT_MEM_IN_DDR) { + + IPADBG("map sz=0x%zx into vma size=0x%08x\n", + mld_ptr->table_alloc_size, + vsize); + result = dma_mmap_coherent( - ipa3_ctx->pdev, vma, - dev->vaddr, dev->dma_handle, - dev->size); + ipa3_ctx->pdev, + vma, + mld_ptr->vaddr, + mld_ptr->dma_handle, + mld_ptr->table_alloc_size); + if (result) { - IPAERR("unable to map memory. Err:%d\n", result); - goto bail; + IPAERR("dma_mmap_coherent failed. Err:%d\n", result); + goto unlock; } - dev->base_address = dev->vaddr; + + mld_ptr->base_address = mld_ptr->vaddr; } else { - IPADBG("Mapping shared(local) memory\n"); - IPADBG("map sz=0x%lx\n", vsize); - - phys_addr = ipa3_ctx->ipa_wrapper_base + - ipa3_ctx->ctrl->ipa_reg_base_ofst + - ipahal_get_reg_n_ofst(IPA_SW_AREA_RAM_DIRECT_ACCESS_n, - dev->smem_offset); - - if (remap_pfn_range( - vma, vma->vm_start, - phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) { - IPAERR("remap failed\n"); - result = -EAGAIN; - goto bail; + if (nmi == IPA_NAT_MEM_IN_SRAM) { + + IPADBG("map phys_mem_size(0x%08X) -> vma sz(0x%08X)\n", + dev->phys_mem_size, vsize); + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + result = vm_iomap_memory( + vma, mld_ptr->phys_addr, dev->phys_mem_size); + + if (result) { + IPAERR("vm_iomap_memory failed. Err:%d\n", + result); + goto unlock; + } + + mld_ptr->base_address = mld_ptr->vaddr; } - dev->base_address = (void *)vma->vm_start; } - result = 0; + + mld_ptr->is_mapped = true; + vma->vm_ops = &ipa3_nat_ipv6ct_remap_vm_ops; - dev->is_mapped = true; - IPADBG("return\n"); -bail: + +unlock: mutex_unlock(&dev->lock); + +bail: + IPADBG("Out\n"); + return result; } @@ -196,28 +280,38 @@ static struct ipa3_nat_ipv6ct_tmp_mem *ipa3_nat_ipv6ct_allocate_tmp_memory(void) static int ipa3_nat_ipv6ct_init_device( struct ipa3_nat_ipv6ct_common_mem *dev, - const char *name, - u32 phys_mem_size, - u32 smem_offset, - struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem) + const char *name, + u32 phys_mem_size, + u32 phys_mem_ofst, + struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem) { - int result; + int result = 0; + + IPADBG("In: Init of %s\n", name); - IPADBG("Init %s\n", name); + mutex_init(&dev->lock); + + dev->is_nat_mem = IS_NAT_MEM_DEV(dev); + dev->is_ipv6ct_mem = IS_IPV6CT_MEM_DEV(dev); if (strnlen(name, IPA_DEV_NAME_MAX_LEN) == IPA_DEV_NAME_MAX_LEN) { IPAERR("device name is too long\n"); - return -ENODEV; + result = -ENODEV; + goto bail; } strlcpy(dev->name, name, IPA_DEV_NAME_MAX_LEN); dev->class = class_create(THIS_MODULE, name); + if (IS_ERR(dev->class)) { IPAERR("unable to create the class for %s\n", name); - return -ENODEV; + result = -ENODEV; + goto bail; } + result = alloc_chrdev_region(&dev->dev_num, 0, 1, name); + if (result) { IPAERR("alloc_chrdev_region err. for %s\n", name); result = -ENODEV; @@ -233,63 +327,80 @@ static int ipa3_nat_ipv6ct_init_device( } cdev_init(&dev->cdev, &ipa3_nat_ipv6ct_fops); + dev->cdev.owner = THIS_MODULE; - mutex_init(&dev->lock); mutex_lock(&dev->lock); result = cdev_add(&dev->cdev, dev->dev_num, 1); + if (result) { IPAERR("cdev_add err=%d\n", -result); goto cdev_add_fail; } + dev->tmp_mem = tmp_mem; dev->phys_mem_size = phys_mem_size; - dev->smem_offset = smem_offset; + dev->phys_mem_ofst = phys_mem_ofst; + dev->is_dev_init = true; - dev->is_dev_init = true; - dev->tmp_mem = tmp_mem; mutex_unlock(&dev->lock); - IPADBG("ipa dev %s added successful. major:%d minor:%d\n", name, - MAJOR(dev->dev_num), MINOR(dev->dev_num)); - return 0; + IPADBG("ipa dev %s added successfully. major:%d minor:%d\n", name, + MAJOR(dev->dev_num), MINOR(dev->dev_num)); + + result = 0; + + goto bail; cdev_add_fail: mutex_unlock(&dev->lock); device_destroy(dev->class, dev->dev_num); + device_create_fail: unregister_chrdev_region(dev->dev_num, 1); + alloc_chrdev_region_fail: class_destroy(dev->class); + +bail: + IPADBG("Out\n"); + return result; } static void ipa3_nat_ipv6ct_destroy_device( struct ipa3_nat_ipv6ct_common_mem *dev) { - IPADBG("\n"); + IPADBG("In\n"); mutex_lock(&dev->lock); - if (dev->tmp_mem != NULL && - !(ipa3_ctx->nat_mem.is_tmp_mem_allocated)) { - dev->tmp_mem = NULL; - } else if (dev->tmp_mem != NULL && - ipa3_ctx->nat_mem.is_tmp_mem_allocated) { - dma_free_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE, - dev->tmp_mem->vaddr, dev->tmp_mem->dma_handle); - kfree(dev->tmp_mem); + if (dev->tmp_mem) { + if (ipa3_ctx->nat_mem.is_tmp_mem_allocated) { + dma_free_coherent( + ipa3_ctx->pdev, + IPA_NAT_IPV6CT_TEMP_MEM_SIZE, + dev->tmp_mem->vaddr, + dev->tmp_mem->dma_handle); + kfree(dev->tmp_mem); + dev->tmp_mem = NULL; + ipa3_ctx->nat_mem.is_tmp_mem_allocated = false; + } dev->tmp_mem = NULL; - ipa3_ctx->nat_mem.is_tmp_mem_allocated = false; } + device_destroy(dev->class, dev->dev_num); + unregister_chrdev_region(dev->dev_num, 1); + class_destroy(dev->class); + dev->is_dev_init = false; + mutex_unlock(&dev->lock); - IPADBG("return\n"); + IPADBG("Out\n"); } /** @@ -367,16 +478,28 @@ void ipa3_nat_ipv6ct_destroy_devices(void) ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->ipv6ct_mem.dev); } -static int ipa3_nat_ipv6ct_allocate_mem(struct ipa3_nat_ipv6ct_common_mem *dev, +static int ipa3_nat_ipv6ct_allocate_mem( + struct ipa3_nat_ipv6ct_common_mem *dev, struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc, enum ipahal_nat_type nat_type) { gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO; - int result = 0; size_t nat_entry_size; - IPADBG("passed memory size %zu for %s\n", - table_alloc->size, dev->name); + struct ipa3_nat_mem *nm_ptr; + struct ipa3_nat_mem_loc_data *mld_ptr; + uintptr_t tmp_ptr; + + int result = 0; + + IPADBG("In: Requested alloc size %zu for %s\n", + table_alloc->size, dev->name); + + if (!table_alloc->size) { + IPAERR_RL("Invalid Parameters\n"); + result = -EPERM; + goto bail; + } if (!dev->is_dev_init) { IPAERR("%s hasn't been initialized\n", dev->name); @@ -384,47 +507,131 @@ static int ipa3_nat_ipv6ct_allocate_mem(struct ipa3_nat_ipv6ct_common_mem *dev, goto bail; } - if (dev->is_mem_allocated) { - IPAERR("Memory already allocated\n"); + if ((dev->is_nat_mem && nat_type != IPAHAL_NAT_IPV4) || + (dev->is_ipv6ct_mem && nat_type != IPAHAL_NAT_IPV6CT)) { + IPAERR("%s dev type(%s) and nat_type(%s) mismatch\n", + dev->name, + (dev->is_nat_mem) ? "V4" : "V6", + ipahal_nat_type_str(nat_type)); result = -EPERM; goto bail; } ipahal_nat_entry_size(nat_type, &nat_entry_size); + if (table_alloc->size > MAX_ALLOC_NAT_SIZE(nat_entry_size)) { IPAERR("Trying allocate more size = %zu, Max allowed = %zu\n", - table_alloc->size, - MAX_ALLOC_NAT_SIZE(nat_entry_size)); + table_alloc->size, + MAX_ALLOC_NAT_SIZE(nat_entry_size)); result = -EPERM; goto bail; } - if (!table_alloc->size) { - IPAERR_RL("Invalid Parameters\n"); - result = -EPERM; - goto bail; - } + if (nat_type == IPAHAL_NAT_IPV4) { - if (table_alloc->size > IPA_NAT_PHYS_MEM_SIZE) { - IPADBG("Allocating system memory\n"); - dev->is_sys_mem = true; - dev->vaddr = - dma_alloc_coherent(ipa3_ctx->pdev, table_alloc->size, - &dev->dma_handle, gfp_flags); - if (dev->vaddr == NULL) { - IPAERR("memory alloc failed\n"); - result = -ENOMEM; - goto bail; + nm_ptr = (struct ipa3_nat_mem *) dev; + + if (table_alloc->size <= IPA_NAT_PHYS_MEM_SIZE) { + /* + * CAN fit in SRAM, hence we'll use SRAM... + */ + IPADBG("V4 NAT will reside in: %s\n", + ipa3_nat_mem_in_as_str(IPA_NAT_MEM_IN_SRAM)); + + if (nm_ptr->sram_in_use) { + IPAERR("Memory already allocated\n"); + result = -EPERM; + goto bail; + } + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_SRAM]; + + mld_ptr->table_alloc_size = table_alloc->size; + + mld_ptr->phys_addr = + ipa3_ctx->ipa_wrapper_base + + ipa3_ctx->ctrl->ipa_reg_base_ofst + + ipahal_get_reg_n_ofst( + IPA_SW_AREA_RAM_DIRECT_ACCESS_n, + 0) + + IPA_NAT_PHYS_MEM_OFFSET; + + mld_ptr->io_vaddr = ioremap( + mld_ptr->phys_addr, IPA_NAT_PHYS_MEM_SIZE); + + if (mld_ptr->io_vaddr == NULL) { + IPAERR("ioremap failed\n"); + result = -ENOMEM; + goto bail; + } + + tmp_ptr = (uintptr_t) mld_ptr->io_vaddr; + + mld_ptr->vaddr = (void *) tmp_ptr; + + nm_ptr->sram_in_use = true; + nm_ptr->last_alloc_loc = IPA_NAT_MEM_IN_SRAM; + + } else { + + /* + * CAN NOT fit in SRAM, hence we'll allocate DDR... + */ + IPADBG("V4 NAT will reside in: %s\n", + ipa3_nat_mem_in_as_str(IPA_NAT_MEM_IN_DDR)); + + if (nm_ptr->ddr_in_use) { + IPAERR("Memory already allocated\n"); + result = -EPERM; + goto bail; + } + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_DDR]; + + mld_ptr->table_alloc_size = table_alloc->size; + + mld_ptr->vaddr = + dma_alloc_coherent( + ipa3_ctx->pdev, + mld_ptr->table_alloc_size, + &mld_ptr->dma_handle, + gfp_flags); + + if (mld_ptr->vaddr == NULL) { + IPAERR("memory alloc failed\n"); + result = -ENOMEM; + goto bail; + } + + nm_ptr->ddr_in_use = true; + nm_ptr->last_alloc_loc = IPA_NAT_MEM_IN_DDR; } - dev->size = table_alloc->size; } else { - IPADBG("using shared(local) memory\n"); - dev->is_sys_mem = false; - } + if (nat_type == IPAHAL_NAT_IPV6CT) { - IPADBG("return\n"); + dev->table_alloc_size = table_alloc->size; + + IPADBG("V6 NAT will reside in: %s\n", + ipa3_nat_mem_in_as_str(IPA_NAT_MEM_IN_DDR)); + + dev->vaddr = + dma_alloc_coherent( + ipa3_ctx->pdev, + dev->table_alloc_size, + &dev->dma_handle, + gfp_flags); + + if (dev->vaddr == NULL) { + IPAERR("memory alloc failed\n"); + result = -ENOMEM; + goto bail; + } + } + } bail: + IPADBG("Out\n"); + return result; } @@ -464,62 +671,94 @@ int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem) * * Returns: 0 on success, negative on failure */ -int ipa3_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc) +int ipa3_allocate_nat_table( + struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc) { - struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem); - gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO; + struct ipa3_nat_mem *nm_ptr = &(ipa3_ctx->nat_mem); + struct ipa3_nat_mem_loc_data *mld_ptr; + int result; - IPADBG("\n"); + IPADBG("table size:%u offset:%u\n", + table_alloc->size, table_alloc->offset); - mutex_lock(&nat_ctx->dev.lock); + mutex_lock(&nm_ptr->dev.lock); + + result = ipa3_nat_ipv6ct_allocate_mem( + &nm_ptr->dev, + table_alloc, + IPAHAL_NAT_IPV4); - result = ipa3_nat_ipv6ct_allocate_mem(&nat_ctx->dev, table_alloc, - IPAHAL_NAT_IPV4); if (result) goto bail; - if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) { + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 + && + nm_ptr->pdn_mem.base == NULL) { + + gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO; size_t pdn_entry_size; - struct ipa_mem_buffer *pdn_mem = &nat_ctx->pdn_mem; + struct ipa_mem_buffer *pdn_mem_ptr = &nm_ptr->pdn_mem; ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size); - pdn_mem->size = pdn_entry_size * IPA_MAX_PDN_NUM; - if (IPA_MEM_PART(pdn_config_size) < pdn_mem->size) { + + pdn_mem_ptr->size = pdn_entry_size * IPA_MAX_PDN_NUM; + + if (IPA_MEM_PART(pdn_config_size) < pdn_mem_ptr->size) { IPAERR( "number of PDN entries exceeds SRAM available space\n"); result = -ENOMEM; goto fail_alloc_pdn; } - pdn_mem->base = dma_zalloc_coherent(ipa3_ctx->pdev, - pdn_mem->size, - &pdn_mem->phys_base, - gfp_flags); - if (!pdn_mem->base) { + pdn_mem_ptr->base = + dma_zalloc_coherent( + ipa3_ctx->pdev, + pdn_mem_ptr->size, + &pdn_mem_ptr->phys_base, + gfp_flags); + + if (pdn_mem_ptr->base == NULL) { IPAERR("fail to allocate PDN memory\n"); result = -ENOMEM; goto fail_alloc_pdn; } + IPADBG("IPA NAT dev allocated PDN memory successfully\n"); } - nat_ctx->dev.is_mem_allocated = true; IPADBG("IPA NAT dev init successfully\n"); - mutex_unlock(&nat_ctx->dev.lock); + + mutex_unlock(&nm_ptr->dev.lock); IPADBG("return\n"); return 0; fail_alloc_pdn: - if (nat_ctx->dev.vaddr) { - dma_free_coherent(ipa3_ctx->pdev, table_alloc->size, - nat_ctx->dev.vaddr, nat_ctx->dev.dma_handle); - nat_ctx->dev.vaddr = NULL; + mld_ptr = &nm_ptr->mem_loc[nm_ptr->last_alloc_loc]; + + if (nm_ptr->last_alloc_loc == IPA_NAT_MEM_IN_DDR) { + if (mld_ptr->vaddr) { + dma_free_coherent( + ipa3_ctx->pdev, + mld_ptr->table_alloc_size, + mld_ptr->vaddr, + mld_ptr->dma_handle); + mld_ptr->vaddr = NULL; + } + } + + if (nm_ptr->last_alloc_loc == IPA_NAT_MEM_IN_SRAM) { + if (mld_ptr->io_vaddr) { + iounmap(mld_ptr->io_vaddr); + mld_ptr->io_vaddr = NULL; + mld_ptr->vaddr = NULL; + } } + bail: - mutex_unlock(&nat_ctx->dev.lock); + mutex_unlock(&nm_ptr->dev.lock); return result; } @@ -548,11 +787,13 @@ int ipa3_allocate_ipv6ct_table( mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock); result = ipa3_nat_ipv6ct_allocate_mem( - &ipa3_ctx->ipv6ct_mem.dev, table_alloc, IPAHAL_NAT_IPV6CT); + &ipa3_ctx->ipv6ct_mem.dev, + table_alloc, + IPAHAL_NAT_IPV6CT); + if (result) goto bail; - ipa3_ctx->ipv6ct_mem.dev.is_mem_allocated = true; IPADBG("IPA IPv6CT dev init successfully\n"); bail: @@ -562,42 +803,89 @@ int ipa3_allocate_ipv6ct_table( static int ipa3_nat_ipv6ct_check_table_params( struct ipa3_nat_ipv6ct_common_mem *dev, - uint32_t offset, uint16_t entries_num, + enum ipa3_nat_mem_in nmi, + uint32_t offset, + uint16_t entries_num, enum ipahal_nat_type nat_type) { - int result; - size_t entry_size, table_size; + size_t entry_size, table_size, orig_alloc_size; - result = ipahal_nat_entry_size(nat_type, &entry_size); - if (result) { + struct ipa3_nat_mem *nm_ptr; + struct ipa3_nat_mem_loc_data *mld_ptr; + + int ret = 0; + + IPADBG("In\n"); + + IPADBG( + "v4(%u) v6(%u) nmi(%s) ofst(%u) ents(%u) nt(%s)\n", + dev->is_nat_mem, + dev->is_ipv6ct_mem, + ipa3_nat_mem_in_as_str(nmi), + offset, + entries_num, + ipahal_nat_type_str(nat_type)); + + if (dev->is_ipv6ct_mem) { + + orig_alloc_size = dev->table_alloc_size; + + if (offset > UINT_MAX - dev->dma_handle) { + IPAERR_RL("Failed due to integer overflow\n"); + IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n", + dev->name, &dev->dma_handle, offset); + ret = -EPERM; + goto bail; + } + + } else { /* dev->is_nat_mem */ + + nm_ptr = (struct ipa3_nat_mem *) dev; + + mld_ptr = &nm_ptr->mem_loc[nmi]; + orig_alloc_size = mld_ptr->table_alloc_size; + + if (nmi == IPA_NAT_MEM_IN_DDR) { + if (offset > UINT_MAX - mld_ptr->dma_handle) { + IPAERR_RL("Failed due to integer overflow\n"); + IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n", + dev->name, &mld_ptr->dma_handle, offset); + ret = -EPERM; + goto bail; + } + } + } + + ret = ipahal_nat_entry_size(nat_type, &entry_size); + + if (ret) { IPAERR("Failed to retrieve size of entry for %s\n", - ipahal_nat_type_str(nat_type)); - return result; + ipahal_nat_type_str(nat_type)); + goto bail; } + table_size = entry_size * entries_num; /* check for integer overflow */ if (offset > UINT_MAX - table_size) { IPAERR_RL("Detected overflow\n"); - return -EPERM; + ret = -EPERM; + goto bail; } /* Check offset is not beyond allocated size */ - if (dev->size < offset + table_size) { + if (offset + table_size > orig_alloc_size) { IPAERR_RL("Table offset not valid\n"); IPAERR_RL("offset:%d entries:%d table_size:%zu mem_size:%zu\n", - offset, entries_num, table_size, dev->size); - return -EPERM; + offset, entries_num, table_size, orig_alloc_size); + ret = -EPERM; + goto bail; } - if (dev->is_sys_mem && offset > UINT_MAX - dev->dma_handle) { - IPAERR_RL("Failed due to integer overflow\n"); - IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n", - dev->name, &dev->dma_handle, offset); - return -EPERM; - } +bail: + IPADBG("Out\n"); - return 0; + return ret; } static inline void ipa3_nat_ipv6ct_create_init_cmd( @@ -631,26 +919,148 @@ static inline void ipa3_nat_ipv6ct_create_init_cmd( table_name, expn_table_entries); } -static inline void ipa3_nat_ipv6ct_init_device_structure( +static inline bool chk_sram_offset_alignment( + uintptr_t addr, + u32 mask) +{ + if (addr & (uintptr_t) mask) { + IPAERR("sram addr(%pK) is not properly aligned\n", addr); + return false; + } + return true; +} + +static inline int ipa3_nat_ipv6ct_init_device_structure( struct ipa3_nat_ipv6ct_common_mem *dev, + enum ipa3_nat_mem_in nmi, uint32_t base_table_offset, uint32_t expn_table_offset, uint16_t table_entries, - uint16_t expn_table_entries) + uint16_t expn_table_entries, + uint32_t index_offset, + uint32_t index_expn_offset, + uint8_t focus_change) { - dev->base_table_addr = (char *)dev->base_address + base_table_offset; - IPADBG("%s base_table_addr: 0x%pK\n", dev->name, dev->base_table_addr); + int ret = 0; + + IPADBG("In\n"); + + IPADBG( + "v4(%u) v6(%u) nmi(%s) bto(%u) eto(%u) t_ents(%u) et_ents(%u) io(%u) ieo(%u)\n", + dev->is_nat_mem, + dev->is_ipv6ct_mem, + ipa3_nat_mem_in_as_str(nmi), + base_table_offset, + expn_table_offset, + table_entries, + expn_table_entries, + index_offset, + index_expn_offset); + + if (dev->is_ipv6ct_mem) { + + IPADBG("v6\n"); + + dev->base_table_addr = + (char *) dev->base_address + base_table_offset; + + IPADBG("%s base_table_addr: 0x%pK\n", + dev->name, dev->base_table_addr); + + dev->expansion_table_addr = + (char *) dev->base_address + expn_table_offset; + + IPADBG("%s expansion_table_addr: 0x%pK\n", + dev->name, dev->expansion_table_addr); + + IPADBG("%s table_entries: %d\n", + dev->name, table_entries); + + dev->table_entries = table_entries; + + IPADBG("%s expn_table_entries: %d\n", + dev->name, expn_table_entries); + + dev->expn_table_entries = expn_table_entries; + + } else if (dev->is_nat_mem) { + + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa3_nat_mem_loc_data *mld_p = + &nm_ptr->mem_loc[nmi]; + + IPADBG("v4\n"); + + nm_ptr->active_table = nmi; + + mld_p->base_table_addr = + (char *) mld_p->base_address + base_table_offset; + + IPADBG("%s base_table_addr: 0x%pK\n", + dev->name, mld_p->base_table_addr); + + mld_p->expansion_table_addr = + (char *) mld_p->base_address + expn_table_offset; + + IPADBG("%s expansion_table_addr: 0x%pK\n", + dev->name, mld_p->expansion_table_addr); + + IPADBG("%s table_entries: %d\n", + dev->name, table_entries); + + mld_p->table_entries = table_entries; + + IPADBG("%s expn_table_entries: %d\n", + dev->name, expn_table_entries); + + mld_p->expn_table_entries = expn_table_entries; - dev->expansion_table_addr = - (char *)dev->base_address + expn_table_offset; - IPADBG("%s expansion_table_addr: 0x%pK\n", - dev->name, dev->expansion_table_addr); + mld_p->index_table_addr = + (char *) mld_p->base_address + index_offset; - IPADBG("%s table_entries: %d\n", dev->name, table_entries); - dev->table_entries = table_entries; + IPADBG("index_table_addr: 0x%pK\n", + mld_p->index_table_addr); - IPADBG("%s expn_table_entries: %d\n", dev->name, expn_table_entries); - dev->expn_table_entries = expn_table_entries; + mld_p->index_table_expansion_addr = + (char *) mld_p->base_address + index_expn_offset; + + IPADBG("index_table_expansion_addr: 0x%pK\n", + mld_p->index_table_expansion_addr); + + if (nmi == IPA_NAT_MEM_IN_DDR) { + if (focus_change) + nm_ptr->switch2ddr_cnt++; + } else { + /* + * The IPA wants certain SRAM addresses + * to have particular low order bits to + * be zero. We test here to ensure... + */ + if (!chk_sram_offset_alignment( + (uintptr_t) mld_p->base_table_addr, + 31) || + !chk_sram_offset_alignment( + (uintptr_t) mld_p->expansion_table_addr, + 31) || + !chk_sram_offset_alignment( + (uintptr_t) mld_p->index_table_addr, + 3) || + !chk_sram_offset_alignment( + (uintptr_t) mld_p->index_table_expansion_addr, + 3)) { + ret = -ENODEV; + goto done; + } + + if (focus_change) + nm_ptr->switch2sram_cnt++; + } + } + +done: + IPADBG("Out\n"); + + return ret; } static void ipa3_nat_create_init_cmd( @@ -682,6 +1092,7 @@ static void ipa3_nat_create_init_cmd( cmd->index_table_expansion_addr = base_addr + init->index_expn_offset; IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset); + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) { /* * starting IPAv4.0 public ip field changed to store the @@ -707,7 +1118,7 @@ static void ipa3_nat_create_modify_pdn_cmd( ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size); mem_size = pdn_entry_size * IPA_MAX_PDN_NUM; - if (zero_mem) + if (zero_mem && ipa3_ctx->nat_mem.pdn_mem.base) memset(ipa3_ctx->nat_mem.pdn_mem.base, 0, mem_size); /* Copy the PDN config table to SRAM */ @@ -909,83 +1320,129 @@ static int ipa3_ipv6ct_send_init_cmd(struct ipahal_imm_cmd_ip_v6_ct_init *cmd) * * Returns: 0 on success, negative on failure */ -int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) +int ipa3_nat_init_cmd( + struct ipa_ioc_v4_nat_init *init) { + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + enum ipa3_nat_mem_in nmi; + struct ipa3_nat_mem_loc_data *mld_ptr; + struct ipahal_imm_cmd_ip_v4_nat_init cmd; - int result; - IPADBG("\n"); + int result; - if (!ipa3_ctx->nat_mem.dev.is_mapped) { - IPAERR_RL("attempt to init %s before mmap\n", - ipa3_ctx->nat_mem.dev.name); - return -EPERM; + IPADBG("In\n"); + + if (!sram_compatible) { + init->mem_type = 0; + init->focus_change = 0; } - if (init->tbl_index >= 1) { - IPAERR_RL("Unsupported table index %d\n", init->tbl_index); - return -EPERM; + nmi = init->mem_type; + + IPADBG("tbl_index(%d) table_entries(%u)\n", + init->tbl_index, + init->table_entries); + + memset(&cmd, 0, sizeof(cmd)); + + if (!IPA_VALID_TBL_INDEX(init->tbl_index)) { + IPAERR_RL("Unsupported table index %d\n", + init->tbl_index); + result = -EPERM; + goto bail; } if (init->table_entries == 0) { IPAERR_RL("Table entries is zero\n"); - return -EPERM; + result = -EPERM; + goto bail; } - result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->nat_mem.dev, - init->ipv4_rules_offset, - init->table_entries + 1, - IPAHAL_NAT_IPV4); - if (result) { - IPAERR_RL("Bad params for NAT base table\n"); - return result; + if (!IPA_VALID_NAT_MEM_IN(nmi)) { + IPAERR_RL("Bad ipa3_nat_mem_in type\n"); + result = -EPERM; + goto bail; + } + + IPADBG("nmi(%s)\n", ipa3_nat_mem_in_as_str(nmi)); + + mld_ptr = &nm_ptr->mem_loc[nmi]; + + if (!mld_ptr->is_mapped) { + IPAERR_RL("Attempt to init %s before mmap\n", dev->name); + result = -EPERM; + goto bail; } result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->nat_mem.dev, + dev, nmi, + init->ipv4_rules_offset, + init->table_entries + 1, + IPAHAL_NAT_IPV4); + + if (result) { + IPAERR_RL("Bad params for NAT base table\n"); + goto bail; + } + + result = ipa3_nat_ipv6ct_check_table_params( + dev, nmi, init->expn_rules_offset, init->expn_table_entries, IPAHAL_NAT_IPV4); + if (result) { IPAERR_RL("Bad params for NAT expansion table\n"); - return result; + goto bail; } result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->nat_mem.dev, + dev, nmi, init->index_offset, init->table_entries + 1, IPAHAL_NAT_IPV4_INDEX); + if (result) { IPAERR_RL("Bad params for index table\n"); - return result; + goto bail; } result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->nat_mem.dev, + dev, nmi, init->index_expn_offset, init->expn_table_entries, IPAHAL_NAT_IPV4_INDEX); + if (result) { IPAERR_RL("Bad params for index expansion table\n"); - return result; + goto bail; } - if (ipa3_ctx->nat_mem.dev.is_sys_mem) { - IPADBG("using system memory for nat table\n"); - /* - * Safe to process, since integer overflow was - * checked in ipa3_nat_ipv6ct_check_table_params - */ - ipa3_nat_create_init_cmd(init, false, - ipa3_ctx->nat_mem.dev.dma_handle, &cmd); + IPADBG("Table memory becoming active: %s\n", + ipa3_nat_mem_in_as_str(nmi)); + + if (nmi == IPA_NAT_MEM_IN_DDR) { + ipa3_nat_create_init_cmd( + init, + false, + mld_ptr->dma_handle, + &cmd); } else { - IPADBG("using shared(local) memory for nat table\n"); - ipa3_nat_create_init_cmd(init, true, IPA_RAM_NAT_OFST, &cmd); + ipa3_nat_create_init_cmd( + init, + true, + IPA_RAM_NAT_OFST, + &cmd); } - if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) { + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 + && + nm_ptr->pdn_mem.base + && + !init->focus_change) { + struct ipahal_nat_pdn_entry pdn_entry; /* store ip in pdn entry cache array */ @@ -996,46 +1453,48 @@ int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) result = ipahal_nat_construct_entry( IPAHAL_NAT_IPV4_PDN, &pdn_entry, - ipa3_ctx->nat_mem.pdn_mem.base); + nm_ptr->pdn_mem.base); + if (result) { IPAERR("Fail to construct NAT pdn entry\n"); - return result; + goto bail; } - - IPADBG("Public ip address:0x%x\n", init->ip_addr); } - IPADBG("posting NAT init command\n"); + IPADBG("Posting NAT init command\n"); + result = ipa3_nat_send_init_cmd(&cmd, false); + if (result) { IPAERR("Fail to send NAT init immediate command\n"); - return result; + goto bail; } - ipa3_nat_ipv6ct_init_device_structure( - &ipa3_ctx->nat_mem.dev, + result = ipa3_nat_ipv6ct_init_device_structure( + dev, nmi, init->ipv4_rules_offset, init->expn_rules_offset, init->table_entries, - init->expn_table_entries); + init->expn_table_entries, + init->index_offset, + init->index_expn_offset, + init->focus_change); - ipa3_ctx->nat_mem.public_ip_addr = init->ip_addr; - IPADBG("Public IP address:%pI4h\n", &ipa3_ctx->nat_mem.public_ip_addr); + if (result) { + IPAERR("Table offset initialization failure\n"); + goto bail; + } - ipa3_ctx->nat_mem.index_table_addr = - (char *)ipa3_ctx->nat_mem.dev.base_address + - init->index_offset; - IPADBG("index_table_addr: 0x%pK\n", - ipa3_ctx->nat_mem.index_table_addr); + nm_ptr->public_ip_addr = init->ip_addr; - ipa3_ctx->nat_mem.index_table_expansion_addr = - (char *)ipa3_ctx->nat_mem.dev.base_address + init->index_expn_offset; - IPADBG("index_table_expansion_addr: 0x%pK\n", - ipa3_ctx->nat_mem.index_table_expansion_addr); + IPADBG("Public IP address:%pI4h\n", &nm_ptr->public_ip_addr); - ipa3_ctx->nat_mem.dev.is_hw_init = true; - IPADBG("return\n"); - return 0; + dev->is_hw_init = true; + +bail: + IPADBG("Out\n"); + + return result; } /** @@ -1046,25 +1505,25 @@ int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) * * Returns: 0 on success, negative on failure */ -int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init) +int ipa3_ipv6ct_init_cmd( + struct ipa_ioc_ipv6ct_init *init) { + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->ipv6ct_mem.dev; + struct ipahal_imm_cmd_ip_v6_ct_init cmd; + int result; - IPADBG("\n"); + IPADBG("In\n"); + + memset(&cmd, 0, sizeof(cmd)); if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) { IPAERR_RL("IPv6 connection tracking isn't supported\n"); return -EPERM; } - if (!ipa3_ctx->ipv6ct_mem.dev.is_mapped) { - IPAERR_RL("attempt to init %s before mmap\n", - ipa3_ctx->ipv6ct_mem.dev.name); - return -EPERM; - } - - if (init->tbl_index >= 1) { + if (!IPA_VALID_TBL_INDEX(init->tbl_index)) { IPAERR_RL("Unsupported table index %d\n", init->tbl_index); return -EPERM; } @@ -1074,72 +1533,70 @@ int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init) return -EPERM; } + if (!dev->is_mapped) { + IPAERR_RL("attempt to init %s before mmap\n", + dev->name); + return -EPERM; + } + result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->ipv6ct_mem.dev, + dev, IPA_NAT_MEM_IN_DDR, init->base_table_offset, init->table_entries + 1, IPAHAL_NAT_IPV6CT); + if (result) { IPAERR_RL("Bad params for IPv6CT base table\n"); return result; } result = ipa3_nat_ipv6ct_check_table_params( - &ipa3_ctx->ipv6ct_mem.dev, + dev, IPA_NAT_MEM_IN_DDR, init->expn_table_offset, init->expn_table_entries, IPAHAL_NAT_IPV6CT); + if (result) { IPAERR_RL("Bad params for IPv6CT expansion table\n"); return result; } - if (ipa3_ctx->ipv6ct_mem.dev.is_sys_mem) { - IPADBG("using system memory for nat table\n"); - /* - * Safe to process, since integer overflow was - * checked in ipa3_nat_ipv6ct_check_table_params - */ - ipa3_nat_ipv6ct_create_init_cmd( - &cmd.table_init, - false, - ipa3_ctx->ipv6ct_mem.dev.dma_handle, - init->tbl_index, - init->base_table_offset, - init->expn_table_offset, - init->table_entries, - init->expn_table_entries, - ipa3_ctx->ipv6ct_mem.dev.name); - } else { - IPADBG("using shared(local) memory for nat table\n"); - ipa3_nat_ipv6ct_create_init_cmd( - &cmd.table_init, - true, - IPA_RAM_IPV6CT_OFST, - init->tbl_index, - init->base_table_offset, - init->expn_table_offset, - init->table_entries, - init->expn_table_entries, - ipa3_ctx->ipv6ct_mem.dev.name); - } + IPADBG("Will install v6 NAT in: %s\n", + ipa3_nat_mem_in_as_str(IPA_NAT_MEM_IN_DDR)); + + ipa3_nat_ipv6ct_create_init_cmd( + &cmd.table_init, + false, + dev->dma_handle, + init->tbl_index, + init->base_table_offset, + init->expn_table_offset, + init->table_entries, + init->expn_table_entries, + dev->name); IPADBG("posting ip_v6_ct_init imm command\n"); + result = ipa3_ipv6ct_send_init_cmd(&cmd); + if (result) { IPAERR("fail to send IPv6CT init immediate command\n"); return result; } ipa3_nat_ipv6ct_init_device_structure( - &ipa3_ctx->ipv6ct_mem.dev, + dev, + IPA_NAT_MEM_IN_DDR, init->base_table_offset, init->expn_table_offset, init->table_entries, - init->expn_table_entries); + init->expn_table_entries, + 0, 0, 0); + + dev->is_hw_init = true; + + IPADBG("Out\n"); - ipa3_ctx->ipv6ct_mem.dev.is_hw_init = true; - IPADBG("return\n"); return 0; } @@ -1151,19 +1608,25 @@ int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init) * * Returns: 0 on success, negative on failure */ -int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn) +int ipa3_nat_mdfy_pdn( + struct ipa_ioc_nat_pdn_entry *mdfy_pdn) { + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa_mem_buffer *pdn_mem_ptr = &nm_ptr->pdn_mem; + struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 }; - struct ipa3_desc desc; + struct ipahal_nat_pdn_entry pdn_fields = { 0 }; + struct ipa3_desc desc = { 0 }; struct ipahal_imm_cmd_pyld *cmd_pyld; - int result = 0; - struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem); - struct ipahal_nat_pdn_entry pdn_fields; + size_t entry_size; - IPADBG("\n"); + int result = 0; - mutex_lock(&nat_ctx->dev.lock); + IPADBG("In\n"); + + mutex_lock(&dev->lock); if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) { IPAERR_RL("IPA HW does not support multi PDN\n"); @@ -1171,9 +1634,9 @@ int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn) goto bail; } - if (!nat_ctx->dev.is_mem_allocated) { + if (pdn_mem_ptr->base == NULL) { IPAERR_RL( - "attempt to modify a PDN entry before the PDN table memory allocation\n"); + "Attempt to modify a PDN entry before the PDN table memory allocation\n"); result = -EPERM; goto bail; } @@ -1184,21 +1647,27 @@ int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn) goto bail; } - /* store ip in pdn entry cache array */ - pdn_fields.public_ip = mdfy_pdn->public_ip; + /* + * Store ip in pdn entry cache array + */ + pdn_fields.public_ip = mdfy_pdn->public_ip; pdn_fields.dst_metadata = mdfy_pdn->dst_metadata; pdn_fields.src_metadata = mdfy_pdn->src_metadata; - /* mark tethering bit for remote modem */ + /* + * Mark tethering bit for remote modem + */ if (ipa3_ctx->ipa_hw_type == IPA_HW_v4_1) { - pdn_fields.src_metadata |= - IPA_QMAP_TETH_BIT; + pdn_fields.src_metadata |= IPA_QMAP_TETH_BIT; } - /* get size of the entry */ + /* + * Get size of the entry + */ result = ipahal_nat_entry_size( IPAHAL_NAT_IPV4_PDN, &entry_size); + if (result) { IPAERR("Failed to retrieve pdn entry size\n"); goto bail; @@ -1207,8 +1676,8 @@ int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn) result = ipahal_nat_construct_entry( IPAHAL_NAT_IPV4_PDN, &pdn_fields, - (nat_ctx->pdn_mem.base + - (mdfy_pdn->pdn_index)*(entry_size))); + (pdn_mem_ptr->base + (mdfy_pdn->pdn_index)*(entry_size))); + if (result) { IPAERR("Fail to construct NAT pdn entry\n"); goto bail; @@ -1217,65 +1686,77 @@ int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn) IPADBG("Modify PDN in index: %d Public ip address:%pI4h\n", mdfy_pdn->pdn_index, &pdn_fields.public_ip); + IPADBG("Modify PDN dst metadata: 0x%x src metadata: 0x%x\n", pdn_fields.dst_metadata, pdn_fields.src_metadata); - /* Copy the PDN config table to SRAM */ + /* + * Copy the PDN config table to SRAM + */ ipa3_nat_create_modify_pdn_cmd(&mem_cmd, false); + cmd_pyld = ipahal_construct_imm_cmd( IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false); + if (!cmd_pyld) { IPAERR( "fail construct dma_shared_mem cmd: for pdn table\n"); result = -ENOMEM; goto bail; } + ipa3_init_imm_cmd_desc(&desc, cmd_pyld); IPADBG("sending PDN table copy cmd\n"); + result = ipa3_send_cmd(1, &desc); + if (result) IPAERR("Fail to send PDN table copy immediate command\n"); ipahal_destroy_imm_cmd(cmd_pyld); - IPADBG("return\n"); - bail: - mutex_unlock(&nat_ctx->dev.lock); + mutex_unlock(&dev->lock); + + IPADBG("Out\n"); + return result; } -static uint32_t ipa3_nat_ipv6ct_calculate_table_size(uint8_t base_addr) +static uint32_t ipa3_nat_ipv6ct_calculate_table_size( + enum ipa3_nat_mem_in nmi, + uint8_t base_addr) { size_t entry_size; - u32 entries_num; + u32 num_entries; enum ipahal_nat_type nat_type; + struct ipa3_nat_mem_loc_data *mld_ptr = &ipa3_ctx->nat_mem.mem_loc[nmi]; switch (base_addr) { case IPA_NAT_BASE_TBL: - entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1; + num_entries = mld_ptr->table_entries + 1; nat_type = IPAHAL_NAT_IPV4; break; case IPA_NAT_EXPN_TBL: - entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries; + num_entries = mld_ptr->expn_table_entries; nat_type = IPAHAL_NAT_IPV4; break; case IPA_NAT_INDX_TBL: - entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1; + num_entries = mld_ptr->table_entries + 1; nat_type = IPAHAL_NAT_IPV4_INDEX; break; case IPA_NAT_INDEX_EXPN_TBL: - entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries; + num_entries = mld_ptr->expn_table_entries; nat_type = IPAHAL_NAT_IPV4_INDEX; break; case IPA_IPV6CT_BASE_TBL: - entries_num = ipa3_ctx->ipv6ct_mem.dev.table_entries + 1; + num_entries = ipa3_ctx->ipv6ct_mem.dev.table_entries + 1; nat_type = IPAHAL_NAT_IPV6CT; break; case IPA_IPV6CT_EXPN_TBL: - entries_num = ipa3_ctx->ipv6ct_mem.dev.expn_table_entries; + num_entries = ipa3_ctx->ipv6ct_mem.dev.expn_table_entries; nat_type = IPAHAL_NAT_IPV6CT; break; default: @@ -1285,15 +1766,18 @@ static uint32_t ipa3_nat_ipv6ct_calculate_table_size(uint8_t base_addr) } ipahal_nat_entry_size(nat_type, &entry_size); - return entry_size * entries_num; + + return entry_size * num_entries; } -static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param) +static int ipa3_table_validate_table_dma_one( + enum ipa3_nat_mem_in nmi, + struct ipa_ioc_nat_dma_one *param) { uint32_t table_size; if (param->table_index >= 1) { - IPAERR_RL("Unsupported table index %d\n", param->table_index); + IPAERR_RL("Unsupported table index %u\n", param->table_index); return -EPERM; } @@ -1307,6 +1791,7 @@ static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param) ipa3_ctx->nat_mem.dev.name); return -EPERM; } + IPADBG("nmi(%s)\n", ipa3_nat_mem_in_as_str(nmi)); break; case IPA_IPV6CT_BASE_TBL: case IPA_IPV6CT_EXPN_TBL: @@ -1327,10 +1812,13 @@ static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param) return -EPERM; } - table_size = ipa3_nat_ipv6ct_calculate_table_size(param->base_addr); + table_size = ipa3_nat_ipv6ct_calculate_table_size( + nmi, + param->base_addr); + if (!table_size) { IPAERR_RL("Failed to calculate table size for base_addr %d\n", - param->base_addr); + param->base_addr); return -EPERM; } @@ -1354,23 +1842,48 @@ static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param) * * Returns: 0 on success, negative on failure */ -int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) +int ipa3_table_dma_cmd( + struct ipa_ioc_nat_dma_cmd *dma) { - struct ipahal_imm_cmd_table_dma cmd; + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA; + + struct ipahal_imm_cmd_table_dma cmd; struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC]; struct ipa3_desc desc[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC]; + uint8_t cnt, num_cmd = 0; + int result = 0; int i; struct ipahal_reg_valmask valmask; struct ipahal_imm_cmd_register_write reg_write_coal_close; int max_dma_table_cmds = IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC; - IPADBG("\n"); + IPADBG("In\n"); - memset(desc, 0, sizeof(desc)); + if (!sram_compatible) + dma->mem_type = 0; + + if (!dev->is_dev_init) { + IPAERR_RL("NAT hasn't been initialized\n"); + result = -EPERM; + goto bail; + } + + if (!IPA_VALID_NAT_MEM_IN(dma->mem_type)) { + IPAERR_RL("Invalid ipa3_nat_mem_in type (%u)\n", + dma->mem_type); + result = -EPERM; + goto bail; + } + + IPADBG("nmi(%s)\n", ipa3_nat_mem_in_as_str(dma->mem_type)); + + memset(&cmd, 0, sizeof(cmd)); memset(cmd_pyld, 0, sizeof(cmd_pyld)); + memset(desc, 0, sizeof(desc)); /** * We use a descriptor for closing coalsceing endpoint @@ -1381,24 +1894,21 @@ int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) if (ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_COAL_CONS) != -1) max_dma_table_cmds -= 1; - if (!dma->entries || - dma->entries > (max_dma_table_cmds - 1)) { + if (!dma->entries || dma->entries > (max_dma_table_cmds - 1)) { IPAERR_RL("Invalid number of entries %d\n", dma->entries); result = -EPERM; goto bail; } - if (!ipa3_ctx->nat_mem.dev.is_dev_init) { - IPAERR_RL("NAT hasn't been initialized\n"); - return -EPERM; - } - for (cnt = 0; cnt < dma->entries; ++cnt) { - result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]); + + result = ipa3_table_validate_table_dma_one( + dma->mem_type, &dma->dma[cnt]); + if (result) { IPAERR_RL("Table DMA command parameter %d is invalid\n", - cnt); + cnt); goto bail; } } @@ -1425,46 +1935,61 @@ int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) ++num_cmd; } - /* NO-OP IC for ensuring that IPA pipeline is empty */ + /* + * NO-OP IC for ensuring that IPA pipeline is empty + */ cmd_pyld[num_cmd] = ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false); + if (!cmd_pyld[num_cmd]) { IPAERR("Failed to construct NOP imm cmd\n"); result = -ENOMEM; goto destroy_imm_cmd; } + ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]); + ++num_cmd; - /* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */ + /* + * NAT_DMA was renamed to TABLE_DMA starting from IPAv4 + */ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) cmd_name = IPA_IMM_CMD_TABLE_DMA; for (cnt = 0; cnt < dma->entries; ++cnt) { + cmd.table_index = dma->dma[cnt].table_index; - cmd.base_addr = dma->dma[cnt].base_addr; - cmd.offset = dma->dma[cnt].offset; - cmd.data = dma->dma[cnt].data; + cmd.base_addr = dma->dma[cnt].base_addr; + cmd.offset = dma->dma[cnt].offset; + cmd.data = dma->dma[cnt].data; + cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(cmd_name, &cmd, false); + if (!cmd_pyld[num_cmd]) { IPAERR_RL("Fail to construct table_dma imm cmd\n"); result = -ENOMEM; goto destroy_imm_cmd; } + ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]); + ++num_cmd; } + result = ipa3_send_cmd(num_cmd, desc); + if (result) IPAERR("Fail to send table_dma immediate command\n"); - IPADBG("return\n"); - destroy_imm_cmd: for (cnt = 0; cnt < num_cmd; ++cnt) ipahal_destroy_imm_cmd(cmd_pyld[cnt]); + bail: + IPADBG("Out\n"); + return result; } @@ -1481,38 +2006,100 @@ int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) return ipa3_table_dma_cmd(dma); } -static void ipa3_nat_ipv6ct_free_mem(struct ipa3_nat_ipv6ct_common_mem *dev) +static void ipa3_nat_ipv6ct_free_mem( + struct ipa3_nat_ipv6ct_common_mem *dev) { - IPADBG("\n"); - if (!dev->is_mem_allocated) { - IPADBG("attempt to delete %s before memory allocation\n", - dev->name); - /* Deletion of partly initialized table is not an error */ - goto clear; - } + struct ipa3_nat_mem *nm_ptr; + struct ipa3_nat_mem_loc_data *mld_ptr; - if (dev->is_sys_mem) { - IPADBG("freeing the dma memory for %s\n", dev->name); - dma_free_coherent( - ipa3_ctx->pdev, dev->size, - dev->vaddr, dev->dma_handle); - dev->size = 0; - dev->vaddr = NULL; - } + if (dev->is_ipv6ct_mem) { + + IPADBG("In: v6\n"); - dev->is_mem_allocated = false; + if (dev->vaddr) { + IPADBG("Freeing dma memory for %s\n", dev->name); -clear: - dev->table_entries = 0; - dev->expn_table_entries = 0; - dev->base_table_addr = NULL; - dev->expansion_table_addr = NULL; + dma_free_coherent( + ipa3_ctx->pdev, + dev->table_alloc_size, + dev->vaddr, + dev->dma_handle); + } - dev->is_hw_init = false; - dev->is_mapped = false; - dev->is_sys_mem = false; + dev->vaddr = NULL; + dev->dma_handle = 0; + dev->table_alloc_size = 0; + dev->base_table_addr = NULL; + dev->expansion_table_addr = NULL; + dev->table_entries = 0; + dev->expn_table_entries = 0; - IPADBG("return\n"); + dev->is_hw_init = false; + dev->is_mapped = false; + } else { + if (dev->is_nat_mem) { + + IPADBG("In: v4\n"); + + nm_ptr = (struct ipa3_nat_mem *) dev; + + if (nm_ptr->ddr_in_use) { + + nm_ptr->ddr_in_use = false; + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_DDR]; + + if (mld_ptr->vaddr) { + IPADBG("Freeing dma memory for %s\n", + dev->name); + + dma_free_coherent( + ipa3_ctx->pdev, + mld_ptr->table_alloc_size, + mld_ptr->vaddr, + mld_ptr->dma_handle); + } + + mld_ptr->vaddr = NULL; + mld_ptr->dma_handle = 0; + mld_ptr->table_alloc_size = 0; + mld_ptr->table_entries = 0; + mld_ptr->expn_table_entries = 0; + mld_ptr->base_table_addr = NULL; + mld_ptr->expansion_table_addr = NULL; + mld_ptr->index_table_addr = NULL; + mld_ptr->index_table_expansion_addr = NULL; + } + + if (nm_ptr->sram_in_use) { + + nm_ptr->sram_in_use = false; + + mld_ptr = &nm_ptr->mem_loc[IPA_NAT_MEM_IN_SRAM]; + + if (mld_ptr->io_vaddr) { + IPADBG("Unmappung sram memory for %s\n", + dev->name); + iounmap(mld_ptr->io_vaddr); + } + + mld_ptr->io_vaddr = NULL; + mld_ptr->vaddr = NULL; + mld_ptr->dma_handle = 0; + mld_ptr->table_alloc_size = 0; + mld_ptr->table_entries = 0; + mld_ptr->expn_table_entries = 0; + mld_ptr->base_table_addr = NULL; + mld_ptr->expansion_table_addr = NULL; + mld_ptr->index_table_addr = NULL; + mld_ptr->index_table_expansion_addr = NULL; + } + + memset(nm_ptr->mem_loc, 0, sizeof(nm_ptr->mem_loc)); + } + } + + IPADBG("Out\n"); } static int ipa3_nat_ipv6ct_create_del_table_cmd( @@ -1523,14 +2110,18 @@ static int ipa3_nat_ipv6ct_create_del_table_cmd( { bool mem_type_shared = true; - IPADBG("\n"); + IPADBG("In: tbl_index(%u) base_addr(%u) v4(%u) v6(%u)\n", + tbl_index, + base_addr, + dev->is_nat_mem, + dev->is_ipv6ct_mem); - if (tbl_index >= 1) { + if (!IPA_VALID_TBL_INDEX(tbl_index)) { IPAERR_RL("Unsupported table index %d\n", tbl_index); return -EPERM; } - if (dev->tmp_mem != NULL) { + if (dev->tmp_mem) { IPADBG("using temp memory during %s del\n", dev->name); mem_type_shared = false; base_addr = dev->tmp_mem->dma_handle; @@ -1543,44 +2134,56 @@ static int ipa3_nat_ipv6ct_create_del_table_cmd( table_init_cmd->expansion_table_addr_shared = mem_type_shared; table_init_cmd->size_base_table = 0; table_init_cmd->size_expansion_table = 0; - IPADBG("return\n"); + + IPADBG("Out\n"); return 0; } -static int ipa3_nat_send_del_table_cmd(uint8_t tbl_index) +static int ipa3_nat_send_del_table_cmd( + uint8_t tbl_index) { struct ipahal_imm_cmd_ip_v4_nat_init cmd; - int result; + int result = 0; - IPADBG("\n"); + IPADBG("In\n"); + + result = + ipa3_nat_ipv6ct_create_del_table_cmd( + tbl_index, + IPA_NAT_PHYS_MEM_OFFSET, + &ipa3_ctx->nat_mem.dev, + &cmd.table_init); - result = ipa3_nat_ipv6ct_create_del_table_cmd( - tbl_index, - IPA_NAT_PHYS_MEM_OFFSET, - &ipa3_ctx->nat_mem.dev, - &cmd.table_init); if (result) { IPAERR( "Fail to create immediate command to delete NAT table\n"); - return result; + goto bail; } - cmd.index_table_addr = cmd.table_init.base_table_addr; - cmd.index_table_addr_shared = cmd.table_init.base_table_addr_shared; - cmd.index_table_expansion_addr = cmd.index_table_addr; - cmd.index_table_expansion_addr_shared = cmd.index_table_addr_shared; + cmd.index_table_addr = + cmd.table_init.base_table_addr; + cmd.index_table_addr_shared = + cmd.table_init.base_table_addr_shared; + cmd.index_table_expansion_addr = + cmd.index_table_addr; + cmd.index_table_expansion_addr_shared = + cmd.index_table_addr_shared; cmd.public_addr_info = 0; - IPADBG("posting NAT delete command\n"); + IPADBG("Posting NAT delete command\n"); + result = ipa3_nat_send_init_cmd(&cmd, true); + if (result) { IPAERR("Fail to send NAT delete immediate command\n"); - return result; + goto bail; } - IPADBG("return\n"); - return 0; +bail: + IPADBG("Out\n"); + + return result; } static int ipa3_ipv6ct_send_del_table_cmd(uint8_t tbl_index) @@ -1637,46 +2240,89 @@ int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del) * * Returns: 0 on success, negative on failure */ -int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del) +int ipa3_del_nat_table( + struct ipa_ioc_nat_ipv6ct_table_del *del) { + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + struct ipa3_nat_mem *nm_ptr = (struct ipa3_nat_mem *) dev; + struct ipa3_nat_mem_loc_data *mld_ptr; + enum ipa3_nat_mem_in nmi; + int result = 0; - IPADBG("\n"); - if (!ipa3_ctx->nat_mem.dev.is_dev_init) { + IPADBG("In\n"); + + if (!sram_compatible) + del->mem_type = 0; + + nmi = del->mem_type; + + if (!dev->is_dev_init) { IPAERR("NAT hasn't been initialized\n"); - return -EPERM; + result = -EPERM; + goto bail; } - mutex_lock(&ipa3_ctx->nat_mem.dev.lock); + if (!IPA_VALID_TBL_INDEX(del->table_index)) { + IPAERR_RL("Unsupported table index %d\n", + del->table_index); + result = -EPERM; + goto bail; + } + + if (!IPA_VALID_NAT_MEM_IN(nmi)) { + IPAERR_RL("Bad ipa3_nat_mem_in type\n"); + result = -EPERM; + goto bail; + } + + IPADBG("nmi(%s)\n", ipa3_nat_mem_in_as_str(nmi)); + + mld_ptr = &nm_ptr->mem_loc[nmi]; + + mutex_lock(&dev->lock); + + if (dev->is_hw_init) { - if (ipa3_ctx->nat_mem.dev.is_hw_init) { result = ipa3_nat_send_del_table_cmd(del->table_index); + if (result) { IPAERR( "Fail to send immediate command to delete NAT table\n"); - goto bail; + goto unlock; } } - ipa3_ctx->nat_mem.public_ip_addr = 0; - ipa3_ctx->nat_mem.index_table_addr = 0; - ipa3_ctx->nat_mem.index_table_expansion_addr = 0; + nm_ptr->public_ip_addr = 0; + + mld_ptr->index_table_addr = NULL; + mld_ptr->index_table_expansion_addr = NULL; + + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 + && + nm_ptr->pdn_mem.base) { + + struct ipa_mem_buffer *pdn_mem_ptr = &nm_ptr->pdn_mem; + + IPADBG("Freeing the PDN memory\n"); + + dma_free_coherent( + ipa3_ctx->pdev, + pdn_mem_ptr->size, + pdn_mem_ptr->base, + pdn_mem_ptr->phys_base); - if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 && - ipa3_ctx->nat_mem.dev.is_mem_allocated) { - IPADBG("freeing the PDN memory\n"); - dma_free_coherent(ipa3_ctx->pdev, - ipa3_ctx->nat_mem.pdn_mem.size, - ipa3_ctx->nat_mem.pdn_mem.base, - ipa3_ctx->nat_mem.pdn_mem.phys_base); - ipa3_ctx->nat_mem.pdn_mem.base = NULL; + pdn_mem_ptr->base = NULL; } - ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->nat_mem.dev); - IPADBG("return\n"); + ipa3_nat_ipv6ct_free_mem(dev); + +unlock: + mutex_unlock(&dev->lock); bail: - mutex_unlock(&ipa3_ctx->nat_mem.dev.lock); + IPADBG("Out\n"); + return result; } @@ -1688,38 +2334,112 @@ int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del) * * Returns: 0 on success, negative on failure */ -int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del) +int ipa3_del_ipv6ct_table( + struct ipa_ioc_nat_ipv6ct_table_del *del) { + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->ipv6ct_mem.dev; + int result = 0; - IPADBG("\n"); + IPADBG("In\n"); - if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) { - IPAERR_RL("IPv6 connection tracking isn't supported\n"); - return -EPERM; - } + if (!sram_compatible) + del->mem_type = 0; - if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) { + if (!dev->is_dev_init) { IPAERR("IPv6 connection tracking hasn't been initialized\n"); - return -EPERM; + result = -EPERM; + goto bail; } - mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock); + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) { + IPAERR_RL("IPv6 connection tracking isn't supported\n"); + result = -EPERM; + goto bail; + } - if (ipa3_ctx->ipv6ct_mem.dev.is_hw_init) { + mutex_lock(&dev->lock); + + if (dev->is_hw_init) { result = ipa3_ipv6ct_send_del_table_cmd(del->table_index); + if (result) { - IPAERR( - "Fail to send immediate command to delete IPv6CT table\n"); - goto bail; + IPAERR("ipa3_ipv6ct_send_del_table_cmd() fail\n"); + goto unlock; } } ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->ipv6ct_mem.dev); - IPADBG("return\n"); + +unlock: + mutex_unlock(&dev->lock); bail: - mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock); + IPADBG("Out\n"); + return result; } +int ipa3_nat_get_sram_info( + struct ipa_nat_in_sram_info *info_ptr) +{ + struct ipa3_nat_ipv6ct_common_mem *dev = &ipa3_ctx->nat_mem.dev; + + int ret = 0; + + IPADBG("In\n"); + + if (!info_ptr) { + IPAERR("Bad argument passed\n"); + ret = -EINVAL; + goto bail; + } + + if (!dev->is_dev_init) { + IPAERR_RL("NAT hasn't been initialized\n"); + ret = -EPERM; + goto bail; + } + + sram_compatible = true; + + memset(info_ptr, + 0, + sizeof(struct ipa_nat_in_sram_info)); + + /* + * Size of SRAM set aside for the NAT table. + */ + info_ptr->sram_mem_available_for_nat = IPA_RAM_NAT_SIZE; + + /* + * If table's phys addr in SRAM is not page aligned, it will be + * offset into the mmap'd VM by the amount calculated below. This + * value can be used by the app, so that it can know where the + * table actually lives in the mmap'd VM... + */ + info_ptr->nat_table_offset_into_mmap = + (ipa3_ctx->ipa_wrapper_base + + ipa3_ctx->ctrl->ipa_reg_base_ofst + + ipahal_get_reg_n_ofst( + IPA_SW_AREA_RAM_DIRECT_ACCESS_n, + 0) + + IPA_RAM_NAT_OFST) & ~PAGE_MASK; + + /* + * If the offset above plus the size of the NAT table causes the + * table to extend beyond the next page boundary, the app needs to + * know it, so that it can increase the size used in the mmap + * request... + */ + info_ptr->best_nat_in_sram_size_rqst = + roundup( + info_ptr->nat_table_offset_into_mmap + + IPA_RAM_NAT_SIZE, + PAGE_SIZE); + +bail: + IPADBG("Out\n"); + + return ret; +} diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 6f2bfbab849ffdb57f8ac6d6a6fd965d2296fee5..0ef5ff4f09678ceff7914de1da1a6625935c46c6 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.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 @@ -590,10 +590,10 @@ static int ipa3_qmi_init_modem_send_sync_msg(void) } req.hw_stats_quota_base_addr_valid = true; req.hw_stats_quota_base_addr = - IPA_MEM_PART(stats_quota_ofst) + smem_restr_bytes; + IPA_MEM_PART(stats_quota_q6_ofst) + smem_restr_bytes; req.hw_stats_quota_size_valid = true; - req.hw_stats_quota_size = IPA_MEM_PART(stats_quota_size); + req.hw_stats_quota_size = IPA_MEM_PART(stats_quota_q6_size); req.hw_drop_stats_base_addr_valid = true; req.hw_drop_stats_base_addr = diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index 1edc56b79083b1c925f0d78819c1e844ba442b26..19ead0f676d81c22c26aa3a1000764b4bc91427b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.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 "ipa_i.h" @@ -312,8 +312,6 @@ static void ipa3_uc_save_dbg_stats(u32 size) } else goto unmap; break; - case IPA_HW_PROTOCOL_11ad: - break; case IPA_HW_PROTOCOL_WDI: if (!ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio) { ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_size = @@ -336,8 +334,6 @@ static void ipa3_uc_save_dbg_stats(u32 size) } else goto unmap; break; - case IPA_HW_PROTOCOL_ETH: - break; case IPA_HW_PROTOCOL_MHIP: if (!ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio) { ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_size = @@ -1248,8 +1244,6 @@ int ipa3_uc_debug_stats_dealloc(uint32_t prot_id) iounmap(ipa3_ctx->aqc_ctx.dbg_stats.uc_dbg_stats_mmio); ipa3_ctx->aqc_ctx.dbg_stats.uc_dbg_stats_mmio = NULL; break; - case IPA_HW_PROTOCOL_11ad: - break; case IPA_HW_PROTOCOL_WDI: iounmap(ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio); ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio = NULL; @@ -1258,8 +1252,6 @@ int ipa3_uc_debug_stats_dealloc(uint32_t prot_id) iounmap(ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_mmio); ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_mmio = NULL; break; - case IPA_HW_PROTOCOL_ETH: - break; default: IPAERR("unknown protocols %d\n", prot_id); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index d5918c4b75753d2594922e54e22761690a9fb98e..a7524eebd5f0e9bb7a68efa62efd0577ca07d8fa 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.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 @@ -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, @@ -2956,8 +2956,10 @@ static struct ipa3_mem_partition ipa_4_1_mem_part = { .uc_descriptor_ram_size = 0x400, .pdn_config_ofst = 0xbd8, .pdn_config_size = 0x50, - .stats_quota_ofst = 0xc30, - .stats_quota_size = 0x60, + .stats_quota_q6_ofst = 0xc30, + .stats_quota_q6_size = 0x60, + .stats_quota_ap_ofst = 0, + .stats_quota_ap_size = 0, .stats_tethering_ofst = 0xc90, .stats_tethering_size = 0x140, .stats_flt_v4_ofst = 0xdd0, @@ -3045,8 +3047,10 @@ static struct ipa3_mem_partition ipa_4_2_mem_part = { .uc_descriptor_ram_size = 0x0, .pdn_config_ofst = 0x9F8, .pdn_config_size = 0x50, - .stats_quota_ofst = 0xa50, - .stats_quota_size = 0x60, + .stats_quota_q6_ofst = 0xa50, + .stats_quota_q6_size = 0x60, + .stats_quota_ap_ofst = 0, + .stats_quota_ap_size = 0, .stats_tethering_ofst = 0xab0, .stats_tethering_size = 0x140, .stats_flt_v4_ofst = 0xbf0, @@ -3110,13 +3114,11 @@ static struct ipa3_mem_partition ipa_4_5_mem_part = { .apps_hdr_proc_ctx_size = 0x200, .apps_hdr_proc_ctx_size_ddr = 0x0, .nat_tbl_ofst = 0x1800, - .nat_tbl_size = 0x800, - .nat_index_tbl_ofst = 0x2000, - .nat_index_tbl_size = 0x100, - .nat_exp_tbl_ofst = 0x2100, - .nat_exp_tbl_size = 0x400, - .stats_quota_ofst = 0x2510, - .stats_quota_size = 0x78, + .nat_tbl_size = 0xd00, + .stats_quota_q6_ofst = 0x2510, + .stats_quota_q6_size = 0x30, + .stats_quota_ap_ofst = 0x2540, + .stats_quota_ap_size = 0x48, .stats_tethering_ofst = 0x2588, .stats_tethering_size = 0x238, .stats_flt_v4_ofst = 0, @@ -3207,15 +3209,13 @@ static struct ipa3_mem_partition ipa_4_7_mem_part = { .apps_hdr_proc_ctx_size = 0x200, .apps_hdr_proc_ctx_size_ddr = 0x0, .nat_tbl_ofst = 0x17A0, - .nat_tbl_size = 0x800, - .nat_index_tbl_ofst = 0x1FA0, - .nat_index_tbl_size = 0x100, - .nat_exp_tbl_ofst = 0x20A0, - .nat_exp_tbl_size = 0x400, + .nat_tbl_size = 0xd00, .pdn_config_ofst = 0x24A8, .pdn_config_size = 0x50, - .stats_quota_ofst = 0x2500, - .stats_quota_size = 0x78, + .stats_quota_q6_ofst = 0x2500, + .stats_quota_q6_size = 0x30, + .stats_quota_ap_ofst = 0x2530, + .stats_quota_ap_size = 0x48, .stats_tethering_ofst = 0x2578, .stats_tethering_size = 0x238, .stats_flt_v4_ofst = 0, @@ -5829,32 +5829,12 @@ int ipa3_init_mem_partition(enum ipa_hw_type type) } IPADBG("NAT TBL OFST 0x%x SIZE 0x%x\n", - IPA_MEM_PART(nat_tbl_ofst), - IPA_MEM_PART(nat_tbl_size)); + IPA_MEM_PART(nat_tbl_ofst), + IPA_MEM_PART(nat_tbl_size)); if (IPA_MEM_PART(nat_tbl_ofst) & 31) { - IPAERR("NAT TBL OFST 0x%x is unaligned\n", - IPA_MEM_PART(nat_tbl_ofst)); - return -ENODEV; - } - - IPADBG("NAT INDEX TBL OFST 0x%x SIZE 0x%x\n", - IPA_MEM_PART(nat_index_tbl_ofst), - IPA_MEM_PART(nat_index_tbl_size)); - - if (IPA_MEM_PART(nat_index_tbl_ofst) & 3) { - IPAERR("NAT INDEX TBL OFST 0x%x is unaligned\n", - IPA_MEM_PART(nat_index_tbl_ofst)); - return -ENODEV; - } - - IPADBG("NAT EXP TBL OFST 0x%x SIZE 0x%x\n", - IPA_MEM_PART(nat_exp_tbl_ofst), - IPA_MEM_PART(nat_exp_tbl_size)); - - if (IPA_MEM_PART(nat_exp_tbl_ofst) & 31) { - IPAERR("NAT EXP TBL OFST 0x%x is unaligned\n", - IPA_MEM_PART(nat_exp_tbl_ofst)); + IPAERR("NAT TBL OFST 0x%x is not aligned properly\n", + IPA_MEM_PART(nat_tbl_ofst)); return -ENODEV; } @@ -5868,13 +5848,23 @@ int ipa3_init_mem_partition(enum ipa_hw_type type) return -ENODEV; } - IPADBG("QUOTA STATS OFST 0x%x SIZE 0x%x\n", - IPA_MEM_PART(stats_quota_ofst), - IPA_MEM_PART(stats_quota_size)); + IPADBG("Q6 QUOTA STATS OFST 0x%x SIZE 0x%x\n", + IPA_MEM_PART(stats_quota_q6_ofst), + IPA_MEM_PART(stats_quota_q6_size)); - if (IPA_MEM_PART(stats_quota_ofst) & 7) { - IPAERR("QUOTA STATS OFST 0x%x is unaligned\n", - IPA_MEM_PART(stats_quota_ofst)); + if (IPA_MEM_PART(stats_quota_q6_ofst) & 7) { + IPAERR("Q6 QUOTA STATS OFST 0x%x is unaligned\n", + IPA_MEM_PART(stats_quota_q6_ofst)); + return -ENODEV; + } + + IPADBG("AP QUOTA STATS OFST 0x%x SIZE 0x%x\n", + IPA_MEM_PART(stats_quota_ap_ofst), + IPA_MEM_PART(stats_quota_ap_size)); + + if (IPA_MEM_PART(stats_quota_ap_ofst) & 7) { + IPAERR("AP QUOTA STATS OFST 0x%x is unaligned\n", + IPA_MEM_PART(stats_quota_ap_ofst)); return -ENODEV; } @@ -6346,11 +6336,20 @@ int ipa3_tag_process(struct ipa3_desc desc[], u32 retry_cnt = 0; struct ipahal_reg_valmask valmask; struct ipahal_imm_cmd_register_write reg_write_coal_close; + int req_num_tag_desc = REQUIRED_TAG_PROCESS_DESCRIPTORS; + + /** + * We use a descriptor for closing coalsceing endpoint + * by immediate command. So, REQUIRED_TAG_PROCESS_DESCRIPTORS + * should be incremented by 1 to overcome buffer overflow. + */ + if (ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_COAL_CONS) != -1) + req_num_tag_desc += 1; /* Not enough room for the required descriptors for the tag process */ - if (IPA_TAG_MAX_DESC - descs_num < REQUIRED_TAG_PROCESS_DESCRIPTORS) { + if (IPA_TAG_MAX_DESC - descs_num < req_num_tag_desc) { IPAERR("up to %d descriptors are allowed (received %d)\n", - IPA_TAG_MAX_DESC - REQUIRED_TAG_PROCESS_DESCRIPTORS, + IPA_TAG_MAX_DESC - req_num_tag_desc, descs_num); return -ENOMEM; } @@ -6399,8 +6398,8 @@ int ipa3_tag_process(struct ipa3_desc desc[], goto fail_free_tag_desc; } ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld); - desc[desc_idx].callback = ipa3_tag_destroy_imm; - desc[desc_idx].user1 = cmd_pyld; + tag_desc[desc_idx].callback = ipa3_tag_destroy_imm; + tag_desc[desc_idx].user1 = cmd_pyld; ++desc_idx; } @@ -6540,7 +6539,7 @@ int ipa3_tag_process(struct ipa3_desc desc[], * of the initial allocations above */ for (i = descs_num; - i < min(REQUIRED_TAG_PROCESS_DESCRIPTORS, desc_idx); i++) + i < min(req_num_tag_desc, desc_idx); i++) if (tag_desc[i].callback) tag_desc[i].callback(tag_desc[i].user1, tag_desc[i].user2); @@ -8694,3 +8693,48 @@ int ipa3_get_prot_id(enum ipa_client_type client) return prot_id; } +int ipa3_app_clk_vote( + enum ipa_app_clock_vote_type vote_type) +{ + const char *str_ptr = "APP_VOTE"; + int ret = 0; + + IPADBG("In\n"); + + mutex_lock(&ipa3_ctx->app_clock_vote.mutex); + + switch (vote_type) { + case IPA_APP_CLK_VOTE: + if ((ipa3_ctx->app_clock_vote.cnt + 1) <= IPA_APP_VOTE_MAX) { + ipa3_ctx->app_clock_vote.cnt++; + IPA_ACTIVE_CLIENTS_INC_SPECIAL(str_ptr); + } else { + IPAERR_RL("App vote count max hit\n"); + ret = -EPERM; + break; + } + break; + case IPA_APP_CLK_DEVOTE: + if (ipa3_ctx->app_clock_vote.cnt) { + ipa3_ctx->app_clock_vote.cnt--; + IPA_ACTIVE_CLIENTS_DEC_SPECIAL(str_ptr); + } + break; + case IPA_APP_CLK_RESET_VOTE: + while (ipa3_ctx->app_clock_vote.cnt > 0) { + IPA_ACTIVE_CLIENTS_DEC_SPECIAL(str_ptr); + ipa3_ctx->app_clock_vote.cnt--; + } + break; + default: + IPAERR_RL("Unknown vote_type(%u)\n", vote_type); + ret = -EPERM; + break; + } + + mutex_unlock(&ipa3_ctx->app_clock_vote.mutex); + + IPADBG("Out\n"); + + return ret; +} diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 6c58fb228a4c1d3eafd3058362dd09a1ae5c960d..06890ad77bc8c49cf3ad13ab867e3fcc5f39cff8 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/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c index e78a9827fbcc810cedcedc753462365f7b328730..bb1df7ecffdf5a2438721a2caac2a3e12fe06f9e 100644 --- a/drivers/platform/msm/qcom-geni-se.c +++ b/drivers/platform/msm/qcom-geni-se.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 @@ -1260,6 +1260,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. @@ -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); @@ -1540,7 +1565,8 @@ void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base, return; geni_se_dev = dev_get_drvdata(rsc->wrapper_dev); - if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw)) + if (unlikely(!geni_se_dev || !(geni_se_dev->bus_bw || + geni_se_dev->bus_bw_noc))) return; if (unlikely(list_empty(&rsc->ab_list) || list_empty(&rsc->ib_list))) { GENI_SE_DBG(ipc, false, NULL, "%s: Clocks not on\n", __func__); diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c index b2534053d979bdf1455e049d127dda9e20d73a72..4e49a85c6cefda5949e216cd6be7c4c68be3f469 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 @@ -53,7 +53,7 @@ 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", diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index b4224389febebe4688ea2195d8a3a786ba3c2081..d0ffdd5d9199bb48f8ff508a84c98b301894acfd 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); @@ -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 9371603a0ac90ca5f206e86f356f9e33b74814c2..b0f421fea2a58ed61d0063625c1855d61e575108 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; - - 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); + u32 val = enable ? ISPSSPM0_IUNIT_POWER_ON : + ISPSSPM0_IUNIT_POWER_OFF; - /* 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 a26f410800c2137ba856cecb25badc1398eb84f3..f40b1c1921064b734614705c72dc212700f979fc 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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 "); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 742a0c2179256c6339f5959123f4bd859f0a116b..69e28c12d59157499ab1bb42978c09fc767ba63e 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 6a61028cbb3c670f0cfca839f1250fc4d2caaef6..429166e6ec9e9b2d417724cea94d279d69512567 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -445,6 +445,13 @@ 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"), + }, + }, { /*sentinel*/ } }; diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 1360a7fa542c5dca0a79c4b611f2518e5af556f4..8760477d0e8af2db32ee1435a722c8accc6e0e96 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 0206cce328b3da69feb45917114debc5c849d577..d9493e893d64e2cef36a697a3d6c0fe36c052944 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 02356f9b5f22a4e89ef74160a8a54e88b117e733..8bb89c697c1ebfe2563e65f4dac2fbb1db97986f 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 3bae02380bb229579b6203aab4577797d41ce53f..e183a22de71538685d9e700041d079fd5ee4fda2 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 d19307f791c68957c4f2851462e00ad4017c138d..9e6472834e373010f34b3489895bfb88353b3885 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 cad7d1a8feec7d95ad0ec9399d6cfae68c741063..aa65e6c36c55eb7d3e03629032a817bfe147e804 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 fe0d9a712ee81603e179a1dc81aa4775e38b446e..1a188a9a0d59277d33f1c6bbdc554ea1f326dc99 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 eed473e1d5b764aef54013bd2e3d8949f09d662c..d2573b7c2bd39d817d92f160be4b1a13797d082d 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -77,4 +77,17 @@ config HL6111R 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 4132b7b0178e21969a9a4bac63a393ce90d0d8c8..0c8d8d39dffb89082d1936b7a436c6a2d174934e 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_QPNP_QNOVO5) += qpnp-qnovo5.o battery.o pmic-voter.o 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 53fdd16e92cccc6cc9bbb21da7841404e7c2d273..a87725bf2eb7337047c8293507473381b142f7d3 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__ @@ -99,6 +99,7 @@ struct pl_data { bool cp_disabled; int taper_entry_fv; int main_fcc_max; + enum power_supply_type charger_type; /* debugfs directory */ struct dentry *dfs_root; u32 float_voltage_uv; @@ -191,12 +192,10 @@ static int cp_get_parallel_mode(struct pl_data *chip, int mode) static int get_hvdcp3_icl_limit(struct pl_data *chip) { - int rc, main_icl, target_icl = -EINVAL; - union power_supply_propval pval = {0, }; + int main_icl, target_icl = -EINVAL; - rc = power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_REAL_TYPE, &pval); - if ((rc < 0) || (pval.intval != 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; /* @@ -270,6 +269,16 @@ static void cp_configure_ilim(struct pl_data *chip, const char *voter, int ilim) else vote(chip->cp_ilim_votable, voter, true, ilim); + /* + * Rerun FCC votable to ensure offset for ILIM compensation is + * recalculated based on new ILIM. + */ + if (!chip->fcc_main_votable) + chip->fcc_main_votable = find_votable("FCC_MAIN"); + if ((chip->charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) + && chip->fcc_main_votable) + rerun_election(chip->fcc_main_votable); + pl_dbg(chip, PR_PARALLEL, "ILIM: vote: %d voter:%s min_ilim=%d fcc = %d\n", ilim, voter, pval.intval, fcc); @@ -1827,6 +1836,12 @@ static void handle_usb_change(struct pl_data *chip) chip->total_fcc_ua = 0; chip->slave_fcc_ua = 0; chip->main_fcc_ua = 0; + chip->charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + } else { + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &pval); + if (!rc) + chip->charger_type = pval.intval; } } diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h index 0af5993dd1ef864d90114e51904655a3dc30bcc7..08b5a875cf4c3e8c671ca9eeb32bbf0515e97d88 100644 --- a/drivers/power/supply/qcom/qg-reg.h +++ b/drivers/power/supply/qcom/qg-reg.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 __QG_REG_H__ @@ -90,6 +90,7 @@ #define QG_POST_ESR_I_DATA0_REG 0x7E #define QG_S2_NORMAL_AVG_V_DATA0_REG 0x80 +#define QG_S2_NORMAL_AVG_I_DATA0_REG 0x82 #define QG_V_ACCUM_DATA0_RT_REG 0x88 #define QG_I_ACCUM_DATA0_RT_REG 0x8B diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c index f16f2b73862ce5b7bc16024a298422b76951b465..4071edcd29a30c52006463f5f720784ac4d7878a 100644 --- a/drivers/power/supply/qcom/qg-soc.c +++ b/drivers/power/supply/qcom/qg-soc.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) "QG-K: %s: " fmt, __func__ @@ -385,14 +385,7 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip) /* TCSS */ chip->sys_soc = qg_process_tcss_soc(chip, chip->sys_soc); - if (chip->sys_soc <= 50) { /* 0.5% */ - /* Hold SOC to 1% of VBAT has not dropped below cutoff */ - rc = qg_get_battery_voltage(chip, &vbat_uv); - if (!rc && vbat_uv >= (vcutoff_uv + VBAT_LOW_HYST_UV)) - soc = 1; - else - soc = 0; - } else if (chip->sys_soc == QG_MAX_SOC) { + if (chip->sys_soc == QG_MAX_SOC) { soc = FULL_SOC; } else if (chip->sys_soc >= (QG_MAX_SOC - 100)) { /* Hold SOC to 100% if we are dropping from 100 to 99 */ @@ -404,15 +397,25 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip) soc = DIV_ROUND_CLOSEST(chip->sys_soc, 100); } - qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n", - chip->sys_soc, soc); - /* FVSS */ soc = qg_process_fvss_soc(chip, soc); /* BASS */ soc = qg_process_bass_soc(chip, soc); + if (soc == 0) { + /* Hold SOC to 1% if we have not dropped below cutoff */ + rc = qg_get_vbat_avg(chip, &vbat_uv); + if (!rc && (vbat_uv >= (vcutoff_uv + VBAT_LOW_HYST_UV))) { + soc = 1; + qg_dbg(chip, QG_DEBUG_SOC, "vbat_uv=%duV holding SOC to 1%\n", + vbat_uv); + } + } + + qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n", + chip->sys_soc, soc); + chip->last_adj_ssoc = soc; return soc; diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c index 4a23ab101cf4dbc581d78ed6530a679ffdd91652..2a810d8a9ac1b537c5daa63be9ca2aa3c0cec7e4 100644 --- a/drivers/power/supply/qcom/qg-util.c +++ b/drivers/power/supply/qcom/qg-util.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 @@ -437,3 +437,21 @@ int qg_get_vbat_avg(struct qpnp_qg *chip, int *vbat_uv) return 0; } + +int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua) +{ + int rc = 0; + int last_ibat = 0; + + rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_AVG_I_DATA0_REG, + (u8 *)&last_ibat, 2); + if (rc < 0) { + pr_err("Failed to read S2_NORMAL_AVG_I reg, rc=%d\n", rc); + return rc; + } + + last_ibat = sign_extend32(last_ibat, 15); + *ibat_ua = qg_iraw_to_ua(chip, last_ibat); + + return 0; +} diff --git a/drivers/power/supply/qcom/qg-util.h b/drivers/power/supply/qcom/qg-util.h index 8f25213ac56a669755da69312bbdd4238f3f9c91..7a1892b0e627ccea5c1050258028e8c30b48185d 100644 --- a/drivers/power/supply/qcom/qg-util.h +++ b/drivers/power/supply/qcom/qg-util.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 __QG_UTIL_H__ @@ -25,5 +25,6 @@ int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua); int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv); int qg_get_vbat_avg(struct qpnp_qg *chip, int *vbat_uv); s64 qg_iraw_to_ua(struct qpnp_qg *chip, int iraw); +int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua); #endif diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index ed110380fe36d9b0cbe4063095939c755bb3e898..79045956bfc52618efbc03979f5b1e6f5e52063c 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.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) "QG-K: %s: " fmt, __func__ @@ -2202,6 +2202,9 @@ static int qg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_AVG: rc = qg_get_vbat_avg(chip, &pval->intval); break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + rc = qg_get_ibat_avg(chip, &pval->intval); + break; case POWER_SUPPLY_PROP_POWER_NOW: rc = qg_get_power(chip, &pval->intval, false); break; @@ -2271,6 +2274,7 @@ static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_CC_SOC, POWER_SUPPLY_PROP_FG_RESET, POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_POWER_NOW, POWER_SUPPLY_PROP_SCALE_MODE_EN, diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 827a31501278c46a9b77e1b5f5fefd7da7373e44..45912b3e23a4f4df627c19fc2f0e2e10020ad034 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 @@ -19,6 +19,7 @@ #include #include #include +#include #include "smb5-reg.h" #include "smb5-lib.h" #include "schgm-flash.h" @@ -820,6 +821,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 +966,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 +1061,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 +1084,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; @@ -1313,6 +1328,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 +1404,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: @@ -1757,6 +1774,14 @@ static int smb5_batt_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: val->intval = 0; + if (!chg->qnovo_disable_votable) + chg->qnovo_disable_votable = + find_votable("QNOVO_DISABLE"); + + if (chg->qnovo_disable_votable) + val->intval = + !get_effective_result( + chg->qnovo_disable_votable); break; case POWER_SUPPLY_PROP_CHARGE_FULL: rc = smblib_get_prop_from_bms(chg, @@ -3375,6 +3400,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; @@ -3531,6 +3587,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/smb1390-charger-psy.c b/drivers/power/supply/qcom/smb1390-charger-psy.c index 0977b65900a301e02d22496d221aaccb25996de6..5d359810617e81403471cd5842b7a73bbcd6cddd 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; @@ -1060,12 +1066,22 @@ static void smb1390_configure_ilim(struct smb1390 *chip, int mode) /* 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)) { + if (!chip->fcc_main_votable) + chip->fcc_main_votable = find_votable("FCC_MAIN"); + rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); - if (rc < 0) + if (rc < 0) { pr_err("Couldn't get usb aicl rc=%d\n", rc); - else + } else { vote(chip->ilim_votable, ICL_VOTER, true, pval.intval); + /* + * Rerun FCC votable to ensure offset for ILIM + * compensation is recalculated based on new ILIM. + */ + if (chip->fcc_main_votable) + rerun_election(chip->fcc_main_votable); + } } } @@ -1322,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) @@ -1330,65 +1385,69 @@ 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; + } + 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; @@ -1405,6 +1464,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; @@ -1425,7 +1486,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 0000000000000000000000000000000000000000..5b373a2b8b80da277e4bc277ca687d1bad28382a --- /dev/null +++ b/drivers/power/supply/qcom/smb1398-charger.c @@ -0,0 +1,2472 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 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; + + 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, "Couldn't get 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(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; + + switch (prop) { + case POWER_SUPPLY_PROP_CP_STATUS1: + rc = smb1398_div2_cp_get_status1(chip, &status); + if (!rc) + val->intval = status; + break; + case POWER_SUPPLY_PROP_CP_STATUS2: + rc = smb1398_div2_cp_get_status2(chip, &status); + if (!rc) + val->intval = status; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + rc = smb1398_get_enable_status(chip); + if (!rc) + 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) + 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) + val->intval = isns_ua; + break; + case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CP_DIE_TEMP: + if (!chip->in_suspend) { + rc = smb1398_get_die_temp(chip, &temp); + if ((rc >= 0) && (temp <= THERMAL_SUSPEND_DECIDEGC)) + chip->die_temp = temp; + } + + if (chip->die_temp != -ENODATA) + val->intval = chip->die_temp; + else + rc = -ENODATA; + 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; + } + 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); + 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; + + 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; + + 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 5c1be1aa6c8596ff39299a5872e9cd151badde4e..682c0fc318a01f8febc7d71a8513d6fc61a1fe82 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.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 @@ -881,18 +881,54 @@ static bool is_cp_available(struct smb_charger *chg) return !!chg->cp_psy; } +static bool is_cp_topo_vbatt(struct smb_charger *chg) +{ + int rc; + bool is_vbatt; + union power_supply_propval pval; + + if (!is_cp_available(chg)) + return false; + + rc = power_supply_get_property(chg->cp_psy, + POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE, &pval); + if (rc < 0) + return false; + + is_vbatt = (pval.intval == POWER_SUPPLY_PL_OUTPUT_VBAT); + + smblib_dbg(chg, PR_WLS, "%s\n", is_vbatt ? "true" : "false"); + + return is_vbatt; +} + #define CP_TO_MAIN_ICL_OFFSET_PC 10 int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua) { union power_supply_propval pval = {0, }; int rc; - if ((chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3) - || chg->hvdcp3_standalone_config || !is_cp_available(chg)) { - *offset_ua = 0; - return 0; + /* + * Apply ILIM offset to main charger's FCC if all of the following + * conditions are met: + * - HVDCP3 adapter with CP as parallel charger + * - 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_3P5)) + return -EINVAL; + + rc = power_supply_get_property(chg->cp_psy, POWER_SUPPLY_PROP_CP_ENABLE, + &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get CP ENABLE rc=%d\n", rc); + return rc; } + if (!pval.intval) + return -EINVAL; + rc = power_supply_get_property(chg->cp_psy, POWER_SUPPLY_PROP_CP_ILIM, &pval); if (rc < 0) { @@ -1033,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; @@ -1052,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 @@ -1062,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; } @@ -1208,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); @@ -1247,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) @@ -2498,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); @@ -2631,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; @@ -3243,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) @@ -3272,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; @@ -3290,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 */ @@ -3359,13 +3415,13 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, { union power_supply_propval pval = {0, }; int rc, ret = 0; - u8 reg; + u8 reg, adc_ch_reg; mutex_lock(&chg->adc_lock); if (chg->wa_flags & USBIN_ADC_WA) { /* Store ADC channel config in order to restore later */ - rc = smblib_read(chg, BATIF_ADC_CHANNEL_EN_REG, ®); + rc = smblib_read(chg, BATIF_ADC_CHANNEL_EN_REG, &adc_ch_reg); if (rc < 0) { smblib_err(chg, "Couldn't read ADC config rc=%d\n", rc); ret = rc; @@ -3422,7 +3478,7 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, restore_adc_config: /* Restore ADC channel config */ if (chg->wa_flags & USBIN_ADC_WA) { - rc = smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG, reg); + rc = smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG, adc_ch_reg); if (rc < 0) smblib_err(chg, "Couldn't write ADC config rc=%d\n", rc); @@ -3855,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) { @@ -3864,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; @@ -5444,6 +5503,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) @@ -5460,13 +5520,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", @@ -5818,6 +5894,219 @@ 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; + + if (!chg->typec_port) + return 0; + + if (chg->typec_partner && chg->pr_swap_in_progress) + return 0; + + 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); + return rc; + } + + 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); + } + + return rc; +} + +static void typec_partner_unregister(struct smb_charger *chg) +{ + 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; + } +} + +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; @@ -5833,6 +6122,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) @@ -5851,6 +6142,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); @@ -5967,7 +6259,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) @@ -6149,6 +6445,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: @@ -6171,6 +6471,15 @@ 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. + */ + + if (chg->typec_port && !chg->pr_swap_in_progress) + smblib_force_dr_mode(chg, TYPEC_PORT_DRP); + if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) schedule_delayed_work(&chg->lpd_detach_work, msecs_to_jiffies(1000)); @@ -6347,27 +6656,6 @@ irqreturn_t dcin_uv_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static bool is_cp_topo_vbatt(struct smb_charger *chg) -{ - int rc; - bool is_vbatt; - union power_supply_propval pval; - - if (!is_cp_available(chg)) - return false; - - rc = power_supply_get_property(chg->cp_psy, - POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE, &pval); - if (rc < 0) - return false; - - is_vbatt = (pval.intval == POWER_SUPPLY_PL_OUTPUT_VBAT); - - smblib_dbg(chg, PR_WLS, "%s\n", is_vbatt ? "true" : "false"); - - return is_vbatt; -} - irqreturn_t dc_plugin_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -7159,6 +7447,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, @@ -7168,6 +7468,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) { @@ -7230,6 +7532,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; @@ -7610,6 +7940,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, @@ -7655,6 +7989,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: @@ -7747,6 +8082,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); @@ -7761,6 +8097,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 dd2d7e57ddf63490c7aefa5d8f2d2980ce98c702..0ec38288004205b47b01b129b655203c8d2207ca 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 #include #include +#include #include #include #include #include #include +#include #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; @@ -435,6 +446,7 @@ struct smb_charger { struct votable *limited_irq_disable_votable; struct votable *hdc_irq_disable_votable; struct votable *temp_change_irq_disable_votable; + struct votable *qnovo_disable_votable; /* work */ struct work_struct bms_update_work; @@ -456,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; @@ -506,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; @@ -560,6 +576,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; @@ -776,6 +794,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); @@ -789,6 +808,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 b6a7d9f74cf30b0c1d1ab1a9f506206725690210..0e202d4273fb627f671af379f591bdabbcae297b 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/pwm/core.c b/drivers/pwm/core.c index e91dd2ba3d37769a6be93a589a6cf5cdd8bd0a93..4b6c0622cf346e923546c5744f11f3632cc227a9 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 d961a8207b1cbe660cfb6f38d21b7383f1834847..31b01035d0ab32e833226726369e2cd7d45aa2aa 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 7c8d6a168ceb254c69420ab293f08e3b43fcdd18..b91c477cc84be90fc29d200a3528fdffd20ae1aa 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 26ec24e457b12414cb9ca3a7cbe049f8a49f70e9..7e16b7def0dcb6ee6f32b4ab9b2a9689081c6cd7 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 4721a264bac2580cf8d21ee54396e0b494f1c9dc..1e69c1c9ec09635c9dffbb6b4d9cfd157f0c9108 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 a7eaf962a95b15725e43426edca3bd0f4d28acf6..567f5e2771c47288488063f49363fd3e0b4eaab7 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 062f2cfc45ec69545d3150c212d346597d76335e..3762432dd6a7fe7c245d6d4b843a942e92a6af4b 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 62f3005dcbe0fe839a21b460b516eb4050318da0..9d6e77d1679bc823035c2738e9ffb001fbf00fbd 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o 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/of_regulator.c b/drivers/regulator/of_regulator.c index 210fc20f7de7a9cd26dbbee68e24a7c0bc2dc94a..b255590aef36eeb79426553175074d8efede208e 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 bb5ab7d78895b817a9d42e03e6bd890d6b99e33e..c2cc392a27d4075ffc40dc123a78775d0e034a8f 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 31c3a236120a80909ede26f29caa5288011d8cbf..69a377ab260413db25d7d82263f63d37380dc645 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/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index cced1ffb896c1169dba81bde5c9b83a63890ceab..89b9314d64c9dbe5fe152b06c252b1567600857c 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 02ccdaa226a73f97a5fc812f5173c2bef44d25f4..5ebb6ee73f0770283eff5c15f5c71d0b56fe08bb 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 e230bef71be1c67abb92e44ca540545bdf3333ff..d200334577f68f79e0a0c990a8d9044b6409d85b 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 602af839421deeb5d93d9818984635b9ffe840eb..0d33e3079f0dc2abb0cab3bca2a8bf46a885d58f 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 47be411400e56aed1b48f44d4254e0178a640412..3a4c3d7cafca35f7752c9ddb402a643549be4050 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 225e34c56b94a2e315f17a598bd7a74d6dae1932..f7bf20493f23e9d5eedb9c3c6361a8f890272254 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 c996be2b553b3e6a14cc6c372044550ddda00229..514c5428d8de4cf0ded4dbf2787e67c6ca021e91 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 4938a2de35d6d1ec793f8611a9a2252310004310..949d113c39efcdbfae49e529dacede50f246d88a 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 3d577e259e91b94f6e1b5abbce60bf3746308803..ce051f91829f9f0aa4477e3f3d773821aabb3f95 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 bde53c8ccee2cbbcac52120dde4cc217e9c10f93..b74338d6dde60fe554b35838a03d9dea4fe7bd82 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 ea18a8f4bce063a91587d4424c8d615788af5dfd..033f65aef5788f225fd6dffd4fa65ebe8b0f403a 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 8a60900d6b8b53af51d348355c065590bc6518a1..4aff349ae301a476ab579b753fa388f83b4a45fd 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 08c661a332ec0e73b47e08522feb39ad1f8ff5ae..20e50d9fdf88230165a3029e49281afa5e065b51 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 385f8303bb412becfbe497ff24d2ebbe8ccfe09a..e9a25ec4d434f9c30c9b6a46c272547374d8a026 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 3fcd2cbafc84570d9c1738e1c60081ad944a124c..2e03021f15d136b97c73f461d63c210566c6765b 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 f85a1a93e669f9e14d4f68ab1a7040662eb60804..343bb6ed17839c91e6b003547dceb7b633ef14a0 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 29358a04592581c537d4d30fed775ecb81222175..9f8cbbd81ba10cfd0e432ff7dc3280d24b02796d 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 #include #include @@ -313,6 +307,7 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; unsigned int ctrl_reg; + u8 value[NUM_8_BIT_RTC_REGS] = {0}; spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); @@ -331,6 +326,16 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) goto rtc_rw_fail; } + /* Clear Alarm register */ + if (!enable) { + rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, + sizeof(value)); + if (rc) { + dev_err(dev, "Write to RTC ALARM register failed\n"); + goto rtc_rw_fail; + } + } + rtc_rw_fail: spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); return rc; @@ -444,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 */ @@ -452,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 29fc3d210392387ec27814500fcc215a3c838682..17ccef5d5db1a2138d80f66be8acfbda2a327ba9 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 77feb603cd4c0b363cf57abad36fb03b30f61cb7..3c64dbb08109acd6951d2b9fb4e6c85252e537e9 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 75c8c5033e0877bc313527491df9a2e9edf21d3c..58e03ac3578b7310fbc3badacc2fff2b55da861e 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 f1ff30ade5343b07ff54d05bcd82fddbc3bea0d6..9746c32eee2eb64c186e63a30f5f57ee66d1dc4c 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 08dbefc79520e57093cc3fefc9629e2f4200b95a..61c110b2045f836dbfffb54563e8d7057fae3cc1 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/char/Makefile b/drivers/s390/char/Makefile index c6ab34f94b1b54c96d704abf3f19e6aa16eaca78..3072b89785ddf7329165d6f2c8e678821f79e1a4 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -11,6 +11,7 @@ endif 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/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 3be54651698a33babdb3797d54a273128eeabd06..027a53eec42a537af5e39309a9ee3132cd658104 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 0aa4b3ccc948c10cbbd9ac904d6da353e341fd78..576ac08777c509ee038a50ec95a892fc79776355 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -14,6 +14,9 @@ #include #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/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 461afc276db72b47f783e3e9f682f7d8544bf5d9..81e2c591acb0ba3f416e5d624fae707897d3d344 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 c1c35eccd5b653500a591293a88a5825457bcc9d..95669d47c389eb67c39db0c450a564ece2bc2d83 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 9c5e801b3f6cb15cc070c1d439482e25d87d0980..52e0ae4dc7241225ff87ebc5e1292e1cd015a070 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 3b368fcf13f403c0b1c186b6e6a137e1a95352e6..946380f0d7199830751fe38b2cba98a5de295bfa 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 332701db7379dc16e7143deb608463be500c7ca4..f602b42b8343d3fcc44d199c8864a4c11cffc97d 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 df888506e363e475df608a4cc87fc7811c89d302..91aa4bfcf8d612041ce5ffad18681f02fc01c62f 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 7c097006c54db679c40ebd51ca5efcc8d8dc297d..a8ac480276323df176c45e98cd72ec4573c31cab 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -862,7 +862,7 @@ config SCSI_SNI_53C710 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 5160d6214a36b040aaf5f6b134899350baaea89d..8ec68dcc0cc4aa7d0af7199497382f6a4158cd1e 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -131,6 +131,7 @@ 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 +514,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 +886,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 +911,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) - * - * 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. +/** + * NCR5380_select - attempt arbitration and selection for a given command + * @instance: the Scsi_Host instance + * @cmd: the scsi_cmnd to execute * - * Inputs : instance - instantiation of the 5380 driver on which this - * target lives, cmd - SCSI command to execute. + * This routine establishes an I_T_L nexus for a SCSI command. This involves + * ARBITRATION, SELECTION and MESSAGE OUT phases and an IDENTIFY message. * - * 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 +928,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 +944,9 @@ 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; NCR5380_dprint(NDEBUG_ARBITRATION, instance); dsprintk(NDEBUG_ARBITRATION, instance, "starting arbitration, id = %d\n", @@ -950,7 +955,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 +991,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 +1040,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 +1123,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 +1161,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 +1177,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 +1192,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 +1717,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 +1800,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 +1954,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 +2017,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 +2035,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 +2047,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 +2112,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 +2286,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 +2305,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 +2322,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 +2339,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 +2355,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 8a6d002e67894011183d33de402ec3897acc7e69..5935fd6d1a0581178c6e4676d25c740415899cb0 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 12316ef4c89318536f88e6b84f7f92d7d887d8f2..c75d4695f9828d0379a2e3218c7473a73f3f8bbc 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/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h index 3d0c96a5c8735d608c0fd15b94b788f9f99758ce..c19c26e0e405e467eb8426427c8577cf44f2d806 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 e61ed8dad0b4f01c7ec374592bf6f46bfd834e8e..bd4ac187fd8e7830bcee23d1ee1e975582830aad 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 ed2dae657964be4997934c1efe191fb8b556363f..1793981337dd98763ca595ef62f597d1bf3ef929 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/dc395x.c b/drivers/scsi/dc395x.c index 1ed2cd82129d2d840d89ab91b58add55b718913d..3943347ec3c7ca02f1555d2ee371ede711588321 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 9c21938ed67ed4e0cadfa4c4a77c4f3c053358c0..c95c782b93a53b54e6fa5b292426f85cd64c4e30 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 6c7d2e201abed7ec29e96bea97852ad8f3812a23..d499c446616612592be462a96c1e53e61454bdc0 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 fd9d82c9033de6cc5021f1209617b3e0cbdb0a45..f35c56217694b5cbce73aafc83f52a73cff4497c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -716,7 +716,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 +886,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 +895,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 +905,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 +944,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 +960,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 +969,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 +1032,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 +1417,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 +1483,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 +1862,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 410eccf0bc5eb17f026236b08fc791e56513c96c..8aa3222fe48659356679e1c2ea36965dd2b84ade 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 1c4ea58da1ae1a60dd14069b0dd556c37326b9eb..ebc984ffe6a22cb49bf3c273826d77326470dfe8 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 3922b17e2ea392649491229032dc74f4150517ce..ce2f232b3df382089e524be179391235f3b40d91 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 bd6ac6b5980a1128af2b0d4234e51f9a97c52a5b..fe587ef1741d48f7a812ded989be7800a3a2e614 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 1ee3868ade079dfd06ef8f7415830a4384e4498b..7b5deae68d33b538b33314f4866f19bd6c9b06a4 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 b3539928073c628729af52c1301be111ae77b9dc..6bc3f022630a286012cc73fc4b223160e0461624 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 ed197bc8e801a604029ac12e93bf24d9bdce7e34..2f151708b59ae36086de84b0a3c12e63eeea60df 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 6dcaed0c1fc8cf32c5e6956ff858b95818caf6d1..fb6eba331ac6eb9f51496682da04f50a33bdd063 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 b025a0b7434174cea0096b23fad34aa18a22de20..23354f206533b92acd9e06f11aa777f4bf6cc5b4 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -800,7 +800,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 4ad61cfa69c0f7886bedae6f32942c662829c6f1..7a05c72717666344a6aae41f332e2130b6444bf0 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_expander.c b/drivers/scsi/libsas/sas_expander.c index b141d1061f38ea90d88407a9deb7bdc7888f934a..3e74fe92576179711929aac49b6cf5846e639dc9 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 43732e8d13473f84b88945072d7156d7a2982279..706aca3f7c253ad8815aaedca83006cef3a0c076 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 55cd96e2469c65d8ee0012b75d6ac42c068a9efa..fe084d47ed9e54b5ba405e8ed31d7e16b6450d9c 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 90745feca80800395f6a7af4905f60a8d1eebb60..99aea52e584b0fa089f9ae4c6bc5f9cb2bf91e1b 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; diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index d909d90035bb25f0409c39830fd54035a2cfd0a8..384f5cd7c3c81f3d010ceda13358db28bd778145 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 f3c6801c0b31281ec84e4600c7fb946b12391612..4f4d1b3b3bbc4b6aa1a5e1ad4b0dc0c275c972ce 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; } @@ -4264,14 +4288,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, default: return 1; } - /* Xmit ELS ACC response tag */ - 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 */ + 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 ccdd82b1123f7e1f3a80fa1f000c2e5709753e1d..b36b3da323a0a54ffe78371ac0a1f293f5b307ca 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); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 9acb5b44ce4c13a5753deb9c51143e0751d0be41..57510a831735bc4d30069fee23f2d33930c08657 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 deb094fdbb793ecc21b09989a8144ca0f6a0322b..e6bf5e8bc76700eb00531e53ba7136f9a5633b22 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 a6619fd8238c12270de9857522b353d1b5cc0a29..bd8dc6a2243c0bd8422669dfed2f56502f3ff8d2 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -844,9 +844,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 +2323,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 +2401,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 +2659,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 +2868,9 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* DSM in event on NPort in 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 645ffb5332b4ab970aac7bfe37faf13e65cb3811..f73726e55e44d257a8f4e41a92e8325b892af5bc 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); diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index e2575c8ec93e8fb4da8e5abcf71268a36aaee685..768eba8c111d9867ddcca2875529a8d46935d10d 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 0ec1082ce7ef62dd503ce8086cbc47a56a5b9ec5..3b170284a0e59b7376006a17140e712efe0a6d97 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 200b5bca1f5f441721d7633302fe651388548b84..425b83618a2e59af07f3a7da5b0320845b660ffd 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 @@ -3516,6 +3526,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 a490e63c94b671e6b9bb081639321ace40c18437..f459fd62e493c20b735b2651e73a7a07148b3490 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); - 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); + /* 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); /* 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); @@ -18435,15 +18425,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 399c0015c54655787c3e31b4f545fc909580f4d4..3dcc6615a23b20e7595156a32b9ce18d7f0913c1 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/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index bc37666f998e64e970ab8077baebb6c18c245c1b..2f31d266339f85317ad8426f9686cbff1c3eba98 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 d2ab52026014f99934989c3a0514f259fd16ceeb..2c556c7fcf0dc69e338b156389c690fdbce63929 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 d29a2dcc7d0eca93a0595e6d97297489c645f1ee..9b01c5a7aebd9df0a1e3e911449e3f688f333860 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_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 73d661a0ecbb9e06d03e6fce2964ce08b5e264fe..d3c944d997039849f2b90618a66a771f9c5cebc3 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 4dd6cad330e8e2abf142bf759956ddb5f88b7894..3e814c0469fbd2e4d2bde841d941f3c0fbe587e5 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 947d6017d004c83b3e758392d6278524613a8621..59feda261e08888c33a4893b944cfdaf52a3e23f 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 80b4dd6df0c251adfc12e67d8f7cf64299f73a7d..1816e351071fa4d1cd3a23ce068f99052ce4067a 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 42f0405601ad1bd0a495af2d99f1702b5763ba78..5021aed87f33ae1b35c576b733c4f555d31f4ecb 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, @@ -3752,6 +3762,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 +3809,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 889e69ce3689b18683ac142550dc6522cb97dcfd..7dd2699d0efb5da1b12483ec7af41788dd4ba78f 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/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 15d493f30810fc6556ec79a32d14e81e2d7cf0de..b008d583dd6e1f9bf700d03cf1b03da01061304f 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 4a9fd8d944d6079159296ff0eb0b6f6a5c9ef096..47f062e96e62c077fc80d73c6bf01cbe9a469130 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 34ff4bbc8de10e96d332037df0981ccc31fbecee..f621cb55ccfb27196605f849d8a56cb2a4df0b6c 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 bee9cfb29152983e965ab744b70bf79ea3389463..4512aaa16f7817f59f3920e45485c6155504f7a2 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) @@ -4874,19 +4866,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 +6498,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 +8723,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 119927220299e7ec9df947097d230f416fb48139..c699bbb8485bbc4398d054675fa1d091d84d6d9e 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -3297,19 +3297,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 88d8acf86a2a44d6f0db00d6efa8c9d00d3726da..afe15b3e45fbf676ebac3588e3460b047d10ae4c 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2837,6 +2837,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 +2848,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 +3121,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 +3182,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 +3202,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 +3254,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 +3311,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 +3328,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 +3418,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 84f57f075455ea38a62f728aba80ca0da8d9cdf5..b01f69dd4b28343dcabdde1a392cd9428c6e8457 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); @@ -6130,17 +6131,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 d620f4bebcd0d191ac76df53e2a376c20cc593f2..516fccdbcebd4209e99a2ce340e77d5d6f527e57 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 e6545cb9a2c199f903f949d07bce79ea997699ef..5590d6e8b57624404df0cb481ab134f2e3374a5d 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,7 +554,7 @@ 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); } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 60b6019a2fcaee23974a34997b8a1de2a3d27957..bb20a4a228cfebef595509be1140fcd6105a67a6 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; + + 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; + } - ql_dbg(ql_dbg_dpc, base_vha, 0x4007, - "ISP abort scheduled.\n"); - if (!(test_and_set_bit(ABORT_ISP_ACTIVE, + 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 d6dc320f81a7a0281ca61ccd9d7ecafb1064c50e..210ce294038df61856cd02c0752d2814c5b71ef4 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4703,6 +4703,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)) { @@ -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 b8c1a739dfbd1f5a4951b75330297abe8414662a..654e1af7f542c0ebcaa4d9937c97688d95d2b003 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 7550ba2831c36a890f26312ef1404170c3766f78..147cf6c903666b4aa3844a51014fe54e0ef8e1d2 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/scsi_lib.c b/drivers/scsi/scsi_lib.c index af349d1ac542a934602ced68605dd874bec2af2c..f86a6bea456a170ec32834203e8743a7a7b402ac 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/sni_53c710.c b/drivers/scsi/sni_53c710.c index 1f9a087daf69f6de5057570bf1ea9eb4726d0252..3102a75984d3b02ce39d6d59d376ccbde87b9c94 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/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index bd3f6e2d68344a008ca7dbca2232409f0b668b1d..0a2a54517b151d8d1451ba85b5d7b17366d4e6d2 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/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 4a0cef37ce3b6da96a5532243a499ea2ccf98f1e..b6690a068768770480f3bb7b705a6baf1fbd3dc9 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -311,6 +311,10 @@ UFS_DEVICE_DESC_PARAM(device_version, _DEV_VER, 2); UFS_DEVICE_DESC_PARAM(number_of_secure_wpa, _NUM_SEC_WPA, 1); UFS_DEVICE_DESC_PARAM(psa_max_data_size, _PSA_MAX_DATA, 4); UFS_DEVICE_DESC_PARAM(psa_state_timeout, _PSA_TMT, 1); +UFS_DEVICE_DESC_PARAM(ext_feature_sup, _EXT_UFS_FEATURE_SUP, 4); +UFS_DEVICE_DESC_PARAM(wb_presv_us_en, _WB_US_RED_EN, 1); +UFS_DEVICE_DESC_PARAM(wb_type, _WB_TYPE, 1); +UFS_DEVICE_DESC_PARAM(wb_shared_alloc_units, _WB_SHARED_ALLOC_UNITS, 4); static struct attribute *ufs_sysfs_device_descriptor[] = { &dev_attr_device_type.attr, @@ -339,6 +343,10 @@ static struct attribute *ufs_sysfs_device_descriptor[] = { &dev_attr_number_of_secure_wpa.attr, &dev_attr_psa_max_data_size.attr, &dev_attr_psa_state_timeout.attr, + &dev_attr_ext_feature_sup.attr, + &dev_attr_wb_presv_us_en.attr, + &dev_attr_wb_type.attr, + &dev_attr_wb_shared_alloc_units.attr, NULL, }; @@ -408,6 +416,12 @@ UFS_GEOMETRY_DESC_PARAM(enh4_memory_max_alloc_units, _ENM4_MAX_NUM_UNITS, 4); UFS_GEOMETRY_DESC_PARAM(enh4_memory_capacity_adjustment_factor, _ENM4_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(wb_max_alloc_units, _WB_MAX_ALLOC_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(wb_max_wb_luns, _WB_MAX_WB_LUNS, 1); +UFS_GEOMETRY_DESC_PARAM(wb_buff_cap_adj, _WB_BUFF_CAP_ADJ, 1); +UFS_GEOMETRY_DESC_PARAM(wb_sup_red_type, _WB_SUP_RED_TYPE, 1); +UFS_GEOMETRY_DESC_PARAM(wb_sup_wb_type, _WB_SUP_WB_TYPE, 1); + static struct attribute *ufs_sysfs_geometry_descriptor[] = { &dev_attr_raw_device_capacity.attr, @@ -439,6 +453,11 @@ static struct attribute *ufs_sysfs_geometry_descriptor[] = { &dev_attr_enh3_memory_capacity_adjustment_factor.attr, &dev_attr_enh4_memory_max_alloc_units.attr, &dev_attr_enh4_memory_capacity_adjustment_factor.attr, + &dev_attr_wb_max_alloc_units.attr, + &dev_attr_wb_max_wb_luns.attr, + &dev_attr_wb_buff_cap_adj.attr, + &dev_attr_wb_sup_red_type.attr, + &dev_attr_wb_sup_wb_type.attr, NULL, }; @@ -651,7 +670,7 @@ static ssize_t _name##_show(struct device *dev, \ pm_runtime_put_sync(hba->dev); \ if (ret) \ return -EINVAL; \ - return sprintf(buf, "%s\n", flag ? "true" : "false"); \ + return snprintf(buf, PAGE_SIZE, "%s\n", flag ? "true" : "false"); \ } \ static DEVICE_ATTR_RO(_name) @@ -663,6 +682,9 @@ UFS_FLAG(life_span_mode_enable, _LIFE_SPAN_MODE_ENABLE); UFS_FLAG(phy_resource_removal, _FPHYRESOURCEREMOVAL); UFS_FLAG(busy_rtc, _BUSY_RTC); UFS_FLAG(disable_fw_update, _PERMANENTLY_DISABLE_FW_UPDATE); +UFS_FLAG(wb_enable, _WB_EN); +UFS_FLAG(wb_flush_en, _WB_BUFF_FLUSH_EN); +UFS_FLAG(wb_flush_during_h8, _WB_BUFF_FLUSH_DURING_HIBERN8); static struct attribute *ufs_sysfs_device_flags[] = { &dev_attr_device_init.attr, @@ -673,6 +695,9 @@ static struct attribute *ufs_sysfs_device_flags[] = { &dev_attr_phy_resource_removal.attr, &dev_attr_busy_rtc.attr, &dev_attr_disable_fw_update.attr, + &dev_attr_wb_enable.attr, + &dev_attr_wb_flush_en.attr, + &dev_attr_wb_flush_during_h8.attr, NULL, }; @@ -694,7 +719,7 @@ static ssize_t _name##_show(struct device *dev, \ pm_runtime_put_sync(hba->dev); \ if (ret) \ return -EINVAL; \ - return sprintf(buf, "0x%08X\n", value); \ + return snprintf(buf, PAGE_SIZE, "0x%08X\n", value); \ } \ static DEVICE_ATTR_RO(_name) @@ -714,6 +739,11 @@ UFS_ATTRIBUTE(exception_event_status, _EE_STATUS); UFS_ATTRIBUTE(ffu_status, _FFU_STATUS); UFS_ATTRIBUTE(psa_state, _PSA_STATE); UFS_ATTRIBUTE(psa_data_size, _PSA_DATA_SIZE); +UFS_ATTRIBUTE(wb_flush_status, _WB_FLUSH_STATUS); +UFS_ATTRIBUTE(wb_avail_buf, _AVAIL_WB_BUFF_SIZE); +UFS_ATTRIBUTE(wb_life_time_est, _WB_BUFF_LIFE_TIME_EST); +UFS_ATTRIBUTE(wb_cur_buf, _CURR_WB_BUFF_SIZE); + static struct attribute *ufs_sysfs_attributes[] = { &dev_attr_boot_lun_enabled.attr, @@ -732,6 +762,10 @@ static struct attribute *ufs_sysfs_attributes[] = { &dev_attr_ffu_status.attr, &dev_attr_psa_state.attr, &dev_attr_psa_data_size.attr, + &dev_attr_wb_flush_status.attr, + &dev_attr_wb_avail_buf.attr, + &dev_attr_wb_life_time_est.attr, + &dev_attr_wb_cur_buf.attr, NULL, }; @@ -783,6 +817,8 @@ UFS_UNIT_DESC_PARAM(provisioning_type, _PROVISIONING_TYPE, 1); UFS_UNIT_DESC_PARAM(physical_memory_resourse_count, _PHY_MEM_RSRC_CNT, 8); UFS_UNIT_DESC_PARAM(context_capabilities, _CTX_CAPABILITIES, 2); UFS_UNIT_DESC_PARAM(large_unit_granularity, _LARGE_UNIT_SIZE_M1, 1); +UFS_UNIT_DESC_PARAM(wb_buf_alloc_units, _WB_BUF_ALLOC_UNITS, 4); + static struct attribute *ufs_sysfs_unit_descriptor[] = { &dev_attr_boot_lun_id.attr, @@ -798,6 +834,7 @@ static struct attribute *ufs_sysfs_unit_descriptor[] = { &dev_attr_physical_memory_resourse_count.attr, &dev_attr_context_capabilities.attr, &dev_attr_large_unit_granularity.attr, + &dev_attr_wb_buf_alloc_units.attr, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 371f160fa99d2c5ccbf643ac421bbb5cb69316db..cededde98be3e7993f59558760bbde4433185cdd 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -165,6 +165,7 @@ enum unit_desc_param { UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18, UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20, UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, + UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29, }; /* Device descriptor parameters offsets in bytes*/ @@ -205,6 +206,9 @@ enum device_desc_param { DEVICE_DESC_PARAM_PSA_TMT = 0x29, DEVICE_DESC_PARAM_PRDCT_REV = 0x2A, DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F, + DEVICE_DESC_PARAM_WB_US_RED_EN = 0x53, + DEVICE_DESC_PARAM_WB_TYPE = 0x54, + DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55, }; /* Interconnect descriptor parameters offsets in bytes*/ @@ -249,6 +253,11 @@ enum geometry_desc_param { GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E, GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42, GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44, + GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F, + GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53, + GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54, + GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55, + GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56, }; /* Health descriptor parameters offsets in bytes*/ @@ -600,6 +609,7 @@ struct ufs_dev_info { u8 i_product_name; u16 w_spec_version; u32 d_ext_ufs_feature_sup; + u8 b_wb_buffer_type; /* query flags */ bool f_power_on_wp_en; @@ -613,6 +623,8 @@ struct ufs_dev_info { unsigned int quirks; bool keep_vcc_on; + + bool wb_config_lun; }; #define MAX_MODEL_LEN 16 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 14c335589ea07d7eff92be717abd65797a0c420e..3e86261b932d6b890ee59dc39419204250e97914 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 @@ -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; @@ -6058,6 +6086,37 @@ static int ufshcd_get_lu_wp(struct ufs_hba *hba, return ret; } +/* + * ufshcd_get_wb_alloc_units - returns "dLUNumWriteBoosterBufferAllocUnits" + * @hba: per-adapter instance + * @lun: UFS device lun id + * @d_lun_wbb_au: pointer to buffer to hold the LU's alloc units info + * + * Returns 0 in case of success and d_lun_wbb_au would be returned + * Returns -ENOTSUPP if reading d_lun_wbb_au is not supported. + * Returns -EINVAL in case of invalid parameters passed to this function. + */ +static int ufshcd_get_wb_alloc_units(struct ufs_hba *hba, + u8 lun, + u8 *d_lun_wbb_au) +{ + int ret; + + if (!d_lun_wbb_au) + ret = -EINVAL; + + /* WB can be supported only from LU0..LU7 */ + else if (lun >= UFS_UPIU_MAX_GENERAL_LUN) + ret = -ENOTSUPP; + else + ret = ufshcd_read_unit_desc_param(hba, + lun, + UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS, + d_lun_wbb_au, + sizeof(*d_lun_wbb_au)); + return ret; +} + /** * ufshcd_get_lu_power_on_wp_status - get LU's power on write protect * status @@ -6847,8 +6906,10 @@ static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba) static bool ufshcd_wb_sup(struct ufs_hba *hba) { - return !!(hba->dev_info.d_ext_ufs_feature_sup & - UFS_DEV_WRITE_BOOSTER_SUP); + return ((hba->dev_info.d_ext_ufs_feature_sup & + UFS_DEV_WRITE_BOOSTER_SUP) && + (hba->dev_info.b_wb_buffer_type + || hba->dev_info.wb_config_lun)); } static int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable) @@ -8435,7 +8496,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba, int err; size_t buff_len; u8 model_index; - u8 *desc_buf; + u8 *desc_buf, wb_buf[4]; + u32 lun, res; buff_len = max_t(size_t, hba->desc_size.dev_desc, QUERY_DESC_MAX_SIZE + 1); @@ -8467,7 +8529,7 @@ static int ufs_get_device_desc(struct ufs_hba *hba, if ((dev_desc->wspecversion >= 0x310) || (dev_desc->wmanufacturerid == UFS_VENDOR_TOSHIBA && dev_desc->wspecversion >= 0x300 && - hba->desc_size.dev_desc >= 0x59)) + hba->desc_size.dev_desc >= 0x59)) { hba->dev_info.d_ext_ufs_feature_sup = desc_buf[DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP] << 24 | @@ -8476,7 +8538,29 @@ static int ufs_get_device_desc(struct ufs_hba *hba, desc_buf[DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP + 2] << 8 | desc_buf[DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP + 3]; + hba->dev_info.b_wb_buffer_type = + desc_buf[DEVICE_DESC_PARAM_WB_TYPE]; + + if (hba->dev_info.b_wb_buffer_type) + goto skip_unit_desc; + hba->dev_info.wb_config_lun = false; + for (lun = 0; lun < UFS_UPIU_MAX_GENERAL_LUN; lun++) { + memset(wb_buf, 0, sizeof(wb_buf)); + err = ufshcd_get_wb_alloc_units(hba, lun, wb_buf); + if (err) + break; + + res = wb_buf[0] << 24 | wb_buf[1] << 16 | + wb_buf[2] << 8 | wb_buf[3]; + if (res) { + hba->dev_info.wb_config_lun = true; + break; + } + } + } + +skip_unit_desc: /* Zero-pad entire buffer for string termination. */ memset(desc_buf, 0, buff_len); @@ -9048,6 +9132,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 @@ -9086,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); @@ -9102,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)), @@ -9937,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); @@ -10761,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)) { @@ -10956,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 @@ -11007,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 @@ -11062,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 bb70882e6b56e0bd4f1d37e367e205bbb16f19ac..6a5b547eae5902502590994af92d1db1d646c619 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 86522302eb813facfe7066a722dfc3103e942724..72efa3996c501bce16bf67c5dceb31eb1dca9796 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 @@ -170,12 +170,7 @@ static int ngd_slim_qmi_new_server(struct qmi_handle *hdl, 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; } @@ -185,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; } @@ -273,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:%lu\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) { @@ -1560,6 +1571,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; @@ -1597,17 +1609,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; } @@ -1622,8 +1653,10 @@ static int ngd_notify_slaves(void *data) ret = ngd_slim_qmi_svc_event_init(&dev->qmi); if (ret) { pr_err("Slimbus QMI service registration failed:%d\n", ret); + pm_relax(dev->dev); return ret; } + ngd_dom_init(dev); while (!kthread_should_stop()) { wait_for_completion_interruptible(&dev->qmi.slave_notify); @@ -1639,7 +1672,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); } @@ -1661,7 +1693,11 @@ 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; } @@ -1685,8 +1721,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); } @@ -1956,6 +1999,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); @@ -2118,6 +2162,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) { @@ -2177,6 +2232,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); @@ -2220,6 +2281,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) { @@ -2235,6 +2297,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 5fed0aad500c9bd606e1de9fd3dde83542a8b143..eabcecfef110cf2646b683e53f08e6409ac48659 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 113e884697fd8c3478d9b7900a79360fb3fad2f5..f0d46b16e08c4f518e7a9be90a948fab7c129b0f 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_ARCH_GEMINI) += gemini/ 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 a5577dd5eb0870bccff391f38c69f47c8d708fde..8ee06347447c0dfa3dede7d3164ba584631e7866 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 2f71f7df3465a0a65301cb031ab183c4b0ddee7c..f9edd28894fda4163bb69b992aa5765205acf384 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 b3da635970ea70f56d98a4db94d234178cfa9a82..d160fc2a7b7a20a39d9710c2f5ac49a2737b1dda 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 b5f7d7f6c1636efe8c0fa1370e48f04ac1f6641e..ab38c2016c0718406877f47c67a9acaa3dcb5dcb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -133,6 +133,15 @@ config QCOM_LITO_LLCC 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 @@ -747,6 +756,14 @@ config MSM_SUSPEND_STATS_FIRST_BUCKET endif # MSM_IDLE_STATS endif # MSM_PM +config QTI_L2_REUSE + bool "Qualcomm Technologies Inc (QTI) L2 reuse" + depends on ARCH_QCOM + help + This module allows to configure the L2 reuse feature dynamically + to let the power collapsed cluster's L2 cache usage by the active + cluster cpu. Use sysfs interface to control enabling this feature. + config QTI_RPM_STATS_LOG bool "Qualcomm Technologies RPM Stats Driver" depends on QCOM_RPMH || MSM_RPM_SMD @@ -838,6 +855,8 @@ config QCOM_HYP_CORE_CTL 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 19d915ec88e05210c97c4313017e17ec6359325c..530043d857f7fbd3741425de72f41fb8204f9b53 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o 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 @@ -97,3 +98,5 @@ obj-$(CONFIG_ICNSS) += icnss.o obj-$(CONFIG_ICNSS_QMI) += icnss_qmi.o wlan_firmware_service_v01.o 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 57af8a5373325f2895f08022629a723fb68a3d01..ee9197f5aae96e62c1cd79af0f546cb5623621b5 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 53376fbf6e62468127c752794f09b71bd7d3bd1e..1057e5596f107384fb5bbf0816913cca88480026 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 @@ -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/dfc_defs.h b/drivers/soc/qcom/dfc_defs.h index 7553707d3ab63c3291ae3021899e14e413f0c3ff..2a30a29f903b7c64b11512658737371469d69761 100644 --- a/drivers/soc/qcom/dfc_defs.h +++ b/drivers/soc/qcom/dfc_defs.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 _DFC_DEFS_H @@ -53,6 +53,8 @@ struct dfc_flow_status_info_type_v01 { u16 seq_num; u8 qos_ids_len; struct dfc_qos_id_type_v01 qos_ids[DFC_MAX_QOS_ID_V01]; + u8 rx_bytes_valid; + u32 rx_bytes; }; struct dfc_ancillary_info_type_v01 { @@ -88,7 +90,8 @@ struct dfc_tx_link_status_ind_msg_v01 { }; void dfc_do_burst_flow_control(struct dfc_qmi_data *dfc, - struct dfc_flow_status_ind_msg_v01 *ind); + struct dfc_flow_status_ind_msg_v01 *ind, + bool is_query); void dfc_handle_tx_link_status_ind(struct dfc_qmi_data *dfc, struct dfc_tx_link_status_ind_msg_v01 *ind); diff --git a/drivers/soc/qcom/dfc_qmap.c b/drivers/soc/qcom/dfc_qmap.c index 5e26739a50e2cdb0e2dac3e3a93e93ffbdad68b8..84430aaaa08fe34592bad29eeff1bb9b199260b3 100644 --- a/drivers/soc/qcom/dfc_qmap.c +++ b/drivers/soc/qcom/dfc_qmap.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 @@ -65,13 +65,14 @@ struct qmap_dfc_ind { u8 reserved2; u8 tx_info_valid:1; u8 tx_info:1; - u8 reserved3:6; + u8 rx_bytes_valid:1; + u8 reserved3:5; u8 bearer_id; u8 tcp_bidir:1; u8 bearer_status:3; u8 reserved4:4; __be32 grant; - u32 reserved5; + __be32 rx_bytes; u32 reserved6; } __aligned(1); @@ -89,11 +90,12 @@ struct qmap_dfc_query_resp { u8 cmd_ver; u8 bearer_id; u8 tcp_bidir:1; - u8 reserved:7; + u8 rx_bytes_valid:1; + u8 reserved:6; u8 invalid:1; u8 reserved2:7; __be32 grant; - u32 reserved3; + __be32 rx_bytes; u32 reserved4; } __aligned(1); @@ -185,6 +187,11 @@ static int dfc_qmap_handle_ind(struct dfc_qmi_data *dfc, qmap_flow_ind.flow_status[0].num_bytes = ntohl(cmd->grant); qmap_flow_ind.flow_status[0].seq_num = ntohs(cmd->seq_num); + if (cmd->rx_bytes_valid) { + qmap_flow_ind.flow_status[0].rx_bytes_valid = 1; + qmap_flow_ind.flow_status[0].rx_bytes = ntohl(cmd->rx_bytes); + } + if (cmd->tcp_bidir) { qmap_flow_ind.ancillary_info_valid = 1; qmap_flow_ind.ancillary_info_len = 1; @@ -193,7 +200,7 @@ static int dfc_qmap_handle_ind(struct dfc_qmi_data *dfc, qmap_flow_ind.ancillary_info[0].reserved = DFC_MASK_TCP_BIDIR; } - dfc_do_burst_flow_control(dfc, &qmap_flow_ind); + dfc_do_burst_flow_control(dfc, &qmap_flow_ind, false); done: return QMAP_CMD_ACK; @@ -221,6 +228,11 @@ static int dfc_qmap_handle_query_resp(struct dfc_qmi_data *dfc, qmap_flow_ind.flow_status[0].num_bytes = ntohl(cmd->grant); qmap_flow_ind.flow_status[0].seq_num = 0xFFFF; + if (cmd->rx_bytes_valid) { + qmap_flow_ind.flow_status[0].rx_bytes_valid = 1; + qmap_flow_ind.flow_status[0].rx_bytes = ntohl(cmd->rx_bytes); + } + if (cmd->tcp_bidir) { qmap_flow_ind.ancillary_info_valid = 1; qmap_flow_ind.ancillary_info_len = 1; @@ -229,7 +241,7 @@ static int dfc_qmap_handle_query_resp(struct dfc_qmi_data *dfc, qmap_flow_ind.ancillary_info[0].reserved = DFC_MASK_TCP_BIDIR; } - dfc_do_burst_flow_control(dfc, &qmap_flow_ind); + dfc_do_burst_flow_control(dfc, &qmap_flow_ind, true); return QMAP_CMD_DONE; } diff --git a/drivers/soc/qcom/dfc_qmi.c b/drivers/soc/qcom/dfc_qmi.c index 1054532ce967c20474941a88605981ea27dbc15a..f569c5aae06c898e6d243e64522587cbf8028d4c 100644 --- a/drivers/soc/qcom/dfc_qmi.c +++ b/drivers/soc/qcom/dfc_qmi.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 @@ -945,6 +945,7 @@ static int dfc_all_bearer_flow_ctl(struct net_device *dev, bearer->tcp_bidir = DFC_IS_TCP_BIDIR(ancillary); bearer->last_grant = fc_info->num_bytes; bearer->last_seq = fc_info->seq_num; + bearer->last_adjusted_grant = fc_info->num_bytes; dfc_bearer_flow_ctl(dev, bearer, qos); } @@ -952,13 +953,40 @@ static int dfc_all_bearer_flow_ctl(struct net_device *dev, return 0; } +static u32 dfc_adjust_grant(struct rmnet_bearer_map *bearer, + struct dfc_flow_status_info_type_v01 *fc_info) +{ + u32 grant; + + if (!fc_info->rx_bytes_valid) + return fc_info->num_bytes; + + if (bearer->bytes_in_flight > fc_info->rx_bytes) + bearer->bytes_in_flight -= fc_info->rx_bytes; + else + bearer->bytes_in_flight = 0; + + /* Adjusted grant = grant - bytes_in_flight */ + if (fc_info->num_bytes > bearer->bytes_in_flight) + grant = fc_info->num_bytes - bearer->bytes_in_flight; + else + grant = 0; + + trace_dfc_adjust_grant(fc_info->mux_id, fc_info->bearer_id, + fc_info->num_bytes, fc_info->rx_bytes, + bearer->bytes_in_flight, grant); + return grant; +} + static int dfc_update_fc_map(struct net_device *dev, struct qos_info *qos, u8 ack_req, u32 ancillary, - struct dfc_flow_status_info_type_v01 *fc_info) + struct dfc_flow_status_info_type_v01 *fc_info, + bool is_query) { struct rmnet_bearer_map *itm = NULL; int rc = 0; bool action = false; + u32 adjusted_grant; itm = qmi_rmnet_get_bearer_map(qos, fc_info->bearer_id); if (!itm) @@ -978,8 +1006,16 @@ static int dfc_update_fc_map(struct net_device *dev, struct qos_info *qos, if (itm->tx_off && fc_info->num_bytes > 0) return 0; - if ((itm->grant_size == 0 && fc_info->num_bytes > 0) || - (itm->grant_size > 0 && fc_info->num_bytes == 0)) + /* Adjuste grant for query */ + if (dfc_qmap && is_query) { + adjusted_grant = dfc_adjust_grant(itm, fc_info); + } else { + adjusted_grant = fc_info->num_bytes; + itm->bytes_in_flight = 0; + } + + if ((itm->grant_size == 0 && adjusted_grant > 0) || + (itm->grant_size > 0 && adjusted_grant == 0)) action = true; /* This is needed by qmap */ @@ -987,13 +1023,24 @@ static int dfc_update_fc_map(struct net_device *dev, struct qos_info *qos, dfc_qmap_send_ack(qos, itm->bearer_id, itm->seq, DFC_ACK_TYPE_DISABLE); - itm->grant_size = fc_info->num_bytes; - itm->grant_thresh = qmi_rmnet_grant_per(itm->grant_size); + itm->grant_size = adjusted_grant; + + /* No further query if the adjusted grant is less + * than 20% of the original grant + */ + if (dfc_qmap && is_query && + itm->grant_size < (fc_info->num_bytes / 5)) + itm->grant_thresh = itm->grant_size; + else + itm->grant_thresh = + qmi_rmnet_grant_per(itm->grant_size); + itm->seq = fc_info->seq_num; itm->ack_req = ack_req; itm->tcp_bidir = DFC_IS_TCP_BIDIR(ancillary); itm->last_grant = fc_info->num_bytes; itm->last_seq = fc_info->seq_num; + itm->last_adjusted_grant = adjusted_grant; if (action) rc = dfc_bearer_flow_ctl(dev, itm, qos); @@ -1003,7 +1050,8 @@ static int dfc_update_fc_map(struct net_device *dev, struct qos_info *qos, } void dfc_do_burst_flow_control(struct dfc_qmi_data *dfc, - struct dfc_flow_status_ind_msg_v01 *ind) + struct dfc_flow_status_ind_msg_v01 *ind, + bool is_query) { struct net_device *dev; struct qos_info *qos; @@ -1059,7 +1107,8 @@ void dfc_do_burst_flow_control(struct dfc_qmi_data *dfc, dev, qos, ack_req, ancillary, flow_status); else dfc_update_fc_map( - dev, qos, ack_req, ancillary, flow_status); + dev, qos, ack_req, ancillary, flow_status, + is_query); spin_unlock_bh(&qos->qos_lock); } @@ -1085,6 +1134,7 @@ static void dfc_update_tx_link_status(struct net_device *dev, if (itm->grant_size && !tx_status) { itm->grant_size = 0; itm->tcp_bidir = false; + itm->bytes_in_flight = 0; dfc_bearer_flow_ctl(dev, itm, qos); } else if (itm->grant_size == 0 && tx_status && !itm->rat_switch) { itm->grant_size = DEFAULT_GRANT; @@ -1162,7 +1212,8 @@ static void dfc_qmi_ind_work(struct work_struct *work) if (!dfc->restart_state) { if (svc_ind->msg_id == QMI_DFC_FLOW_STATUS_IND_V01) dfc_do_burst_flow_control( - dfc, &svc_ind->d.dfc_info); + dfc, &svc_ind->d.dfc_info, + false); else if (svc_ind->msg_id == QMI_DFC_TX_LINK_STATUS_IND_V01) dfc_handle_tx_link_status_ind( @@ -1437,6 +1488,8 @@ void dfc_qmi_burst_check(struct net_device *dev, struct qos_info *qos, trace_dfc_flow_check(dev->name, bearer->bearer_id, len, mark, bearer->grant_size); + bearer->bytes_in_flight += len; + if (!bearer->grant_size) goto out; @@ -1490,7 +1543,7 @@ void dfc_qmi_query_flow(void *dfc_data) svc_ind->d.dfc_info.flow_status_len = resp->flow_status_len; memcpy(&svc_ind->d.dfc_info.flow_status, resp->flow_status, sizeof(resp->flow_status[0]) * resp->flow_status_len); - dfc_do_burst_flow_control(data, &svc_ind->d.dfc_info); + dfc_do_burst_flow_control(data, &svc_ind->d.dfc_info, true); done: kfree(svc_ind); diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c index 1204ebc796cf253abc96a86daa4dfd7ca762ae7a..0ee43a8c495ec4655c7de9525b32aea6a7c7b85c 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 @@ -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/icnss2/Kconfig b/drivers/soc/qcom/icnss2/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..34ff85b763418218c28d5f2fc0731b92e5a5fed6 --- /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 0000000000000000000000000000000000000000..433483ec7adaff1979fee4fb4fb034b49a9fcbed --- /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 0000000000000000000000000000000000000000..8731a7b4df436ebb62bf1aa8e307bb3c72aa05c5 --- /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 +#include +#include +#include +#include +#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 > /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 > /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 > /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 0000000000000000000000000000000000000000..ada100e79cc08a747403a22082746183452f084d --- /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 +#include + +#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 0000000000000000000000000000000000000000..96b8301da849dc021d2374b2c3cfd2c7d0252c92 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..26741b0813fc96c205a7337ccf2b67373f2de0b6 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..8ed9f9851237007b260f02cb93da90c7d2f97e30 --- /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 +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..003694135435554f812f17b9318b762326b2cb4c --- /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 0000000000000000000000000000000000000000..cbe9c11f3c366266e57a7d8be1331fafbc5505eb --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..964579b9aac072aa98e801c9f65401152089c986 --- /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 55e0eea6050b339c4457db4e8aa1ca45770c4055..8a59ff588b2c6e961f729f11dad1e5a01fb66cf3 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 new file mode 100644 index 0000000000000000000000000000000000000000..622e914e486974d31898790be68df11ee55a344d --- /dev/null +++ b/drivers/soc/qcom/l2_reuse.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define L2_REUSE_SMC_ID 0x02001F01 + +static bool l2_reuse_enable = true; +static struct kobject *l2_reuse_kobj; + +static ssize_t sysfs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u\n", l2_reuse_enable); +} + +static ssize_t sysfs_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct arm_smccc_res res; + int ret; + + ret = kstrtobool(buf, &l2_reuse_enable); + if (ret) { + pr_err("Invalid argument passed\n"); + return ret; + } + + arm_smccc_smc(L2_REUSE_SMC_ID, l2_reuse_enable, 1, 0, 0, 0, 0, 0, &res); + return count; +} + +struct kobj_attribute l2_reuse_attr = __ATTR(l2_reuse_enable, 0660, + sysfs_show, sysfs_store); + +static int __init l2_reuse_driver_init(void) +{ + l2_reuse_kobj = kobject_create_and_add("l2_reuse_enable", power_kobj); + + if (!l2_reuse_kobj) { + pr_info("kobj creation for l2_reuse failed\n"); + return 0; + } + + if (sysfs_create_file(l2_reuse_kobj, &l2_reuse_attr.attr)) + kobject_put(l2_reuse_kobj); + + return 0; +} + +void __exit l2_reuse_driver_exit(void) +{ + if (l2_reuse_kobj) { + sysfs_remove_file(power_kobj, &l2_reuse_attr.attr); + kobject_put(l2_reuse_kobj); + } +} + +module_init(l2_reuse_driver_init); +module_exit(l2_reuse_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc L2 REUSE Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/llcc-lagoon.c b/drivers/soc/qcom/llcc-lagoon.c new file mode 100644 index 0000000000000000000000000000000000000000..21c0e7cd9555b4779341c97df58f946939adeded --- /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 +#include +#include +#include +#include + +/* + * 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/llcc-lito.c b/drivers/soc/qcom/llcc-lito.c index e03053c1597ebffdf6d7f8fa78dff766764c5bcd..55c1d77bea9ad71540b94833949b1e83955bf8d9 100644 --- a/drivers/soc/qcom/llcc-lito.c +++ b/drivers/soc/qcom/llcc-lito.c @@ -54,13 +54,14 @@ static struct llcc_slice_config lito_data[] = { SCT_ENTRY(LLCC_CPUSS, 1, 1536, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1), SCT_ENTRY(LLCC_AUDIO, 6, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_MDM, 8, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), - SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), + SCT_ENTRY(LLCC_GPUHTW, 11, 256, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_GPU, 12, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_DISP, 16, 1536, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_NPU, 23, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), SCT_ENTRY(LLCC_MODEMVPE, 29, 128, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0), + SCT_ENTRY(LLCC_WRTCH, 31, 128, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 0, 1), }; static int lito_qcom_llcc_probe(struct platform_device *pdev) diff --git a/drivers/soc/qcom/mem-offline.c b/drivers/soc/qcom/mem-offline.c index aefe453281fd7ffceae5ce397d47b0015cf6b505..210dac4520b81d24b1daa8fe2d48f1ebfc655587 100644 --- a/drivers/soc/qcom/mem-offline.c +++ b/drivers/soc/qcom/mem-offline.c @@ -19,7 +19,9 @@ #include #include #include +#include +#define RPM_DDR_REQ 0x726464 #define AOP_MSG_ADDR_MASK 0xffffffff #define AOP_MSG_ADDR_HIGH_SHIFT 32 #define MAX_LEN 96 @@ -27,6 +29,7 @@ static unsigned long start_section_nr, end_section_nr; static struct kobject *kobj; static unsigned int offline_granule, sections_per_block; +static bool is_rpm_controller; #define MODULE_CLASS_NAME "mem-offline" #define BUF_LEN 100 @@ -53,6 +56,15 @@ static struct mem_offline_mailbox { struct mbox_chan *mbox; } mailbox; +struct memory_refresh_request { + u64 start; /* Lower bit signifies action + * 0 - disable self-refresh + * 1 - enable self-refresh + * upper bits are for base address + */ + u32 size; /* size of memory region */ +}; + static struct section_stat *mem_info; static void clear_pgtable_mapping(phys_addr_t start, phys_addr_t end) @@ -119,6 +131,25 @@ void record_stat(unsigned long sec, ktime_t delay, int mode) mem_info[blk_nr].last_recorded_time = delay; } +static int mem_region_refresh_control(unsigned long pfn, + unsigned long nr_pages, + bool enable) +{ + struct memory_refresh_request mem_req; + struct msm_rpm_kvp rpm_kvp; + + mem_req.start = enable; + mem_req.start |= pfn << PAGE_SHIFT; + mem_req.size = nr_pages * PAGE_SIZE; + + rpm_kvp.key = RPM_DDR_REQ; + rpm_kvp.data = (void *)&mem_req; + rpm_kvp.length = sizeof(mem_req); + + return msm_rpm_send_message(MSM_RPM_CTX_ACTIVE_SET, RPM_DDR_REQ, 0, + &rpm_kvp, 1); +} + static int aop_send_msg(unsigned long addr, bool online) { struct qmp_pkt pkt; @@ -163,9 +194,16 @@ static int send_msg(struct memory_notify *mn, bool online, int count) start = section_nr_to_pfn(base_sec_nr); for (i = 0; i < count; ++i) { - ret = aop_send_msg(__pfn_to_phys(start), online); + if (is_rpm_controller) + ret = mem_region_refresh_control(start, + segment_size >> PAGE_SHIFT, + online); + else + ret = aop_send_msg(__pfn_to_phys(start), online); + if (ret) { - pr_err("PASR: AOP %s request addr:0x%llx failed\n", + pr_err("PASR: %s %s request addr:0x%llx failed\n", + is_rpm_controller ? "RPM" : "AOP", online ? "online" : "offline", __pfn_to_phys(start)); goto undo; @@ -180,7 +218,13 @@ static int send_msg(struct memory_notify *mn, bool online, int count) while (i-- > 0) { int ret; - ret = aop_send_msg(__pfn_to_phys(start), !online); + if (is_rpm_controller) + ret = mem_region_refresh_control(start, + segment_size >> PAGE_SHIFT, + !online); + else + ret = aop_send_msg(__pfn_to_phys(start), !online); + if (ret) panic("Failed to completely online/offline a hotpluggable segment. A quasi state of memblock can cause randomn system failures."); start = __phys_to_pfn(__pfn_to_phys(start) + segment_size); @@ -530,6 +574,11 @@ static int mem_parse_dt(struct platform_device *pdev) return -EINVAL; } + if (!of_find_property(node, "mboxes", NULL)) { + is_rpm_controller = true; + return 0; + } + mailbox.cl.dev = &pdev->dev; mailbox.cl.tx_block = true; mailbox.cl.tx_tout = 1000; diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 1f5cc0bde8939740e362efa508fc7c78d1a23cf4..59d164be6c79e8d2fef4ded9c72db21830db2b35 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 @@ -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); + 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++; - } 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); + 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; + } + + ret = config_cpuss_register(dev, p, system_reg_end_index, + cmd, register_offset, val); + if (ret) + goto err; - *(p + system_reg_end_index) = register_offset | - length_in_words; + 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/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c index cb736f47a065bcadeec62f65280a617870ca226e..f784f61ed5c9b1b91a8743a1e1ea0fc7159337de 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.c +++ b/drivers/soc/qcom/memshare/msm_memshare.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. */ #include @@ -260,31 +260,34 @@ static int mem_share_do_ramdump(void) dev_err(memsh_child->dev, "memshare: %s: failed to map the memory region to APPS\n", client_name); + continue; } else { memblock[i].hyp_mapping = 0; } } - ramdump_segments_tmp = kcalloc(1, - sizeof(struct ramdump_segment), - GFP_KERNEL); - if (!ramdump_segments_tmp) - return -ENOMEM; + if (!memblock[i].hyp_mapping) { + ramdump_segments_tmp = kcalloc(1, + sizeof(struct ramdump_segment), + GFP_KERNEL); + if (!ramdump_segments_tmp) + return -ENOMEM; - ramdump_segments_tmp[0].size = memblock[i].size; - ramdump_segments_tmp[0].address = memblock[i].phy_addr; + ramdump_segments_tmp[0].size = memblock[i].size; + ramdump_segments_tmp[0].address = memblock[i].phy_addr; - dev_dbg(memsh_child->dev, "memshare: %s: Begin elf dump for size = %d\n", - client_name, memblock[i].size); + dev_dbg(memsh_child->dev, "memshare: %s: Begin elf dump for size = %d\n", + client_name, memblock[i].size); - ret = do_elf_ramdump(memshare_ramdump_dev[i], - ramdump_segments_tmp, 1); - kfree(ramdump_segments_tmp); - if (ret < 0) { - dev_err(memsh_child->dev, - "memshare: %s: Unable to elf dump with failure: %d\n", - client_name, ret); - return ret; + ret = do_elf_ramdump(memshare_ramdump_dev[i], + ramdump_segments_tmp, 1); + kfree(ramdump_segments_tmp); + if (ret < 0) { + dev_err(memsh_child->dev, + "memshare: %s: Unable to elf dump with failure: %d\n", + client_name, ret); + return ret; + } } } return 0; diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c index a3e2070f49e5c4f50eb846040d38af22b149239d..87b72ed3856895f5a38be62a071b4fd94cffce44 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 @@ -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/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c index 13472a3b3ce03d07f2c3d7dd80ff486adf553a11..3a48c03a8be79fd419ffb5acae2aa65b700666de 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c @@ -343,22 +343,26 @@ static int tcs_cmd_list_gen(int *n_active, continue; list_for_each_entry(cur_bcm, &cur_bcm_clist[i], link) { commit = false; - if ((cur_bcm->node_vec[DUAL_CTX].vec_a == - cur_bcm->node_vec[ACTIVE_CTX].vec_a) && - (cur_bcm->node_vec[DUAL_CTX].vec_b == - cur_bcm->node_vec[ACTIVE_CTX].vec_b)) { - if (last_tcs != -1 && - list_is_last(&cur_bcm->link, - &cur_bcm_clist[i])) { - cmdlist_wake[last_tcs].data |= - BCM_TCS_CMD_COMMIT_MASK; - cmdlist_sleep[last_tcs].data |= - BCM_TCS_CMD_COMMIT_MASK; - cmdlist_wake[last_tcs].wait = true; - cmdlist_sleep[last_tcs].wait = true; - idx++; + if (cur_rsc->node_info->id != MSM_BUS_RSC_DISP) { + if ((cur_bcm->node_vec[DUAL_CTX].vec_a == + cur_bcm->node_vec[ACTIVE_CTX].vec_a) && + (cur_bcm->node_vec[DUAL_CTX].vec_b == + cur_bcm->node_vec[ACTIVE_CTX].vec_b)) { + if (last_tcs != -1 && + list_is_last(&cur_bcm->link, + &cur_bcm_clist[i])) { + cmdlist_wake[last_tcs].data |= + BCM_TCS_CMD_COMMIT_MASK; + cmdlist_sleep[last_tcs].data |= + BCM_TCS_CMD_COMMIT_MASK; + cmdlist_wake[last_tcs].wait = + true; + cmdlist_sleep[last_tcs].wait = + true; + idx++; + } + continue; } - continue; } last_tcs = k; n_sleep[idx]++; @@ -649,14 +653,14 @@ int msm_bus_commit_data(struct list_head *clist) MSM_BUS_ERR("%s: error sending active/awake sets: %d\n", __func__, ret); } - if (cnt_wake) { + if (cnt_wake || (cur_rsc->node_info->id == MSM_BUS_RSC_DISP)) { ret = rpmh_write_batch(cur_mbox, RPMH_WAKE_ONLY_STATE, cmdlist_wake, n_wake); if (ret) MSM_BUS_ERR("%s: error sending wake sets: %d\n", __func__, ret); } - if (cnt_sleep) { + if (cnt_sleep || (cur_rsc->node_info->id == MSM_BUS_RSC_DISP)) { ret = rpmh_write_batch(cur_mbox, RPMH_SLEEP_STATE, cmdlist_sleep, n_sleep); if (ret) @@ -672,7 +676,8 @@ int msm_bus_commit_data(struct list_head *clist) exit_msm_bus_commit_data: list_for_each_entry_safe(node, node_tmp, clist, link) { - bcm_clist_clean(node); + if (cur_rsc->node_info->id != MSM_BUS_RSC_DISP) + bcm_clist_clean(node); node->dirty = false; list_del_init(&node->link); } diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index feed3db21c10888681a0f205cccd24c73be078c1..ee89ffb6dde84cacc0f37a82918461b8b20eb206 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 / == @req_freq + * - if @exact is false then @res_freq / <= @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 de319e3aeb467accd497d9698a9b4639fdc5ba3b..e9f986b3baf9baaa718e46afa1b0206987b47a8f 100644 --- a/drivers/soc/qcom/qmi_rmnet.c +++ b/drivers/soc/qcom/qmi_rmnet.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 @@ -461,7 +461,8 @@ static void qmi_rmnet_query_flows(struct qmi_info *qmi) int i; for (i = 0; i < MAX_CLIENT_NUM; i++) { - if (qmi->dfc_clients[i] && !dfc_qmap) + if (qmi->dfc_clients[i] && !dfc_qmap && + !qmi->dfc_client_exiting[i]) dfc_qmi_query_flow(qmi->dfc_clients[i]); } } @@ -537,6 +538,7 @@ qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm) err = dfc_qmap_client_init(port, idx, &svc, qmi); else err = dfc_qmi_client_init(port, idx, &svc, qmi); + qmi->dfc_client_exiting[idx] = false; } if ((tcm->tcm_ifindex & FLAG_POWERSAVE_MASK) && @@ -597,6 +599,7 @@ qmi_rmnet_delete_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm) qmi->wda_client = NULL; qmi->wda_pending = NULL; } else { + qmi->dfc_client_exiting[idx] = true; qmi_rmnet_flush_ps_wq(); } @@ -712,15 +715,18 @@ 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) { + 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); - bearer->seq = 0; - bearer->ack_req = 0; - bearer->tcp_bidir = false; - bearer->rat_switch = false; if (do_wake) dfc_bearer_flow_ctl(dev, bearer, qos); diff --git a/drivers/soc/qcom/qmi_rmnet_i.h b/drivers/soc/qcom/qmi_rmnet_i.h index 15af9851adfe918216866abed0f709abe52172dc..badf7a778a49a133959afd610b0a23d3a8d73119 100644 --- a/drivers/soc/qcom/qmi_rmnet_i.h +++ b/drivers/soc/qcom/qmi_rmnet_i.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 _RMNET_QMI_I_H @@ -36,6 +36,8 @@ struct rmnet_bearer_map { u8 ack_req; u32 last_grant; u16 last_seq; + u32 bytes_in_flight; + u32 last_adjusted_grant; bool tcp_bidir; bool rat_switch; bool tx_off; @@ -80,6 +82,7 @@ struct qmi_info { void *wda_pending; void *dfc_clients[MAX_CLIENT_NUM]; void *dfc_pending[MAX_CLIENT_NUM]; + bool dfc_client_exiting[MAX_CLIENT_NUM]; unsigned long ps_work_active; bool ps_enabled; bool dl_msg_active; diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c index 8201946986e5c7080d4ae68b205f2cb64d09a974..3d9d3799f3f41afa053bf1c62424f203867f0978 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 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1446,6 +1447,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; @@ -1517,6 +1519,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; } diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index c0da352e12dcb583df567675bd727a40df3d15af..3d00736306514cf722e9c32e5cdda7b39fbbbe7d 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2009-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -331,6 +331,7 @@ static struct msm_soc_info cpu_of_id[] = { /* Bengalp ID */ [445] = {MSM_CPU_BENGALP, "BENGALP"}, + [420] = {MSM_CPU_BENGALP, "BENGALP"}, /* Scuba ID */ [441] = {MSM_CPU_SCUBA, "SCUBA"}, diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 62a224d02940d3380b36144c1c08301a0a36f94a..d24eb7c914899a302c70dc954684a7a1b805fe64 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.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. */ /* @@ -38,7 +38,7 @@ * User Space Request & Response are synchronous. * read() & write() operations are blocking until completed or terminated. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include /* min() */ #include /* MODULE_LICENSE */ @@ -63,6 +63,41 @@ #include #include #include +#include + +#define SPCOM_LOG_PAGE_CNT 10 + +#define spcom_ipc_log_string(_x...) do { \ + if (spcom_ipc_log_context) \ + ipc_log_string(spcom_ipc_log_context, _x); \ + } while (0) + +#define spcom_pr_err(_fmt, ...) do { \ + pr_err(_fmt); \ + spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \ + } while (0) + +#define spcom_pr_warn(_fmt, ...) do { \ + pr_warn(_fmt); \ + spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \ + } while (0) + +#define spcom_pr_info(_fmt, ...) do { \ + pr_info(_fmt); \ + spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \ + } while (0) + +#if defined(DEBUG) +#define spcom_pr_dbg(_fmt, ...) do { \ + pr_debug(_fmt); \ + spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \ + } while (0) +#else +#define spcom_pr_dbg(_fmt, ...) do { \ + no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \ + spcom_ipc_log_string("%s" pr_fmt(_fmt), "", ##__VA_ARGS__); \ + } while (0) +#endif /** * Request buffer size. @@ -239,7 +274,10 @@ struct spcom_device { /* Device Driver State */ static struct spcom_device *spcom_dev; +static void *spcom_ipc_log_context; +/* error registers shared with SPU */ +static u32 spcom_rmb_error_reg_addr; /* Physical address of SP2SOC RMB shared register */ /* SP_SCSR_RMB_SP2SOC_IRQ_SET_ADDR */ static u32 spcom_sp2soc_rmb_reg_addr; @@ -309,7 +347,7 @@ static int spcom_create_predefined_channels_chardev(void) break; ret = spcom_create_channel_chardev(name, false); if (ret) { - pr_err("failed to create chardev [%s], ret [%d]\n", + spcom_pr_err("failed to create chardev [%s], ret [%d]\n", name, ret); return -EFAULT; } @@ -336,7 +374,7 @@ static int spcom_init_channel(struct spcom_channel *ch, const char *name) { if (!ch || !name || !name[0]) { - pr_err("invalid parameters\n"); + spcom_pr_err("invalid parameters\n"); return -EINVAL; } @@ -406,7 +444,7 @@ static int spcom_rx(struct spcom_channel *ch, mutex_lock(&ch->lock); if (ch->rx_buf_txn_id != ch->txn_id) { - pr_debug("rpmsg_rx_buf is updated in a different session\n"); + spcom_pr_dbg("rpmsg_rx_buf is updated in a different session\n"); if (ch->rpmsg_rx_buf) { memset(ch->rpmsg_rx_buf, 0, ch->actual_rx_size); kfree((void *)ch->rpmsg_rx_buf); @@ -421,7 +459,6 @@ static int spcom_rx(struct spcom_channel *ch, mutex_unlock(&ch->lock); /* unlock while waiting */ /* wait for rx response */ - pr_debug("wait for rx done, timeout_msec=%d\n", timeout_msec); if (timeout_msec) timeleft = wait_for_completion_interruptible_timeout( &ch->rx_done, jiffies); @@ -431,34 +468,34 @@ static int spcom_rx(struct spcom_channel *ch, mutex_lock(&ch->lock); if (timeout_msec && timeleft == 0) { ch->txn_id++; /* to drop expired rx packet later */ - pr_err("rx_done timeout expired %d ms, set txn_id=%d\n", + spcom_pr_err("rx_done timeout expired %d ms, set txn_id=%d\n", timeout_msec, ch->txn_id); ret = -ETIMEDOUT; goto exit_err; } else if (ch->rpmsg_abort) { - pr_warn("rpmsg channel is closing\n"); + spcom_pr_warn("rpmsg channel is closing\n"); ret = -ERESTART; goto exit_err; } else if (ret < 0 || timeleft == -ERESTARTSYS) { - pr_debug("wait interrupted: ret=%d, timeleft=%ld\n", + spcom_pr_dbg("wait interrupted: ret=%d, timeleft=%ld\n", ret, timeleft); if (timeleft == -ERESTARTSYS) ret = -ERESTARTSYS; goto exit_err; } else if (ch->actual_rx_size) { - pr_debug("actual_rx_size is [%zu], txn_id %d\n", + spcom_pr_dbg("actual_rx_size is [%zu], txn_id %d\n", ch->actual_rx_size, ch->txn_id); } else { - pr_err("actual_rx_size is zero\n"); + spcom_pr_err("actual_rx_size is zero\n"); ret = -EFAULT; goto exit_err; } } else { - pr_debug("pending data size [%zu], requested size [%u], ch->txn_id %d\n", - ch->actual_rx_size, size, ch->txn_id); + spcom_pr_dbg("ch[%s]:rx data size [%zu], txn_id:%d\n", + ch->name, ch->actual_rx_size, ch->txn_id); } if (!ch->rpmsg_rx_buf) { - pr_err("invalid rpmsg_rx_buf\n"); + spcom_pr_err("invalid rpmsg_rx_buf\n"); ret = -ENOMEM; goto exit_err; } @@ -466,8 +503,6 @@ static int spcom_rx(struct spcom_channel *ch, size = min_t(size_t, ch->actual_rx_size, size); memcpy(buf, ch->rpmsg_rx_buf, size); - pr_debug("copy size [%d]\n", (int) size); - memset(ch->rpmsg_rx_buf, 0, ch->actual_rx_size); kfree((void *)ch->rpmsg_rx_buf); ch->rpmsg_rx_buf = NULL; @@ -504,17 +539,16 @@ static int spcom_get_next_request_size(struct spcom_channel *ch) /* check if already got it via callback */ if (ch->actual_rx_size) { - pr_debug("next-req-size already ready ch [%s] size [%zu]\n", + spcom_pr_dbg("next-req-size already ready ch [%s] size [%zu]\n", ch->name, ch->actual_rx_size); ret = -EFAULT; goto exit_ready; } mutex_unlock(&ch->lock); /* unlock while waiting */ - pr_debug("Wait for Rx Done, ch [%s]\n", ch->name); ret = wait_for_completion_interruptible(&ch->rx_done); if (ret < 0) { - pr_debug("ch [%s]:interrupted wait ret=%d\n", + spcom_pr_dbg("ch [%s]:interrupted wait ret=%d\n", ch->name, ret); goto exit_error; } @@ -522,7 +556,7 @@ static int spcom_get_next_request_size(struct spcom_channel *ch) mutex_lock(&ch->lock); /* re-lock after waiting */ if (ch->actual_rx_size == 0) { - pr_err("invalid rx size [%zu] ch [%s]\n", + spcom_pr_err("invalid rx size [%zu] ch [%s]\n", ch->actual_rx_size, ch->name); mutex_unlock(&ch->lock); ret = -EFAULT; @@ -535,7 +569,7 @@ static int spcom_get_next_request_size(struct spcom_channel *ch) if (size > sizeof(struct spcom_msg_hdr)) { size -= sizeof(struct spcom_msg_hdr); } else { - pr_err("rx size [%d] too small\n", size); + spcom_pr_err("rx size [%d] too small\n", size); ret = -EFAULT; mutex_unlock(&ch->lock); goto exit_error; @@ -568,18 +602,16 @@ static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size) const size_t maxlen = sizeof(cmd->ch_name); if (cmd_size != sizeof(*cmd)) { - pr_err("cmd_size [%d] , expected [%d]\n", + spcom_pr_err("cmd_size [%d] , expected [%d]\n", (int) cmd_size, (int) sizeof(*cmd)); return -EINVAL; } if (strnlen(cmd->ch_name, maxlen) == maxlen) { - pr_err("channel name is not NULL terminated\n"); + spcom_pr_err("channel name is not NULL terminated\n"); return -EINVAL; } - pr_debug("ch_name [%s]\n", cmd->ch_name); - ret = spcom_create_channel_chardev(cmd->ch_name, cmd->is_sharable); return ret; @@ -595,16 +627,33 @@ static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size) */ static int spcom_local_powerup(const struct subsys_desc *subsys) { - void __iomem *regs; + void __iomem *regs, *err_regs; + u32 pbl_status_reg = 0; - regs = ioremap_nocache(spcom_sp2soc_rmb_reg_addr, sizeof(u32)); - if (!regs) + err_regs = ioremap_nocache(spcom_rmb_error_reg_addr, sizeof(u32)); + if (!err_regs) return -ENOMEM; - writel_relaxed(spcom_sp2soc_pbldone_mask|spcom_sp2soc_initdone_mask, - regs); - iounmap(regs); - pr_debug("spcom local powerup - SPSS cold boot\n"); + pbl_status_reg = readl_relaxed(err_regs); + + if (pbl_status_reg == 0) { + regs = ioremap_nocache(spcom_sp2soc_rmb_reg_addr, sizeof(u32)); + if (!regs) { + iounmap(err_regs); + return -ENOMEM; + } + + writel_relaxed( + spcom_sp2soc_pbldone_mask|spcom_sp2soc_initdone_mask, + regs); + iounmap(regs); + } else { + spcom_pr_err("PBL has returned an error= 0x%x. Not sending sw_init_done\n", + pbl_status_reg); + } + + iounmap(err_regs); + spcom_pr_dbg("spcom local powerup - SPSS cold boot\n"); return 0; } @@ -620,7 +669,7 @@ static int spcom_local_powerup_after_fota(const struct subsys_desc *subsys) { (void)subsys; - pr_err("SSR after firmware update before calling IAR update - panic\n"); + spcom_pr_err("SSR after firmware update before calling IAR update - panic\n"); panic("SSR after SPU firmware update\n"); return 0; @@ -642,33 +691,33 @@ static int spcom_handle_restart_sp_command(void *cmd_buf, int cmd_size) struct subsys_desc *desc_p = NULL; if (!cmd) { - pr_err("NULL cmd_buf\n"); + spcom_pr_err("NULL cmd_buf\n"); return -EINVAL; } if (cmd_size != sizeof(*cmd)) { - pr_err("cmd_size [%d] , expected [%d]\n", + spcom_pr_err("cmd_size [%d] , expected [%d]\n", (int) cmd_size, (int) sizeof(*cmd)); return -EINVAL; } - pr_debug("restart - PIL FW loading initiated: preloaded=%d\n", + spcom_pr_dbg("restart - PIL FW loading initiated: preloaded=%d\n", cmd->arg); if (cmd->arg) { subsystem_get_retval = find_subsys_device("spss"); if (!subsystem_get_retval) { - pr_err("restart - no device\n"); + spcom_pr_err("restart - no device\n"); return -ENODEV; } desc_p = *(struct subsys_desc **)subsystem_get_retval; if (!desc_p) { - pr_err("restart - no device\n"); + spcom_pr_err("restart - no device\n"); return -ENODEV; } - pr_debug("restart - Name: %s FW name: %s Depends on: %s\n", + spcom_pr_dbg("restart - Name: %s FW name: %s Depends on: %s\n", desc_p->name, desc_p->fw_name, desc_p->pon_depends_on); desc_powerup = desc_p->powerup; /** @@ -688,7 +737,7 @@ static int spcom_handle_restart_sp_command(void *cmd_buf, int cmd_size) subsystem_get_retval = subsystem_get("spss"); if (!subsystem_get_retval) { - pr_err("restart - unable to trigger PIL process for FW loading\n"); + spcom_pr_err("restart - unable to trigger PIL process for FW loading\n"); return -EINVAL; } @@ -702,7 +751,7 @@ static int spcom_handle_restart_sp_command(void *cmd_buf, int cmd_size) desc_p->powerup = desc_powerup; } } - pr_debug("restart - PIL FW loading process is complete\n"); + spcom_pr_dbg("restart - PIL FW loading process is complete\n"); return 0; } @@ -727,21 +776,21 @@ static int spcom_handle_send_command(struct spcom_channel *ch, uint32_t timeout_msec; int time_msec = 0; - pr_debug("send req/resp ch [%s] size [%d]\n", ch->name, size); + spcom_pr_dbg("send req/resp ch [%s] size [%d]\n", ch->name, size); /* * check that cmd buf size is at least struct size, * to allow access to struct fields. */ if (size < sizeof(*cmd)) { - pr_err("ch [%s] invalid cmd buf\n", + spcom_pr_err("ch [%s] invalid cmd buf\n", ch->name); return -EINVAL; } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { - pr_err("ch [%s] remote side not connect\n", ch->name); + spcom_pr_err("ch [%s] remote side not connect\n", ch->name); return -ENOTCONN; } @@ -752,12 +801,12 @@ static int spcom_handle_send_command(struct spcom_channel *ch, /* Check param validity */ if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { - pr_err("ch [%s] invalid buf size [%d]\n", + spcom_pr_err("ch [%s] invalid buf size [%d]\n", ch->name, buf_size); return -EINVAL; } if (size != sizeof(*cmd) + buf_size) { - pr_err("ch [%s] invalid cmd size [%d]\n", + spcom_pr_err("ch [%s] invalid cmd size [%d]\n", ch->name, size); return -EINVAL; } @@ -773,7 +822,7 @@ static int spcom_handle_send_command(struct spcom_channel *ch, mutex_lock(&ch->lock); if (ch->comm_role_undefined) { - pr_debug("ch [%s] send first -> it is client\n", ch->name); + spcom_pr_dbg("ch [%s] send first -> it is client\n", ch->name); ch->comm_role_undefined = false; ch->is_server = false; } @@ -790,7 +839,7 @@ static int spcom_handle_send_command(struct spcom_channel *ch, time_msec = 0; do { if (ch->rpmsg_abort) { - pr_err("ch [%s] aborted\n", ch->name); + spcom_pr_err("ch [%s] aborted\n", ch->name); ret = -ECANCELED; break; } @@ -804,7 +853,7 @@ static int spcom_handle_send_command(struct spcom_channel *ch, mutex_lock(&ch->lock); } while ((ret == -EBUSY || ret == -EAGAIN) && time_msec < timeout_msec); if (ret) - pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", + spcom_pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", ch->name, ret, timeout_msec); mutex_unlock(&ch->lock); @@ -839,35 +888,34 @@ static int modify_ion_addr(void *buf, ptr += buf_offset; if (fd < 0) { - pr_err("invalid fd [%d]\n", fd); + spcom_pr_err("invalid fd [%d]\n", fd); return -ENODEV; } if (buf_size < sizeof(uint64_t)) { - pr_err("buf size too small [%d]\n", buf_size); + spcom_pr_err("buf size too small [%d]\n", buf_size); return -ENODEV; } if (buf_offset % sizeof(uint64_t)) - pr_debug("offset [%d] is NOT 64-bit aligned\n", buf_offset); + spcom_pr_dbg("offset [%d] is NOT 64-bit aligned\n", buf_offset); else - pr_debug("offset [%d] is 64-bit aligned\n", buf_offset); + spcom_pr_dbg("offset [%d] is 64-bit aligned\n", buf_offset); if (buf_offset > buf_size - sizeof(uint64_t)) { - pr_err("invalid buf_offset [%d]\n", buf_offset); + spcom_pr_err("invalid buf_offset [%d]\n", buf_offset); return -ENODEV; } dma_buf = dma_buf_get(fd); if (IS_ERR_OR_NULL(dma_buf)) { - pr_err("fail to get dma buf handle\n"); + spcom_pr_err("fail to get dma buf handle\n"); return -EINVAL; } - pr_debug("dma_buf handle ok\n"); attach = dma_buf_attach(dma_buf, &spcom_dev->pdev->dev); if (IS_ERR_OR_NULL(attach)) { ret = PTR_ERR(attach); - pr_err("fail to attach dma buf %d\n", ret); + spcom_pr_err("fail to attach dma buf %d\n", ret); dma_buf_put(dma_buf); goto mem_map_table_failed; } @@ -875,19 +923,19 @@ static int modify_ion_addr(void *buf, sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR_OR_NULL(sg)) { ret = PTR_ERR(sg); - pr_err("fail to get sg table of dma buf %d\n", ret); + spcom_pr_err("fail to get sg table of dma buf %d\n", ret); goto mem_map_table_failed; } if (sg->sgl) { phy_addr = sg->sgl->dma_address; } else { - pr_err("sgl is NULL\n"); + spcom_pr_err("sgl is NULL\n"); ret = -ENOMEM; goto mem_map_sg_failed; } /* Set the physical address at the buffer offset */ - pr_debug("ion phys addr = [0x%lx]\n", (long) phy_addr); + spcom_pr_dbg("ion phys addr = [0x%lx]\n", (long) phy_addr); memcpy(ptr, &phy_addr, sizeof(phy_addr)); mem_map_sg_failed: @@ -925,21 +973,21 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, uint32_t timeout_msec; int time_msec = 0; - pr_debug("send req/resp ch [%s] size [%d]\n", ch->name, size); + spcom_pr_dbg("send req/resp ch [%s] size [%d]\n", ch->name, size); /* * check that cmd buf size is at least struct size, * to allow access to struct fields. */ if (size < sizeof(*cmd)) { - pr_err("ch [%s] invalid cmd buf\n", + spcom_pr_err("ch [%s] invalid cmd buf\n", ch->name); return -EINVAL; } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { - pr_err("ch [%s] remote side not connect\n", ch->name); + spcom_pr_err("ch [%s] remote side not connect\n", ch->name); return -ENOTCONN; } @@ -951,12 +999,12 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, /* Check param validity */ if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { - pr_err("ch [%s] invalid buf size [%d]\n", + spcom_pr_err("ch [%s] invalid buf size [%d]\n", ch->name, buf_size); return -EINVAL; } if (size != sizeof(*cmd) + buf_size) { - pr_err("ch [%s] invalid cmd size [%d]\n", + spcom_pr_err("ch [%s] invalid cmd size [%d]\n", ch->name, size); return -EINVAL; } @@ -972,7 +1020,7 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, mutex_lock(&ch->lock); if (ch->comm_role_undefined) { - pr_debug("ch [%s] send first -> it is client\n", ch->name); + spcom_pr_dbg("ch [%s] send first -> it is client\n", ch->name); ch->comm_role_undefined = false; ch->is_server = false; } @@ -990,7 +1038,6 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]); if (ret < 0) { mutex_unlock(&ch->lock); - pr_err("modify_ion_addr() error [%d]\n", ret); memset(tx_buf, 0, tx_buf_size); kfree(tx_buf); return -EFAULT; @@ -1001,7 +1048,7 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, time_msec = 0; do { if (ch->rpmsg_abort) { - pr_err("ch [%s] aborted\n", ch->name); + spcom_pr_err("ch [%s] aborted\n", ch->name); ret = -ECANCELED; break; } @@ -1015,7 +1062,7 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch, mutex_lock(&ch->lock); } while ((ret == -EBUSY || ret == -EAGAIN) && time_msec < timeout_msec); if (ret) - pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", + spcom_pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", ch->name, ret, timeout_msec); mutex_unlock(&ch->lock); @@ -1040,23 +1087,22 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, struct dma_buf *dma_buf; if (size != sizeof(*cmd)) { - pr_err("cmd size [%d] , expected [%d]\n", + spcom_pr_err("cmd size [%d] , expected [%d]\n", (int) size, (int) sizeof(*cmd)); return -EINVAL; } if (cmd->arg > (unsigned int)INT_MAX) { - pr_err("int overflow [%u]\n", cmd->arg); + spcom_pr_err("int overflow [%u]\n", cmd->arg); return -EINVAL; } fd = cmd->arg; dma_buf = dma_buf_get(fd); if (IS_ERR_OR_NULL(dma_buf)) { - pr_err("fail to get dma buf handle\n"); + spcom_pr_err("fail to get dma buf handle\n"); return -EINVAL; } - pr_debug("dma_buf referenced ok\n"); /* shared buf lock doesn't involve any rx/tx data to SP. */ mutex_lock(&ch->lock); @@ -1064,7 +1110,8 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, /* Check if this shared buffer is already locked */ for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { if (ch->dmabuf_handle_table[i] == dma_buf) { - pr_debug("fd [%d] shared buf is already locked\n", fd); + spcom_pr_dbg("fd [%d] shared buf is already locked\n", + fd); /* decrement back the ref count */ mutex_unlock(&ch->lock); dma_buf_put(dma_buf); @@ -1077,7 +1124,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, if (ch->dmabuf_handle_table[i] == NULL) { ch->dmabuf_handle_table[i] = dma_buf; ch->dmabuf_fd_table[i] = fd; - pr_debug("ch [%s] locked ion buf #%d fd [%d] dma_buf=0x%pK\n", + spcom_pr_dbg("ch [%s] locked ion buf #%d fd [%d] dma_buf=0x%pK\n", ch->name, i, ch->dmabuf_fd_table[i], ch->dmabuf_handle_table[i]); @@ -1089,7 +1136,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, mutex_unlock(&ch->lock); /* decrement back the ref count */ dma_buf_put(dma_buf); - pr_err("no free entry to store ion handle of fd [%d]\n", fd); + spcom_pr_err("no free entry to store ion handle of fd [%d]\n", fd); return -EFAULT; } @@ -1110,35 +1157,34 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, struct dma_buf *dma_buf; if (size != sizeof(*cmd)) { - pr_err("cmd size [%d], expected [%d]\n", + spcom_pr_err("cmd size [%d], expected [%d]\n", (int)size, (int)sizeof(*cmd)); return -EINVAL; } if (cmd->arg > (unsigned int)INT_MAX) { - pr_err("int overflow [%u]\n", cmd->arg); + spcom_pr_err("int overflow [%u]\n", cmd->arg); return -EINVAL; } fd = cmd->arg; - pr_debug("Unlock ion buf ch [%s] fd [%d]\n", ch->name, fd); + spcom_pr_dbg("Unlock ion buf ch [%s] fd [%d]\n", ch->name, fd); dma_buf = dma_buf_get(fd); if (IS_ERR_OR_NULL(dma_buf)) { - pr_err("fail to get dma buf handle\n"); + spcom_pr_err("fail to get dma buf handle\n"); return -EINVAL; } dma_buf_put(dma_buf); - pr_debug("dma_buf referenced ok\n"); /* shared buf unlock doesn't involve any rx/tx data to SP. */ mutex_lock(&ch->lock); if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) { - pr_debug("unlocked ALL ion buf ch [%s]\n", ch->name); + spcom_pr_dbg("unlocked ALL ion buf ch [%s]\n", ch->name); found = true; /* unlock all buf */ for (i = 0; i < ARRAY_SIZE(ch->dmabuf_handle_table); i++) { if (ch->dmabuf_handle_table[i] != NULL) { - pr_debug("unlocked ion buf #%d fd [%d]\n", + spcom_pr_dbg("unlocked ion buf #%d fd [%d]\n", i, ch->dmabuf_fd_table[i]); dma_buf_put(ch->dmabuf_handle_table[i]); ch->dmabuf_handle_table[i] = NULL; @@ -1151,7 +1197,7 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, if (!ch->dmabuf_handle_table[i]) continue; if (ch->dmabuf_handle_table[i] == dma_buf) { - pr_debug("ch [%s] unlocked ion buf #%d fd [%d] dma_buf=0x%pK\n", + spcom_pr_dbg("ch [%s] unlocked ion buf #%d fd [%d] dma_buf=0x%pK\n", ch->name, i, ch->dmabuf_fd_table[i], ch->dmabuf_handle_table[i]); @@ -1166,7 +1212,7 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, mutex_unlock(&ch->lock); if (!found) { - pr_err("ch [%s] fd [%d] was not found\n", ch->name, fd); + spcom_pr_err("ch [%s] fd [%d] was not found\n", ch->name, fd); return -ENODEV; } @@ -1187,23 +1233,23 @@ static int spcom_handle_enable_ssr_command(void) void *subsystem_get_retval = find_subsys_device("spss"); if (!subsystem_get_retval) { - pr_err("restart - no device\n"); + spcom_pr_err("restart - no device\n"); return -ENODEV; } desc_p = *(struct subsys_desc **)subsystem_get_retval; if (!desc_p) { - pr_err("restart - no device\n"); + spcom_pr_err("restart - no device\n"); return -ENODEV; } if (!desc_powerup) { - pr_err("no original SSR function\n"); + spcom_pr_err("no original SSR function\n"); return -ENODEV; } desc_p->powerup = desc_powerup; - pr_info("SSR is enabled after FOTA\n"); + spcom_pr_info("SSR is enabled after FOTA\n"); return 0; } @@ -1226,25 +1272,26 @@ static int spcom_handle_write(struct spcom_channel *ch, /* Minimal command should have command-id and argument */ if (buf_size < sizeof(struct spcom_user_command)) { - pr_err("Command buffer size [%d] too small\n", buf_size); + spcom_pr_err("Command buffer size [%d] too small\n", buf_size); return -EINVAL; } cmd = (struct spcom_user_command *)buf; cmd_id = (int) cmd->cmd_id; - pr_debug("cmd_id [0x%x]\n", cmd_id); + spcom_pr_dbg("cmd_id [0x%x]\n", cmd_id); if (!ch && cmd_id != SPCOM_CMD_CREATE_CHANNEL && cmd_id != SPCOM_CMD_RESTART_SP && cmd_id != SPCOM_CMD_ENABLE_SSR) { - pr_err("channel context is null\n"); + spcom_pr_err("channel context is null\n"); return -EINVAL; } if (cmd_id == SPCOM_CMD_SEND || cmd_id == SPCOM_CMD_SEND_MODIFIED) { if (!spcom_is_channel_connected(ch)) { - pr_err("ch [%s] remote side not connected\n", ch->name); + spcom_pr_err("ch [%s] remote side not connected\n", + ch->name); return -ENOTCONN; } } @@ -1284,7 +1331,7 @@ static int spcom_handle_write(struct spcom_channel *ch, ret = spcom_handle_enable_ssr_command(); break; default: - pr_err("Invalid Command Id [0x%x]\n", (int) cmd->cmd_id); + spcom_pr_err("Invalid Command Id [0x%x]\n", (int) cmd->cmd_id); ret = -EINVAL; } @@ -1308,7 +1355,7 @@ static int spcom_handle_get_req_size(struct spcom_channel *ch, uint32_t next_req_size = 0; if (size < sizeof(next_req_size)) { - pr_err("buf size [%d] too small\n", (int) size); + spcom_pr_err("buf size [%d] too small\n", (int) size); return -EINVAL; } @@ -1318,7 +1365,7 @@ static int spcom_handle_get_req_size(struct spcom_channel *ch, next_req_size = (uint32_t) ret; memcpy(buf, &next_req_size, sizeof(next_req_size)); - pr_debug("next_req_size [%d]\n", next_req_size); + spcom_pr_dbg("next_req_size [%d]\n", next_req_size); return sizeof(next_req_size); /* can't exceed user buffer size */ } @@ -1344,13 +1391,13 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { - pr_err("ch [%s] remote side not connect\n", ch->name); + spcom_pr_err("ch [%s] remote side not connect\n", ch->name); return -ENOTCONN; } /* Check param validity */ if (size > SPCOM_MAX_RESPONSE_SIZE) { - pr_err("ch [%s] invalid size [%d]\n", + spcom_pr_err("ch [%s] invalid size [%d]\n", ch->name, size); return -EINVAL; } @@ -1367,12 +1414,12 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, */ if (!ch->is_server) { timeout_msec = ch->response_timeout_msec; - pr_debug("response_timeout_msec = %d\n", (int) timeout_msec); + spcom_pr_dbg("response_timeout_msec:%d\n", (int) timeout_msec); } ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec); if (ret < 0) { - pr_err("rx error %d\n", ret); + spcom_pr_err("rx error %d\n", ret); goto exit_err; } else { size = ret; /* actual_rx_size */ @@ -1382,7 +1429,7 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, if (ch->is_server) { ch->txn_id = hdr->txn_id; - pr_debug("request txn_id [0x%x]\n", ch->txn_id); + spcom_pr_dbg("request txn_id [0x%x]\n", ch->txn_id); } /* copy data to user without the header */ @@ -1390,7 +1437,7 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, size -= sizeof(*hdr); memcpy(buf, hdr->buf, size); } else { - pr_err("rx size [%d] too small\n", size); + spcom_pr_err("rx size [%d] too small\n", size); ret = -EFAULT; goto exit_err; } @@ -1422,16 +1469,12 @@ static int spcom_handle_read(struct spcom_channel *ch, int ret = -1; if (size == SPCOM_GET_NEXT_REQUEST_SIZE) { - pr_debug("get next request size, ch [%s]\n", ch->name); ch->is_server = true; ret = spcom_handle_get_req_size(ch, buf, size); } else { - pr_debug("get request/response, ch [%s]\n", ch->name); ret = spcom_handle_read_req_resp(ch, buf, size); } - pr_debug("ch [%s] , size = %d\n", ch->name, size); - return ret; } @@ -1480,26 +1523,26 @@ static int spcom_device_open(struct inode *inode, struct file *filp) u32 pid = current_pid(); int i = 0; - pr_debug("open file [%s]\n", name); + spcom_pr_dbg("open file [%s]\n", name); if (strcmp(name, "unknown") == 0) { - pr_err("name is unknown\n"); + spcom_pr_err("name is unknown\n"); return -EINVAL; } if (strcmp(name, DEVICE_NAME) == 0) { - pr_debug("root dir skipped\n"); + spcom_pr_dbg("root dir skipped\n"); return 0; } if (strcmp(name, "sp_ssr") == 0) { - pr_debug("sp_ssr dev node skipped\n"); + spcom_pr_dbg("sp_ssr dev node skipped\n"); return 0; } ch = spcom_find_channel_by_name(name); if (!ch) { - pr_err("channel %s doesn't exist, load App first\n", name); + spcom_pr_err("ch[%s] doesn't exist, load app first\n", name); return -ENODEV; } @@ -1509,14 +1552,14 @@ static int spcom_device_open(struct inode *inode, struct file *filp) /* channel was closed need to register drv again */ ret = spcom_register_rpmsg_drv(ch); if (ret < 0) { - pr_err("register rpmsg driver failed %d\n", ret); + spcom_pr_err("register rpmsg driver failed %d\n", ret); mutex_unlock(&ch->lock); return ret; } } /* max number of channel clients reached */ if (ch->is_busy) { - pr_err("channel [%s] is BUSY and has %d of clients, already in use\n", + spcom_pr_err("channel [%s] is BUSY and has %d of clients, already in use\n", name, ch->num_clients); mutex_unlock(&ch->lock); return -EBUSY; @@ -1527,7 +1570,7 @@ static int spcom_device_open(struct inode *inode, struct file *filp) */ for (i = 0; i < SPCOM_MAX_CHANNEL_CLIENTS; i++) { if (ch->pid[i] == pid) { - pr_err("client with pid [%d] is already registered with channel[%s]\n", + spcom_pr_err("client with pid [%d] is already registered with channel[%s]\n", pid, name); mutex_unlock(&ch->lock); return -EINVAL; @@ -1584,30 +1627,31 @@ static int spcom_device_release(struct inode *inode, struct file *filp) int i = 0; if (strcmp(name, "unknown") == 0) { - pr_err("name is unknown\n"); + spcom_pr_err("name is unknown\n"); return -EINVAL; } if (strcmp(name, DEVICE_NAME) == 0) { - pr_debug("root dir skipped\n"); + spcom_pr_dbg("root dir skipped\n"); return 0; } if (strcmp(name, "sp_ssr") == 0) { - pr_debug("sp_ssr dev node skipped\n"); + spcom_pr_dbg("sp_ssr dev node skipped\n"); return 0; } ch = filp->private_data; if (!ch) { - pr_debug("ch is NULL, file name %s\n", file_to_filename(filp)); + spcom_pr_dbg("ch is NULL, file name %s\n", + file_to_filename(filp)); return -ENODEV; } mutex_lock(&ch->lock); /* channel might be already closed or disconnected */ if (!spcom_is_channel_open(ch)) { - pr_debug("ch [%s] already closed\n", name); + spcom_pr_dbg("ch [%s] already closed\n", name); mutex_unlock(&ch->lock); return 0; } @@ -1625,7 +1669,7 @@ static int spcom_device_release(struct inode *inode, struct file *filp) * release the sync_lock if applicable */ if (ch->active_pid == current_pid()) { - pr_debug("active_pid [%x] is releasing ch [%s] sync lock\n", + spcom_pr_dbg("active_pid [%x] is releasing ch [%s] sync lock\n", ch->active_pid, name); /* No longer the current active user of the channel */ ch->active_pid = 0; @@ -1642,7 +1686,7 @@ static int spcom_device_release(struct inode *inode, struct file *filp) ch->active_pid = 0; if (ch->rpmsg_rx_buf) { - pr_debug("ch [%s] discarting unconsumed rx packet actual_rx_size=%zd\n", + spcom_pr_dbg("ch [%s] discarting unconsumed rx packet actual_rx_size=%zd\n", name, ch->actual_rx_size); kfree(ch->rpmsg_rx_buf); ch->rpmsg_rx_buf = NULL; @@ -1672,29 +1716,27 @@ static ssize_t spcom_device_write(struct file *filp, int buf_size = 0; if (!user_buff || !f_pos || !filp) { - pr_err("invalid null parameters\n"); + spcom_pr_err("invalid null parameters\n"); return -EINVAL; } if (*f_pos != 0) { - pr_err("offset should be zero, no sparse buffer\n"); + spcom_pr_err("offset should be zero, no sparse buffer\n"); return -EINVAL; } if (!name) { - pr_err("name is NULL\n"); + spcom_pr_err("name is NULL\n"); return -EINVAL; } - pr_debug("write file [%s] size [%d] pos [%d]\n", - name, (int) size, (int) *f_pos); if (strcmp(name, "unknown") == 0) { - pr_err("name is unknown\n"); + spcom_pr_err("name is unknown\n"); return -EINVAL; } if (size > SPCOM_MAX_COMMAND_SIZE) { - pr_err("size [%d] > max size [%d]\n", + spcom_pr_err("size [%d] > max size [%d]\n", (int) size, (int) SPCOM_MAX_COMMAND_SIZE); return -EINVAL; } @@ -1702,10 +1744,10 @@ static ssize_t spcom_device_write(struct file *filp, ch = filp->private_data; if (!ch) { if (strcmp(name, DEVICE_NAME) != 0) { - pr_err("invalid ch pointer, command not allowed\n"); + spcom_pr_err("invalid ch pointer, command not allowed\n"); return -EINVAL; } - pr_debug("control device - no channel context\n"); + spcom_pr_dbg("control device - no channel context\n"); } buf_size = size; /* explicit casting size_t to int */ buf = kzalloc(size, GFP_KERNEL); @@ -1714,14 +1756,14 @@ static ssize_t spcom_device_write(struct file *filp, ret = copy_from_user(buf, user_buff, size); if (ret) { - pr_err("Unable to copy from user (err %d)\n", ret); + spcom_pr_err("Unable to copy from user (err %d)\n", ret); kfree(buf); return -EFAULT; } ret = spcom_handle_write(ch, buf, buf_size); if (ret) { - pr_err("handle command error [%d]\n", ret); + spcom_pr_err("handle command error [%d]\n", ret); kfree(buf); if (ch && ch->active_pid == current_pid()) { ch->active_pid = 0; @@ -1754,16 +1796,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, uint32_t buf_size = 0; u32 cur_pid = current_pid(); - pr_debug("read file [%s], size = %d bytes\n", name, (int) size); + spcom_pr_dbg("read file [%s], size = %d bytes\n", name, (int) size); if (strcmp(name, "unknown") == 0) { - pr_err("name is unknown\n"); + spcom_pr_err("name is unknown\n"); return -EINVAL; } if (!user_buff || !f_pos || (size == 0) || (size > SPCOM_MAX_READ_SIZE)) { - pr_err("invalid parameters\n"); + spcom_pr_err("invalid parameters\n"); return -EINVAL; } buf_size = size; /* explicit casting size_t to uint32_t */ @@ -1771,12 +1813,12 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, ch = filp->private_data; if (ch == NULL) { - pr_err("invalid ch pointer, file [%s]\n", name); + spcom_pr_err("invalid ch pointer, file [%s]\n", name); return -EINVAL; } if (!spcom_is_channel_open(ch)) { - pr_err("ch is not open, file [%s]\n", name); + spcom_pr_err("ch is not open, file [%s]\n", name); return -EINVAL; } @@ -1789,25 +1831,23 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, ret = spcom_handle_read(ch, buf, buf_size); if (ret < 0) { if (ret != -ERESTARTSYS) - pr_err("read error [%d]\n", ret); + spcom_pr_err("read error [%d]\n", ret); goto exit_err; } actual_size = ret; if ((actual_size == 0) || (actual_size > size)) { - pr_err("invalid actual_size [%d]\n", actual_size); + spcom_pr_err("invalid actual_size [%d]\n", actual_size); ret = -EFAULT; goto exit_err; } ret = copy_to_user(user_buff, buf, actual_size); if (ret) { - pr_err("Unable to copy to user, err = %d\n", ret); + spcom_pr_err("Unable to copy to user, err = %d\n", ret); ret = -EFAULT; goto exit_err; } - kfree(buf); - pr_debug("ch [%s] ret [%d]\n", name, (int) actual_size); if (ch->active_pid == cur_pid) { ch->active_pid = 0; @@ -1833,7 +1873,6 @@ static inline int handle_poll(struct file *file, int ret = 0; void __iomem *regs; - pr_debug("SPCOM_POLL_STATE - wait:%d, op:%d\n", op->wait, op->cmd_id); switch (op->cmd_id) { case SPCOM_LINK_STATE_REQ: @@ -1841,7 +1880,8 @@ static inline int handle_poll(struct file *file, reinit_completion(&spcom_dev->rpmsg_state_change); ready = wait_for_completion_interruptible( &spcom_dev->rpmsg_state_change); - pr_debug("ch [%s] link state change signaled\n", name); + spcom_pr_dbg("ch [%s] link state change signaled\n", + name); regs = ioremap_nocache(spcom_soc2sp_rmb_reg_addr, sizeof(u32)); if (regs) { @@ -1849,14 +1889,14 @@ static inline int handle_poll(struct file *file, regs); iounmap(regs); } else { - pr_err("failed to set register indicating SSR\n"); + spcom_pr_err("failed to set register indicating SSR\n"); } } op->retval = atomic_read(&spcom_dev->rpmsg_dev_count) > 0; break; case SPCOM_CH_CONN_STATE_REQ: if (strcmp(name, DEVICE_NAME) == 0) { - pr_err("invalid control device is used: %s\n", name); + spcom_pr_err("invalid control device: %s\n", name); return -EINVAL; } /* @@ -1866,27 +1906,26 @@ static inline int handle_poll(struct file *file, */ ch = file->private_data; if (!ch) { - pr_err("invalid ch pointer, file [%s]\n", name); + spcom_pr_err("invalid ch pointer, file [%s]\n", name); ret = -EINVAL; break; } if (op->wait) { reinit_completion(&ch->connect); ready = wait_for_completion_interruptible(&ch->connect); - pr_debug("ch [%s] connect signaled\n", name); + spcom_pr_dbg("ch [%s] connect signaled\n", name); } mutex_lock(&ch->lock); op->retval = (ch->rpdev != NULL); mutex_unlock(&ch->lock); - pr_debug("ch [%s] reported retval=%d\n", name, op->retval); break; default: - pr_err("ch [%s] unsupported ioctl:%u\n", op->cmd_id); + spcom_pr_err("ch [%s] unsupported ioctl:%u\n", op->cmd_id); ret = -EINVAL; } - pr_debug("name=%s, retval=%d, ready=%d\n", name, op->retval, ready); + spcom_pr_dbg("name=%s, retval=%d\n", name, op->retval); if (ready < 0) { /* wait was interrupted */ - pr_info("interrupted wait retval=%d\n", op->retval); + spcom_pr_info("interrupted wait retval=%d\n", op->retval); ret = -EINTR; } return ret; @@ -1902,7 +1941,7 @@ static long spcom_device_ioctl(struct file *file, int ret = 0; if (strcmp(name, "unknown") == 0) { - pr_err("name is unknown\n"); + spcom_pr_err("name is unknown\n"); return -EINVAL; } @@ -1917,7 +1956,7 @@ static long spcom_device_ioctl(struct file *file, ret = copy_from_user(&op, argp, sizeof(struct spcom_poll_param)); if (ret) { - pr_err("Unable to copy from user [%d]\n", ret); + spcom_pr_err("Unable to copy from user [%d]\n", ret); return -EINVAL; } @@ -1928,12 +1967,12 @@ static long spcom_device_ioctl(struct file *file, ret = copy_to_user(argp, &op, sizeof(struct spcom_poll_param)); if (ret) { - pr_err("Unable to copy to user [%d]\n", ret); + spcom_pr_err("Unable to copy to user [%d]\n", ret); return -EINVAL; } break; default: - pr_err("Unsupported ioctl:%d\n", ioctl); + spcom_pr_err("Unsupported ioctl:%d\n", ioctl); ret = -EINVAL; } @@ -1964,33 +2003,33 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable) void *priv; struct cdev *cdev; - pr_debug("Add channel [%s]\n", name); + spcom_pr_dbg("creating channel [%s]\n", name); mutex_lock(&spcom_dev->create_channel_lock); ch = spcom_find_channel_by_name(name); if (ch) { - pr_err("channel [%s] already exist\n", name); + spcom_pr_err("channel [%s] already exist\n", name); mutex_unlock(&spcom_dev->create_channel_lock); return -EBUSY; } ch = spcom_find_channel_by_name(""); /* find reserved channel */ if (!ch) { - pr_err("no free channel\n"); + spcom_pr_err("no free channel\n"); mutex_unlock(&spcom_dev->create_channel_lock); return -ENODEV; } ret = spcom_init_channel(ch, is_sharable, name); if (ret < 0) { - pr_err("can't init channel %d\n", ret); + spcom_pr_err("can't init channel %d\n", ret); mutex_unlock(&spcom_dev->create_channel_lock); return ret; } ret = spcom_register_rpmsg_drv(ch); if (ret < 0) { - pr_err("register rpmsg driver failed %d\n", ret); + spcom_pr_err("register rpmsg driver failed %d\n", ret); goto exit_destroy_channel; } @@ -2004,7 +2043,7 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable) priv = ch; dev = device_create(cls, parent, devt, priv, name); if (IS_ERR(dev)) { - pr_err("device_create failed\n"); + spcom_pr_err("device_create failed\n"); ret = -ENODEV; goto exit_free_cdev; } @@ -2014,7 +2053,7 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable) ret = cdev_add(cdev, devt, 1); if (ret < 0) { - pr_err("cdev_add failed %d\n", ret); + spcom_pr_err("cdev_add failed %d\n", ret); ret = -ENODEV; goto exit_destroy_device; } @@ -2034,7 +2073,7 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable) exit_unregister_drv: ret = spcom_unregister_rpmsg_drv(ch); if (ret != 0) - pr_err("can't unregister rpmsg drv %d\n", ret); + spcom_pr_err("can't unregister rpmsg drv %d\n", ret); exit_destroy_channel: /* empty channel leaves free slot for next time*/ mutex_lock(&ch->lock); @@ -2054,14 +2093,14 @@ static int spcom_register_chardev(void) ret = alloc_chrdev_region(&spcom_dev->device_no, baseminor, count, DEVICE_NAME); if (ret < 0) { - pr_err("alloc_chrdev_region failed %d\n", ret); + spcom_pr_err("alloc_chrdev_region failed %d\n", ret); return ret; } spcom_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(spcom_dev->driver_class)) { ret = -ENOMEM; - pr_err("class_create failed %d\n", ret); + spcom_pr_err("class_create failed %d\n", ret); goto exit_unreg_chrdev_region; } @@ -2070,7 +2109,7 @@ static int spcom_register_chardev(void) DEVICE_NAME); if (IS_ERR(spcom_dev->class_dev)) { - pr_err("class_device_create failed %d\n", ret); + spcom_pr_err("class_device_create failed %d\n", ret); ret = -ENOMEM; goto exit_destroy_class; } @@ -2082,11 +2121,11 @@ static int spcom_register_chardev(void) MKDEV(MAJOR(spcom_dev->device_no), 0), SPCOM_MAX_CHANNELS); if (ret < 0) { - pr_err("cdev_add failed %d\n", ret); + spcom_pr_err("cdev_add failed %d\n", ret); goto exit_destroy_device; } - pr_debug("char device created\n"); + spcom_pr_dbg("char device created\n"); return 0; @@ -2121,24 +2160,31 @@ static int spcom_parse_dt(struct device_node *np) u32 soc2sp_rmb_sp_ssr_bit = 0; /* Read SP HLOS SCSR RMB IRQ register address */ + ret = of_property_read_u32(np, "qcom,spcom-rmb-err-reg-addr", + &spcom_rmb_error_reg_addr); + if (ret < 0) { + pr_err("can't get rmb error reg addr\n"); + return ret; + } + ret = of_property_read_u32(np, "qcom,spcom-sp2soc-rmb-reg-addr", &spcom_sp2soc_rmb_reg_addr); if (ret < 0) { - pr_err("can't get sp2soc rmb reg addr\n"); + spcom_pr_err("can't get sp2soc rmb reg addr\n"); return ret; } ret = of_property_read_u32(np, "qcom,spcom-sp2soc-rmb-pbldone-bit", &sp2soc_rmb_pbldone_bit); if (ret < 0) { - pr_err("can't get sp2soc rmb pbl done bit\n"); + spcom_pr_err("can't get sp2soc rmb pbl done bit\n"); return ret; } ret = of_property_read_u32(np, "qcom,spcom-sp2soc-rmb-initdone-bit", &sp2soc_rmb_initdone_bit); if (ret < 0) { - pr_err("can't get sp2soc rmb sw init done bit\n"); + spcom_pr_err("can't get sp2soc rmb sw init done bit\n"); return ret; } @@ -2149,14 +2195,14 @@ static int spcom_parse_dt(struct device_node *np) ret = of_property_read_u32(np, "qcom,spcom-soc2sp-rmb-reg-addr", &spcom_soc2sp_rmb_reg_addr); if (ret < 0) { - pr_err("can't get soc2sp rmb reg addr\n"); + spcom_pr_err("can't get soc2sp rmb reg addr\n"); return ret; } ret = of_property_read_u32(np, "qcom,spcom-soc2sp-rmb-sp-ssr-bit", &soc2sp_rmb_sp_ssr_bit); if (ret < 0) { - pr_err("can't get soc2sp rmb SP SSR bit\n"); + spcom_pr_err("can't get soc2sp rmb SP SSR bit\n"); return ret; } @@ -2165,27 +2211,27 @@ static int spcom_parse_dt(struct device_node *np) /* Get predefined channels info */ num_ch = of_property_count_strings(np, propname); if (num_ch < 0) { - pr_err("wrong format of predefined channels definition [%d]\n", + spcom_pr_err("wrong format of predefined channels definition [%d]\n", num_ch); return num_ch; } if (num_ch > ARRAY_SIZE(spcom_dev->predefined_ch_name)) { - pr_err("too many predefined channels [%d]\n", num_ch); + spcom_pr_err("too many predefined channels [%d]\n", num_ch); return -EINVAL; } - pr_debug("num of predefined channels [%d]\n", num_ch); + spcom_pr_dbg("num of predefined channels [%d]\n", num_ch); for (i = 0; i < num_ch; i++) { ret = of_property_read_string_index(np, propname, i, &name); if (ret) { - pr_err("failed to read DT channel [%d] name\n", i); + spcom_pr_err("failed to read DT ch#%d name\n", i); return -EFAULT; } strlcpy(spcom_dev->predefined_ch_name[i], name, sizeof(spcom_dev->predefined_ch_name[i])); - pr_debug("found ch [%s]\n", name); + spcom_pr_dbg("found ch [%s]\n", name); } return num_ch; @@ -2194,7 +2240,7 @@ static int spcom_parse_dt(struct device_node *np) /* * the function is running on system workqueue context, * processes delayed (by rpmsg rx callback) packets: - * each paket belong to its destination spcom channel ch + * each packet belong to its destination spcom channel ch */ static void spcom_signal_rx_done(struct work_struct *ignored) { @@ -2212,7 +2258,7 @@ static void spcom_signal_rx_done(struct work_struct *ignored) spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); if (!rx_item) { - pr_err("empty entry in pending rx list\n"); + spcom_pr_err("empty entry in pending rx list\n"); spin_lock_irqsave(&spcom_dev->rx_lock, flags); continue; } @@ -2224,13 +2270,13 @@ static void spcom_signal_rx_done(struct work_struct *ignored) ch->comm_role_undefined = false; ch->is_server = true; ch->txn_id = hdr->txn_id; - pr_debug("ch [%s] first packet txn_id=%d, it is server\n", + spcom_pr_dbg("ch [%s] first packet txn_id=%d, it is server\n", ch->name, ch->txn_id); } if (ch->rpmsg_abort) { if (ch->rpmsg_rx_buf) { - pr_debug("ch [%s] rx aborted free %zd bytes\n", + spcom_pr_dbg("ch [%s] rx aborted free %zd bytes\n", ch->name, ch->actual_rx_size); kfree(ch->rpmsg_rx_buf); ch->actual_rx_size = 0; @@ -2238,17 +2284,20 @@ static void spcom_signal_rx_done(struct work_struct *ignored) goto rx_aborted; } if (ch->rpmsg_rx_buf) { - pr_err("ch [%s] previous buffer not consumed %zd bytes\n", + spcom_pr_err("ch [%s] previous buffer not consumed %zd bytes\n", ch->name, ch->actual_rx_size); kfree(ch->rpmsg_rx_buf); ch->rpmsg_rx_buf = NULL; ch->actual_rx_size = 0; } if (!ch->is_server && (hdr->txn_id != ch->txn_id)) { - pr_err("ch [%s] rx dropped txn_id %d, ch->txn_id %d\n", + spcom_pr_err("ch [%s] rx dropped txn_id %d, ch->txn_id %d\n", ch->name, hdr->txn_id, ch->txn_id); goto rx_aborted; } + spcom_pr_dbg("ch[%s] rx txn_id %d, ch->txn_id %d, size=%d\n", + ch->name, hdr->txn_id, ch->txn_id, + rx_item->rx_buf_size); ch->rpmsg_rx_buf = rx_item->rpmsg_rx_buf; ch->actual_rx_size = rx_item->rx_buf_size; ch->rx_buf_txn_id = ch->txn_id; @@ -2277,17 +2326,16 @@ static int spcom_rpdev_cb(struct rpmsg_device *rpdev, unsigned long flags; if (!rpdev || !data) { - pr_err("rpdev or data is NULL\n"); + spcom_pr_err("rpdev or data is NULL\n"); return -EINVAL; } - pr_debug("incoming msg from %s\n", rpdev->id.name); ch = dev_get_drvdata(&rpdev->dev); if (!ch) { - pr_err("%s: invalid ch\n", __func__); + spcom_pr_err("%s: invalid ch\n"); return -EINVAL; } if (len > SPCOM_RX_BUF_SIZE || len <= 0) { - pr_err("got msg size %d, max allowed %d\n", + spcom_pr_err("got msg size %d, max allowed %d\n", len, SPCOM_RX_BUF_SIZE); return -EINVAL; } @@ -2306,8 +2354,6 @@ static int spcom_rpdev_cb(struct rpmsg_device *rpdev, spin_lock_irqsave(&spcom_dev->rx_lock, flags); list_add(&rx_item->list, &spcom_dev->rx_list_head); spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); - pr_debug("signaling rx item for %s, received %d bytes\n", - rpdev->id.name, len); schedule_work(&rpmsg_rx_consumer); return 0; @@ -2319,14 +2365,14 @@ static int spcom_rpdev_probe(struct rpmsg_device *rpdev) struct spcom_channel *ch; if (!rpdev) { - pr_err("rpdev is NULL\n"); + spcom_pr_err("rpdev is NULL\n"); return -EINVAL; } name = rpdev->id.name; - pr_debug("new channel %s rpmsg_device arrived\n", name); + spcom_pr_dbg("new channel %s rpmsg_device arrived\n", name); ch = spcom_find_channel_by_name(name); if (!ch) { - pr_err("channel %s not found\n", name); + spcom_pr_err("channel %s not found\n", name); return -ENODEV; } mutex_lock(&ch->lock); @@ -2341,7 +2387,7 @@ static int spcom_rpdev_probe(struct rpmsg_device *rpdev) /* used to evaluate underlying transport link up/down */ atomic_inc(&spcom_dev->rpmsg_dev_count); if (atomic_read(&spcom_dev->rpmsg_dev_count) == 1) { - pr_err("Signal link up\n"); + spcom_pr_info("Signal link up\n"); complete_all(&spcom_dev->rpmsg_state_change); } @@ -2354,14 +2400,14 @@ static void spcom_rpdev_remove(struct rpmsg_device *rpdev) int i; if (!rpdev) { - pr_err("rpdev is NULL\n"); + spcom_pr_err("rpdev is NULL\n"); return; } dev_info(&rpdev->dev, "rpmsg device %s removed\n", rpdev->id.name); ch = dev_get_drvdata(&rpdev->dev); if (!ch) { - pr_err("channel %s not found\n", rpdev->id.name); + spcom_pr_err("channel %s not found\n", rpdev->id.name); return; } @@ -2370,7 +2416,7 @@ static void spcom_rpdev_remove(struct rpmsg_device *rpdev) if (strcmp(ch->name, "sp_kernel") == 0) { for (i = 0; i < ARRAY_SIZE(ch->dmabuf_handle_table); i++) { if (ch->dmabuf_handle_table[i] != NULL) { - pr_debug("unlocked ion buf #%d fd [%d]\n", + spcom_pr_dbg("unlocked ion buf #%d fd [%d]\n", i, ch->dmabuf_fd_table[i]); dma_buf_put(ch->dmabuf_handle_table[i]); ch->dmabuf_handle_table[i] = NULL; @@ -2387,7 +2433,7 @@ static void spcom_rpdev_remove(struct rpmsg_device *rpdev) /* used to evaluate underlying transport link up/down */ if (atomic_dec_and_test(&spcom_dev->rpmsg_dev_count)) { - pr_err("Signal link down\n"); + spcom_pr_err("Signal link down\n"); complete_all(&spcom_dev->rpmsg_state_change); } } @@ -2401,8 +2447,8 @@ static int spcom_register_rpmsg_drv(struct spcom_channel *ch) int ret; if (ch->rpdrv) { - pr_err("ch:%s, rpmsg driver %s already registered\n", ch->name, - ch->rpdrv->id_table->name); + spcom_pr_err("ch:%s, rpmsg driver %s already registered\n", + ch->name, ch->rpdrv->id_table->name); return -ENODEV; } @@ -2420,7 +2466,7 @@ static int spcom_register_rpmsg_drv(struct spcom_channel *ch) drv_name = kasprintf(GFP_KERNEL, "%s_%s", "spcom_rpmsg_drv", ch->name); if (!drv_name) { - pr_err("can't allocate drv_name for %s\n", ch->name); + spcom_pr_err("can't allocate drv_name for %s\n", ch->name); kfree(rpdrv); kfree(match); return -ENOMEM; @@ -2433,7 +2479,7 @@ static int spcom_register_rpmsg_drv(struct spcom_channel *ch) rpdrv->drv.name = drv_name; ret = register_rpmsg_driver(rpdrv); if (ret) { - pr_err("can't register rpmsg_driver for %s\n", ch->name); + spcom_pr_err("can't register rpmsg_driver for %s\n", ch->name); kfree(rpdrv); kfree(match); kfree(drv_name); @@ -2512,9 +2558,14 @@ static int spcom_probe(struct platform_device *pdev) pr_err("create character device failed\n"); goto fail_reg_chardev; } - pr_debug("Driver Initialization ok\n"); - return 0; + spcom_ipc_log_context = ipc_log_context_create(SPCOM_LOG_PAGE_CNT, + "spcom", 0); + if (!spcom_ipc_log_context) + pr_err("Unable to create IPC log context\n"); + + spcom_pr_dbg("Driver Initialization ok\n"); + return 0; fail_reg_chardev: pr_err("failed to init driver\n"); spcom_unregister_chrdev(); @@ -2544,7 +2595,7 @@ static int __init spcom_init(void) ret = platform_driver_register(&spcom_driver); if (ret) - pr_err("spcom_driver register failed %d\n", ret); + spcom_pr_err("spcom_driver register failed %d\n", ret); return ret; } diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 8343023af06ed30b6fee779eceb6f1d28d475c47..7b8485ed2d5e77a76f49f1aeceb394b8b8a28d55 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 @@ -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 df3ccb30bc2dddba0d2d6accccdd6a6c5a7bc53c..373400dd816d6802378dcf5bc7373fb921b22343 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 caf894f193edcc6446165d2d763c98fd090da540..77422baa7a56a494d7fec1d5d32cc9ac11b6e81b 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 9265fb525ef342281546683ff2d0dcf13e335f25..a8dbe55e8ba82d7e40d6c2cdf58fa26796834513 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 15579ebc5ed2059dbace28ba6137614e0a4c81e7..664b244eb1dd9d95fabe3305334784f69213c90c 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 4b452f36f05471409c19ea2cc45e9e2a78cf1c14..f17a678154047ed2fc935d8e96ce7ef9e31a6dac 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); + + /* update I/O voltage */ + value = tegra_pmc_readl(PMC_PWR_DET_VALUE); - tegra_pmc_writel(value, 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 1ba1556f1987827fb4099e8e60f6ad41446a8699..c7708feaa62e332c8c215ebd4e356df654050ad2 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 dcc0ff9f0c224e93d88a05aa933b293c4390d559..df172bf3925f6415747c6d4f1b35928432e95270 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_init.c b/drivers/soundwire/intel_init.c index d1ea6b4d0ad306e8413bc81a4e6d4bfebd839924..5c8a20d998786459afa4f4c7d8531c20e8d92631 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 3f890d16293411ba86618d54d4f8f70887aec31d..2fb43c582559de0b9e1b8571fcf015246ca0b374 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 c23849f7aa7bc673bd9cba50ca8755b77742ab34..9a06ffdb73b88641e68da218e63f59ba15704ebf 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-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e6d5cc6ab108b190e4551994d3f21f355d9e08c1..51670976faa35b668152a35b69211e802cc613d6 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-mt65xx.c b/drivers/spi/spi-mt65xx.c index 86bf45667a0402c43ab76057dc01d2527f35457c..0c2867deb36fce48c74b92388d210371ebd1a6d0 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 e2be7da7434389fed49f8c7298bbd2cbc5bdeeea..eb2d2de172af3da4ded93b5f1a613392ec402247 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 f8a45af1fa9f2434689b7946435d26859f895306..288002f6c613ee81a5a421568b86ef06561da332 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-rockchip.c b/drivers/spi/spi-rockchip.c index 4b77fa187f3f591898d0bf771a640927ad8943e9..e05426079b4df27562d9ddcf9925ed729ad5a78e 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 101cd6aae2ea520afcac89671071cdabe2341f8f..30ea0a2068e094ab47822b63da58da4778858ee3 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/spidev.c b/drivers/spi/spidev.c index cda10719d1d1b21b32866d2b79363faa461ab8e1..c5fe08bc34a0aedf1dcd533ba68edfc4f3bd29c8 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -724,11 +724,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/ion/ion_secure_util.c b/drivers/staging/android/ion/ion_secure_util.c index e15c90ba34f1592ce01c03266b875b6a0afbaaad..c0b4c4d45c558128d442a25e608507c8f3da93d4 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 d029608c64746e0d5874d0d3ee79f9d070b2d2b0..c667518f090bd469439fd8d02a8dbd6edfb8059d 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]); } diff --git a/drivers/staging/android/ion/ion_system_secure_heap.c b/drivers/staging/android/ion/ion_system_secure_heap.c index f0d8d727e8bf48f10846bc8b5c09acf37f1fd025..1971385b89a44fb0a2788b34d39eb33fdf0fe263 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 @@ -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/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c index e18c0723b760c80763e235489c32e1c98c4639fb..0d38589c2600729181b9386ed0d5ef937a08ee9a 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 - * 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 2db99cff3c992f480e7dd6c4b0434048e99f3923..d48687ca21990279a8bead27bf2b12d9524d2360 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/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c index f66dd3ebbab1fe787fccc180b70441a4e598ab09..856bcfa60c6c47220984ea16fb3fae1aa62ba21f 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 d17ce1fb4ef51faef27bb3a8112e2966e885dc44..0f8fdc347091b5fb6726b7909ad8ea94c2f32cd4 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 37cf9c3273bebca6b157ff301563709badde200d..fc361271130729cf8c21548fee9ab048c2e47aec 100644 --- a/drivers/staging/mt7621-pinctrl/Kconfig +++ b/drivers/staging/mt7621-pinctrl/Kconfig @@ -2,3 +2,4 @@ config PINCTRL_RT2880 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 aa98fbb170139ef7ba443d80ae2c8da72e9a4357..80e7067cfb797a3361148137b53f513a02fc3e0b 100644 --- a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c +++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -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/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index dfee6985efa6126cb652fc96bad1bfd352aa764c..55952dd883598798e5317a26fe61df33823725fb 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 d2605158546bb54c2985ea1bf1c0dffa185ba63d..7cdced0b0581ef3c4e4fcdae9b4676b5d7897208 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/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index 85eadddfaf06092e1525c762c9c0334418844d01..5e2cdc25401bad4a76333fa969d73a2ac7d5fed6 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 6d02904de63f393d7eca2c6031ea24051993c880..49ea780f9f42a71adbb71abe95f1d1dbbf9f737c 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 b19c960d549079b287ebe965acfb08b2eeef11fd..d46eee3698640acc640d162e7dd5f6629cc0365a 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/target_core_device.c b/drivers/target/target_core_device.c index 47b5ef153135cbbe89d8a1395b75ff94054ef6f4..e9ff2a7c0c0e67af564b951d1a4a4fb3f468b530 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1128,27 +1128,6 @@ passthrough_parse_cdb(struct se_cmd *cmd, struct se_device *dev = cmd->se_dev; 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 7159e8363b83b0ef624542237474e7330703b415..7ee0a75ce4526518dcb4ed18d1bed5e2a357ebf1 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 e1aafe842d660bb11b1146133105f37bff24873e..2f254f957b0af3df3c7d2e64dba1cbbc57a24f3c 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 e16b3cb1808c589dc8d2e50e426e9f973138429f..1c9830b2c84da97d808a2cd099f3d9ded3175edc 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 c5820e598f353f5085a32e1eb2a84245e9e2a552..71785f2fad39faeb54765b290ab0d9e93463ec81 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 343a6310a90205fbd8751116456089163e9f3fd3..89cb5ced5846726d59b696df9dc3d4011cc96e94 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 537912ca5f421f8cd7be3ade93c0342898c60917..9306105385558e388b9635ffdb0ac36d1b8b1374 100644 --- a/drivers/thermal/qcom/adc-tm.c +++ b/drivers/thermal/qcom/adc-tm.c @@ -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) @@ -165,6 +198,9 @@ 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 +260,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 +272,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 +287,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 +339,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 46f6db4680173d2706802101bdaf77b3b727124f..b616aff934fc93f2ce64ba36213ad19cfa20bc34 100644 --- a/drivers/thermal/qcom/adc-tm.h +++ b/drivers/thermal/qcom/adc-tm.h @@ -15,7 +15,7 @@ #include #include -#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 286be8cf0a2f724e337e674d0dd9f2802793882b..3371802c41cbdb8a6dcba3a4f9150103e2de28f5 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 0000000000000000000000000000000000000000..4e8c87fec5fe47f620eb5b107fd863ff0ab08ec4 --- /dev/null +++ b/drivers/thermal/qcom/adc-tm7.c @@ -0,0 +1,834 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 8df2ce94c28d86b80a337487e5a1c602fda2f4d7..4dc30e7890f6c4c99eed6a8109ffd7aaf880d2a0 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 5cd6bdfa068f9bc91fe2e83bedcd4e81160230c4..d436a1534fc2bb1f0be976b769b0dc7e8a6929c4 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 bc7efa6e515d08400f14e4083faf1c556d662f2f..678bf336594707439914939ec6f7f8b868228236 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: @@ -1358,6 +1378,19 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) if (!sw->dma_port) 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 @@ -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/n_hdlc.c b/drivers/tty/n_hdlc.c index bb63519db7ae493365fb919ca5a5218342d0968e..0636e10c76c7fe3ffc7bcdcb398e8f6aa16238d5 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 dbf1ab36758ebd6404e97a2fb0fbc4273f6c0460..a3969b773cbe42a98d2188d2a0f5e6719e88faa2 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 fd031c14fe411aa35b348b1e55ebe09aa71b3a90..31756015d074ba12a3a6df7bdd91680e718d9f47 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -847,7 +847,12 @@ static void continue_process_echoes(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, echo_delayed_work.work); - struct n_tty_data *ldata = tty->disc_data; + struct n_tty_data *ldata; + + /* Possible for n_tty_close() is called here and free the disc_data */ + ldata = tty->disc_data; + if (!ldata) + return; mutex_lock(&ldata->output_lock); tty->delayed_work = 0; @@ -1730,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 @@ -1772,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; @@ -1918,6 +1923,11 @@ static void n_tty_close(struct tty_struct *tty) if (tty->link) n_tty_packet_mode_flush(tty); +#if defined(CONFIG_TTY_FLUSH_LOCAL_ECHO) + if (tty->echo_delayed_work.work.func) + cancel_delayed_work_sync(&tty->echo_delayed_work); +#endif + vfree(ldata); tty->disc_data = NULL; } @@ -2239,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; } @@ -2393,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 678406e0948b227ab0188d938450b384e4dbae82..00099a8439d219184c131f5d24c1e60fa2c20285 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -28,6 +28,7 @@ #include #include #include +#include #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 8fe3d0ed229ed27c9bdb53ade9c356aaed1abc85..69aaee5d7fe141984eeff400a2f0bd53366232a2 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 6640a4c7ddd1dd5e10f96049249ed97595a365c0..bb9571eed275d10cc51b7872a5f203d57a8e5f80 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 127017cc41d9296042b82acf2026ceb7d84fcf89..057b1eaf6d2ebfddfcf8b5bcfc7bc8c929c09dc6 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 98125de2f0a6c615351db1525f84c03393f745d4..2488de1c4bc4b9ddeaff6f4f572806537b9be795 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 89ade213a1a9a66c3fa1b1956135456ded401f7b..af21122dfadec14e15dd200583f0a5f35455144d 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/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 50b6746a8b5d7b60805929ee6b5c87de25408b0c..ee8a5cb61a5f10a3956c6dddead1b33183d04886 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 ffefd218761e04c633171b3f1b14151e7f05b721..31033d517e828eca8058a4eb8e094871b1169eb1 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 105de92b0b3bfa5f48b04b773ecaa1154a51a5c4..989ca7d662f3fa18fea74d3aa41ffca96362124f 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 bd3e6cf81af5cfb61d82d70e13dd59015a623aee..0c35c3c5e37349953df8fe1daff97a0b94bd1394 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 8e27de96bae57697903216e6c07b49919e706a15..b707dff5469b882241e58362f7930ac9665c45d0 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -23,6 +23,7 @@ #include #include #include +#include /* UART specific GENI registers */ #define SE_UART_LOOPBACK_CFG (0x22C) @@ -1091,7 +1092,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 +1099,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 +1125,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 +1259,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 +1451,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 +1479,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 +1767,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 +1793,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 +1823,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 +1836,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; } @@ -2805,6 +2795,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 310bbae515b048ce574397798dabc32a2f119318..c10d9cc553c60bff08c10bd0a527cdc4ac2043c7 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) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 34acdf29713d75073ee513fb2a9bacf442734ea9..4c188f4079b3ea68ee51982b41d4b14ba27567c4 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 29a6dc6a8d23c7350fb77a71da9949d9c5b659bf..73fcc6bdb031220ff79272ddaa135d4b98ea4bb5 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 5b96df4ad5b30fb746af7df58b955dea2a17769b..b3f7d1a1e97f824e4b4793ec30c677432261954a 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 c6058b52d5d597210f86a1125f4867d8a14d3d6a..2a49b6d876b87b0bdf23ce1f85bfb77bd44c0e02 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 372cc7ff228fce672ad3399448a890621ae1a485..ebea4a9d8e694bddf7789a3ee751925b6c30d973 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 fe9261ffe3dbcfd96703530d6f90e2210b3a7554..dc56cddca6dc8db7638252c59dcb33ee2442d8ac 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 07f318603e7401c1b0420fc937a1f66f08afc448..af0412a784d2786ebc5adb5e336c82858258973c 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 5550289e6678b87e71cdb8bca2930c719c27f94e..9e1a6af23ca2b1257ebcaab83cb80a2c7935ded3 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 77efa0a43fe768089c064ab127d8cc2097915b9f..66d49d511885354cdb927501fd275054c4080e22 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; - - /* 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); - } + int may_wake; - /* - * 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)) { + may_wake = device_may_wakeup(device); + + 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; - - /* 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); - } + int may_wake; + + 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 a94086597ebd682a46f537b1c62b2f3b2a8b17e9..b88ecf102764e43e532a461f148ad97c282bea78 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 7c7217a69da5479be7bce25bab001483c6a4686a..c2c0aa57cc0d99d2dd6a19322e0f0fbfd2c83599 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 0617e87ab343925f9568eef6a9a508afe68b0ecc..8d6074f8ba41f291d87ce9df6254a3fbc077701a 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 2384ea85ffafed31c9450481125dc7740a4b387c..2fb509d57e88c4ee5d1e99b00086ff11ccaec361 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 e401be8321ab573b0d4c973fb7ac3f673de0f49b..170fa1f8f00e049d0565041036cbe786b166472e 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 2754b4ce7136c65039a5f2b65ec36e4992815404..60e292ce04f7d1a547984f5477cc2fa3561e6726 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 db4ceffcf2a6118c9063fa720830cc337a6f92c3..f25d4827fd49c41179326f517b8aeaa7999f54dd 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 34ad5bf8acd8d0ee58cba9c1a5efadace009b9a0..424ecb1f003feb1f8e6cdc01b12d6277f85326f7 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 83ffa5a14c3dbbc53e8b964a4101e6e4a19c57a5..e6a7c86b70f250ee4077948b2bdbcb2ec3d868d4 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 08e6fea48c3a4bbdfe7b212371e27e38017f746d..52e2bd57cad0947deed2bcbb8f4371b73d68b31d 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -348,6 +348,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 */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4884591b39e137ed47fdffbe0360784a645df7f0..9301706de5ee63774d6d65caedc2b55eb8ef5f30 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 @@ -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 f51750bcd1528ba5aa3c8996b9dd0bc320ce8beb..5e844097a9e30929f33e72e93559af975b5f662e 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 51d83f77dc04ac447789eebb602c80f3a17f2bab..633ba0123efbff959a75f88aac63f2fcfea3a0bf 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 aad7edc29bddd4d1b14febcd098e1dfde2b90885..a5c8329fd4625dab8517c9debe6b127d2f5bdf1c 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 dff2c6e8d797cb2b8de59de5fa6bcd4654ee7522..a93415f33bf3692fe26c2d4bec80b524eb66540f 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 8a0f116739e01a780574481a742ad56d8d22586e..818a358b4dcff3bf0de2bc72c3b04e70b5bd8f47 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 9dcbfc5706f815688bebdf2a4911048967924fe8..d28be098c2eb23bf415ba4e4c0a7aba520a9f675 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 f1659b70fa117e8cca4de8f5fc7fd3d57e3ad291..dc71517c22eb9cb41a15743c5001fcc165294d11 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -154,6 +154,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 9e47b005e095d8d09b9a7c69b081384682a4de5e..c5b7974e6cc2aba138dc6195e46ad3c438d7804f 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 81d667e9fd248fd4bba1ca5642782ea316ab7811..bfeae6b345442b2be70aa376b9a8b57569705bee 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 @@ -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: diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8cced3609e243b186caedd3eeb79e338c6151a73..8fa39e66494057cd478815b58c4d7381198f09f6 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 76b3fb41495752c7c38609540f977016b3712889..1b70b8da2a7208e8337faa0e59becd346323df02 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 d80c410568bae8b963418b3f3acc5ab3a29dfcf1..8794971825c681321f73152ed1c3eda2a9990a90 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; @@ -817,7 +813,7 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) if (dep->number == 1 && dwc->ep0state != EP0_SETUP_PHASE) { unsigned int dir; - dbg_log_string("CTRLPEND", dwc->ep0state); + dbg_log_string("CTRLPEND(%d)", dwc->ep0state); dir = !!dwc->ep0_expect_in; if (dwc->ep0state == EP0_DATA_PHASE) dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); @@ -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; } @@ -2893,11 +2902,10 @@ 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; @@ -3034,8 +3042,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: @@ -3308,7 +3319,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) if (dwc->ep0state != EP0_SETUP_PHASE) { unsigned int dir; - dbg_event(0xFF, "CONTRPEND", dwc->ep0state); + dbg_event(0xFF, "CONTRPEND(%d)", dwc->ep0state); dir = !!dwc->ep0_expect_in; if (dwc->ep0state == EP0_DATA_PHASE) dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); @@ -3317,6 +3328,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_ep0_stall_and_restart(dwc); } + dwc->delayed_status = false; dwc3_stop_active_transfers(dwc); dwc3_clear_stall_all_ep(dwc); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index aa1ec8ad388fc8a8358b4f9b9ff2259e662550cc..90e166e88dc8147c1db09e1ba10265df53d01e73 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 e1309b00bb10fec3a1801cf7f13deb9d2a4428a1..97c39be57c0fbb254c8a8efab799f291c529d359 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1901,7 +1901,11 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[intf]; if (!f) break; - status = f->get_status ? f->get_status(f) : 0; + + if (USB_CONFIG_ATT_WAKEUP & cdev->config->bmAttributes) + status = f->get_status ? f->get_status(f) : 0; + else + status = 0; if (status < 0) break; put_unaligned_le16(status & 0x0000ffff, req->buf); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 22ca9c2d631c8e7a9fdc6133530a9ce08f9e832b..3006274daf0d024683f5ccf5c2c28af5171c188b 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 54d6ccc38001ba9c6007931876d57dd4bd75c61e..591bff983a2571195e8bd5ce965076f0dc6a8fa1 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/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 29436f75bbe06b5e94df5a063a1a5222c3b6509d..d4d317db89df5ded4af8ee8979a1214bb68eb2ab 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 b51f0d27882697504707c3d00c9f72fb0b6e87b6..2e4c0391b583652aed360dc43384d829b8ee4c57 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -9,6 +9,9 @@ * * Author: Andrzej Pietrasiewicz */ + +#include + #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 d3567b90343a4ab57cd185360b30761307304e3a..2c9821ec836e771e3c22b0899da60974ff2417fb 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 8f267be1745d82985c4185b1051e75b867812c4d..a4ab230335786853798e137b168fb784e38aa2aa 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 808cefe9ba5e15b570116f7b6bbefab147518c01..4e944c04be419d601b814306fe05d0ec197acd19 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/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index 587c5037ff079e0d74984703c91cc5dbca8428cd..bc6abaea907d88db2555afdd9549d1486a96498f 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 be59309e848c335fafc8301eaec211f3fec9657e..d44b26d5b2a2c5b4b26c54018a692dccee36bb17 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 afaea11ec7719c3350080f010777a3d0f81ef925..991184b8bb41ebd7b43e83550f0f7bb8254fe390 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 7e4c13346a1ee154240c9beb9cfc91836da50a0e..7d20296cbe9f9459d31742a29049c4c9c518b27e 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) + ret = PTR_ERR(phy); + if (ret == -ENODEV) { /* no PHY */ + phy = NULL; continue; + } - ret = PTR_ERR(phy); if (ret != -EPROBE_DEFER) dev_err(dev, "Can't get PHY for port %d: %d\n", i, ret); diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 7ba6afc7ef230ed69da605ce48e31cac4783a709..76c3f29562d2b9d2e3263f3d3dc1b45b92728140 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 1b7ada8a1f115d03186744908b41759e7e2e0c79..e4c377218ba8038c8d16a4187f69b67d0a9f4948 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 9b8d9108717fb88a0b780756d178b54f7e90825c..ecf73f6c08d6bc57ff94e5f24acc5b094d6cf93c 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 fa33d6e5b1cbd8aaab17c4995088d1b544530530..d04fdd173ed2eff9bd77740cf69e1fc847b8ac7b 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 1493d0fdf5ad13079b7ab495f622a92374fb65ba..74aeaa61f5c6a0a47592e3f9c4ea5dd025d7d2ca 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -495,6 +495,18 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) } #endif /* CONFIG_PM */ +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); +} + /*-------------------------------------------------------------------------*/ /* PCI driver selection metadata; PCI hotplugging uses this */ @@ -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 edef3e21477c51233581a2b10c803da03c3fcbfd..eff69c2db10d35ec49ab693e7a2875f478dfc0b7 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 8b73bfe98820af740e2bb91a95a62dfacd95c661..c65237ba1a7f657f59be1c46477df01ff39a71b3 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 1d8c40f982b5318724ccb4145925de2a82d5969b..076fdd5408a2db66386fdf422d68ac6f9c3df263 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 9a51760df0266a293522c2ba84e1776f1b308117..b8073f36ffdc6cff2d5cf7a47771bbeec7afdcb7 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 1c6da8d6cccf821190e0b4cf03707346356d7191..718d692b07ac8ab50de231ed59901df18631fc4f 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 34e6cd6f40d3005fbda6dc428be6c7e19fccf6bb..87067c3d6109b966fb7a35be4e0ed6da9f15c9af 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 20b0f91a5d9b6921e0afb1d33b1dc9bf34a6bae4..bb24527f3c7010854c9cf58a75e9c95ecf94cd5c 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 6b3a6fd7d271dd021fb3cac3dccfe7938c3c604f..67c1b8f5d54d6088c973198e61d1080d29191fad 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 62dab2441ec4fcb9b005070d20e335d0ed84b8df..23061f1526b4e03be818de50add926e982187ae2 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 ac2b4fcc265f65c10a128bcf092de3a134319dc8..f48a23adbc35ddbbc66c5227d8c59e3413791b05 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 48d10a61e271c9cd84c1ee534796afdaf4708df3..86069352013262d04b3ca4e884c152cfdf238906 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 5c60a8c5a0b5c7cef25ca2fcbf2ac396c1b5b6b5..bbcd3332471dc2217042e62527b12745c48142e6 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 ff62ba2321779c3a0ae92b517317d12be8fed0f3..326b40747128c2d5902de0050c9b75b2ac792cf0 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/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index ce1afb06167d07881b5a2ac55ead1b77ba966adb..0b488b1199a8d55a4a0391fb443613c8a93076e0 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.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 @@ -561,6 +561,11 @@ static void qusb_phy_shutdown(struct usb_phy *phy) dev_dbg(phy->dev, "%s\n", __func__); + if (qphy->eud_enable_reg && readl_relaxed(qphy->eud_enable_reg)) { + dev_err(qphy->phy.dev, "eud is enabled\n"); + return; + } + qusb_phy_enable_clocks(qphy, true); /* Disable the PHY */ @@ -647,15 +652,20 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_INTR_CTRL); - /* Disable PHY */ - writel_relaxed(POWER_DOWN | readl_relaxed(qphy->base + + if (!qphy->eud_enable_reg || + !readl_relaxed(qphy->eud_enable_reg)) { + /* Disable PHY */ + writel_relaxed(POWER_DOWN | + readl_relaxed(qphy->base + QUSB2PHY_PORT_POWERDOWN), qphy->base + QUSB2PHY_PORT_POWERDOWN); - /* Make sure that above write is completed */ - wmb(); + /* Make sure that above write is completed */ + wmb(); - if (qphy->tcsr_clamp_dig_n) - writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); + if (qphy->tcsr_clamp_dig_n) + writel_relaxed(0x0, + qphy->tcsr_clamp_dig_n); + } qusb_phy_enable_clocks(qphy, false); qusb_phy_enable_power(qphy, false); diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 99116af07f1d9ca1e7c275a70122f370ec0d32a3..1dd492e89719e501d6bbd621339ba047fde6ea42 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 e732949f65670e3b48339bd389b9e8837fe4590f..7ae121567098c9442350a9e140322754b3f6a18b 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 e0035c0231202da04e88830e9b2c3e11e8416aad..2c58649fd47a4d121ebbd2aecdb0b2e98e8c3091 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 4dfbff20bda4cbfb354aa9efcad90f5e4ce30461..db6c93c04b3c8fd52e433930b3b10b3aadabb827 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 f06706efb7f14d0cdb741821bdf30a7d69c3ce2e..3c0f38cd3a5a478225e26bb0f30b437d1316112b 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 22d66217cb412b99689ecc18f517066433fc337b..e8373528264c3634a9e474f2836a82d2b5b8b7a5 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 97c69d373ca6544d484f7ea246aebb13e703f1af..97eb738b058fa85463d2c46dc23f45625a67d685 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 e8f275a0326df0055a316eea2cb090e1e3de03f6..c0232b67a40f4b975d46931b05377c4dbdea9e3e 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 b42bad85097ae853a49740a87356ec284bde2482..4a7bd26841af00b52b9d022e096800556fbaccc6 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 3cc659a62782e46b87ac183a9073ef4ed139aa9a..2905274e36264ec3be7540f2f41b80b8dcdf8967 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) }, @@ -1990,6 +1993,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 1c7b46a8620cc039847bcdbbb814f76576a53e41..16b275123f10eb2798f7b7b77f9a3580572f6600 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 72c1b0cf406314970f324f3f27a4dab4659f64eb..56a3e8323f330fcd3d691e4732965fdae94b3088 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 59d82b45e758bb93cdb7dc41e27bdbcc4b21482f..f287ee8183df7aba9b83c3c8640619c61110e8f4 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 @@ -74,15 +73,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 5b1d09367475100bfc0b8c8cb53a9dfe6f31085f..1c6eb3a8741ee52bae713a65495a3c23f64d2a79 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -796,29 +796,9 @@ 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. @@ -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. @@ -860,6 +844,12 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_FIX_CAPACITY) 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 diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 00141e05bc72429ba1b4f0b54eeb9f661b20207a..1916ee1600b47c201203b35d7eb7664b636619dd 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 819ae3b2bd7e881248fcf3e9f8501d30eb0c3950..39cf19001239313f5adb8ada3c8d0362eb342454 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 a20b65cb6678f99821d17990cf873b9dbc6b58f5..8276a20ecea7e44fb2f1469e49d029018c149cf3 100644 --- a/drivers/usb/usbip/Kconfig +++ b/drivers/usb/usbip/Kconfig @@ -2,6 +2,7 @@ config USBIP_CORE 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 35618ceb279134bc02c6d8ff83671f55b25c82a8..d11270560c2477870374bfc8d694de9a294f6d31 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 bf8a5feb0ee937a35ccd7a478e7c3dd01770b4e1..a20bb2d04f4dedcf2d3edceb48af4dcbb2119d79 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -6,6 +6,7 @@ #include #include #include +#include #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); - - kmem_cache_free(stub_priv_cache, priv); + for (i = 0; i < priv->num_urbs; i++) + usb_kill_urb(priv->urbs[i]); - 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 dbfb2f24d71ea4dd974e891d5aed8bea56d793c5..8c55cd8330988b168e0a7cc58fa17e213ccf7542 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -7,6 +7,7 @@ #include #include #include +#include #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) { + 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; + } + } + + /* 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; } - } - /* 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; + 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); - - 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); - - /* - * Pessimistic. - * This connection will be discarded. - */ - usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + 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->urbs[i]); + + /* + * 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 f0ec41a50cbc16f9814ca8b2a7590dbe3c465fab..36010a82b35930f1d3b154b5eebd1eac3e5b5857 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -5,25 +5,11 @@ #include #include +#include #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 9756752c0681f99c2acb1aaf1213ea23e03055f9..d88a5b15f0730288a0c9f8f88329fba7e5f54881 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,48 @@ 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 (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 1e592ec94ba49d19ba457af7274cc71c9779ea35..d5a036bf904bcf0fd811fb5b3ac8d6d6f33b861b 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 44cd645189250565ab0ea651a6a1a47b4649febd..33f8972ba842feedb7837854a78d281a73c9cce5 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -90,6 +90,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, 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 9aed15a358b7b98b0fab9e6b02b6820cff9ff808..acac49402c2b1b4ca714188ef436c228c860f244 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -5,6 +5,7 @@ #include #include +#include #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 a92c2868d902146758797d11d020ff936ad9516b..0a6eb53e79fbfb23bfd81d7c83ea634ac8255eb5 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; + + 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); - if (IS_ENABLED(CONFIG_VFIO_PCI_INTX) && !vdev->nointx && pin) - return 1; + 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 115a36f6f40398b75dfa75b61b50e81f98bc5a63..423ea1f98441a27e1ac351a14f29fb10ad462e16 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; } @@ -1609,6 +1611,15 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev) return 0; } +/* + * 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 @@ -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 1c46045b0e7fc6b2e8ef421853742851aa880d7e..94594dc63c4173ad79b08ac70f5f31542422d499 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 96721b154454f617689e56ccc029eac9181e4b85..ec53310f1613601c2e9ed9a5490c044dca6d782e 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 bab495d73195f985ef30b26c36f5ddb0b33a5ec3..8dcee4faf701724c66382a754032a06739a8ba40 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); diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c index cd50df5807eadb2b4bf18b6fa8efc906beda0dee..086611c7bc03cf3505bbe7e041dc6419841f30f9 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 591a13a597874ec008430f00d448d9aab4a7b847..f99558d006bf43501efb0d00213d5841951ff660 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 @@ config FIRMWARE_EDID 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 @@ config FB_ACORN 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 @@ if FB_VIA 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_DIRECT_PROCFS 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 @@ config FB_SIMPLE 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 @@ config FB_SM712 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 . + +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 076d24afbd728bb3e8b4ffbc41f8a3be9642b51d..4ed55e6bbb84047927c0da3a314b419e9312dd02 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -22,6 +22,7 @@ #include #include #include +#include &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 0000000000000000000000000000000000000000..c32b99b80e19e8b19192684475146805c607d6f9 --- /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 +#include + +__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 6f289a49e5ecf01552026bef35534a93d8cd136c..1a9b282dd0be236ae0a8b1ac5ce61417422f32d5 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 \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 \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 a023d0d62b25c5986a648ff5d8d68fc528252e49..d651bc1501bdb230e0b193d86ff39e3894a483ef 100644 --- a/tools/testing/selftests/tc-testing/tdc_config.py +++ b/tools/testing/selftests/tc-testing/tdc_config.py @@ -16,7 +16,9 @@ NAMES = { '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 9601bc24454d9426fe0e6a954c84c6f031ec9934..17da711f26afb49d557e767cb6e2adf1a3b209c9 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 6e290874b70e212e72c0f337bbdb597036d00d48..f1c6e025cbe549a3eb2f083aa363080ec2db8126 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 ec3a0b794f159bd33a972b8a9d932f8f4c66c9f3..67ae6c1557b8cf9a31acf28a59dfe80a1be58add 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 dc93fadbee96355d5cc81ecb600c60948ecd67be..b0f7489d069d0747ee7f0b9309c87f511eacb142 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 37908a83ddc27eac33d40f76ea869edf8bdd957d..1ff3a6c0367b087ee4d84e0fa3b0ed0e2752d2b3 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/mmu.c b/virt/kvm/arm/mmu.c index 1344557a708527158fea8ede84aa24a3373b2e1b..bf330b493c1e7be80308e2347e1d5c812a6cc46a 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 8b958ed05306efbac2dd40f6baef26c793d317e0..62a3a2f716bf32a92503bd175b63162ce85565db 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 4a584a57522161ff6e389f8a55e21b600a50694f..9502b1a44232ca4b7d10a3ee55fdf5a98d045930 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -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; +}