mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 05:44:11 +00:00
Merge 5.10-rc7 into char-misc-next
We want the fixes in here, and this resolves a merge issue with drivers/misc/habanalabs/common/memory.c. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
commit
a3ab07c642
236 changed files with 2012 additions and 2025 deletions
2
.mailmap
2
.mailmap
|
@ -322,6 +322,8 @@ TripleX Chung <xxx.phy@gmail.com> <zhongyu@18mail.cn>
|
||||||
Tsuneo Yoshioka <Tsuneo.Yoshioka@f-secure.com>
|
Tsuneo Yoshioka <Tsuneo.Yoshioka@f-secure.com>
|
||||||
Tycho Andersen <tycho@tycho.pizza> <tycho@tycho.ws>
|
Tycho Andersen <tycho@tycho.pizza> <tycho@tycho.ws>
|
||||||
Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
|
Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
|
||||||
|
Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
|
||||||
|
Uwe Kleine-König <ukleinek@strlen.de>
|
||||||
Uwe Kleine-König <ukl@pengutronix.de>
|
Uwe Kleine-König <ukl@pengutronix.de>
|
||||||
Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
|
Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
|
||||||
Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
|
Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
|
||||||
|
|
5
CREDITS
5
CREDITS
|
@ -740,6 +740,11 @@ S: (ask for current address)
|
||||||
S: Portland, Oregon
|
S: Portland, Oregon
|
||||||
S: USA
|
S: USA
|
||||||
|
|
||||||
|
N: Jason Cooper
|
||||||
|
D: ARM/Marvell SOC co-maintainer
|
||||||
|
D: irqchip co-maintainer
|
||||||
|
D: MVEBU PCI DRIVER co-maintainer
|
||||||
|
|
||||||
N: Robin Cornelius
|
N: Robin Cornelius
|
||||||
E: robincornelius@users.sourceforge.net
|
E: robincornelius@users.sourceforge.net
|
||||||
D: Ralink rt2x00 WLAN driver
|
D: Ralink rt2x00 WLAN driver
|
||||||
|
|
|
@ -137,15 +137,24 @@ Boot Kernel With a Boot Config
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Since the boot configuration file is loaded with initrd, it will be added
|
Since the boot configuration file is loaded with initrd, it will be added
|
||||||
to the end of the initrd (initramfs) image file with size, checksum and
|
to the end of the initrd (initramfs) image file with padding, size,
|
||||||
12-byte magic word as below.
|
checksum and 12-byte magic word as below.
|
||||||
|
|
||||||
[initrd][bootconfig][size(u32)][checksum(u32)][#BOOTCONFIG\n]
|
[initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
|
||||||
|
|
||||||
|
The size and checksum fields are unsigned 32bit little endian value.
|
||||||
|
|
||||||
|
When the boot configuration is added to the initrd image, the total
|
||||||
|
file size is aligned to 4 bytes. To fill the gap, null characters
|
||||||
|
(``\0``) will be added. Thus the ``size`` is the length of the bootconfig
|
||||||
|
file + padding bytes.
|
||||||
|
|
||||||
The Linux kernel decodes the last part of the initrd image in memory to
|
The Linux kernel decodes the last part of the initrd image in memory to
|
||||||
get the boot configuration data.
|
get the boot configuration data.
|
||||||
Because of this "piggyback" method, there is no need to change or
|
Because of this "piggyback" method, there is no need to change or
|
||||||
update the boot loader and the kernel image itself.
|
update the boot loader and the kernel image itself as long as the boot
|
||||||
|
loader passes the correct initrd file size. If by any chance, the boot
|
||||||
|
loader passes a longer size, the kernel feils to find the bootconfig data.
|
||||||
|
|
||||||
To do this operation, Linux kernel provides "bootconfig" command under
|
To do this operation, Linux kernel provides "bootconfig" command under
|
||||||
tools/bootconfig, which allows admin to apply or delete the config file
|
tools/bootconfig, which allows admin to apply or delete the config file
|
||||||
|
@ -176,7 +185,8 @@ up to 512 key-value pairs. If keys contains 3 words in average, it can
|
||||||
contain 256 key-value pairs. In most cases, the number of config items
|
contain 256 key-value pairs. In most cases, the number of config items
|
||||||
will be under 100 entries and smaller than 8KB, so it would be enough.
|
will be under 100 entries and smaller than 8KB, so it would be enough.
|
||||||
If the node number exceeds 1024, parser returns an error even if the file
|
If the node number exceeds 1024, parser returns an error even if the file
|
||||||
size is smaller than 32KB.
|
size is smaller than 32KB. (Note that this maximum size is not including
|
||||||
|
the padding null characters.)
|
||||||
Anyway, since bootconfig command verifies it when appending a boot config
|
Anyway, since bootconfig command verifies it when appending a boot config
|
||||||
to initrd image, user can notice it before boot.
|
to initrd image, user can notice it before boot.
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ tcan4x5x: tcan4x5x@0 {
|
||||||
spi-max-frequency = <10000000>;
|
spi-max-frequency = <10000000>;
|
||||||
bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
|
bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
|
||||||
interrupt-parent = <&gpio1>;
|
interrupt-parent = <&gpio1>;
|
||||||
interrupts = <14 GPIO_ACTIVE_LOW>;
|
interrupts = <14 IRQ_TYPE_LEVEL_LOW>;
|
||||||
device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
||||||
device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||||
reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>;
|
reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
|
@ -25,7 +25,7 @@ Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2):
|
||||||
clock-frequency = <100000>;
|
clock-frequency = <100000>;
|
||||||
|
|
||||||
interrupt-parent = <&gpio1>;
|
interrupt-parent = <&gpio1>;
|
||||||
interrupts = <29 GPIO_ACTIVE_HIGH>;
|
interrupts = <29 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
||||||
enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
|
enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
|
||||||
firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
|
firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
|
@ -25,7 +25,7 @@ Example (for ARM-based BeagleBone with PN544 on I2C2):
|
||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
|
|
||||||
interrupt-parent = <&gpio1>;
|
interrupt-parent = <&gpio1>;
|
||||||
interrupts = <17 GPIO_ACTIVE_HIGH>;
|
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
||||||
enable-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
enable-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
||||||
firmware-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
|
firmware-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
31
MAINTAINERS
31
MAINTAINERS
|
@ -2014,7 +2014,6 @@ M: Philipp Zabel <philipp.zabel@gmail.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
||||||
ARM/Marvell Dove/MV78xx0/Orion SOC support
|
ARM/Marvell Dove/MV78xx0/Orion SOC support
|
||||||
M: Jason Cooper <jason@lakedaemon.net>
|
|
||||||
M: Andrew Lunn <andrew@lunn.ch>
|
M: Andrew Lunn <andrew@lunn.ch>
|
||||||
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||||
M: Gregory Clement <gregory.clement@bootlin.com>
|
M: Gregory Clement <gregory.clement@bootlin.com>
|
||||||
|
@ -2031,7 +2030,6 @@ F: arch/arm/plat-orion/
|
||||||
F: drivers/soc/dove/
|
F: drivers/soc/dove/
|
||||||
|
|
||||||
ARM/Marvell Kirkwood and Armada 370, 375, 38x, 39x, XP, 3700, 7K/8K, CN9130 SOC support
|
ARM/Marvell Kirkwood and Armada 370, 375, 38x, 39x, XP, 3700, 7K/8K, CN9130 SOC support
|
||||||
M: Jason Cooper <jason@lakedaemon.net>
|
|
||||||
M: Andrew Lunn <andrew@lunn.ch>
|
M: Andrew Lunn <andrew@lunn.ch>
|
||||||
M: Gregory Clement <gregory.clement@bootlin.com>
|
M: Gregory Clement <gregory.clement@bootlin.com>
|
||||||
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||||
|
@ -3357,6 +3355,17 @@ S: Supported
|
||||||
F: arch/x86/net/
|
F: arch/x86/net/
|
||||||
X: arch/x86/net/bpf_jit_comp32.c
|
X: arch/x86/net/bpf_jit_comp32.c
|
||||||
|
|
||||||
|
BPF LSM (Security Audit and Enforcement using BPF)
|
||||||
|
M: KP Singh <kpsingh@chromium.org>
|
||||||
|
R: Florent Revest <revest@chromium.org>
|
||||||
|
R: Brendan Jackman <jackmanb@chromium.org>
|
||||||
|
L: bpf@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/bpf/bpf_lsm.rst
|
||||||
|
F: include/linux/bpf_lsm.h
|
||||||
|
F: kernel/bpf/bpf_lsm.c
|
||||||
|
F: security/bpf/
|
||||||
|
|
||||||
BROADCOM B44 10/100 ETHERNET DRIVER
|
BROADCOM B44 10/100 ETHERNET DRIVER
|
||||||
M: Michael Chan <michael.chan@broadcom.com>
|
M: Michael Chan <michael.chan@broadcom.com>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
|
@ -4276,6 +4285,7 @@ B: https://github.com/ClangBuiltLinux/linux/issues
|
||||||
C: irc://chat.freenode.net/clangbuiltlinux
|
C: irc://chat.freenode.net/clangbuiltlinux
|
||||||
F: Documentation/kbuild/llvm.rst
|
F: Documentation/kbuild/llvm.rst
|
||||||
F: scripts/clang-tools/
|
F: scripts/clang-tools/
|
||||||
|
F: scripts/lld-version.sh
|
||||||
K: \b(?i:clang|llvm)\b
|
K: \b(?i:clang|llvm)\b
|
||||||
|
|
||||||
CLEANCACHE API
|
CLEANCACHE API
|
||||||
|
@ -9069,10 +9079,7 @@ S: Supported
|
||||||
F: drivers/net/wireless/intel/iwlegacy/
|
F: drivers/net/wireless/intel/iwlegacy/
|
||||||
|
|
||||||
INTEL WIRELESS WIFI LINK (iwlwifi)
|
INTEL WIRELESS WIFI LINK (iwlwifi)
|
||||||
M: Johannes Berg <johannes.berg@intel.com>
|
|
||||||
M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
|
|
||||||
M: Luca Coelho <luciano.coelho@intel.com>
|
M: Luca Coelho <luciano.coelho@intel.com>
|
||||||
M: Intel Linux Wireless <linuxwifi@intel.com>
|
|
||||||
L: linux-wireless@vger.kernel.org
|
L: linux-wireless@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
|
W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
|
||||||
|
@ -9248,7 +9255,6 @@ F: kernel/irq/
|
||||||
|
|
||||||
IRQCHIP DRIVERS
|
IRQCHIP DRIVERS
|
||||||
M: Thomas Gleixner <tglx@linutronix.de>
|
M: Thomas Gleixner <tglx@linutronix.de>
|
||||||
M: Jason Cooper <jason@lakedaemon.net>
|
|
||||||
M: Marc Zyngier <maz@kernel.org>
|
M: Marc Zyngier <maz@kernel.org>
|
||||||
L: linux-kernel@vger.kernel.org
|
L: linux-kernel@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -13394,7 +13400,6 @@ F: drivers/pci/controller/mobiveil/pcie-mobiveil*
|
||||||
|
|
||||||
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
|
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
|
||||||
M: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
|
M: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
|
||||||
M: Jason Cooper <jason@lakedaemon.net>
|
|
||||||
L: linux-pci@vger.kernel.org
|
L: linux-pci@vger.kernel.org
|
||||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -19122,12 +19127,17 @@ L: netdev@vger.kernel.org
|
||||||
L: bpf@vger.kernel.org
|
L: bpf@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: include/net/xdp.h
|
F: include/net/xdp.h
|
||||||
|
F: include/net/xdp_priv.h
|
||||||
F: include/trace/events/xdp.h
|
F: include/trace/events/xdp.h
|
||||||
F: kernel/bpf/cpumap.c
|
F: kernel/bpf/cpumap.c
|
||||||
F: kernel/bpf/devmap.c
|
F: kernel/bpf/devmap.c
|
||||||
F: net/core/xdp.c
|
F: net/core/xdp.c
|
||||||
N: xdp
|
F: samples/bpf/xdp*
|
||||||
K: xdp
|
F: tools/testing/selftests/bpf/*xdp*
|
||||||
|
F: tools/testing/selftests/bpf/*/*xdp*
|
||||||
|
F: drivers/net/ethernet/*/*/*/*/*xdp*
|
||||||
|
F: drivers/net/ethernet/*/*/*xdp*
|
||||||
|
K: (?:\b|_)xdp(?:\b|_)
|
||||||
|
|
||||||
XDP SOCKETS (AF_XDP)
|
XDP SOCKETS (AF_XDP)
|
||||||
M: Björn Töpel <bjorn.topel@intel.com>
|
M: Björn Töpel <bjorn.topel@intel.com>
|
||||||
|
@ -19136,9 +19146,12 @@ R: Jonathan Lemon <jonathan.lemon@gmail.com>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
L: bpf@vger.kernel.org
|
L: bpf@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
F: Documentation/networking/af_xdp.rst
|
||||||
F: include/net/xdp_sock*
|
F: include/net/xdp_sock*
|
||||||
F: include/net/xsk_buff_pool.h
|
F: include/net/xsk_buff_pool.h
|
||||||
F: include/uapi/linux/if_xdp.h
|
F: include/uapi/linux/if_xdp.h
|
||||||
|
F: include/uapi/linux/xdp_diag.h
|
||||||
|
F: include/net/netns/xdp.h
|
||||||
F: net/xdp/
|
F: net/xdp/
|
||||||
F: samples/bpf/xdpsock*
|
F: samples/bpf/xdpsock*
|
||||||
F: tools/lib/bpf/xsk*
|
F: tools/lib/bpf/xsk*
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -2,7 +2,7 @@
|
||||||
VERSION = 5
|
VERSION = 5
|
||||||
PATCHLEVEL = 10
|
PATCHLEVEL = 10
|
||||||
SUBLEVEL = 0
|
SUBLEVEL = 0
|
||||||
EXTRAVERSION = -rc6
|
EXTRAVERSION = -rc7
|
||||||
NAME = Kleptomaniac Octopus
|
NAME = Kleptomaniac Octopus
|
||||||
|
|
||||||
# *DOCUMENTATION*
|
# *DOCUMENTATION*
|
||||||
|
@ -826,7 +826,9 @@ else
|
||||||
DEBUG_CFLAGS += -g
|
DEBUG_CFLAGS += -g
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(LLVM_IAS),1)
|
||||||
KBUILD_AFLAGS += -Wa,-gdwarf-2
|
KBUILD_AFLAGS += -Wa,-gdwarf-2
|
||||||
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_DEBUG_INFO_DWARF4
|
ifdef CONFIG_DEBUG_INFO_DWARF4
|
||||||
DEBUG_CFLAGS += -gdwarf-4
|
DEBUG_CFLAGS += -gdwarf-4
|
||||||
|
@ -944,7 +946,7 @@ KBUILD_CFLAGS += $(call cc-option,-Werror=incompatible-pointer-types)
|
||||||
KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
|
KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
|
||||||
|
|
||||||
# change __FILE__ to the relative path from the srctree
|
# change __FILE__ to the relative path from the srctree
|
||||||
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
|
KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
|
||||||
|
|
||||||
# ensure -fcf-protection is disabled when using retpoline as it is
|
# ensure -fcf-protection is disabled when using retpoline as it is
|
||||||
# incompatible with -mindirect-branch=thunk-extern
|
# incompatible with -mindirect-branch=thunk-extern
|
||||||
|
@ -982,6 +984,12 @@ ifeq ($(CONFIG_RELR),y)
|
||||||
LDFLAGS_vmlinux += --pack-dyn-relocs=relr
|
LDFLAGS_vmlinux += --pack-dyn-relocs=relr
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# We never want expected sections to be placed heuristically by the
|
||||||
|
# linker. All sections should be explicitly named in the linker script.
|
||||||
|
ifdef CONFIG_LD_ORPHAN_WARN
|
||||||
|
LDFLAGS_vmlinux += --orphan-handling=warn
|
||||||
|
endif
|
||||||
|
|
||||||
# Align the bit size of userspace programs with the kernel
|
# Align the bit size of userspace programs with the kernel
|
||||||
KBUILD_USERCFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CFLAGS))
|
KBUILD_USERCFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CFLAGS))
|
||||||
KBUILD_USERLDFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CFLAGS))
|
KBUILD_USERLDFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CFLAGS))
|
||||||
|
|
|
@ -1028,6 +1028,15 @@ config HAVE_STATIC_CALL_INLINE
|
||||||
bool
|
bool
|
||||||
depends on HAVE_STATIC_CALL
|
depends on HAVE_STATIC_CALL
|
||||||
|
|
||||||
|
config ARCH_WANT_LD_ORPHAN_WARN
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
An arch should select this symbol once all linker sections are explicitly
|
||||||
|
included, size-asserted, or discarded in the linker scripts. This is
|
||||||
|
important because we never want expected sections to be placed heuristically
|
||||||
|
by the linker, since the locations of such sections can change between linker
|
||||||
|
versions.
|
||||||
|
|
||||||
source "kernel/gcov/Kconfig"
|
source "kernel/gcov/Kconfig"
|
||||||
|
|
||||||
source "scripts/gcc-plugins/Kconfig"
|
source "scripts/gcc-plugins/Kconfig"
|
||||||
|
|
|
@ -35,6 +35,7 @@ config ARM
|
||||||
select ARCH_USE_CMPXCHG_LOCKREF
|
select ARCH_USE_CMPXCHG_LOCKREF
|
||||||
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
|
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
|
||||||
select ARCH_WANT_IPC_PARSE_VERSION
|
select ARCH_WANT_IPC_PARSE_VERSION
|
||||||
|
select ARCH_WANT_LD_ORPHAN_WARN
|
||||||
select BINFMT_FLAT_ARGVP_ENVP_ON_STACK
|
select BINFMT_FLAT_ARGVP_ENVP_ON_STACK
|
||||||
select BUILDTIME_TABLE_SORT if MMU
|
select BUILDTIME_TABLE_SORT if MMU
|
||||||
select CLONE_BACKWARDS
|
select CLONE_BACKWARDS
|
||||||
|
|
|
@ -16,10 +16,6 @@ LDFLAGS_vmlinux += --be8
|
||||||
KBUILD_LDFLAGS_MODULE += --be8
|
KBUILD_LDFLAGS_MODULE += --be8
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# We never want expected sections to be placed heuristically by the
|
|
||||||
# linker. All sections should be explicitly named in the linker script.
|
|
||||||
LDFLAGS_vmlinux += $(call ld-option, --orphan-handling=warn)
|
|
||||||
|
|
||||||
GZFLAGS :=-9
|
GZFLAGS :=-9
|
||||||
#KBUILD_CFLAGS +=-pipe
|
#KBUILD_CFLAGS +=-pipe
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,9 @@ LDFLAGS_vmlinux += --no-undefined
|
||||||
# Delete all temporary local symbols
|
# Delete all temporary local symbols
|
||||||
LDFLAGS_vmlinux += -X
|
LDFLAGS_vmlinux += -X
|
||||||
# Report orphan sections
|
# Report orphan sections
|
||||||
LDFLAGS_vmlinux += $(call ld-option, --orphan-handling=warn)
|
ifdef CONFIG_LD_ORPHAN_WARN
|
||||||
|
LDFLAGS_vmlinux += --orphan-handling=warn
|
||||||
|
endif
|
||||||
# Next argument is a linker script
|
# Next argument is a linker script
|
||||||
LDFLAGS_vmlinux += -T
|
LDFLAGS_vmlinux += -T
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,6 @@ CONFIG_PARTITION_ADVANCED=y
|
||||||
CONFIG_BINFMT_MISC=y
|
CONFIG_BINFMT_MISC=y
|
||||||
CONFIG_CMA=y
|
CONFIG_CMA=y
|
||||||
CONFIG_ZSMALLOC=m
|
CONFIG_ZSMALLOC=m
|
||||||
CONFIG_ZSMALLOC_PGTABLE_MAPPING=y
|
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
|
|
@ -288,7 +288,7 @@ static struct gpiod_lookup_table osk_usb_gpio_table = {
|
||||||
.dev_id = "ohci",
|
.dev_id = "ohci",
|
||||||
.table = {
|
.table = {
|
||||||
/* Power GPIO on the I2C-attached TPS65010 */
|
/* Power GPIO on the I2C-attached TPS65010 */
|
||||||
GPIO_LOOKUP("i2c-tps65010", 1, "power", GPIO_ACTIVE_HIGH),
|
GPIO_LOOKUP("tps65010", 0, "power", GPIO_ACTIVE_HIGH),
|
||||||
GPIO_LOOKUP(OMAP_GPIO_LABEL, 9, "overcurrent",
|
GPIO_LOOKUP(OMAP_GPIO_LABEL, 9, "overcurrent",
|
||||||
GPIO_ACTIVE_HIGH),
|
GPIO_ACTIVE_HIGH),
|
||||||
},
|
},
|
||||||
|
|
|
@ -81,6 +81,7 @@ config ARM64
|
||||||
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
|
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
|
||||||
select ARCH_WANT_FRAME_POINTERS
|
select ARCH_WANT_FRAME_POINTERS
|
||||||
select ARCH_WANT_HUGE_PMD_SHARE if ARM64_4K_PAGES || (ARM64_16K_PAGES && !ARM64_VA_BITS_36)
|
select ARCH_WANT_HUGE_PMD_SHARE if ARM64_4K_PAGES || (ARM64_16K_PAGES && !ARM64_VA_BITS_36)
|
||||||
|
select ARCH_WANT_LD_ORPHAN_WARN
|
||||||
select ARCH_HAS_UBSAN_SANITIZE_ALL
|
select ARCH_HAS_UBSAN_SANITIZE_ALL
|
||||||
select ARM_AMBA
|
select ARM_AMBA
|
||||||
select ARM_ARCH_TIMER
|
select ARM_ARCH_TIMER
|
||||||
|
|
|
@ -28,10 +28,6 @@ LDFLAGS_vmlinux += --fix-cortex-a53-843419
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# We never want expected sections to be placed heuristically by the
|
|
||||||
# linker. All sections should be explicitly named in the linker script.
|
|
||||||
LDFLAGS_vmlinux += $(call ld-option, --orphan-handling=warn)
|
|
||||||
|
|
||||||
ifeq ($(CONFIG_ARM64_USE_LSE_ATOMICS), y)
|
ifeq ($(CONFIG_ARM64_USE_LSE_ATOMICS), y)
|
||||||
ifneq ($(CONFIG_ARM64_LSE_ATOMICS), y)
|
ifneq ($(CONFIG_ARM64_LSE_ATOMICS), y)
|
||||||
$(warning LSE atomics not supported by binutils)
|
$(warning LSE atomics not supported by binutils)
|
||||||
|
|
|
@ -128,6 +128,9 @@ static inline void local_daif_inherit(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long flags = regs->pstate & DAIF_MASK;
|
unsigned long flags = regs->pstate & DAIF_MASK;
|
||||||
|
|
||||||
|
if (interrupts_enabled(regs))
|
||||||
|
trace_hardirqs_on();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't use local_daif_restore(regs->pstate) here as
|
* We can't use local_daif_restore(regs->pstate) here as
|
||||||
* system_has_prio_mask_debugging() won't restore the I bit if it can
|
* system_has_prio_mask_debugging() won't restore the I bit if it can
|
||||||
|
|
|
@ -31,7 +31,12 @@ static inline u32 disr_to_esr(u64 disr)
|
||||||
return esr;
|
return esr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmlinkage void noinstr enter_el1_irq_or_nmi(struct pt_regs *regs);
|
||||||
|
asmlinkage void noinstr exit_el1_irq_or_nmi(struct pt_regs *regs);
|
||||||
asmlinkage void enter_from_user_mode(void);
|
asmlinkage void enter_from_user_mode(void);
|
||||||
|
asmlinkage void exit_to_user_mode(void);
|
||||||
|
void arm64_enter_nmi(struct pt_regs *regs);
|
||||||
|
void arm64_exit_nmi(struct pt_regs *regs);
|
||||||
void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
|
void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
|
||||||
void do_undefinstr(struct pt_regs *regs);
|
void do_undefinstr(struct pt_regs *regs);
|
||||||
void do_bti(struct pt_regs *regs);
|
void do_bti(struct pt_regs *regs);
|
||||||
|
|
|
@ -193,6 +193,10 @@ struct pt_regs {
|
||||||
/* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */
|
/* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */
|
||||||
u64 pmr_save;
|
u64 pmr_save;
|
||||||
u64 stackframe[2];
|
u64 stackframe[2];
|
||||||
|
|
||||||
|
/* Only valid for some EL1 exceptions. */
|
||||||
|
u64 lockdep_hardirqs;
|
||||||
|
u64 exit_rcu;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool in_syscall(struct pt_regs const *regs)
|
static inline bool in_syscall(struct pt_regs const *regs)
|
||||||
|
|
|
@ -987,7 +987,7 @@
|
||||||
#define SYS_TFSR_EL1_TF0_SHIFT 0
|
#define SYS_TFSR_EL1_TF0_SHIFT 0
|
||||||
#define SYS_TFSR_EL1_TF1_SHIFT 1
|
#define SYS_TFSR_EL1_TF1_SHIFT 1
|
||||||
#define SYS_TFSR_EL1_TF0 (UL(1) << SYS_TFSR_EL1_TF0_SHIFT)
|
#define SYS_TFSR_EL1_TF0 (UL(1) << SYS_TFSR_EL1_TF0_SHIFT)
|
||||||
#define SYS_TFSR_EL1_TF1 (UK(2) << SYS_TFSR_EL1_TF1_SHIFT)
|
#define SYS_TFSR_EL1_TF1 (UL(1) << SYS_TFSR_EL1_TF1_SHIFT)
|
||||||
|
|
||||||
/* Safe value for MPIDR_EL1: Bit31:RES1, Bit30:U:0, Bit24:MT:0 */
|
/* Safe value for MPIDR_EL1: Bit31:RES1, Bit30:U:0, Bit24:MT:0 */
|
||||||
#define SYS_MPIDR_SAFE_VAL (BIT(31))
|
#define SYS_MPIDR_SAFE_VAL (BIT(31))
|
||||||
|
|
|
@ -17,40 +17,164 @@
|
||||||
#include <asm/mmu.h>
|
#include <asm/mmu.h>
|
||||||
#include <asm/sysreg.h>
|
#include <asm/sysreg.h>
|
||||||
|
|
||||||
static void notrace el1_abort(struct pt_regs *regs, unsigned long esr)
|
/*
|
||||||
|
* This is intended to match the logic in irqentry_enter(), handling the kernel
|
||||||
|
* mode transitions only.
|
||||||
|
*/
|
||||||
|
static void noinstr enter_from_kernel_mode(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
regs->exit_rcu = false;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_TINY_RCU) && is_idle_task(current)) {
|
||||||
|
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||||
|
rcu_irq_enter();
|
||||||
|
trace_hardirqs_off_finish();
|
||||||
|
|
||||||
|
regs->exit_rcu = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||||
|
rcu_irq_enter_check_tick();
|
||||||
|
trace_hardirqs_off_finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is intended to match the logic in irqentry_exit(), handling the kernel
|
||||||
|
* mode transitions only, and with preemption handled elsewhere.
|
||||||
|
*/
|
||||||
|
static void noinstr exit_to_kernel_mode(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
lockdep_assert_irqs_disabled();
|
||||||
|
|
||||||
|
if (interrupts_enabled(regs)) {
|
||||||
|
if (regs->exit_rcu) {
|
||||||
|
trace_hardirqs_on_prepare();
|
||||||
|
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
|
||||||
|
rcu_irq_exit();
|
||||||
|
lockdep_hardirqs_on(CALLER_ADDR0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_hardirqs_on();
|
||||||
|
} else {
|
||||||
|
if (regs->exit_rcu)
|
||||||
|
rcu_irq_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noinstr arm64_enter_nmi(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
regs->lockdep_hardirqs = lockdep_hardirqs_enabled();
|
||||||
|
|
||||||
|
__nmi_enter();
|
||||||
|
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||||
|
lockdep_hardirq_enter();
|
||||||
|
rcu_nmi_enter();
|
||||||
|
|
||||||
|
trace_hardirqs_off_finish();
|
||||||
|
ftrace_nmi_enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void noinstr arm64_exit_nmi(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
bool restore = regs->lockdep_hardirqs;
|
||||||
|
|
||||||
|
ftrace_nmi_exit();
|
||||||
|
if (restore) {
|
||||||
|
trace_hardirqs_on_prepare();
|
||||||
|
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_nmi_exit();
|
||||||
|
lockdep_hardirq_exit();
|
||||||
|
if (restore)
|
||||||
|
lockdep_hardirqs_on(CALLER_ADDR0);
|
||||||
|
__nmi_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
asmlinkage void noinstr enter_el1_irq_or_nmi(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && !interrupts_enabled(regs))
|
||||||
|
arm64_enter_nmi(regs);
|
||||||
|
else
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
asmlinkage void noinstr exit_el1_irq_or_nmi(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && !interrupts_enabled(regs))
|
||||||
|
arm64_exit_nmi(regs);
|
||||||
|
else
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void noinstr el1_abort(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
local_daif_inherit(regs);
|
local_daif_inherit(regs);
|
||||||
far = untagged_addr(far);
|
far = untagged_addr(far);
|
||||||
do_mem_abort(far, esr, regs);
|
do_mem_abort(far, esr, regs);
|
||||||
|
local_daif_mask();
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_abort);
|
|
||||||
|
|
||||||
static void notrace el1_pc(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el1_pc(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
local_daif_inherit(regs);
|
local_daif_inherit(regs);
|
||||||
do_sp_pc_abort(far, esr, regs);
|
do_sp_pc_abort(far, esr, regs);
|
||||||
|
local_daif_mask();
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_pc);
|
|
||||||
|
|
||||||
static void notrace el1_undef(struct pt_regs *regs)
|
static void noinstr el1_undef(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
local_daif_inherit(regs);
|
local_daif_inherit(regs);
|
||||||
do_undefinstr(regs);
|
do_undefinstr(regs);
|
||||||
|
local_daif_mask();
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_undef);
|
|
||||||
|
|
||||||
static void notrace el1_inv(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el1_inv(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
local_daif_inherit(regs);
|
local_daif_inherit(regs);
|
||||||
bad_mode(regs, 0, esr);
|
bad_mode(regs, 0, esr);
|
||||||
|
local_daif_mask();
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_inv);
|
|
||||||
|
|
||||||
static void notrace el1_dbg(struct pt_regs *regs, unsigned long esr)
|
static void noinstr arm64_enter_el1_dbg(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
regs->lockdep_hardirqs = lockdep_hardirqs_enabled();
|
||||||
|
|
||||||
|
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||||
|
rcu_nmi_enter();
|
||||||
|
|
||||||
|
trace_hardirqs_off_finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void noinstr arm64_exit_el1_dbg(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
bool restore = regs->lockdep_hardirqs;
|
||||||
|
|
||||||
|
if (restore) {
|
||||||
|
trace_hardirqs_on_prepare();
|
||||||
|
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_nmi_exit();
|
||||||
|
if (restore)
|
||||||
|
lockdep_hardirqs_on(CALLER_ADDR0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
|
@ -62,18 +186,21 @@ static void notrace el1_dbg(struct pt_regs *regs, unsigned long esr)
|
||||||
if (system_uses_irq_prio_masking())
|
if (system_uses_irq_prio_masking())
|
||||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||||
|
|
||||||
|
arm64_enter_el1_dbg(regs);
|
||||||
do_debug_exception(far, esr, regs);
|
do_debug_exception(far, esr, regs);
|
||||||
|
arm64_exit_el1_dbg(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_dbg);
|
|
||||||
|
|
||||||
static void notrace el1_fpac(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el1_fpac(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
|
enter_from_kernel_mode(regs);
|
||||||
local_daif_inherit(regs);
|
local_daif_inherit(regs);
|
||||||
do_ptrauth_fault(regs, esr);
|
do_ptrauth_fault(regs, esr);
|
||||||
|
local_daif_mask();
|
||||||
|
exit_to_kernel_mode(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_fpac);
|
|
||||||
|
|
||||||
asmlinkage void notrace el1_sync_handler(struct pt_regs *regs)
|
asmlinkage void noinstr el1_sync_handler(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long esr = read_sysreg(esr_el1);
|
unsigned long esr = read_sysreg(esr_el1);
|
||||||
|
|
||||||
|
@ -106,20 +233,34 @@ asmlinkage void notrace el1_sync_handler(struct pt_regs *regs)
|
||||||
el1_inv(regs, esr);
|
el1_inv(regs, esr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el1_sync_handler);
|
|
||||||
|
|
||||||
static void notrace el0_da(struct pt_regs *regs, unsigned long esr)
|
asmlinkage void noinstr enter_from_user_mode(void)
|
||||||
|
{
|
||||||
|
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||||
|
CT_WARN_ON(ct_state() != CONTEXT_USER);
|
||||||
|
user_exit_irqoff();
|
||||||
|
trace_hardirqs_off_finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
asmlinkage void noinstr exit_to_user_mode(void)
|
||||||
|
{
|
||||||
|
trace_hardirqs_on_prepare();
|
||||||
|
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
|
||||||
|
user_enter_irqoff();
|
||||||
|
lockdep_hardirqs_on(CALLER_ADDR0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void noinstr el0_da(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
far = untagged_addr(far);
|
far = untagged_addr(far);
|
||||||
do_mem_abort(far, esr, regs);
|
do_mem_abort(far, esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_da);
|
|
||||||
|
|
||||||
static void notrace el0_ia(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_ia(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
|
@ -131,90 +272,80 @@ static void notrace el0_ia(struct pt_regs *regs, unsigned long esr)
|
||||||
if (!is_ttbr0_addr(far))
|
if (!is_ttbr0_addr(far))
|
||||||
arm64_apply_bp_hardening();
|
arm64_apply_bp_hardening();
|
||||||
|
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_mem_abort(far, esr, regs);
|
do_mem_abort(far, esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_ia);
|
|
||||||
|
|
||||||
static void notrace el0_fpsimd_acc(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_fpsimd_acc(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_fpsimd_acc(esr, regs);
|
do_fpsimd_acc(esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_fpsimd_acc);
|
|
||||||
|
|
||||||
static void notrace el0_sve_acc(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_sve_acc(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_sve_acc(esr, regs);
|
do_sve_acc(esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_sve_acc);
|
|
||||||
|
|
||||||
static void notrace el0_fpsimd_exc(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_fpsimd_exc(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_fpsimd_exc(esr, regs);
|
do_fpsimd_exc(esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_fpsimd_exc);
|
|
||||||
|
|
||||||
static void notrace el0_sys(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_sys(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_sysinstr(esr, regs);
|
do_sysinstr(esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_sys);
|
|
||||||
|
|
||||||
static void notrace el0_pc(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_pc(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
if (!is_ttbr0_addr(instruction_pointer(regs)))
|
if (!is_ttbr0_addr(instruction_pointer(regs)))
|
||||||
arm64_apply_bp_hardening();
|
arm64_apply_bp_hardening();
|
||||||
|
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_sp_pc_abort(far, esr, regs);
|
do_sp_pc_abort(far, esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_pc);
|
|
||||||
|
|
||||||
static void notrace el0_sp(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_sp(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_sp_pc_abort(regs->sp, esr, regs);
|
do_sp_pc_abort(regs->sp, esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_sp);
|
|
||||||
|
|
||||||
static void notrace el0_undef(struct pt_regs *regs)
|
static void noinstr el0_undef(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_undefinstr(regs);
|
do_undefinstr(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_undef);
|
|
||||||
|
|
||||||
static void notrace el0_bti(struct pt_regs *regs)
|
static void noinstr el0_bti(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_bti(regs);
|
do_bti(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_bti);
|
|
||||||
|
|
||||||
static void notrace el0_inv(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_inv(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
bad_el0_sync(regs, 0, esr);
|
bad_el0_sync(regs, 0, esr);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_inv);
|
|
||||||
|
|
||||||
static void notrace el0_dbg(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
/* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
|
/* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
@ -222,30 +353,28 @@ static void notrace el0_dbg(struct pt_regs *regs, unsigned long esr)
|
||||||
if (system_uses_irq_prio_masking())
|
if (system_uses_irq_prio_masking())
|
||||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||||
|
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
do_debug_exception(far, esr, regs);
|
do_debug_exception(far, esr, regs);
|
||||||
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_dbg);
|
|
||||||
|
|
||||||
static void notrace el0_svc(struct pt_regs *regs)
|
static void noinstr el0_svc(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (system_uses_irq_prio_masking())
|
if (system_uses_irq_prio_masking())
|
||||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||||
|
|
||||||
|
enter_from_user_mode();
|
||||||
do_el0_svc(regs);
|
do_el0_svc(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_svc);
|
|
||||||
|
|
||||||
static void notrace el0_fpac(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_fpac(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_ptrauth_fault(regs, esr);
|
do_ptrauth_fault(regs, esr);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_fpac);
|
|
||||||
|
|
||||||
asmlinkage void notrace el0_sync_handler(struct pt_regs *regs)
|
asmlinkage void noinstr el0_sync_handler(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long esr = read_sysreg(esr_el1);
|
unsigned long esr = read_sysreg(esr_el1);
|
||||||
|
|
||||||
|
@ -297,27 +426,25 @@ asmlinkage void notrace el0_sync_handler(struct pt_regs *regs)
|
||||||
el0_inv(regs, esr);
|
el0_inv(regs, esr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_sync_handler);
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
static void notrace el0_cp15(struct pt_regs *regs, unsigned long esr)
|
static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
user_exit_irqoff();
|
enter_from_user_mode();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
do_cp15instr(esr, regs);
|
do_cp15instr(esr, regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_cp15);
|
|
||||||
|
|
||||||
static void notrace el0_svc_compat(struct pt_regs *regs)
|
static void noinstr el0_svc_compat(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (system_uses_irq_prio_masking())
|
if (system_uses_irq_prio_masking())
|
||||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||||
|
|
||||||
|
enter_from_user_mode();
|
||||||
do_el0_svc_compat(regs);
|
do_el0_svc_compat(regs);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_svc_compat);
|
|
||||||
|
|
||||||
asmlinkage void notrace el0_sync_compat_handler(struct pt_regs *regs)
|
asmlinkage void noinstr el0_sync_compat_handler(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long esr = read_sysreg(esr_el1);
|
unsigned long esr = read_sysreg(esr_el1);
|
||||||
|
|
||||||
|
@ -360,5 +487,4 @@ asmlinkage void notrace el0_sync_compat_handler(struct pt_regs *regs)
|
||||||
el0_inv(regs, esr);
|
el0_inv(regs, esr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(el0_sync_compat_handler);
|
|
||||||
#endif /* CONFIG_COMPAT */
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
|
@ -30,18 +30,18 @@
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Context tracking subsystem. Used to instrument transitions
|
* Context tracking and irqflag tracing need to instrument transitions between
|
||||||
* between user and kernel mode.
|
* user and kernel mode.
|
||||||
*/
|
*/
|
||||||
.macro ct_user_exit_irqoff
|
.macro user_exit_irqoff
|
||||||
#ifdef CONFIG_CONTEXT_TRACKING
|
#if defined(CONFIG_CONTEXT_TRACKING) || defined(CONFIG_TRACE_IRQFLAGS)
|
||||||
bl enter_from_user_mode
|
bl enter_from_user_mode
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro ct_user_enter
|
.macro user_enter_irqoff
|
||||||
#ifdef CONFIG_CONTEXT_TRACKING
|
#if defined(CONFIG_CONTEXT_TRACKING) || defined(CONFIG_TRACE_IRQFLAGS)
|
||||||
bl context_tracking_user_enter
|
bl exit_to_user_mode
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
@ -298,9 +298,6 @@ alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
||||||
alternative_else_nop_endif
|
alternative_else_nop_endif
|
||||||
|
|
||||||
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
||||||
.if \el == 0
|
|
||||||
ct_user_enter
|
|
||||||
.endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
|
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
|
||||||
alternative_if_not ARM64_HAS_PAN
|
alternative_if_not ARM64_HAS_PAN
|
||||||
|
@ -637,16 +634,8 @@ SYM_CODE_START_LOCAL_NOALIGN(el1_irq)
|
||||||
gic_prio_irq_setup pmr=x20, tmp=x1
|
gic_prio_irq_setup pmr=x20, tmp=x1
|
||||||
enable_da_f
|
enable_da_f
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
mov x0, sp
|
||||||
test_irqs_unmasked res=x0, pmr=x20
|
bl enter_el1_irq_or_nmi
|
||||||
cbz x0, 1f
|
|
||||||
bl asm_nmi_enter
|
|
||||||
1:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
||||||
bl trace_hardirqs_off
|
|
||||||
#endif
|
|
||||||
|
|
||||||
irq_handler
|
irq_handler
|
||||||
|
|
||||||
|
@ -665,26 +654,8 @@ alternative_else_nop_endif
|
||||||
1:
|
1:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
mov x0, sp
|
||||||
/*
|
bl exit_el1_irq_or_nmi
|
||||||
* When using IRQ priority masking, we can get spurious interrupts while
|
|
||||||
* PMR is set to GIC_PRIO_IRQOFF. An NMI might also have occurred in a
|
|
||||||
* section with interrupts disabled. Skip tracing in those cases.
|
|
||||||
*/
|
|
||||||
test_irqs_unmasked res=x0, pmr=x20
|
|
||||||
cbz x0, 1f
|
|
||||||
bl asm_nmi_exit
|
|
||||||
1:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
|
||||||
test_irqs_unmasked res=x0, pmr=x20
|
|
||||||
cbnz x0, 1f
|
|
||||||
#endif
|
|
||||||
bl trace_hardirqs_on
|
|
||||||
1:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
kernel_exit 1
|
kernel_exit 1
|
||||||
SYM_CODE_END(el1_irq)
|
SYM_CODE_END(el1_irq)
|
||||||
|
@ -726,21 +697,14 @@ SYM_CODE_START_LOCAL_NOALIGN(el0_irq)
|
||||||
kernel_entry 0
|
kernel_entry 0
|
||||||
el0_irq_naked:
|
el0_irq_naked:
|
||||||
gic_prio_irq_setup pmr=x20, tmp=x0
|
gic_prio_irq_setup pmr=x20, tmp=x0
|
||||||
ct_user_exit_irqoff
|
user_exit_irqoff
|
||||||
enable_da_f
|
enable_da_f
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
||||||
bl trace_hardirqs_off
|
|
||||||
#endif
|
|
||||||
|
|
||||||
tbz x22, #55, 1f
|
tbz x22, #55, 1f
|
||||||
bl do_el0_irq_bp_hardening
|
bl do_el0_irq_bp_hardening
|
||||||
1:
|
1:
|
||||||
irq_handler
|
irq_handler
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
||||||
bl trace_hardirqs_on
|
|
||||||
#endif
|
|
||||||
b ret_to_user
|
b ret_to_user
|
||||||
SYM_CODE_END(el0_irq)
|
SYM_CODE_END(el0_irq)
|
||||||
|
|
||||||
|
@ -759,7 +723,7 @@ SYM_CODE_START_LOCAL(el0_error)
|
||||||
el0_error_naked:
|
el0_error_naked:
|
||||||
mrs x25, esr_el1
|
mrs x25, esr_el1
|
||||||
gic_prio_kentry_setup tmp=x2
|
gic_prio_kentry_setup tmp=x2
|
||||||
ct_user_exit_irqoff
|
user_exit_irqoff
|
||||||
enable_dbg
|
enable_dbg
|
||||||
mov x0, sp
|
mov x0, sp
|
||||||
mov x1, x25
|
mov x1, x25
|
||||||
|
@ -774,13 +738,17 @@ SYM_CODE_END(el0_error)
|
||||||
SYM_CODE_START_LOCAL(ret_to_user)
|
SYM_CODE_START_LOCAL(ret_to_user)
|
||||||
disable_daif
|
disable_daif
|
||||||
gic_prio_kentry_setup tmp=x3
|
gic_prio_kentry_setup tmp=x3
|
||||||
ldr x1, [tsk, #TSK_TI_FLAGS]
|
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||||
and x2, x1, #_TIF_WORK_MASK
|
bl trace_hardirqs_off
|
||||||
|
#endif
|
||||||
|
ldr x19, [tsk, #TSK_TI_FLAGS]
|
||||||
|
and x2, x19, #_TIF_WORK_MASK
|
||||||
cbnz x2, work_pending
|
cbnz x2, work_pending
|
||||||
finish_ret_to_user:
|
finish_ret_to_user:
|
||||||
|
user_enter_irqoff
|
||||||
/* Ignore asynchronous tag check faults in the uaccess routines */
|
/* Ignore asynchronous tag check faults in the uaccess routines */
|
||||||
clear_mte_async_tcf
|
clear_mte_async_tcf
|
||||||
enable_step_tsk x1, x2
|
enable_step_tsk x19, x2
|
||||||
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
|
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
|
||||||
bl stackleak_erase
|
bl stackleak_erase
|
||||||
#endif
|
#endif
|
||||||
|
@ -791,11 +759,9 @@ finish_ret_to_user:
|
||||||
*/
|
*/
|
||||||
work_pending:
|
work_pending:
|
||||||
mov x0, sp // 'regs'
|
mov x0, sp // 'regs'
|
||||||
|
mov x1, x19
|
||||||
bl do_notify_resume
|
bl do_notify_resume
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step
|
||||||
bl trace_hardirqs_on // enabled while in userspace
|
|
||||||
#endif
|
|
||||||
ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for single-step
|
|
||||||
b finish_ret_to_user
|
b finish_ret_to_user
|
||||||
SYM_CODE_END(ret_to_user)
|
SYM_CODE_END(ret_to_user)
|
||||||
|
|
||||||
|
|
|
@ -67,18 +67,3 @@ void __init init_IRQ(void)
|
||||||
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Stubs to make nmi_enter/exit() code callable from ASM
|
|
||||||
*/
|
|
||||||
asmlinkage void notrace asm_nmi_enter(void)
|
|
||||||
{
|
|
||||||
nmi_enter();
|
|
||||||
}
|
|
||||||
NOKPROBE_SYMBOL(asm_nmi_enter);
|
|
||||||
|
|
||||||
asmlinkage void notrace asm_nmi_exit(void)
|
|
||||||
{
|
|
||||||
nmi_exit();
|
|
||||||
}
|
|
||||||
NOKPROBE_SYMBOL(asm_nmi_exit);
|
|
||||||
|
|
|
@ -72,13 +72,13 @@ EXPORT_SYMBOL_GPL(pm_power_off);
|
||||||
|
|
||||||
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
|
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
|
||||||
|
|
||||||
static void __cpu_do_idle(void)
|
static void noinstr __cpu_do_idle(void)
|
||||||
{
|
{
|
||||||
dsb(sy);
|
dsb(sy);
|
||||||
wfi();
|
wfi();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __cpu_do_idle_irqprio(void)
|
static void noinstr __cpu_do_idle_irqprio(void)
|
||||||
{
|
{
|
||||||
unsigned long pmr;
|
unsigned long pmr;
|
||||||
unsigned long daif_bits;
|
unsigned long daif_bits;
|
||||||
|
@ -108,7 +108,7 @@ static void __cpu_do_idle_irqprio(void)
|
||||||
* ensure that interrupts are not masked at the PMR (because the core will
|
* ensure that interrupts are not masked at the PMR (because the core will
|
||||||
* not wake up if we block the wake up signal in the interrupt controller).
|
* not wake up if we block the wake up signal in the interrupt controller).
|
||||||
*/
|
*/
|
||||||
void cpu_do_idle(void)
|
void noinstr cpu_do_idle(void)
|
||||||
{
|
{
|
||||||
if (system_uses_irq_prio_masking())
|
if (system_uses_irq_prio_masking())
|
||||||
__cpu_do_idle_irqprio();
|
__cpu_do_idle_irqprio();
|
||||||
|
@ -119,7 +119,7 @@ void cpu_do_idle(void)
|
||||||
/*
|
/*
|
||||||
* This is our default idle handler.
|
* This is our default idle handler.
|
||||||
*/
|
*/
|
||||||
void arch_cpu_idle(void)
|
void noinstr arch_cpu_idle(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This should do all the clock switching and wait for interrupt
|
* This should do all the clock switching and wait for interrupt
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
#include <asm/alternative.h>
|
#include <asm/alternative.h>
|
||||||
|
#include <asm/exception.h>
|
||||||
#include <asm/kprobes.h>
|
#include <asm/kprobes.h>
|
||||||
#include <asm/mmu.h>
|
#include <asm/mmu.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
@ -223,16 +224,16 @@ static __kprobes unsigned long _sdei_handler(struct pt_regs *regs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
asmlinkage __kprobes notrace unsigned long
|
asmlinkage noinstr unsigned long
|
||||||
__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
|
__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
|
||||||
{
|
{
|
||||||
unsigned long ret;
|
unsigned long ret;
|
||||||
|
|
||||||
nmi_enter();
|
arm64_enter_nmi(regs);
|
||||||
|
|
||||||
ret = _sdei_handler(regs, arg);
|
ret = _sdei_handler(regs, arg);
|
||||||
|
|
||||||
nmi_exit();
|
arm64_exit_nmi(regs);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,6 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
|
||||||
|
|
||||||
cortex_a76_erratum_1463225_svc_handler();
|
cortex_a76_erratum_1463225_svc_handler();
|
||||||
local_daif_restore(DAIF_PROCCTX);
|
local_daif_restore(DAIF_PROCCTX);
|
||||||
user_exit();
|
|
||||||
|
|
||||||
if (system_supports_mte() && (flags & _TIF_MTE_ASYNC_FAULT)) {
|
if (system_supports_mte() && (flags & _TIF_MTE_ASYNC_FAULT)) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <asm/daifflags.h>
|
#include <asm/daifflags.h>
|
||||||
#include <asm/debug-monitors.h>
|
#include <asm/debug-monitors.h>
|
||||||
#include <asm/esr.h>
|
#include <asm/esr.h>
|
||||||
|
#include <asm/exception.h>
|
||||||
#include <asm/extable.h>
|
#include <asm/extable.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <asm/kprobes.h>
|
#include <asm/kprobes.h>
|
||||||
|
@ -753,8 +754,10 @@ const char *esr_get_class_string(u32 esr)
|
||||||
* bad_mode handles the impossible case in the exception vector. This is always
|
* bad_mode handles the impossible case in the exception vector. This is always
|
||||||
* fatal.
|
* fatal.
|
||||||
*/
|
*/
|
||||||
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
|
asmlinkage void notrace bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
|
||||||
{
|
{
|
||||||
|
arm64_enter_nmi(regs);
|
||||||
|
|
||||||
console_verbose();
|
console_verbose();
|
||||||
|
|
||||||
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
|
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
|
||||||
|
@ -786,7 +789,7 @@ void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
|
||||||
DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
|
DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
|
||||||
__aligned(16);
|
__aligned(16);
|
||||||
|
|
||||||
asmlinkage void handle_bad_stack(struct pt_regs *regs)
|
asmlinkage void noinstr handle_bad_stack(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long tsk_stk = (unsigned long)current->stack;
|
unsigned long tsk_stk = (unsigned long)current->stack;
|
||||||
unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
|
unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
|
||||||
|
@ -794,6 +797,8 @@ asmlinkage void handle_bad_stack(struct pt_regs *regs)
|
||||||
unsigned int esr = read_sysreg(esr_el1);
|
unsigned int esr = read_sysreg(esr_el1);
|
||||||
unsigned long far = read_sysreg(far_el1);
|
unsigned long far = read_sysreg(far_el1);
|
||||||
|
|
||||||
|
arm64_enter_nmi(regs);
|
||||||
|
|
||||||
console_verbose();
|
console_verbose();
|
||||||
pr_emerg("Insufficient stack space to handle exception!");
|
pr_emerg("Insufficient stack space to handle exception!");
|
||||||
|
|
||||||
|
@ -865,24 +870,17 @@ bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr)
|
asmlinkage void noinstr do_serror(struct pt_regs *regs, unsigned int esr)
|
||||||
{
|
{
|
||||||
nmi_enter();
|
arm64_enter_nmi(regs);
|
||||||
|
|
||||||
/* non-RAS errors are not containable */
|
/* non-RAS errors are not containable */
|
||||||
if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr))
|
if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr))
|
||||||
arm64_serror_panic(regs, esr);
|
arm64_serror_panic(regs, esr);
|
||||||
|
|
||||||
nmi_exit();
|
arm64_exit_nmi(regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void enter_from_user_mode(void)
|
|
||||||
{
|
|
||||||
CT_WARN_ON(ct_state() != CONTEXT_USER);
|
|
||||||
user_exit_irqoff();
|
|
||||||
}
|
|
||||||
NOKPROBE_SYMBOL(enter_from_user_mode);
|
|
||||||
|
|
||||||
/* GENERIC_BUG traps */
|
/* GENERIC_BUG traps */
|
||||||
|
|
||||||
int is_valid_bugaddr(unsigned long addr)
|
int is_valid_bugaddr(unsigned long addr)
|
||||||
|
|
|
@ -789,25 +789,6 @@ void __init hook_debug_fault_code(int nr,
|
||||||
*/
|
*/
|
||||||
static void debug_exception_enter(struct pt_regs *regs)
|
static void debug_exception_enter(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
|
|
||||||
* already disabled to preserve the last enabled/disabled addresses.
|
|
||||||
*/
|
|
||||||
if (interrupts_enabled(regs))
|
|
||||||
trace_hardirqs_off();
|
|
||||||
|
|
||||||
if (user_mode(regs)) {
|
|
||||||
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* We might have interrupted pretty much anything. In
|
|
||||||
* fact, if we're a debug exception, we can even interrupt
|
|
||||||
* NMI processing. We don't want this code makes in_nmi()
|
|
||||||
* to return true, but we need to notify RCU.
|
|
||||||
*/
|
|
||||||
rcu_nmi_enter();
|
|
||||||
}
|
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
|
|
||||||
/* This code is a bit fragile. Test it. */
|
/* This code is a bit fragile. Test it. */
|
||||||
|
@ -818,12 +799,6 @@ NOKPROBE_SYMBOL(debug_exception_enter);
|
||||||
static void debug_exception_exit(struct pt_regs *regs)
|
static void debug_exception_exit(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
preempt_enable_no_resched();
|
preempt_enable_no_resched();
|
||||||
|
|
||||||
if (!user_mode(regs))
|
|
||||||
rcu_nmi_exit();
|
|
||||||
|
|
||||||
if (interrupts_enabled(regs))
|
|
||||||
trace_hardirqs_on();
|
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(debug_exception_exit);
|
NOKPROBE_SYMBOL(debug_exception_exit);
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ config PPC
|
||||||
select ARCH_USE_QUEUED_SPINLOCKS if PPC_QUEUED_SPINLOCKS
|
select ARCH_USE_QUEUED_SPINLOCKS if PPC_QUEUED_SPINLOCKS
|
||||||
select ARCH_WANT_IPC_PARSE_VERSION
|
select ARCH_WANT_IPC_PARSE_VERSION
|
||||||
select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
|
select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
|
||||||
|
select ARCH_WANT_LD_ORPHAN_WARN
|
||||||
select ARCH_WEAK_RELEASE_ACQUIRE
|
select ARCH_WEAK_RELEASE_ACQUIRE
|
||||||
select BINFMT_ELF
|
select BINFMT_ELF
|
||||||
select BUILDTIME_TABLE_SORT
|
select BUILDTIME_TABLE_SORT
|
||||||
|
|
|
@ -123,7 +123,6 @@ endif
|
||||||
LDFLAGS_vmlinux-y := -Bstatic
|
LDFLAGS_vmlinux-y := -Bstatic
|
||||||
LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie
|
LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie
|
||||||
LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y)
|
LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y)
|
||||||
LDFLAGS_vmlinux += $(call ld-option,--orphan-handling=warn)
|
|
||||||
|
|
||||||
ifdef CONFIG_PPC64
|
ifdef CONFIG_PPC64
|
||||||
ifeq ($(call cc-option-yn,-mcmodel=medium),y)
|
ifeq ($(call cc-option-yn,-mcmodel=medium),y)
|
||||||
|
|
|
@ -242,6 +242,18 @@ extern void radix_init_pseries(void);
|
||||||
static inline void radix_init_pseries(void) { };
|
static inline void radix_init_pseries(void) { };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
#define arch_clear_mm_cpumask_cpu(cpu, mm) \
|
||||||
|
do { \
|
||||||
|
if (cpumask_test_cpu(cpu, mm_cpumask(mm))) { \
|
||||||
|
atomic_dec(&(mm)->context.active_cpus); \
|
||||||
|
cpumask_clear_cpu(cpu, mm_cpumask(mm)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void cleanup_cpu_mmu_context(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline int get_user_context(mm_context_t *ctx, unsigned long ea)
|
static inline int get_user_context(mm_context_t *ctx, unsigned long ea)
|
||||||
{
|
{
|
||||||
int index = ea >> MAX_EA_BITS_PER_CONTEXT;
|
int index = ea >> MAX_EA_BITS_PER_CONTEXT;
|
||||||
|
|
|
@ -1214,12 +1214,9 @@ void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu)
|
||||||
static bool kvmppc_xive_vcpu_id_valid(struct kvmppc_xive *xive, u32 cpu)
|
static bool kvmppc_xive_vcpu_id_valid(struct kvmppc_xive *xive, u32 cpu)
|
||||||
{
|
{
|
||||||
/* We have a block of xive->nr_servers VPs. We just need to check
|
/* We have a block of xive->nr_servers VPs. We just need to check
|
||||||
* raw vCPU ids are below the expected limit for this guest's
|
* packed vCPU ids are below that.
|
||||||
* core stride ; kvmppc_pack_vcpu_id() will pack them down to an
|
|
||||||
* index that can be safely used to compute a VP id that belongs
|
|
||||||
* to the VP block.
|
|
||||||
*/
|
*/
|
||||||
return cpu < xive->nr_servers * xive->kvm->arch.emul_smt_mode;
|
return kvmppc_pack_vcpu_id(xive->kvm, cpu) < xive->nr_servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvmppc_xive_compute_vp_id(struct kvmppc_xive *xive, u32 cpu, u32 *vp)
|
int kvmppc_xive_compute_vp_id(struct kvmppc_xive *xive, u32 cpu, u32 *vp)
|
||||||
|
|
|
@ -68,7 +68,7 @@ static __always_inline void tlbiel_hash_set_isa300(unsigned int set, unsigned in
|
||||||
rs = ((unsigned long)pid << PPC_BITLSHIFT(31));
|
rs = ((unsigned long)pid << PPC_BITLSHIFT(31));
|
||||||
|
|
||||||
asm volatile(PPC_TLBIEL(%0, %1, %2, %3, %4)
|
asm volatile(PPC_TLBIEL(%0, %1, %2, %3, %4)
|
||||||
: : "r"(rb), "r"(rs), "i"(ric), "i"(prs), "r"(r)
|
: : "r"(rb), "r"(rs), "i"(ric), "i"(prs), "i"(r)
|
||||||
: "memory");
|
: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,16 +92,15 @@ static void tlbiel_all_isa300(unsigned int num_sets, unsigned int is)
|
||||||
asm volatile("ptesync": : :"memory");
|
asm volatile("ptesync": : :"memory");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flush the first set of the TLB, and any caching of partition table
|
* Flush the partition table cache if this is HV mode.
|
||||||
* entries. Then flush the remaining sets of the TLB. Hash mode uses
|
|
||||||
* partition scoped TLB translations.
|
|
||||||
*/
|
*/
|
||||||
tlbiel_hash_set_isa300(0, is, 0, 2, 0);
|
if (early_cpu_has_feature(CPU_FTR_HVMODE))
|
||||||
for (set = 1; set < num_sets; set++)
|
tlbiel_hash_set_isa300(0, is, 0, 2, 0);
|
||||||
tlbiel_hash_set_isa300(set, is, 0, 0, 0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now invalidate the process table cache.
|
* Now invalidate the process table cache. UPRT=0 HPT modes (what
|
||||||
|
* current hardware implements) do not use the process table, but
|
||||||
|
* add the flushes anyway.
|
||||||
*
|
*
|
||||||
* From ISA v3.0B p. 1078:
|
* From ISA v3.0B p. 1078:
|
||||||
* The following forms are invalid.
|
* The following forms are invalid.
|
||||||
|
@ -110,6 +109,14 @@ static void tlbiel_all_isa300(unsigned int num_sets, unsigned int is)
|
||||||
*/
|
*/
|
||||||
tlbiel_hash_set_isa300(0, is, 0, 2, 1);
|
tlbiel_hash_set_isa300(0, is, 0, 2, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then flush the sets of the TLB proper. Hash mode uses
|
||||||
|
* partition scoped TLB translations, which may be flushed
|
||||||
|
* in !HV mode.
|
||||||
|
*/
|
||||||
|
for (set = 0; set < num_sets; set++)
|
||||||
|
tlbiel_hash_set_isa300(set, is, 0, 0, 0);
|
||||||
|
|
||||||
ppc_after_tlbiel_barrier();
|
ppc_after_tlbiel_barrier();
|
||||||
|
|
||||||
asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory");
|
asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory");
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
|
||||||
#include <asm/mmu_context.h>
|
#include <asm/mmu_context.h>
|
||||||
#include <asm/pgalloc.h>
|
#include <asm/pgalloc.h>
|
||||||
|
@ -307,3 +308,22 @@ void radix__switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
|
||||||
isync();
|
isync();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cleanup_cpu_mmu_context - Clean up MMU details for this CPU (newly offlined)
|
||||||
|
*
|
||||||
|
* This clears the CPU from mm_cpumask for all processes, and then flushes the
|
||||||
|
* local TLB to ensure TLB coherency in case the CPU is onlined again.
|
||||||
|
*
|
||||||
|
* KVM guest translations are not necessarily flushed here. If KVM started
|
||||||
|
* using mm_cpumask or the Linux APIs which do, this would have to be resolved.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
void cleanup_cpu_mmu_context(void)
|
||||||
|
{
|
||||||
|
int cpu = smp_processor_id();
|
||||||
|
|
||||||
|
clear_tasks_mm_cpumask(cpu);
|
||||||
|
tlbiel_all();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -742,8 +742,7 @@ static int __init parse_numa_properties(void)
|
||||||
of_node_put(cpu);
|
of_node_put(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(nid > 0))
|
node_set_online(nid);
|
||||||
node_set_online(nid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells);
|
get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells);
|
||||||
|
|
|
@ -911,6 +911,8 @@ static int smp_core99_cpu_disable(void)
|
||||||
|
|
||||||
mpic_cpu_set_priority(0xf);
|
mpic_cpu_set_priority(0xf);
|
||||||
|
|
||||||
|
cleanup_cpu_mmu_context();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,11 +211,16 @@ static void __init pnv_init(void)
|
||||||
add_preferred_console("hvc", 0, NULL);
|
add_preferred_console("hvc", 0, NULL);
|
||||||
|
|
||||||
if (!radix_enabled()) {
|
if (!radix_enabled()) {
|
||||||
|
size_t size = sizeof(struct slb_entry) * mmu_slb_size;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Allocate per cpu area to save old slb contents during MCE */
|
/* Allocate per cpu area to save old slb contents during MCE */
|
||||||
for_each_possible_cpu(i)
|
for_each_possible_cpu(i) {
|
||||||
paca_ptrs[i]->mce_faulty_slbs = memblock_alloc_node(mmu_slb_size, __alignof__(*paca_ptrs[i]->mce_faulty_slbs), cpu_to_node(i));
|
paca_ptrs[i]->mce_faulty_slbs =
|
||||||
|
memblock_alloc_node(size,
|
||||||
|
__alignof__(struct slb_entry),
|
||||||
|
cpu_to_node(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,9 @@ static int pnv_smp_cpu_disable(void)
|
||||||
xive_smp_disable_cpu();
|
xive_smp_disable_cpu();
|
||||||
else
|
else
|
||||||
xics_migrate_irqs_away();
|
xics_migrate_irqs_away();
|
||||||
|
|
||||||
|
cleanup_cpu_mmu_context();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,9 @@ static int pseries_cpu_disable(void)
|
||||||
xive_smp_disable_cpu();
|
xive_smp_disable_cpu();
|
||||||
else
|
else
|
||||||
xics_migrate_irqs_away();
|
xics_migrate_irqs_away();
|
||||||
|
|
||||||
|
cleanup_cpu_mmu_context();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -458,7 +458,8 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
|
||||||
return hwirq;
|
return hwirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
virq = irq_create_mapping(NULL, hwirq);
|
virq = irq_create_mapping_affinity(NULL, hwirq,
|
||||||
|
entry->affinity);
|
||||||
|
|
||||||
if (!virq) {
|
if (!virq) {
|
||||||
pr_debug("rtas_msi: Failed mapping hwirq %d\n", hwirq);
|
pr_debug("rtas_msi: Failed mapping hwirq %d\n", hwirq);
|
||||||
|
|
|
@ -763,12 +763,7 @@ ENTRY(io_int_handler)
|
||||||
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
|
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
|
||||||
TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ
|
TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ
|
||||||
jo .Lio_restore
|
jo .Lio_restore
|
||||||
#if IS_ENABLED(CONFIG_TRACE_IRQFLAGS)
|
|
||||||
tmhh %r8,0x300
|
|
||||||
jz 1f
|
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
1:
|
|
||||||
#endif
|
|
||||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||||
.Lio_loop:
|
.Lio_loop:
|
||||||
lgr %r2,%r11 # pass pointer to pt_regs
|
lgr %r2,%r11 # pass pointer to pt_regs
|
||||||
|
@ -791,12 +786,7 @@ ENTRY(io_int_handler)
|
||||||
TSTMSK __LC_CPU_FLAGS,_CIF_WORK
|
TSTMSK __LC_CPU_FLAGS,_CIF_WORK
|
||||||
jnz .Lio_work
|
jnz .Lio_work
|
||||||
.Lio_restore:
|
.Lio_restore:
|
||||||
#if IS_ENABLED(CONFIG_TRACE_IRQFLAGS)
|
|
||||||
tm __PT_PSW(%r11),3
|
|
||||||
jno 0f
|
|
||||||
TRACE_IRQS_ON
|
TRACE_IRQS_ON
|
||||||
0:
|
|
||||||
#endif
|
|
||||||
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
|
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
|
||||||
tm __PT_PSW+1(%r11),0x01 # returning to user ?
|
tm __PT_PSW+1(%r11),0x01 # returning to user ?
|
||||||
jno .Lio_exit_kernel
|
jno .Lio_exit_kernel
|
||||||
|
@ -976,12 +966,7 @@ ENTRY(ext_int_handler)
|
||||||
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
|
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
|
||||||
TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ
|
TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ
|
||||||
jo .Lio_restore
|
jo .Lio_restore
|
||||||
#if IS_ENABLED(CONFIG_TRACE_IRQFLAGS)
|
|
||||||
tmhh %r8,0x300
|
|
||||||
jz 1f
|
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
1:
|
|
||||||
#endif
|
|
||||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||||
lgr %r2,%r11 # pass pointer to pt_regs
|
lgr %r2,%r11 # pass pointer to pt_regs
|
||||||
lghi %r3,EXT_INTERRUPT
|
lghi %r3,EXT_INTERRUPT
|
||||||
|
|
|
@ -33,7 +33,7 @@ EXPORT_SYMBOL(__delay);
|
||||||
|
|
||||||
static void __udelay_disabled(unsigned long long usecs)
|
static void __udelay_disabled(unsigned long long usecs)
|
||||||
{
|
{
|
||||||
unsigned long cr0, cr0_new, psw_mask, flags;
|
unsigned long cr0, cr0_new, psw_mask;
|
||||||
struct s390_idle_data idle;
|
struct s390_idle_data idle;
|
||||||
u64 end;
|
u64 end;
|
||||||
|
|
||||||
|
@ -45,9 +45,8 @@ static void __udelay_disabled(unsigned long long usecs)
|
||||||
psw_mask = __extract_psw() | PSW_MASK_EXT | PSW_MASK_WAIT;
|
psw_mask = __extract_psw() | PSW_MASK_EXT | PSW_MASK_WAIT;
|
||||||
set_clock_comparator(end);
|
set_clock_comparator(end);
|
||||||
set_cpu_flag(CIF_IGNORE_IRQ);
|
set_cpu_flag(CIF_IGNORE_IRQ);
|
||||||
local_irq_save(flags);
|
|
||||||
psw_idle(&idle, psw_mask);
|
psw_idle(&idle, psw_mask);
|
||||||
local_irq_restore(flags);
|
trace_hardirqs_off();
|
||||||
clear_cpu_flag(CIF_IGNORE_IRQ);
|
clear_cpu_flag(CIF_IGNORE_IRQ);
|
||||||
set_clock_comparator(S390_lowcore.clock_comparator);
|
set_clock_comparator(S390_lowcore.clock_comparator);
|
||||||
__ctl_load(cr0, 0, 0);
|
__ctl_load(cr0, 0, 0);
|
||||||
|
|
|
@ -103,9 +103,10 @@ static int zpci_set_irq_affinity(struct irq_data *data, const struct cpumask *de
|
||||||
{
|
{
|
||||||
struct msi_desc *entry = irq_get_msi_desc(data->irq);
|
struct msi_desc *entry = irq_get_msi_desc(data->irq);
|
||||||
struct msi_msg msg = entry->msg;
|
struct msi_msg msg = entry->msg;
|
||||||
|
int cpu_addr = smp_cpu_get_cpu_address(cpumask_first(dest));
|
||||||
|
|
||||||
msg.address_lo &= 0xff0000ff;
|
msg.address_lo &= 0xff0000ff;
|
||||||
msg.address_lo |= (cpumask_first(dest) << 8);
|
msg.address_lo |= (cpu_addr << 8);
|
||||||
pci_write_msi_msg(data->irq, &msg);
|
pci_write_msi_msg(data->irq, &msg);
|
||||||
|
|
||||||
return IRQ_SET_MASK_OK;
|
return IRQ_SET_MASK_OK;
|
||||||
|
@ -238,6 +239,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
unsigned long bit;
|
unsigned long bit;
|
||||||
struct msi_desc *msi;
|
struct msi_desc *msi;
|
||||||
struct msi_msg msg;
|
struct msi_msg msg;
|
||||||
|
int cpu_addr;
|
||||||
int rc, irq;
|
int rc, irq;
|
||||||
|
|
||||||
zdev->aisb = -1UL;
|
zdev->aisb = -1UL;
|
||||||
|
@ -287,9 +289,15 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
handle_percpu_irq);
|
handle_percpu_irq);
|
||||||
msg.data = hwirq - bit;
|
msg.data = hwirq - bit;
|
||||||
if (irq_delivery == DIRECTED) {
|
if (irq_delivery == DIRECTED) {
|
||||||
|
if (msi->affinity)
|
||||||
|
cpu = cpumask_first(&msi->affinity->mask);
|
||||||
|
else
|
||||||
|
cpu = 0;
|
||||||
|
cpu_addr = smp_cpu_get_cpu_address(cpu);
|
||||||
|
|
||||||
msg.address_lo = zdev->msi_addr & 0xff0000ff;
|
msg.address_lo = zdev->msi_addr & 0xff0000ff;
|
||||||
msg.address_lo |= msi->affinity ?
|
msg.address_lo |= (cpu_addr << 8);
|
||||||
(cpumask_first(&msi->affinity->mask) << 8) : 0;
|
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
airq_iv_set_data(zpci_ibv[cpu], hwirq, irq);
|
airq_iv_set_data(zpci_ibv[cpu], hwirq, irq);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ config X86
|
||||||
select ARCH_WANT_DEFAULT_BPF_JIT if X86_64
|
select ARCH_WANT_DEFAULT_BPF_JIT if X86_64
|
||||||
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
|
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
|
||||||
select ARCH_WANT_HUGE_PMD_SHARE
|
select ARCH_WANT_HUGE_PMD_SHARE
|
||||||
|
select ARCH_WANT_LD_ORPHAN_WARN
|
||||||
select ARCH_WANTS_THP_SWAP if X86_64
|
select ARCH_WANTS_THP_SWAP if X86_64
|
||||||
select BUILDTIME_TABLE_SORT
|
select BUILDTIME_TABLE_SORT
|
||||||
select CLKEVT_I8253
|
select CLKEVT_I8253
|
||||||
|
|
|
@ -209,9 +209,6 @@ ifdef CONFIG_X86_64
|
||||||
LDFLAGS_vmlinux += -z max-page-size=0x200000
|
LDFLAGS_vmlinux += -z max-page-size=0x200000
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# We never want expected sections to be placed heuristically by the
|
|
||||||
# linker. All sections should be explicitly named in the linker script.
|
|
||||||
LDFLAGS_vmlinux += $(call ld-option, --orphan-handling=warn)
|
|
||||||
|
|
||||||
archscripts: scripts_basic
|
archscripts: scripts_basic
|
||||||
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
|
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
|
||||||
|
|
|
@ -61,7 +61,9 @@ KBUILD_LDFLAGS += $(call ld-option,--no-ld-generated-unwind-info)
|
||||||
# Compressed kernel should be built as PIE since it may be loaded at any
|
# Compressed kernel should be built as PIE since it may be loaded at any
|
||||||
# address by the bootloader.
|
# address by the bootloader.
|
||||||
LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
|
LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
|
||||||
LDFLAGS_vmlinux += $(call ld-option, --orphan-handling=warn)
|
ifdef CONFIG_LD_ORPHAN_WARN
|
||||||
|
LDFLAGS_vmlinux += --orphan-handling=warn
|
||||||
|
endif
|
||||||
LDFLAGS_vmlinux += -T
|
LDFLAGS_vmlinux += -T
|
||||||
|
|
||||||
hostprogs := mkpiggy
|
hostprogs := mkpiggy
|
||||||
|
|
|
@ -32,13 +32,12 @@ struct ghcb *boot_ghcb;
|
||||||
*/
|
*/
|
||||||
static bool insn_has_rep_prefix(struct insn *insn)
|
static bool insn_has_rep_prefix(struct insn *insn)
|
||||||
{
|
{
|
||||||
|
insn_byte_t p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
insn_get_prefixes(insn);
|
insn_get_prefixes(insn);
|
||||||
|
|
||||||
for (i = 0; i < insn->prefixes.nbytes; i++) {
|
for_each_insn_prefix(insn, i, p) {
|
||||||
insn_byte_t p = insn->prefixes.bytes[i];
|
|
||||||
|
|
||||||
if (p == 0xf2 || p == 0xf3)
|
if (p == 0xf2 || p == 0xf3)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1916,7 +1916,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
|
||||||
* that caused the PEBS record. It's called collision.
|
* that caused the PEBS record. It's called collision.
|
||||||
* If collision happened, the record will be dropped.
|
* If collision happened, the record will be dropped.
|
||||||
*/
|
*/
|
||||||
if (p->status != (1ULL << bit)) {
|
if (pebs_status != (1ULL << bit)) {
|
||||||
for_each_set_bit(i, (unsigned long *)&pebs_status, size)
|
for_each_set_bit(i, (unsigned long *)&pebs_status, size)
|
||||||
error[i]++;
|
error[i]++;
|
||||||
continue;
|
continue;
|
||||||
|
@ -1940,7 +1940,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
|
||||||
if (error[bit]) {
|
if (error[bit]) {
|
||||||
perf_log_lost_samples(event, error[bit]);
|
perf_log_lost_samples(event, error[bit]);
|
||||||
|
|
||||||
if (perf_event_account_interrupt(event))
|
if (iregs && perf_event_account_interrupt(event))
|
||||||
x86_pmu_stop(event, 0);
|
x86_pmu_stop(event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,21 @@ static inline int insn_offset_immediate(struct insn *insn)
|
||||||
return insn_offset_displacement(insn) + insn->displacement.nbytes;
|
return insn_offset_displacement(insn) + insn->displacement.nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for_each_insn_prefix() -- Iterate prefixes in the instruction
|
||||||
|
* @insn: Pointer to struct insn.
|
||||||
|
* @idx: Index storage.
|
||||||
|
* @prefix: Prefix byte.
|
||||||
|
*
|
||||||
|
* Iterate prefix bytes of given @insn. Each prefix byte is stored in @prefix
|
||||||
|
* and the index is stored in @idx (note that this @idx is just for a cursor,
|
||||||
|
* do not change it.)
|
||||||
|
* Since prefixes.nbytes can be bigger than 4 if some prefixes
|
||||||
|
* are repeated, it cannot be used for looping over the prefixes.
|
||||||
|
*/
|
||||||
|
#define for_each_insn_prefix(insn, idx, prefix) \
|
||||||
|
for (idx = 0; idx < ARRAY_SIZE(insn->prefixes.bytes) && (prefix = insn->prefixes.bytes[idx]) != 0; idx++)
|
||||||
|
|
||||||
#define POP_SS_OPCODE 0x1f
|
#define POP_SS_OPCODE 0x1f
|
||||||
#define MOV_SREG_OPCODE 0x8e
|
#define MOV_SREG_OPCODE 0x8e
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ static int __init early_set_hub_type(void)
|
||||||
/* UV4/4A only have a revision difference */
|
/* UV4/4A only have a revision difference */
|
||||||
case UV4_HUB_PART_NUMBER:
|
case UV4_HUB_PART_NUMBER:
|
||||||
uv_min_hub_revision_id = node_id.s.revision
|
uv_min_hub_revision_id = node_id.s.revision
|
||||||
+ UV4_HUB_REVISION_BASE;
|
+ UV4_HUB_REVISION_BASE - 1;
|
||||||
uv_hub_type_set(UV4);
|
uv_hub_type_set(UV4);
|
||||||
if (uv_min_hub_revision_id == UV4A_HUB_REVISION_BASE)
|
if (uv_min_hub_revision_id == UV4A_HUB_REVISION_BASE)
|
||||||
uv_hub_type_set(UV4|UV4A);
|
uv_hub_type_set(UV4|UV4A);
|
||||||
|
|
|
@ -570,6 +570,8 @@ static void domain_add_cpu(int cpu, struct rdt_resource *r)
|
||||||
|
|
||||||
if (d) {
|
if (d) {
|
||||||
cpumask_set_cpu(cpu, &d->cpu_mask);
|
cpumask_set_cpu(cpu, &d->cpu_mask);
|
||||||
|
if (r->cache.arch_has_per_cpu_cfg)
|
||||||
|
rdt_domain_reconfigure_cdp(r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -923,6 +925,7 @@ static __init void rdt_init_res_defs_intel(void)
|
||||||
r->rid == RDT_RESOURCE_L2CODE) {
|
r->rid == RDT_RESOURCE_L2CODE) {
|
||||||
r->cache.arch_has_sparse_bitmaps = false;
|
r->cache.arch_has_sparse_bitmaps = false;
|
||||||
r->cache.arch_has_empty_bitmaps = false;
|
r->cache.arch_has_empty_bitmaps = false;
|
||||||
|
r->cache.arch_has_per_cpu_cfg = false;
|
||||||
} else if (r->rid == RDT_RESOURCE_MBA) {
|
} else if (r->rid == RDT_RESOURCE_MBA) {
|
||||||
r->msr_base = MSR_IA32_MBA_THRTL_BASE;
|
r->msr_base = MSR_IA32_MBA_THRTL_BASE;
|
||||||
r->msr_update = mba_wrmsr_intel;
|
r->msr_update = mba_wrmsr_intel;
|
||||||
|
@ -943,6 +946,7 @@ static __init void rdt_init_res_defs_amd(void)
|
||||||
r->rid == RDT_RESOURCE_L2CODE) {
|
r->rid == RDT_RESOURCE_L2CODE) {
|
||||||
r->cache.arch_has_sparse_bitmaps = true;
|
r->cache.arch_has_sparse_bitmaps = true;
|
||||||
r->cache.arch_has_empty_bitmaps = true;
|
r->cache.arch_has_empty_bitmaps = true;
|
||||||
|
r->cache.arch_has_per_cpu_cfg = true;
|
||||||
} else if (r->rid == RDT_RESOURCE_MBA) {
|
} else if (r->rid == RDT_RESOURCE_MBA) {
|
||||||
r->msr_base = MSR_IA32_MBA_BW_BASE;
|
r->msr_base = MSR_IA32_MBA_BW_BASE;
|
||||||
r->msr_update = mba_wrmsr_amd;
|
r->msr_update = mba_wrmsr_amd;
|
||||||
|
|
|
@ -360,6 +360,8 @@ struct msr_param {
|
||||||
* executing entities
|
* executing entities
|
||||||
* @arch_has_sparse_bitmaps: True if a bitmap like f00f is valid.
|
* @arch_has_sparse_bitmaps: True if a bitmap like f00f is valid.
|
||||||
* @arch_has_empty_bitmaps: True if the '0' bitmap is valid.
|
* @arch_has_empty_bitmaps: True if the '0' bitmap is valid.
|
||||||
|
* @arch_has_per_cpu_cfg: True if QOS_CFG register for this cache
|
||||||
|
* level has CPU scope.
|
||||||
*/
|
*/
|
||||||
struct rdt_cache {
|
struct rdt_cache {
|
||||||
unsigned int cbm_len;
|
unsigned int cbm_len;
|
||||||
|
@ -369,6 +371,7 @@ struct rdt_cache {
|
||||||
unsigned int shareable_bits;
|
unsigned int shareable_bits;
|
||||||
bool arch_has_sparse_bitmaps;
|
bool arch_has_sparse_bitmaps;
|
||||||
bool arch_has_empty_bitmaps;
|
bool arch_has_empty_bitmaps;
|
||||||
|
bool arch_has_per_cpu_cfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1909,8 +1909,13 @@ static int set_cache_qos_cfg(int level, bool enable)
|
||||||
|
|
||||||
r_l = &rdt_resources_all[level];
|
r_l = &rdt_resources_all[level];
|
||||||
list_for_each_entry(d, &r_l->domains, list) {
|
list_for_each_entry(d, &r_l->domains, list) {
|
||||||
/* Pick one CPU from each domain instance to update MSR */
|
if (r_l->cache.arch_has_per_cpu_cfg)
|
||||||
cpumask_set_cpu(cpumask_any(&d->cpu_mask), cpu_mask);
|
/* Pick all the CPUs in the domain instance */
|
||||||
|
for_each_cpu(cpu, &d->cpu_mask)
|
||||||
|
cpumask_set_cpu(cpu, cpu_mask);
|
||||||
|
else
|
||||||
|
/* Pick one CPU from each domain instance to update MSR */
|
||||||
|
cpumask_set_cpu(cpumask_any(&d->cpu_mask), cpu_mask);
|
||||||
}
|
}
|
||||||
cpu = get_cpu();
|
cpu = get_cpu();
|
||||||
/* Update QOS_CFG MSR on this cpu if it's in cpu_mask. */
|
/* Update QOS_CFG MSR on this cpu if it's in cpu_mask. */
|
||||||
|
|
|
@ -255,12 +255,13 @@ static volatile u32 good_2byte_insns[256 / 32] = {
|
||||||
|
|
||||||
static bool is_prefix_bad(struct insn *insn)
|
static bool is_prefix_bad(struct insn *insn)
|
||||||
{
|
{
|
||||||
|
insn_byte_t p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < insn->prefixes.nbytes; i++) {
|
for_each_insn_prefix(insn, i, p) {
|
||||||
insn_attr_t attr;
|
insn_attr_t attr;
|
||||||
|
|
||||||
attr = inat_get_opcode_attribute(insn->prefixes.bytes[i]);
|
attr = inat_get_opcode_attribute(p);
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case INAT_MAKE_PREFIX(INAT_PFX_ES):
|
case INAT_MAKE_PREFIX(INAT_PFX_ES):
|
||||||
case INAT_MAKE_PREFIX(INAT_PFX_CS):
|
case INAT_MAKE_PREFIX(INAT_PFX_CS):
|
||||||
|
@ -715,6 +716,7 @@ static const struct uprobe_xol_ops push_xol_ops = {
|
||||||
static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
|
static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
|
||||||
{
|
{
|
||||||
u8 opc1 = OPCODE1(insn);
|
u8 opc1 = OPCODE1(insn);
|
||||||
|
insn_byte_t p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
switch (opc1) {
|
switch (opc1) {
|
||||||
|
@ -746,8 +748,8 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
|
||||||
* Intel and AMD behavior differ in 64-bit mode: Intel ignores 66 prefix.
|
* Intel and AMD behavior differ in 64-bit mode: Intel ignores 66 prefix.
|
||||||
* No one uses these insns, reject any branch insns with such prefix.
|
* No one uses these insns, reject any branch insns with such prefix.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < insn->prefixes.nbytes; i++) {
|
for_each_insn_prefix(insn, i, p) {
|
||||||
if (insn->prefixes.bytes[i] == 0x66)
|
if (p == 0x66)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,12 @@ static bool is_string_insn(struct insn *insn)
|
||||||
*/
|
*/
|
||||||
bool insn_has_rep_prefix(struct insn *insn)
|
bool insn_has_rep_prefix(struct insn *insn)
|
||||||
{
|
{
|
||||||
|
insn_byte_t p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
insn_get_prefixes(insn);
|
insn_get_prefixes(insn);
|
||||||
|
|
||||||
for (i = 0; i < insn->prefixes.nbytes; i++) {
|
for_each_insn_prefix(insn, i, p) {
|
||||||
insn_byte_t p = insn->prefixes.bytes[i];
|
|
||||||
|
|
||||||
if (p == 0xf2 || p == 0xf3)
|
if (p == 0xf2 || p == 0xf3)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -95,14 +94,15 @@ static int get_seg_reg_override_idx(struct insn *insn)
|
||||||
{
|
{
|
||||||
int idx = INAT_SEG_REG_DEFAULT;
|
int idx = INAT_SEG_REG_DEFAULT;
|
||||||
int num_overrides = 0, i;
|
int num_overrides = 0, i;
|
||||||
|
insn_byte_t p;
|
||||||
|
|
||||||
insn_get_prefixes(insn);
|
insn_get_prefixes(insn);
|
||||||
|
|
||||||
/* Look for any segment override prefixes. */
|
/* Look for any segment override prefixes. */
|
||||||
for (i = 0; i < insn->prefixes.nbytes; i++) {
|
for_each_insn_prefix(insn, i, p) {
|
||||||
insn_attr_t attr;
|
insn_attr_t attr;
|
||||||
|
|
||||||
attr = inat_get_opcode_attribute(insn->prefixes.bytes[i]);
|
attr = inat_get_opcode_attribute(p);
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case INAT_MAKE_PREFIX(INAT_PFX_CS):
|
case INAT_MAKE_PREFIX(INAT_PFX_CS):
|
||||||
idx = INAT_SEG_REG_CS;
|
idx = INAT_SEG_REG_CS;
|
||||||
|
|
|
@ -144,7 +144,7 @@ static struct bio *blk_bio_write_same_split(struct request_queue *q,
|
||||||
static inline unsigned get_max_io_size(struct request_queue *q,
|
static inline unsigned get_max_io_size(struct request_queue *q,
|
||||||
struct bio *bio)
|
struct bio *bio)
|
||||||
{
|
{
|
||||||
unsigned sectors = blk_max_size_offset(q, bio->bi_iter.bi_sector);
|
unsigned sectors = blk_max_size_offset(q, bio->bi_iter.bi_sector, 0);
|
||||||
unsigned max_sectors = sectors;
|
unsigned max_sectors = sectors;
|
||||||
unsigned pbs = queue_physical_block_size(q) >> SECTOR_SHIFT;
|
unsigned pbs = queue_physical_block_size(q) >> SECTOR_SHIFT;
|
||||||
unsigned lbs = queue_logical_block_size(q) >> SECTOR_SHIFT;
|
unsigned lbs = queue_logical_block_size(q) >> SECTOR_SHIFT;
|
||||||
|
|
|
@ -547,7 +547,10 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
|
||||||
|
|
||||||
t->io_min = max(t->io_min, b->io_min);
|
t->io_min = max(t->io_min, b->io_min);
|
||||||
t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
|
t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
|
||||||
t->chunk_sectors = lcm_not_zero(t->chunk_sectors, b->chunk_sectors);
|
|
||||||
|
/* Set non-power-of-2 compatible chunk_sectors boundary */
|
||||||
|
if (b->chunk_sectors)
|
||||||
|
t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
|
||||||
|
|
||||||
/* Physical block size a multiple of the logical block size? */
|
/* Physical block size a multiple of the logical block size? */
|
||||||
if (t->physical_block_size & (t->logical_block_size - 1)) {
|
if (t->physical_block_size & (t->logical_block_size - 1)) {
|
||||||
|
|
|
@ -145,6 +145,7 @@ obj-$(CONFIG_OF) += of/
|
||||||
obj-$(CONFIG_SSB) += ssb/
|
obj-$(CONFIG_SSB) += ssb/
|
||||||
obj-$(CONFIG_BCMA) += bcma/
|
obj-$(CONFIG_BCMA) += bcma/
|
||||||
obj-$(CONFIG_VHOST_RING) += vhost/
|
obj-$(CONFIG_VHOST_RING) += vhost/
|
||||||
|
obj-$(CONFIG_VHOST_IOTLB) += vhost/
|
||||||
obj-$(CONFIG_VHOST) += vhost/
|
obj-$(CONFIG_VHOST) += vhost/
|
||||||
obj-$(CONFIG_VLYNQ) += vlynq/
|
obj-$(CONFIG_VLYNQ) += vlynq/
|
||||||
obj-$(CONFIG_GREYBUS) += greybus/
|
obj-$(CONFIG_GREYBUS) += greybus/
|
||||||
|
|
|
@ -47,27 +47,20 @@ static int spk_ttyio_ldisc_open(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct spk_ldisc_data *ldisc_data;
|
struct spk_ldisc_data *ldisc_data;
|
||||||
|
|
||||||
|
if (tty != speakup_tty)
|
||||||
|
/* Somebody tried to use this line discipline outside speakup */
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
if (!tty->ops->write)
|
if (!tty->ops->write)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
mutex_lock(&speakup_tty_mutex);
|
|
||||||
if (speakup_tty) {
|
|
||||||
mutex_unlock(&speakup_tty_mutex);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
speakup_tty = tty;
|
|
||||||
|
|
||||||
ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);
|
ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);
|
||||||
if (!ldisc_data) {
|
if (!ldisc_data)
|
||||||
speakup_tty = NULL;
|
|
||||||
mutex_unlock(&speakup_tty_mutex);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
init_completion(&ldisc_data->completion);
|
init_completion(&ldisc_data->completion);
|
||||||
ldisc_data->buf_free = true;
|
ldisc_data->buf_free = true;
|
||||||
speakup_tty->disc_data = ldisc_data;
|
tty->disc_data = ldisc_data;
|
||||||
mutex_unlock(&speakup_tty_mutex);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -191,9 +184,25 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
|
||||||
|
|
||||||
tty_unlock(tty);
|
tty_unlock(tty);
|
||||||
|
|
||||||
|
mutex_lock(&speakup_tty_mutex);
|
||||||
|
speakup_tty = tty;
|
||||||
ret = tty_set_ldisc(tty, N_SPEAKUP);
|
ret = tty_set_ldisc(tty, N_SPEAKUP);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
|
speakup_tty = NULL;
|
||||||
|
mutex_unlock(&speakup_tty_mutex);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
/* Success */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
|
||||||
|
|
||||||
|
tty_lock(tty);
|
||||||
|
if (tty->ops->close)
|
||||||
|
tty->ops->close(tty, NULL);
|
||||||
|
tty_unlock(tty);
|
||||||
|
|
||||||
|
tty_kclose(tty);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,7 @@ config FPGA_DFL
|
||||||
tristate "FPGA Device Feature List (DFL) support"
|
tristate "FPGA Device Feature List (DFL) support"
|
||||||
select FPGA_BRIDGE
|
select FPGA_BRIDGE
|
||||||
select FPGA_REGION
|
select FPGA_REGION
|
||||||
|
depends on HAS_IOMEM
|
||||||
help
|
help
|
||||||
Device Feature List (DFL) defines a feature list structure that
|
Device Feature List (DFL) defines a feature list structure that
|
||||||
creates a linked list of feature headers within the MMIO space
|
creates a linked list of feature headers within the MMIO space
|
||||||
|
|
|
@ -1011,6 +1011,11 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, boo
|
||||||
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
|
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_CNTL, tmp);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_CNTL, tmp);
|
||||||
|
|
||||||
|
/* Stall DPG before WPTR/RPTR reset */
|
||||||
|
WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS),
|
||||||
|
UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK,
|
||||||
|
~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK);
|
||||||
|
|
||||||
/* set the write pointer delay */
|
/* set the write pointer delay */
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR_CNTL, 0);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR_CNTL, 0);
|
||||||
|
|
||||||
|
@ -1033,6 +1038,10 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, boo
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR,
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR,
|
||||||
lower_32_bits(ring->wptr));
|
lower_32_bits(ring->wptr));
|
||||||
|
|
||||||
|
/* Unstall DPG */
|
||||||
|
WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS),
|
||||||
|
0, ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,8 +1565,14 @@ static int vcn_v3_0_pause_dpg_mode(struct amdgpu_device *adev,
|
||||||
UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK,
|
UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK,
|
||||||
UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK);
|
UVD_DPG_PAUSE__NJ_PAUSE_DPG_ACK_MASK);
|
||||||
|
|
||||||
|
/* Stall DPG before WPTR/RPTR reset */
|
||||||
|
WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS),
|
||||||
|
UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK,
|
||||||
|
~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK);
|
||||||
|
|
||||||
/* Restore */
|
/* Restore */
|
||||||
ring = &adev->vcn.inst[inst_idx].ring_enc[0];
|
ring = &adev->vcn.inst[inst_idx].ring_enc[0];
|
||||||
|
ring->wptr = 0;
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO, ring->gpu_addr);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO, ring->gpu_addr);
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE, ring->ring_size / 4);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE, ring->ring_size / 4);
|
||||||
|
@ -1565,14 +1580,16 @@ static int vcn_v3_0_pause_dpg_mode(struct amdgpu_device *adev,
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR, lower_32_bits(ring->wptr));
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR, lower_32_bits(ring->wptr));
|
||||||
|
|
||||||
ring = &adev->vcn.inst[inst_idx].ring_enc[1];
|
ring = &adev->vcn.inst[inst_idx].ring_enc[1];
|
||||||
|
ring->wptr = 0;
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO2, ring->gpu_addr);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_LO2, ring->gpu_addr);
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE2, ring->ring_size / 4);
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_SIZE2, ring->ring_size / 4);
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_RPTR2, lower_32_bits(ring->wptr));
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_RPTR2, lower_32_bits(ring->wptr));
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR2, lower_32_bits(ring->wptr));
|
WREG32_SOC15(VCN, inst_idx, mmUVD_RB_WPTR2, lower_32_bits(ring->wptr));
|
||||||
|
|
||||||
WREG32_SOC15(VCN, inst_idx, mmUVD_RBC_RB_WPTR,
|
/* Unstall DPG */
|
||||||
RREG32_SOC15(VCN, inst_idx, mmUVD_SCRATCH2) & 0x7FFFFFFF);
|
WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS),
|
||||||
|
0, ~UVD_POWER_STATUS__STALL_DPG_POWER_UP_MASK);
|
||||||
|
|
||||||
SOC15_WAIT_ON_RREG(VCN, inst_idx, mmUVD_POWER_STATUS,
|
SOC15_WAIT_ON_RREG(VCN, inst_idx, mmUVD_POWER_STATUS,
|
||||||
UVD_PGFSM_CONFIG__UVDM_UVDU_PWR_ON, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
|
UVD_PGFSM_CONFIG__UVDM_UVDU_PWR_ON, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
|
||||||
|
@ -1630,10 +1647,6 @@ static void vcn_v3_0_dec_ring_set_wptr(struct amdgpu_ring *ring)
|
||||||
{
|
{
|
||||||
struct amdgpu_device *adev = ring->adev;
|
struct amdgpu_device *adev = ring->adev;
|
||||||
|
|
||||||
if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
|
|
||||||
WREG32_SOC15(VCN, ring->me, mmUVD_SCRATCH2,
|
|
||||||
lower_32_bits(ring->wptr) | 0x80000000);
|
|
||||||
|
|
||||||
if (ring->use_doorbell) {
|
if (ring->use_doorbell) {
|
||||||
adev->wb.wb[ring->wptr_offs] = lower_32_bits(ring->wptr);
|
adev->wb.wb[ring->wptr_offs] = lower_32_bits(ring->wptr);
|
||||||
WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
|
WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
|
||||||
|
|
|
@ -163,8 +163,17 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base,
|
||||||
new_clocks->dppclk_khz = 100000;
|
new_clocks->dppclk_khz = 100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) {
|
/*
|
||||||
if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz)
|
* Temporally ignore thew 0 cases for disp and dpp clks.
|
||||||
|
* We may have a new feature that requires 0 clks in the future.
|
||||||
|
*/
|
||||||
|
if (new_clocks->dppclk_khz == 0 || new_clocks->dispclk_khz == 0) {
|
||||||
|
new_clocks->dppclk_khz = clk_mgr_base->clks.dppclk_khz;
|
||||||
|
new_clocks->dispclk_khz = clk_mgr_base->clks.dispclk_khz;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) {
|
||||||
|
if (clk_mgr_base->clks.dppclk_khz > new_clocks->dppclk_khz)
|
||||||
dpp_clock_lowered = true;
|
dpp_clock_lowered = true;
|
||||||
clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz;
|
clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz;
|
||||||
update_dppclk = true;
|
update_dppclk = true;
|
||||||
|
|
|
@ -1164,7 +1164,12 @@ int smu_v11_0_set_fan_speed_rpm(struct smu_context *smu,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
crystal_clock_freq = amdgpu_asic_get_xclk(adev);
|
/*
|
||||||
|
* crystal_clock_freq div by 4 is required since the fan control
|
||||||
|
* module refers to 25MHz
|
||||||
|
*/
|
||||||
|
|
||||||
|
crystal_clock_freq = amdgpu_asic_get_xclk(adev) / 4;
|
||||||
tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
|
tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
|
||||||
WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
|
WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
|
||||||
REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
|
REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
|
||||||
|
|
|
@ -18021,16 +18021,6 @@ int intel_modeset_init_nogem(struct drm_i915_private *i915)
|
||||||
if (!HAS_GMCH(i915))
|
if (!HAS_GMCH(i915))
|
||||||
sanitize_watermarks(i915);
|
sanitize_watermarks(i915);
|
||||||
|
|
||||||
/*
|
|
||||||
* Force all active planes to recompute their states. So that on
|
|
||||||
* mode_setcrtc after probe, all the intel_plane_state variables
|
|
||||||
* are already calculated and there is no assert_plane warnings
|
|
||||||
* during bootup.
|
|
||||||
*/
|
|
||||||
ret = intel_initial_commit(dev);
|
|
||||||
if (ret)
|
|
||||||
drm_dbg_kms(&i915->drm, "Initial commit in probe failed.\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18039,11 +18029,21 @@ int intel_modeset_init(struct drm_i915_private *i915)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
intel_overlay_setup(i915);
|
|
||||||
|
|
||||||
if (!HAS_DISPLAY(i915))
|
if (!HAS_DISPLAY(i915))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Force all active planes to recompute their states. So that on
|
||||||
|
* mode_setcrtc after probe, all the intel_plane_state variables
|
||||||
|
* are already calculated and there is no assert_plane warnings
|
||||||
|
* during bootup.
|
||||||
|
*/
|
||||||
|
ret = intel_initial_commit(&i915->drm);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
intel_overlay_setup(i915);
|
||||||
|
|
||||||
ret = intel_fbdev_init(&i915->drm);
|
ret = intel_fbdev_init(&i915->drm);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -101,18 +101,37 @@ static void __intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b)
|
||||||
intel_gt_pm_put_async(b->irq_engine->gt);
|
intel_gt_pm_put_async(b->irq_engine->gt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b)
|
||||||
|
{
|
||||||
|
spin_lock(&b->irq_lock);
|
||||||
|
if (b->irq_armed)
|
||||||
|
__intel_breadcrumbs_disarm_irq(b);
|
||||||
|
spin_unlock(&b->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static void add_signaling_context(struct intel_breadcrumbs *b,
|
static void add_signaling_context(struct intel_breadcrumbs *b,
|
||||||
struct intel_context *ce)
|
struct intel_context *ce)
|
||||||
{
|
{
|
||||||
intel_context_get(ce);
|
lockdep_assert_held(&ce->signal_lock);
|
||||||
list_add_tail(&ce->signal_link, &b->signalers);
|
|
||||||
|
spin_lock(&b->signalers_lock);
|
||||||
|
list_add_rcu(&ce->signal_link, &b->signalers);
|
||||||
|
spin_unlock(&b->signalers_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_signaling_context(struct intel_breadcrumbs *b,
|
static bool remove_signaling_context(struct intel_breadcrumbs *b,
|
||||||
struct intel_context *ce)
|
struct intel_context *ce)
|
||||||
{
|
{
|
||||||
list_del(&ce->signal_link);
|
lockdep_assert_held(&ce->signal_lock);
|
||||||
intel_context_put(ce);
|
|
||||||
|
if (!list_empty(&ce->signals))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
spin_lock(&b->signalers_lock);
|
||||||
|
list_del_rcu(&ce->signal_link);
|
||||||
|
spin_unlock(&b->signalers_lock);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __request_completed(const struct i915_request *rq)
|
static inline bool __request_completed(const struct i915_request *rq)
|
||||||
|
@ -175,6 +194,8 @@ static void add_retire(struct intel_breadcrumbs *b, struct intel_timeline *tl)
|
||||||
|
|
||||||
static bool __signal_request(struct i915_request *rq)
|
static bool __signal_request(struct i915_request *rq)
|
||||||
{
|
{
|
||||||
|
GEM_BUG_ON(test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags));
|
||||||
|
|
||||||
if (!__dma_fence_signal(&rq->fence)) {
|
if (!__dma_fence_signal(&rq->fence)) {
|
||||||
i915_request_put(rq);
|
i915_request_put(rq);
|
||||||
return false;
|
return false;
|
||||||
|
@ -195,15 +216,12 @@ static void signal_irq_work(struct irq_work *work)
|
||||||
struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work);
|
struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work);
|
||||||
const ktime_t timestamp = ktime_get();
|
const ktime_t timestamp = ktime_get();
|
||||||
struct llist_node *signal, *sn;
|
struct llist_node *signal, *sn;
|
||||||
struct intel_context *ce, *cn;
|
struct intel_context *ce;
|
||||||
struct list_head *pos, *next;
|
|
||||||
|
|
||||||
signal = NULL;
|
signal = NULL;
|
||||||
if (unlikely(!llist_empty(&b->signaled_requests)))
|
if (unlikely(!llist_empty(&b->signaled_requests)))
|
||||||
signal = llist_del_all(&b->signaled_requests);
|
signal = llist_del_all(&b->signaled_requests);
|
||||||
|
|
||||||
spin_lock(&b->irq_lock);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep the irq armed until the interrupt after all listeners are gone.
|
* Keep the irq armed until the interrupt after all listeners are gone.
|
||||||
*
|
*
|
||||||
|
@ -229,47 +247,44 @@ static void signal_irq_work(struct irq_work *work)
|
||||||
* interrupt draw less ire from other users of the system and tools
|
* interrupt draw less ire from other users of the system and tools
|
||||||
* like powertop.
|
* like powertop.
|
||||||
*/
|
*/
|
||||||
if (!signal && b->irq_armed && list_empty(&b->signalers))
|
if (!signal && READ_ONCE(b->irq_armed) && list_empty(&b->signalers))
|
||||||
__intel_breadcrumbs_disarm_irq(b);
|
intel_breadcrumbs_disarm_irq(b);
|
||||||
|
|
||||||
list_for_each_entry_safe(ce, cn, &b->signalers, signal_link) {
|
rcu_read_lock();
|
||||||
GEM_BUG_ON(list_empty(&ce->signals));
|
list_for_each_entry_rcu(ce, &b->signalers, signal_link) {
|
||||||
|
struct i915_request *rq;
|
||||||
|
|
||||||
list_for_each_safe(pos, next, &ce->signals) {
|
list_for_each_entry_rcu(rq, &ce->signals, signal_link) {
|
||||||
struct i915_request *rq =
|
bool release;
|
||||||
list_entry(pos, typeof(*rq), signal_link);
|
|
||||||
|
|
||||||
GEM_BUG_ON(!check_signal_order(ce, rq));
|
|
||||||
if (!__request_completed(rq))
|
if (!__request_completed(rq))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (!test_and_clear_bit(I915_FENCE_FLAG_SIGNAL,
|
||||||
|
&rq->fence.flags))
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Queue for execution after dropping the signaling
|
* Queue for execution after dropping the signaling
|
||||||
* spinlock as the callback chain may end up adding
|
* spinlock as the callback chain may end up adding
|
||||||
* more signalers to the same context or engine.
|
* more signalers to the same context or engine.
|
||||||
*/
|
*/
|
||||||
clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
|
spin_lock(&ce->signal_lock);
|
||||||
|
list_del_rcu(&rq->signal_link);
|
||||||
|
release = remove_signaling_context(b, ce);
|
||||||
|
spin_unlock(&ce->signal_lock);
|
||||||
|
|
||||||
if (__signal_request(rq))
|
if (__signal_request(rq))
|
||||||
/* We own signal_node now, xfer to local list */
|
/* We own signal_node now, xfer to local list */
|
||||||
signal = slist_add(&rq->signal_node, signal);
|
signal = slist_add(&rq->signal_node, signal);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (release) {
|
||||||
* We process the list deletion in bulk, only using a list_add
|
|
||||||
* (not list_move) above but keeping the status of
|
|
||||||
* rq->signal_link known with the I915_FENCE_FLAG_SIGNAL bit.
|
|
||||||
*/
|
|
||||||
if (!list_is_first(pos, &ce->signals)) {
|
|
||||||
/* Advance the list to the first incomplete request */
|
|
||||||
__list_del_many(&ce->signals, pos);
|
|
||||||
if (&ce->signals == pos) { /* now empty */
|
|
||||||
add_retire(b, ce->timeline);
|
add_retire(b, ce->timeline);
|
||||||
remove_signaling_context(b, ce);
|
intel_context_put(ce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
spin_unlock(&b->irq_lock);
|
|
||||||
|
|
||||||
llist_for_each_safe(signal, sn, signal) {
|
llist_for_each_safe(signal, sn, signal) {
|
||||||
struct i915_request *rq =
|
struct i915_request *rq =
|
||||||
|
@ -298,14 +313,15 @@ intel_breadcrumbs_create(struct intel_engine_cs *irq_engine)
|
||||||
if (!b)
|
if (!b)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
spin_lock_init(&b->irq_lock);
|
b->irq_engine = irq_engine;
|
||||||
|
|
||||||
|
spin_lock_init(&b->signalers_lock);
|
||||||
INIT_LIST_HEAD(&b->signalers);
|
INIT_LIST_HEAD(&b->signalers);
|
||||||
init_llist_head(&b->signaled_requests);
|
init_llist_head(&b->signaled_requests);
|
||||||
|
|
||||||
|
spin_lock_init(&b->irq_lock);
|
||||||
init_irq_work(&b->irq_work, signal_irq_work);
|
init_irq_work(&b->irq_work, signal_irq_work);
|
||||||
|
|
||||||
b->irq_engine = irq_engine;
|
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,9 +363,9 @@ void intel_breadcrumbs_free(struct intel_breadcrumbs *b)
|
||||||
kfree(b);
|
kfree(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insert_breadcrumb(struct i915_request *rq,
|
static void insert_breadcrumb(struct i915_request *rq)
|
||||||
struct intel_breadcrumbs *b)
|
|
||||||
{
|
{
|
||||||
|
struct intel_breadcrumbs *b = READ_ONCE(rq->engine)->breadcrumbs;
|
||||||
struct intel_context *ce = rq->context;
|
struct intel_context *ce = rq->context;
|
||||||
struct list_head *pos;
|
struct list_head *pos;
|
||||||
|
|
||||||
|
@ -371,6 +387,7 @@ static void insert_breadcrumb(struct i915_request *rq,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_empty(&ce->signals)) {
|
if (list_empty(&ce->signals)) {
|
||||||
|
intel_context_get(ce);
|
||||||
add_signaling_context(b, ce);
|
add_signaling_context(b, ce);
|
||||||
pos = &ce->signals;
|
pos = &ce->signals;
|
||||||
} else {
|
} else {
|
||||||
|
@ -396,8 +413,9 @@ static void insert_breadcrumb(struct i915_request *rq,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_add(&rq->signal_link, pos);
|
list_add_rcu(&rq->signal_link, pos);
|
||||||
GEM_BUG_ON(!check_signal_order(ce, rq));
|
GEM_BUG_ON(!check_signal_order(ce, rq));
|
||||||
|
GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &rq->fence.flags));
|
||||||
set_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
|
set_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -410,7 +428,7 @@ static void insert_breadcrumb(struct i915_request *rq,
|
||||||
|
|
||||||
bool i915_request_enable_breadcrumb(struct i915_request *rq)
|
bool i915_request_enable_breadcrumb(struct i915_request *rq)
|
||||||
{
|
{
|
||||||
struct intel_breadcrumbs *b;
|
struct intel_context *ce = rq->context;
|
||||||
|
|
||||||
/* Serialises with i915_request_retire() using rq->lock */
|
/* Serialises with i915_request_retire() using rq->lock */
|
||||||
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &rq->fence.flags))
|
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &rq->fence.flags))
|
||||||
|
@ -425,67 +443,30 @@ bool i915_request_enable_breadcrumb(struct i915_request *rq)
|
||||||
if (!test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags))
|
if (!test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/*
|
spin_lock(&ce->signal_lock);
|
||||||
* rq->engine is locked by rq->engine->active.lock. That however
|
|
||||||
* is not known until after rq->engine has been dereferenced and
|
|
||||||
* the lock acquired. Hence we acquire the lock and then validate
|
|
||||||
* that rq->engine still matches the lock we hold for it.
|
|
||||||
*
|
|
||||||
* Here, we are using the breadcrumb lock as a proxy for the
|
|
||||||
* rq->engine->active.lock, and we know that since the breadcrumb
|
|
||||||
* will be serialised within i915_request_submit/i915_request_unsubmit,
|
|
||||||
* the engine cannot change while active as long as we hold the
|
|
||||||
* breadcrumb lock on that engine.
|
|
||||||
*
|
|
||||||
* From the dma_fence_enable_signaling() path, we are outside of the
|
|
||||||
* request submit/unsubmit path, and so we must be more careful to
|
|
||||||
* acquire the right lock.
|
|
||||||
*/
|
|
||||||
b = READ_ONCE(rq->engine)->breadcrumbs;
|
|
||||||
spin_lock(&b->irq_lock);
|
|
||||||
while (unlikely(b != READ_ONCE(rq->engine)->breadcrumbs)) {
|
|
||||||
spin_unlock(&b->irq_lock);
|
|
||||||
b = READ_ONCE(rq->engine)->breadcrumbs;
|
|
||||||
spin_lock(&b->irq_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now that we are finally serialised with request submit/unsubmit,
|
|
||||||
* [with b->irq_lock] and with i915_request_retire() [via checking
|
|
||||||
* SIGNALED with rq->lock] confirm the request is indeed active. If
|
|
||||||
* it is no longer active, the breadcrumb will be attached upon
|
|
||||||
* i915_request_submit().
|
|
||||||
*/
|
|
||||||
if (test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags))
|
if (test_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags))
|
||||||
insert_breadcrumb(rq, b);
|
insert_breadcrumb(rq);
|
||||||
|
spin_unlock(&ce->signal_lock);
|
||||||
spin_unlock(&b->irq_lock);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void i915_request_cancel_breadcrumb(struct i915_request *rq)
|
void i915_request_cancel_breadcrumb(struct i915_request *rq)
|
||||||
{
|
{
|
||||||
struct intel_breadcrumbs *b = rq->engine->breadcrumbs;
|
struct intel_context *ce = rq->context;
|
||||||
|
bool release;
|
||||||
|
|
||||||
/*
|
if (!test_and_clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags))
|
||||||
* We must wait for b->irq_lock so that we know the interrupt handler
|
return;
|
||||||
* has released its reference to the intel_context and has completed
|
|
||||||
* the DMA_FENCE_FLAG_SIGNALED_BIT/I915_FENCE_FLAG_SIGNAL dance (if
|
|
||||||
* required).
|
|
||||||
*/
|
|
||||||
spin_lock(&b->irq_lock);
|
|
||||||
if (test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags)) {
|
|
||||||
struct intel_context *ce = rq->context;
|
|
||||||
|
|
||||||
list_del(&rq->signal_link);
|
spin_lock(&ce->signal_lock);
|
||||||
if (list_empty(&ce->signals))
|
list_del_rcu(&rq->signal_link);
|
||||||
remove_signaling_context(b, ce);
|
release = remove_signaling_context(rq->engine->breadcrumbs, ce);
|
||||||
|
spin_unlock(&ce->signal_lock);
|
||||||
|
if (release)
|
||||||
|
intel_context_put(ce);
|
||||||
|
|
||||||
clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
|
i915_request_put(rq);
|
||||||
i915_request_put(rq);
|
|
||||||
}
|
|
||||||
spin_unlock(&b->irq_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_signals(struct intel_breadcrumbs *b, struct drm_printer *p)
|
static void print_signals(struct intel_breadcrumbs *b, struct drm_printer *p)
|
||||||
|
@ -495,18 +476,17 @@ static void print_signals(struct intel_breadcrumbs *b, struct drm_printer *p)
|
||||||
|
|
||||||
drm_printf(p, "Signals:\n");
|
drm_printf(p, "Signals:\n");
|
||||||
|
|
||||||
spin_lock_irq(&b->irq_lock);
|
rcu_read_lock();
|
||||||
list_for_each_entry(ce, &b->signalers, signal_link) {
|
list_for_each_entry_rcu(ce, &b->signalers, signal_link) {
|
||||||
list_for_each_entry(rq, &ce->signals, signal_link) {
|
list_for_each_entry_rcu(rq, &ce->signals, signal_link)
|
||||||
drm_printf(p, "\t[%llx:%llx%s] @ %dms\n",
|
drm_printf(p, "\t[%llx:%llx%s] @ %dms\n",
|
||||||
rq->fence.context, rq->fence.seqno,
|
rq->fence.context, rq->fence.seqno,
|
||||||
i915_request_completed(rq) ? "!" :
|
i915_request_completed(rq) ? "!" :
|
||||||
i915_request_started(rq) ? "*" :
|
i915_request_started(rq) ? "*" :
|
||||||
"",
|
"",
|
||||||
jiffies_to_msecs(jiffies - rq->emitted_jiffies));
|
jiffies_to_msecs(jiffies - rq->emitted_jiffies));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&b->irq_lock);
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine,
|
void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine,
|
||||||
|
|
|
@ -29,18 +29,16 @@
|
||||||
* the overhead of waking that client is much preferred.
|
* the overhead of waking that client is much preferred.
|
||||||
*/
|
*/
|
||||||
struct intel_breadcrumbs {
|
struct intel_breadcrumbs {
|
||||||
spinlock_t irq_lock; /* protects the lists used in hardirq context */
|
|
||||||
|
|
||||||
/* Not all breadcrumbs are attached to physical HW */
|
/* Not all breadcrumbs are attached to physical HW */
|
||||||
struct intel_engine_cs *irq_engine;
|
struct intel_engine_cs *irq_engine;
|
||||||
|
|
||||||
|
spinlock_t signalers_lock; /* protects the list of signalers */
|
||||||
struct list_head signalers;
|
struct list_head signalers;
|
||||||
struct llist_head signaled_requests;
|
struct llist_head signaled_requests;
|
||||||
|
|
||||||
|
spinlock_t irq_lock; /* protects the interrupt from hardirq context */
|
||||||
struct irq_work irq_work; /* for use from inside irq_lock */
|
struct irq_work irq_work; /* for use from inside irq_lock */
|
||||||
|
|
||||||
unsigned int irq_enabled;
|
unsigned int irq_enabled;
|
||||||
|
|
||||||
bool irq_armed;
|
bool irq_armed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,16 @@ static struct intel_context *intel_context_alloc(void)
|
||||||
return kmem_cache_zalloc(global.slab_ce, GFP_KERNEL);
|
return kmem_cache_zalloc(global.slab_ce, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rcu_context_free(struct rcu_head *rcu)
|
||||||
|
{
|
||||||
|
struct intel_context *ce = container_of(rcu, typeof(*ce), rcu);
|
||||||
|
|
||||||
|
kmem_cache_free(global.slab_ce, ce);
|
||||||
|
}
|
||||||
|
|
||||||
void intel_context_free(struct intel_context *ce)
|
void intel_context_free(struct intel_context *ce)
|
||||||
{
|
{
|
||||||
kmem_cache_free(global.slab_ce, ce);
|
call_rcu(&ce->rcu, rcu_context_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct intel_context *
|
struct intel_context *
|
||||||
|
@ -356,8 +363,7 @@ static int __intel_context_active(struct i915_active *active)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
intel_context_init(struct intel_context *ce,
|
intel_context_init(struct intel_context *ce, struct intel_engine_cs *engine)
|
||||||
struct intel_engine_cs *engine)
|
|
||||||
{
|
{
|
||||||
GEM_BUG_ON(!engine->cops);
|
GEM_BUG_ON(!engine->cops);
|
||||||
GEM_BUG_ON(!engine->gt->vm);
|
GEM_BUG_ON(!engine->gt->vm);
|
||||||
|
@ -373,7 +379,8 @@ intel_context_init(struct intel_context *ce,
|
||||||
|
|
||||||
ce->vm = i915_vm_get(engine->gt->vm);
|
ce->vm = i915_vm_get(engine->gt->vm);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&ce->signal_link);
|
/* NB ce->signal_link/lock is used under RCU */
|
||||||
|
spin_lock_init(&ce->signal_lock);
|
||||||
INIT_LIST_HEAD(&ce->signals);
|
INIT_LIST_HEAD(&ce->signals);
|
||||||
|
|
||||||
mutex_init(&ce->pin_mutex);
|
mutex_init(&ce->pin_mutex);
|
||||||
|
|
|
@ -25,6 +25,7 @@ DECLARE_EWMA(runtime, 3, 8);
|
||||||
struct i915_gem_context;
|
struct i915_gem_context;
|
||||||
struct i915_gem_ww_ctx;
|
struct i915_gem_ww_ctx;
|
||||||
struct i915_vma;
|
struct i915_vma;
|
||||||
|
struct intel_breadcrumbs;
|
||||||
struct intel_context;
|
struct intel_context;
|
||||||
struct intel_ring;
|
struct intel_ring;
|
||||||
|
|
||||||
|
@ -44,7 +45,16 @@ struct intel_context_ops {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct intel_context {
|
struct intel_context {
|
||||||
struct kref ref;
|
/*
|
||||||
|
* Note: Some fields may be accessed under RCU.
|
||||||
|
*
|
||||||
|
* Unless otherwise noted a field can safely be assumed to be protected
|
||||||
|
* by strong reference counting.
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
struct kref ref; /* no kref_get_unless_zero()! */
|
||||||
|
struct rcu_head rcu;
|
||||||
|
};
|
||||||
|
|
||||||
struct intel_engine_cs *engine;
|
struct intel_engine_cs *engine;
|
||||||
struct intel_engine_cs *inflight;
|
struct intel_engine_cs *inflight;
|
||||||
|
@ -54,8 +64,15 @@ struct intel_context {
|
||||||
struct i915_address_space *vm;
|
struct i915_address_space *vm;
|
||||||
struct i915_gem_context __rcu *gem_context;
|
struct i915_gem_context __rcu *gem_context;
|
||||||
|
|
||||||
struct list_head signal_link;
|
/*
|
||||||
struct list_head signals;
|
* @signal_lock protects the list of requests that need signaling,
|
||||||
|
* @signals. While there are any requests that need signaling,
|
||||||
|
* we add the context to the breadcrumbs worker, and remove it
|
||||||
|
* upon completion/cancellation of the last request.
|
||||||
|
*/
|
||||||
|
struct list_head signal_link; /* Accessed under RCU */
|
||||||
|
struct list_head signals; /* Guarded by signal_lock */
|
||||||
|
spinlock_t signal_lock; /* protects signals, the list of requests */
|
||||||
|
|
||||||
struct i915_vma *state;
|
struct i915_vma *state;
|
||||||
struct intel_ring *ring;
|
struct intel_ring *ring;
|
||||||
|
|
|
@ -131,7 +131,19 @@ static const struct drm_i915_mocs_entry skl_mocs_table[] = {
|
||||||
GEN9_MOCS_ENTRIES,
|
GEN9_MOCS_ENTRIES,
|
||||||
MOCS_ENTRY(I915_MOCS_CACHED,
|
MOCS_ENTRY(I915_MOCS_CACHED,
|
||||||
LE_3_WB | LE_TC_2_LLC_ELLC | LE_LRUM(3),
|
LE_3_WB | LE_TC_2_LLC_ELLC | LE_LRUM(3),
|
||||||
L3_3_WB)
|
L3_3_WB),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mocs:63
|
||||||
|
* - used by the L3 for all of its evictions.
|
||||||
|
* Thus it is expected to allow LLC cacheability to enable coherent
|
||||||
|
* flows to be maintained.
|
||||||
|
* - used to force L3 uncachable cycles.
|
||||||
|
* Thus it is expected to make the surface L3 uncacheable.
|
||||||
|
*/
|
||||||
|
MOCS_ENTRY(63,
|
||||||
|
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3),
|
||||||
|
L3_1_UC)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NOTE: the LE_TGT_CACHE is not used on Broxton */
|
/* NOTE: the LE_TGT_CACHE is not used on Broxton */
|
||||||
|
|
|
@ -883,6 +883,10 @@ void intel_rps_park(struct intel_rps *rps)
|
||||||
adj = -2;
|
adj = -2;
|
||||||
rps->last_adj = adj;
|
rps->last_adj = adj;
|
||||||
rps->cur_freq = max_t(int, rps->cur_freq + adj, rps->min_freq);
|
rps->cur_freq = max_t(int, rps->cur_freq + adj, rps->min_freq);
|
||||||
|
if (rps->cur_freq < rps->efficient_freq) {
|
||||||
|
rps->cur_freq = rps->efficient_freq;
|
||||||
|
rps->last_adj = 0;
|
||||||
|
}
|
||||||
|
|
||||||
GT_TRACE(rps_to_gt(rps), "park:%x\n", rps->cur_freq);
|
GT_TRACE(rps_to_gt(rps), "park:%x\n", rps->cur_freq);
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,10 +103,13 @@ static int __shmem_rw(struct file *file, loff_t off,
|
||||||
return PTR_ERR(page);
|
return PTR_ERR(page);
|
||||||
|
|
||||||
vaddr = kmap(page);
|
vaddr = kmap(page);
|
||||||
if (write)
|
if (write) {
|
||||||
memcpy(vaddr + offset_in_page(off), ptr, this);
|
memcpy(vaddr + offset_in_page(off), ptr, this);
|
||||||
else
|
set_page_dirty(page);
|
||||||
|
} else {
|
||||||
memcpy(ptr, vaddr + offset_in_page(off), this);
|
memcpy(ptr, vaddr + offset_in_page(off), this);
|
||||||
|
}
|
||||||
|
mark_page_accessed(page);
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
put_page(page);
|
put_page(page);
|
||||||
|
|
||||||
|
|
|
@ -177,10 +177,8 @@ struct i915_request {
|
||||||
struct intel_ring *ring;
|
struct intel_ring *ring;
|
||||||
struct intel_timeline __rcu *timeline;
|
struct intel_timeline __rcu *timeline;
|
||||||
|
|
||||||
union {
|
struct list_head signal_link;
|
||||||
struct list_head signal_link;
|
struct llist_node signal_node;
|
||||||
struct llist_node signal_node;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The rcu epoch of when this request was allocated. Used to judiciously
|
* The rcu epoch of when this request was allocated. Used to judiciously
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <drm/drm_fb_cma_helper.h>
|
#include <drm/drm_fb_cma_helper.h>
|
||||||
#include <drm/drm_fourcc.h>
|
#include <drm/drm_fourcc.h>
|
||||||
#include <drm/drm_gem_cma_helper.h>
|
#include <drm/drm_gem_cma_helper.h>
|
||||||
|
#include <drm/drm_gem_framebuffer_helper.h>
|
||||||
#include <drm/drm_plane.h>
|
#include <drm/drm_plane.h>
|
||||||
#include <drm/drm_plane_helper.h>
|
#include <drm/drm_plane_helper.h>
|
||||||
#include <drm/drm_vblank.h>
|
#include <drm/drm_vblank.h>
|
||||||
|
@ -484,17 +485,27 @@ static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
|
||||||
writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
|
writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool mxsfb_format_mod_supported(struct drm_plane *plane,
|
||||||
|
uint32_t format,
|
||||||
|
uint64_t modifier)
|
||||||
|
{
|
||||||
|
return modifier == DRM_FORMAT_MOD_LINEAR;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
|
static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
|
||||||
|
.prepare_fb = drm_gem_fb_prepare_fb,
|
||||||
.atomic_check = mxsfb_plane_atomic_check,
|
.atomic_check = mxsfb_plane_atomic_check,
|
||||||
.atomic_update = mxsfb_plane_primary_atomic_update,
|
.atomic_update = mxsfb_plane_primary_atomic_update,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
|
static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
|
||||||
|
.prepare_fb = drm_gem_fb_prepare_fb,
|
||||||
.atomic_check = mxsfb_plane_atomic_check,
|
.atomic_check = mxsfb_plane_atomic_check,
|
||||||
.atomic_update = mxsfb_plane_overlay_atomic_update,
|
.atomic_update = mxsfb_plane_overlay_atomic_update,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_plane_funcs mxsfb_plane_funcs = {
|
static const struct drm_plane_funcs mxsfb_plane_funcs = {
|
||||||
|
.format_mod_supported = mxsfb_format_mod_supported,
|
||||||
.update_plane = drm_atomic_helper_update_plane,
|
.update_plane = drm_atomic_helper_update_plane,
|
||||||
.disable_plane = drm_atomic_helper_disable_plane,
|
.disable_plane = drm_atomic_helper_disable_plane,
|
||||||
.destroy = drm_plane_cleanup,
|
.destroy = drm_plane_cleanup,
|
||||||
|
|
|
@ -1214,8 +1214,8 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
reg->bus.offset = handle;
|
reg->bus.offset = handle;
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
|
@ -195,8 +195,7 @@ static void sdi_bridge_mode_set(struct drm_bridge *bridge,
|
||||||
sdi->pixelclock = adjusted_mode->clock * 1000;
|
sdi->pixelclock = adjusted_mode->clock * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdi_bridge_enable(struct drm_bridge *bridge,
|
static void sdi_bridge_enable(struct drm_bridge *bridge)
|
||||||
struct drm_bridge_state *bridge_state)
|
|
||||||
{
|
{
|
||||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||||
struct dispc_clock_info dispc_cinfo;
|
struct dispc_clock_info dispc_cinfo;
|
||||||
|
@ -259,8 +258,7 @@ static void sdi_bridge_enable(struct drm_bridge *bridge,
|
||||||
regulator_disable(sdi->vdds_sdi_reg);
|
regulator_disable(sdi->vdds_sdi_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdi_bridge_disable(struct drm_bridge *bridge,
|
static void sdi_bridge_disable(struct drm_bridge *bridge)
|
||||||
struct drm_bridge_state *bridge_state)
|
|
||||||
{
|
{
|
||||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||||
|
|
||||||
|
@ -278,8 +276,8 @@ static const struct drm_bridge_funcs sdi_bridge_funcs = {
|
||||||
.mode_valid = sdi_bridge_mode_valid,
|
.mode_valid = sdi_bridge_mode_valid,
|
||||||
.mode_fixup = sdi_bridge_mode_fixup,
|
.mode_fixup = sdi_bridge_mode_fixup,
|
||||||
.mode_set = sdi_bridge_mode_set,
|
.mode_set = sdi_bridge_mode_set,
|
||||||
.atomic_enable = sdi_bridge_enable,
|
.enable = sdi_bridge_enable,
|
||||||
.atomic_disable = sdi_bridge_disable,
|
.disable = sdi_bridge_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sdi_bridge_init(struct sdi_device *sdi)
|
static void sdi_bridge_init(struct sdi_device *sdi)
|
||||||
|
|
|
@ -629,7 +629,7 @@ static int acx565akm_probe(struct spi_device *spi)
|
||||||
lcd->spi = spi;
|
lcd->spi = spi;
|
||||||
mutex_init(&lcd->mutex);
|
mutex_init(&lcd->mutex);
|
||||||
|
|
||||||
lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
|
lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
|
||||||
if (IS_ERR(lcd->reset_gpio)) {
|
if (IS_ERR(lcd->reset_gpio)) {
|
||||||
dev_err(&spi->dev, "failed to get reset GPIO\n");
|
dev_err(&spi->dev, "failed to get reset GPIO\n");
|
||||||
return PTR_ERR(lcd->reset_gpio);
|
return PTR_ERR(lcd->reset_gpio);
|
||||||
|
|
|
@ -544,7 +544,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||||
struct device_node *port, *endpoint;
|
struct device_node *port, *endpoint;
|
||||||
int ret = 0, child_count = 0;
|
int ret = 0, child_count = 0;
|
||||||
const char *name;
|
const char *name;
|
||||||
u32 endpoint_id;
|
u32 endpoint_id = 0;
|
||||||
|
|
||||||
lvds->drm_dev = drm_dev;
|
lvds->drm_dev = drm_dev;
|
||||||
port = of_graph_get_port_by_id(dev->of_node, 1);
|
port = of_graph_get_port_by_id(dev->of_node, 1);
|
||||||
|
|
|
@ -90,7 +90,7 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
|
||||||
if (!fpriv)
|
if (!fpriv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
idr_init(&fpriv->contexts);
|
idr_init_base(&fpriv->contexts, 1);
|
||||||
mutex_init(&fpriv->lock);
|
mutex_init(&fpriv->lock);
|
||||||
filp->driver_priv = fpriv;
|
filp->driver_priv = fpriv;
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,6 @@ int tegra_output_probe(struct tegra_output *output)
|
||||||
|
|
||||||
if (!output->ddc) {
|
if (!output->ddc) {
|
||||||
err = -EPROBE_DEFER;
|
err = -EPROBE_DEFER;
|
||||||
of_node_put(ddc);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -397,7 +397,6 @@ struct tegra_sor;
|
||||||
struct tegra_sor_ops {
|
struct tegra_sor_ops {
|
||||||
const char *name;
|
const char *name;
|
||||||
int (*probe)(struct tegra_sor *sor);
|
int (*probe)(struct tegra_sor *sor);
|
||||||
int (*remove)(struct tegra_sor *sor);
|
|
||||||
void (*audio_enable)(struct tegra_sor *sor);
|
void (*audio_enable)(struct tegra_sor *sor);
|
||||||
void (*audio_disable)(struct tegra_sor *sor);
|
void (*audio_disable)(struct tegra_sor *sor);
|
||||||
};
|
};
|
||||||
|
@ -2942,6 +2941,24 @@ static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
|
||||||
.atomic_check = tegra_sor_encoder_atomic_check,
|
.atomic_check = tegra_sor_encoder_atomic_check,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void tegra_sor_disable_regulator(void *data)
|
||||||
|
{
|
||||||
|
struct regulator *reg = data;
|
||||||
|
|
||||||
|
regulator_disable(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_sor_enable_regulator(struct tegra_sor *sor, struct regulator *reg)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = regulator_enable(reg);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(sor->dev, tegra_sor_disable_regulator, reg);
|
||||||
|
}
|
||||||
|
|
||||||
static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -2953,7 +2970,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
||||||
return PTR_ERR(sor->avdd_io_supply);
|
return PTR_ERR(sor->avdd_io_supply);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regulator_enable(sor->avdd_io_supply);
|
err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(sor->dev, "failed to enable AVDD I/O supply: %d\n",
|
dev_err(sor->dev, "failed to enable AVDD I/O supply: %d\n",
|
||||||
err);
|
err);
|
||||||
|
@ -2967,7 +2984,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
||||||
return PTR_ERR(sor->vdd_pll_supply);
|
return PTR_ERR(sor->vdd_pll_supply);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regulator_enable(sor->vdd_pll_supply);
|
err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(sor->dev, "failed to enable VDD PLL supply: %d\n",
|
dev_err(sor->dev, "failed to enable VDD PLL supply: %d\n",
|
||||||
err);
|
err);
|
||||||
|
@ -2981,7 +2998,7 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
||||||
return PTR_ERR(sor->hdmi_supply);
|
return PTR_ERR(sor->hdmi_supply);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regulator_enable(sor->hdmi_supply);
|
err = tegra_sor_enable_regulator(sor, sor->hdmi_supply);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(sor->dev, "failed to enable HDMI supply: %d\n", err);
|
dev_err(sor->dev, "failed to enable HDMI supply: %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
|
@ -2992,19 +3009,9 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_sor_hdmi_remove(struct tegra_sor *sor)
|
|
||||||
{
|
|
||||||
regulator_disable(sor->hdmi_supply);
|
|
||||||
regulator_disable(sor->vdd_pll_supply);
|
|
||||||
regulator_disable(sor->avdd_io_supply);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct tegra_sor_ops tegra_sor_hdmi_ops = {
|
static const struct tegra_sor_ops tegra_sor_hdmi_ops = {
|
||||||
.name = "HDMI",
|
.name = "HDMI",
|
||||||
.probe = tegra_sor_hdmi_probe,
|
.probe = tegra_sor_hdmi_probe,
|
||||||
.remove = tegra_sor_hdmi_remove,
|
|
||||||
.audio_enable = tegra_sor_hdmi_audio_enable,
|
.audio_enable = tegra_sor_hdmi_audio_enable,
|
||||||
.audio_disable = tegra_sor_hdmi_audio_disable,
|
.audio_disable = tegra_sor_hdmi_audio_disable,
|
||||||
};
|
};
|
||||||
|
@ -3017,7 +3024,7 @@ static int tegra_sor_dp_probe(struct tegra_sor *sor)
|
||||||
if (IS_ERR(sor->avdd_io_supply))
|
if (IS_ERR(sor->avdd_io_supply))
|
||||||
return PTR_ERR(sor->avdd_io_supply);
|
return PTR_ERR(sor->avdd_io_supply);
|
||||||
|
|
||||||
err = regulator_enable(sor->avdd_io_supply);
|
err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -3025,25 +3032,16 @@ static int tegra_sor_dp_probe(struct tegra_sor *sor)
|
||||||
if (IS_ERR(sor->vdd_pll_supply))
|
if (IS_ERR(sor->vdd_pll_supply))
|
||||||
return PTR_ERR(sor->vdd_pll_supply);
|
return PTR_ERR(sor->vdd_pll_supply);
|
||||||
|
|
||||||
err = regulator_enable(sor->vdd_pll_supply);
|
err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_sor_dp_remove(struct tegra_sor *sor)
|
|
||||||
{
|
|
||||||
regulator_disable(sor->vdd_pll_supply);
|
|
||||||
regulator_disable(sor->avdd_io_supply);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct tegra_sor_ops tegra_sor_dp_ops = {
|
static const struct tegra_sor_ops tegra_sor_dp_ops = {
|
||||||
.name = "DP",
|
.name = "DP",
|
||||||
.probe = tegra_sor_dp_probe,
|
.probe = tegra_sor_dp_probe,
|
||||||
.remove = tegra_sor_dp_remove,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tegra_sor_init(struct host1x_client *client)
|
static int tegra_sor_init(struct host1x_client *client)
|
||||||
|
@ -3145,6 +3143,7 @@ static int tegra_sor_init(struct host1x_client *client)
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(sor->dev, "failed to deassert SOR reset: %d\n",
|
dev_err(sor->dev, "failed to deassert SOR reset: %d\n",
|
||||||
err);
|
err);
|
||||||
|
clk_disable_unprepare(sor->clk);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3152,12 +3151,17 @@ static int tegra_sor_init(struct host1x_client *client)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = clk_prepare_enable(sor->clk_safe);
|
err = clk_prepare_enable(sor->clk_safe);
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
|
clk_disable_unprepare(sor->clk);
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
err = clk_prepare_enable(sor->clk_dp);
|
err = clk_prepare_enable(sor->clk_dp);
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
|
clk_disable_unprepare(sor->clk_safe);
|
||||||
|
clk_disable_unprepare(sor->clk);
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3764,17 +3768,16 @@ static int tegra_sor_probe(struct platform_device *pdev)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = tegra_output_probe(&sor->output);
|
err = tegra_output_probe(&sor->output);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
dev_err(&pdev->dev, "failed to probe output: %d\n", err);
|
return dev_err_probe(&pdev->dev, err,
|
||||||
return err;
|
"failed to probe output\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (sor->ops && sor->ops->probe) {
|
if (sor->ops && sor->ops->probe) {
|
||||||
err = sor->ops->probe(sor);
|
err = sor->ops->probe(sor);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev, "failed to probe %s: %d\n",
|
dev_err(&pdev->dev, "failed to probe %s: %d\n",
|
||||||
sor->ops->name, err);
|
sor->ops->name, err);
|
||||||
goto output;
|
goto remove;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3955,9 +3958,6 @@ static int tegra_sor_probe(struct platform_device *pdev)
|
||||||
rpm_disable:
|
rpm_disable:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
remove:
|
remove:
|
||||||
if (sor->ops && sor->ops->remove)
|
|
||||||
sor->ops->remove(sor);
|
|
||||||
output:
|
|
||||||
tegra_output_remove(&sor->output);
|
tegra_output_remove(&sor->output);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -3976,12 +3976,6 @@ static int tegra_sor_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
if (sor->ops && sor->ops->remove) {
|
|
||||||
err = sor->ops->remove(sor);
|
|
||||||
if (err < 0)
|
|
||||||
dev_err(&pdev->dev, "failed to remove SOR: %d\n", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
tegra_output_remove(&sor->output);
|
tegra_output_remove(&sor->output);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -734,6 +734,7 @@ config I2C_LPC2K
|
||||||
config I2C_MLXBF
|
config I2C_MLXBF
|
||||||
tristate "Mellanox BlueField I2C controller"
|
tristate "Mellanox BlueField I2C controller"
|
||||||
depends on MELLANOX_PLATFORM && ARM64
|
depends on MELLANOX_PLATFORM && ARM64
|
||||||
|
select I2C_SLAVE
|
||||||
help
|
help
|
||||||
Enabling this option will add I2C SMBus support for Mellanox BlueField
|
Enabling this option will add I2C SMBus support for Mellanox BlueField
|
||||||
system.
|
system.
|
||||||
|
|
|
@ -412,6 +412,19 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
||||||
dma->chan_using = NULL;
|
dma->chan_using = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits)
|
||||||
|
{
|
||||||
|
unsigned int temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* i2sr_clr_opcode is the value to clear all interrupts. Here we want to
|
||||||
|
* clear only <bits>, so we write ~i2sr_clr_opcode with just <bits>
|
||||||
|
* toggled. This is required because i.MX needs W0C and Vybrid uses W1C.
|
||||||
|
*/
|
||||||
|
temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
|
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
|
||||||
{
|
{
|
||||||
unsigned long orig_jiffies = jiffies;
|
unsigned long orig_jiffies = jiffies;
|
||||||
|
@ -424,8 +437,7 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool a
|
||||||
|
|
||||||
/* check for arbitration lost */
|
/* check for arbitration lost */
|
||||||
if (temp & I2SR_IAL) {
|
if (temp & I2SR_IAL) {
|
||||||
temp &= ~I2SR_IAL;
|
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,7 +481,7 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
|
||||||
*/
|
*/
|
||||||
readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100);
|
readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100);
|
||||||
i2c_imx->i2csr = regval;
|
i2c_imx->i2csr = regval;
|
||||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL);
|
||||||
} else {
|
} else {
|
||||||
wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
|
wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
|
||||||
}
|
}
|
||||||
|
@ -478,6 +490,16 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check for arbitration lost */
|
||||||
|
if (i2c_imx->i2csr & I2SR_IAL) {
|
||||||
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
|
||||||
|
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
||||||
|
|
||||||
|
i2c_imx->i2csr = 0;
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
|
||||||
i2c_imx->i2csr = 0;
|
i2c_imx->i2csr = 0;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -593,6 +615,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
|
||||||
/* Stop I2C transaction */
|
/* Stop I2C transaction */
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
if (!(temp & I2CR_MSTA))
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
if (i2c_imx->dma)
|
if (i2c_imx->dma)
|
||||||
temp &= ~I2CR_DMAEN;
|
temp &= ~I2CR_DMAEN;
|
||||||
|
@ -623,9 +647,7 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
||||||
if (temp & I2SR_IIF) {
|
if (temp & I2SR_IIF) {
|
||||||
/* save status register */
|
/* save status register */
|
||||||
i2c_imx->i2csr = temp;
|
i2c_imx->i2csr = temp;
|
||||||
temp &= ~I2SR_IIF;
|
i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
|
||||||
temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
|
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
|
||||||
wake_up(&i2c_imx->queue);
|
wake_up(&i2c_imx->queue);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -758,9 +780,12 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||||
*/
|
*/
|
||||||
dev_dbg(dev, "<%s> clear MSTA\n", __func__);
|
dev_dbg(dev, "<%s> clear MSTA\n", __func__);
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
if (!(temp & I2CR_MSTA))
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
i2c_imx_bus_busy(i2c_imx, 0, false);
|
if (!i2c_imx->stopped)
|
||||||
|
i2c_imx_bus_busy(i2c_imx, 0, false);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* For i2c master receiver repeat restart operation like:
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
@ -885,9 +910,12 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> clear MSTA\n", __func__);
|
"<%s> clear MSTA\n", __func__);
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
if (!(temp & I2CR_MSTA))
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
i2c_imx_bus_busy(i2c_imx, 0, atomic);
|
if (!i2c_imx->stopped)
|
||||||
|
i2c_imx_bus_busy(i2c_imx, 0, atomic);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* For i2c master receiver repeat restart operation like:
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
|
|
@ -1258,9 +1258,9 @@ static int mlxbf_i2c_get_gpio(struct platform_device *pdev,
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
gpio_res->io = devm_ioremap(dev, params->start, size);
|
gpio_res->io = devm_ioremap(dev, params->start, size);
|
||||||
if (IS_ERR(gpio_res->io)) {
|
if (!gpio_res->io) {
|
||||||
devm_release_mem_region(dev, params->start, size);
|
devm_release_mem_region(dev, params->start, size);
|
||||||
return PTR_ERR(gpio_res->io);
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1323,9 +1323,9 @@ static int mlxbf_i2c_get_corepll(struct platform_device *pdev,
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
corepll_res->io = devm_ioremap(dev, params->start, size);
|
corepll_res->io = devm_ioremap(dev, params->start, size);
|
||||||
if (IS_ERR(corepll_res->io)) {
|
if (!corepll_res->io) {
|
||||||
devm_release_mem_region(dev, params->start, size);
|
devm_release_mem_region(dev, params->start, size);
|
||||||
return PTR_ERR(corepll_res->io);
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1717,9 +1717,9 @@ static int mlxbf_i2c_init_coalesce(struct platform_device *pdev,
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
coalesce_res->io = ioremap(params->start, size);
|
coalesce_res->io = ioremap(params->start, size);
|
||||||
if (IS_ERR(coalesce_res->io)) {
|
if (!coalesce_res->io) {
|
||||||
release_mem_region(params->start, size);
|
release_mem_region(params->start, size);
|
||||||
return PTR_ERR(coalesce_res->io);
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->coalesce = coalesce_res;
|
priv->coalesce = coalesce_res;
|
||||||
|
|
|
@ -194,9 +194,9 @@ static irqreturn_t cci_isr(int irq, void *dev)
|
||||||
if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_ERROR)) {
|
if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_ERROR)) {
|
||||||
if (val & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR ||
|
if (val & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR ||
|
||||||
val & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR)
|
val & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR)
|
||||||
cci->master[0].status = -ENXIO;
|
cci->master[1].status = -ENXIO;
|
||||||
else
|
else
|
||||||
cci->master[0].status = -EIO;
|
cci->master[1].status = -EIO;
|
||||||
|
|
||||||
writel(CCI_HALT_REQ_I2C_M1_Q0Q1, cci->base + CCI_HALT_REQ);
|
writel(CCI_HALT_REQ_I2C_M1_Q0Q1, cci->base + CCI_HALT_REQ);
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
|
|
|
@ -801,7 +801,8 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
|
||||||
if (ret || qup->bus_err || qup->qup_err) {
|
if (ret || qup->bus_err || qup->qup_err) {
|
||||||
reinit_completion(&qup->xfer);
|
reinit_completion(&qup->xfer);
|
||||||
|
|
||||||
if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
|
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||||
|
if (ret) {
|
||||||
dev_err(qup->dev, "change to run state timed out");
|
dev_err(qup->dev, "change to run state timed out");
|
||||||
goto desc_err;
|
goto desc_err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1140,6 +1140,20 @@ static bool __init intel_idle_max_cstate_reached(int cstate)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool __init intel_idle_state_needs_timer_stop(struct cpuidle_state *state)
|
||||||
|
{
|
||||||
|
unsigned long eax = flg2MWAIT(state->flags);
|
||||||
|
|
||||||
|
if (boot_cpu_has(X86_FEATURE_ARAT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch over to one-shot tick broadcast if the target C-state
|
||||||
|
* is deeper than C1.
|
||||||
|
*/
|
||||||
|
return !!((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
|
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
|
||||||
#include <acpi/processor.h>
|
#include <acpi/processor.h>
|
||||||
|
|
||||||
|
@ -1210,20 +1224,6 @@ static bool __init intel_idle_acpi_cst_extract(void)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool __init intel_idle_state_needs_timer_stop(struct cpuidle_state *state)
|
|
||||||
{
|
|
||||||
unsigned long eax = flg2MWAIT(state->flags);
|
|
||||||
|
|
||||||
if (boot_cpu_has(X86_FEATURE_ARAT))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Switch over to one-shot tick broadcast if the target C-state
|
|
||||||
* is deeper than C1.
|
|
||||||
*/
|
|
||||||
return !!((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv)
|
static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv)
|
||||||
{
|
{
|
||||||
int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count);
|
int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count);
|
||||||
|
|
|
@ -241,6 +241,7 @@ static const struct xpad_device {
|
||||||
{ 0x1038, 0x1430, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
|
{ 0x1038, 0x1430, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
|
||||||
{ 0x1038, 0x1431, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
|
{ 0x1038, 0x1431, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
|
||||||
{ 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 },
|
{ 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x1209, 0x2882, "Ardwiino Controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
|
{ 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
|
||||||
{ 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
|
@ -418,6 +419,7 @@ static const struct usb_device_id xpad_table[] = {
|
||||||
XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */
|
XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x1038), /* SteelSeries Controllers */
|
XPAD_XBOX360_VENDOR(0x1038), /* SteelSeries Controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */
|
XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */
|
||||||
|
XPAD_XBOX360_VENDOR(0x1209), /* Ardwiino Controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */
|
XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */
|
||||||
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
|
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
|
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
|
|
|
@ -219,6 +219,10 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
|
DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
|
||||||
},
|
},
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
|
@ -1471,7 +1471,8 @@ static int __init i8042_setup_aux(void)
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_ports;
|
goto err_free_ports;
|
||||||
|
|
||||||
if (aux_enable())
|
error = aux_enable();
|
||||||
|
if (error)
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
|
|
||||||
i8042_aux_irq_registered = true;
|
i8042_aux_irq_registered = true;
|
||||||
|
|
|
@ -2183,11 +2183,11 @@ static int mxt_initialize(struct mxt_data *data)
|
||||||
msleep(MXT_FW_RESET_TIME);
|
msleep(MXT_FW_RESET_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = mxt_acquire_irq(data);
|
error = mxt_check_retrigen(data);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = mxt_check_retrigen(data);
|
error = mxt_acquire_irq(data);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
|
@ -712,10 +712,6 @@ static bool block_size_is_power_of_two(struct cache *cache)
|
||||||
return cache->sectors_per_block_shift >= 0;
|
return cache->sectors_per_block_shift >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gcc on ARM generates spurious references to __udivdi3 and __umoddi3 */
|
|
||||||
#if defined(CONFIG_ARM) && __GNUC__ == 4 && __GNUC_MINOR__ <= 6
|
|
||||||
__always_inline
|
|
||||||
#endif
|
|
||||||
static dm_block_t block_div(dm_block_t b, uint32_t n)
|
static dm_block_t block_div(dm_block_t b, uint32_t n)
|
||||||
{
|
{
|
||||||
do_div(b, n);
|
do_div(b, n);
|
||||||
|
|
|
@ -3462,7 +3462,7 @@ static int get_mac(struct crypto_shash **hash, struct alg_spec *a, char **error,
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (a->alg_string) {
|
if (a->alg_string) {
|
||||||
*hash = crypto_alloc_shash(a->alg_string, 0, 0);
|
*hash = crypto_alloc_shash(a->alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY);
|
||||||
if (IS_ERR(*hash)) {
|
if (IS_ERR(*hash)) {
|
||||||
*error = error_alg;
|
*error = error_alg;
|
||||||
r = PTR_ERR(*hash);
|
r = PTR_ERR(*hash);
|
||||||
|
@ -3519,7 +3519,7 @@ static int create_journal(struct dm_integrity_c *ic, char **error)
|
||||||
struct journal_completion comp;
|
struct journal_completion comp;
|
||||||
|
|
||||||
comp.ic = ic;
|
comp.ic = ic;
|
||||||
ic->journal_crypt = crypto_alloc_skcipher(ic->journal_crypt_alg.alg_string, 0, 0);
|
ic->journal_crypt = crypto_alloc_skcipher(ic->journal_crypt_alg.alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY);
|
||||||
if (IS_ERR(ic->journal_crypt)) {
|
if (IS_ERR(ic->journal_crypt)) {
|
||||||
*error = "Invalid journal cipher";
|
*error = "Invalid journal cipher";
|
||||||
r = PTR_ERR(ic->journal_crypt);
|
r = PTR_ERR(ic->journal_crypt);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/lcm.h>
|
|
||||||
#include <linux/blk-mq.h>
|
#include <linux/blk-mq.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
#include <linux/dax.h>
|
#include <linux/dax.h>
|
||||||
|
@ -1247,12 +1246,6 @@ void dm_table_event_callback(struct dm_table *t,
|
||||||
|
|
||||||
void dm_table_event(struct dm_table *t)
|
void dm_table_event(struct dm_table *t)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* You can no longer call dm_table_event() from interrupt
|
|
||||||
* context, use a bottom half instead.
|
|
||||||
*/
|
|
||||||
BUG_ON(in_interrupt());
|
|
||||||
|
|
||||||
mutex_lock(&_event_lock);
|
mutex_lock(&_event_lock);
|
||||||
if (t->event_fn)
|
if (t->event_fn)
|
||||||
t->event_fn(t->event_context);
|
t->event_fn(t->event_context);
|
||||||
|
@ -1455,10 +1448,6 @@ int dm_calculate_queue_limits(struct dm_table *table,
|
||||||
zone_sectors = ti_limits.chunk_sectors;
|
zone_sectors = ti_limits.chunk_sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stack chunk_sectors if target-specific splitting is required */
|
|
||||||
if (ti->max_io_len)
|
|
||||||
ti_limits.chunk_sectors = lcm_not_zero(ti->max_io_len,
|
|
||||||
ti_limits.chunk_sectors);
|
|
||||||
/* Set I/O hints portion of queue limits */
|
/* Set I/O hints portion of queue limits */
|
||||||
if (ti->type->io_hints)
|
if (ti->type->io_hints)
|
||||||
ti->type->io_hints(ti, &ti_limits);
|
ti->type->io_hints(ti, &ti_limits);
|
||||||
|
|
|
@ -319,7 +319,7 @@ static int persistent_memory_claim(struct dm_writecache *wc)
|
||||||
#else
|
#else
|
||||||
static int persistent_memory_claim(struct dm_writecache *wc)
|
static int persistent_memory_claim(struct dm_writecache *wc)
|
||||||
{
|
{
|
||||||
BUG();
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2041,7 +2041,7 @@ static int writecache_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||||
struct wc_memory_superblock s;
|
struct wc_memory_superblock s;
|
||||||
|
|
||||||
static struct dm_arg _args[] = {
|
static struct dm_arg _args[] = {
|
||||||
{0, 10, "Invalid number of feature args"},
|
{0, 16, "Invalid number of feature args"},
|
||||||
};
|
};
|
||||||
|
|
||||||
as.argc = argc;
|
as.argc = argc;
|
||||||
|
@ -2479,6 +2479,8 @@ static void writecache_status(struct dm_target *ti, status_type_t type,
|
||||||
extra_args += 2;
|
extra_args += 2;
|
||||||
if (wc->autocommit_time_set)
|
if (wc->autocommit_time_set)
|
||||||
extra_args += 2;
|
extra_args += 2;
|
||||||
|
if (wc->max_age != MAX_AGE_UNSPECIFIED)
|
||||||
|
extra_args += 2;
|
||||||
if (wc->cleaner)
|
if (wc->cleaner)
|
||||||
extra_args++;
|
extra_args++;
|
||||||
if (wc->writeback_fua_set)
|
if (wc->writeback_fua_set)
|
||||||
|
|
|
@ -476,8 +476,10 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
map = dm_get_live_table(md, &srcu_idx);
|
map = dm_get_live_table(md, &srcu_idx);
|
||||||
if (!map)
|
if (!map) {
|
||||||
return -EIO;
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
struct dm_target *tgt;
|
struct dm_target *tgt;
|
||||||
|
@ -507,7 +509,6 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
|
|
||||||
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
||||||
struct block_device **bdev)
|
struct block_device **bdev)
|
||||||
__acquires(md->io_barrier)
|
|
||||||
{
|
{
|
||||||
struct dm_target *tgt;
|
struct dm_target *tgt;
|
||||||
struct dm_table *map;
|
struct dm_table *map;
|
||||||
|
@ -541,7 +542,6 @@ static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx)
|
static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx)
|
||||||
__releases(md->io_barrier)
|
|
||||||
{
|
{
|
||||||
dm_put_live_table(md, srcu_idx);
|
dm_put_live_table(md, srcu_idx);
|
||||||
}
|
}
|
||||||
|
@ -1037,15 +1037,18 @@ static sector_t max_io_len(struct dm_target *ti, sector_t sector)
|
||||||
sector_t max_len;
|
sector_t max_len;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Does the target need to split even further?
|
* Does the target need to split IO even further?
|
||||||
* - q->limits.chunk_sectors reflects ti->max_io_len so
|
* - varied (per target) IO splitting is a tenet of DM; this
|
||||||
* blk_max_size_offset() provides required splitting.
|
* explains why stacked chunk_sectors based splitting via
|
||||||
* - blk_max_size_offset() also respects q->limits.max_sectors
|
* blk_max_size_offset() isn't possible here. So pass in
|
||||||
|
* ti->max_io_len to override stacked chunk_sectors.
|
||||||
*/
|
*/
|
||||||
max_len = blk_max_size_offset(ti->table->md->queue,
|
if (ti->max_io_len) {
|
||||||
target_offset);
|
max_len = blk_max_size_offset(ti->table->md->queue,
|
||||||
if (len > max_len)
|
target_offset, ti->max_io_len);
|
||||||
len = max_len;
|
if (len > max_len)
|
||||||
|
len = max_len;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -1196,11 +1199,9 @@ static int dm_dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||||
* ->zero_page_range() is mandatory dax operation. If we are
|
* ->zero_page_range() is mandatory dax operation. If we are
|
||||||
* here, something is wrong.
|
* here, something is wrong.
|
||||||
*/
|
*/
|
||||||
dm_put_live_table(md, srcu_idx);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = ti->type->dax_zero_page_range(ti, pgoff, nr_pages);
|
ret = ti->type->dax_zero_page_range(ti, pgoff, nr_pages);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
dm_put_live_table(md, srcu_idx);
|
dm_put_live_table(md, srcu_idx);
|
||||||
|
|
||||||
|
|
|
@ -242,16 +242,16 @@ static int device_cdev_sysfs_add(struct hl_device *hdev)
|
||||||
|
|
||||||
static void device_cdev_sysfs_del(struct hl_device *hdev)
|
static void device_cdev_sysfs_del(struct hl_device *hdev)
|
||||||
{
|
{
|
||||||
/* device_release() won't be called so must free devices explicitly */
|
if (!hdev->cdev_sysfs_created)
|
||||||
if (!hdev->cdev_sysfs_created) {
|
goto put_devices;
|
||||||
kfree(hdev->dev_ctrl);
|
|
||||||
kfree(hdev->dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hl_sysfs_fini(hdev);
|
hl_sysfs_fini(hdev);
|
||||||
cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
|
cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
|
||||||
cdev_device_del(&hdev->cdev, hdev->dev);
|
cdev_device_del(&hdev->cdev, hdev->dev);
|
||||||
|
|
||||||
|
put_devices:
|
||||||
|
put_device(hdev->dev);
|
||||||
|
put_device(hdev->dev_ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_hard_reset_pending(struct work_struct *work)
|
static void device_hard_reset_pending(struct work_struct *work)
|
||||||
|
@ -1430,9 +1430,9 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
|
||||||
early_fini:
|
early_fini:
|
||||||
device_early_fini(hdev);
|
device_early_fini(hdev);
|
||||||
free_dev_ctrl:
|
free_dev_ctrl:
|
||||||
kfree(hdev->dev_ctrl);
|
put_device(hdev->dev_ctrl);
|
||||||
free_dev:
|
free_dev:
|
||||||
kfree(hdev->dev);
|
put_device(hdev->dev);
|
||||||
out_disabled:
|
out_disabled:
|
||||||
hdev->disabled = true;
|
hdev->disabled = true;
|
||||||
if (add_cdev_sysfs_on_err)
|
if (add_cdev_sysfs_on_err)
|
||||||
|
|
|
@ -46,14 +46,4 @@ config INTEL_MEI_TXE
|
||||||
Supported SoCs:
|
Supported SoCs:
|
||||||
Intel Bay Trail
|
Intel Bay Trail
|
||||||
|
|
||||||
config INTEL_MEI_VIRTIO
|
|
||||||
tristate "Intel MEI interface emulation with virtio framework"
|
|
||||||
select INTEL_MEI
|
|
||||||
depends on X86 && PCI && VIRTIO_PCI
|
|
||||||
help
|
|
||||||
This module implements mei hw emulation over virtio transport.
|
|
||||||
The module will be called mei_virtio.
|
|
||||||
Enable this if your virtual machine supports virtual mei
|
|
||||||
device over virtio.
|
|
||||||
|
|
||||||
source "drivers/misc/mei/hdcp/Kconfig"
|
source "drivers/misc/mei/hdcp/Kconfig"
|
||||||
|
|
|
@ -22,9 +22,6 @@ obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o
|
||||||
mei-txe-objs := pci-txe.o
|
mei-txe-objs := pci-txe.o
|
||||||
mei-txe-objs += hw-txe.o
|
mei-txe-objs += hw-txe.o
|
||||||
|
|
||||||
obj-$(CONFIG_INTEL_MEI_VIRTIO) += mei-virtio.o
|
|
||||||
mei-virtio-objs := hw-virtio.o
|
|
||||||
|
|
||||||
mei-$(CONFIG_EVENT_TRACING) += mei-trace.o
|
mei-$(CONFIG_EVENT_TRACING) += mei-trace.o
|
||||||
CFLAGS_mei-trace.o = -I$(src)
|
CFLAGS_mei-trace.o = -I$(src)
|
||||||
|
|
||||||
|
|
|
@ -1,874 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/*
|
|
||||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
|
||||||
* Copyright (c) 2018-2020, Intel Corporation.
|
|
||||||
*/
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/virtio.h>
|
|
||||||
#include <linux/virtio_config.h>
|
|
||||||
#include <linux/virtio_ids.h>
|
|
||||||
#include <linux/atomic.h>
|
|
||||||
|
|
||||||
#include "mei_dev.h"
|
|
||||||
#include "hbm.h"
|
|
||||||
#include "client.h"
|
|
||||||
|
|
||||||
#define MEI_VIRTIO_RPM_TIMEOUT 500
|
|
||||||
/* ACRN virtio device types */
|
|
||||||
#ifndef VIRTIO_ID_MEI
|
|
||||||
#define VIRTIO_ID_MEI 0xFFFE /* virtio mei */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct mei_virtio_cfg - settings passed from the virtio backend
|
|
||||||
* @buf_depth: read buffer depth in slots (4bytes)
|
|
||||||
* @hw_ready: hw is ready for operation
|
|
||||||
* @host_reset: synchronize reset with virtio backend
|
|
||||||
* @reserved: reserved for alignment
|
|
||||||
* @fw_status: FW status
|
|
||||||
*/
|
|
||||||
struct mei_virtio_cfg {
|
|
||||||
u32 buf_depth;
|
|
||||||
u8 hw_ready;
|
|
||||||
u8 host_reset;
|
|
||||||
u8 reserved[2];
|
|
||||||
u32 fw_status[MEI_FW_STATUS_MAX];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct mei_virtio_hw {
|
|
||||||
struct mei_device mdev;
|
|
||||||
char name[32];
|
|
||||||
|
|
||||||
struct virtqueue *in;
|
|
||||||
struct virtqueue *out;
|
|
||||||
|
|
||||||
bool host_ready;
|
|
||||||
struct work_struct intr_handler;
|
|
||||||
|
|
||||||
u32 *recv_buf;
|
|
||||||
u8 recv_rdy;
|
|
||||||
size_t recv_sz;
|
|
||||||
u32 recv_idx;
|
|
||||||
u32 recv_len;
|
|
||||||
|
|
||||||
/* send buffer */
|
|
||||||
atomic_t hbuf_ready;
|
|
||||||
const void *send_hdr;
|
|
||||||
const void *send_buf;
|
|
||||||
|
|
||||||
struct mei_virtio_cfg cfg;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define to_virtio_hw(_dev) container_of(_dev, struct mei_virtio_hw, mdev)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_fw_status() - read status register of mei
|
|
||||||
* @dev: mei device
|
|
||||||
* @fw_status: fw status register values
|
|
||||||
*
|
|
||||||
* Return: always 0
|
|
||||||
*/
|
|
||||||
static int mei_virtio_fw_status(struct mei_device *dev,
|
|
||||||
struct mei_fw_status *fw_status)
|
|
||||||
{
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
fw_status->count = MEI_FW_STATUS_MAX;
|
|
||||||
virtio_cread_bytes(vdev, offsetof(struct mei_virtio_cfg, fw_status),
|
|
||||||
fw_status->status, sizeof(fw_status->status));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_pg_state() - translate internal pg state
|
|
||||||
* to the mei power gating state
|
|
||||||
* There is no power management in ACRN mode always return OFF
|
|
||||||
* @dev: mei device
|
|
||||||
*
|
|
||||||
* Return:
|
|
||||||
* * MEI_PG_OFF - if aliveness is on (always)
|
|
||||||
* * MEI_PG_ON - (never)
|
|
||||||
*/
|
|
||||||
static inline enum mei_pg_state mei_virtio_pg_state(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
return MEI_PG_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_config() - configure hw dependent settings
|
|
||||||
*
|
|
||||||
* @dev: mei device
|
|
||||||
*
|
|
||||||
* Return: always 0
|
|
||||||
*/
|
|
||||||
static int mei_virtio_hw_config(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hbuf_empty_slots() - counts write empty slots.
|
|
||||||
* @dev: the device structure
|
|
||||||
*
|
|
||||||
* Return: always return frontend buf size if buffer is ready, 0 otherwise
|
|
||||||
*/
|
|
||||||
static int mei_virtio_hbuf_empty_slots(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
return (atomic_read(&hw->hbuf_ready) == 1) ? hw->cfg.buf_depth : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hbuf_is_ready() - checks if write buffer is ready
|
|
||||||
* @dev: the device structure
|
|
||||||
*
|
|
||||||
* Return: true if hbuf is ready
|
|
||||||
*/
|
|
||||||
static bool mei_virtio_hbuf_is_ready(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
return atomic_read(&hw->hbuf_ready) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hbuf_max_depth() - returns depth of FE write buffer.
|
|
||||||
* @dev: the device structure
|
|
||||||
*
|
|
||||||
* Return: size of frontend write buffer in bytes
|
|
||||||
*/
|
|
||||||
static u32 mei_virtio_hbuf_depth(const struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
return hw->cfg.buf_depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_intr_clear() - clear and stop interrupts
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
static void mei_virtio_intr_clear(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* In our virtio solution, there are two types of interrupts,
|
|
||||||
* vq interrupt and config change interrupt.
|
|
||||||
* 1) start/reset rely on virtio config changed interrupt;
|
|
||||||
* 2) send/recv rely on virtio virtqueue interrupts.
|
|
||||||
* They are all virtual interrupts. So, we don't have corresponding
|
|
||||||
* operation to do here.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_intr_enable() - enables mei BE virtqueues callbacks
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
static void mei_virtio_intr_enable(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
virtio_config_enable(vdev);
|
|
||||||
|
|
||||||
virtqueue_enable_cb(hw->in);
|
|
||||||
virtqueue_enable_cb(hw->out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_intr_disable() - disables mei BE virtqueues callbacks
|
|
||||||
*
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
static void mei_virtio_intr_disable(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
virtio_config_disable(vdev);
|
|
||||||
|
|
||||||
virtqueue_disable_cb(hw->in);
|
|
||||||
virtqueue_disable_cb(hw->out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_synchronize_irq() - wait for pending IRQ handlers for all
|
|
||||||
* virtqueue
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
static void mei_virtio_synchronize_irq(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now, all IRQ handlers are converted to workqueue.
|
|
||||||
* Change synchronize irq to flush this work.
|
|
||||||
*/
|
|
||||||
flush_work(&hw->intr_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mei_virtio_free_outbufs(struct mei_virtio_hw *hw)
|
|
||||||
{
|
|
||||||
kfree(hw->send_hdr);
|
|
||||||
kfree(hw->send_buf);
|
|
||||||
hw->send_hdr = NULL;
|
|
||||||
hw->send_buf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_write_message() - writes a message to mei virtio back-end service.
|
|
||||||
* @dev: the device structure
|
|
||||||
* @hdr: mei header of message
|
|
||||||
* @hdr_len: header length
|
|
||||||
* @data: message payload will be written
|
|
||||||
* @data_len: message payload length
|
|
||||||
*
|
|
||||||
* Return:
|
|
||||||
* * 0: on success
|
|
||||||
* * -EIO: if write has failed
|
|
||||||
* * -ENOMEM: on memory allocation failure
|
|
||||||
*/
|
|
||||||
static int mei_virtio_write_message(struct mei_device *dev,
|
|
||||||
const void *hdr, size_t hdr_len,
|
|
||||||
const void *data, size_t data_len)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct scatterlist sg[2];
|
|
||||||
const void *hbuf, *dbuf;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (WARN_ON(!atomic_add_unless(&hw->hbuf_ready, -1, 0)))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
hbuf = kmemdup(hdr, hdr_len, GFP_KERNEL);
|
|
||||||
hw->send_hdr = hbuf;
|
|
||||||
|
|
||||||
dbuf = kmemdup(data, data_len, GFP_KERNEL);
|
|
||||||
hw->send_buf = dbuf;
|
|
||||||
|
|
||||||
if (!hbuf || !dbuf) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
sg_init_table(sg, 2);
|
|
||||||
sg_set_buf(&sg[0], hbuf, hdr_len);
|
|
||||||
sg_set_buf(&sg[1], dbuf, data_len);
|
|
||||||
|
|
||||||
ret = virtqueue_add_outbuf(hw->out, sg, 2, hw, GFP_KERNEL);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev->dev, "failed to add outbuf\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtqueue_kick(hw->out);
|
|
||||||
return 0;
|
|
||||||
fail:
|
|
||||||
|
|
||||||
mei_virtio_free_outbufs(hw);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_count_full_read_slots() - counts read full slots.
|
|
||||||
* @dev: the device structure
|
|
||||||
*
|
|
||||||
* Return: -EOVERFLOW if overflow, otherwise filled slots count
|
|
||||||
*/
|
|
||||||
static int mei_virtio_count_full_read_slots(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
if (hw->recv_idx > hw->recv_len)
|
|
||||||
return -EOVERFLOW;
|
|
||||||
|
|
||||||
return hw->recv_len - hw->recv_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_read_hdr() - Reads 32bit dword from mei virtio receive buffer
|
|
||||||
*
|
|
||||||
* @dev: the device structure
|
|
||||||
*
|
|
||||||
* Return: 32bit dword of receive buffer (u32)
|
|
||||||
*/
|
|
||||||
static inline u32 mei_virtio_read_hdr(const struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
WARN_ON(hw->cfg.buf_depth < hw->recv_idx + 1);
|
|
||||||
|
|
||||||
return hw->recv_buf[hw->recv_idx++];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mei_virtio_read(struct mei_device *dev, unsigned char *buffer,
|
|
||||||
unsigned long len)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
u32 slots = mei_data2slots(len);
|
|
||||||
|
|
||||||
if (WARN_ON(hw->cfg.buf_depth < hw->recv_idx + slots))
|
|
||||||
return -EOVERFLOW;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Assumption: There is only one MEI message in recv_buf each time.
|
|
||||||
* Backend service need follow this rule too.
|
|
||||||
*/
|
|
||||||
memcpy(buffer, hw->recv_buf + hw->recv_idx, len);
|
|
||||||
hw->recv_idx += slots;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mei_virtio_pg_is_enabled(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mei_virtio_pg_in_transition(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mei_virtio_add_recv_buf(struct mei_virtio_hw *hw)
|
|
||||||
{
|
|
||||||
struct scatterlist sg;
|
|
||||||
|
|
||||||
if (hw->recv_rdy) /* not needed */
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* refill the recv_buf to IN virtqueue to get next message */
|
|
||||||
sg_init_one(&sg, hw->recv_buf, mei_slots2data(hw->cfg.buf_depth));
|
|
||||||
hw->recv_len = 0;
|
|
||||||
hw->recv_idx = 0;
|
|
||||||
hw->recv_rdy = 1;
|
|
||||||
virtqueue_add_inbuf(hw->in, &sg, 1, hw->recv_buf, GFP_KERNEL);
|
|
||||||
virtqueue_kick(hw->in);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_is_ready() - check whether the BE(hw) has turned ready
|
|
||||||
* @dev: mei device
|
|
||||||
* Return: bool
|
|
||||||
*/
|
|
||||||
static bool mei_virtio_hw_is_ready(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
virtio_cread(vdev, struct mei_virtio_cfg,
|
|
||||||
hw_ready, &hw->cfg.hw_ready);
|
|
||||||
|
|
||||||
dev_dbg(dev->dev, "hw ready %d\n", hw->cfg.hw_ready);
|
|
||||||
|
|
||||||
return hw->cfg.hw_ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_reset - resets virtio hw.
|
|
||||||
*
|
|
||||||
* @dev: the device structure
|
|
||||||
* @intr_enable: virtio use data/config callbacks
|
|
||||||
*
|
|
||||||
* Return: 0 on success an error code otherwise
|
|
||||||
*/
|
|
||||||
static int mei_virtio_hw_reset(struct mei_device *dev, bool intr_enable)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
dev_dbg(dev->dev, "hw reset\n");
|
|
||||||
|
|
||||||
dev->recvd_hw_ready = false;
|
|
||||||
hw->host_ready = false;
|
|
||||||
atomic_set(&hw->hbuf_ready, 0);
|
|
||||||
hw->recv_len = 0;
|
|
||||||
hw->recv_idx = 0;
|
|
||||||
|
|
||||||
hw->cfg.host_reset = 1;
|
|
||||||
virtio_cwrite(vdev, struct mei_virtio_cfg,
|
|
||||||
host_reset, &hw->cfg.host_reset);
|
|
||||||
|
|
||||||
mei_virtio_hw_is_ready(dev);
|
|
||||||
|
|
||||||
if (intr_enable)
|
|
||||||
mei_virtio_intr_enable(dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_reset_release() - release device from the reset
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
static void mei_virtio_hw_reset_release(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(dev->dev);
|
|
||||||
|
|
||||||
dev_dbg(dev->dev, "hw reset release\n");
|
|
||||||
hw->cfg.host_reset = 0;
|
|
||||||
virtio_cwrite(vdev, struct mei_virtio_cfg,
|
|
||||||
host_reset, &hw->cfg.host_reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_ready_wait() - wait until the virtio(hw) has turned ready
|
|
||||||
* or timeout is reached
|
|
||||||
* @dev: mei device
|
|
||||||
*
|
|
||||||
* Return: 0 on success, error otherwise
|
|
||||||
*/
|
|
||||||
static int mei_virtio_hw_ready_wait(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
mutex_unlock(&dev->device_lock);
|
|
||||||
wait_event_timeout(dev->wait_hw_ready,
|
|
||||||
dev->recvd_hw_ready,
|
|
||||||
mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT));
|
|
||||||
mutex_lock(&dev->device_lock);
|
|
||||||
if (!dev->recvd_hw_ready) {
|
|
||||||
dev_err(dev->dev, "wait hw ready failed\n");
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->recvd_hw_ready = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_hw_start() - hw start routine
|
|
||||||
* @dev: mei device
|
|
||||||
*
|
|
||||||
* Return: 0 on success, error otherwise
|
|
||||||
*/
|
|
||||||
static int mei_virtio_hw_start(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
dev_dbg(dev->dev, "hw start\n");
|
|
||||||
mei_virtio_hw_reset_release(dev);
|
|
||||||
|
|
||||||
ret = mei_virtio_hw_ready_wait(dev);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
mei_virtio_add_recv_buf(hw);
|
|
||||||
atomic_set(&hw->hbuf_ready, 1);
|
|
||||||
dev_dbg(dev->dev, "hw is ready\n");
|
|
||||||
hw->host_ready = true;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_host_is_ready() - check whether the FE has turned ready
|
|
||||||
* @dev: mei device
|
|
||||||
*
|
|
||||||
* Return: bool
|
|
||||||
*/
|
|
||||||
static bool mei_virtio_host_is_ready(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = to_virtio_hw(dev);
|
|
||||||
|
|
||||||
dev_dbg(dev->dev, "host ready %d\n", hw->host_ready);
|
|
||||||
|
|
||||||
return hw->host_ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_data_in() - The callback of recv virtqueue of virtio mei
|
|
||||||
* @vq: receiving virtqueue
|
|
||||||
*/
|
|
||||||
static void mei_virtio_data_in(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vq->vdev->priv;
|
|
||||||
|
|
||||||
/* disable interrupts (enabled again from in the interrupt worker) */
|
|
||||||
virtqueue_disable_cb(hw->in);
|
|
||||||
|
|
||||||
schedule_work(&hw->intr_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_virtio_data_out() - The callback of send virtqueue of virtio mei
|
|
||||||
* @vq: transmitting virtqueue
|
|
||||||
*/
|
|
||||||
static void mei_virtio_data_out(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vq->vdev->priv;
|
|
||||||
|
|
||||||
schedule_work(&hw->intr_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mei_virtio_intr_handler(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw =
|
|
||||||
container_of(work, struct mei_virtio_hw, intr_handler);
|
|
||||||
struct mei_device *dev = &hw->mdev;
|
|
||||||
LIST_HEAD(complete_list);
|
|
||||||
s32 slots;
|
|
||||||
int rets = 0;
|
|
||||||
void *data;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
mutex_lock(&dev->device_lock);
|
|
||||||
|
|
||||||
if (dev->dev_state == MEI_DEV_DISABLED) {
|
|
||||||
dev_warn(dev->dev, "Interrupt in disabled state.\n");
|
|
||||||
mei_virtio_intr_disable(dev);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if ME wants a reset */
|
|
||||||
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
|
|
||||||
dev_warn(dev->dev, "BE service not ready: resetting.\n");
|
|
||||||
schedule_work(&dev->reset_work);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if we need to start the dev */
|
|
||||||
if (!mei_host_is_ready(dev)) {
|
|
||||||
if (mei_hw_is_ready(dev)) {
|
|
||||||
dev_dbg(dev->dev, "we need to start the dev.\n");
|
|
||||||
dev->recvd_hw_ready = true;
|
|
||||||
wake_up(&dev->wait_hw_ready);
|
|
||||||
} else {
|
|
||||||
dev_warn(dev->dev, "Spurious Interrupt\n");
|
|
||||||
}
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read */
|
|
||||||
if (hw->recv_rdy) {
|
|
||||||
data = virtqueue_get_buf(hw->in, &len);
|
|
||||||
if (!data || !len) {
|
|
||||||
dev_dbg(dev->dev, "No data %d", len);
|
|
||||||
} else {
|
|
||||||
dev_dbg(dev->dev, "data_in %d\n", len);
|
|
||||||
WARN_ON(data != hw->recv_buf);
|
|
||||||
hw->recv_len = mei_data2slots(len);
|
|
||||||
hw->recv_rdy = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write */
|
|
||||||
if (!atomic_read(&hw->hbuf_ready)) {
|
|
||||||
if (!virtqueue_get_buf(hw->out, &len)) {
|
|
||||||
dev_warn(dev->dev, "Failed to getbuf\n");
|
|
||||||
} else {
|
|
||||||
mei_virtio_free_outbufs(hw);
|
|
||||||
atomic_inc(&hw->hbuf_ready);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check slots available for reading */
|
|
||||||
slots = mei_count_full_read_slots(dev);
|
|
||||||
while (slots > 0) {
|
|
||||||
dev_dbg(dev->dev, "slots to read = %08x\n", slots);
|
|
||||||
rets = mei_irq_read_handler(dev, &complete_list, &slots);
|
|
||||||
|
|
||||||
if (rets &&
|
|
||||||
(dev->dev_state != MEI_DEV_RESETTING &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_DOWN)) {
|
|
||||||
dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n",
|
|
||||||
rets);
|
|
||||||
schedule_work(&dev->reset_work);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
|
||||||
|
|
||||||
mei_irq_write_handler(dev, &complete_list);
|
|
||||||
|
|
||||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
|
||||||
|
|
||||||
mei_irq_compl_handler(dev, &complete_list);
|
|
||||||
|
|
||||||
mei_virtio_add_recv_buf(hw);
|
|
||||||
|
|
||||||
end:
|
|
||||||
if (dev->dev_state != MEI_DEV_DISABLED) {
|
|
||||||
if (!virtqueue_enable_cb(hw->in))
|
|
||||||
schedule_work(&hw->intr_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&dev->device_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mei_virtio_config_changed(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
struct mei_device *dev = &hw->mdev;
|
|
||||||
|
|
||||||
virtio_cread(vdev, struct mei_virtio_cfg,
|
|
||||||
hw_ready, &hw->cfg.hw_ready);
|
|
||||||
|
|
||||||
if (dev->dev_state == MEI_DEV_DISABLED) {
|
|
||||||
dev_dbg(dev->dev, "disabled state don't start\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run intr handler once to handle reset notify */
|
|
||||||
schedule_work(&hw->intr_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mei_virtio_remove_vqs(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
|
|
||||||
virtqueue_detach_unused_buf(hw->in);
|
|
||||||
hw->recv_len = 0;
|
|
||||||
hw->recv_idx = 0;
|
|
||||||
hw->recv_rdy = 0;
|
|
||||||
|
|
||||||
virtqueue_detach_unused_buf(hw->out);
|
|
||||||
|
|
||||||
mei_virtio_free_outbufs(hw);
|
|
||||||
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are two virtqueues, one is for send and another is for recv.
|
|
||||||
*/
|
|
||||||
static int mei_virtio_init_vqs(struct mei_virtio_hw *hw,
|
|
||||||
struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct virtqueue *vqs[2];
|
|
||||||
|
|
||||||
vq_callback_t *cbs[] = {
|
|
||||||
mei_virtio_data_in,
|
|
||||||
mei_virtio_data_out,
|
|
||||||
};
|
|
||||||
static const char * const names[] = {
|
|
||||||
"in",
|
|
||||||
"out",
|
|
||||||
};
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = virtio_find_vqs(vdev, 2, vqs, cbs, names, NULL);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
hw->in = vqs[0];
|
|
||||||
hw->out = vqs[1];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct mei_hw_ops mei_virtio_ops = {
|
|
||||||
.fw_status = mei_virtio_fw_status,
|
|
||||||
.pg_state = mei_virtio_pg_state,
|
|
||||||
|
|
||||||
.host_is_ready = mei_virtio_host_is_ready,
|
|
||||||
|
|
||||||
.hw_is_ready = mei_virtio_hw_is_ready,
|
|
||||||
.hw_reset = mei_virtio_hw_reset,
|
|
||||||
.hw_config = mei_virtio_hw_config,
|
|
||||||
.hw_start = mei_virtio_hw_start,
|
|
||||||
|
|
||||||
.pg_in_transition = mei_virtio_pg_in_transition,
|
|
||||||
.pg_is_enabled = mei_virtio_pg_is_enabled,
|
|
||||||
|
|
||||||
.intr_clear = mei_virtio_intr_clear,
|
|
||||||
.intr_enable = mei_virtio_intr_enable,
|
|
||||||
.intr_disable = mei_virtio_intr_disable,
|
|
||||||
.synchronize_irq = mei_virtio_synchronize_irq,
|
|
||||||
|
|
||||||
.hbuf_free_slots = mei_virtio_hbuf_empty_slots,
|
|
||||||
.hbuf_is_ready = mei_virtio_hbuf_is_ready,
|
|
||||||
.hbuf_depth = mei_virtio_hbuf_depth,
|
|
||||||
|
|
||||||
.write = mei_virtio_write_message,
|
|
||||||
|
|
||||||
.rdbuf_full_slots = mei_virtio_count_full_read_slots,
|
|
||||||
.read_hdr = mei_virtio_read_hdr,
|
|
||||||
.read = mei_virtio_read,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int mei_virtio_probe(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
hw = devm_kzalloc(&vdev->dev, sizeof(*hw), GFP_KERNEL);
|
|
||||||
if (!hw)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
vdev->priv = hw;
|
|
||||||
|
|
||||||
INIT_WORK(&hw->intr_handler, mei_virtio_intr_handler);
|
|
||||||
|
|
||||||
ret = mei_virtio_init_vqs(hw, vdev);
|
|
||||||
if (ret)
|
|
||||||
goto vqs_failed;
|
|
||||||
|
|
||||||
virtio_cread(vdev, struct mei_virtio_cfg,
|
|
||||||
buf_depth, &hw->cfg.buf_depth);
|
|
||||||
|
|
||||||
hw->recv_buf = kzalloc(mei_slots2data(hw->cfg.buf_depth), GFP_KERNEL);
|
|
||||||
if (!hw->recv_buf) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto hbuf_failed;
|
|
||||||
}
|
|
||||||
atomic_set(&hw->hbuf_ready, 0);
|
|
||||||
|
|
||||||
virtio_device_ready(vdev);
|
|
||||||
|
|
||||||
mei_device_init(&hw->mdev, &vdev->dev, &mei_virtio_ops);
|
|
||||||
|
|
||||||
pm_runtime_get_noresume(&vdev->dev);
|
|
||||||
pm_runtime_set_active(&vdev->dev);
|
|
||||||
pm_runtime_enable(&vdev->dev);
|
|
||||||
|
|
||||||
ret = mei_start(&hw->mdev);
|
|
||||||
if (ret)
|
|
||||||
goto mei_start_failed;
|
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(&vdev->dev, MEI_VIRTIO_RPM_TIMEOUT);
|
|
||||||
pm_runtime_use_autosuspend(&vdev->dev);
|
|
||||||
|
|
||||||
ret = mei_register(&hw->mdev, &vdev->dev);
|
|
||||||
if (ret)
|
|
||||||
goto mei_failed;
|
|
||||||
|
|
||||||
pm_runtime_put(&vdev->dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mei_failed:
|
|
||||||
mei_stop(&hw->mdev);
|
|
||||||
mei_start_failed:
|
|
||||||
mei_cancel_work(&hw->mdev);
|
|
||||||
mei_disable_interrupts(&hw->mdev);
|
|
||||||
kfree(hw->recv_buf);
|
|
||||||
hbuf_failed:
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
vqs_failed:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __maybe_unused mei_virtio_pm_runtime_idle(struct device *device)
|
|
||||||
{
|
|
||||||
struct virtio_device *vdev = dev_to_virtio(device);
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
|
|
||||||
dev_dbg(&vdev->dev, "rpm: mei_virtio : runtime_idle\n");
|
|
||||||
|
|
||||||
if (!hw)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
if (mei_write_is_idle(&hw->mdev))
|
|
||||||
pm_runtime_autosuspend(device);
|
|
||||||
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __maybe_unused mei_virtio_pm_runtime_suspend(struct device *device)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __maybe_unused mei_virtio_pm_runtime_resume(struct device *device)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __maybe_unused mei_virtio_freeze(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
|
|
||||||
dev_dbg(&vdev->dev, "freeze\n");
|
|
||||||
|
|
||||||
if (!hw)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
mei_stop(&hw->mdev);
|
|
||||||
mei_disable_interrupts(&hw->mdev);
|
|
||||||
cancel_work_sync(&hw->intr_handler);
|
|
||||||
vdev->config->reset(vdev);
|
|
||||||
mei_virtio_remove_vqs(vdev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __maybe_unused mei_virtio_restore(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
dev_dbg(&vdev->dev, "restore\n");
|
|
||||||
|
|
||||||
if (!hw)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
ret = mei_virtio_init_vqs(hw, vdev);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
virtio_device_ready(vdev);
|
|
||||||
|
|
||||||
ret = mei_restart(&hw->mdev);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Start timer if stopped in suspend */
|
|
||||||
schedule_delayed_work(&hw->mdev.timer_work, HZ);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct dev_pm_ops mei_virtio_pm_ops = {
|
|
||||||
SET_RUNTIME_PM_OPS(mei_virtio_pm_runtime_suspend,
|
|
||||||
mei_virtio_pm_runtime_resume,
|
|
||||||
mei_virtio_pm_runtime_idle)
|
|
||||||
};
|
|
||||||
|
|
||||||
static void mei_virtio_remove(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct mei_virtio_hw *hw = vdev->priv;
|
|
||||||
|
|
||||||
mei_stop(&hw->mdev);
|
|
||||||
mei_disable_interrupts(&hw->mdev);
|
|
||||||
cancel_work_sync(&hw->intr_handler);
|
|
||||||
mei_deregister(&hw->mdev);
|
|
||||||
vdev->config->reset(vdev);
|
|
||||||
mei_virtio_remove_vqs(vdev);
|
|
||||||
kfree(hw->recv_buf);
|
|
||||||
pm_runtime_disable(&vdev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct virtio_device_id id_table[] = {
|
|
||||||
{ VIRTIO_ID_MEI, VIRTIO_DEV_ANY_ID },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct virtio_driver mei_virtio_driver = {
|
|
||||||
.id_table = id_table,
|
|
||||||
.probe = mei_virtio_probe,
|
|
||||||
.remove = mei_virtio_remove,
|
|
||||||
.config_changed = mei_virtio_config_changed,
|
|
||||||
.driver = {
|
|
||||||
.name = KBUILD_MODNAME,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &mei_virtio_pm_ops,
|
|
||||||
},
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
.freeze = mei_virtio_freeze,
|
|
||||||
.restore = mei_virtio_restore,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
module_virtio_driver(mei_virtio_driver);
|
|
||||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
|
||||||
MODULE_DESCRIPTION("Virtio MEI frontend driver");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue