* [PATCH 4/4] ARM: dts: bcm2837-rpi-3-b: add GPIO expander
From: Stefan Wahren @ 2018-01-02 19:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a29df633ee568e150c150debde66c0783a57c70a.1514898134.git.baruch@tkos.co.il>
Hi Baruch,
> Baruch Siach <baruch@tkos.co.il> hat am 2. Januar 2018 um 14:19 geschrieben:
>
>
> Add a description of the RPi3 GPIO expander that the VC4 firmware controls.
>
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
> ---
> arch/arm/boot/dts/bcm2837-rpi-3-b.dts | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/arch/arm/boot/dts/bcm2837-rpi-3-b.dts b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
> index b44b3b5af00d..09dca48da2bd 100644
> --- a/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
> +++ b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
> @@ -23,6 +23,16 @@
> gpios = <&gpio 47 0>;
> };
> };
> +
> + soc {
unfortunately this is a common issue of the RPi firmware drivers. These nodes shouldn't be child of the soc, because it's only bound for memory mapped IO. So please move it to the same level as soc. Otherwise we get more warnings with W=1 and Rob Herring gets unhappy.
> + expgpio: expgpio {
> + compatible = "brcm,bcm2835-expgpio";
> + gpio-controller;
> + #gpio-cells = <2>;
> + firmware = <&firmware>;
Please add the gpio-line-names from Eric's patch [1].
Thanks
Stefan
[1] - https://patchwork.kernel.org/patch/9339857/
> + status = "okay";
> + };
> + };
> };
>
> /* uart0 communicates with the BT module */
> --
> 2.15.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] Nokia N9: add support for up/down keys in the dts
From: Sebastian Reichel @ 2018-01-02 19:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102125948.GA20051@amd>
Hi,
On Tue, Jan 02, 2018 at 01:59:48PM +0100, Pavel Machek wrote:
>
> This adds support for volume up/down keys in the dts.
>
> Signed-off-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
-- Sebastian
>
> diff --git a/arch/arm/boot/dts/omap3-n9.dts b/arch/arm/boot/dts/omap3-n9.dts
> index 39e35f8..57a6679 100644
> --- a/arch/arm/boot/dts/omap3-n9.dts
> +++ b/arch/arm/boot/dts/omap3-n9.dts
> @@ -11,9 +11,10 @@
> /dts-v1/;
>
> #include "omap3-n950-n9.dtsi"
> +#include <dt-bindings/input/input.h>
>
> / {
> model = "Nokia N9";
> compatible = "nokia,omap3-n9", "ti,omap36xx", "ti,omap3";
> };
>
> @@ -72,3 +133,9 @@
> st,max-limit-y = <46>;
> st,max-limit-z = <46>;
> };
> +
> +&twl_keypad {
> + linux,keymap = < MATRIX_KEY(6, 8, KEY_VOLUMEUP)
> + MATRIX_KEY(7, 8, KEY_VOLUMEDOWN)
> + >;
> +};
>
>
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180102/1fd5da38/attachment.sig>
^ permalink raw reply
* [PATCH v7 00/10] add support for relative references in special sections
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
This adds support for emitting special sections such as initcall arrays,
PCI fixups and tracepoints as relative references rather than absolute
references. This reduces the size by 50% on 64-bit architectures, but
more importantly, it removes the need for carrying relocation metadata
for these sections in relocatables kernels (e.g., for KASLR) that need
to fix up these absolute references at boot time. On arm64, this reduces
the vmlinux footprint of such a reference by 8x (8 byte absolute reference
+ 24 byte RELA entry vs 4 byte relative reference)
Patch #3 was sent out before as a single patch. This series supersedes
the previous submission. This version makes relative ksymtab entries
dependent on the new Kconfig symbol HAVE_ARCH_PREL32_RELOCATIONS rather
than trying to infer from kbuild test robot replies for which architectures
it should be blacklisted.
Patch #1 introduces the new Kconfig symbol HAVE_ARCH_PREL32_RELOCATIONS,
and sets it for the main architectures that are expected to benefit the
most from this feature, i.e., 64-bit architectures or ones that use
runtime relocations.
Patches #4 - #6 implement relative references for initcalls, PCI fixups
and tracepoints, respectively, all of which produce sections with order
~1000 entries on an arm64 defconfig kernel with tracing enabled. This
means we save about 28 KB of vmlinux space for each of these patches.
Patches #7 - #10 have been added in v5, and implement relative references
in jump tables for arm64 and x86. On arm64, this results in significant
space savings (650+ KB on a typical distro kernel). On x86, the savings
are not as impressive, but still worthwhile. (Note that these patches
do not rely on CONFIG_HAVE_ARCH_PREL32_RELOCATIONS, given that the
inline asm that is emitted is already per-arch)
For the arm64 kernel, all patches combined reduce the memory footprint of
vmlinux by about 1.3 MB (using a config copied from Ubuntu that has KASLR
enabled), of which ~1 MB is the size reduction of the RELA section in .init,
and the remaining 300 KB is reduction of .text/.data.
Branch:
git://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git relative-special-sections-v7
Changes since v6:
- drop S390 from patch #1 introducing HAVE_ARCH_PREL32_RELOCATIONS: kbuild
robot threw me some s390 curveballs, and given that s390 does not define
CONFIG_RELOCATABLE in the first place, it does not benefit as much from
relative references as arm64, x86 and power do
- add patch to allow symbol exports to be disabled at compilation unit
granularity (#2)
- get rid of arm64 vmlinux.lds.S hunk to ensure code generated by __ADDRESSABLE
gets discarded from the EFI stub - it is no longer needed after adding #2 (#1)
- change _ADDRESSABLE() to emit a data reference, not a code reference - this
is another simplification made possible by patch #2 (#3)
- add Steven's ack to #6
- split x86 jump_label patch into two (#9, #10)
Changes since v5:
- add missing jump_label prototypes to s390 jump_label.h (#6)
- fix inverted condition in call to jump_entry_is_module_init() (#6)
Changes since v4:
- add patches to convert x86 and arm64 to use relative references for jump
tables (#6 - #8)
- rename PCI patch and add Bjorn's ack (#4)
- rebase onto v4.15-rc5
Changes since v3:
- fix module unload issue in patch #5 reported by Jessica, by reusing the
updated routine for_each_tracepoint_range() for the quiescent check at
module unload time; this requires this routine to be moved before
tracepoint_module_going() in kernel/tracepoint.c
- add Jessica's ack to #2
- rebase onto v4.14-rc1
Changes since v2:
- Revert my slightly misguided attempt to appease checkpatch, which resulted
in needless churn and worse code. This v3 is based on v1 with a few tweaks
that were actually reasonable checkpatch warnings: unnecessary braces (as
pointed out by Ingo) and other minor whitespace misdemeanors.
Changes since v1:
- Remove checkpatch errors to the extent feasible: in some cases, this
involves moving extern declarations into C files, and switching to
struct definitions rather than typedefs. Some errors are impossible
to fix: please find the remaining ones after the diffstat.
- Used 'int' instead if 'signed int' for the various offset fields: there
is no ambiguity between architectures regarding its signedness (unlike
'char')
- Refactor the different patches to be more uniform in the way they define
the section entry type and accessors in the .h file, and avoid the need to
add #ifdefs to the C code.
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Thomas Garnier <thgarnie@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Nicolas Pitre <nico@linaro.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jessica Yu <jeyu@kernel.org>
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-kernel at vger.kernel.org
Cc: linux-mips at linux-mips.org
Cc: linuxppc-dev at lists.ozlabs.org
Cc: linux-s390 at vger.kernel.org
Cc: sparclinux at vger.kernel.org
Cc: x86 at kernel.org
Ard Biesheuvel (10):
arch: enable relative relocations for arm64, power and x86
module: allow symbol exports to be disabled
module: use relative references for __ksymtab entries
init: allow initcall tables to be emitted using relative references
PCI: Add support for relative addressing in quirk tables
kernel: tracepoints: add support for relative references
kernel/jump_label: abstract jump_entry member accessors
arm64/kernel: jump_label: use relative references
x86: jump_label: switch to jump_entry accessors
x86/kernel: jump_table: use relative references
arch/Kconfig | 10 ++++
arch/arm/include/asm/jump_label.h | 27 +++++++++
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/jump_label.h | 48 +++++++++++++---
arch/arm64/kernel/jump_label.c | 22 +++++++-
arch/mips/include/asm/jump_label.h | 27 +++++++++
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/jump_label.h | 27 +++++++++
arch/s390/include/asm/jump_label.h | 27 +++++++++
arch/sparc/include/asm/jump_label.h | 27 +++++++++
arch/tile/include/asm/jump_label.h | 27 +++++++++
arch/x86/Kconfig | 1 +
arch/x86/boot/compressed/kaslr.c | 5 +-
arch/x86/include/asm/Kbuild | 1 +
arch/x86/include/asm/export.h | 5 --
arch/x86/include/asm/jump_label.h | 56 +++++++++++++++----
arch/x86/kernel/jump_label.c | 59 ++++++++++++++------
drivers/firmware/efi/libstub/Makefile | 3 +-
drivers/pci/quirks.c | 13 ++++-
include/asm-generic/export.h | 12 +++-
include/linux/compiler.h | 10 ++++
include/linux/export.h | 55 ++++++++++++++----
include/linux/init.h | 44 +++++++++++----
include/linux/pci.h | 20 +++++++
include/linux/tracepoint.h | 19 +++++--
init/main.c | 32 +++++------
kernel/jump_label.c | 38 ++++++-------
kernel/module.c | 33 +++++++++--
kernel/printk/printk.c | 4 +-
kernel/tracepoint.c | 50 +++++++++--------
security/security.c | 4 +-
tools/objtool/special.c | 4 +-
32 files changed, 560 insertions(+), 152 deletions(-)
delete mode 100644 arch/x86/include/asm/export.h
--
2.11.0
^ permalink raw reply
* [PATCH v7 01/10] arch: enable relative relocations for arm64, power and x86
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
Before updating certain subsystems to use place relative 32-bit
relocations in special sections, to save space and reduce the
number of absolute relocations that need to be processed at runtime
by relocatable kernels, introduce the Kconfig symbol and define it
for some architectures that should be able to support and benefit
from it.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: x86 at kernel.org
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/Kconfig | 10 ++++++++++
arch/arm64/Kconfig | 1 +
arch/powerpc/Kconfig | 1 +
arch/x86/Kconfig | 1 +
4 files changed, 13 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index 400b9e1b2f27..dbc036a7bd1b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -959,4 +959,14 @@ config REFCOUNT_FULL
against various use-after-free conditions that can be used in
security flaw exploits.
+config HAVE_ARCH_PREL32_RELOCATIONS
+ bool
+ help
+ May be selected by an architecture if it supports place-relative
+ 32-bit relocations, both in the toolchain and in the module loader,
+ in which case relative references can be used in special sections
+ for PCI fixup, initcalls etc which are only half the size on 64 bit
+ architectures, and don't require runtime relocation on relocatable
+ kernels.
+
source "kernel/gcov/Kconfig"
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index c9a7e9e1414f..66c7b9ab2a3d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -89,6 +89,7 @@ config ARM64
select HAVE_ARCH_KGDB
select HAVE_ARCH_MMAP_RND_BITS
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
+ select HAVE_ARCH_PREL32_RELOCATIONS
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c51e6ce42e7a..e172478e2ae7 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -177,6 +177,7 @@ config PPC
select HAVE_ARCH_KGDB
select HAVE_ARCH_MMAP_RND_BITS
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
+ select HAVE_ARCH_PREL32_RELOCATIONS
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select ARCH_HAS_STRICT_KERNEL_RWX if ((PPC_BOOK3S_64 || PPC32) && !RELOCATABLE && !HIBERNATION)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index d4fc98c50378..9f2bb853aedb 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -115,6 +115,7 @@ config X86
select HAVE_ARCH_MMAP_RND_BITS if MMU
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
+ select HAVE_ARCH_PREL32_RELOCATIONS
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE
--
2.11.0
^ permalink raw reply related
* [PATCH v7 02/10] module: allow symbol exports to be disabled
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
To allow existing C code to be incorporated into the decompressor or
the UEFI stub, introduce a CPP macro that turns all EXPORT_SYMBOL_xxx
declarations into nops, and #define it in places where such exports
are undesirable. Note that this gets rid of a rather dodgy redefine
of linux/export.h's header guard.
Cc: matt at codeblueprint.co.uk
Cc: keescook at chromium.org
Cc: jeyu at kernel.org
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/x86/boot/compressed/kaslr.c | 5 +----
drivers/firmware/efi/libstub/Makefile | 3 ++-
include/linux/export.h | 9 +++++++++
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 8199a6187251..3a2a6d7049e4 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -23,11 +23,8 @@
* _ctype[] in lib/ctype.c is needed by isspace() of linux/ctype.h.
* While both lib/ctype.c and lib/cmdline.c will bring EXPORT_SYMBOL
* which is meaningless and will cause compiling error in some cases.
- * So do not include linux/export.h and define EXPORT_SYMBOL(sym)
- * as empty.
*/
-#define _LINUX_EXPORT_H
-#define EXPORT_SYMBOL(sym)
+#define __DISABLE_EXPORTS
#include "misc.h"
#include "error.h"
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index adaa4a964f0c..312bd0b64a61 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
- $(call cc-option,-fno-stack-protector)
+ $(call cc-option,-fno-stack-protector) \
+ -D__DISABLE_EXPORTS
GCOV_PROFILE := n
KASAN_SANITIZE := n
diff --git a/include/linux/export.h b/include/linux/export.h
index 1a1dfdb2a5c6..6dba2fb08f77 100644
--- a/include/linux/export.h
+++ b/include/linux/export.h
@@ -83,6 +83,15 @@ extern struct module __this_module;
*/
#define __EXPORT_SYMBOL(sym, sec) === __KSYM_##sym ===
+#elif defined(__DISABLE_EXPORTS)
+
+/*
+ * Allow symbol exports to be disabled completely so that C code may
+ * be reused in other execution contexts such as the UEFI stub or the
+ * decompressor.
+ */
+#define __EXPORT_SYMBOL(sym, sec)
+
#elif defined(CONFIG_TRIM_UNUSED_KSYMS)
#include <generated/autoksyms.h>
--
2.11.0
^ permalink raw reply related
* [PATCH v7 03/10] module: use relative references for __ksymtab entries
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
An ordinary arm64 defconfig build has ~64 KB worth of __ksymtab
entries, each consisting of two 64-bit fields containing absolute
references, to the symbol itself and to a char array containing
its name, respectively.
When we build the same configuration with KASLR enabled, we end
up with an additional ~192 KB of relocations in the .init section,
i.e., one 24 byte entry for each absolute reference, which all need
to be processed at boot time.
Given how the struct kernel_symbol that describes each entry is
completely local to module.c (except for the references emitted
by EXPORT_SYMBOL() itself), we can easily modify it to contain
two 32-bit relative references instead. This reduces the size of
the __ksymtab section by 50% for all 64-bit architectures, and
gets rid of the runtime relocations entirely for architectures
implementing KASLR, either via standard PIE linking (arm64) or
using custom host tools (x86).
Note that the binary search involving __ksymtab contents relies
on each section being sorted by symbol name. This is implemented
based on the input section names, not the names in the ksymtab
entries, so this patch does not interfere with that.
Given that the use of place-relative relocations requires support
both in the toolchain and in the module loader, we cannot enable
this feature for all architectures. So make it dependent on whether
CONFIG_HAVE_ARCH_PREL32_RELOCATIONS is defined.
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Thomas Garnier <thgarnie@google.com>
Cc: Nicolas Pitre <nico@linaro.org>
Acked-by: Jessica Yu <jeyu@kernel.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/x86/include/asm/Kbuild | 1 +
arch/x86/include/asm/export.h | 5 ---
include/asm-generic/export.h | 12 ++++-
include/linux/compiler.h | 10 +++++
include/linux/export.h | 46 +++++++++++++++-----
kernel/module.c | 33 +++++++++++---
6 files changed, 83 insertions(+), 24 deletions(-)
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 5d6a53fd7521..3e8a88dcaa1d 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -9,5 +9,6 @@ generated-y += xen-hypercalls.h
generic-y += clkdev.h
generic-y += dma-contiguous.h
generic-y += early_ioremap.h
+generic-y += export.h
generic-y += mcs_spinlock.h
generic-y += mm-arch-hooks.h
diff --git a/arch/x86/include/asm/export.h b/arch/x86/include/asm/export.h
deleted file mode 100644
index 2a51d66689c5..000000000000
--- a/arch/x86/include/asm/export.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifdef CONFIG_64BIT
-#define KSYM_ALIGN 16
-#endif
-#include <asm-generic/export.h>
diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h
index 719db1968d81..97ce606459ae 100644
--- a/include/asm-generic/export.h
+++ b/include/asm-generic/export.h
@@ -5,12 +5,10 @@
#define KSYM_FUNC(x) x
#endif
#ifdef CONFIG_64BIT
-#define __put .quad
#ifndef KSYM_ALIGN
#define KSYM_ALIGN 8
#endif
#else
-#define __put .long
#ifndef KSYM_ALIGN
#define KSYM_ALIGN 4
#endif
@@ -25,6 +23,16 @@
#define KSYM(name) name
#endif
+.macro __put, val, name
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ .long \val - ., \name - .
+#elif defined(CONFIG_64BIT)
+ .quad \val, \name
+#else
+ .long \val, \name
+#endif
+.endm
+
/*
* note on .section use: @progbits vs %progbits nastiness doesn't matter,
* since we immediately emit into those sections anyway.
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 52e611ab9a6c..79db4aa87d75 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -327,4 +327,14 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
compiletime_assert(__native_word(t), \
"Need native word sized stores/loads for atomicity.")
+/*
+ * Force the compiler to emit 'sym' as a symbol, so that we can reference
+ * it from inline assembler. Necessary in case 'sym' could be inlined
+ * otherwise, or eliminated entirely due to lack of references that are
+ * visible to the compiler.
+ */
+#define __ADDRESSABLE(sym) \
+ static void * const __attribute__((section(".discard"), used)) \
+ __PASTE(__addressable_##sym, __LINE__) = (void *)&sym;
+
#endif /* __LINUX_COMPILER_H */
diff --git a/include/linux/export.h b/include/linux/export.h
index 6dba2fb08f77..4744cf4736b0 100644
--- a/include/linux/export.h
+++ b/include/linux/export.h
@@ -24,12 +24,6 @@
#define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x)
#ifndef __ASSEMBLY__
-struct kernel_symbol
-{
- unsigned long value;
- const char *name;
-};
-
#ifdef MODULE
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
@@ -60,17 +54,47 @@ extern struct module __this_module;
#define __CRC_SYMBOL(sym, sec)
#endif
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+#include <linux/compiler.h>
+/*
+ * Emit the ksymtab entry as a pair of relative references: this reduces
+ * the size by half on 64-bit architectures, and eliminates the need for
+ * absolute relocations that require runtime processing on relocatable
+ * kernels.
+ */
+#define __KSYMTAB_ENTRY(sym, sec) \
+ __ADDRESSABLE(sym) \
+ asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
+ " .balign 8 \n" \
+ VMLINUX_SYMBOL_STR(__ksymtab_##sym) ": \n" \
+ " .long " VMLINUX_SYMBOL_STR(sym) "- . \n" \
+ " .long " VMLINUX_SYMBOL_STR(__kstrtab_##sym) "- .\n" \
+ " .previous \n")
+
+struct kernel_symbol {
+ int value_offset;
+ int name_offset;
+};
+#else
+#define __KSYMTAB_ENTRY(sym, sec) \
+ static const struct kernel_symbol __ksymtab_##sym \
+ __attribute__((section("___ksymtab" sec "+" #sym), used)) \
+ = { (unsigned long)&sym, __kstrtab_##sym }
+
+struct kernel_symbol {
+ unsigned long value;
+ const char *name;
+};
+#endif
+
/* For every exported symbol, place a struct in the __ksymtab section */
#define ___EXPORT_SYMBOL(sym, sec) \
extern typeof(sym) sym; \
__CRC_SYMBOL(sym, sec) \
static const char __kstrtab_##sym[] \
- __attribute__((section("__ksymtab_strings"), aligned(1))) \
+ __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
= VMLINUX_SYMBOL_STR(sym); \
- static const struct kernel_symbol __ksymtab_##sym \
- __used \
- __attribute__((section("___ksymtab" sec "+" #sym), used)) \
- = { (unsigned long)&sym, __kstrtab_##sym }
+ __KSYMTAB_ENTRY(sym, sec)
#if defined(__KSYM_DEPS__)
diff --git a/kernel/module.c b/kernel/module.c
index dea01ac9cb74..d3a908ffc42c 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -549,12 +549,31 @@ static bool check_symbol(const struct symsearch *syms,
return true;
}
+static unsigned long kernel_symbol_value(const struct kernel_symbol *sym)
+{
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ return (unsigned long)&sym->value_offset + sym->value_offset;
+#else
+ return sym->value;
+#endif
+}
+
+static const char *kernel_symbol_name(const struct kernel_symbol *sym)
+{
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ return (const char *)((unsigned long)&sym->name_offset +
+ sym->name_offset);
+#else
+ return sym->name;
+#endif
+}
+
static int cmp_name(const void *va, const void *vb)
{
const char *a;
const struct kernel_symbol *b;
a = va; b = vb;
- return strcmp(a, b->name);
+ return strcmp(a, kernel_symbol_name(b));
}
static bool find_symbol_in_section(const struct symsearch *syms,
@@ -2198,7 +2217,7 @@ void *__symbol_get(const char *symbol)
sym = NULL;
preempt_enable();
- return sym ? (void *)sym->value : NULL;
+ return sym ? (void *)kernel_symbol_value(sym) : NULL;
}
EXPORT_SYMBOL_GPL(__symbol_get);
@@ -2228,10 +2247,12 @@ static int verify_export_symbols(struct module *mod)
for (i = 0; i < ARRAY_SIZE(arr); i++) {
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
- if (find_symbol(s->name, &owner, NULL, true, false)) {
+ if (find_symbol(kernel_symbol_name(s), &owner, NULL,
+ true, false)) {
pr_err("%s: exports duplicate symbol %s"
" (owned by %s)\n",
- mod->name, s->name, module_name(owner));
+ mod->name, kernel_symbol_name(s),
+ module_name(owner));
return -ENOEXEC;
}
}
@@ -2280,7 +2301,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
ksym = resolve_symbol_wait(mod, info, name);
/* Ok if resolved. */
if (ksym && !IS_ERR(ksym)) {
- sym[i].st_value = ksym->value;
+ sym[i].st_value = kernel_symbol_value(ksym);
break;
}
@@ -2540,7 +2561,7 @@ static int is_exported(const char *name, unsigned long value,
ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
else
ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
- return ks != NULL && ks->value == value;
+ return ks != NULL && kernel_symbol_value(ks) == value;
}
/* As per nm */
--
2.11.0
^ permalink raw reply related
* [PATCH v7 04/10] init: allow initcall tables to be emitted using relative references
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
Allow the initcall tables to be emitted using relative references that
are only half the size on 64-bit architectures and don't require fixups
at runtime on relocatable kernels.
Cc: Petr Mladek <pmladek@suse.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: James Morris <james.l.morris@oracle.com>
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
include/linux/init.h | 44 +++++++++++++++-----
init/main.c | 32 +++++++-------
kernel/printk/printk.c | 4 +-
security/security.c | 4 +-
4 files changed, 53 insertions(+), 31 deletions(-)
diff --git a/include/linux/init.h b/include/linux/init.h
index ea1b31101d9e..cef8e817e5a5 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -109,8 +109,24 @@
typedef int (*initcall_t)(void);
typedef void (*exitcall_t)(void);
-extern initcall_t __con_initcall_start[], __con_initcall_end[];
-extern initcall_t __security_initcall_start[], __security_initcall_end[];
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+typedef int initcall_entry_t;
+
+static inline initcall_t initcall_from_entry(initcall_entry_t *entry)
+{
+ return (initcall_t)((unsigned long)entry + *entry);
+}
+#else
+typedef initcall_t initcall_entry_t;
+
+static inline initcall_t initcall_from_entry(initcall_entry_t *entry)
+{
+ return *entry;
+}
+#endif
+
+extern initcall_entry_t __con_initcall_start[], __con_initcall_end[];
+extern initcall_entry_t __security_initcall_start[], __security_initcall_end[];
/* Used for contructor calls. */
typedef void (*ctor_fn_t)(void);
@@ -160,9 +176,20 @@ extern bool initcall_debug;
* as KEEP() in the linker script.
*/
-#define __define_initcall(fn, id) \
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+#define ___define_initcall(fn, id, __sec) \
+ __ADDRESSABLE(fn) \
+ asm(".section \"" #__sec ".init\", \"a\" \n" \
+ "__initcall_" #fn #id ": \n" \
+ ".long " VMLINUX_SYMBOL_STR(fn) " - . \n" \
+ ".previous \n");
+#else
+#define ___define_initcall(fn, id, __sec) \
static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" #id ".init"))) = fn;
+ __attribute__((__section__(#__sec ".init"))) = fn;
+#endif
+
+#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
/*
* Early initcalls run before initializing SMP.
@@ -201,13 +228,8 @@ extern bool initcall_debug;
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
-#define console_initcall(fn) \
- static initcall_t __initcall_##fn \
- __used __section(.con_initcall.init) = fn
-
-#define security_initcall(fn) \
- static initcall_t __initcall_##fn \
- __used __section(.security_initcall.init) = fn
+#define console_initcall(fn) ___define_initcall(fn,, .con_initcall)
+#define security_initcall(fn) ___define_initcall(fn,, .security_initcall)
struct obs_kernel_param {
const char *str;
diff --git a/init/main.c b/init/main.c
index a8100b954839..d81487cc126d 100644
--- a/init/main.c
+++ b/init/main.c
@@ -848,18 +848,18 @@ int __init_or_module do_one_initcall(initcall_t fn)
}
-extern initcall_t __initcall_start[];
-extern initcall_t __initcall0_start[];
-extern initcall_t __initcall1_start[];
-extern initcall_t __initcall2_start[];
-extern initcall_t __initcall3_start[];
-extern initcall_t __initcall4_start[];
-extern initcall_t __initcall5_start[];
-extern initcall_t __initcall6_start[];
-extern initcall_t __initcall7_start[];
-extern initcall_t __initcall_end[];
-
-static initcall_t *initcall_levels[] __initdata = {
+extern initcall_entry_t __initcall_start[];
+extern initcall_entry_t __initcall0_start[];
+extern initcall_entry_t __initcall1_start[];
+extern initcall_entry_t __initcall2_start[];
+extern initcall_entry_t __initcall3_start[];
+extern initcall_entry_t __initcall4_start[];
+extern initcall_entry_t __initcall5_start[];
+extern initcall_entry_t __initcall6_start[];
+extern initcall_entry_t __initcall7_start[];
+extern initcall_entry_t __initcall_end[];
+
+static initcall_entry_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
@@ -885,7 +885,7 @@ static char *initcall_level_names[] __initdata = {
static void __init do_initcall_level(int level)
{
- initcall_t *fn;
+ initcall_entry_t *fn;
strcpy(initcall_command_line, saved_command_line);
parse_args(initcall_level_names[level],
@@ -895,7 +895,7 @@ static void __init do_initcall_level(int level)
NULL, &repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
- do_one_initcall(*fn);
+ do_one_initcall(initcall_from_entry(fn));
}
static void __init do_initcalls(void)
@@ -926,10 +926,10 @@ static void __init do_basic_setup(void)
static void __init do_pre_smp_initcalls(void)
{
- initcall_t *fn;
+ initcall_entry_t *fn;
for (fn = __initcall_start; fn < __initcall0_start; fn++)
- do_one_initcall(*fn);
+ do_one_initcall(initcall_from_entry(fn));
}
/*
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index b9006617710f..0516005261c7 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2611,7 +2611,7 @@ EXPORT_SYMBOL(unregister_console);
*/
void __init console_init(void)
{
- initcall_t *call;
+ initcall_entry_t *call;
/* Setup the default TTY line discipline. */
n_tty_init();
@@ -2622,7 +2622,7 @@ void __init console_init(void)
*/
call = __con_initcall_start;
while (call < __con_initcall_end) {
- (*call)();
+ initcall_from_entry(call)();
call++;
}
}
diff --git a/security/security.c b/security/security.c
index 1cd8526cb0b7..f648eeff06de 100644
--- a/security/security.c
+++ b/security/security.c
@@ -45,10 +45,10 @@ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
static void __init do_security_initcalls(void)
{
- initcall_t *call;
+ initcall_entry_t *call;
call = __security_initcall_start;
while (call < __security_initcall_end) {
- (*call) ();
+ initcall_from_entry(call)();
call++;
}
}
--
2.11.0
^ permalink raw reply related
* [PATCH v7 05/10] PCI: Add support for relative addressing in quirk tables
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
Allow the PCI quirk tables to be emitted in a way that avoids absolute
references to the hook functions. This reduces the size of the entries,
and, more importantly, makes them invariant under runtime relocation
(e.g., for KASLR)
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
drivers/pci/quirks.c | 13 ++++++++++---
include/linux/pci.h | 20 ++++++++++++++++++++
2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 10684b17d0bd..b6d51b4d5ce1 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3556,9 +3556,16 @@ static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
f->vendor == (u16) PCI_ANY_ID) &&
(f->device == dev->device ||
f->device == (u16) PCI_ANY_ID)) {
- calltime = fixup_debug_start(dev, f->hook);
- f->hook(dev);
- fixup_debug_report(dev, calltime, f->hook);
+ void (*hook)(struct pci_dev *dev);
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ hook = (void *)((unsigned long)&f->hook_offset +
+ f->hook_offset);
+#else
+ hook = f->hook;
+#endif
+ calltime = fixup_debug_start(dev, hook);
+ hook(dev);
+ fixup_debug_report(dev, calltime, hook);
}
}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c170c9250c8b..086c3965710b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1792,7 +1792,11 @@ struct pci_fixup {
u16 device; /* You can use PCI_ANY_ID here of course */
u32 class; /* You can use PCI_ANY_ID here too */
unsigned int class_shift; /* should be 0, 8, 16 */
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ int hook_offset;
+#else
void (*hook)(struct pci_dev *dev);
+#endif
};
enum pci_fixup_pass {
@@ -1806,12 +1810,28 @@ enum pci_fixup_pass {
pci_fixup_suspend_late, /* pci_device_suspend_late() */
};
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+#define __DECLARE_PCI_FIXUP_SECTION(sec, name, vendor, device, class, \
+ class_shift, hook) \
+ __ADDRESSABLE(hook) \
+ asm(".section " #sec ", \"a\" \n" \
+ ".balign 16 \n" \
+ ".short " #vendor ", " #device " \n" \
+ ".long " #class ", " #class_shift " \n" \
+ ".long " VMLINUX_SYMBOL_STR(hook) " - . \n" \
+ ".previous \n");
+#define DECLARE_PCI_FIXUP_SECTION(sec, name, vendor, device, class, \
+ class_shift, hook) \
+ __DECLARE_PCI_FIXUP_SECTION(sec, name, vendor, device, class, \
+ class_shift, hook)
+#else
/* Anonymous variables would be nice... */
#define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, class, \
class_shift, hook) \
static const struct pci_fixup __PASTE(__pci_fixup_##name,__LINE__) __used \
__attribute__((__section__(#section), aligned((sizeof(void *))))) \
= { vendor, device, class, class_shift, hook };
+#endif
#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class, \
class_shift, hook) \
--
2.11.0
^ permalink raw reply related
* [PATCH v7 06/10] kernel: tracepoints: add support for relative references
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
To avoid the need for relocating absolute references to tracepoint
structures at boot time when running relocatable kernels (which may
take a disproportionate amount of space), add the option to emit
these tables as relative references instead.
Cc: Ingo Molnar <mingo@redhat.com>
Acked-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
include/linux/tracepoint.h | 19 ++++++--
kernel/tracepoint.c | 50 +++++++++++---------
2 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index a26ffbe09e71..d02bf1a695e8 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -228,6 +228,19 @@ extern void syscall_unregfunc(void);
return static_key_false(&__tracepoint_##name.key); \
}
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+#define __TRACEPOINT_ENTRY(name) \
+ asm(" .section \"__tracepoints_ptrs\", \"a\" \n" \
+ " .balign 4 \n" \
+ " .long " VMLINUX_SYMBOL_STR(__tracepoint_##name) " - .\n" \
+ " .previous \n")
+#else
+#define __TRACEPOINT_ENTRY(name) \
+ static struct tracepoint * const __tracepoint_ptr_##name __used \
+ __attribute__((section("__tracepoints_ptrs"))) = \
+ &__tracepoint_##name
+#endif
+
/*
* We have no guarantee that gcc and the linker won't up-align the tracepoint
* structures, so we create an array of pointers that will be used for iteration
@@ -237,11 +250,9 @@ extern void syscall_unregfunc(void);
static const char __tpstrtab_##name[] \
__attribute__((section("__tracepoints_strings"))) = #name; \
struct tracepoint __tracepoint_##name \
- __attribute__((section("__tracepoints"))) = \
+ __attribute__((section("__tracepoints"), used)) = \
{ __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL };\
- static struct tracepoint * const __tracepoint_ptr_##name __used \
- __attribute__((section("__tracepoints_ptrs"))) = \
- &__tracepoint_##name;
+ __TRACEPOINT_ENTRY(name);
#define DEFINE_TRACE(name) \
DEFINE_TRACE_FN(name, NULL, NULL);
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index 685c50ae6300..05649fef106c 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -327,6 +327,28 @@ int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data)
}
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
+static void for_each_tracepoint_range(struct tracepoint * const *begin,
+ struct tracepoint * const *end,
+ void (*fct)(struct tracepoint *tp, void *priv),
+ void *priv)
+{
+ if (!begin)
+ return;
+
+ if (IS_ENABLED(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)) {
+ const int *iter;
+
+ for (iter = (const int *)begin; iter < (const int *)end; iter++)
+ fct((struct tracepoint *)((unsigned long)iter + *iter),
+ priv);
+ } else {
+ struct tracepoint * const *iter;
+
+ for (iter = begin; iter < end; iter++)
+ fct(*iter, priv);
+ }
+}
+
#ifdef CONFIG_MODULES
bool trace_module_has_bad_taint(struct module *mod)
{
@@ -391,15 +413,9 @@ EXPORT_SYMBOL_GPL(unregister_tracepoint_module_notifier);
* Ensure the tracer unregistered the module's probes before the module
* teardown is performed. Prevents leaks of probe and data pointers.
*/
-static void tp_module_going_check_quiescent(struct tracepoint * const *begin,
- struct tracepoint * const *end)
+static void tp_module_going_check_quiescent(struct tracepoint *tp, void *priv)
{
- struct tracepoint * const *iter;
-
- if (!begin)
- return;
- for (iter = begin; iter < end; iter++)
- WARN_ON_ONCE((*iter)->funcs);
+ WARN_ON_ONCE(tp->funcs);
}
static int tracepoint_module_coming(struct module *mod)
@@ -450,8 +466,9 @@ static void tracepoint_module_going(struct module *mod)
* Called the going notifier before checking for
* quiescence.
*/
- tp_module_going_check_quiescent(mod->tracepoints_ptrs,
- mod->tracepoints_ptrs + mod->num_tracepoints);
+ for_each_tracepoint_range(mod->tracepoints_ptrs,
+ mod->tracepoints_ptrs + mod->num_tracepoints,
+ tp_module_going_check_quiescent, NULL);
break;
}
}
@@ -503,19 +520,6 @@ static __init int init_tracepoints(void)
__initcall(init_tracepoints);
#endif /* CONFIG_MODULES */
-static void for_each_tracepoint_range(struct tracepoint * const *begin,
- struct tracepoint * const *end,
- void (*fct)(struct tracepoint *tp, void *priv),
- void *priv)
-{
- struct tracepoint * const *iter;
-
- if (!begin)
- return;
- for (iter = begin; iter < end; iter++)
- fct(*iter, priv);
-}
-
/**
* for_each_kernel_tracepoint - iteration on all kernel tracepoints
* @fct: callback
--
2.11.0
^ permalink raw reply related
* [PATCH v7 07/10] kernel/jump_label: abstract jump_entry member accessors
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
In preparation of allowing architectures to use relative references
in jump_label entries [which can dramatically reduce the memory
footprint], introduce abstractions for references to the 'code' and
'key' members of struct jump_entry.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/arm/include/asm/jump_label.h | 27 ++++++++++++++
arch/arm64/include/asm/jump_label.h | 27 ++++++++++++++
arch/mips/include/asm/jump_label.h | 27 ++++++++++++++
arch/powerpc/include/asm/jump_label.h | 27 ++++++++++++++
arch/s390/include/asm/jump_label.h | 27 ++++++++++++++
arch/sparc/include/asm/jump_label.h | 27 ++++++++++++++
arch/tile/include/asm/jump_label.h | 27 ++++++++++++++
arch/x86/include/asm/jump_label.h | 27 ++++++++++++++
kernel/jump_label.c | 38 +++++++++-----------
9 files changed, 232 insertions(+), 22 deletions(-)
diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h
index e12d7d096fc0..7b05b404063a 100644
--- a/arch/arm/include/asm/jump_label.h
+++ b/arch/arm/include/asm/jump_label.h
@@ -45,5 +45,32 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h
index 1b5e0e843c3a..9d6e46355c89 100644
--- a/arch/arm64/include/asm/jump_label.h
+++ b/arch/arm64/include/asm/jump_label.h
@@ -62,5 +62,32 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* __ASSEMBLY__ */
#endif /* __ASM_JUMP_LABEL_H */
diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h
index e77672539e8e..70df9293dc49 100644
--- a/arch/mips/include/asm/jump_label.h
+++ b/arch/mips/include/asm/jump_label.h
@@ -66,5 +66,32 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_MIPS_JUMP_LABEL_H */
diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h
index 9a287e0ac8b1..412b2699c9f6 100644
--- a/arch/powerpc/include/asm/jump_label.h
+++ b/arch/powerpc/include/asm/jump_label.h
@@ -59,6 +59,33 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#else
#define ARCH_STATIC_BRANCH(LABEL, KEY) \
1098: nop; \
diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h
index 40f651292aa7..1ecfd46835d9 100644
--- a/arch/s390/include/asm/jump_label.h
+++ b/arch/s390/include/asm/jump_label.h
@@ -50,5 +50,32 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h
index 94eb529dcb77..18e893687f7c 100644
--- a/arch/sparc/include/asm/jump_label.h
+++ b/arch/sparc/include/asm/jump_label.h
@@ -48,5 +48,32 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/tile/include/asm/jump_label.h b/arch/tile/include/asm/jump_label.h
index cde7573f397b..86acaa6ff33d 100644
--- a/arch/tile/include/asm/jump_label.h
+++ b/arch/tile/include/asm/jump_label.h
@@ -55,4 +55,31 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#endif /* _ASM_TILE_JUMP_LABEL_H */
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 8c0de4282659..009ff2699d07 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -74,6 +74,33 @@ struct jump_entry {
jump_label_t key;
};
+static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
+{
+ return entry->code;
+}
+
+static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
+{
+ return (struct static_key *)((unsigned long)entry->key & ~1UL);
+}
+
+static inline bool jump_entry_is_branch(const struct jump_entry *entry)
+{
+ return (unsigned long)entry->key & 1UL;
+}
+
+static inline bool jump_entry_is_module_init(const struct jump_entry *entry)
+{
+ return entry->code == 0;
+}
+
+static inline void jump_entry_set_module_init(struct jump_entry *entry)
+{
+ entry->code = 0;
+}
+
+#define jump_label_swap NULL
+
#else /* __ASSEMBLY__ */
.macro STATIC_JUMP_IF_TRUE target, key, def
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 8594d24e4adc..4f44db58d981 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -37,10 +37,12 @@ static int jump_label_cmp(const void *a, const void *b)
const struct jump_entry *jea = a;
const struct jump_entry *jeb = b;
- if (jea->key < jeb->key)
+ if ((unsigned long)jump_entry_key(jea) <
+ (unsigned long)jump_entry_key(jeb))
return -1;
- if (jea->key > jeb->key)
+ if ((unsigned long)jump_entry_key(jea) >
+ (unsigned long)jump_entry_key(jeb))
return 1;
return 0;
@@ -53,7 +55,8 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
size = (((unsigned long)stop - (unsigned long)start)
/ sizeof(struct jump_entry));
- sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL);
+ sort(start, size, sizeof(struct jump_entry), jump_label_cmp,
+ jump_label_swap);
}
static void jump_label_update(struct static_key *key);
@@ -254,8 +257,8 @@ EXPORT_SYMBOL_GPL(jump_label_rate_limit);
static int addr_conflict(struct jump_entry *entry, void *start, void *end)
{
- if (entry->code <= (unsigned long)end &&
- entry->code + JUMP_LABEL_NOP_SIZE > (unsigned long)start)
+ if (jump_entry_code(entry) <= (unsigned long)end &&
+ jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE > (unsigned long)start)
return 1;
return 0;
@@ -314,16 +317,6 @@ static inline void static_key_set_linked(struct static_key *key)
key->type |= JUMP_TYPE_LINKED;
}
-static inline struct static_key *jump_entry_key(struct jump_entry *entry)
-{
- return (struct static_key *)((unsigned long)entry->key & ~1UL);
-}
-
-static bool jump_entry_branch(struct jump_entry *entry)
-{
- return (unsigned long)entry->key & 1UL;
-}
-
/***
* A 'struct static_key' uses a union such that it either points directly
* to a table of 'struct jump_entry' or to a linked list of modules which in
@@ -348,7 +341,7 @@ static enum jump_label_type jump_label_type(struct jump_entry *entry)
{
struct static_key *key = jump_entry_key(entry);
bool enabled = static_key_enabled(key);
- bool branch = jump_entry_branch(entry);
+ bool branch = jump_entry_is_branch(entry);
/* See the comment in linux/jump_label.h */
return enabled ^ branch;
@@ -364,7 +357,8 @@ static void __jump_label_update(struct static_key *key,
* kernel_text_address() verifies we are not in core kernel
* init code, see jump_label_invalidate_module_init().
*/
- if (entry->code && kernel_text_address(entry->code))
+ if (!jump_entry_is_module_init(entry) &&
+ kernel_text_address(jump_entry_code(entry)))
arch_jump_label_transform(entry, jump_label_type(entry));
}
}
@@ -417,7 +411,7 @@ static enum jump_label_type jump_label_init_type(struct jump_entry *entry)
{
struct static_key *key = jump_entry_key(entry);
bool type = static_key_type(key);
- bool branch = jump_entry_branch(entry);
+ bool branch = jump_entry_is_branch(entry);
/* See the comment in linux/jump_label.h */
return type ^ branch;
@@ -541,7 +535,7 @@ static int jump_label_add_module(struct module *mod)
continue;
key = iterk;
- if (within_module(iter->key, mod)) {
+ if (within_module((unsigned long)key, mod)) {
static_key_set_entries(key, iter);
continue;
}
@@ -591,7 +585,7 @@ static void jump_label_del_module(struct module *mod)
key = jump_entry_key(iter);
- if (within_module(iter->key, mod))
+ if (within_module((unsigned long)key, mod))
continue;
/* No memory during module load */
@@ -634,8 +628,8 @@ static void jump_label_invalidate_module_init(struct module *mod)
struct jump_entry *iter;
for (iter = iter_start; iter < iter_stop; iter++) {
- if (within_module_init(iter->code, mod))
- iter->code = 0;
+ if (within_module_init(jump_entry_code(iter), mod))
+ jump_entry_set_module_init(iter);
}
}
--
2.11.0
^ permalink raw reply related
* [PATCH v7 08/10] arm64/kernel: jump_label: use relative references
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
On a randomly chosen distro kernel build for arm64, vmlinux.o shows the
following sections, containing jump label entries, and the associated
RELA relocation records, respectively:
...
[38088] __jump_table PROGBITS 0000000000000000 00e19f30
000000000002ea10 0000000000000000 WA 0 0 8
[38089] .rela__jump_table RELA 0000000000000000 01fd8bb0
000000000008be30 0000000000000018 I 38178 38088 8
...
In other words, we have 190 KB worth of 'struct jump_entry' instances,
and 573 KB worth of RELA entries to relocate each entry's code, target
and key members. This means the RELA section occupies 10% of the .init
segment, and the two sections combined represent 5% of vmlinux's entire
memory footprint.
So let's switch from 64-bit absolute references to 32-bit relative
references: this reduces the size of the __jump_table by 50%, and gets
rid of the RELA section entirely.
Note that this requires some extra care in the sorting routine, given
that the offsets change when entries are moved around in the jump_entry
table.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/arm64/include/asm/jump_label.h | 27 ++++++++++++--------
arch/arm64/kernel/jump_label.c | 22 +++++++++++++---
2 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h
index 9d6e46355c89..8f82adeb7b0b 100644
--- a/arch/arm64/include/asm/jump_label.h
+++ b/arch/arm64/include/asm/jump_label.h
@@ -30,8 +30,8 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran
{
asm goto("1: nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
- ".align 3\n\t"
- ".quad 1b, %l[l_yes], %c0\n\t"
+ ".align 2\n\t"
+ ".long 1b - ., %l[l_yes] - ., %c0 - .\n\t"
".popsection\n\t"
: : "i"(&((char *)key)[branch]) : : l_yes);
@@ -44,8 +44,8 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool
{
asm goto("1: b %l[l_yes]\n\t"
".pushsection __jump_table, \"aw\"\n\t"
- ".align 3\n\t"
- ".quad 1b, %l[l_yes], %c0\n\t"
+ ".align 2\n\t"
+ ".long 1b - ., %l[l_yes] - ., %c0 - .\n\t"
".popsection\n\t"
: : "i"(&((char *)key)[branch]) : : l_yes);
@@ -57,19 +57,26 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool
typedef u64 jump_label_t;
struct jump_entry {
- jump_label_t code;
- jump_label_t target;
- jump_label_t key;
+ s32 code;
+ s32 target;
+ s32 key;
};
static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
{
- return entry->code;
+ return (unsigned long)&entry->code + entry->code;
+}
+
+static inline jump_label_t jump_entry_target(const struct jump_entry *entry)
+{
+ return (unsigned long)&entry->target + entry->target;
}
static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
{
- return (struct static_key *)((unsigned long)entry->key & ~1UL);
+ unsigned long key = (unsigned long)&entry->key + entry->key;
+
+ return (struct static_key *)(key & ~1UL);
}
static inline bool jump_entry_is_branch(const struct jump_entry *entry)
@@ -87,7 +94,7 @@ static inline void jump_entry_set_module_init(struct jump_entry *entry)
entry->code = 0;
}
-#define jump_label_swap NULL
+void jump_label_swap(void *a, void *b, int size);
#endif /* __ASSEMBLY__ */
#endif /* __ASM_JUMP_LABEL_H */
diff --git a/arch/arm64/kernel/jump_label.c b/arch/arm64/kernel/jump_label.c
index c2dd1ad3e648..2b8e459e91f7 100644
--- a/arch/arm64/kernel/jump_label.c
+++ b/arch/arm64/kernel/jump_label.c
@@ -25,12 +25,12 @@
void arch_jump_label_transform(struct jump_entry *entry,
enum jump_label_type type)
{
- void *addr = (void *)entry->code;
+ void *addr = (void *)jump_entry_code(entry);
u32 insn;
if (type == JUMP_LABEL_JMP) {
- insn = aarch64_insn_gen_branch_imm(entry->code,
- entry->target,
+ insn = aarch64_insn_gen_branch_imm(jump_entry_code(entry),
+ jump_entry_target(entry),
AARCH64_INSN_BRANCH_NOLINK);
} else {
insn = aarch64_insn_gen_nop();
@@ -50,4 +50,20 @@ void arch_jump_label_transform_static(struct jump_entry *entry,
*/
}
+void jump_label_swap(void *a, void *b, int size)
+{
+ long delta = (unsigned long)a - (unsigned long)b;
+ struct jump_entry *jea = a;
+ struct jump_entry *jeb = b;
+ struct jump_entry tmp = *jea;
+
+ jea->code = jeb->code - delta;
+ jea->target = jeb->target - delta;
+ jea->key = jeb->key - delta;
+
+ jeb->code = tmp.code + delta;
+ jeb->target = tmp.target + delta;
+ jeb->key = tmp.key + delta;
+}
+
#endif /* HAVE_JUMP_LABEL */
--
2.11.0
^ permalink raw reply related
* [PATCH v7 09/10] x86: jump_label: switch to jump_entry accessors
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
In preparation of switching x86 to use place-relative references for
the code, target and key members of struct jump_entry, replace direct
references to the struct member with invocations of the new accessors.
This will allow us to make the switch by modifying the accessors only.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/x86/kernel/jump_label.c | 43 ++++++++++++--------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
index e56c95be2808..d64296092ef5 100644
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -52,22 +52,24 @@ static void __jump_label_transform(struct jump_entry *entry,
* Jump label is enabled for the first time.
* So we expect a default_nop...
*/
- if (unlikely(memcmp((void *)entry->code, default_nop, 5)
- != 0))
- bug_at((void *)entry->code, __LINE__);
+ if (unlikely(memcmp((void *)jump_entry_code(entry),
+ default_nop, 5) != 0))
+ bug_at((void *)jump_entry_code(entry),
+ __LINE__);
} else {
/*
* ...otherwise expect an ideal_nop. Otherwise
* something went horribly wrong.
*/
- if (unlikely(memcmp((void *)entry->code, ideal_nop, 5)
- != 0))
- bug_at((void *)entry->code, __LINE__);
+ if (unlikely(memcmp((void *)jump_entry_code(entry),
+ ideal_nop, 5) != 0))
+ bug_at((void *)jump_entry_code(entry),
+ __LINE__);
}
code.jump = 0xe9;
- code.offset = entry->target -
- (entry->code + JUMP_LABEL_NOP_SIZE);
+ code.offset = jump_entry_target(entry) -
+ (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
} else {
/*
* We are disabling this jump label. If it is not what
@@ -76,14 +78,18 @@ static void __jump_label_transform(struct jump_entry *entry,
* are converting the default nop to the ideal nop.
*/
if (init) {
- if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0))
- bug_at((void *)entry->code, __LINE__);
+ if (unlikely(memcmp((void *)jump_entry_code(entry),
+ default_nop, 5) != 0))
+ bug_at((void *)jump_entry_code(entry),
+ __LINE__);
} else {
code.jump = 0xe9;
- code.offset = entry->target -
- (entry->code + JUMP_LABEL_NOP_SIZE);
- if (unlikely(memcmp((void *)entry->code, &code, 5) != 0))
- bug_at((void *)entry->code, __LINE__);
+ code.offset = jump_entry_target(entry) -
+ (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
+ if (unlikely(memcmp((void *)jump_entry_code(entry),
+ &code, 5) != 0))
+ bug_at((void *)jump_entry_code(entry),
+ __LINE__);
}
memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
}
@@ -97,10 +103,13 @@ static void __jump_label_transform(struct jump_entry *entry,
*
*/
if (poker)
- (*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
+ (*poker)((void *)jump_entry_code(entry), &code,
+ JUMP_LABEL_NOP_SIZE);
else
- text_poke_bp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE,
- (void *)entry->code + JUMP_LABEL_NOP_SIZE);
+ text_poke_bp((void *)jump_entry_code(entry), &code,
+ JUMP_LABEL_NOP_SIZE,
+ (void *)jump_entry_code(entry) +
+ JUMP_LABEL_NOP_SIZE);
}
void arch_jump_label_transform(struct jump_entry *entry,
--
2.11.0
^ permalink raw reply related
* [PATCH v7 10/10] x86/kernel: jump_table: use relative references
From: Ard Biesheuvel @ 2018-01-02 20:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102200549.22984-1-ard.biesheuvel@linaro.org>
Similar to the arm64 case, 64-bit x86 can benefit from using 32-bit
relative references rather than 64-bit absolute ones when emitting
struct jump_entry instances. Not only does this reduce the memory
footprint of the entries themselves by 50%, it also removes the need
for carrying relocation metadata on relocatable builds (i.e., for KASLR)
which saves a fair chunk of .init space as well (although the savings
are not as dramatic as on arm64)
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
arch/x86/include/asm/jump_label.h | 35 ++++++++++++--------
arch/x86/kernel/jump_label.c | 16 +++++++++
tools/objtool/special.c | 4 +--
3 files changed, 39 insertions(+), 16 deletions(-)
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 009ff2699d07..35fc2c5ec846 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -36,8 +36,8 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran
asm_volatile_goto("1:"
".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t"
".pushsection __jump_table, \"aw\" \n\t"
- _ASM_ALIGN "\n\t"
- _ASM_PTR "1b, %l[l_yes], %c0 + %c1 \n\t"
+ ".balign 4\n\t"
+ ".long 1b - ., %l[l_yes] - ., %c0 + %c1 - .\n\t"
".popsection \n\t"
: : "i" (key), "i" (branch) : : l_yes);
@@ -52,8 +52,8 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool
".byte 0xe9\n\t .long %l[l_yes] - 2f\n\t"
"2:\n\t"
".pushsection __jump_table, \"aw\" \n\t"
- _ASM_ALIGN "\n\t"
- _ASM_PTR "1b, %l[l_yes], %c0 + %c1 \n\t"
+ ".balign 4\n\t"
+ ".long 1b - ., %l[l_yes] - ., %c0 + %c1 - .\n\t"
".popsection \n\t"
: : "i" (key), "i" (branch) : : l_yes);
@@ -69,19 +69,26 @@ typedef u32 jump_label_t;
#endif
struct jump_entry {
- jump_label_t code;
- jump_label_t target;
- jump_label_t key;
+ s32 code;
+ s32 target;
+ s32 key;
};
static inline jump_label_t jump_entry_code(const struct jump_entry *entry)
{
- return entry->code;
+ return (unsigned long)&entry->code + entry->code;
+}
+
+static inline jump_label_t jump_entry_target(const struct jump_entry *entry)
+{
+ return (unsigned long)&entry->target + entry->target;
}
static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
{
- return (struct static_key *)((unsigned long)entry->key & ~1UL);
+ unsigned long key = (unsigned long)&entry->key + entry->key;
+
+ return (struct static_key *)(key & ~1UL);
}
static inline bool jump_entry_is_branch(const struct jump_entry *entry)
@@ -99,7 +106,7 @@ static inline void jump_entry_set_module_init(struct jump_entry *entry)
entry->code = 0;
}
-#define jump_label_swap NULL
+void jump_label_swap(void *a, void *b, int size);
#else /* __ASSEMBLY__ */
@@ -114,8 +121,8 @@ static inline void jump_entry_set_module_init(struct jump_entry *entry)
.byte STATIC_KEY_INIT_NOP
.endif
.pushsection __jump_table, "aw"
- _ASM_ALIGN
- _ASM_PTR .Lstatic_jump_\@, \target, \key
+ .balign 4
+ .long .Lstatic_jump_\@ - ., \target - ., \key - .
.popsection
.endm
@@ -130,8 +137,8 @@ static inline void jump_entry_set_module_init(struct jump_entry *entry)
.Lstatic_jump_after_\@:
.endif
.pushsection __jump_table, "aw"
- _ASM_ALIGN
- _ASM_PTR .Lstatic_jump_\@, \target, \key + 1
+ .balign 4
+ .long .Lstatic_jump_\@ - ., \target - ., \key + 1 - .
.popsection
.endm
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
index d64296092ef5..cc5034b42335 100644
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -149,4 +149,20 @@ __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
__jump_label_transform(entry, type, text_poke_early, 1);
}
+void jump_label_swap(void *a, void *b, int size)
+{
+ long delta = (unsigned long)a - (unsigned long)b;
+ struct jump_entry *jea = a;
+ struct jump_entry *jeb = b;
+ struct jump_entry tmp = *jea;
+
+ jea->code = jeb->code - delta;
+ jea->target = jeb->target - delta;
+ jea->key = jeb->key - delta;
+
+ jeb->code = tmp.code + delta;
+ jeb->target = tmp.target + delta;
+ jeb->key = tmp.key + delta;
+}
+
#endif
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index 84f001d52322..98ae55b39037 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -30,9 +30,9 @@
#define EX_ORIG_OFFSET 0
#define EX_NEW_OFFSET 4
-#define JUMP_ENTRY_SIZE 24
+#define JUMP_ENTRY_SIZE 12
#define JUMP_ORIG_OFFSET 0
-#define JUMP_NEW_OFFSET 8
+#define JUMP_NEW_OFFSET 4
#define ALT_ENTRY_SIZE 13
#define ALT_ORIG_OFFSET 0
--
2.11.0
^ permalink raw reply related
* [RESEND PATCH v2 08/15] ASoC: qcom: q6asm: add support to audio stream apis
From: Bjorn Andersson @ 2018-01-02 20:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-9-srinivas.kandagatla@linaro.org>
On Thu 14 Dec 09:33 PST 2017, srinivas.kandagatla at linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>
> This patch adds support to open, write and media format commands
> in the q6asm module.
>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
> sound/soc/qcom/qdsp6/q6asm.c | 530 ++++++++++++++++++++++++++++++++++++++++++-
> sound/soc/qcom/qdsp6/q6asm.h | 42 ++++
> 2 files changed, 571 insertions(+), 1 deletion(-)
>
> diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
> index 4be92441f524..dabd6509ef99 100644
> --- a/sound/soc/qcom/qdsp6/q6asm.c
> +++ b/sound/soc/qcom/qdsp6/q6asm.c
> @@ -8,16 +8,34 @@
> #include <linux/soc/qcom/apr.h>
> #include <linux/device.h>
> #include <linux/platform_device.h>
> +#include <uapi/sound/asound.h>
> #include <linux/delay.h>
> #include <linux/slab.h>
> #include <linux/mm.h>
> #include "q6asm.h"
> #include "common.h"
>
> +#define ASM_STREAM_CMD_CLOSE 0x00010BCD
> +#define ASM_STREAM_CMD_FLUSH 0x00010BCE
> +#define ASM_SESSION_CMD_PAUSE 0x00010BD3
> +#define ASM_DATA_CMD_EOS 0x00010BDB
> +#define DEFAULT_POPP_TOPOLOGY 0x00010BE4
> +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09
> #define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92
> #define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93
> #define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94
> -
> +#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98
> +#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99
> +#define ASM_SESSION_CMD_RUN_V2 0x00010DAA
> +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
> +#define ASM_DATA_CMD_WRITE_V2 0x00010DAB
> +#define ASM_SESSION_CMD_SUSPEND 0x00010DEC
> +#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3
> +
> +#define ASM_LEGACY_STREAM_SESSION 0
> +#define ASM_END_POINT_DEVICE_MATRIX 0
> +#define DEFAULT_APP_TYPE 0
> +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */
> #define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */
> #define SYNC_IO_MODE 0x0001
> #define ASYNC_IO_MODE 0x0002
Probably prettier to reorder these and make them Q6ASM_IO_MODE_xyz
[..]
>
> +static int32_t q6asm_callback(struct apr_device *adev,
This callback is an extracted part of q6asm_srvc_callback(), can it be
given a more descriptive name?
> + struct apr_client_data *data, int session_id)
> +{
> + struct audio_client *ac;// = (struct audio_client *)priv;
> + uint32_t token;
> + uint32_t *payload;
> + uint32_t wakeup_flag = 1;
> + uint32_t client_event = 0;
> + struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
> +
> + if (data == NULL)
> + return -EINVAL;
> +
> + ac = q6asm_get_audio_client(q6asm, session_id);
> + if (!q6asm_is_valid_audio_client(ac))
> + return -EINVAL;
> +
> + payload = data->payload;
> +
> + if (data->opcode == APR_BASIC_RSP_RESULT) {
Move this into the switch.
> + token = data->token;
> + switch (payload[0]) {
This is again that common response struct.
> + case ASM_SESSION_CMD_PAUSE:
> + client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE;
> + break;
> + case ASM_SESSION_CMD_SUSPEND:
> + client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;
> + break;
> + case ASM_DATA_CMD_EOS:
> + client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
> + break;
> + break;
> + case ASM_STREAM_CMD_FLUSH:
> + client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;
> + break;
> + case ASM_SESSION_CMD_RUN_V2:
> + client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE;
> + break;
> +
> + case ASM_STREAM_CMD_FLUSH_READBUFS:
> + if (token != ac->session) {
> + dev_err(ac->dev, "session invalid\n");
> + return -EINVAL;
> + }
> + case ASM_STREAM_CMD_CLOSE:
> + client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE;
> + break;
> + case ASM_STREAM_CMD_OPEN_WRITE_V3:
> + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
> + if (payload[1] != 0) {
> + dev_err(ac->dev,
> + "cmd = 0x%x returned error = 0x%x\n",
> + payload[0], payload[1]);
> + if (wakeup_flag) {
> + ac->cmd_state = payload[1];
> + wake_up(&ac->cmd_wait);
> + }
> + return 0;
> + }
> + break;
> + default:
> + dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
> + payload[0]);
> + break;
> + }
> +
> + if (ac->cmd_state && wakeup_flag) {
> + ac->cmd_state = 0;
> + wake_up(&ac->cmd_wait);
> + }
> + if (ac->cb)
> + ac->cb(client_event, data->token,
> + data->payload, ac->priv);
> +
> + return 0;
> + }
> +
> + switch (data->opcode) {
> + case ASM_DATA_EVENT_WRITE_DONE_V2:{
> + struct audio_port_data *port =
> + &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
> +
> + client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE;
> +
> + if (ac->io_mode & SYNC_IO_MODE) {
> + dma_addr_t phys = port->buf[data->token].phys;
> +
> + if (lower_32_bits(phys) != payload[0] ||
> + upper_32_bits(phys) != payload[1]) {
> + dev_err(ac->dev, "Expected addr %pa\n",
> + &port->buf[data->token].phys);
> + return -EINVAL;
> + }
> + token = data->token;
> + port->buf[token].used = 1;
> + }
> + break;
> + }
> + }
> + if (ac->cb)
> + ac->cb(client_event, data->token, data->payload, ac->priv);
> +
> + return 0;
> +}
> +
> static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data)
> {
> struct q6asm *a, *q6asm = dev_get_drvdata(&adev->dev);
> @@ -415,12 +581,16 @@ static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *
> struct audio_port_data *port;
> uint32_t dir = 0;
> uint32_t sid = 0;
> + int dest_port;
> uint32_t *payload;
>
> if (!data) {
> dev_err(&adev->dev, "%s: Invalid CB\n", __func__);
> return 0;
> }
> + dest_port = (data->dest_port >> 8) & 0xFF;
> + if (dest_port)
> + return q6asm_callback(adev, data, dest_port);
You call dest_port "session_id" above, this seems to be a better name
for this variable.
>
> payload = data->payload;
> sid = (data->token >> 8) & 0x0F;
> @@ -540,6 +710,364 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev,
> }
> EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
>
> +static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
> + uint16_t bits_per_sample, uint32_t stream_id,
> + bool is_gapless_mode)
> +{
> + struct asm_stream_cmd_open_write_v3 open;
> + int rc;
> +
> + q6asm_add_hdr(ac, &open.hdr, sizeof(open), true, stream_id);
> + ac->cmd_state = -1;
> +
> + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
> + open.mode_flags = 0x00;
> + open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
> + if (is_gapless_mode)
This is hard coded as false.
> + open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG;
> +
> + /* source endpoint : matrix */
> + open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
> + open.bits_per_sample = bits_per_sample;
> + open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
> +
> + switch (format) {
> + case FORMAT_LINEAR_PCM:
> + open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
> + break;
> + default:
> + dev_err(ac->dev, "Invalid format 0x%x\n", format);
> + return -EINVAL;
> + }
> + rc = apr_send_pkt(ac->adev, (uint32_t *) &open);
> + if (rc < 0)
> + return rc;
> +
> + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
> + if (!rc) {
> + dev_err(ac->dev, "timeout on open write\n");
> + return -ETIMEDOUT;
> + }
Almost every time you apr_send_pkt() you have this wait with timeout,
can this send/wait/return be wrapped in a helper function to reduce the
duplication?
Creating a q6asm_send_sync() and q6asm_send_async() pair with this logic
should help quite a bit.
> +
> + if (ac->cmd_state > 0)
> + return adsp_err_get_lnx_err_code(ac->cmd_state);
> +
> + ac->io_mode |= TUN_WRITE_IO_MODE;
> +
> + return 0;
> +}
> +
> +/**
> + * q6asm_open_write() - Open audio client for writing
> + *
> + * @ac: audio client pointer
> + * @format: audio sample format
> + * @bits_per_sample: bits per sample
> + *
> + * Return: Will be an negative value on error or zero on success
> + */
> +int q6asm_open_write(struct audio_client *ac, uint32_t format,
> + uint16_t bits_per_sample)
> +{
> + return __q6asm_open_write(ac, format, bits_per_sample,
I don't see a particular reason for not inlining this, is there one
coming later in the series?
> + ac->stream_id, false);
> +}
> +EXPORT_SYMBOL_GPL(q6asm_open_write);
> +
> +static int __q6asm_run(struct audio_client *ac, uint32_t flags,
> + uint32_t msw_ts, uint32_t lsw_ts, bool wait)
> +{
> + struct asm_session_cmd_run_v2 run;
> + int rc;
> +
> + q6asm_add_hdr(ac, &run.hdr, sizeof(run), true, ac->stream_id);
> + ac->cmd_state = -1;
> +
> + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
> + run.flags = flags;
> + run.time_lsw = lsw_ts;
> + run.time_msw = msw_ts;
> +
> + rc = apr_send_pkt(ac->adev, (uint32_t *) &run);
> + if (rc < 0)
> + return rc;
> +
> + if (wait) {
Rather than having half of the function conditional I would recommend
inlining this function in the two callers.
In particular if you can come up with a helper function for the
send/wait/handle-error case.
> + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0),
> + 5 * HZ);
> + if (!rc) {
> + dev_err(ac->dev, "timeout on run cmd\n");
> + return -ETIMEDOUT;
> + }
> + if (ac->cmd_state > 0)
> + return adsp_err_get_lnx_err_code(ac->cmd_state);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * q6asm_run() - start the audio client
> + *
> + * @ac: audio client pointer
> + * @flags: flags associated with write
> + * @msw_ts: timestamp msw
> + * @lsw_ts: timestamp lsw
> + *
> + * Return: Will be an negative value on error or zero on success
> + */
> +int q6asm_run(struct audio_client *ac, uint32_t flags,
> + uint32_t msw_ts, uint32_t lsw_ts)
> +{
> + return __q6asm_run(ac, flags, msw_ts, lsw_ts, true);
> +}
> +EXPORT_SYMBOL_GPL(q6asm_run);
> +
> +/**
> + * q6asm_run_nowait() - start the audio client withou blocking
> + *
> + * @ac: audio client pointer
> + * @flags: flags associated with write
> + * @msw_ts: timestamp msw
> + * @lsw_ts: timestamp lsw
> + *
> + * Return: Will be an negative value on error or zero on success
> + */
> +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
> + uint32_t msw_ts, uint32_t lsw_ts)
> +{
> + return __q6asm_run(ac, flags, msw_ts, lsw_ts, false);
> +}
> +EXPORT_SYMBOL_GPL(q6asm_run_nowait);
> +
> +/**
> + * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration
> + *
> + * @ac: audio client pointer
> + * @rate: audio sample rate
> + * @channels: number of audio channels.
> + * @use_default_chmap: flag to use default ch map.
> + * @channel_map: channel map pointer
> + * @bits_per_sample: bits per sample
> + *
> + * Return: Will be an negative value on error or zero on success
> + */
> +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
> + uint32_t rate, uint32_t channels,
> + bool use_default_chmap,
> + char *channel_map,
This should be u8 channel_map[PCM_FORMAT_MAX_NUM_CHANNEL], possibly
char. Unless you, as I suggest below, want to be able to represent
use_default_chmap = false, by setting this to NULL.
> + uint16_t bits_per_sample)
> +{
> + struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
> + u8 *channel_mapping;
> + int rc = 0;
Unnecessary initialization.
> +
> + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), true, ac->stream_id);
> + ac->cmd_state = -1;
> +
> + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
> + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
> + sizeof(fmt.fmt_blk);
> + fmt.num_channels = channels;
> + fmt.bits_per_sample = bits_per_sample;
> + fmt.sample_rate = rate;
> + fmt.is_signed = 1;
> +
> + channel_mapping = fmt.channel_mapping;
> +
> + if (use_default_chmap) {
Passing NULL as channel_map would probably be a nicer way to say this,
instead of having a separate bool.
> + if (q6dsp_map_channels(channel_mapping, channels)) {
> + dev_err(ac->dev, " map channels failed %d\n", channels);
> + return -EINVAL;
> + }
> + } else {
> + memcpy(channel_mapping, channel_map,
> + PCM_FORMAT_MAX_NUM_CHANNEL);
> + }
> +
> + rc = apr_send_pkt(ac->adev, (uint32_t *) &fmt);
> + if (rc < 0)
> + goto fail_cmd;
> +
> + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
> + if (!rc) {
> + dev_err(ac->dev, "timeout on format update\n");
> + return -ETIMEDOUT;
> + }
> + if (ac->cmd_state > 0)
> + return adsp_err_get_lnx_err_code(ac->cmd_state);
> +
> + return 0;
> +fail_cmd:
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
> +
> +/**
> + * q6asm_write_nolock() - non blocking write
> + *
> + * @ac: audio client pointer
> + * @len: lenght in bytes
> + * @msw_ts: timestamp msw
> + * @lsw_ts: timestamp lsw
> + * @flags: flags associated with write
> + *
> + * Return: Will be an negative value on error or zero on success
> + */
> +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
> + uint32_t lsw_ts, uint32_t flags)
q6asm_write_async() is probably a better name, nolock indicates some
relationship to mutual exclusions...
> +{
> + struct asm_data_cmd_write_v2 write;
> + struct audio_port_data *port;
> + struct audio_buffer *ab;
> + int dsp_buf = 0;
> + int rc = 0;
> +
> + if (ac->io_mode & SYNC_IO_MODE) {
Bail early if this isn't true, to save you the indentation level.
> + port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
> + q6asm_add_hdr(ac, &write.hdr, sizeof(write), false,
> + ac->stream_id);
> +
> + dsp_buf = port->dsp_buf;
> + ab = &port->buf[dsp_buf];
So we're just unconditionally telling the remote side about the next buf
in our ring buffer. Do we need to ensure that this is available/ready?
> +
> + write.hdr.token = port->dsp_buf;
> + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
> + write.buf_addr_lsw = lower_32_bits(ab->phys);
> + write.buf_addr_msw = upper_32_bits(ab->phys);
> + write.buf_size = len;
> + write.seq_id = port->dsp_buf;
> + write.timestamp_lsw = lsw_ts;
> + write.timestamp_msw = msw_ts;
> + write.mem_map_handle =
> + ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle;
> +
> + if (flags == NO_TIMESTAMP)
> + write.flags = (flags & 0x800000FF);
Fill in the constant and this becomes
if flags == 0xff00:
write.flags = 0xff00 & 0x800000ff;
Or in other words:
if flags == 0xff00:
write.flags = 0;
> + else
> + write.flags = (0x80000000 | flags);
Drop the parenthesis and flip the |. It would be nice to have a define
or a comment indicating what BIT(31) is...
> +
> + port->dsp_buf++;
> +
> + if (port->dsp_buf >= port->max_buf_cnt)
> + port->dsp_buf = 0;
> +
> + rc = apr_send_pkt(ac->adev, (uint32_t *) &write);
> + if (rc < 0)
> + return rc;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(q6asm_write_nolock);
> +
> +static void q6asm_reset_buf_state(struct audio_client *ac)
> +{
> + int cnt = 0;
> + int loopcnt = 0;
> + int used;
> + struct audio_port_data *port = NULL;
> +
> + if (ac->io_mode & SYNC_IO_MODE) {
> + used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
> + mutex_lock(&ac->cmd_lock);
> + for (loopcnt = 0; loopcnt <= SNDRV_PCM_STREAM_CAPTURE;
> + loopcnt++) {
> + port = &ac->port[loopcnt];
> + cnt = port->max_buf_cnt - 1;
> + port->dsp_buf = 0;
> + while (cnt >= 0) {
> + if (!port->buf)
> + continue;
> + port->buf[cnt].used = used;
> + cnt--;
> + }
> + }
> + mutex_unlock(&ac->cmd_lock);
> + }
> +}
> +
> +static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait)
> +{
> + int stream_id = ac->stream_id;
> + struct apr_hdr hdr;
> + int rc;
> +
> + q6asm_add_hdr(ac, &hdr, sizeof(hdr), true, stream_id);
> + ac->cmd_state = -1;
Resetting cmd_state relates to the send, don't mix it with building the
packet.
> + switch (cmd) {
> + case CMD_PAUSE:
> + hdr.opcode = ASM_SESSION_CMD_PAUSE;
> + break;
> + case CMD_SUSPEND:
> + hdr.opcode = ASM_SESSION_CMD_SUSPEND;
> + break;
> + case CMD_FLUSH:
> + hdr.opcode = ASM_STREAM_CMD_FLUSH;
> + break;
> + case CMD_OUT_FLUSH:
> + hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
> + break;
> + case CMD_EOS:
> + hdr.opcode = ASM_DATA_CMD_EOS;
> + ac->cmd_state = 0;
> + break;
> + case CMD_CLOSE:
> + hdr.opcode = ASM_STREAM_CMD_CLOSE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + rc = apr_send_pkt(ac->adev, (uint32_t *) &hdr);
> + if (rc < 0)
> + return rc;
> +
> + if (!wait)
> + return 0;
I've asked you to split the others into _sync() vs _async() operations.
One particular concern I have is that I don't see any mutual exclusion
protecting the cmd_state and a call with !wait will overwrite the
existing value, which might be unexpected.
> +
> + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
> + if (!rc) {
> + dev_err(ac->dev, "timeout response for opcode[0x%x]\n",
> + hdr.opcode);
> + return -ETIMEDOUT;
> + }
> + if (ac->cmd_state > 0)
> + return adsp_err_get_lnx_err_code(ac->cmd_state);
> +
> + if (cmd == CMD_FLUSH)
> + q6asm_reset_buf_state(ac);
> +
> + return 0;
> +}
[..]
> diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
> index e1409c368600..b4896059da79 100644
> --- a/sound/soc/qcom/qdsp6/q6asm.h
> +++ b/sound/soc/qcom/qdsp6/q6asm.h
> @@ -2,7 +2,34 @@
> #ifndef __Q6_ASM_H__
> #define __Q6_ASM_H__
>
> +/* ASM client callback events */
> +#define CMD_PAUSE 0x0001
These defines has rather generic names...
[..]
> +
> +#define MSM_FRONTEND_DAI_MULTIMEDIA1 0
> +#define MSM_FRONTEND_DAI_MULTIMEDIA2 1
> +#define MSM_FRONTEND_DAI_MULTIMEDIA3 2
> +#define MSM_FRONTEND_DAI_MULTIMEDIA4 3
> +#define MSM_FRONTEND_DAI_MULTIMEDIA5 4
> +#define MSM_FRONTEND_DAI_MULTIMEDIA6 5
> +#define MSM_FRONTEND_DAI_MULTIMEDIA7 6
> +#define MSM_FRONTEND_DAI_MULTIMEDIA8 7
> +
> #define MAX_SESSIONS 16
> +#define NO_TIMESTAMP 0xFF00
> +#define FORMAT_LINEAR_PCM 0x0000
Ditto.
Regards,
Bjorn
^ permalink raw reply
* [GIT PULL] Amlogic 64-bit DT changes for v4.16, round 2
From: Kevin Hilman @ 2018-01-02 20:16 UTC (permalink / raw)
To: linux-arm-kernel
Arnd, Olof,
Here's a second round of 64-bit DT changes for v4.16. This branch is on
top of the previous round (already merged.)
This adds a few more basics (clock, pinctrl, PWM, reset) for the new AXG
family of Amlogic SoCs.
Kevin
The following changes since commit 3106507e1004dd398ef75d0caf048f97ba2dfd0b:
ARM64: dts: meson-gxm: fix q200 interrupt number (2017-12-08 10:47:28 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git tags/amlogic-dt64-2
for you to fetch changes up to 43b9f617b5f98f2f7abb508fef5e535e5fb66a41:
arm64: dts: meson-axg: add new reset DT node (2017-12-15 16:20:29 -0800)
----------------------------------------------------------------
Amlogic 64-bit DT updates for v4.16, round 2
- clock, pinctrl, PWM and reset nodes for new AXG SoC family
----------------------------------------------------------------
Jian Hu (1):
ARM64: dts: meson-axg: add PWM DT info for Meson-Axg SoC
Qiufang Dai (1):
arm64: dts: meson-axg: add clock DT info for Meson AXG SoC
Xingyu Chen (2):
documentation: Add compatibles for Amlogic Meson AXG pin controllers
ARM64: dts: meson-axg: add pinctrl DT info for Meson-AXG SoC
Yixun Lan (1):
arm64: dts: meson-axg: add new reset DT node
Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt | 2 +
arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 177 insertions(+)
^ permalink raw reply
* [PATCH 01/33] clk_ops: change round_rate() to return unsigned long
From: Bryan O'Donoghue @ 2018-01-02 20:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102190159.GH7997@codeaurora.org>
On 02/01/18 19:01, Stephen Boyd wrote:
> On 12/31, Bryan O'Donoghue wrote:
>> On 30/12/17 16:36, Mikko Perttunen wrote:
>>> FWIW, we had this problem some years ago with the Tegra CPU clock
>>> - then it was determined that a simpler solution was to have the
>>> determine_rate callback support unsigned long rates - so clock
>>> drivers that need to return rates higher than 2^31 can instead
>>> implement the determine_rate callback. That is what's currently
>>> implemented.
>>>
>>> Mikko
>>
>> Granted we could work around it but, having both zero and less than
>> zero indicate error means you can't support larger than LONG_MAX
>> which is I think worth fixing.
>>
>
> Ok. But can you implement the determine_rate op instead of the
> round_rate op for your clk?
Don't know .
> It's not a work-around, it's the
> preferred solution. That would allow rates larger than 2^31 for
> the clk without pushing through a change to all the drivers to
> express zero as "error" and non-zero as the rounded rate.
>
> I'm not entirely opposed to this approach, because we probably
> don't care to pass the particular error value from a clk provider
> to a clk consumer about what the error is.
Which was my thought. The return value of clk_ops->round_rate() appears
not to get pushed up the stack, which is what the last patch in this
series deals with.
[PATCH 33/33] clk: change handling of round_rate() such that only zero
is an error
> It's actually what we
> proposed as the solution for clk_round_rate() to return values
> larger than LONG_MAX to consumers. But doing that consumer API
> change or this provider side change is going to require us to
> evaluate all the consumers of these clks to make sure they don't
> check for some error value that's less than zero. This series
> does half the work,
Do you mean users of clk_rounda_rate() ? I have a set of patches for
that but wanted to separate that from clk_ops->round_rate() so as not to
send ~70 patches out to LKML at once - even if they are in two blocks.
If so, I can publish that set too for reference.
AFAICT on clk_ops->round_rate the last patch #33 ought to cover the
usage of the return value of clk_ops->round_rate().
Have I missed something ?
> by changing the provider side, while ignoring
> the consumer side and any potential fallout of the less than zero
> to zero return value change.
>
Can you look at #33 ? I'm not sure if you saw that one.
---
bod
^ permalink raw reply
* [PATCH v4 10/21] arm64: entry.S: move SError handling into a C function for future expansion
From: Adam Wallis @ 2018-01-02 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171019145807.23251-11-james.morse@arm.com>
James
On 10/19/2017 10:57 AM, James Morse wrote:
[..]
> kernel_ventry el1_fiq_invalid // FIQ EL1h
> - kernel_ventry el1_error_invalid // Error EL1h
> + kernel_ventry el1_error // Error EL1h
>
> kernel_ventry el0_sync // Synchronous 64-bit EL0
> kernel_ventry el0_irq // IRQ 64-bit EL0
> kernel_ventry el0_fiq_invalid // FIQ 64-bit EL0
> - kernel_ventry el0_error_invalid // Error 64-bit EL0
> + kernel_ventry el0_error // Error 64-bit EL0
>
> #ifdef CONFIG_COMPAT
> kernel_ventry el0_sync_compat // Synchronous 32-bit EL0
> kernel_ventry el0_irq_compat // IRQ 32-bit EL0
> kernel_ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
> - kernel_ventry el0_error_invalid_compat // Error 32-bit EL0
> + kernel_ventry el0_error_compat // Error 32-bit EL0
> #else
> kernel_ventry el0_sync_invalid // Synchronous 32-bit EL0
> kernel_ventry el0_irq_invalid // IRQ 32-bit EL0
> @@ -455,10 +455,6 @@ ENDPROC(el0_error_invalid)
> el0_fiq_invalid_compat:
> inv_entry 0, BAD_FIQ, 32
> ENDPROC(el0_fiq_invalid_compat)
> -
> -el0_error_invalid_compat:
> - inv_entry 0, BAD_ERROR, 32
> -ENDPROC(el0_error_invalid_compat)
> #endif
Perhaps I missed something quite obvious, but is there any reason to not also
remove el1_error_invalid, since SError handling now jumps to el1_error?
> el1_sync_invalid:
> @@ -663,6 +659,10 @@ el0_svc_compat:
> el0_irq_compat:
> kernel_entry 0, 32
> b el0_irq_naked
> +
> +el0_error_compat:
> + kernel_entry 0, 32
> + b el0_error_naked
> #endif
>
> el0_da:
> @@ -780,6 +780,28 @@ el0_irq_naked:
> b ret_to_user
> ENDPROC(el0_irq)
>
> +el1_error:
> + kernel_entry 1
> + mrs x1, esr_el1
> + enable_dbg
> + mov x0, sp
> + bl do_serror
> + kernel_exit 1
> +ENDPROC(el1_error)
> +
> +el0_error:
> + kernel_entry 0
> +el0_error_naked:
> + mrs x1, esr_el1
> + enable_dbg
> + mov x0, sp
> + bl do_serror
> + enable_daif
> + ct_user_exit
> + b ret_to_user
> +ENDPROC(el0_error)
[..]
Thanks
Adam
--
Adam Wallis
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v4 5/7] clk: Introduce davinci clocks
From: David Lechner @ 2018-01-02 21:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1514763588-31560-6-git-send-email-david@lechnology.com>
Forgot to cc linux-clk, so doing that now...
On 12/31/2017 05:39 PM, David Lechner wrote:
> This introduces new drivers for arch/arm/mach-davinci. The code is based
> on the clock drivers from there and adapted to use the common clock
> framework.
>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
> drivers/clk/Makefile | 1 +
> drivers/clk/davinci/Makefile | 3 +
> drivers/clk/davinci/da8xx-cfgchip-clk.c | 380 ++++++++++++++++++++++++++++++
> drivers/clk/davinci/pll.c | 333 ++++++++++++++++++++++++++
> drivers/clk/davinci/psc.c | 217 +++++++++++++++++
> include/linux/clk/davinci.h | 46 ++++
> include/linux/platform_data/davinci_clk.h | 25 ++
> 7 files changed, 1005 insertions(+)
> create mode 100644 drivers/clk/davinci/Makefile
> create mode 100644 drivers/clk/davinci/da8xx-cfgchip-clk.c
> create mode 100644 drivers/clk/davinci/pll.c
> create mode 100644 drivers/clk/davinci/psc.c
> create mode 100644 include/linux/clk/davinci.h
> create mode 100644 include/linux/platform_data/davinci_clk.h
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7f761b..c865fd0 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/
> obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
> obj-y += bcm/
> obj-$(CONFIG_ARCH_BERLIN) += berlin/
> +obj-$(CONFIG_ARCH_DAVINCI) += davinci/
> obj-$(CONFIG_H8300) += h8300/
> obj-$(CONFIG_ARCH_HISI) += hisilicon/
> obj-y += imgtec/
> diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
> new file mode 100644
> index 0000000..5efbdcd
> --- /dev/null
> +++ b/drivers/clk/davinci/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_PHY_DA8XX_USB) += da8xx-cfgchip-clk.o
> +obj-y += pll.o
> +obj-y += psc.o
> \ No newline at end of file
> diff --git a/drivers/clk/davinci/da8xx-cfgchip-clk.c b/drivers/clk/davinci/da8xx-cfgchip-clk.c
> new file mode 100644
> index 0000000..780bb25
> --- /dev/null
> +++ b/drivers/clk/davinci/da8xx-cfgchip-clk.c
> @@ -0,0 +1,380 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * da8xx-cfgchip-clk - TI DaVinci DA8xx CFGCHIP clocks driver
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + *
> + * This driver exposes the USB PHY clocks on DA8xx/AM18xx/OMAP-L13x SoCs.
> + * The clocks consist of two muxes and a PLL. The USB 2.0 PHY mux and PLL are
> + * combined into a single clock in Linux. The USB 1.0 PHY clock just consists
> + * of a mux. These clocks are controlled through CFGCHIP2, which is accessed
> + * as a syscon regmap since it is shared with other devices.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/mfd/da8xx-cfgchip.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/davinci_clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/**
> + * da8xx_cfgchip_clk
> + * @usb0_hw: The USB 2.0 PHY clock (mux + PLL)
> + * @usb1_hw: The USB 1.1 PHY clock (mux)
> + * @usb0_clk: The USB 2.0 subsystem PSC clock
> + * @regmap: The CFGCHIP syscon regmap
> + */
> +struct da8xx_cfgchip_clk {
> + struct clk_hw usb0_hw;
> + struct clk_hw usb1_hw;
> + struct clk *usb0_clk;
> + struct regmap *regmap;
> +};
> +
> +/* The USB 2.0 PHY can use either USB_REFCLKIN or AUXCLK */
> +enum usb0_phy_clk_parent {
> + USB20_PHY_CLK_PARENT_USB_REFCLKIN,
> + USB20_PHY_CLK_PARENT_PLL0_AUX,
> +};
> +
> +/* The USB 1.1 PHY can use either the PLL output from the USB 2.0 PHY or
> + * USB_REFCLKIN
> + */
> +enum usb1_phy_clk_parent {
> + USB1_PHY_CLK_PARENT_USB_REFCLKIN,
> + USB1_PHY_CLK_PARENT_USB0_PHY_PLL,
> +};
> +
> +/* --- USB 2.0 PHY clock --- */
> +
> +static int usb0_phy_clk_prepare(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +
> + /* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0
> + * PHY clock enable, but since clk_prepare() can't be called in an
> + * atomic context (i.e. in clk_enable()), we have to prepare it here.
> + */
> + return clk_prepare(clk->usb0_clk);
> +}
> +
> +static void usb0_phy_clk_unprepare(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +
> + clk_unprepare(clk->usb0_clk);
> +}
> +
> +static int usb0_phy_clk_enable(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> + unsigned int mask, val;
> + int ret;
> +
> + /* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled
> + * temporaily. It can be turned back off once the PLL is locked.
> + */
> + clk_enable(clk->usb0_clk);
> +
> + /* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1
> + * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used.
> + */
> + mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON;
> + val = CFGCHIP2_PHY_PLLON;
> +
> + regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> + ret = regmap_read_poll_timeout(clk->regmap, CFGCHIP(2), val,
> + val & CFGCHIP2_PHYCLKGD, 0, 500000);
> +
> + clk_disable(clk->usb0_clk);
> +
> + return ret;
> +}
> +
> +static void usb0_phy_clk_disable(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> + unsigned int val;
> +
> + val = CFGCHIP2_PHYPWRDN;
> + regmap_write_bits(clk->regmap, CFGCHIP(2), val, val);
> +}
> +
> +static unsigned long usb0_phy_clk_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> + unsigned int mask, val;
> +
> + /* The parent clock rate must be one of the following */
> + mask = CFGCHIP2_REFFREQ_MASK;
> + switch (parent_rate) {
> + case 12000000:
> + val = CFGCHIP2_REFFREQ_12MHZ;
> + break;
> + case 13000000:
> + val = CFGCHIP2_REFFREQ_13MHZ;
> + break;
> + case 19200000:
> + val = CFGCHIP2_REFFREQ_19_2MHZ;
> + break;
> + case 20000000:
> + val = CFGCHIP2_REFFREQ_20MHZ;
> + break;
> + case 24000000:
> + val = CFGCHIP2_REFFREQ_24MHZ;
> + break;
> + case 26000000:
> + val = CFGCHIP2_REFFREQ_26MHZ;
> + break;
> + case 38400000:
> + val = CFGCHIP2_REFFREQ_38_4MHZ;
> + break;
> + case 40000000:
> + val = CFGCHIP2_REFFREQ_40MHZ;
> + break;
> + case 48000000:
> + val = CFGCHIP2_REFFREQ_48MHZ;
> + break;
> + default:
> + return 0;
> + }
> +
> + regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> + /* USB 2.0 PLL always supplies 48MHz */
> + return 48000000;
> +}
> +
> +static long usb0_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *parent_rate)
> +{
> + return 48000000;
> +}
> +
> +static int usb0_phy_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> + unsigned int mask, val;
> +
> + /* Set the mux depending on the parent clock. */
> + mask = CFGCHIP2_USB2PHYCLKMUX;
> + switch (index) {
> + case USB20_PHY_CLK_PARENT_USB_REFCLKIN:
> + val = 0;
> + break;
> + case USB20_PHY_CLK_PARENT_PLL0_AUX:
> + val = CFGCHIP2_USB2PHYCLKMUX;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> + return 0;
> +}
> +
> +static u8 usb0_phy_clk_get_parent(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> + unsigned int val;
> +
> + regmap_read(clk->regmap, CFGCHIP(2), &val);
> +
> + if (val & CFGCHIP2_USB2PHYCLKMUX)
> + return USB20_PHY_CLK_PARENT_PLL0_AUX;
> +
> + return USB20_PHY_CLK_PARENT_USB_REFCLKIN;
> +}
> +
> +static const struct clk_ops usb0_phy_clk_ops = {
> + .prepare = usb0_phy_clk_prepare,
> + .unprepare = usb0_phy_clk_unprepare,
> + .enable = usb0_phy_clk_enable,
> + .disable = usb0_phy_clk_disable,
> + .recalc_rate = usb0_phy_clk_recalc_rate,
> + .round_rate = usb0_phy_clk_round_rate,
> + .set_parent = usb0_phy_clk_set_parent,
> + .get_parent = usb0_phy_clk_get_parent,
> +};
> +
> +static const char * const usb0_phy_clk_parent_names[] = {
> + [USB20_PHY_CLK_PARENT_USB_REFCLKIN] = "usb_refclkin",
> + [USB20_PHY_CLK_PARENT_PLL0_AUX] = "pll0_aux_clk",
> +};
> +
> +static const struct clk_init_data usb0_phy_clk_init_data = {
> + .name = "usb0_phy_clk",
> + .ops = &usb0_phy_clk_ops,
> + .parent_names = usb0_phy_clk_parent_names,
> + .num_parents = ARRAY_SIZE(usb0_phy_clk_parent_names),
> +};
> +
> +/* --- USB 1.1 PHY clock --- */
> +
> +static int usb1_phy_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
> + unsigned int mask, val;
> +
> + /* Set the USB 1.1 PHY clock mux based on the parent clock. */
> + mask = CFGCHIP2_USB1PHYCLKMUX;
> + switch (index) {
> + case USB1_PHY_CLK_PARENT_USB_REFCLKIN:
> + val = CFGCHIP2_USB1PHYCLKMUX;
> + break;
> + case USB1_PHY_CLK_PARENT_USB0_PHY_PLL:
> + val = 0;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> + return 0;
> +}
> +
> +static u8 usb1_phy_clk_get_parent(struct clk_hw *hw)
> +{
> + struct da8xx_cfgchip_clk *clk =
> + container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
> + unsigned int val;
> +
> + regmap_read(clk->regmap, CFGCHIP(2), &val);
> +
> + if (val & CFGCHIP2_USB1PHYCLKMUX)
> + return USB1_PHY_CLK_PARENT_USB_REFCLKIN;
> +
> + return USB1_PHY_CLK_PARENT_USB0_PHY_PLL;
> +}
> +
> +static const struct clk_ops usb1_phy_clk_ops = {
> + .set_parent = usb1_phy_clk_set_parent,
> + .get_parent = usb1_phy_clk_get_parent,
> +};
> +
> +static const char * const usb1_phy_clk_parent_names[] = {
> + [USB1_PHY_CLK_PARENT_USB_REFCLKIN] = "usb_refclkin",
> + [USB1_PHY_CLK_PARENT_USB0_PHY_PLL] = "usb0_phy_clk",
> +};
> +
> +static struct clk_init_data usb1_phy_clk_init_data = {
> + .name = "usb1_phy_clk",
> + .ops = &usb1_phy_clk_ops,
> + .parent_names = usb1_phy_clk_parent_names,
> + .num_parents = ARRAY_SIZE(usb1_phy_clk_parent_names),
> +};
> +
> +/* --- platform driver --- */
> +
> +static int da8xx_cfgchip_clk_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct da8xx_cfgchip_clk_data *pdata = dev->platform_data;
> + struct da8xx_cfgchip_clk *phy_clk;
> + const char *parent_name;
> + struct clk *parent;
> + int ret;
> +
> + if (!pdata)
> + return -EINVAL;
> +
> + phy_clk = devm_kzalloc(dev, sizeof(*phy_clk), GFP_KERNEL);
> + if (!phy_clk)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, phy_clk);
> +
> + phy_clk->regmap = syscon_regmap_lookup_by_pdevname("syscon");
> + if (IS_ERR(phy_clk->regmap)) {
> + dev_err(dev, "Failed to get syscon\n");
> + return PTR_ERR(phy_clk->regmap);
> + }
> +
> + /* USB 2.0 subsystem PSC clock - needed to lock PLL */
> + phy_clk->usb0_clk = clk_get(dev, "usb20");
> + if (IS_ERR(phy_clk->usb0_clk)) {
> + dev_err(dev, "Failed to get usb20 clock\n");
> + return PTR_ERR(phy_clk->usb0_clk);
> + }
> +
> + phy_clk->usb0_hw.init = &usb0_phy_clk_init_data;
> + ret = devm_clk_hw_register(dev, &phy_clk->usb0_hw);
> + if (ret) {
> + dev_err(dev, "Failed to register usb0_phy_clk\n");
> + return ret;
> + }
> +
> + phy_clk->usb1_hw.init = &usb1_phy_clk_init_data;
> + ret = devm_clk_hw_register(dev, &phy_clk->usb1_hw);
> + if (ret) {
> + dev_err(dev, "Failed to register usb1_phy_clk\n");
> + return ret;
> + }
> +
> + parent_name = pdata->usb0_use_refclkin ? "usb_refclkin" : "pll0_aux";
> + parent = devm_clk_get(dev, parent_name);
> + if (IS_ERR(parent)) {
> + dev_err(dev, "Failed to get usb0 parent clock %s\n",
> + parent_name);
> + return PTR_ERR(parent);
> + }
> +
> + ret = clk_set_parent(phy_clk->usb0_hw.clk, parent);
> + if (ret) {
> + dev_err(dev, "Failed to set usb0 parent clock to %s\n",
> + parent_name);
> + return ret;
> + }
> +
> + clk_hw_register_clkdev(&phy_clk->usb0_hw, NULL, "da8xx-cfgchip-clk");
> +
> + parent_name = pdata->usb1_use_refclkin ? "usb_refclkin" : "usb0_phy_clk";
> + parent = devm_clk_get(dev, parent_name);
> + if (IS_ERR(parent)) {
> + dev_err(dev, "Failed to get usb1 parent clock %s\n",
> + parent_name);
> + return PTR_ERR(parent);
> + }
> +
> + ret = clk_set_parent(phy_clk->usb1_hw.clk, parent);
> + if (ret) {
> + dev_err(dev, "Failed to set usb1 parent clock to %s\n",
> + parent_name);
> + return ret;
> + }
> +
> + clk_hw_register_clkdev(&phy_clk->usb0_hw, "usb20_phy", "da8xx-usb-phy");
> + clk_hw_register_clkdev(&phy_clk->usb1_hw, "usb11_phy", "da8xx-usb-phy");
> +
> + return 0;
> +}
> +
> +static struct platform_driver da8xx_cfgchip_clk_driver = {
> + .probe = da8xx_cfgchip_clk_probe,
> + .driver = {
> + .name = "da8xx-cfgchip-clk",
> + },
> +};
> +module_platform_driver(da8xx_cfgchip_clk_driver);
> +
> +MODULE_ALIAS("platform:da8xx-cfgchip-clk");
> +MODULE_AUTHOR("David Lechner <david@lechnology.com>");
> +MODULE_DESCRIPTION("TI DA8xx CFGCHIP clock driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
> new file mode 100644
> index 0000000..035cd91
> --- /dev/null
> +++ b/drivers/clk/davinci/pll.c
> @@ -0,0 +1,333 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PLL clock driver for Davinci devices
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + *
> + * Based on drivers/clk/keystone/pll.c
> + * Copyright (C) 2013 Texas Instruments Inc.
> + * Murali Karicheri <m-karicheri2@ti.com>
> + * Santosh Shilimkar <santosh.shilimkar@ti.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +
> +#define REVID 0x000
> +#define PLLCTL 0x100
> +#define OCSEL 0x104
> +#define PLLSECCTL 0x108
> +#define PLLM 0x110
> +#define PREDIV 0x114
> +#define PLLDIV1 0x118
> +#define PLLDIV2 0x11c
> +#define PLLDIV3 0x120
> +#define OSCDIV 0x124
> +#define POSTDIV 0x128
> +#define BPDIV 0x12c
> +#define PLLCMD 0x138
> +#define PLLSTAT 0x13c
> +#define ALNCTL 0x140
> +#define DCHANGE 0x144
> +#define CKEN 0x148
> +#define CKSTAT 0x14c
> +#define SYSTAT 0x150
> +#define PLLDIV4 0x160
> +#define PLLDIV5 0x164
> +#define PLLDIV6 0x168
> +#define PLLDIV7 0x16c
> +#define PLLDIV8 0x170
> +#define PLLDIV9 0x174
> +
> +#define PLLM_MASK 0x1f
> +#define PREDIV_RATIO_MASK 0x1f
> +#define PLLDIV_RATIO_WIDTH 5
> +#define PLLDIV_ENABLE_SHIFT 15
> +#define OSCDIV_RATIO_WIDTH 5
> +#define POSTDIV_RATIO_MASK 0x1f
> +#define BPDIV_RATIO_SHIFT 0
> +#define BPDIV_RATIO_WIDTH 5
> +#define CKEN_OBSCLK_SHIFT 1
> +#define CKEN_AUXEN_SHIFT 0
> +
> +/**
> + * struct davinci_pll_clk - Main PLL clock
> + * @hw: clk_hw for the pll
> + * @base: Base memory address
> + * @parent_rate: Saved parent rate used by some child clocks
> + */
> +struct davinci_pll_clk {
> + struct clk_hw hw;
> + void __iomem *base;
> +};
> +
> +#define to_davinci_pll_clk(_hw) container_of((_hw), struct davinci_pll_clk, hw)
> +
> +static unsigned long davinci_pll_clk_recalc(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
> + unsigned long rate = parent_rate;
> + u32 prediv, mult, postdiv;
> +
> + prediv = readl(pll->base + PREDIV) & PREDIV_RATIO_MASK;
> + mult = readl(pll->base + PLLM) & PLLM_MASK;
> + postdiv = readl(pll->base + POSTDIV) & POSTDIV_RATIO_MASK;
> +
> + rate /= prediv + 1;
> + rate *= mult + 1;
> + rate /= postdiv + 1;
> +
> + return rate;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +
> +#define DEBUG_REG(n) \
> +{ \
> + .name = #n, \
> + .offset = n, \
> +}
> +
> +static const struct debugfs_reg32 davinci_pll_regs[] = {
> + DEBUG_REG(REVID),
> + DEBUG_REG(PLLCTL),
> + DEBUG_REG(OCSEL),
> + DEBUG_REG(PLLSECCTL),
> + DEBUG_REG(PLLM),
> + DEBUG_REG(PREDIV),
> + DEBUG_REG(PLLDIV1),
> + DEBUG_REG(PLLDIV2),
> + DEBUG_REG(PLLDIV3),
> + DEBUG_REG(OSCDIV),
> + DEBUG_REG(POSTDIV),
> + DEBUG_REG(BPDIV),
> + DEBUG_REG(PLLCMD),
> + DEBUG_REG(PLLSTAT),
> + DEBUG_REG(ALNCTL),
> + DEBUG_REG(DCHANGE),
> + DEBUG_REG(CKEN),
> + DEBUG_REG(CKSTAT),
> + DEBUG_REG(SYSTAT),
> + DEBUG_REG(PLLDIV4),
> + DEBUG_REG(PLLDIV5),
> + DEBUG_REG(PLLDIV6),
> + DEBUG_REG(PLLDIV7),
> + DEBUG_REG(PLLDIV8),
> + DEBUG_REG(PLLDIV9),
> +};
> +
> +static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
> +{
> + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
> + struct debugfs_regset32 *regset;
> + struct dentry *d;
> +
> + regset = kzalloc(sizeof(regset), GFP_KERNEL);
> + if (!regset)
> + return -ENOMEM;
> +
> + regset->regs = davinci_pll_regs;
> + regset->nregs = ARRAY_SIZE(davinci_pll_regs);
> + regset->base = pll->base;
> +
> + d = debugfs_create_regset32("registers", 0400, dentry, regset);
> + if (IS_ERR(d)) {
> + kfree(regset);
> + return PTR_ERR(d);
> + }
> +
> + return 0;
> +}
> +#else
> +#define davinci_pll_debug_init NULL
> +#endif
> +
> +static const struct clk_ops davinci_pll_clk_ops = {
> + .recalc_rate = davinci_pll_clk_recalc,
> + .debug_init = davinci_pll_debug_init,
> +};
> +
> +/**
> + * davinci_pll_clk_register - Register a PLL clock
> + * @name: The clock name
> + * @parent_name: The parent clock name
> + * @base: The PLL's memory region
> + */
> +struct clk *davinci_pll_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base)
> +{
> + struct clk_init_data init;
> + struct davinci_pll_clk *pll;
> + struct clk *clk;
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &davinci_pll_clk_ops;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> +
> + pll->base = base;
> + pll->hw.init = &init;
> +
> + clk = clk_register(NULL, &pll->hw);
> + if (IS_ERR(clk))
> + kfree(pll);
> +
> + return clk;
> +}
> +
> +struct davinci_pll_aux_clk {
> + struct clk_hw hw;
> + struct davinci_pll_clk *pll;
> +};
> +
> +/**
> + * davinci_pll_aux_clk_register - Register bypass clock (AUXCLK)
> + * @name: The clock name
> + * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
> + * the PLL)
> + * @base: The PLL memory region
> + */
> +struct clk *davinci_pll_aux_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base)
> +{
> + return clk_register_gate(NULL, name, parent_name, 0, base + CKEN,
> + CKEN_AUXEN_SHIFT, 0, NULL);
> +}
> +
> +/**
> + * davinci_pll_bpdiv_clk_register - Register bypass divider clock (SYSCLKBP)
> + * @name: The clock name
> + * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
> + * the PLL)
> + * @base: The PLL memory region
> + */
> +struct clk *davinci_pll_bpdiv_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base)
> +{
> + return clk_register_divider(NULL, name, parent_name, 0, base + BPDIV,
> + BPDIV_RATIO_SHIFT, BPDIV_RATIO_WIDTH,
> + CLK_DIVIDER_READ_ONLY, NULL);
> +}
> +
> +/**
> + * davinci_pll_obs_clk_register - Register oscillator divider clock (OBSCLK)
> + * @name: The clock name
> + * @parent_names: The parent clock names
> + * @num_parents: The number of paren clocks
> + * @base: The PLL memory region
> + * @table: A table of values cooresponding to the parent clocks (see OCSEL
> + * register in SRM for values)
> + */
> +struct clk *davinci_pll_obs_clk_register(const char *name,
> + const char * const *parent_names,
> + u8 num_parents,
> + void __iomem *base,
> + u32 *table)
> +{
> + struct clk_mux *mux;
> + struct clk_gate *gate;
> + struct clk_divider *divider;
> + struct clk *clk;
> +
> + mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> + if (!mux)
> + return ERR_PTR(-ENOMEM);
> +
> + mux->reg = base + OCSEL;
> + mux->table = table;
> +
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> + if (!gate) {
> + kfree(mux);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + gate->reg = base + CKEN;
> + gate->bit_idx = CKEN_OBSCLK_SHIFT;
> +
> + divider = kzalloc(sizeof(*divider), GFP_KERNEL);
> + if (!divider) {
> + kfree(gate);
> + kfree(mux);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + divider->reg = base + OSCDIV;
> + divider->width = OSCDIV_RATIO_WIDTH;
> +
> + clk = clk_register_composite(NULL, name, parent_names, num_parents,
> + &mux->hw, &clk_mux_ops,
> + ÷r->hw, &clk_divider_ops,
> + &gate->hw, &clk_gate_ops, 0);
> + if (IS_ERR(clk)) {
> + kfree(divider);
> + kfree(gate);
> + kfree(mux);
> + }
> +
> + return clk;
> +}
> +
> +/**
> + * davinci_pll_div_clk_register - Register a PLLDIV (SYSCLK) clock
> + * @name: The clock name
> + * @parent_name: The parent clock name
> + * @base: The PLL memory region
> + * @id: The id of the divider (n in PLLDIVn)
> + */
> +struct clk *davinci_pll_div_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base,
> + u32 id)
> +{
> + const char * const *parent_names = (parent_name ? &parent_name : NULL);
> + int num_parents = (parent_name ? 1 : 0);
> + struct clk_gate *gate;
> + struct clk_divider *divider;
> + struct clk *clk;
> + u32 reg;
> +
> + /* PLLDIVn registers are not entirely consecutive */
> + if (id < 4)
> + reg = PLLDIV1 + 4 * (id - 1);
> + else
> + reg = PLLDIV4 + 4 * (id - 4);
> +
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> + if (!gate)
> + return ERR_PTR(-ENOMEM);
> +
> + gate->reg = base + reg;
> + gate->bit_idx = PLLDIV_ENABLE_SHIFT;
> +
> + divider = kzalloc(sizeof(*divider), GFP_KERNEL);
> + if (!divider) {
> + kfree(gate);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + divider->reg = base + reg;
> + divider->width = PLLDIV_RATIO_WIDTH;
> + divider->flags = CLK_DIVIDER_READ_ONLY;
> +
> + clk = clk_register_composite(NULL, name, parent_names, num_parents,
> + NULL, NULL, ÷r->hw, &clk_divider_ops,
> + &gate->hw, &clk_gate_ops, 0);
> + if (IS_ERR(clk)) {
> + kfree(divider);
> + kfree(gate);
> + }
> +
> + return clk;
> +}
> diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
> new file mode 100644
> index 0000000..8ae85ee
> --- /dev/null
> +++ b/drivers/clk/davinci/psc.c
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x PSC controllers
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + *
> + * Based on: drivers/clk/keystone/gate.c
> + * Copyright (C) 2013 Texas Instruments.
> + * Murali Karicheri <m-karicheri2@ti.com>
> + * Santosh Shilimkar <santosh.shilimkar@ti.com>
> + *
> + * And: arch/arm/mach-davinci/psc.c
> + * Copyright (C) 2006 Texas Instruments.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +
> +/* PSC register offsets */
> +#define EPCPR 0x070
> +#define PTCMD 0x120
> +#define PTSTAT 0x128
> +#define PDSTAT 0x200
> +#define PDCTL 0x300
> +#define MDSTAT 0x800
> +#define MDCTL 0xa00
> +
> +/* PSC module states */
> +enum davinci_psc_state {
> + PSC_STATE_SWRSTDISABLE = 0,
> + PSC_STATE_SYNCRST = 1,
> + PSC_STATE_DISABLE = 2,
> + PSC_STATE_ENABLE = 3,
> +};
> +
> +#define MDSTAT_STATE_MASK 0x3f
> +#define MDSTAT_MCKOUT BIT(12)
> +#define PDSTAT_STATE_MASK 0x1f
> +#define MDCTL_FORCE BIT(31)
> +#define MDCTL_LRESET BIT(8)
> +#define PDCTL_EPCGOOD BIT(8)
> +#define PDCTL_NEXT BIT(0)
> +
> +/**
> + * struct davinci_psc_clk - PSC clock structure
> + * @hw: clk_hw for the psc
> + * @psc_data: PSC driver specific data
> + * @lpsc: Local PSC number (module id)
> + * @pd: Power domain
> + */
> +struct davinci_psc_clk {
> + struct clk_hw hw;
> + void __iomem *base;
> + u32 lpsc;
> + u32 pd;
> +};
> +
> +#define to_davinci_psc_clk(_hw) container_of(_hw, struct davinci_psc_clk, hw)
> +
> +static void psc_config(struct davinci_psc_clk *psc,
> + enum davinci_psc_state next_state)
> +{
> + u32 epcpr, ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat;
> +
> + mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
> + mdctl &= ~MDSTAT_STATE_MASK;
> + mdctl |= next_state;
> + /* TODO: old davinci clocks for da850 set MDCTL_FORCE bit for sata and
> + * dsp here. Is this really needed?
> + */
> + writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
> +
> + pdstat = readl(psc->base + PDSTAT + 4 * psc->pd);
> + if ((pdstat & PDSTAT_STATE_MASK) == 0) {
> + pdctl = readl(psc->base + PDSTAT + 4 * psc->pd);
> + pdctl |= PDCTL_NEXT;
> + writel(pdctl, psc->base + PDSTAT + 4 * psc->pd);
> +
> + ptcmd = BIT(psc->pd);
> + writel(ptcmd, psc->base + PTCMD);
> +
> + do {
> + epcpr = __raw_readl(psc->base + EPCPR);
> + } while (!(epcpr & BIT(psc->pd)));
> +
> + pdctl = __raw_readl(psc->base + PDCTL + 4 * psc->pd);
> + pdctl |= PDCTL_EPCGOOD;
> + __raw_writel(pdctl, psc->base + PDCTL + 4 * psc->pd);
> + } else {
> + ptcmd = BIT(psc->pd);
> + writel(ptcmd, psc->base + PTCMD);
> + }
> +
> + do {
> + ptstat = readl(psc->base + PTSTAT);
> + } while (ptstat & BIT(psc->pd));
> +
> + do {
> + mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
> + } while (!((mdstat & MDSTAT_STATE_MASK) == next_state));
> +}
> +
> +static int davinci_psc_clk_enable(struct clk_hw *hw)
> +{
> + struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> +
> + psc_config(psc, PSC_STATE_ENABLE);
> +
> + return 0;
> +}
> +
> +static void davinci_psc_clk_disable(struct clk_hw *hw)
> +{
> + struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> +
> + psc_config(psc, PSC_STATE_DISABLE);
> +}
> +
> +static int davinci_psc_clk_is_enabled(struct clk_hw *hw)
> +{
> + struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> + u32 mdstat;
> +
> + mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
> +
> + return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
> +}
> +
> +static const struct clk_ops davinci_psc_clk_ops = {
> + .enable = davinci_psc_clk_enable,
> + .disable = davinci_psc_clk_disable,
> + .is_enabled = davinci_psc_clk_is_enabled,
> +};
> +
> +/**
> + * davinci_psc_clk_register - register psc clock
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @base: memory mapped register for the PSC
> + * @lpsc: local PSC number
> + * @pd: power domain
> + */
> +struct clk *davinci_psc_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base,
> + u32 lpsc, u32 pd)
> +{
> + struct clk_init_data init;
> + struct davinci_psc_clk *psc;
> + struct clk *clk;
> +
> + psc = kzalloc(sizeof(*psc), GFP_KERNEL);
> + if (!psc)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &davinci_psc_clk_ops;
> + init.flags = 0;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> +
> + psc->base = base;
> + psc->hw.init = &init;
> + psc->lpsc = lpsc;
> + psc->pd = pd;
> +
> + clk = clk_register(NULL, &psc->hw);
> + if (IS_ERR(clk))
> + kfree(psc);
> +
> + return clk;
> +}
> +
> +/* FIXME: This needs to be converted to a reset controller. But, the reset
> + * framework is currently device tree only.
> + */
> +
> +DEFINE_SPINLOCK(davinci_psc_reset_lock);
> +
> +static int davinci_psc_clk_reset(struct davinci_psc_clk *psc, bool reset)
> +{
> + unsigned long flags;
> + u32 mdctl;
> +
> + if (IS_ERR_OR_NULL(psc))
> + return -EINVAL;
> +
> + spin_lock_irqsave(&davinci_psc_reset_lock, flags);
> + mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
> + if (reset)
> + mdctl &= ~MDCTL_LRESET;
> + else
> + mdctl |= MDCTL_LRESET;
> + writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
> + spin_unlock_irqrestore(&davinci_psc_reset_lock, flags);
> +
> + return 0;
> +}
> +
> +int davinci_clk_reset_assert(struct clk *clk)
> +{
> + struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> + return davinci_psc_clk_reset(psc, true);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_assert);
> +
> +int davinci_clk_reset_deassert(struct clk *clk)
> +{
> + struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> + return davinci_psc_clk_reset(psc, false);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_deassert);
> diff --git a/include/linux/clk/davinci.h b/include/linux/clk/davinci.h
> new file mode 100644
> index 0000000..c5d2181
> --- /dev/null
> +++ b/include/linux/clk/davinci.h
> @@ -0,0 +1,46 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TI Davinci clocks
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + */
> +#ifndef __LINUX_CLK_DAVINCI_H__
> +#define __LINUX_CLK_DAVINCI_H__
> +
> +#include <linux/clk-provider.h>
> +#include <linux/types.h>
> +
> +struct clk *davinci_pll_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base);
> +struct clk *davinci_pll_aux_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base);
> +struct clk *davinci_pll_bpdiv_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base);
> +struct clk *davinci_pll_obs_clk_register(const char *name,
> + const char * const *parent_names,
> + u8 num_parents,
> + void __iomem *base,
> + u32 *table);
> +struct clk *davinci_pll_div_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base,
> + u32 id);
> +struct clk *davinci_psc_clk_register(const char *name,
> + const char *parent_name,
> + void __iomem *base,
> + u32 lpsc, u32 pd);
> +
> +/* convience macros for board declaration files */
> +#define EXT_CLK(n, r) clk_register_fixed_rate(NULL, (n), NULL, 0, (r))
> +#define FIX_CLK(n, p) clk_register_fixed_factor(NULL, (n), (p), 0, 1, 1)
> +#define PLL_CLK davinci_pll_clk_register
> +#define PLL_DIV_CLK davinci_pll_div_clk_register
> +#define PLL_AUX_CLK davinci_pll_aux_clk_register
> +#define PLL_BP_CLK davinci_pll_bpdiv_clk_register
> +#define PLL_OBS_CLK davinci_pll_obs_clk_register
> +#define PSC_CLK davinci_psc_clk_register
> +
> +#endif /* __LINUX_CLK_DAVINCI_H__ */
> diff --git a/include/linux/platform_data/davinci_clk.h b/include/linux/platform_data/davinci_clk.h
> new file mode 100644
> index 0000000..7576ace
> --- /dev/null
> +++ b/include/linux/platform_data/davinci_clk.h
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TI DaVinci Clock support
> + *
> + * Copyright (C) 2017 David Lechner <david@lechnology.com>
> + */
> +
> +#ifndef __PLATFORM_DATA_DAVINCI_CLK_H
> +#define __PLATFORM_DATA_DAVINCI_CLK_H
> +
> +#include <linux/types.h>
> +
> +/**
> + * da8xx_cfgchip_clk_data - DA8xx CFGCHIP clock platform data
> + * @usb0_use_refclkin: when true, use USB_REFCLKIN, otherwise use AUXCLK for
> + * USB 2.0 PHY clock
> + * @usb1_use_refclkin: when true, use USB_REFCLKIN, otherwise use USB 2.0 PHY
> + * PLL for USB 1.1 PHY clock
> + */
> +struct da8xx_cfgchip_clk_data {
> + bool usb0_use_refclkin;
> + bool usb1_use_refclkin;
> +};
> +
> +#endif /* __PLATFORM_DATA_DAVINCI_CLK_H */
>
^ permalink raw reply
* [PATCH V3 3/3] arm64: Extend early page table code to allow for larger kernels
From: Ard Biesheuvel @ 2018-01-02 22:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180102151254.4063-4-steve.capper@arm.com>
Hi Steve,
On 2 January 2018 at 15:12, Steve Capper <steve.capper@arm.com> wrote:
> Currently the early assembler page table code assumes that precisely
> 1xpgd, 1xpud, 1xpmd are sufficient to represent the early kernel text
> mappings.
>
> Unfortunately this is rarely the case when running with a 16KB granule,
> and we also run into limits with 4KB granule when building much larger
> kernels.
>
> This patch re-writes the early page table logic to compute indices of
> mappings for each level of page table, and if multiple indices are
> required, the next-level page table is scaled up accordingly.
>
> Also the required size of the swapper_pg_dir is computed at link time
> to cover the mapping [KIMAGE_ADDR + VOFFSET, _end]. When KASLR is
> enabled, an extra page is set aside for each level that may require extra
> entries at runtime.
>
> Signed-off-by: Steve Capper <steve.capper@arm.com>
>
> ---
> Changed in V3:
> Corrected KASLR computation
> Rebased against arm64/for-next/core, particularly Kristina's 52-bit
> PA series.
> ---
> arch/arm64/include/asm/kernel-pgtable.h | 47 ++++++++++-
> arch/arm64/include/asm/pgtable.h | 1 +
> arch/arm64/kernel/head.S | 145 +++++++++++++++++++++++---------
> arch/arm64/kernel/vmlinux.lds.S | 1 +
> arch/arm64/mm/mmu.c | 3 +-
> 5 files changed, 157 insertions(+), 40 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
> index 77a27af01371..82386e860dd2 100644
> --- a/arch/arm64/include/asm/kernel-pgtable.h
> +++ b/arch/arm64/include/asm/kernel-pgtable.h
> @@ -52,7 +52,52 @@
> #define IDMAP_PGTABLE_LEVELS (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT))
> #endif
>
> -#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE)
> +
> +/*
> + * If KASLR is enabled, then an offset K is added to the kernel address
> + * space. The bottom 21 bits of this offset are zero to guarantee 2MB
> + * alignment for PA and VA.
> + *
> + * For each pagetable level of the swapper, we know that the shift will
> + * be larger than 21 (for the 4KB granule case we use section maps thus
> + * the smallest shift is actually 30) thus there is the possibility that
> + * KASLR can increase the number of pagetable entries by 1, so we make
> + * room for this extra entry.
> + *
> + * Note KASLR cannot increase the number of required entries for a level
> + * by more than one because it increments both the virtual start and end
> + * addresses equally (the extra entry comes from the case where the end
> + * address is just pushed over a boundary and the start address isn't).
> + */
> +
> +#ifdef CONFIG_RANDOMIZE_BASE
> +#define EARLY_KASLR (1)
> +#else
> +#define EARLY_KASLR (0)
> +#endif
> +
> +#define EARLY_ENTRIES(vstart, vend, shift) (((vend) >> (shift)) \
> + - ((vstart) >> (shift)) + 1 + EARLY_KASLR)
> +
> +#define EARLY_PGDS(vstart, vend) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT))
> +
> +#if SWAPPER_PGTABLE_LEVELS > 3
> +#define EARLY_PUDS(vstart, vend) (EARLY_ENTRIES(vstart, vend, PUD_SHIFT))
> +#else
> +#define EARLY_PUDS(vstart, vend) (0)
> +#endif
> +
> +#if SWAPPER_PGTABLE_LEVELS > 2
> +#define EARLY_PMDS(vstart, vend) (EARLY_ENTRIES(vstart, vend, SWAPPER_TABLE_SHIFT))
> +#else
> +#define EARLY_PMDS(vstart, vend) (0)
> +#endif
> +
> +#define EARLY_PAGES(vstart, vend) ( 1 /* PGDIR page */ \
> + + EARLY_PGDS((vstart), (vend)) /* each PGDIR needs a next level page table */ \
> + + EARLY_PUDS((vstart), (vend)) /* each PUD needs a next level page table */ \
> + + EARLY_PMDS((vstart), (vend))) /* each PMD needs a next level page table */
> +#define SWAPPER_DIR_SIZE (PAGE_SIZE * EARLY_PAGES(KIMAGE_VADDR + TEXT_OFFSET, _end))
> #define IDMAP_DIR_SIZE (IDMAP_PGTABLE_LEVELS * PAGE_SIZE)
>
> #ifdef CONFIG_ARM64_SW_TTBR0_PAN
> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> index bfa237e892f1..54b0a8398055 100644
> --- a/arch/arm64/include/asm/pgtable.h
> +++ b/arch/arm64/include/asm/pgtable.h
> @@ -706,6 +706,7 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
> #endif
>
> extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
> +extern pgd_t swapper_pg_end[];
> extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
> extern pgd_t tramp_pg_dir[PTRS_PER_PGD];
>
> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
> index 66f01869e97c..539e2642ed41 100644
> --- a/arch/arm64/kernel/head.S
> +++ b/arch/arm64/kernel/head.S
> @@ -191,44 +191,110 @@ ENDPROC(preserve_boot_args)
> .endm
>
> /*
> - * Macro to populate the PGD (and possibily PUD) for the corresponding
> - * block entry in the next level (tbl) for the given virtual address.
> + * Macro to populate page table entries, these entries can be pointers to the next level
> + * or last level entries pointing to physical memory.
> *
> - * Preserves: tbl, next, virt
> - * Corrupts: ptrs_per_pgd, tmp1, tmp2
> + * tbl: page table address
> + * rtbl: pointer to page table or physical memory
> + * index: start index to write
> + * eindex: end index to write - [index, eindex] written to
> + * flags: flags for pagetable entry to or in
> + * inc: increment to rtbl between each entry
> + * tmp1: temporary variable
> + *
> + * Preserves: tbl, eindex, flags, inc
> + * Corrupts: index, tmp1
> + * Returns: rtbl
> */
> - .macro create_pgd_entry, tbl, virt, ptrs_per_pgd, tmp1, tmp2
> - create_table_entry \tbl, \virt, PGDIR_SHIFT, \ptrs_per_pgd, \tmp1, \tmp2
> -#if SWAPPER_PGTABLE_LEVELS > 3
> - mov \ptrs_per_pgd, PTRS_PER_PUD
> - create_table_entry \tbl, \virt, PUD_SHIFT, \ptrs_per_pgd, \tmp1, \tmp2
> -#endif
> -#if SWAPPER_PGTABLE_LEVELS > 2
> - mov \ptrs_per_pgd, PTRS_PER_PTE
> - create_table_entry \tbl, \virt, SWAPPER_TABLE_SHIFT, \ptrs_per_pgd, \tmp1, \tmp2
> -#endif
> + .macro populate_entries, tbl, rtbl, index, eindex, flags, inc, tmp1
> +9999: phys_to_pte \rtbl, \tmp1
I know this is existing code, but you could take the opportunity to
replace this label with
.L\@ :
(and update the branch instruction accordingly) so that we don't have
to rely on the uniqueness of '9999'
> + orr \tmp1, \tmp1, \flags // tmp1 = table entry
> + str \tmp1, [\tbl, \index, lsl #3]
> + add \rtbl, \rtbl, \inc // rtbl = pa next level
> + add \index, \index, #1
> + cmp \index, \eindex
> + b.ls 9999b
> .endm
>
> /*
> - * Macro to populate block entries in the page table for the start..end
> - * virtual range (inclusive).
> + * Compute indices of table entries from virtual address range. If multiple entries
> + * were needed in the previous page table level then the next page table level is assumed
> + * to be composed of multiple pages. (This effectively scales the end index).
> + *
> + * vstart: virtual address of start of range
> + * vend: virtual address of end of range
> + * shift: shift used to transform virtual address into index
> + * ptrs: number of entries in page table
> + * istart: index in table corresponding to vstart
> + * iend: index in table corresponding to vend
> + * count: On entry: how many entries required in previous level, scales our end index
> + * On exit: returns how many entries required for next page table level
> *
If you make 'count' the number of /additional/ entries, you no longer
have to add/sub #1 each time.
> - * Preserves: tbl, flags
> - * Corrupts: phys, start, end, tmp, pstate
> + * Preserves: vstart, vend, shift, ptrs
> + * Returns: istart, iend, count
> */
> - .macro create_block_map, tbl, flags, phys, start, end, tmp
> - lsr \start, \start, #SWAPPER_BLOCK_SHIFT
> - and \start, \start, #PTRS_PER_PTE - 1 // table index
> - bic \phys, \phys, #SWAPPER_BLOCK_SIZE - 1
> - lsr \end, \end, #SWAPPER_BLOCK_SHIFT
> - and \end, \end, #PTRS_PER_PTE - 1 // table end index
> -9999: phys_to_pte \phys, \tmp
> - orr \tmp, \tmp, \flags // table entry
> - str \tmp, [\tbl, \start, lsl #3] // store the entry
> - add \start, \start, #1 // next entry
> - add \phys, \phys, #SWAPPER_BLOCK_SIZE // next block
> - cmp \start, \end
> - b.ls 9999b
> + .macro compute_indices, vstart, vend, shift, ptrs, istart, iend, count
> + lsr \iend, \vend, \shift
> + mov \istart, \ptrs
> + sub \istart, \istart, #1
> + and \iend, \iend, \istart // iend = (vend >> shift) & (ptrs - 1)
> + mov \istart, \ptrs
> + sub \count, \count, #1
> + mul \istart, \istart, \count
> + add \iend, \iend, \istart // iend += (count - 1) * ptrs
> + // our entries span multiple tables
> +
> + lsr \istart, \vstart, \shift
> + mov \count, \ptrs
> + sub \count, \count, #1
> + and \istart, \istart, \count
> +
> + sub \count, \iend, \istart
> + add \count, \count, #1
> + .endm
> +
You can simplify this macro by using an immediate for \ptrs. Please
see the diff below [whitespace mangling courtesy of Gmail]
> +/*
> + * Map memory for specified virtual address range. Each level of page table needed supports
> + * multiple entries. If a level requires n entries the next page table level is assumed to be
> + * formed from n pages.
> + *
> + * tbl: location of page table
> + * rtbl: address to be used for first level page table entry (typically tbl + PAGE_SIZE)
> + * vstart: start address to map
> + * vend: end address to map - we map [vstart, vend]
> + * flags: flags to use to map last level entries
> + * phys: physical address corresponding to vstart - physical memory is contiguous
> + * pgds: the number of pgd entries
> + *
> + * Temporaries: istart, iend, tmp, count, sv - these need to be different registers
> + * Preserves: vstart, vend, flags
> + * Corrupts: tbl, rtbl, istart, iend, tmp, count, sv
> + */
> + .macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
> + add \rtbl, \tbl, #PAGE_SIZE
> + mov \sv, \rtbl
> + mov \count, #1
#0 if you make \count the number of additional entries.
In any case, for the series:
Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> + compute_indices \vstart, \vend, #PGDIR_SHIFT, \pgds, \istart, \iend, \count
> + populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
> + mov \tbl, \sv
> + mov \sv, \rtbl
> +
> +#if SWAPPER_PGTABLE_LEVELS > 3
> + compute_indices \vstart, \vend, #PUD_SHIFT, #PTRS_PER_PUD, \istart, \iend, \count
> + populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
> + mov \tbl, \sv
> + mov \sv, \rtbl
> +#endif
> +
> +#if SWAPPER_PGTABLE_LEVELS > 2
> + compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #PTRS_PER_PMD, \istart, \iend, \count
> + populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
> + mov \tbl, \sv
> +#endif
> +
> + compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #PTRS_PER_PTE, \istart, \iend, \count
> + bic \count, \phys, #SWAPPER_BLOCK_SIZE - 1
> + populate_entries \tbl, \count, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
> .endm
>
> /*
> @@ -246,14 +312,16 @@ __create_page_tables:
> * dirty cache lines being evicted.
> */
> adrp x0, idmap_pg_dir
> - ldr x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
> + adrp x1, swapper_pg_end
> + sub x1, x1, x0
> bl __inval_dcache_area
>
> /*
> * Clear the idmap and swapper page tables.
> */
> adrp x0, idmap_pg_dir
> - ldr x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
> + adrp x1, swapper_pg_end
> + sub x1, x1, x0
> 1: stp xzr, xzr, [x0], #16
> stp xzr, xzr, [x0], #16
> stp xzr, xzr, [x0], #16
> @@ -318,10 +386,10 @@ __create_page_tables:
> #endif
> 1:
> ldr_l x4, idmap_ptrs_per_pgd
> - create_pgd_entry x0, x3, x4, x5, x6
> mov x5, x3 // __pa(__idmap_text_start)
> adr_l x6, __idmap_text_end // __pa(__idmap_text_end)
> - create_block_map x0, x7, x3, x5, x6, x4
> +
> + map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
>
> /*
> * Map the kernel image (starting with PHYS_OFFSET).
> @@ -330,12 +398,12 @@ __create_page_tables:
> mov_q x5, KIMAGE_VADDR + TEXT_OFFSET // compile time __va(_text)
> add x5, x5, x23 // add KASLR displacement
> mov x4, PTRS_PER_PGD
> - create_pgd_entry x0, x5, x4, x3, x6
> adrp x6, _end // runtime __pa(_end)
> adrp x3, _text // runtime __pa(_text)
> sub x6, x6, x3 // _end - _text
> add x6, x6, x5 // runtime __va(_end)
> - create_block_map x0, x7, x3, x5, x6, x4
> +
> + map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14
>
> /*
> * Since the page tables have been populated with non-cacheable
> @@ -343,7 +411,8 @@ __create_page_tables:
> * tables again to remove any speculatively loaded cache lines.
> */
> adrp x0, idmap_pg_dir
> - ldr x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
> + adrp x1, swapper_pg_end
> + sub x1, x1, x0
> dmb sy
> bl __inval_dcache_area
>
> diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
> index 4c7112a47469..0221aca6493d 100644
> --- a/arch/arm64/kernel/vmlinux.lds.S
> +++ b/arch/arm64/kernel/vmlinux.lds.S
> @@ -230,6 +230,7 @@ SECTIONS
> #endif
> swapper_pg_dir = .;
> . += SWAPPER_DIR_SIZE;
> + swapper_pg_end = .;
>
> __pecoff_data_size = ABSOLUTE(. - __initdata_begin);
> _end = .;
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index 4071602031ed..fdac11979bae 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -644,7 +644,8 @@ void __init paging_init(void)
> * allocated with it.
> */
> memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE,
> - SWAPPER_DIR_SIZE - PAGE_SIZE);
> + __pa_symbol(swapper_pg_end) - __pa_symbol(swapper_pg_dir)
> + - PAGE_SIZE);
> }
>
> /*
> --
> 2.11.0
>
------8<---------
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 539e2642ed41..0432dd8d083c 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -235,9 +235,7 @@ ENDPROC(preserve_boot_args)
*/
.macro compute_indices, vstart, vend, shift, ptrs, istart, iend, count
lsr \iend, \vend, \shift
- mov \istart, \ptrs
- sub \istart, \istart, #1
- and \iend, \iend, \istart // iend = (vend >> shift) & (ptrs - 1)
+ and \iend, \iend, \ptrs - 1 // iend = (vend >> shift) & (ptrs - 1)
mov \istart, \ptrs
sub \count, \count, #1
mul \istart, \istart, \count
@@ -245,9 +243,7 @@ ENDPROC(preserve_boot_args)
// our entries span multiple tables
lsr \istart, \vstart, \shift
- mov \count, \ptrs
- sub \count, \count, #1
- and \istart, \istart, \count
+ and \istart, \istart, \ptrs - 1
sub \count, \iend, \istart
add \count, \count, #1
@@ -376,6 +372,7 @@ __create_page_tables:
mov x4, EXTRA_PTRS
create_table_entry x0, x3, EXTRA_SHIFT, x4, x5, x6
+ .set .Lidmap_ptrs_per_pgd, PTRS_PER_PGD
#else
/*
* If VA_BITS == 48, we don't have to configure an additional
@@ -383,13 +380,13 @@ __create_page_tables:
*/
mov x4, #1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT)
str_l x4, idmap_ptrs_per_pgd, x5
+ .set .Lidmap_ptrs_per_pgd, 1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT)
#endif
1:
- ldr_l x4, idmap_ptrs_per_pgd
mov x5, x3 // __pa(__idmap_text_start)
adr_l x6, __idmap_text_end // __pa(__idmap_text_end)
- map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
+ map_memory x0, x1, x3, x6, x7, x3, .Lidmap_ptrs_per_pgd, x10,
x11, x12, x13, x14
/*
* Map the kernel image (starting with PHYS_OFFSET).
@@ -397,13 +394,12 @@ __create_page_tables:
adrp x0, swapper_pg_dir
mov_q x5, KIMAGE_VADDR + TEXT_OFFSET // compile time __va(_text)
add x5, x5, x23 // add KASLR displacement
- mov x4, PTRS_PER_PGD
adrp x6, _end // runtime __pa(_end)
adrp x3, _text // runtime __pa(_text)
sub x6, x6, x3 // _end - _text
add x6, x6, x5 // runtime __va(_end)
- map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14
+ map_memory x0, x1, x5, x6, x7, x3, PTRS_PER_PGD, x10, x11, x12, x13, x14
/*
* Since the page tables have been populated with non-cacheable
^ permalink raw reply related
* [RESEND PATCH v2 09/15] ASoC: qcom: qdsp6: Add support to Q6CORE
From: Bjorn Andersson @ 2018-01-02 22:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-10-srinivas.kandagatla@linaro.org>
On Thu 14 Dec 09:33 PST 2017, srinivas.kandagatla at linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>
> This patch adds support to core apr service, which is used to query
> status of other static and dynamic services on the dsp.
>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
> sound/soc/qcom/Kconfig | 5 +
> sound/soc/qcom/qdsp6/Makefile | 1 +
> sound/soc/qcom/qdsp6/q6core.c | 227 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 233 insertions(+)
> create mode 100644 sound/soc/qcom/qdsp6/q6core.c
>
> diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
> index 7ebdb879a8a3..121b9c957024 100644
> --- a/sound/soc/qcom/Kconfig
> +++ b/sound/soc/qcom/Kconfig
> @@ -56,11 +56,16 @@ config SND_SOC_QDSP6_ASM
> tristate
> default n
>
> +config SND_SOC_QDSP6_CORE
> + tristate
> + default n
> +
> config SND_SOC_QDSP6
> tristate "SoC ALSA audio driver for QDSP6"
> select SND_SOC_QDSP6_AFE
> select SND_SOC_QDSP6_ADM
> select SND_SOC_QDSP6_ASM
> + select SND_SOC_QDSP6_CORE
> help
> To add support for MSM QDSP6 Soc Audio.
> This will enable sound soc platform specific
> diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
> index 49dd3ccab27b..ad7f10691e54 100644
> --- a/sound/soc/qcom/qdsp6/Makefile
> +++ b/sound/soc/qcom/qdsp6/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
> obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
> obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
> +obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
> diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
> new file mode 100644
> index 000000000000..d4e2dbc62489
> --- /dev/null
> +++ b/sound/soc/qcom/qdsp6/q6core.c
> @@ -0,0 +1,227 @@
> +/* SPDX-License-Identifier: GPL-2.0
> +* Copyright (c) 2017, Linaro Limited
> +*/
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/jiffies.h>
> +#include <linux/wait.h>
> +#include <linux/soc/qcom/apr.h>
> +#include <linux/platform_device.h>
> +#include <sound/asound.h>
> +#include "common.h"
> +
> +#define ADSP_STATE_READY_TIMEOUT_MS 3000
> +#define Q6_READY_TIMEOUT_MS 100
> +#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C
> +#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
> +#define AVCS_GET_VERSIONS 0x00012905
> +#define AVCS_GET_VERSIONS_RSP 0x00012906
> +
> +struct avcs_svc_info {
> + uint32_t service_id;
> + uint32_t version;
> +} __packed;
> +
> +struct q6core {
> + struct apr_device *adev;
> + wait_queue_head_t wait;
> + uint32_t avcs_state;
> + int resp_received;
bool
> + uint32_t num_services;
> + struct avcs_svc_info *svcs_info;
> +};
> +
> +static struct apr_device_id static_services[] = {
> + ADSP_AUDIO_APR_DEV("AFE", APR_SVC_AFE),
> + ADSP_AUDIO_APR_DEV("ASM", APR_SVC_ASM),
> + ADSP_AUDIO_APR_DEV("ADM", APR_SVC_ADM),
> + ADSP_AUDIO_APR_DEV("TEST", APR_SVC_TEST_CLIENT),
> + ADSP_AUDIO_APR_DEV("MVM", APR_SVC_ADSP_MVM),
> + ADSP_AUDIO_APR_DEV("CVS", APR_SVC_ADSP_CVS),
> + ADSP_AUDIO_APR_DEV("CVP", APR_SVC_ADSP_CVP),
> + ADSP_AUDIO_APR_DEV("USM", APR_SVC_USM),
> + ADSP_AUDIO_APR_DEV("VIDC", APR_SVC_VIDC),
> + ADSP_AUDIO_APR_DEV("LSM", APR_SVC_LSM),
> +};
> +
> +static int core_callback(struct apr_device *adev, struct apr_client_data *data)
> +{
> + struct q6core *core = dev_get_drvdata(&adev->dev);
> + uint32_t *payload;
> +
> + switch (data->opcode) {
> + case AVCS_GET_VERSIONS_RSP:
> + payload = data->payload;
> + core->num_services = payload[1];
Describe the payload in a struct (with flexible array member for the
svcs_info list).
> +
> + if (!core->svcs_info)
> + core->svcs_info = kcalloc(core->num_services,
> + sizeof(*core->svcs_info),
> + GFP_ATOMIC);
> + if (!core->svcs_info)
> + return -ENOMEM;
> +
If we receive this twice with different num_services for some reason the
memcpy might trash the heap.
But as this is the get_version response and we're only going to issue
that once you should remove the check for !core->svcs_info above.
And don't forget to free svcs_info once you have added your services.
> + /* svc info is after 8 bytes */
> + memcpy(core->svcs_info, payload + 2,
> + core->num_services * sizeof(*core->svcs_info));
> +
> + core->resp_received = 1;
> + wake_up(&core->wait);
> +
> + break;
> + case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
> + payload = data->payload;
> + core->avcs_state = payload[0];
> +
> + core->resp_received = 1;
> + wake_up(&core->wait);
> + break;
> + default:
> + dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
> + data->opcode);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +void q6core_add_service(struct device *dev, uint32_t svc_id, uint32_t version)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(static_services); i++) {
> + if (static_services[i].svc_id == svc_id) {
> + static_services[i].svc_version = version;
> + apr_add_device(dev->parent, &static_services[i]);
> + dev_info(dev,
Please don't spam the logs, dev_dbg() should be enough. And as
apr_add_device() returns you can find the devices registered in /sys
> + "Adding SVC: %s: id 0x%x API Ver 0x%x:0x%x\n",
> + static_services[i].name, svc_id,
> + APR_SVC_MAJOR_VERSION(version),
> + APR_SVC_MINOR_VERSION(version));
> + }
> + }
> +}
> +
> +static void q6core_add_static_services(struct q6core *core)
The name of this function is deceiving, it doesn't really add the static
services. It adds devices for the services that we've been informed
exists, by the other side - using the static list of services.
Per the comment on a previous patch I don't think the "name" in
apr_device_id provides any real value and in this case if forces you to
perform a lookup using this table.
If you drop the name, you can loop over the list of service ids returned
from the remote and just register them with a hard coded domain id
(based on apr instance?) and client_id. You don't need the lookup table.
> +{
> + int i;
> + struct apr_device *adev = core->adev;
> + struct avcs_svc_info *svcs_info = core->svcs_info;
> +
> + for (i = 0; i < core->num_services; i++)
> + q6core_add_service(&adev->dev, svcs_info[i].service_id,
> + svcs_info[i].version);
> +}
> +
> +static int q6core_get_svc_versions(struct q6core *core)
> +{
> + struct apr_device *adev = core->adev;
> + struct apr_hdr hdr = {0};
> + int rc;
> +
> + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
> + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
> + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
> + hdr.opcode = AVCS_GET_VERSIONS;
> +
> + rc = apr_send_pkt(adev, (uint32_t *)&hdr);
> + if (rc < 0)
> + return rc;
> +
> + rc = wait_event_timeout(core->wait, (core->resp_received == 1),
> + msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
The wait and resp_received could favourably be expressed as a completion
instead, as all we care about is that this happened once.
> + if (rc > 0 && core->resp_received) {
> + core->resp_received = 0;
> + return 0;
> + }
It wasn't obvious at first sight that this is the success case and the
return rc below was the error case...
> +
> + return rc;
And this will actually be 0 if core->resp_received has not become 1 at
the timeout.
> +}
> +
> +static bool q6core_is_adsp_ready(struct q6core *core)
> +{
> + struct apr_device *adev = core->adev;
> + struct apr_hdr hdr = {0};
> + int rc;
> +
> + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
> + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
> + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
> + hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
> +
> + rc = apr_send_pkt(adev, (uint32_t *)&hdr);
> + if (rc < 0)
> + return false;
> +
> + rc = wait_event_timeout(core->wait, (core->resp_received == 1),
> + msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
I think this too would be nicer to describe with a completion.
Currently it's possible to ask is the dsp is ready, time out and ask
again, just to receive the first ack and continue. The service request
sleep might then wake up on this previous ack.
If you describe this by two different completions for the two waits you
avoid any such race conditions occurring.
> + if (rc > 0 && core->resp_received) {
> + core->resp_received = 0;
> + if (core->avcs_state == 0x1)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static int q6core_probe(struct apr_device *adev)
> +{
> + struct q6core *core;
> + unsigned long timeout = jiffies +
> + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
> + int ret = 0;
> +
> + core = devm_kzalloc(&adev->dev, sizeof(*core), GFP_KERNEL);
> + if (!core)
> + return -ENOMEM;
> +
> + dev_set_drvdata(&adev->dev, core);
> +
> + core->adev = adev;
> + init_waitqueue_head(&core->wait);
> +
> + do {
> + if (!q6core_is_adsp_ready(core)) {
> + dev_info(&adev->dev, "ADSP Audio isn't ready\n");
> + } else {
> + dev_info(&adev->dev, "ADSP Audio is ready\n");
> +
> + ret = q6core_get_svc_versions(core);
> + if (!ret)
> + q6core_add_static_services(core);
> +
> + break;
> + }
> + } while (time_after(timeout, jiffies));
This would be much better rewritten as:
for (;;) {
if (q6core_is_adsp_ready(core))
break;
if (time_after(timeout, jiffies))
return -ETIMEDOUT;
}
ret = q6core_get_svc_versions(core);
if (ret)
return ret;
q6core_add_static_services(core);
> +
> + return ret;
> +}
> +
> +static int q6core_exit(struct apr_device *adev)
> +{
> + return 0;
> +}
> +
> +static const struct apr_device_id core_id[] = {
> + {"Q6CORE", APR_DOMAIN_ADSP, APR_SVC_ADSP_CORE, APR_CLIENT_AUDIO},
> + { },
> +};
> +
> +static struct apr_driver qcom_q6core_driver = {
> + .probe = q6core_probe,
> + .remove = q6core_exit,
> + .callback = core_callback,
> + .id_table = core_id,
> + .driver = {
> + .name = "qcom-q6core",
> + },
Indentation.
> +};
> +
> +module_apr_driver(qcom_q6core_driver);
> +
> +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
> +MODULE_DESCRIPTION("q6 core");
> +MODULE_LICENSE("GPL v2");
> --
> 2.15.0
>
^ permalink raw reply
* [PATCH] ARM: dts: n900: Add aliases for lcd and tvout displays
From: Ivaylo Dimitrov @ 2018-01-02 22:17 UTC (permalink / raw)
To: linux-arm-kernel
When both lcd and tv are enabled, the order in which they will be probed is
unknown, so it might happen (and it happens in reality) that tv is
configured as display0 and lcd as display1, which results in nothing
displayed on lcd, as display1 is disabled by default.
Fix that by providing correct aliases for lcd and tv
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
arch/arm/boot/dts/omap3-n900.dts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 669c51c..4b9c3a7 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -35,6 +35,8 @@
i2c1 = &i2c1;
i2c2 = &i2c2;
i2c3 = &i2c3;
+ display0 = &lcd;
+ display1 = &tv;
};
cpus {
@@ -965,7 +967,7 @@
ti,esd-recovery-timeout-ms = <8000>;
};
- acx565akm at 2 {
+ lcd: acx565akm at 2 {
compatible = "sony,acx565akm";
spi-max-frequency = <6000000>;
reg = <2>;
--
1.9.1
^ permalink raw reply related
* [PATCH v3 09/34] clk: bcm2835: change clk_get_rate() helper return type
From: Eric Anholt @ 2018-01-02 22:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1514835793-1104-10-git-send-email-pure.logic@nexus-software.ie>
Bryan O'Donoghue <pure.logic@nexus-software.ie> writes:
> bcm2835_pll_rate_from_divisor returns a long but the function calling it
> returns an unsigned long. There's no reason to have a type disparity here
> so tidy up the return type of bcm2835_pll_rate_from_divisor() from signed
> to unsigned long.
I'm still surprised that clocks are using longs instead of u64s, but
this seems like a fine change. For the 2 bcm2835 patches,
Reviewed-by: Eric Anholt <eric@anholt.net>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180102/622b6e3d/attachment.sig>
^ permalink raw reply
* [PATCH v2] PCI: imx6: Add PHY reference clock source support
From: Fabio Estevam @ 2018-01-02 22:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1514709094-4110-1-git-send-email-ilya@compulab.co.il>
Hi Ilya,
+ Rob and dt list
On Sun, Dec 31, 2017 at 6:31 AM, Ilya Ledvich <ilya@compulab.co.il> wrote:
> i.MX7D variant of the IP can use either Crystal Oscillator input
> or internal clock input as a Reference Clock input for PCIe PHY.
> Add support for an optional property 'pcie-phy-refclk-internal'.
> If present then an internal clock input is used as PCIe PHY
> reference clock source. By default an external oscillator input
> is still used.
>
> Verified on Compulab SBC-iMX7 Single Board Computer.
>
> Signed-off-by: Ilya Ledvich <ilya@compulab.co.il>
> ---
> Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt | 5 +++++
> drivers/pci/dwc/pci-imx6.c | 8 +++++++-
> 2 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
> index 7b1e48b..581bc09 100644
> --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
> @@ -50,6 +50,11 @@ Additional required properties for imx7d-pcie:
> - "pciephy"
> - "apps"
>
> +Additional optional properties for imx7d-pcie:
> +- pcie-phy-refclk-internal: If present then an internal PLL input is used as
> + PCIe PHY reference clock source. By default an external oscillator input
> + is used.
Should this contain the vendor prefix, like fsl,pcie-phy-refclk-internal ?
^ permalink raw reply
* [RESEND PATCH v2 10/15] ASoC: qcom: qdsp6: Add support to q6routing driver
From: Bjorn Andersson @ 2018-01-02 23:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-11-srinivas.kandagatla@linaro.org>
On Thu 14 Dec 09:33 PST 2017, srinivas.kandagatla at linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>
> This patch adds support to q6 routing driver which configures route
> between ASM and AFE module using ADM apis.
>
> This driver uses dapm widgets to setup the matrix between AFE ports and
> ASM streams.
>
Why is this a separate driver from the q6adm?
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
> sound/soc/qcom/Kconfig | 5 +
> sound/soc/qcom/qdsp6/Makefile | 1 +
> sound/soc/qcom/qdsp6/q6routing.c | 386 +++++++++++++++++++++++++++++++++++++++
> sound/soc/qcom/qdsp6/q6routing.h | 9 +
> 4 files changed, 401 insertions(+)
> create mode 100644 sound/soc/qcom/qdsp6/q6routing.c
> create mode 100644 sound/soc/qcom/qdsp6/q6routing.h
>
> diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
> index 121b9c957024..dd8fb0cde614 100644
> --- a/sound/soc/qcom/Kconfig
> +++ b/sound/soc/qcom/Kconfig
> @@ -60,12 +60,17 @@ config SND_SOC_QDSP6_CORE
> tristate
> default n
>
> +config SND_SOC_QDSP6_ROUTING
> + tristate
> + default n
> +
> config SND_SOC_QDSP6
> tristate "SoC ALSA audio driver for QDSP6"
> select SND_SOC_QDSP6_AFE
> select SND_SOC_QDSP6_ADM
> select SND_SOC_QDSP6_ASM
> select SND_SOC_QDSP6_CORE
> + select SND_SOC_QDSP6_ROUTING
> help
> To add support for MSM QDSP6 Soc Audio.
> This will enable sound soc platform specific
> diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
> index ad7f10691e54..c1ad060a2341 100644
> --- a/sound/soc/qcom/qdsp6/Makefile
> +++ b/sound/soc/qcom/qdsp6/Makefile
> @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
> obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
> obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
> obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
> +obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
> diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
> new file mode 100644
> index 000000000000..f5f12d61a1ee
> --- /dev/null
> +++ b/sound/soc/qcom/qdsp6/q6routing.c
> @@ -0,0 +1,386 @@
> +/* SPDX-License-Identifier: GPL-2.0
> +* Copyright (c) 2011-2016, The Linux Foundation
> +* Copyright (c) 2017, Linaro Limited
> +*/
> +#include <linux/init.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/bitops.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <sound/core.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/pcm.h>
> +#include <sound/control.h>
> +#include <sound/asound.h>
> +#include <sound/pcm_params.h>
> +#include "q6afe.h"
> +#include "q6asm.h"
> +#include "q6adm.h"
> +#include "q6routing.h"
> +
> +struct session_data {
> + int state;
> + int port_id;
> + int path_type;
> + int app_type;
> + int acdb_id;
> + int sample_rate;
> + int bits_per_sample;
> + int channels;
> + int format;
> + int perf_mode;
> + int numcopps;
> + int fedai_id;
> + unsigned long copp_map;
> +};
> +
> +struct msm_routing_data {
> + struct session_data sessions[MAX_SESSIONS];
> + struct device *dev;
> + struct mutex lock;
> +};
> +
> +static struct msm_routing_data *routing_data;
> +
> +/**
> + * q6routing_reg_phy_stream() - Register a new stream for route setup
> + *
> + * @fedai_id: Frontend dai id.
> + * @perf_mode: Performace mode.
"Performance"
> + * @stream_id: ASM stream id to map.
> + * @stream_type: Direction of stream
> + *
> + * Return: Will be an negative on error or a zero on success.
> + */
> +int q6routing_reg_phy_stream(int fedai_id, int perf_mode,
q6routing_stream_open() ?
> + int stream_id, int stream_type)
> +{
> + int j, topology, num_copps = 0;
> + struct route_payload payload;
> + int copp_idx;
> + struct session_data *session;
> +
> + if (!routing_data) {
> + pr_err("Routing driver not yet ready\n");
> + return -EINVAL;
> + }
> +
> + session = &routing_data->sessions[stream_id - 1];
> + mutex_lock(&routing_data->lock);
> + session->fedai_id = fedai_id;
> + payload.num_copps = 0; /* only RX needs to use payload */
> + topology = NULL_COPP_TOPOLOGY;
> + copp_idx = q6adm_open(routing_data->dev, session->port_id,
> + session->path_type, session->sample_rate,
> + session->channels, topology, perf_mode,
> + session->bits_per_sample, 0, 0);
> + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
Make q6adm_open() not return >= MAX_COPPS_PER_PORT.
And drop the extra parenthesis.
> + mutex_unlock(&routing_data->lock);
> + return -EINVAL;
> + }
> +
> + set_bit(copp_idx, &session->copp_map);
> + for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
Use for_each_set_bit()
> + unsigned long copp = session->copp_map;
> +
> + if (test_bit(j, &copp)) {
> + payload.port_id[num_copps] = session->port_id;
> + payload.copp_idx[num_copps] = j;
> + num_copps++;
> + }
> + }
> +
> + if (num_copps) {
> + payload.num_copps = num_copps;
> + payload.session_id = stream_id;
> + q6adm_matrix_map(routing_data->dev, session->path_type,
> + payload, perf_mode);
> + }
> + mutex_unlock(&routing_data->lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(q6routing_reg_phy_stream);
> +
> +static struct session_data *routing_get_session(struct msm_routing_data *data,
> + int port_id, int port_type)
port_type is ignored
> +{
> + int i;
> +
> + for (i = 0; i < MAX_SESSIONS; i++)
> + if (port_id == data->sessions[i].port_id)
> + return &data->sessions[i];
> +
> + return NULL;
> +}
> +
> +static struct session_data *get_session_from_id(struct msm_routing_data *data,
> + int fedai_id)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_SESSIONS; i++) {
> + if (fedai_id == data->sessions[i].fedai_id)
> + return &data->sessions[i];
> + }
> +
> + return NULL;
> +}
> +/**
> + * q6routing_dereg_phy_stream() - Deregister a stream
> + *
> + * @fedai_id: Frontend dai id.
> + * @stream_type: Direction of stream
> + *
> + * Return: Will be an negative on error or a zero on success.
> + */
> +void q6routing_dereg_phy_stream(int fedai_id, int stream_type)
q6routing_stream_close()?
> +{
> + struct session_data *session;
> + int idx;
> +
> + session = get_session_from_id(routing_data, fedai_id);
> + if (!session)
> + return;
> +
> + for_each_set_bit(idx, &session->copp_map, MAX_COPPS_PER_PORT)
> + q6adm_close(routing_data->dev, session->port_id,
> + session->perf_mode, idx);
> +
> + session->fedai_id = -1;
> + session->copp_map = 0;
> +}
> +EXPORT_SYMBOL_GPL(q6routing_dereg_phy_stream);
> +
> +static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_dapm_context *dapm =
> + snd_soc_dapm_kcontrol_dapm(kcontrol);
> + struct soc_mixer_control *mc =
> + (struct soc_mixer_control *)kcontrol->private_value;
> + int session_id = mc->shift;
> + struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm);
> + struct msm_routing_data *priv = snd_soc_platform_get_drvdata(platform);
> + struct session_data *session = &priv->sessions[session_id];
> +
> + if (session->port_id != -1)
> + ucontrol->value.integer.value[0] = 1;
> + else
> + ucontrol->value.integer.value[0] = 0;
> +
> + return 0;
> +}
> +
> +static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_dapm_context *dapm =
> + snd_soc_dapm_kcontrol_dapm(kcontrol);
> + struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm);
> + struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform);
> + struct soc_mixer_control *mc =
> + (struct soc_mixer_control *)kcontrol->private_value;
> + struct snd_soc_dapm_update *update = NULL;
> + int be_id = mc->reg;
> + int session_id = mc->shift;
> + struct session_data *session = &data->sessions[session_id];
> +
> + if (ucontrol->value.integer.value[0]) {
> + session->port_id = be_id;
> + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update);
> + } else {
> + session->port_id = -1;
> + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update);
> + }
> +
> + return 1;
> +}
> +
> +static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
> + SOC_SINGLE_EXT("MultiMedia1", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia2", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia3", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia4", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia5", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia6", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia7", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> + SOC_SINGLE_EXT("MultiMedia8", AFE_PORT_HDMI_RX,
> + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0,
> + msm_routing_get_audio_mixer,
> + msm_routing_put_audio_mixer),
> +};
> +
> +static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
> + /* Frontend AIF */
> + /* Widget name equals to Front-End DAI name<Need confirmation>,
Perhaps this should be confirmed and the comment updated?
> + * Stream name must contains substring of front-end dai name
> + */
> + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
> + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
> +
> + /* Mixer definitions */
> + SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
> + hdmi_mixer_controls,
> + ARRAY_SIZE(hdmi_mixer_controls)),
> +};
> +
> +static const struct snd_soc_dapm_route intercon[] = {
> + {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
> + {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
> + {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
> + {"HDMI Mixer", "MultiMedia4", "MM_DL4"},
> + {"HDMI Mixer", "MultiMedia5", "MM_DL5"},
> + {"HDMI Mixer", "MultiMedia6", "MM_DL6"},
> + {"HDMI Mixer", "MultiMedia7", "MM_DL7"},
> + {"HDMI Mixer", "MultiMedia8", "MM_DL8"},
> + {"HDMI", NULL, "HDMI Mixer"},
> + {"HDMI-RX", NULL, "HDMI"},
> +};
> +
> +static int routing_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + unsigned int be_id = rtd->cpu_dai->id;
> + struct snd_soc_platform *platform = rtd->platform;
> + struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform);
> + struct session_data *session;
> + int port_id, port_type, path_type, bits_per_sample;
bits_per_sample is likely unused.
> +
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + path_type = ADM_PATH_PLAYBACK;
> + port_type = MSM_AFE_PORT_TYPE_RX;
> + }
> +
> + port_id = be_id;
Why alias this variable?
> +
> + session = routing_get_session(data, port_id, port_type);
> +
> + if (!session) {
> + pr_err("No session matrix setup yet..\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&data->lock);
> +
> + session->path_type = path_type;
> + session->sample_rate = params_rate(params);
> + session->channels = params_channels(params);
> + session->format = params_format(params);
> +
> + if (session->format == SNDRV_PCM_FORMAT_S16_LE)
> + session->bits_per_sample = 16;
> + else if (session->format == SNDRV_PCM_FORMAT_S24_LE)
> + bits_per_sample = 24;
session-> ?
Perhaps switch on params_format(params) instead? And fail in the default
case.
> +
> + mutex_unlock(&data->lock);
> + return 0;
> +}
> +
> +static int routing_close(struct snd_pcm_substream *substream)
> +{
> + return 0;
> +}
> +
> +static int routing_prepare(struct snd_pcm_substream *substream)
> +{
> + return 0;
> +}
> +
> +static struct snd_pcm_ops q6pcm_routing_ops = {
> + .hw_params = routing_hw_params,
> + .close = routing_close,
> + .prepare = routing_prepare,
> +};
> +
> +/* Not used but frame seems to require it */
Remove comment?
> +static int msm_routing_probe(struct snd_soc_platform *platform)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_SESSIONS; i++)
> + routing_data->sessions[i].port_id = -1;
> +
> + snd_soc_platform_set_drvdata(platform, routing_data);
> +
> + return 0;
> +}
> +
> +static struct snd_soc_platform_driver msm_soc_routing_platform = {
> + .ops = &q6pcm_routing_ops,
> + .probe = msm_routing_probe,
> + .component_driver = {
> + .dapm_widgets = msm_qdsp6_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets),
> + .dapm_routes = intercon,
> + .num_dapm_routes = ARRAY_SIZE(intercon),
> + },
> +};
> +
> +static int q6pcm_routing_probe(struct platform_device *pdev)
> +{
> +
> + routing_data = devm_kzalloc(&pdev->dev,
> + sizeof(*routing_data), GFP_KERNEL);
> + if (!routing_data)
> + return -ENOMEM;
> +
> + routing_data->dev = &pdev->dev;
> +
> + mutex_init(&routing_data->lock);
> + dev_set_drvdata(&pdev->dev, routing_data);
> +
> + return devm_snd_soc_register_platform(&pdev->dev,
> + &msm_soc_routing_platform);
> +}
> +
> +static int q6pcm_routing_remove(struct platform_device *pdev)
> +{
As you return here routing_data will be freed. The early check in
q6routing_reg_phy_stream() seems to indicate that this driver can be
called even though the routing device isn't available.
So you probably want to clear that variable, at least.
> + return 0;
> +}
> +
> +static struct platform_driver q6pcm_routing_driver = {
> + .driver = {
> + .name = "q6routing",
> + .owner = THIS_MODULE,
Drop .owner
> + },
> + .probe = q6pcm_routing_probe,
> + .remove = q6pcm_routing_remove,
> +};
> +
> +module_platform_driver(q6pcm_routing_driver);
> +
> +MODULE_DESCRIPTION("Q6 Routing platform");
> +MODULE_LICENSE("GPL v2");
> diff --git a/sound/soc/qcom/qdsp6/q6routing.h b/sound/soc/qcom/qdsp6/q6routing.h
> new file mode 100644
> index 000000000000..7f0feb196acc
> --- /dev/null
> +++ b/sound/soc/qcom/qdsp6/q6routing.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _Q6_PCM_ROUTING_H
> +#define _Q6_PCM_ROUTING_H
> +
> +int q6routing_reg_phy_stream(int fedai_id, int perf_mode,
> + int stream_id, int stream_type);
> +void q6routing_dereg_phy_stream(int fedai_id, int stream_type);
> +
> +#endif /*_Q6_PCM_ROUTING_H */
> --
> 2.15.0
>
^ permalink raw reply
* [PATCH 01/33] clk_ops: change round_rate() to return unsigned long
From: Stephen Boyd @ 2018-01-02 23:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c2212d56-a8b5-cba5-46a7-c2c7f66e752b@nexus-software.ie>
On 01/02, Bryan O'Donoghue wrote:
> On 02/01/18 19:01, Stephen Boyd wrote:
> >On 12/31, Bryan O'Donoghue wrote:
> >>On 30/12/17 16:36, Mikko Perttunen wrote:
> >>>FWIW, we had this problem some years ago with the Tegra CPU clock
> >>>- then it was determined that a simpler solution was to have the
> >>>determine_rate callback support unsigned long rates - so clock
> >>>drivers that need to return rates higher than 2^31 can instead
> >>>implement the determine_rate callback. That is what's currently
> >>>implemented.
> >>>
> >>>Mikko
> >>
> >>Granted we could work around it but, having both zero and less than
> >>zero indicate error means you can't support larger than LONG_MAX
> >>which is I think worth fixing.
> >>
> >
> >Ok. But can you implement the determine_rate op instead of the
> >round_rate op for your clk?
>
> Don't know .
Please try.
>
> >It's not a work-around, it's the
> >preferred solution. That would allow rates larger than 2^31 for
> >the clk without pushing through a change to all the drivers to
> >express zero as "error" and non-zero as the rounded rate.
> >
> >I'm not entirely opposed to this approach, because we probably
> >don't care to pass the particular error value from a clk provider
> >to a clk consumer about what the error is.
>
> Which was my thought. The return value of clk_ops->round_rate()
> appears not to get pushed up the stack, which is what the last patch
> in this series deals with.
>
> [PATCH 33/33] clk: change handling of round_rate() such that only
> zero is an error
Hmm? clk_core_determine_round_nolock() returns 'rate' if rate < 0
from the round_rate op. clk_core_round_rate_nolock() returns that
value to clk_round_rate() which returns it to the consumer.
>
> >It's actually what we
> >proposed as the solution for clk_round_rate() to return values
> >larger than LONG_MAX to consumers. But doing that consumer API
> >change or this provider side change is going to require us to
> >evaluate all the consumers of these clks to make sure they don't
> >check for some error value that's less than zero. This series
> >does half the work,
>
> Do you mean users of clk_rounda_rate() ? I have a set of patches for
> that but wanted to separate that from clk_ops->round_rate() so as
> not to send ~70 patches out to LKML at once - even if they are in
> two blocks.
Ok. What have you done to the consumers of clk_round_rate()?
Made them treat 0 as an error instead of less than zero? The
documentation in clk.h needs to be updated. See this patch from
Paul Wamsley[1] for one proposed patch that went nowhere. Also
include Russell King please. It was also proposed to change the
function signature of clk_round_rate() to return unsigned long,
but that didn't go anywhere either.
>
> If so, I can publish that set too for reference.
>
> AFAICT on clk_ops->round_rate the last patch #33 ought to cover the
> usage of the return value of clk_ops->round_rate().
>
> Have I missed something ?
Hopefully not!
>
> >by changing the provider side, while ignoring
> >the consumer side and any potential fallout of the less than zero
> >to zero return value change.
> >
>
> Can you look at #33 ? I'm not sure if you saw that one.
>
Yeah I looked at it. From what I can tell it makes
clk_round_rate() return 0 now instead of whatever negative value
the clk_ops::round_rate function returns.
[1] https://lkml.kernel.org/r/alpine.DEB.2.02.1311251603310.23090 at tamien
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox