From 08a33805518e7845486f88287e8aace6f8439391 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Wed, 9 Mar 2016 11:30:12 -0800 Subject: iio: core: implement iio_device_{claim|release}_direct_mode() It is often the case that the driver wants to be sure a device stays in direct mode while it is executing a task or series of tasks. To accomplish this today, the driver performs this sequence: 1) take the device state lock, 2) verify it is not in a buffered mode, 3) execute some tasks, and 4) release that lock. This patch introduces a pair of helper functions that simplify these steps and make it more semantically expressive. iio_device_claim_direct_mode() If the device is not in any buffered mode it is guaranteed to stay that way until iio_release_direct_mode() is called. iio_device_release_direct_mode() Release the claim. Device is no longer guaranteed to stay in direct mode. Signed-off-by: Alison Schofield Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index b2b16772c651..0b2773ada0ba 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -527,6 +527,8 @@ void iio_device_unregister(struct iio_dev *indio_dev); int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev); void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev); int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp); +int iio_device_claim_direct_mode(struct iio_dev *indio_dev); +void iio_device_release_direct_mode(struct iio_dev *indio_dev); extern struct bus_type iio_bus_type; -- cgit From 49db08c358873af11ba3c25401de88156fa5d365 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 Feb 2016 15:36:07 +0100 Subject: chrdev: emit a warning when we go below dynamic major range Currently a dynamically allocated character device major is taken from 254 and downward. This mechanism is used for RTC, IIO and a few other subsystems. The kernel currently has no check prevening these dynamic allocations from eating into the assigned numbers at 233 and downward. In a recent test it was reported that so many dynamic device majors were used on a test server, that the major number for infiniband (231) was stolen. This occurred when allocating a new major number for GPIO chips. The error messages from the kernel were not helpful. (See: https://lkml.org/lkml/2016/2/14/124) This patch adds a defined lower limit of the dynamic major allocation region will henceforth emit a warning if we start to eat into the assigned numbers. It does not do any semantic changes and will not change the kernels behaviour: numbers will still continue to be stolen, but we will know from dmesg what is going on. This also updates the Documentation/devices.txt to clearly reflect that we are using this range of major numbers for dynamic allocation. Reported-by: Ying Huang Cc: Linus Torvalds Cc: Greg Kroah-Hartman Cc: Alan Cox Cc: Arnd Bergmann Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- Documentation/devices.txt | 6 +++--- fs/char_dev.c | 4 ++++ include/linux/fs.h | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index 87b4c5e82d39..0a3588a9798d 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -3099,9 +3099,9 @@ Your cooperation is appreciated. 129 = /dev/ipath_sma Device used by Subnet Management Agent 130 = /dev/ipath_diag Device used by diagnostics programs -234-239 UNASSIGNED - -240-254 char LOCAL/EXPERIMENTAL USE +234-254 char RESERVED FOR DYNAMIC ASSIGNMENT + Character devices that request a dynamic allocation of major number will + take numbers starting from 254 and downward. 240-254 block LOCAL/EXPERIMENTAL USE Allocated for local/experimental use. For devices not diff --git a/fs/char_dev.c b/fs/char_dev.c index 24b142569ca9..687471dc04a0 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -91,6 +91,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, break; } + if (i < CHRDEV_MAJOR_DYN_END) + pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range", + name, i); + if (i == 0) { ret = -EBUSY; goto out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 14a97194b34b..60082be96de8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2385,6 +2385,8 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev, /* fs/char_dev.c */ #define CHRDEV_MAJOR_HASH_SIZE 255 +/* Marks the bottom of the first segment of free char majors */ +#define CHRDEV_MAJOR_DYN_END 234 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); extern int register_chrdev_region(dev_t, unsigned, const char *); extern int __register_chrdev(unsigned int major, unsigned int baseminor, -- cgit From b3c1be1b789cca6d3e39c950dfed690f0511fe76 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 22 Jan 2016 11:28:07 -0500 Subject: base: isa: Remove X86_32 dependency Many motherboards utilize a LPC to ISA bridge in order to decode ISA-style port-mapped I/O addresses. This is particularly true for embedded motherboards supporting the PC/104 bus (a bus specification derived from ISA). These motherboards are now commonly running 64-bit x86 processors. The X86_32 dependency should be removed from the ISA bus configuration option in order to support these newer motherboards. A new config option, CONFIG_ISA_BUS, is introduced to allow for the compilation of the ISA bus driver independent of the CONFIG_ISA option. Devices which communicate via ISA-compatible buses can now be supported independent of the dependencies of the CONFIG_ISA option. Signed-off-by: William Breathitt Gray Reviewed-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- arch/x86/Kconfig | 6 ++++++ drivers/base/Makefile | 2 +- include/linux/isa.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 2dc18605831f..a5977986f38b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2472,10 +2472,16 @@ config ISA_DMA_API Enables ISA-style DMA support for devices requiring such controllers. If unsure, say Y. +config ISA_BUS + bool "ISA bus support" + help + Enables ISA bus support for devices requiring such controllers. + if X86_32 config ISA bool "ISA support" + depends on ISA_BUS ---help--- Find out whether you have ISA slots on your motherboard. ISA is the name of a bus system, i.e. the way the CPU talks to the other stuff diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 6b2a84e7f2be..4ebfb81cc7e9 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o -obj-$(CONFIG_ISA) += isa.o +obj-$(CONFIG_ISA_BUS) += isa.o obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o diff --git a/include/linux/isa.h b/include/linux/isa.h index b0270e3814c8..2a02862775eb 100644 --- a/include/linux/isa.h +++ b/include/linux/isa.h @@ -22,7 +22,7 @@ struct isa_driver { #define to_isa_driver(x) container_of((x), struct isa_driver, driver) -#ifdef CONFIG_ISA +#ifdef CONFIG_ISA_BUS int isa_register_driver(struct isa_driver *, unsigned int); void isa_unregister_driver(struct isa_driver *); #else -- cgit From f235541699bcf14fb8be797c6bc1d7106df0eb64 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 22 Jan 2016 01:32:26 -0500 Subject: export.h: allow for per-symbol configurable EXPORT_SYMBOL() Similar to include/generated/autoconf.h, include/generated/autoksyms.h will contain a list of defines for each EXPORT_SYMBOL() that we want active. The format is: #define __KSYM_ 1 This list will be auto-generated with another patch. For now we only include the preprocessor magic to automatically create or omit the corresponding struct kernel_symbol declaration. Given the content of include/generated/autoksyms.h may not be known in advance, an empty file is created early on to let the build proceed. Signed-off-by: Nicolas Pitre Acked-by: Rusty Russell --- Makefile | 2 ++ include/linux/export.h | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/Makefile b/Makefile index 916b26e999d8..451acbebee97 100644 --- a/Makefile +++ b/Makefile @@ -998,6 +998,8 @@ prepare2: prepare3 outputmakefile asm-generic prepare1: prepare2 $(version_h) include/generated/utsrelease.h \ include/config/auto.conf $(cmd_crmodverdir) + $(Q)test -e include/generated/autoksyms.h || \ + touch include/generated/autoksyms.h archprepare: archheaders archscripts prepare1 scripts_basic diff --git a/include/linux/export.h b/include/linux/export.h index 96e45ea463e7..77afdb2a2506 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -38,7 +38,7 @@ extern struct module __this_module; #ifdef CONFIG_MODULES -#ifndef __GENKSYMS__ +#if defined(__KERNEL__) && !defined(__GENKSYMS__) #ifdef CONFIG_MODVERSIONS /* Mark the CRC weak since genksyms apparently decides not to * generate a checksums for some symbols */ @@ -53,7 +53,7 @@ extern struct module __this_module; #endif /* For every exported symbol, place a struct in the __ksymtab section */ -#define __EXPORT_SYMBOL(sym, sec) \ +#define ___EXPORT_SYMBOL(sym, sec) \ extern typeof(sym) sym; \ __CRC_SYMBOL(sym, sec) \ static const char __kstrtab_##sym[] \ @@ -65,6 +65,24 @@ extern struct module __this_module; __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ = { (unsigned long)&sym, __kstrtab_##sym } +#ifdef CONFIG_TRIM_UNUSED_KSYMS + +#include +#include + +#define __EXPORT_SYMBOL(sym, sec) \ + __cond_export_sym(sym, sec, config_enabled(__KSYM_##sym)) +#define __cond_export_sym(sym, sec, conf) \ + ___cond_export_sym(sym, sec, conf) +#define ___cond_export_sym(sym, sec, enabled) \ + __cond_export_sym_##enabled(sym, sec) +#define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec) +#define __cond_export_sym_0(sym, sec) /* nothing */ + +#else +#define __EXPORT_SYMBOL ___EXPORT_SYMBOL +#endif + #define EXPORT_SYMBOL(sym) \ __EXPORT_SYMBOL(sym, "") -- cgit From c1a95fda2a40ae8c7aad3fa44fa7718a3710eb2d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 22 Jan 2016 13:41:57 -0500 Subject: kbuild: add fine grained build dependencies for exported symbols Like with kconfig options, we now have the ability to compile in and out individual EXPORT_SYMBOL() declarations based on the content of include/generated/autoksyms.h. However we don't want the entire world to be rebuilt whenever that file is touched. Let's apply the same build dependency trick used for CONFIG_* symbols where the time stamp of empty files whose paths matching those symbols is used to trigger fine grained rebuilds. In our case the key is the symbol name passed to EXPORT_SYMBOL(). However, unlike config options, we cannot just use fixdep to parse the source code for EXPORT_SYMBOL(ksym) because several variants exist and parsing them all in a separate tool, and keeping it in synch, is not trivially maintainable. Furthermore, there are variants such as EXPORT_SYMBOL_GPL(pci_user_read_config_##size); that are instanciated via a macro for which we can't easily determine the actual exported symbol name(s) short of actually running the preprocessor on them. Storing the symbol name string in a special ELF section doesn't work for targets that output assembly or preprocessed source. So the best way is really to leverage the preprocessor by having it output actual symbol names anchored by a special sequence that can be easily filtered out. Then the list of symbols is simply fed to fixdep to be merged with the other dependencies. That implies the preprocessor is executed twice for each source file. A previous attempt relied on a warning pragma for each EXPORT_SYMBOL() instance that was filtered apart from stderr by the build system with a sed script during the actual compilation pass. Unfortunately the preprocessor/compiler diagnostic output isn't stable between versions and this solution, although more efficient, was deemed too fragile. Because of the lowercasing performed by fixdep, there might be name collisions triggering spurious rebuilds for similar symbols. But this shouldn't be a big issue in practice. (This is the case for CONFIG_* symbols and I didn't want to be different here, whatever the original reason for doing so.) To avoid needless build overhead, the exported symbol name gathering is performed only when CONFIG_TRIM_UNUSED_KSYMS is selected. Signed-off-by: Nicolas Pitre Acked-by: Rusty Russell --- include/linux/export.h | 13 ++++++++++++- scripts/Kbuild.include | 27 +++++++++++++++++++++++++++ scripts/basic/fixdep.c | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/export.h b/include/linux/export.h index 77afdb2a2506..2f9ccbe6a639 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -65,7 +65,18 @@ extern struct module __this_module; __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ = { (unsigned long)&sym, __kstrtab_##sym } -#ifdef CONFIG_TRIM_UNUSED_KSYMS +#if defined(__KSYM_DEPS__) + +/* + * For fine grained build dependencies, we want to tell the build system + * about each possible exported symbol even if they're not actually exported. + * We use a string pattern that is unlikely to be valid code that the build + * system filters out from the preprocessor output (see ksym_dep_filter + * in scripts/Kbuild.include). + */ +#define __EXPORT_SYMBOL(sym, sec) === __KSYM_##sym === + +#elif defined(CONFIG_TRIM_UNUSED_KSYMS) #include #include diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 80ca538bfba9..a09927e02713 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -258,12 +258,39 @@ if_changed_dep = $(if $(strip $(any-prereq) $(arg-check) ), \ @set -e; \ $(cmd_and_fixdep), @:) +ifndef CONFIG_TRIM_UNUSED_KSYMS + cmd_and_fixdep = \ $(echo-cmd) $(cmd_$(1)); \ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\ rm -f $(depfile); \ mv -f $(dot-target).tmp $(dot-target).cmd; +else + +# Filter out exported kernel symbol names from the preprocessor output. +# See also __KSYM_DEPS__ in include/linux/export.h. +# We disable the depfile generation here, so as not to overwrite the existing +# depfile while fixdep is parsing it. +flags_nodeps = $(filter-out -Wp$(comma)-M%, $($(1))) +ksym_dep_filter = \ + case "$(1)" in \ + cc_*_c) $(CPP) $(call flags_nodeps,c_flags) -D__KSYM_DEPS__ $< ;; \ + as_*_S) $(CPP) $(call flags_nodeps,a_flags) -D__KSYM_DEPS__ $< ;; \ + boot*|build*|*cpp_lds_S|dtc|host*|vdso*) : ;; \ + *) echo "Don't know how to preprocess $(1)" >&2; false ;; \ + esac | sed -rn 's/^.*=== __KSYM_(.*) ===.*$$/KSYM_\1/p' + +cmd_and_fixdep = \ + $(echo-cmd) $(cmd_$(1)); \ + $(ksym_dep_filter) | \ + scripts/basic/fixdep -e $(depfile) $@ '$(make-cmd)' \ + > $(dot-target).tmp; \ + rm -f $(depfile); \ + mv -f $(dot-target).tmp $(dot-target).cmd; + +endif + # Usage: $(call if_changed_rule,foo) # Will check if $(cmd_foo) or any of the prerequisites changed, # and if so will execute $(rule_foo). diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index 7e90a1f7de0f..746ec1ece614 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c @@ -358,6 +358,7 @@ static void parse_dep_file(void *map, size_t len) /* Ignore certain dependencies */ if (strrcmp(s, "include/generated/autoconf.h") && + strrcmp(s, "include/generated/autoksyms.h") && strrcmp(s, "arch/um/include/uml-config.h") && strrcmp(s, "include/linux/kconfig.h") && strrcmp(s, ".ver")) { -- cgit From 6c96f05c8bb8bc4177613ef3c23a56b455e75887 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 23 Feb 2016 18:46:24 +0100 Subject: reset: Make [of_]reset_control_get[_foo] functions wrappers With both the regular, _by_index and _optional variants we already have quite a few variants of [of_]reset_control_get[_foo], the upcoming addition of shared reset lines support makes this worse. This commit changes all the variants into wrappers around common core functions. For completeness sake this commit also adds a new devm_get_reset_control_by_index wrapper. Signed-off-by: Hans de Goede Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 84 +++++++-------------------------- include/linux/reset.h | 126 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 107 insertions(+), 103 deletions(-) (limited to 'include/linux') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index f15f150b79da..bdf1763da87a 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -136,18 +136,8 @@ int reset_control_status(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_status); -/** - * of_reset_control_get_by_index - Lookup and obtain a reference to a reset - * controller by index. - * @node: device to be reset by the controller - * @index: index of the reset controller - * - * This is to be used to perform a list of resets for a device or power domain - * in whatever order. Returns a struct reset_control or IS_ERR() condition - * containing errno. - */ -struct reset_control *of_reset_control_get_by_index(struct device_node *node, - int index) +struct reset_control *__of_reset_control_get(struct device_node *node, + const char *id, int index) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -155,6 +145,16 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, int rstc_id; int ret; + if (!node) + return ERR_PTR(-EINVAL); + + if (id) { + index = of_property_match_string(node, + "reset-names", id); + if (index < 0) + return ERR_PTR(-ENOENT); + } + ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", index, &args); if (ret) @@ -200,49 +200,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, return rstc; } -EXPORT_SYMBOL_GPL(of_reset_control_get_by_index); - -/** - * of_reset_control_get - Lookup and obtain a reference to a reset controller. - * @node: device to be reset by the controller - * @id: reset line name - * - * Returns a struct reset_control or IS_ERR() condition containing errno. - * - * Use of id names is optional. - */ -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id) -{ - int index = 0; - - if (id) { - index = of_property_match_string(node, - "reset-names", id); - if (index < 0) - return ERR_PTR(-ENOENT); - } - return of_reset_control_get_by_index(node, index); -} -EXPORT_SYMBOL_GPL(of_reset_control_get); - -/** - * reset_control_get - Lookup and obtain a reference to a reset controller. - * @dev: device to be reset by the controller - * @id: reset line name - * - * Returns a struct reset_control or IS_ERR() condition containing errno. - * - * Use of id names is optional. - */ -struct reset_control *reset_control_get(struct device *dev, const char *id) -{ - if (!dev) - return ERR_PTR(-EINVAL); - - return of_reset_control_get(dev->of_node, id); -} -EXPORT_SYMBOL_GPL(reset_control_get); +EXPORT_SYMBOL_GPL(__of_reset_control_get); /** * reset_control_put - free the reset controller @@ -264,16 +222,8 @@ static void devm_reset_control_release(struct device *dev, void *res) reset_control_put(*(struct reset_control **)res); } -/** - * devm_reset_control_get - resource managed reset_control_get() - * @dev: device to be reset by the controller - * @id: reset line name - * - * Managed reset_control_get(). For reset controllers returned from this - * function, reset_control_put() is called automatically on driver detach. - * See reset_control_get() for more information. - */ -struct reset_control *devm_reset_control_get(struct device *dev, const char *id) +struct reset_control *__devm_reset_control_get(struct device *dev, + const char *id, int index) { struct reset_control **ptr, *rstc; @@ -282,7 +232,7 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id) if (!ptr) return ERR_PTR(-ENOMEM); - rstc = reset_control_get(dev, id); + rstc = __of_reset_control_get(dev ? dev->of_node : NULL, id, index); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); @@ -292,7 +242,7 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id) return rstc; } -EXPORT_SYMBOL_GPL(devm_reset_control_get); +EXPORT_SYMBOL_GPL(__devm_reset_control_get); /** * device_reset - find reset controller associated with the device diff --git a/include/linux/reset.h b/include/linux/reset.h index c4c097de0ba9..1bb69a29d6db 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -1,8 +1,8 @@ #ifndef _LINUX_RESET_H_ #define _LINUX_RESET_H_ -struct device; -struct device_node; +#include + struct reset_control; #ifdef CONFIG_RESET_CONTROLLER @@ -12,9 +12,11 @@ int reset_control_assert(struct reset_control *rstc); int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); -struct reset_control *reset_control_get(struct device *dev, const char *id); +struct reset_control *__of_reset_control_get(struct device_node *node, + const char *id, int index); void reset_control_put(struct reset_control *rstc); -struct reset_control *devm_reset_control_get(struct device *dev, const char *id); +struct reset_control *__devm_reset_control_get(struct device *dev, + const char *id, int index); int __must_check device_reset(struct device *dev); @@ -23,24 +25,6 @@ static inline int device_reset_optional(struct device *dev) return device_reset(dev); } -static inline struct reset_control *reset_control_get_optional( - struct device *dev, const char *id) -{ - return reset_control_get(dev, id); -} - -static inline struct reset_control *devm_reset_control_get_optional( - struct device *dev, const char *id) -{ - return devm_reset_control_get(dev, id); -} - -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id); - -struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index); - #else static inline int reset_control_reset(struct reset_control *rstc) @@ -77,44 +61,114 @@ static inline int device_reset_optional(struct device *dev) return -ENOTSUPP; } -static inline struct reset_control *__must_check reset_control_get( - struct device *dev, const char *id) +static inline struct reset_control *__of_reset_control_get( + struct device_node *node, + const char *id, int index) { - WARN_ON(1); return ERR_PTR(-EINVAL); } -static inline struct reset_control *__must_check devm_reset_control_get( - struct device *dev, const char *id) +static inline struct reset_control *__devm_reset_control_get( + struct device *dev, + const char *id, int index) { - WARN_ON(1); return ERR_PTR(-EINVAL); } -static inline struct reset_control *reset_control_get_optional( +#endif /* CONFIG_RESET_CONTROLLER */ + +/** + * reset_control_get - Lookup and obtain a reference to a reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ +static inline struct reset_control *__must_check reset_control_get( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); +#ifndef CONFIG_RESET_CONTROLLER + WARN_ON(1); +#endif + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); } -static inline struct reset_control *devm_reset_control_get_optional( +static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); } +/** + * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ static inline struct reset_control *of_reset_control_get( struct device_node *node, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, id, 0); } +/** + * of_reset_control_get_by_index - Lookup and obtain a reference to a reset + * controller by index. + * @node: device to be reset by the controller + * @index: index of the reset controller + * + * This is to be used to perform a list of resets for a device or power domain + * in whatever order. Returns a struct reset_control or IS_ERR() condition + * containing errno. + */ static inline struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index) + struct device_node *node, int index) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, NULL, index); } -#endif /* CONFIG_RESET_CONTROLLER */ +/** + * devm_reset_control_get - resource managed reset_control_get() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *__must_check devm_reset_control_get( + struct device *dev, const char *id) +{ +#ifndef CONFIG_RESET_CONTROLLER + WARN_ON(1); +#endif + return __devm_reset_control_get(dev, id, 0); +} + +static inline struct reset_control *devm_reset_control_get_optional( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0); +} + +/** + * devm_reset_control_get_by_index - resource managed reset_control_get + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *devm_reset_control_get_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index); +} #endif -- cgit From c15ddec2ca06076a11195313aa1fce47d2a28c5d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 23 Feb 2016 18:46:25 +0100 Subject: reset: Share struct reset_control between reset_control_get calls Now that struct reset_control no longer stores the device pointer for the device calling reset_control_get we can share a single struct reset_control when multiple calls to reset_control_get are made for the same reset line (same id / index). This is a preparation patch for adding support for shared reset lines. Signed-off-by: Hans de Goede Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 84 ++++++++++++++++++++++++++++++---------- include/linux/reset-controller.h | 2 + 2 files changed, 65 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index bdf1763da87a..957750600ff3 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -18,19 +18,23 @@ #include #include -static DEFINE_MUTEX(reset_controller_list_mutex); +static DEFINE_MUTEX(reset_list_mutex); static LIST_HEAD(reset_controller_list); /** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to + * @list: list entry for the rcdev's reset controller list * @id: ID of the reset controller in the reset * controller device + * @refcnt: Number of gets of this reset_control */ struct reset_control { struct reset_controller_dev *rcdev; + struct list_head list; unsigned int id; + unsigned int refcnt; }; /** @@ -62,9 +66,11 @@ int reset_controller_register(struct reset_controller_dev *rcdev) rcdev->of_xlate = of_reset_simple_xlate; } - mutex_lock(&reset_controller_list_mutex); + INIT_LIST_HEAD(&rcdev->reset_control_head); + + mutex_lock(&reset_list_mutex); list_add(&rcdev->list, &reset_controller_list); - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return 0; } @@ -76,9 +82,9 @@ EXPORT_SYMBOL_GPL(reset_controller_register); */ void reset_controller_unregister(struct reset_controller_dev *rcdev) { - mutex_lock(&reset_controller_list_mutex); + mutex_lock(&reset_list_mutex); list_del(&rcdev->list); - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_controller_unregister); @@ -136,6 +142,48 @@ int reset_control_status(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_status); +static struct reset_control *__reset_control_get( + struct reset_controller_dev *rcdev, + unsigned int index) +{ + struct reset_control *rstc; + + lockdep_assert_held(&reset_list_mutex); + + list_for_each_entry(rstc, &rcdev->reset_control_head, list) { + if (rstc->id == index) { + rstc->refcnt++; + return rstc; + } + } + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return ERR_PTR(-ENOMEM); + + try_module_get(rcdev->owner); + + rstc->rcdev = rcdev; + list_add(&rstc->list, &rcdev->reset_control_head); + rstc->id = index; + rstc->refcnt = 1; + + return rstc; +} + +static void __reset_control_put(struct reset_control *rstc) +{ + lockdep_assert_held(&reset_list_mutex); + + if (--rstc->refcnt) + return; + + module_put(rstc->rcdev->owner); + + list_del(&rstc->list); + kfree(rstc); +} + struct reset_control *__of_reset_control_get(struct device_node *node, const char *id, int index) { @@ -160,7 +208,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node, if (ret) return ERR_PTR(ret); - mutex_lock(&reset_controller_list_mutex); + mutex_lock(&reset_list_mutex); rcdev = NULL; list_for_each_entry(r, &reset_controller_list, list) { if (args.np == r->of_node) { @@ -171,32 +219,25 @@ struct reset_control *__of_reset_control_get(struct device_node *node, of_node_put(args.np); if (!rcdev) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(-EPROBE_DEFER); } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(-EINVAL); } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(rstc_id); } - try_module_get(rcdev->owner); - mutex_unlock(&reset_controller_list_mutex); - - rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); - if (!rstc) { - module_put(rcdev->owner); - return ERR_PTR(-ENOMEM); - } + /* reset_list_mutex also protects the rcdev's reset_control list */ + rstc = __reset_control_get(rcdev, rstc_id); - rstc->rcdev = rcdev; - rstc->id = rstc_id; + mutex_unlock(&reset_list_mutex); return rstc; } @@ -212,8 +253,9 @@ void reset_control_put(struct reset_control *rstc) if (IS_ERR(rstc)) return; - module_put(rstc->rcdev->owner); - kfree(rstc); + mutex_lock(&reset_list_mutex); + __reset_control_put(rstc); + mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_control_put); diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index a3a5bcdb1d02..b91ba932bbd4 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -31,6 +31,7 @@ struct of_phandle_args; * @ops: a pointer to device specific struct reset_control_ops * @owner: kernel module of the reset controller driver * @list: internal list of reset controller devices + * @reset_control_head: head of internal list of requested reset controls * @of_node: corresponding device tree node as phandle target * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the @@ -41,6 +42,7 @@ struct reset_controller_dev { const struct reset_control_ops *ops; struct module *owner; struct list_head list; + struct list_head reset_control_head; struct device_node *of_node; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, -- cgit From 0b52297f2288ca239e598afe6c92db83d8d2bfcd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 23 Feb 2016 18:46:26 +0100 Subject: reset: Add support for shared reset controls In some SoCs some hw-blocks share a reset control. Add support for this setup by adding new: reset_control_get_shared() devm_reset_control_get_shared() devm_reset_control_get_shared_by_index() methods to get a reset_control. Note that this patch omits adding of_ variants, if these are needed later they can be easily added. This patch also changes the behavior of the existing exclusive reset_control_get() variants, if these are now called more then once for the same reset_control they will return -EBUSY. To catch existing drivers triggering this error (there should not be any) a WARN_ON(1) is added in this path. When a reset_control is shared, the behavior of reset_control_assert / deassert is changed, for shared reset_controls these will work like the clock-enable/disable and regulator-on/off functions. They will keep a deassert_count, and only (re-)assert the reset after reset_control_assert has been called as many times as reset_control_deassert was called. Calling reset_control_assert without first calling reset_control_deassert is not allowed on a shared reset control. Calling reset_control_reset is also not allowed on a shared reset control. Signed-off-by: Hans de Goede Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 59 +++++++++++++++++++++++++------ include/linux/reset.h | 96 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 129 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 957750600ff3..72b32bd15549 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include #include #include #include @@ -29,12 +30,16 @@ static LIST_HEAD(reset_controller_list); * @id: ID of the reset controller in the reset * controller device * @refcnt: Number of gets of this reset_control + * @shared: Is this a shared (1), or an exclusive (0) reset_control? + * @deassert_cnt: Number of times this reset line has been deasserted */ struct reset_control { struct reset_controller_dev *rcdev; struct list_head list; unsigned int id; unsigned int refcnt; + int shared; + atomic_t deassert_count; }; /** @@ -91,9 +96,14 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister); /** * reset_control_reset - reset the controlled device * @rstc: reset controller + * + * Calling this on a shared reset controller is an error. */ int reset_control_reset(struct reset_control *rstc) { + if (WARN_ON(rstc->shared)) + return -EINVAL; + if (rstc->rcdev->ops->reset) return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); @@ -104,26 +114,48 @@ EXPORT_SYMBOL_GPL(reset_control_reset); /** * reset_control_assert - asserts the reset line * @rstc: reset controller + * + * Calling this on an exclusive reset controller guarantees that the reset + * will be asserted. When called on a shared reset controller the line may + * still be deasserted, as long as other users keep it so. + * + * For shared reset controls a driver cannot expect the hw's registers and + * internal state to be reset, but must be prepared for this to happen. */ int reset_control_assert(struct reset_control *rstc) { - if (rstc->rcdev->ops->assert) - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (WARN_ON(atomic_read(&rstc->deassert_count) == 0)) + return -EINVAL; + + if (atomic_dec_return(&rstc->deassert_count) != 0) + return 0; + } + + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_assert); /** * reset_control_deassert - deasserts the reset line * @rstc: reset controller + * + * After calling this function, the reset is guaranteed to be deasserted. */ int reset_control_deassert(struct reset_control *rstc) { - if (rstc->rcdev->ops->deassert) - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->deassert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (atomic_inc_return(&rstc->deassert_count) != 1) + return 0; + } + + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -144,7 +176,7 @@ EXPORT_SYMBOL_GPL(reset_control_status); static struct reset_control *__reset_control_get( struct reset_controller_dev *rcdev, - unsigned int index) + unsigned int index, int shared) { struct reset_control *rstc; @@ -152,6 +184,9 @@ static struct reset_control *__reset_control_get( list_for_each_entry(rstc, &rcdev->reset_control_head, list) { if (rstc->id == index) { + if (WARN_ON(!rstc->shared || !shared)) + return ERR_PTR(-EBUSY); + rstc->refcnt++; return rstc; } @@ -167,6 +202,7 @@ static struct reset_control *__reset_control_get( list_add(&rstc->list, &rcdev->reset_control_head); rstc->id = index; rstc->refcnt = 1; + rstc->shared = shared; return rstc; } @@ -185,7 +221,7 @@ static void __reset_control_put(struct reset_control *rstc) } struct reset_control *__of_reset_control_get(struct device_node *node, - const char *id, int index) + const char *id, int index, int shared) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -235,7 +271,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node, } /* reset_list_mutex also protects the rcdev's reset_control list */ - rstc = __reset_control_get(rcdev, rstc_id); + rstc = __reset_control_get(rcdev, rstc_id, shared); mutex_unlock(&reset_list_mutex); @@ -265,7 +301,7 @@ static void devm_reset_control_release(struct device *dev, void *res) } struct reset_control *__devm_reset_control_get(struct device *dev, - const char *id, int index) + const char *id, int index, int shared) { struct reset_control **ptr, *rstc; @@ -274,7 +310,8 @@ struct reset_control *__devm_reset_control_get(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - rstc = __of_reset_control_get(dev ? dev->of_node : NULL, id, index); + rstc = __of_reset_control_get(dev ? dev->of_node : NULL, + id, index, shared); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); diff --git a/include/linux/reset.h b/include/linux/reset.h index 1bb69a29d6db..a552134a209e 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -13,10 +13,10 @@ int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); struct reset_control *__of_reset_control_get(struct device_node *node, - const char *id, int index); + const char *id, int index, int shared); void reset_control_put(struct reset_control *rstc); struct reset_control *__devm_reset_control_get(struct device *dev, - const char *id, int index); + const char *id, int index, int shared); int __must_check device_reset(struct device *dev); @@ -63,14 +63,14 @@ static inline int device_reset_optional(struct device *dev) static inline struct reset_control *__of_reset_control_get( struct device_node *node, - const char *id, int index) + const char *id, int index, int shared) { return ERR_PTR(-EINVAL); } static inline struct reset_control *__devm_reset_control_get( struct device *dev, - const char *id, int index) + const char *id, int index, int shared) { return ERR_PTR(-EINVAL); } @@ -78,11 +78,17 @@ static inline struct reset_control *__devm_reset_control_get( #endif /* CONFIG_RESET_CONTROLLER */ /** - * reset_control_get - Lookup and obtain a reference to a reset controller. + * reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. * @dev: device to be reset by the controller * @id: reset line name * * Returns a struct reset_control or IS_ERR() condition containing errno. + * If this function is called more then once for the same reset_control it will + * return -EBUSY. + * + * See reset_control_get_shared for details on shared references to + * reset-controls. * * Use of id names is optional. */ @@ -92,17 +98,46 @@ static inline struct reset_control *__must_check reset_control_get( #ifndef CONFIG_RESET_CONTROLLER WARN_ON(1); #endif - return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { - return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } /** - * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * reset_control_get_shared - Lookup and obtain a shared reference to a + * reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * This function is intended for use with reset-controls which are shared + * between hardware-blocks. + * + * When a reset-control is shared, the behavior of reset_control_assert / + * deassert is changed, the reset-core will keep track of a deassert_count + * and only (re-)assert the reset after reset_control_assert has been called + * as many times as reset_control_deassert was called. Also see the remark + * about shared reset-controls in the reset_control_assert docs. + * + * Calling reset_control_assert without first calling reset_control_deassert + * is not allowed on a shared reset control. Calling reset_control_reset is + * also not allowed on a shared reset control. + * + * Use of id names is optional. + */ +static inline struct reset_control *reset_control_get_shared( + struct device *dev, const char *id) +{ + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 1); +} + +/** + * of_reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. * @node: device to be reset by the controller * @id: reset line name * @@ -113,12 +148,12 @@ static inline struct reset_control *reset_control_get_optional( static inline struct reset_control *of_reset_control_get( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0); + return __of_reset_control_get(node, id, 0, 0); } /** - * of_reset_control_get_by_index - Lookup and obtain a reference to a reset - * controller by index. + * of_reset_control_get_by_index - Lookup and obtain an exclusive reference to + * a reset controller by index. * @node: device to be reset by the controller * @index: index of the reset controller * @@ -129,7 +164,7 @@ static inline struct reset_control *of_reset_control_get( static inline struct reset_control *of_reset_control_get_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index); + return __of_reset_control_get(node, NULL, index, 0); } /** @@ -147,13 +182,13 @@ static inline struct reset_control *__must_check devm_reset_control_get( #ifndef CONFIG_RESET_CONTROLLER WARN_ON(1); #endif - return __devm_reset_control_get(dev, id, 0); + return __devm_reset_control_get(dev, id, 0, 0); } static inline struct reset_control *devm_reset_control_get_optional( struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0); + return __devm_reset_control_get(dev, id, 0, 0); } /** @@ -168,7 +203,38 @@ static inline struct reset_control *devm_reset_control_get_optional( static inline struct reset_control *devm_reset_control_get_by_index( struct device *dev, int index) { - return __devm_reset_control_get(dev, NULL, index); + return __devm_reset_control_get(dev, NULL, index, 0); +} + +/** + * devm_reset_control_get_shared - resource managed reset_control_get_shared() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0, 1); +} + +/** + * devm_reset_control_get_shared_by_index - resource managed + * reset_control_get_shared + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index, 1); } #endif -- cgit From 44debe7a123cc760fc90ccbe253210798c917fa7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 30 Mar 2016 11:26:35 +0200 Subject: vgacon: dummy implementation for vgacon_text_force MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to ditch a ton of ugly #ifdefs from a bunch of drm modeset drivers. v2: Make the dummy function actually return a sane value, spotted by Ville. v3: Because the patch is still in limbo there's no more drivers to convert, noticed by Emil. v4: Rebase once more, because hooray. I'll just go ahead an apply this one later on to drm-misc. Cc: Emil Velikov Cc: Ville Syrjälä Cc: Andrew Morton Cc: Greg Kroah-Hartman Reviewed-by: Emil Velikov Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 2 -- drivers/gpu/drm/ast/ast_drv.c | 2 -- drivers/gpu/drm/cirrus/cirrus_drv.c | 2 -- drivers/gpu/drm/i915/i915_drv.c | 2 -- drivers/gpu/drm/mgag200/mgag200_drv.c | 2 -- drivers/gpu/drm/nouveau/nouveau_drm.c | 2 -- drivers/gpu/drm/qxl/qxl_drv.c | 2 -- drivers/gpu/drm/radeon/radeon_drv.c | 2 -- drivers/gpu/drm/virtio/virtgpu_drv.c | 2 -- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 -- include/linux/console.h | 2 ++ 11 files changed, 2 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index ce79a8b605a0..fba20bd59cfa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -539,12 +539,10 @@ static struct pci_driver amdgpu_kms_pci_driver = { static int __init amdgpu_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) { DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } -#endif DRM_INFO("amdgpu kernel modesetting enabled.\n"); driver = &kms_driver; pdriver = &amdgpu_kms_pci_driver; diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 9a32d9dfdd26..fcd9c0714836 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -218,10 +218,8 @@ static struct drm_driver driver = { static int __init ast_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && ast_modeset == -1) return -EINVAL; -#endif if (ast_modeset == 0) return -EINVAL; diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index b1619e29a564..b394e6d8f01e 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -162,10 +162,8 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && cirrus_modeset == -1) return -EINVAL; -#endif if (cirrus_modeset == 0) return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 44912ecebc1a..8a62690e6513 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1750,10 +1750,8 @@ static int __init i915_init(void) if (i915.modeset == 0) driver.driver_features &= ~DRIVER_MODESET; -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && i915.modeset == -1) driver.driver_features &= ~DRIVER_MODESET; -#endif if (!(driver.driver_features & DRIVER_MODESET)) { /* Silently fail loading to not upset userspace. */ diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index b0af77454d52..ebb470ff7200 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -116,10 +116,8 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && mgag200_modeset == -1) return -EINVAL; -#endif if (mgag200_modeset == 0) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index bb8498c9b13e..731c5c2a8933 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1082,10 +1082,8 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) nouveau_modeset = 0; -#endif } if (!nouveau_modeset) diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 7307b07fe06b..dc9df5fe50ba 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -272,10 +272,8 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && qxl_modeset == -1) return -EINVAL; -#endif if (qxl_modeset == 0) return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index cad25557650f..ad136fc081c8 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -558,12 +558,10 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_init(void) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } -#endif /* set to modesetting by default if not nomodeset */ if (radeon_modeset == -1) radeon_modeset = 1; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 7f898cfdc746..3cc7afa77a35 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -42,10 +42,8 @@ module_param_named(modeset, virtio_gpu_modeset, int, 0400); static int virtio_gpu_probe(struct virtio_device *vdev) { -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && virtio_gpu_modeset == -1) return -EINVAL; -#endif if (virtio_gpu_modeset == 0) return -EINVAL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 0ee76e523a90..fa10395e2a18 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1529,10 +1529,8 @@ static int __init vmwgfx_init(void) { int ret; -#ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) return -EINVAL; -#endif ret = drm_pci_init(&driver, &vmw_pci_driver); if (ret) diff --git a/include/linux/console.h b/include/linux/console.h index ea731af2451e..e49cc1ef19be 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -191,6 +191,8 @@ void vcs_remove_sysfs(int index); #ifdef CONFIG_VGA_CONSOLE extern bool vgacon_text_force(void); +#else +static inline bool vgacon_text_force(void) { return false; } #endif #endif /* _LINUX_CONSOLE_H */ -- cgit From 8cb359e3a1f6318f971bec281623613f48b711be Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Wed, 23 Mar 2016 12:34:41 +0000 Subject: iio: buffer: add missing descriptions in iio_buffer_access_funcs The members buffer_group and attrs of iio_buffer_access_funcs have no descriptions for the documentation. Adding them. Fixes: 08e7e0adaa17 ("iio: buffer: Allocate standard attributes in the core") Signed-off-by: Luis de Bethencourt Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 2ec3ad58e8a0..70a5164f4728 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -83,10 +83,12 @@ struct iio_buffer_access_funcs { * @access: [DRIVER] buffer access functions associated with the * implementation. * @scan_el_dev_attr_list:[INTERN] list of scan element related attributes. + * @buffer_group: [INTERN] attributes of the buffer group * @scan_el_group: [DRIVER] attribute group for those attributes not * created from the iio_chan_info array. * @pollq: [INTERN] wait queue to allow for polling on the buffer. * @stufftoread: [INTERN] flag to indicate new data. + * @attrs: [INTERN] standard attributes of the buffer * @demux_list: [INTERN] list of operations required to demux the scan. * @demux_bounce: [INTERN] buffer for doing gather from incoming scan. * @buffer_list: [INTERN] entry in the devices list of current buffers. -- cgit From fddcca5107051adf9e4481d2a79ae0616577fd2c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 29 Feb 2016 13:20:28 +0100 Subject: mtd: avoid stack overflow in MTD CFI code When map_word gets too large, we use a lot of kernel stack, and for MTD_MAP_BANK_WIDTH_32, this means we use more than the recommended 1024 bytes in a number of functions: drivers/mtd/chips/cfi_cmdset_0020.c: In function 'cfi_staa_write_buffers': drivers/mtd/chips/cfi_cmdset_0020.c:651:1: warning: the frame size of 1336 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/mtd/chips/cfi_cmdset_0020.c: In function 'cfi_staa_erase_varsize': drivers/mtd/chips/cfi_cmdset_0020.c:972:1: warning: the frame size of 1208 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/mtd/chips/cfi_cmdset_0001.c: In function 'do_write_buffer': drivers/mtd/chips/cfi_cmdset_0001.c:1835:1: warning: the frame size of 1240 bytes is larger than 1024 bytes [-Wframe-larger-than=] This can be avoided if all operations on the map word are done indirectly and the stack gets reused between the calls. We can mostly achieve this by selecting MTD_COMPLEX_MAPPINGS whenever MTD_MAP_BANK_WIDTH_32 is set, but for the case that no other bank width is enabled, we also need to use a non-constant map_bankwidth() to convince the compiler to use less stack. Signed-off-by: Arnd Bergmann [Brian: this patch mostly achieves its goal by forcing MTD_COMPLEX_MAPPINGS (and the accompanying indirection) for 256-bit mappings; the rest of the change is mostly a wash, though it helps reduce stack size slightly. If we really care about supporting 256-bit mappings though, we should consider rewriting some of this code to avoid keeping and assigning so many 256-bit objects on the stack.] Signed-off-by: Brian Norris --- drivers/mtd/chips/Kconfig | 1 + include/linux/mtd/map.h | 19 +++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 3b3dabce58de..bbfa1f129266 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -115,6 +115,7 @@ config MTD_MAP_BANK_WIDTH_16 config MTD_MAP_BANK_WIDTH_32 bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY + select MTD_COMPLEX_MAPPINGS if HAS_IOMEM default n help If you wish to support CFI devices on a physical bus which is diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 5e0eb7ccabd4..3aa56e3104bb 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -122,18 +122,13 @@ #endif #ifdef CONFIG_MTD_MAP_BANK_WIDTH_32 -# ifdef map_bankwidth -# undef map_bankwidth -# define map_bankwidth(map) ((map)->bankwidth) -# undef map_bankwidth_is_large -# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) -# undef map_words -# define map_words(map) map_calc_words(map) -# else -# define map_bankwidth(map) 32 -# define map_bankwidth_is_large(map) (1) -# define map_words(map) map_calc_words(map) -# endif +/* always use indirect access for 256-bit to preserve kernel stack */ +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# undef map_bankwidth_is_large +# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) +# undef map_words +# define map_words(map) map_calc_words(map) #define map_bankwidth_is_32(map) (map_bankwidth(map) == 32) #undef MAX_MAP_BANKWIDTH #define MAX_MAP_BANKWIDTH 32 -- cgit From 5651d6aaf489d1db48c253cf884b40214e91c2c5 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 26 Feb 2016 11:50:28 +0100 Subject: mtd: bcm47xxsflash: use ioremap_cache() instead of KSEG0ADDR() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using KSEG0ADDR makes code highly MIPS dependent and not portable. Thanks to the fix a68f376 ("MIPS: io.h: Define `ioremap_cache'") we can use ioremap_cache which is generic and supported on MIPS as well now. KSEG0ADDR was translating 0x1c000000 into 0x9c000000. With ioremap_cache we use MIPS's __ioremap (and then remap_area_pages). This results in different address (e.g. 0xc0080000) but it still should be cached as expected and it was successfully tested with BCM47186B0. Other than that drivers/bcma/driver_chipcommon_sflash.c nicely setups a struct resource for access window, but we wren't using it. Use it now and drop duplicated info. Signed-off-by: Brian Norris Signed-off-by: Rafał Miłecki --- drivers/bcma/driver_chipcommon_sflash.c | 1 - drivers/mtd/devices/bcm47xxsflash.c | 29 ++++++++++++++++++++++++----- drivers/mtd/devices/bcm47xxsflash.h | 3 ++- include/linux/bcma/bcma_driver_chipcommon.h | 1 - 4 files changed, 26 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c index 04d706ca5f43..35b13a08ca3e 100644 --- a/drivers/bcma/driver_chipcommon_sflash.c +++ b/drivers/bcma/driver_chipcommon_sflash.c @@ -146,7 +146,6 @@ int bcma_sflash_init(struct bcma_drv_cc *cc) return -ENOTSUPP; } - sflash->window = BCMA_SOC_FLASH2; sflash->blocksize = e->blocksize; sflash->numblocks = e->numblocks; sflash->size = sflash->blocksize * sflash->numblocks; diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 347bb83db864..1c65c15b31a1 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -109,8 +110,7 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, if ((from + len) > mtd->size) return -EINVAL; - memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), - len); + memcpy_fromio(buf, b47s->window + from, len); *retlen = len; return len; @@ -275,15 +275,33 @@ static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) { - struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct bcma_sflash *sflash = dev_get_platdata(dev); struct bcm47xxsflash *b47s; + struct resource *res; int err; - b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); + b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL); if (!b47s) return -ENOMEM; sflash->priv = b47s; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "invalid resource\n"); + return -EINVAL; + } + if (!devm_request_mem_region(dev, res->start, resource_size(res), + res->name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return -EBUSY; + } + b47s->window = ioremap_cache(res->start, resource_size(res)); + if (!b47s->window) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + return -ENOMEM; + } + b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); b47s->cc_read = bcm47xxsflash_bcma_cc_read; b47s->cc_write = bcm47xxsflash_bcma_cc_write; @@ -297,7 +315,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) break; } - b47s->window = sflash->window; b47s->blocksize = sflash->blocksize; b47s->numblocks = sflash->numblocks; b47s->size = sflash->size; @@ -306,6 +323,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); + iounmap(b47s->window); return err; } @@ -321,6 +339,7 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) struct bcm47xxsflash *b47s = sflash->priv; mtd_device_unregister(&b47s->mtd); + iounmap(b47s->window); return 0; } diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h index fe93daf4f489..1564b62b412e 100644 --- a/drivers/mtd/devices/bcm47xxsflash.h +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -65,7 +65,8 @@ struct bcm47xxsflash { enum bcm47xxsflash_type type; - u32 window; + void __iomem *window; + u32 blocksize; u16 numblocks; u32 size; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 846513c73606..a5ac2cad5cb7 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -587,7 +587,6 @@ struct mtd_info; struct bcma_sflash { bool present; - u32 window; u32 blocksize; u16 numblocks; u32 size; -- cgit From 5b01e4b9efa0b78672cbbea830c9fbcc7f239e29 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:54 +0200 Subject: libata: Implement NCQ autosense Some newer devices support NCQ autosense (cf ACS-4), so we should be using it to retrieve the sense code and speed up recovery. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 12 ++++++++++++ drivers/ata/libata-scsi.c | 7 ++++++- drivers/ata/libata.h | 1 + include/linux/ata.h | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 961acc788f44..8c8355f0792e 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1600,6 +1600,8 @@ static int ata_eh_read_log_10h(struct ata_device *dev, tf->hob_lbah = buf[10]; tf->nsect = buf[12]; tf->hob_nsect = buf[13]; + if (ata_id_has_ncq_autosense(dev->id)) + tf->auxiliary = buf[14] << 16 | buf[15] << 8 | buf[16]; return 0; } @@ -1797,6 +1799,16 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) memcpy(&qc->result_tf, &tf, sizeof(tf)); qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48; qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ; + if (qc->result_tf.auxiliary) { + char sense_key, asc, ascq; + + sense_key = (qc->result_tf.auxiliary >> 16) & 0xff; + asc = (qc->result_tf.auxiliary >> 8) & 0xff; + ascq = qc->result_tf.auxiliary & 0xff; + ata_scsi_set_sense(qc->scsicmd, sense_key, asc, ascq); + qc->flags |= ATA_QCFLAG_SENSE_VALID; + } + ehc->i.err_mask &= ~AC_ERR_DEV; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 567859ce0512..6dc2fadfd7c5 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,8 +270,11 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); -static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) { + if (!cmd) + return; + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); @@ -1784,6 +1787,8 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) ata_gen_passthru_sense(qc); + else if (qc->flags & ATA_QCFLAG_SENSE_VALID) + cmd->result = SAM_STAT_CHECK_CONDITION; else if (need_sense) ata_gen_ata_sense(qc); else diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index f840ca18a7c0..8cfdd9616d16 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -137,6 +137,7 @@ extern int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap, int sync); extern int ata_scsi_offline_dev(struct ata_device *dev); +extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); extern void ata_scsi_media_change_notify(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); diff --git a/include/linux/ata.h b/include/linux/ata.h index c1a2f345cbe6..e797e1b53006 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -528,6 +528,8 @@ struct ata_bmdma_prd { #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) #define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4)) #define ata_id_has_devslp(id) ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8)) +#define ata_id_has_ncq_autosense(id) \ + ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7)) static inline bool ata_id_has_hipm(const u16 *id) { -- cgit From e87fd28cf9a2d9018ac4b6dd92f0b417714bc18d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:55 +0200 Subject: libata: Implement support for sense data reporting ACS-4 defines a sense data reporting feature set. This patch implements support for it. tj: Cosmetic formatting updates. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 20 +++++++++++++- drivers/ata/libata-eh.c | 68 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/ata.h | 16 +++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 55e257c268dd..f991f786227e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2148,6 +2148,24 @@ static int ata_dev_config_ncq(struct ata_device *dev, return 0; } +static void ata_dev_config_sense_reporting(struct ata_device *dev) +{ + unsigned int err_mask; + + if (!ata_id_has_sense_reporting(dev->id)) + return; + + if (ata_id_sense_reporting_enabled(dev->id)) + return; + + err_mask = ata_dev_set_feature(dev, SETFEATURE_SENSE_DATA, 0x1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to enable Sense Data Reporting, Emask 0x%x\n", + err_mask); + } +} + /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -2370,7 +2388,7 @@ int ata_dev_configure(struct ata_device *dev) dev->devslp_timing[i] = sata_setting[j]; } } - + ata_dev_config_sense_reporting(dev); dev->cdb_len = 16; } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8c8355f0792e..170e891e79af 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1637,6 +1637,56 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) return err_mask; } +/** + * ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT + * @dev: device to perform REQUEST_SENSE_SENSE_DATA_EXT to + * @cmd: scsi command for which the sense code should be set + * + * Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK + * SENSE. This function is an EH helper. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +static void ata_eh_request_sense(struct ata_queued_cmd *qc, + struct scsi_cmnd *cmd) +{ + struct ata_device *dev = qc->dev; + struct ata_taskfile tf; + unsigned int err_mask; + + if (qc->ap->pflags & ATA_PFLAG_FROZEN) { + ata_dev_warn(dev, "sense data available but port frozen\n"); + return; + } + + if (!cmd) + return; + + if (!ata_id_sense_reporting_enabled(dev->id)) { + ata_dev_warn(qc->dev, "sense data reporting disabled\n"); + return; + } + + DPRINTK("ATA request sense\n"); + + ata_tf_init(dev, &tf); + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; + tf.command = ATA_CMD_REQ_SENSE_DATA; + tf.protocol = ATA_PROT_NODATA; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + /* Ignore err_mask; ATA_ERR might be set */ + if (tf.command & ATA_SENSE) { + ata_scsi_set_sense(cmd, tf.lbah, tf.lbam, tf.lbal); + qc->flags |= ATA_QCFLAG_SENSE_VALID; + } else { + ata_dev_warn(dev, "request sense failed stat %02x emask %x\n", + tf.command, err_mask); + } +} + /** * atapi_eh_request_sense - perform ATAPI REQUEST_SENSE * @dev: device to perform REQUEST_SENSE to @@ -1838,14 +1888,23 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, return ATA_EH_RESET; } - if (stat & (ATA_ERR | ATA_DF)) + if (stat & (ATA_ERR | ATA_DF)) { qc->err_mask |= AC_ERR_DEV; - else + /* + * Sense data reporting does not work if the + * device fault bit is set. + */ + if (stat & ATA_DF) + stat &= ~ATA_SENSE; + } else { return 0; + } switch (qc->dev->class) { case ATA_DEV_ATA: case ATA_DEV_ZAC: + if (stat & ATA_SENSE) + ata_eh_request_sense(qc, qc->scsicmd); if (err & ATA_ICRC) qc->err_mask |= AC_ERR_ATA_BUS; if (err & (ATA_UNC | ATA_AMNF)) @@ -2581,14 +2640,15 @@ static void ata_eh_link_report(struct ata_link *link) #ifdef CONFIG_ATA_VERBOSE_ERROR if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | - ATA_ERR)) { + ATA_SENSE | ATA_ERR)) { if (res->command & ATA_BUSY) ata_dev_err(qc->dev, "status: { Busy }\n"); else - ata_dev_err(qc->dev, "status: { %s%s%s%s}\n", + ata_dev_err(qc->dev, "status: { %s%s%s%s%s}\n", res->command & ATA_DRDY ? "DRDY " : "", res->command & ATA_DF ? "DF " : "", res->command & ATA_DRQ ? "DRQ " : "", + res->command & ATA_SENSE ? "SENSE " : "", res->command & ATA_ERR ? "ERR " : ""); } diff --git a/include/linux/ata.h b/include/linux/ata.h index e797e1b53006..00aebc4c83ad 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -385,6 +385,8 @@ enum { SATA_SSP = 0x06, /* Software Settings Preservation */ SATA_DEVSLP = 0x09, /* Device Sleep */ + SETFEATURE_SENSE_DATA = 0xC3, /* Sense Data Reporting feature */ + /* feature values for SET_MAX */ ATA_SET_MAX_ADDR = 0x00, ATA_SET_MAX_PASSWD = 0x01, @@ -718,6 +720,20 @@ static inline bool ata_id_has_read_log_dma_ext(const u16 *id) return false; } +static inline bool ata_id_has_sense_reporting(const u16 *id) +{ + if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + return false; + return id[ATA_ID_COMMAND_SET_3] & (1 << 6); +} + +static inline bool ata_id_sense_reporting_enabled(const u16 *id) +{ + if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + return false; + return id[ATA_ID_COMMAND_SET_4] & (1 << 6); +} + /** * ata_id_major_version - get ATA level of drive * @id: Identify data -- cgit From 06dbde5f3a44248fc02e24d662ac4849202abb48 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:03 +0200 Subject: libata: Implement control mode page to select sense format Implement MODE SELECT for the control mode page to allow the OS to switch to descriptor sense. tj: Dropped s/sb/cmd->sense_buffer/ in ata_gen_ata_sense(). Added @dev description to ata_msense_ctl_mode(). Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 4 +- drivers/ata/libata-scsi.c | 116 ++++++++++++++++++++++++++++++++++------------ drivers/ata/libata.h | 3 +- include/linux/libata.h | 1 + 4 files changed, 91 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e37258b78e01..5b340ce4eeac 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1679,7 +1679,7 @@ static void ata_eh_request_sense(struct ata_queued_cmd *qc, err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); /* Ignore err_mask; ATA_ERR might be set */ if (tf.command & ATA_SENSE) { - ata_scsi_set_sense(cmd, tf.lbah, tf.lbam, tf.lbal); + ata_scsi_set_sense(dev, cmd, tf.lbah, tf.lbam, tf.lbal); qc->flags |= ATA_QCFLAG_SENSE_VALID; } else { ata_dev_warn(dev, "request sense failed stat %02x emask %x\n", @@ -1855,7 +1855,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) sense_key = (qc->result_tf.auxiliary >> 16) & 0xff; asc = (qc->result_tf.auxiliary >> 8) & 0xff; ascq = qc->result_tf.auxiliary & 0xff; - ata_scsi_set_sense(qc->scsicmd, sense_key, asc, ascq); + ata_scsi_set_sense(dev, qc->scsicmd, sense_key, asc, ascq); ata_scsi_set_sense_information(dev, qc->scsicmd, &qc->result_tf); qc->flags |= ATA_QCFLAG_SENSE_VALID; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0da03c019f27..2389247bdf6f 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,14 +270,17 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); -void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd, + u8 sk, u8 asc, u8 ascq) { + bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE); + if (!cmd) return; cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); + scsi_build_sense_buffer(d_sense, cmd->sense_buffer, sk, asc, ascq); } void ata_scsi_set_sense_information(struct ata_device *dev, @@ -384,9 +387,10 @@ struct device_attribute *ata_common_sdev_attrs[] = { }; EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); -static void ata_scsi_invalid_field(struct scsi_cmnd *cmd) +static void ata_scsi_invalid_field(struct ata_device *dev, + struct scsi_cmnd *cmd) { - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ cmd->scsi_done(cmd); } @@ -1014,7 +1018,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sense_key, &asc, &ascq, verbose); - ata_scsi_set_sense(cmd, sense_key, asc, ascq); + ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq); } else { /* * ATA PASS-THROUGH INFORMATION AVAILABLE @@ -1112,12 +1116,12 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sense_key, &asc, &ascq, verbose); - ata_scsi_set_sense(cmd, sense_key, asc, ascq); + ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq); } else { /* Could not decode error */ ata_dev_warn(dev, "could not decode error status 0x%x err_mask 0x%x\n", tf->command, qc->err_mask); - ata_scsi_set_sense(cmd, ABORTED_COMMAND, 0, 0); + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); return; } @@ -1440,7 +1444,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; skip: @@ -1679,12 +1683,12 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; out_of_range: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0); /* "Logical Block Address out of range" */ return 1; @@ -1781,12 +1785,12 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) goto out_of_range; /* treat all other errors as -EINVAL, fall through */ invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; out_of_range: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0); /* "Logical Block Address out of range" */ return 1; @@ -2358,6 +2362,7 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) /** * ata_msense_ctl_mode - Simulate MODE SENSE control mode page + * @dev: ATA device of interest * @buf: output buffer * @changeable: whether changeable parameters are requested * @@ -2366,9 +2371,12 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) * LOCKING: * None. */ -static unsigned int ata_msense_ctl_mode(u8 *buf, bool changeable) +static unsigned int ata_msense_ctl_mode(struct ata_device *dev, u8 *buf, + bool changeable) { modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable); + if (changeable && (dev->flags & ATA_DFLAG_D_SENSE)) + buf[2] |= (1 << 2); /* Descriptor sense requested */ return sizeof(def_control_mpage); } @@ -2482,13 +2490,13 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) break; case CONTROL_MPAGE: - p += ata_msense_ctl_mode(p, page_control == 1); + p += ata_msense_ctl_mode(args->dev, p, page_control == 1); break; case ALL_MPAGES: p += ata_msense_rw_recovery(p, page_control == 1); p += ata_msense_caching(args->id, p, page_control == 1); - p += ata_msense_ctl_mode(p, page_control == 1); + p += ata_msense_ctl_mode(args->dev, p, page_control == 1); break; default: /* invalid page code */ @@ -2521,12 +2529,12 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) return 0; invalid_fld: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; saving_not_supp: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); + ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); /* "Saving parameters not supported" */ return 1; } @@ -3163,7 +3171,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); /* "Invalid field in cdb" */ return 1; } @@ -3228,7 +3236,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); /* "Invalid field in cdb" */ return 1; } @@ -3279,6 +3287,51 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc, return 0; } +/** + * ata_mselect_control - Simulate MODE SELECT for control page + * @qc: Storage for translated ATA taskfile + * @buf: input buffer + * @len: number of valid bytes in the input buffer + * + * Prepare a taskfile to modify caching information for the device. + * + * LOCKING: + * None. + */ +static int ata_mselect_control(struct ata_queued_cmd *qc, + const u8 *buf, int len) +{ + struct ata_device *dev = qc->dev; + char mpage[CONTROL_MPAGE_LEN]; + u8 d_sense; + + /* + * The first two bytes of def_control_mpage are a header, so offsets + * in mpage are off by 2 compared to buf. Same for len. + */ + + if (len != CONTROL_MPAGE_LEN - 2) + return -EINVAL; + + d_sense = buf[0] & (1 << 2); + + /* + * Check that read-only bits are not modified. + */ + ata_msense_ctl_mode(dev, mpage, false); + mpage[2] &= ~(1 << 2); + mpage[2] |= d_sense; + if (memcmp(mpage + 2, buf, CONTROL_MPAGE_LEN - 2) != 0) + return -EINVAL; + if (d_sense & (1 << 2)) + dev->flags |= ATA_DFLAG_D_SENSE; + else + dev->flags &= ~ATA_DFLAG_D_SENSE; + qc->scsicmd->result = SAM_STAT_GOOD; + qc->scsicmd->scsi_done(qc->scsicmd); + return 0; +} + /** * ata_scsiop_mode_select - Simulate MODE SELECT 6, 10 commands * @qc: Storage for translated ATA taskfile @@ -3381,7 +3434,10 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) if (ata_mselect_caching(qc, p, pg_len) < 0) goto invalid_param; break; - + case CONTROL_MPAGE: + if (ata_mselect_control(qc, p, pg_len) < 0) + goto invalid_param; + break; default: /* invalid page code */ goto invalid_param; } @@ -3397,17 +3453,17 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) invalid_fld: /* "Invalid field in CDB" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); return 1; invalid_param: /* "Invalid field in parameter list" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x26, 0x0); return 1; invalid_param_len: /* "Parameter list length error" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); return 1; skip: @@ -3611,12 +3667,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) switch(scsicmd[0]) { /* TODO: worth improving? */ case FORMAT_UNIT: - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); break; case INQUIRY: if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); else switch (scsicmd[2]) { @@ -3642,7 +3698,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2); break; default: - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); break; } break; @@ -3660,7 +3716,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); else - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); break; case REPORT_LUNS: @@ -3668,7 +3724,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) break; case REQUEST_SENSE: - ata_scsi_set_sense(cmd, 0, 0, 0); + ata_scsi_set_sense(dev, cmd, 0, 0, 0); cmd->result = (DRIVER_SENSE << 24); cmd->scsi_done(cmd); break; @@ -3692,12 +3748,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) if ((tmp8 == 0x4) && (!scsicmd[3]) && (!scsicmd[4])) ata_scsi_rbuf_fill(&args, ata_scsiop_noop); else - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); break; /* all other commands */ default: - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0); + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0); /* "Invalid command operation code" */ cmd->scsi_done(cmd); break; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index dbc67604b3c5..3b301a48007c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -138,7 +138,8 @@ extern int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap, int sync); extern int ata_scsi_offline_dev(struct ata_device *dev); -extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); +extern void ata_scsi_set_sense(struct ata_device *dev, + struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); extern void ata_scsi_set_sense_information(struct ata_device *dev, struct scsi_cmnd *cmd, const struct ata_taskfile *tf); diff --git a/include/linux/libata.h b/include/linux/libata.h index 2c4ebef79d0c..a418bca0df0d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -180,6 +180,7 @@ enum { ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */ ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */ ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */ + ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */ ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ -- cgit From 4113652252fad972e0c191b1e536dc74a6faebdc Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 1 Apr 2016 21:38:16 +0200 Subject: reset: Add missing function stub for device_reset The Mediatek's thermal driver fails to compile when the RESET_CONTROLLER option is not set. Logically, as the driver depends on this option to compile, the Kconfig should select it but actually that is not correct because the Kconfig provides also the COMPILE_TEST to increase the compile test coverage. By providing the missing 'device_reset' stub for the driver in reset.h, that let the kernel to compile on different platforms with the Mediatek thermal driver enabled with the COMPILE_TEST option. Signed-off-by: Daniel Lezcano Signed-off-by: Philipp Zabel --- include/linux/reset.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/reset.h b/include/linux/reset.h index a552134a209e..ec0306ce7b92 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -56,6 +56,12 @@ static inline void reset_control_put(struct reset_control *rstc) WARN_ON(1); } +static inline int __must_check device_reset(struct device *dev) +{ + WARN_ON(1); + return -ENOTSUPP; +} + static inline int device_reset_optional(struct device *dev) { return -ENOTSUPP; -- cgit From 974e0a4537f556867483f493c7f67ccdcb7fc504 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 4 Apr 2016 12:17:09 -0400 Subject: libata-core: Allow longer timeout for drive spinup from PUIS When spinning up a drive from powered on standby mode (PUIS), SETFEATURES_SPINUP is executed with the default timeout used for any SETFEATURES subcommand, that is 5+10 seconds. The total 15s is too short for some drives to complete spinup (e.g. drives with a large indirection table stored on media), resulting in ata_dev_read_id to fail twice on the execution of SETFEATURES_SPINUP. For this feature, allow a larger default timeout of 30 seconds. However, in the same spirit as with the timeout of other feature subcommands, do not ignore ata_probe_timeout if it is set). Signed-off-by: Damien Le Moal Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 6 +++++- include/linux/ata.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 55e257c268dd..7b21021dbf7d 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4528,6 +4528,7 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) { struct ata_taskfile tf; unsigned int err_mask; + unsigned long timeout = 0; /* set up set-features taskfile */ DPRINTK("set features - SATA features\n"); @@ -4539,7 +4540,10 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) tf.protocol = ATA_PROT_NODATA; tf.nsect = feature; - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (enable == SETFEATURES_SPINUP) + timeout = ata_probe_timeout ? + ata_probe_timeout * 1000 : SETFEATURES_SPINUP_TIMEOUT; + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout); DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; diff --git a/include/linux/ata.h b/include/linux/ata.h index c1a2f345cbe6..f310ec0f072e 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -371,7 +371,8 @@ enum { SETFEATURES_AAM_ON = 0x42, SETFEATURES_AAM_OFF = 0xC2, - SETFEATURES_SPINUP = 0x07, /* Spin-up drive */ + SETFEATURES_SPINUP = 0x07, /* Spin-up drive */ + SETFEATURES_SPINUP_TIMEOUT = 30000, /* 30s timeout for drive spin-up from PUIS */ SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */ SETFEATURES_SATA_DISABLE = 0x90, /* Disable use of SATA feature */ -- cgit From 77ed2c5745d93416992857d124f35834b62b3e70 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 8 Mar 2016 20:01:32 +0900 Subject: android,lowmemorykiller: Don't abuse TIF_MEMDIE. Currently, lowmemorykiller (LMK) is using TIF_MEMDIE for two purposes. One is to remember processes killed by LMK, and the other is to accelerate termination of processes killed by LMK. But since LMK is invoked as a memory shrinker function, there still should be some memory available. It is very likely that memory allocations by processes killed by LMK will succeed without using ALLOC_NO_WATERMARKS via TIF_MEMDIE. Even if their allocations cannot escape from memory allocation loop unless they use ALLOC_NO_WATERMARKS, lowmem_deathpending_timeout can guarantee forward progress by choosing next victim process. On the other hand, mark_oom_victim() assumes that it must be called with oom_lock held and it must not be called after oom_killer_disable() was called. But LMK is calling it without holding oom_lock and checking oom_killer_disabled. It is possible that LMK calls mark_oom_victim() due to allocation requests by kernel threads after current thread returned from oom_killer_disabled(). This will break synchronization for PM/suspend. This patch introduces per a task_struct flag for remembering processes killed by LMK, and replaces TIF_MEMDIE with that flag. By applying this patch, assumption by mark_oom_victim() becomes true. Signed-off-by: Tetsuo Handa Acked-by: Michal Hocko Cc: Arve Hjonnevag Cc: Riley Andrews Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 9 ++------- include/linux/sched.h | 4 ++++ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 2509e5df7244..c79f22425fa8 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -131,7 +131,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (!p) continue; - if (test_tsk_thread_flag(p, TIF_MEMDIE) && + if (task_lmk_waiting(p) && p->mm && time_before_eq(jiffies, lowmem_deathpending_timeout)) { task_unlock(p); rcu_read_unlock(); @@ -162,13 +162,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (selected) { task_lock(selected); send_sig(SIGKILL, selected, 0); - /* - * FIXME: lowmemorykiller shouldn't abuse global OOM killer - * infrastructure. There is no real reason why the selected - * task should have access to the memory reserves. - */ if (selected->mm) - mark_oom_victim(selected); + task_set_lmk_waiting(selected); task_unlock(selected); lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" " to free %ldkB on behalf of '%s' (%d) because\n" diff --git a/include/linux/sched.h b/include/linux/sched.h index 60bba7e032dc..9dff190e6a0a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2184,6 +2184,7 @@ static inline void memalloc_noio_restore(unsigned int flags) #define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */ #define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */ #define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */ +#define PFA_LMK_WAITING 3 /* Lowmemorykiller is waiting */ #define TASK_PFA_TEST(name, func) \ @@ -2207,6 +2208,9 @@ TASK_PFA_TEST(SPREAD_SLAB, spread_slab) TASK_PFA_SET(SPREAD_SLAB, spread_slab) TASK_PFA_CLEAR(SPREAD_SLAB, spread_slab) +TASK_PFA_TEST(LMK_WAITING, lmk_waiting) +TASK_PFA_SET(LMK_WAITING, lmk_waiting) + /* * task->jobctl flags */ -- cgit From 4da56b99d99e5a7df2b7f11e87bfea935f909732 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 4 Apr 2016 14:46:42 +0100 Subject: mm/vmap: Add a notifier for when we run out of vmap address space vmaps are temporary kernel mappings that may be of long duration. Reusing a vmap on an object is preferrable for a driver as the cost of setting up the vmap can otherwise dominate the operation on the object. However, the vmap address space is rather limited on 32bit systems and so we add a notification for vmap pressure in order for the driver to release any cached vmappings. The interface is styled after the oom-notifier where the callees are passed a pointer to an unsigned long counter for them to indicate if they have freed any space. v2: Guard the blocking notifier call with gfpflags_allow_blocking() v3: Correct typo in forward declaration and move to head of file Signed-off-by: Chris Wilson Cc: Andrew Morton Cc: David Rientjes Cc: Roman Peniaev Cc: Mel Gorman Cc: linux-mm@kvack.org Cc: linux-kernel@vger.kernel.org Acked-by: Andrew Morton # for inclusion via DRM Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Link: http://patchwork.freedesktop.org/patch/msgid/1459777603-23618-3-git-send-email-chris@chris-wilson.co.uk Reviewed-by: Joonas Lahtinen --- include/linux/vmalloc.h | 4 ++++ mm/vmalloc.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'include/linux') diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index d1f1d338af20..8b51df3ab334 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -8,6 +8,7 @@ #include struct vm_area_struct; /* vma defining user mapping in mm_types.h */ +struct notifier_block; /* in notifier.h */ /* bits in flags of vmalloc's vm_struct below */ #define VM_IOREMAP 0x00000001 /* ioremap() and friends */ @@ -187,4 +188,7 @@ pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms) #define VMALLOC_TOTAL 0UL #endif +int register_vmap_purge_notifier(struct notifier_block *nb); +int unregister_vmap_purge_notifier(struct notifier_block *nb); + #endif /* _LINUX_VMALLOC_H */ diff --git a/mm/vmalloc.c b/mm/vmalloc.c index fb42a5bffe47..12d27ac303ae 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -344,6 +345,8 @@ static void __insert_vmap_area(struct vmap_area *va) static void purge_vmap_area_lazy(void); +static BLOCKING_NOTIFIER_HEAD(vmap_notify_list); + /* * Allocate a region of KVA of the specified size and alignment, within the * vstart and vend. @@ -363,6 +366,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, BUG_ON(offset_in_page(size)); BUG_ON(!is_power_of_2(align)); + might_sleep_if(gfpflags_allow_blocking(gfp_mask)); + va = kmalloc_node(sizeof(struct vmap_area), gfp_mask & GFP_RECLAIM_MASK, node); if (unlikely(!va)) @@ -468,6 +473,16 @@ overflow: purged = 1; goto retry; } + + if (gfpflags_allow_blocking(gfp_mask)) { + unsigned long freed = 0; + blocking_notifier_call_chain(&vmap_notify_list, 0, &freed); + if (freed > 0) { + purged = 0; + goto retry; + } + } + if (printk_ratelimit()) pr_warn("vmap allocation for size %lu failed: " "use vmalloc= to increase size.\n", size); @@ -475,6 +490,18 @@ overflow: return ERR_PTR(-EBUSY); } +int register_vmap_purge_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&vmap_notify_list, nb); +} +EXPORT_SYMBOL_GPL(register_vmap_purge_notifier); + +int unregister_vmap_purge_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&vmap_notify_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_vmap_purge_notifier); + static void __free_vmap_area(struct vmap_area *va) { BUG_ON(RB_EMPTY_NODE(&va->rb_node)); -- cgit From c663e5f56737757db4d0b317c510ab505f93cecb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 22 Mar 2016 10:51:16 +0100 Subject: gpio: support native single-ended hardware drivers Some GPIO controllers has a special hardware bit we can flip to support open drain / source. This means that on these hardwares we do not need to emulate OD/OS by setting the line to input instead of actively driving it high/low. Add an optional vtable callback to the driver set_single_ended() so that driver can implement this in hardware if they have it. We may need a pinctrl_gpio_set_config() call at some point to propagate this down to a backing pin control device on systems with split GPIO/pin control. Reported-by: Michael Hennerich Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 52 ++++++++++++++++++++++++++++++++------------- include/linux/gpio/driver.h | 25 +++++++++++++++++++++- 2 files changed, 61 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 72065532c1c7..1edc830a1b51 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1509,8 +1509,8 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input); static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) { - struct gpio_chip *chip; - int status = -EINVAL; + struct gpio_chip *gc = desc->gdev->chip; + int ret; /* GPIOs used for IRQs shall not be set as output */ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { @@ -1520,28 +1520,50 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) return -EIO; } - /* Open drain pin should not be driven to 1 */ - if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - return gpiod_direction_input(desc); - - /* Open source pin should not be driven to 0 */ - if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - return gpiod_direction_input(desc); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { + /* First see if we can enable open drain in hardware */ + if (gc->set_single_ended) { + ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc), + LINE_MODE_OPEN_DRAIN); + if (!ret) + goto set_output_value; + } + /* Emulate open drain by not actively driving the line high */ + if (value) + return gpiod_direction_input(desc); + } + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { + if (gc->set_single_ended) { + ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc), + LINE_MODE_OPEN_SOURCE); + if (!ret) + goto set_output_value; + } + /* Emulate open source by not actively driving the line low */ + if (!value) + return gpiod_direction_input(desc); + } else { + /* Make sure to disable open drain/source hardware, if any */ + if (gc->set_single_ended) + gc->set_single_ended(gc, + gpio_chip_hwgpio(desc), + LINE_MODE_PUSH_PULL); + } - chip = desc->gdev->chip; - if (!chip->set || !chip->direction_output) { +set_output_value: + if (!gc->set || !gc->direction_output) { gpiod_warn(desc, "%s: missing set() or direction_output() operations\n", __func__); return -EIO; } - status = chip->direction_output(chip, gpio_chip_hwgpio(desc), value); - if (status == 0) + ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), value); + if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); trace_gpio_value(desc_to_gpio(desc), 0, value); - trace_gpio_direction(desc_to_gpio(desc), 0, status); - return status; + trace_gpio_direction(desc_to_gpio(desc), 0, ret); + return ret; } /** diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index bee976f82788..50882e09289b 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -19,6 +19,18 @@ struct gpio_device; #ifdef CONFIG_GPIOLIB +/** + * enum single_ended_mode - mode for single ended operation + * @LINE_MODE_PUSH_PULL: normal mode for a GPIO line, drive actively high/low + * @LINE_MODE_OPEN_DRAIN: set line to be open drain + * @LINE_MODE_OPEN_SOURCE: set line to be open source + */ +enum single_ended_mode { + LINE_MODE_PUSH_PULL, + LINE_MODE_OPEN_DRAIN, + LINE_MODE_OPEN_SOURCE, +}; + /** * struct gpio_chip - abstract a GPIO controller * @label: a functional name for the GPIO device, such as a part @@ -38,7 +50,15 @@ struct gpio_device; * @set: assigns output value for signal "offset" * @set_multiple: assigns output values for multiple signals defined by "mask" * @set_debounce: optional hook for setting debounce time for specified gpio in - * interrupt triggered gpio chips + * interrupt triggered gpio chips + * @set_single_ended: optional hook for setting a line as open drain, open + * source, or non-single ended (restore from open drain/source to normal + * push-pull mode) this should be implemented if the hardware supports + * open drain or open source settings. The GPIOlib will otherwise try + * to emulate open drain/source by not actively driving lines high/low + * if a consumer request this. The driver may return -ENOTSUPP if e.g. + * it supports just open drain but not open source and is called + * with LINE_MODE_OPEN_SOURCE as mode argument. * @to_irq: optional hook supporting non-static gpio_to_irq() mappings; * implementation may not sleep * @dbg_show: optional routine to show contents in debugfs; default code @@ -130,6 +150,9 @@ struct gpio_chip { int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce); + int (*set_single_ended)(struct gpio_chip *chip, + unsigned offset, + enum single_ended_mode mode); int (*to_irq)(struct gpio_chip *chip, unsigned offset); -- cgit From f9cd476123ced488e628339becedb2cf3243a58a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 4 Apr 2016 22:44:59 +0200 Subject: dmaengine: pl08x: allocate OF slave channel data at probe time The current OF translation of channels can never work with any DMA client using the DMA channels directly: the only way to get the channels initialized properly is in the dma_async_device_register() call, where chan->dev etc is allocated and initialized. Allocate and initialize all possible DMA channels and only augment a target channel with the periph_buses at of_xlate(). Remove some const settings to make things work. Cc: Maxime Ripard Tested-by: Joachim Eastwood Tested-by: Johannes Stezenbach Signed-off-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 86 +++++++++++++++++++++++++++++++--------------- include/linux/amba/pl08x.h | 2 +- 2 files changed, 59 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 9b42c0588550..81db1c4811ce 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -107,16 +107,20 @@ struct pl08x_driver_data; /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives * @channels: the number of channels available in this variant + * @signals: the number of request signals available from the hardware * @dualmaster: whether this version supports dual AHB masters or not. * @nomadik: whether the channels have Nomadik security extension bits * that need to be checked for permission before use and some registers are * missing * @pl080s: whether this version is a PL080S, which has separate register and * LLI word for transfer size. + * @max_transfer_size: the maximum single element transfer size for this + * PL08x variant. */ struct vendor_data { u8 config_offset; u8 channels; + u8 signals; bool dualmaster; bool nomadik; bool pl080s; @@ -235,7 +239,7 @@ struct pl08x_dma_chan { struct virt_dma_chan vc; struct pl08x_phy_chan *phychan; const char *name; - const struct pl08x_channel_data *cd; + struct pl08x_channel_data *cd; struct dma_slave_config cfg; struct pl08x_txd *at; struct pl08x_driver_data *host; @@ -1909,6 +1913,12 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, if (slave) { chan->cd = &pl08x->pd->slave_channels[i]; + /* + * Some implementations have muxed signals, whereas some + * use a mux in front of the signals and need dynamic + * assignment of signals. + */ + chan->signal = i; pl08x_dma_slave_init(chan); } else { chan->cd = &pl08x->pd->memcpy_channel; @@ -2050,40 +2060,33 @@ static struct dma_chan *pl08x_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct pl08x_driver_data *pl08x = ofdma->of_dma_data; - struct pl08x_channel_data *data; - struct pl08x_dma_chan *chan; struct dma_chan *dma_chan; + struct pl08x_dma_chan *plchan; if (!pl08x) return NULL; - if (dma_spec->args_count != 2) + if (dma_spec->args_count != 2) { + dev_err(&pl08x->adev->dev, + "DMA channel translation requires two cells\n"); return NULL; + } dma_chan = pl08x_find_chan_id(pl08x, dma_spec->args[0]); - if (dma_chan) - return dma_get_slave_channel(dma_chan); - - chan = devm_kzalloc(pl08x->slave.dev, sizeof(*chan) + sizeof(*data), - GFP_KERNEL); - if (!chan) + if (!dma_chan) { + dev_err(&pl08x->adev->dev, + "DMA slave channel not found\n"); return NULL; + } - data = (void *)&chan[1]; - data->bus_id = "(none)"; - data->periph_buses = dma_spec->args[1]; - - chan->cd = data; - chan->host = pl08x; - chan->slave = true; - chan->name = data->bus_id; - chan->state = PL08X_CHAN_IDLE; - chan->signal = dma_spec->args[0]; - chan->vc.desc_free = pl08x_desc_free; - - vchan_init(&chan->vc, &pl08x->slave); + plchan = to_pl08x_chan(dma_chan); + dev_dbg(&pl08x->adev->dev, + "translated channel for signal %d\n", + dma_spec->args[0]); - return dma_get_slave_channel(&chan->vc.chan); + /* Augment channel data for applicable AHB buses */ + plchan->cd->periph_buses = dma_spec->args[1]; + return dma_get_slave_channel(dma_chan); } static int pl08x_of_probe(struct amba_device *adev, @@ -2091,9 +2094,11 @@ static int pl08x_of_probe(struct amba_device *adev, struct device_node *np) { struct pl08x_platform_data *pd; + struct pl08x_channel_data *chanp = NULL; u32 cctl_memcpy = 0; u32 val; int ret; + int i; pd = devm_kzalloc(&adev->dev, sizeof(*pd), GFP_KERNEL); if (!pd) @@ -2195,6 +2200,27 @@ static int pl08x_of_probe(struct amba_device *adev, /* Use the buses that can access memory, obviously */ pd->memcpy_channel.periph_buses = pd->mem_buses; + /* + * Allocate channel data for all possible slave channels (one + * for each possible signal), channels will then be allocated + * for a device and have it's AHB interfaces set up at + * translation time. + */ + chanp = devm_kcalloc(&adev->dev, + pl08x->vd->signals, + sizeof(struct pl08x_channel_data), + GFP_KERNEL); + if (!chanp) + return -ENOMEM; + + pd->slave_channels = chanp; + for (i = 0; i < pl08x->vd->signals; i++) { + /* chanp->periph_buses will be assigned at translation */ + chanp->bus_id = kasprintf(GFP_KERNEL, "slave%d", i); + chanp++; + } + pd->num_slave_channels = pl08x->vd->signals; + pl08x->pd = pd; return of_dma_controller_register(adev->dev.of_node, pl08x_of_xlate, @@ -2234,6 +2260,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_pl08x; } + /* Assign useful pointers to the driver state */ + pl08x->adev = adev; + pl08x->vd = vd; + /* Initialize memcpy engine */ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); pl08x->memcpy.dev = &adev->dev; @@ -2284,10 +2314,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) } } - /* Assign useful pointers to the driver state */ - pl08x->adev = adev; - pl08x->vd = vd; - /* By default, AHB1 only. If dualmaster, from platform */ pl08x->lli_buses = PL08X_AHB1; pl08x->mem_buses = PL08X_AHB1; @@ -2438,6 +2464,7 @@ out_no_pl08x: static struct vendor_data vendor_pl080 = { .config_offset = PL080_CH_CONFIG, .channels = 8, + .signals = 16, .dualmaster = true, .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, }; @@ -2445,6 +2472,7 @@ static struct vendor_data vendor_pl080 = { static struct vendor_data vendor_nomadik = { .config_offset = PL080_CH_CONFIG, .channels = 8, + .signals = 32, .dualmaster = true, .nomadik = true, .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, @@ -2453,6 +2481,7 @@ static struct vendor_data vendor_nomadik = { static struct vendor_data vendor_pl080s = { .config_offset = PL080S_CH_CONFIG, .channels = 8, + .signals = 32, .pl080s = true, .max_transfer_size = PL080S_CONTROL_TRANSFER_SIZE_MASK, }; @@ -2460,6 +2489,7 @@ static struct vendor_data vendor_pl080s = { static struct vendor_data vendor_pl081 = { .config_offset = PL080_CH_CONFIG, .channels = 2, + .signals = 16, .dualmaster = false, .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, }; diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 10fe2a211c2e..27e9ec8778eb 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -86,7 +86,7 @@ struct pl08x_channel_data { * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2 */ struct pl08x_platform_data { - const struct pl08x_channel_data *slave_channels; + struct pl08x_channel_data *slave_channels; unsigned int num_slave_channels; struct pl08x_channel_data memcpy_channel; int (*get_xfer_signal)(const struct pl08x_channel_data *); -- cgit From fa59507f720077a856c9952a31cfd45cd97ef6f9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 30 Mar 2016 12:56:28 +0300 Subject: usb: otg-fsm: Add documentation for struct otg_fsm struct otg_fsm is the interface to the OTG state machine. Document the input, output and internal state variables. Definations are taken from Table 7-2 and Table 7-4 of the USB OTG & EH Specification Rev.2.0 Re-arrange some of the members as per use case for more clarity. Signed-off-by: Roger Quadros Acked-by: Peter Chen Signed-off-by: Peter Chen --- include/linux/usb/otg-fsm.h | 90 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index 24198e16f849..8eec0c261be5 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -72,37 +72,113 @@ enum otg_fsm_timer { NUM_OTG_FSM_TIMERS, }; -/* OTG state machine according to the OTG spec */ +/** + * struct otg_fsm - OTG state machine according to the OTG spec + * + * OTG hardware Inputs + * + * Common inputs for A and B device + * @id: TRUE for B-device, FALSE for A-device. + * @adp_change: TRUE when current ADP measurement (n) value, compared to the + * ADP measurement taken at n-2, differs by more than CADP_THR + * @power_up: TRUE when the OTG device first powers up its USB system and + * ADP measurement taken if ADP capable + * + * A-Device state inputs + * @a_srp_det: TRUE if the A-device detects SRP + * @a_vbus_vld: TRUE when VBUS voltage is in regulation + * @b_conn: TRUE if the A-device detects connection from the B-device + * @a_bus_resume: TRUE when the B-device detects that the A-device is signaling + * a resume (K state) + * B-Device state inputs + * @a_bus_suspend: TRUE when the B-device detects that the A-device has put the + * bus into suspend + * @a_conn: TRUE if the B-device detects a connection from the A-device + * @b_se0_srp: TRUE when the line has been at SE0 for more than the minimum + * time before generating SRP + * @b_ssend_srp: TRUE when the VBUS has been below VOTG_SESS_VLD for more than + * the minimum time before generating SRP + * @b_sess_vld: TRUE when the B-device detects that the voltage on VBUS is + * above VOTG_SESS_VLD + * @test_device: TRUE when the B-device switches to B-Host and detects an OTG + * test device. This must be set by host/hub driver + * + * Application inputs (A-Device) + * @a_bus_drop: TRUE when A-device application needs to power down the bus + * @a_bus_req: TRUE when A-device application wants to use the bus. + * FALSE to suspend the bus + * + * Application inputs (B-Device) + * @b_bus_req: TRUE during the time that the Application running on the + * B-device wants to use the bus + * + * Auxilary inputs (OTG v1.3 only. Obsolete now.) + * @a_sess_vld: TRUE if the A-device detects that VBUS is above VA_SESS_VLD + * @b_bus_suspend: TRUE when the A-device detects that the B-device has put + * the bus into suspend + * @b_bus_resume: TRUE when the A-device detects that the B-device is signaling + * resume on the bus + * + * OTG Output status. Read only for users. Updated by OTG FSM helpers defined + * in this file + * + * Outputs for Both A and B device + * @drv_vbus: TRUE when A-device is driving VBUS + * @loc_conn: TRUE when the local device has signaled that it is connected + * to the bus + * @loc_sof: TRUE when the local device is generating activity on the bus + * @adp_prb: TRUE when the local device is in the process of doing + * ADP probing + * + * Outputs for B-device state + * @adp_sns: TRUE when the B-device is in the process of carrying out + * ADP sensing + * @data_pulse: TRUE when the B-device is performing data line pulsing + * + * Internal Variables + * + * a_set_b_hnp_en: TRUE when the A-device has successfully set the + * b_hnp_enable bit in the B-device. + * Unused as OTG fsm uses otg->host->b_hnp_enable instead + * b_srp_done: TRUE when the B-device has completed initiating SRP + * b_hnp_enable: TRUE when the B-device has accepted the + * SetFeature(b_hnp_enable) B-device. + * Unused as OTG fsm uses otg->gadget->b_hnp_enable instead + * a_clr_err: Asserted (by application ?) to clear a_vbus_err due to an + * overcurrent condition and causes the A-device to transition + * to a_wait_vfall + */ struct otg_fsm { /* Input */ int id; int adp_change; int power_up; - int test_device; - int a_bus_drop; - int a_bus_req; int a_srp_det; int a_vbus_vld; int b_conn; int a_bus_resume; int a_bus_suspend; int a_conn; - int b_bus_req; int b_se0_srp; int b_ssend_srp; int b_sess_vld; + int test_device; + int a_bus_drop; + int a_bus_req; + int b_bus_req; + /* Auxilary inputs */ int a_sess_vld; int b_bus_resume; int b_bus_suspend; /* Output */ - int data_pulse; int drv_vbus; int loc_conn; int loc_sof; int adp_prb; int adp_sns; + int data_pulse; /* Internal variables */ int a_set_b_hnp_en; @@ -110,7 +186,7 @@ struct otg_fsm { int b_hnp_enable; int a_clr_err; - /* Informative variables */ + /* Informative variables. All unused as of now */ int a_bus_drop_inf; int a_bus_req_inf; int a_clr_err_inf; -- cgit From 4e332df63487418ec512c3c376c07df9ab3ae035 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 30 Mar 2016 12:56:29 +0300 Subject: usb: otg-fsm: support multiple instances Move the state_changed variable into struct otg_fsm so that we can support multiple instances. Signed-off-by: Roger Quadros Acked-by: Peter Chen Signed-off-by: Peter Chen --- drivers/usb/common/usb-otg-fsm.c | 10 ++++------ include/linux/usb/otg-fsm.h | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index 504708f59b93..9059b7dc185e 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -61,8 +61,6 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) return 0; } -static int state_changed; - /* Called when leaving a state. Do state clean up jobs here */ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { @@ -208,7 +206,6 @@ static void otg_start_hnp_polling(struct otg_fsm *fsm) /* Called when entering a state */ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) { - state_changed = 1; if (fsm->otg->state == new_state) return 0; VDBG("Set state: %s\n", usb_otg_state_string(new_state)); @@ -324,6 +321,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) } fsm->otg->state = new_state; + fsm->state_changed = 1; return 0; } @@ -335,7 +333,7 @@ int otg_statemachine(struct otg_fsm *fsm) mutex_lock(&fsm->lock); state = fsm->otg->state; - state_changed = 0; + fsm->state_changed = 0; /* State machine state change judgement */ switch (state) { @@ -448,7 +446,7 @@ int otg_statemachine(struct otg_fsm *fsm) } mutex_unlock(&fsm->lock); - VDBG("quit statemachine, changed = %d\n", state_changed); - return state_changed; + VDBG("quit statemachine, changed = %d\n", fsm->state_changed); + return fsm->state_changed; } EXPORT_SYMBOL_GPL(otg_statemachine); diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index 8eec0c261be5..7a0350535cb1 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -210,6 +210,7 @@ struct otg_fsm { struct mutex lock; u8 *host_req_flag; struct delayed_work hnp_polling_work; + bool state_changed; }; struct otg_fsm_ops { -- cgit From e68503bd6836ba765dc8e0ee77ea675fedc07e41 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: KEYS: Generalise system_verify_data() to provide access to internal content Generalise system_verify_data() to provide access to internal content through a callback. This allows all the PKCS#7 stuff to be hidden inside this function and removed from the PE file parser and the PKCS#7 test key. If external content is not required, NULL should be passed as data to the function. If the callback is not required, that can be set to NULL. The function is now called verify_pkcs7_signature() to contrast with verify_pefile_signature() and the definitions of both have been moved into linux/verification.h along with the key_being_used_for enum. Signed-off-by: David Howells --- arch/x86/kernel/kexec-bzimage64.c | 18 ++------- certs/system_keyring.c | 45 ++++++++++++++++----- crypto/asymmetric_keys/Kconfig | 4 +- crypto/asymmetric_keys/mscode_parser.c | 21 ++++------ crypto/asymmetric_keys/pkcs7_key_type.c | 72 +++++++++++++-------------------- crypto/asymmetric_keys/pkcs7_parser.c | 21 +++++----- crypto/asymmetric_keys/verify_pefile.c | 40 +++++------------- crypto/asymmetric_keys/verify_pefile.h | 5 +-- include/crypto/pkcs7.h | 3 +- include/crypto/public_key.h | 14 ------- include/keys/asymmetric-type.h | 1 + include/keys/system_keyring.h | 7 +--- include/linux/verification.h | 50 +++++++++++++++++++++++ include/linux/verify_pefile.h | 22 ---------- kernel/module_signing.c | 5 ++- 15 files changed, 155 insertions(+), 173 deletions(-) create mode 100644 include/linux/verification.h delete mode 100644 include/linux/verify_pefile.h (limited to 'include/linux') diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 2af478e3fd4e..f2356bda2b05 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include @@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data) #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) { - bool trusted; - int ret; - - ret = verify_pefile_signature(kernel, kernel_len, - system_trusted_keyring, - VERIFYING_KEXEC_PE_SIGNATURE, - &trusted); - if (ret < 0) - return ret; - if (!trusted) - return -EKEYREJECTED; - return 0; + return verify_pefile_signature(kernel, kernel_len, + NULL, + VERIFYING_KEXEC_PE_SIGNATURE); } #endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index f4180326c2e1..a83bffedc0aa 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -108,16 +108,25 @@ late_initcall(load_system_certificate_list); #ifdef CONFIG_SYSTEM_DATA_VERIFICATION /** - * Verify a PKCS#7-based signature on system data. - * @data: The data to be verified. + * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data. + * @data: The data to be verified (NULL if expecting internal data). * @len: Size of @data. * @raw_pkcs7: The PKCS#7 message that is the signature. * @pkcs7_len: The size of @raw_pkcs7. + * @trusted_keys: Trusted keys to use (NULL for system_trusted_keyring). * @usage: The use to which the key is being put. + * @view_content: Callback to gain access to content. + * @ctx: Context for callback. */ -int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage) +int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + int untrusted_error, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx) { struct pkcs7_message *pkcs7; bool trusted; @@ -128,7 +137,7 @@ int system_verify_data(const void *data, unsigned long len, return PTR_ERR(pkcs7); /* The data should be detached - so we need to supply it. */ - if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) { + if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) { pr_err("PKCS#7 signature with non-detached data\n"); ret = -EBADMSG; goto error; @@ -138,13 +147,29 @@ int system_verify_data(const void *data, unsigned long len, if (ret < 0) goto error; - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); + if (!trusted_keys) + trusted_keys = system_trusted_keyring; + ret = pkcs7_validate_trust(pkcs7, trusted_keys, &trusted); if (ret < 0) goto error; - if (!trusted) { + if (!trusted && untrusted_error) { pr_err("PKCS#7 signature not signed with a trusted key\n"); - ret = -ENOKEY; + ret = untrusted_error; + goto error; + } + + if (view_content) { + size_t asn1hdrlen; + + ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen); + if (ret < 0) { + if (ret == -ENODATA) + pr_devel("PKCS#7 message does not contain data\n"); + goto error; + } + + ret = view_content(ctx, data, len, asn1hdrlen); } error: @@ -152,6 +177,6 @@ error: pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(system_verify_data); +EXPORT_SYMBOL_GPL(verify_pkcs7_signature); #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 91a7e047a765..f7d2ef9789d8 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER config PKCS7_TEST_KEY tristate "PKCS#7 testing key type" - depends on PKCS7_MESSAGE_PARSER - select SYSTEM_TRUSTED_KEYRING + depends on SYSTEM_DATA_VERIFICATION help This option provides a type of key that can be loaded up from a PKCS#7 message - provided the message is signed by a trusted key. If @@ -54,6 +53,7 @@ config PKCS7_TEST_KEY config SIGNED_PE_FILE_VERIFICATION bool "Support for PE file signature verification" depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION select ASN1 select OID_REGISTRY help diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 3242cbfaeaa2..6a76d5c70ef6 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -21,19 +21,13 @@ /* * Parse a Microsoft Individual Code Signing blob */ -int mscode_parse(struct pefile_context *ctx) +int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen) { - const void *content_data; - size_t data_len; - int ret; - - ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1); - - if (ret) { - pr_debug("PKCS#7 message does not contain data\n"); - return ret; - } + struct pefile_context *ctx = _ctx; + content_data -= asn1hdrlen; + data_len += asn1hdrlen; pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), content_data); @@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen, { struct pefile_context *ctx = context; - ctx->digest = value; - ctx->digest_len = vlen; - return 0; + ctx->digest = kmemdup(value, vlen, GFP_KERNEL); + return ctx->digest ? 0 : -ENOMEM; } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index e2d0edbbc71a..ab9bf5363ecd 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -13,12 +13,9 @@ #include #include #include +#include #include -#include -#include #include -#include -#include "pkcs7_parser.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PKCS#7 testing key type"); @@ -29,59 +26,46 @@ MODULE_PARM_DESC(pkcs7_usage, "Usage to specify when verifying the PKCS#7 message"); /* - * Preparse a PKCS#7 wrapped and validated data blob. + * Retrieve the PKCS#7 message content. */ -static int pkcs7_preparse(struct key_preparsed_payload *prep) +static int pkcs7_view_content(void *ctx, const void *data, size_t len, + size_t asn1hdrlen) { - enum key_being_used_for usage = pkcs7_usage; - struct pkcs7_message *pkcs7; - const void *data, *saved_prep_data; - size_t datalen, saved_prep_datalen; - bool trusted; + struct key_preparsed_payload *prep = ctx; + const void *saved_prep_data; + size_t saved_prep_datalen; int ret; - kenter(""); - - if (usage >= NR__KEY_BEING_USED_FOR) { - pr_err("Invalid usage type %d\n", usage); - return -EINVAL; - } - saved_prep_data = prep->data; saved_prep_datalen = prep->datalen; - pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen); - if (IS_ERR(pkcs7)) { - ret = PTR_ERR(pkcs7); - goto error; - } - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error_free; - - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); - if (ret < 0) - goto error_free; - if (!trusted) - pr_warn("PKCS#7 message doesn't chain back to a trusted key\n"); - - ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false); - if (ret < 0) - goto error_free; - prep->data = data; - prep->datalen = datalen; + prep->datalen = len; + ret = user_preparse(prep); + prep->data = saved_prep_data; prep->datalen = saved_prep_datalen; - -error_free: - pkcs7_free_message(pkcs7); -error: - kleave(" = %d", ret); return ret; } +/* + * Preparse a PKCS#7 wrapped and validated data blob. + */ +static int pkcs7_preparse(struct key_preparsed_payload *prep) +{ + enum key_being_used_for usage = pkcs7_usage; + + if (usage >= NR__KEY_BEING_USED_FOR) { + pr_err("Invalid usage type %d\n", usage); + return -EINVAL; + } + + return verify_pkcs7_signature(NULL, 0, + prep->data, prep->datalen, + NULL, -ENOKEY, usage, + pkcs7_view_content, prep); +} + /* * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 835701613125..af4cd8649117 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -168,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message); * @pkcs7: The preparsed PKCS#7 message to access * @_data: Place to return a pointer to the data * @_data_len: Place to return the data length - * @want_wrapper: True if the ASN.1 object header should be included in the data + * @_headerlen: Size of ASN.1 header not included in _data * - * Get access to the data content of the PKCS#7 message, including, optionally, - * the header of the ASN.1 object that contains it. Returns -ENODATA if the - * data object was missing from the message. + * Get access to the data content of the PKCS#7 message. The size of the + * header of the ASN.1 object that contains it is also provided and can be used + * to adjust *_data and *_data_len to get the entire object. + * + * Returns -ENODATA if the data object was missing from the message. */ int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_data_len, - bool want_wrapper) + size_t *_headerlen) { - size_t wrapper; - if (!pkcs7->data) return -ENODATA; - wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; - *_data = pkcs7->data - wrapper; - *_data_len = pkcs7->data_len + wrapper; + *_data = pkcs7->data; + *_data_len = pkcs7->data_len; + if (_headerlen) + *_headerlen = pkcs7->data_hdrlen; return 0; } EXPORT_SYMBOL_GPL(pkcs7_get_content_data); diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 7e8c2338ae25..265351075b0e 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include "verify_pefile.h" @@ -392,9 +392,8 @@ error_no_desc: * verify_pefile_signature - Verify the signature on a PE binary image * @pebuf: Buffer containing the PE binary image * @pelen: Length of the binary image - * @trust_keyring: Signing certificates to use as starting points + * @trust_keys: Signing certificate(s) to use as starting points * @usage: The use to which the key is being put. - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message inside the PE * binary image intersects keys we already know and trust. @@ -418,14 +417,10 @@ error_no_desc: * May also return -ENOMEM. */ int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted) + struct key *trusted_keys, + enum key_being_used_for usage) { - struct pkcs7_message *pkcs7; struct pefile_context ctx; - const void *data; - size_t datalen; int ret; kenter(""); @@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; - pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len); - if (IS_ERR(pkcs7)) - return PTR_ERR(pkcs7); - ctx.pkcs7 = pkcs7; - - ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false); - if (ret < 0 || datalen == 0) { - pr_devel("PKCS#7 message does not contain data\n"); - ret = -EBADMSG; - goto error; - } - - ret = mscode_parse(&ctx); + ret = verify_pkcs7_signature(NULL, 0, + pebuf + ctx.sig_offset, ctx.sig_len, + trusted_keys, -EKEYREJECTED, usage, + mscode_parse, &ctx); if (ret < 0) goto error; @@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, * contents. */ ret = pefile_digest_pe(pebuf, pelen, &ctx); - if (ret < 0) - goto error; - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error; - - ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted); error: - pkcs7_free_message(ctx.pkcs7); + kfree(ctx.digest); return ret; } diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index a133eb81a492..cd4d20930728 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -9,7 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -#include #include #include @@ -23,7 +22,6 @@ struct pefile_context { unsigned sig_offset; unsigned sig_len; const struct section_header *secs; - struct pkcs7_message *pkcs7; /* PKCS#7 MS Individual Code Signing content */ const void *digest; /* Digest */ @@ -39,4 +37,5 @@ struct pefile_context { /* * mscode_parser.c */ -extern int mscode_parse(struct pefile_context *ctx); +extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen); diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 441aff9b5aa7..8323e3e57131 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -12,6 +12,7 @@ #ifndef _CRYPTO_PKCS7_H #define _CRYPTO_PKCS7_H +#include #include struct key; @@ -26,7 +27,7 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, - bool want_wrapper); + size_t *_headerlen); /* * pkcs7_trust.c diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 2f5de5c1a3a0..b3928e801b8c 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -14,20 +14,6 @@ #ifndef _LINUX_PUBLIC_KEY_H #define _LINUX_PUBLIC_KEY_H -/* - * The use to which an asymmetric key is being put. - */ -enum key_being_used_for { - VERIFYING_MODULE_SIGNATURE, - VERIFYING_FIRMWARE_SIGNATURE, - VERIFYING_KEXEC_PE_SIGNATURE, - VERIFYING_KEY_SIGNATURE, - VERIFYING_KEY_SELF_SIGNATURE, - VERIFYING_UNSPECIFIED_SIGNATURE, - NR__KEY_BEING_USED_FOR -}; -extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; - /* * Cryptographic data for the public-key subtype of the asymmetric key type. * diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 70a8775bb444..d1e23dda4363 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -15,6 +15,7 @@ #define _KEYS_ASYMMETRIC_TYPE_H #include +#include extern struct key_type key_type_asymmetric; diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 39fd38cfa8c9..b2d645ac35a0 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -15,6 +15,7 @@ #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING #include +#include #include extern struct key *system_trusted_keyring; @@ -29,12 +30,6 @@ static inline struct key *get_system_trusted_keyring(void) } #endif -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION -extern int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage); -#endif - #ifdef CONFIG_IMA_MOK_KEYRING extern struct key *ima_mok_keyring; extern struct key *ima_blacklist_keyring; diff --git a/include/linux/verification.h b/include/linux/verification.h new file mode 100644 index 000000000000..bb0fcf941cb7 --- /dev/null +++ b/include/linux/verification.h @@ -0,0 +1,50 @@ +/* Signature verification + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_VERIFICATION_H +#define _LINUX_VERIFICATION_H + +/* + * The use to which an asymmetric key is being put. + */ +enum key_being_used_for { + VERIFYING_MODULE_SIGNATURE, + VERIFYING_FIRMWARE_SIGNATURE, + VERIFYING_KEXEC_PE_SIGNATURE, + VERIFYING_KEY_SIGNATURE, + VERIFYING_KEY_SELF_SIGNATURE, + VERIFYING_UNSPECIFIED_SIGNATURE, + NR__KEY_BEING_USED_FOR +}; +extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; + +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION + +struct key; + +extern int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + int untrusted_error, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx); + +#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keys, + enum key_being_used_for usage); +#endif + +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ +#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h deleted file mode 100644 index da2049b5161c..000000000000 --- a/include/linux/verify_pefile.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Signed PE file verification - * - * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _LINUX_VERIFY_PEFILE_H -#define _LINUX_VERIFY_PEFILE_H - -#include - -extern int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted); - -#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 64b9dead4a07..593aace88a02 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return system_verify_data(mod, modlen, mod + modlen, sig_len, - VERIFYING_MODULE_SIGNATURE); + return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + NULL, -ENOKEY, VERIFYING_MODULE_SIGNATURE, + NULL, NULL); } -- cgit From bda850cd214e90b1be0cc25bc48c4f6ac53eb543 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: PKCS#7: Make trust determination dependent on contents of trust keyring Make the determination of the trustworthiness of a key dependent on whether a key that can verify it is present in the supplied ring of trusted keys rather than whether or not the verifying key has KEY_FLAG_TRUSTED set. verify_pkcs7_signature() will return -ENOKEY if the PKCS#7 message trust chain cannot be verified. Signed-off-by: David Howells --- certs/system_keyring.c | 13 ++++--------- crypto/asymmetric_keys/pkcs7_key_type.c | 2 +- crypto/asymmetric_keys/pkcs7_parser.h | 1 - crypto/asymmetric_keys/pkcs7_trust.c | 18 +++--------------- crypto/asymmetric_keys/verify_pefile.c | 2 +- crypto/asymmetric_keys/x509_parser.h | 1 - include/crypto/pkcs7.h | 3 +-- include/linux/verification.h | 1 - kernel/module_signing.c | 2 +- 9 files changed, 11 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/certs/system_keyring.c b/certs/system_keyring.c index a83bffedc0aa..dc18869ff680 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -121,7 +121,6 @@ late_initcall(load_system_certificate_list); int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, struct key *trusted_keys, - int untrusted_error, enum key_being_used_for usage, int (*view_content)(void *ctx, const void *data, size_t len, @@ -129,7 +128,6 @@ int verify_pkcs7_signature(const void *data, size_t len, void *ctx) { struct pkcs7_message *pkcs7; - bool trusted; int ret; pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); @@ -149,13 +147,10 @@ int verify_pkcs7_signature(const void *data, size_t len, if (!trusted_keys) trusted_keys = system_trusted_keyring; - ret = pkcs7_validate_trust(pkcs7, trusted_keys, &trusted); - if (ret < 0) - goto error; - - if (!trusted && untrusted_error) { - pr_err("PKCS#7 signature not signed with a trusted key\n"); - ret = untrusted_error; + ret = pkcs7_validate_trust(pkcs7, trusted_keys); + if (ret < 0) { + if (ret == -ENOKEY) + pr_err("PKCS#7 signature not signed with a trusted key\n"); goto error; } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index ab9bf5363ecd..3b92523882e5 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -62,7 +62,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep) return verify_pkcs7_signature(NULL, 0, prep->data, prep->datalen, - NULL, -ENOKEY, usage, + NULL, usage, pkcs7_view_content, prep); } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index d5eec31e95b6..f4e81074f5e0 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -22,7 +22,6 @@ struct pkcs7_signed_info { struct pkcs7_signed_info *next; struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; - bool trusted; bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b9a5487cd82d..36e77cb07bd0 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -30,7 +30,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; struct key *key; - bool trusted; int ret; kenter(",%u,", sinfo->index); @@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, for (x509 = sinfo->signer; x509; x509 = x509->signer) { if (x509->seen) { - if (x509->verified) { - trusted = x509->trusted; + if (x509->verified) goto verified; - } kleave(" = -ENOKEY [cached]"); return -ENOKEY; } @@ -122,7 +119,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, matched: ret = verify_signature(key, sig); - trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); key_put(key); if (ret < 0) { if (ret == -ENOMEM) @@ -134,12 +130,9 @@ matched: verified: if (x509) { x509->verified = true; - for (p = sinfo->signer; p != x509; p = p->signer) { + for (p = sinfo->signer; p != x509; p = p->signer) p->verified = true; - p->trusted = trusted; - } } - sinfo->trusted = trusted; kleave(" = 0"); return 0; } @@ -148,7 +141,6 @@ verified: * pkcs7_validate_trust - Validate PKCS#7 trust chain * @pkcs7: The PKCS#7 certificate to validate * @trust_keyring: Signing certificates to use as starting points - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message intersects * keys we already know and trust. @@ -170,16 +162,13 @@ verified: * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted) + struct key *trust_keyring) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; int cached_ret = -ENOKEY; int ret; - *_trusted = false; - for (p = pkcs7->certs; p; p = p->next) p->seen = false; @@ -193,7 +182,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, cached_ret = -ENOPKG; continue; case 0: - *_trusted |= sinfo->trusted; cached_ret = 0; continue; default: diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 265351075b0e..672a94c2c3ff 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -436,7 +436,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, ret = verify_pkcs7_signature(NULL, 0, pebuf + ctx.sig_offset, ctx.sig_len, - trusted_keys, -EKEYREJECTED, usage, + trusted_keys, usage, mscode_parse, &ctx); if (ret < 0) goto error; diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index f24f4d808e7f..05eef1c68881 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -39,7 +39,6 @@ struct x509_certificate { unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; - bool trusted; bool self_signed; /* T if self-signed (check unsupported_sig too) */ bool unsupported_key; /* T if key uses unsupported crypto */ bool unsupported_sig; /* T if signature uses unsupported crypto */ diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8323e3e57131..583f199400a3 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -33,8 +33,7 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted); + struct key *trust_keyring); /* * pkcs7_verify.c diff --git a/include/linux/verification.h b/include/linux/verification.h index bb0fcf941cb7..a10549a6c7cd 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -33,7 +33,6 @@ struct key; extern int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, struct key *trusted_keys, - int untrusted_error, enum key_being_used_for usage, int (*view_content)(void *ctx, const void *data, size_t len, diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 593aace88a02..6a64e03b9f44 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -81,6 +81,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) } return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, - NULL, -ENOKEY, VERIFYING_MODULE_SIGNATURE, + NULL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); } -- cgit From 31e6850e0fdb3a586363cc4d2f9801cdf9374310 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Tue, 5 Apr 2016 12:39:30 +0100 Subject: iommu: Add MMIO mapping type On some platforms, MMIO regions might need slightly different treatment compared to mapping regular memory; add the notion of MMIO mappings to the IOMMU API's memory type flags, so that callers can let the IOMMU drivers know to do the right thing. Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index a5c539fa5d2b..34b643227df1 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -30,6 +30,7 @@ #define IOMMU_WRITE (1 << 1) #define IOMMU_CACHE (1 << 2) /* DMA cache coherency */ #define IOMMU_NOEXEC (1 << 3) +#define IOMMU_MMIO (1 << 4) /* e.g. things like MSI doorbells */ struct iommu_ops; struct iommu_group; -- cgit From 848fc67da5fb43ef7d92d20891eef79f5de45816 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 4 Mar 2016 15:32:40 +0100 Subject: clk: renesas: mstp: Drop check for CONFIG_PM_GENERIC_DOMAINS_OF As of commit 71d076ceb245f0d9 ("ARM: shmobile: Enable PM and PM_GENERIC_DOMAINS for SoCs with PM Domains"), CONFIG_PM_GENERIC_DOMAINS_OF is always enabled for SoCs with MSTP clocks. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart --- drivers/clk/renesas/clk-mstp.c | 3 --- include/linux/clk/renesas.h | 4 ---- 2 files changed, 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 3d44e183aedd..5e3b3d856eef 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -243,8 +243,6 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) } CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); - -#ifdef CONFIG_PM_GENERIC_DOMAINS_OF int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev) { struct device_node *np = dev->of_node; @@ -326,4 +324,3 @@ void __init cpg_mstp_add_clk_domain(struct device_node *np) of_genpd_add_provider_simple(np, pd); } -#endif /* !CONFIG_PM_GENERIC_DOMAINS_OF */ diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 7adfd80fbf55..2a3379cf1330 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -24,12 +24,8 @@ void r8a7778_clocks_init(u32 mode); void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); -#ifdef CONFIG_PM_GENERIC_DOMAINS_OF void cpg_mstp_add_clk_domain(struct device_node *np); int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev); void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev); -#else -static inline void cpg_mstp_add_clk_domain(struct device_node *np) {} -#endif #endif -- cgit From 12a56817b329d8a73ab53bad09aa976aeea46db9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 4 Mar 2016 16:59:26 +0100 Subject: clk: renesas: mstp: Clarify cpg_mstp_{at,de}tach_dev() domain parameter Make it clear that the "domain" parameter of the cpg_mstp_attach_dev() and cpg_mstp_detach_dev() functions is not used. The cpg_mstp_attach_dev() and cpg_mstp_detach_dev() callbacks are not only used by the CPG/MSTP Clock Domain driver, but also by the R-Mobile SYSC PM Domain driver. Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/clk-mstp.c | 4 ++-- include/linux/clk/renesas.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 5e3b3d856eef..8b597b9a3804 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -243,7 +243,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) } CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); -int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev) +int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev) { struct device_node *np = dev->of_node; struct of_phandle_args clkspec; @@ -295,7 +295,7 @@ fail_put: return error; } -void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev) +void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev) { if (!list_empty(&dev->power.subsys_data->clock_list)) pm_clk_destroy(dev); diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 2a3379cf1330..095b1681daf4 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -25,7 +25,7 @@ void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); void cpg_mstp_add_clk_domain(struct device_node *np); -int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev); -void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev); +int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev); +void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev); #endif -- cgit From 1d111406c6d91f4d7f6cc69a43e59546e8010aae Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 20 Mar 2016 13:57:20 +0100 Subject: PCI: Add Intel Thunderbolt device IDs Intel Gen 1 and 2 chips use the same ID for NHI, bridges and switch. Gen 3 chips and onward use a distinct ID for the NHI. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Acked-by: Andreas Noever --- drivers/pci/quirks.c | 16 ++++++++++------ drivers/thunderbolt/nhi.c | 8 +++++--- drivers/thunderbolt/switch.c | 9 +++++---- include/linux/pci_ids.h | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 8e678027b900..b584ddf83555 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3232,7 +3232,8 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev) acpi_execute_simple_method(SXIO, NULL, 0); acpi_execute_simple_method(SXLV, NULL, 0); } -DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, 0x1547, +DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, quirk_apple_poweroff_thunderbolt); /* @@ -3266,9 +3267,10 @@ static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev) if (!nhi) goto out; if (nhi->vendor != PCI_VENDOR_ID_INTEL - || (nhi->device != 0x1547 && nhi->device != 0x156c) - || nhi->subsystem_vendor != 0x2222 - || nhi->subsystem_device != 0x1111) + || (nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C && + nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI) + || nhi->subsystem_vendor != 0x2222 + || nhi->subsystem_device != 0x1111) goto out; dev_info(&dev->dev, "quirk: waiting for thunderbolt to reestablish PCI tunnels...\n"); device_pm_wait_for_dev(&dev->dev, &nhi->dev); @@ -3276,9 +3278,11 @@ out: pci_dev_put(nhi); pci_dev_put(sibling); } -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x1547, +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, quirk_apple_wait_for_thunderbolt); -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x156d, +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE, quirk_apple_wait_for_thunderbolt); #endif diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 20a41f7de76f..36be23babb89 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -633,16 +633,18 @@ static const struct dev_pm_ops nhi_pm_ops = { static struct pci_device_id nhi_ids[] = { /* * We have to specify class, the TB bridges use the same device and - * vendor (sub)id. + * vendor (sub)id on gen 1 and gen 2 controllers. */ { .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, .device = 0x1547, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, .subvendor = 0x2222, .subdevice = 0x1111, }, { .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, .device = 0x156c, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, { 0,} diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index aeb982969629..db73ffed68a9 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -293,9 +293,9 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active) if (active) { data = data & 0xFFFFFF83; switch (sw->config.device_id) { - case 0x1513: - case 0x151a: - case 0x1549: + case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: + case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE: + case PCI_DEVICE_ID_INTEL_PORT_RIDGE: break; default: data |= 4; @@ -370,7 +370,8 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) tb_sw_warn(sw, "unknown switch vendor id %#x\n", sw->config.vendor_id); - if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549) + if (sw->config.device_id != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C && + sw->config.device_id != PCI_DEVICE_ID_INTEL_PORT_RIDGE) tb_sw_warn(sw, "unsupported switch device id %#x\n", sw->config.device_id); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 247da8c95860..c58752fe16c4 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2604,6 +2604,24 @@ #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82380FB 0x124b #define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_LIGHT_RIDGE 0x1513 /* Tbt 1 Gen 1 */ +#define PCI_DEVICE_ID_INTEL_EAGLE_RIDGE 0x151a +#define PCI_DEVICE_ID_INTEL_LIGHT_PEAK 0x151b +#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C 0x1547 /* Tbt 1 Gen 2 */ +#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C 0x1548 +#define PCI_DEVICE_ID_INTEL_PORT_RIDGE 0x1549 +#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_NHI 0x1566 /* Tbt 1 Gen 3 */ +#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE 0x1567 +#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_NHI 0x1568 +#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE 0x1569 +#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI 0x156a /* Thunderbolt 2 */ +#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE 0x156b +#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI 0x156c +#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE 0x156d +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI 0x1575 /* Thunderbolt 3 */ +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE 0x1576 +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI 0x1577 +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE 0x1578 #define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 -- cgit From 32b9b10961860860268961d9aad0c56a73018c37 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 11 Feb 2016 13:19:09 -0800 Subject: clk: Allow clocks to be marked as CRITICAL Critical clocks are those which must not be gated, else undefined or catastrophic failure would occur. Here we have chosen to ensure the prepare/enable counts are correctly incremented, so as not to confuse users with enabled clocks with no visible users. Signed-off-by: Lee Jones Signed-off-by: Michael Turquette Link: lkml.kernel.org/r/1455225554-13267-2-git-send-email-mturquette@baylibre.com --- drivers/clk/clk.c | 5 +++++ include/linux/clk-provider.h | 1 + 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fb74dc1f7520..275201fd7b01 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2397,6 +2397,11 @@ static int __clk_core_init(struct clk_core *core) if (core->ops->init) core->ops->init(core->hw); + if (core->flags & CLK_IS_CRITICAL) { + clk_core_prepare(core); + clk_core_enable(core); + } + kref_init(&core->ref); out: clk_prepare_unlock(); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index da95258127aa..0638b4154502 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -32,6 +32,7 @@ #define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ #define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ #define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ +#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ struct clk; struct clk_hw; -- cgit From d56f8994b6fb928f59481fabc25bcd1c2f9bd06d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 11 Feb 2016 13:19:11 -0800 Subject: clk: Provide OF helper to mark clocks as CRITICAL This call matches clocks which have been marked as critical in DT and sets the appropriate flag. These flags can then be used to mark the clock core flags appropriately prior to registration. Legacy bindings requiring this feature must add the clock-critical property to their binding descriptions, as it is not a part of common-clock binding. Cc: devicetree@vger.kernel.org Signed-off-by: Lee Jones Reviewed-by: Stephen Boyd Signed-off-by: Michael Turquette Link: lkml.kernel.org/r/1455225554-13267-4-git-send-email-mturquette@baylibre.com --- drivers/clk/clk.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 8 +++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index dede0ce679e4..9f77cc67cdc3 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3137,6 +3137,41 @@ static int parent_ready(struct device_node *np) } } +/** + * of_clk_detect_critical() - set CLK_IS_CRITICAL flag from Device Tree + * @np: Device node pointer associated with clock provider + * @index: clock index + * @flags: pointer to clk_core->flags + * + * Detects if the clock-critical property exists and, if so, sets the + * corresponding CLK_IS_CRITICAL flag. + * + * Do not use this function. It exists only for legacy Device Tree + * bindings, such as the one-clock-per-node style that are outdated. + * Those bindings typically put all clock data into .dts and the Linux + * driver has no clock data, thus making it impossible to set this flag + * correctly from the driver. Only those drivers may call + * of_clk_detect_critical from their setup functions. + * + * Return: error code or zero on success + */ +int of_clk_detect_critical(struct device_node *np, + int index, unsigned long *flags) +{ + struct property *prop; + const __be32 *cur; + uint32_t idx; + + if (!np || !flags) + return -EINVAL; + + of_property_for_each_u32(np, "clock-critical", prop, cur, idx) + if (index == idx) + *flags |= CLK_IS_CRITICAL; + + return 0; +} + /** * of_clk_init() - Scan and init clock providers from the DT * @matches: array of compatible values and init functions for providers. diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 0638b4154502..156286445a25 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -721,7 +721,8 @@ unsigned int of_clk_get_parent_count(struct device_node *np); int of_clk_parent_fill(struct device_node *np, const char **parents, unsigned int size); const char *of_clk_get_parent_name(struct device_node *np, int index); - +int of_clk_detect_critical(struct device_node *np, int index, + unsigned long *flags); void of_clk_init(const struct of_device_id *matches); #else /* !CONFIG_OF */ @@ -758,6 +759,11 @@ static inline const char *of_clk_get_parent_name(struct device_node *np, { return NULL; } +static inline int of_clk_detect_critical(struct device_node *np, int index, + unsigned long *flags) +{ + return 0; +} static inline void of_clk_init(const struct of_device_id *matches) {} #endif /* CONFIG_OF */ -- cgit From 3c9d6296b7aee536a96ea2b53a15d23511738c1c Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 8 Apr 2016 12:20:30 +0200 Subject: security: drop the unused hook skb_owned_by The skb_owned_by hook was added with the commit ca10b9e9a8ca ("selinux: add a skb_owned_by() hook") and later removed when said commit was reverted. Later on, when switching to list of hooks, a field named 'skb_owned_by' was included into the security_hook_head struct, but without any users nor caller. This commit removes the said left-over field. Fixes: b1d9e6b0646d ("LSM: Switch to lists of hooks") Signed-off-by: Paolo Abeni Acked-by: Casey Schaufler Acked-by: Paul Moore Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 1 - security/security.c | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index cdee11cbcdf1..ae2537886177 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1804,7 +1804,6 @@ struct security_hook_heads { struct list_head tun_dev_attach_queue; struct list_head tun_dev_attach; struct list_head tun_dev_open; - struct list_head skb_owned_by; #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM struct list_head xfrm_policy_alloc_security; diff --git a/security/security.c b/security/security.c index 3644b0344d29..554c3fb7d4a5 100644 --- a/security/security.c +++ b/security/security.c @@ -1848,7 +1848,6 @@ struct security_hook_heads security_hook_heads = { .tun_dev_attach = LIST_HEAD_INIT(security_hook_heads.tun_dev_attach), .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open), - .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by), #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = -- cgit From adae28c59a6a71a971ffed713550911546df0e20 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 23 Mar 2016 11:36:53 +0100 Subject: mfd: syscon: Include errno.h from header The syscon header uses the ENOTSUPP error constant, but doesn't include the header that defines it. This causes a build error after the imx pinctrl driver started using syscon: include/linux/mfd/syscon.h: In function 'syscon_node_to_regmap': include/linux/mfd/syscon.h:32:18: error: 'ENOTSUPP' undeclared (first use in this function) return ERR_PTR(-ENOTSUPP); ^~~~~~~~ This adds the missing #include. Signed-off-by: Arnd Bergmann Fixes: 8626ada871f1 ("pinctrl: imx: attach iomuxc device to gpr syscon") Signed-off-by: Lee Jones --- include/linux/mfd/syscon.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index 1088149be0c9..40a76b97b7ab 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -16,6 +16,7 @@ #define __LINUX_MFD_SYSCON_H__ #include +#include struct device_node; -- cgit From a16d6ebca6efb73f6402f36e5aebf84f61721856 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:45 +0200 Subject: i2c: introduce helper function to get 8 bit address from a message Drivers do this in various ways, let's use one standard way of doing it. Note: I2C_M_RD is bit 0, so the code could be simplified. To be extremly robust and to advertise good coding practices, I still use the ternary operator and let the compilers do the optimizing job. Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 200cf13b00f6..c30833b7b073 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -654,6 +654,11 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap) return adap->nr; } +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) +{ + return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); +} + /** * module_i2c_driver() - Helper macro for registering a modular I2C driver * @__i2c_driver: i2c_driver struct -- cgit From f0af9593372abfde34460aa1250e670cc535a7d8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 24 Feb 2016 13:43:45 -0600 Subject: PCI: Add pci_add_dma_alias() to abstract implementation Add a pci_add_dma_alias() interface to encapsulate the details of adding an alias. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Williamson --- drivers/pci/pci.c | 14 ++++++++++++++ drivers/pci/quirks.c | 19 +++++++------------ include/linux/pci.h | 1 + 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 25e0327d4429..1162118d1093 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4578,6 +4578,20 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode, return 0; } +/** + * pci_add_dma_alias - Add a DMA devfn alias for a device + * @dev: the PCI device for which alias is added + * @devfn: alias slot and function + * + * This helper encodes 8-bit devfn as bit number in dma_alias_mask. + * It should be called early, preferably as PCI fixup header quirk. + */ +void pci_add_dma_alias(struct pci_dev *dev, u8 devfn) +{ + dev->dma_alias_devfn = devfn; + dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; +} + bool pci_device_is_present(struct pci_dev *pdev) { u32 v; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 8e678027b900..e45a7a8338bb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3610,10 +3610,8 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) static void quirk_dma_func0_alias(struct pci_dev *dev) { - if (PCI_FUNC(dev->devfn) != 0) { - dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); - dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; - } + if (PCI_FUNC(dev->devfn) != 0) + pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); } /* @@ -3626,10 +3624,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias); static void quirk_dma_func1_alias(struct pci_dev *dev) { - if (PCI_FUNC(dev->devfn) != 1) { - dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1); - dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; - } + if (PCI_FUNC(dev->devfn) != 1) + pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); } /* @@ -3696,11 +3692,10 @@ static void quirk_fixed_dma_alias(struct pci_dev *dev) id = pci_match_id(fixed_dma_alias_tbl, dev); if (id) { - dev->dma_alias_devfn = id->driver_data; - dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; + pci_add_dma_alias(dev, id->driver_data); dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n", - PCI_SLOT(dev->dma_alias_devfn), - PCI_FUNC(dev->dma_alias_devfn)); + PCI_SLOT(id->driver_data), + PCI_FUNC(id->driver_data)); } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 004b8133417d..7e7019064437 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1988,6 +1988,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) } #endif +void pci_add_dma_alias(struct pci_dev *dev, u8 devfn); int pci_for_each_dma_alias(struct pci_dev *pdev, int (*fn)(struct pci_dev *pdev, u16 alias, void *data), void *data); -- cgit From 338c3149a221527e202ee26b1e35f76c965bb6c0 Mon Sep 17 00:00:00 2001 From: Jacek Lawrynowicz Date: Thu, 3 Mar 2016 15:38:02 +0100 Subject: PCI: Add support for multiple DMA aliases Solve IOMMU support issues with PCIe non-transparent bridges that use Requester ID look-up tables (RID-LUT), e.g., the PEX8733. The NTB connects devices in two independent PCI domains. Devices separated by the NTB are not able to discover each other. A PCI packet being forwared from one domain to another has to have its RID modified so it appears on correct bus and completions are forwarded back to the original domain through the NTB. The RID is translated using a preprogrammed table (LUT) and the PCI packet propagates upstream away from the NTB. If the destination system has IOMMU enabled, the packet will be discarded because the new RID is unknown to the IOMMU. Adding a DMA alias for the new RID allows IOMMU to properly recognize the packet. Each device behind the NTB has a unique RID assigned in the RID-LUT. The current DMA alias implementation supports only a single alias, so it's not possible to support mutiple devices behind the NTB when IOMMU is enabled. Enable all possible aliases on a given bus (256) that are stored in a bitset. Alias devfn is directly translated to a bit number. The bitset is not allocated for devices that have no need for DMA aliases. More details can be found in the following article: http://www.plxtech.com/files/pdf/technical/expresslane/RTC_Enabling%20MulitHostSystemDesigns.pdf Signed-off-by: Jacek Lawrynowicz Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Williamson Acked-by: David Woodhouse Acked-by: Joerg Roedel --- drivers/iommu/iommu.c | 10 +++------- drivers/pci/pci.c | 19 +++++++++++++++++-- drivers/pci/probe.c | 1 + drivers/pci/search.c | 14 +++++++++----- include/linux/pci.h | 5 ++--- 5 files changed, 32 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index bfd4f7c3b1d8..1b49e940a318 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -660,8 +660,8 @@ static struct iommu_group *get_pci_function_alias_group(struct pci_dev *pdev, } /* - * Look for aliases to or from the given device for exisiting groups. The - * dma_alias_devfn only supports aliases on the same bus, therefore the search + * Look for aliases to or from the given device for existing groups. DMA + * aliases are only supported on the same bus, therefore the search * space is quite small (especially since we're really only looking at pcie * device, and therefore only expect multiple slots on the root complex or * downstream switch ports). It's conceivable though that a pair of @@ -686,11 +686,7 @@ static struct iommu_group *get_pci_alias_group(struct pci_dev *pdev, continue; /* We alias them or they alias us */ - if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && - pdev->dma_alias_devfn == tmp->devfn) || - ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && - tmp->dma_alias_devfn == pdev->devfn)) { - + if (pci_devs_are_dma_aliases(pdev, tmp)) { group = get_pci_alias_group(tmp, devfns); if (group) { pci_dev_put(tmp); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c82ebd0f6982..0b90c2186f1c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4588,12 +4588,27 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode, */ void pci_add_dma_alias(struct pci_dev *dev, u8 devfn) { - dev->dma_alias_devfn = devfn; - dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; + if (!dev->dma_alias_mask) + dev->dma_alias_mask = kcalloc(BITS_TO_LONGS(U8_MAX), + sizeof(long), GFP_KERNEL); + if (!dev->dma_alias_mask) { + dev_warn(&dev->dev, "Unable to allocate DMA alias mask\n"); + return; + } + + set_bit(devfn, dev->dma_alias_mask); dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n", PCI_SLOT(devfn), PCI_FUNC(devfn)); } +bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2) +{ + return (dev1->dma_alias_mask && + test_bit(dev2->devfn, dev1->dma_alias_mask)) || + (dev2->dma_alias_mask && + test_bit(dev1->devfn, dev2->dma_alias_mask)); +} + bool pci_device_is_present(struct pci_dev *pdev) { u32 v; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8004f67c57ec..ae7daeb83e21 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1537,6 +1537,7 @@ static void pci_release_dev(struct device *dev) pcibios_release_device(pci_dev); pci_bus_put(pci_dev->bus); kfree(pci_dev->driver_override); + kfree(pci_dev->dma_alias_mask); kfree(pci_dev); } diff --git a/drivers/pci/search.c b/drivers/pci/search.c index a20ce7d5e2a7..33e0f033a48e 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -40,11 +40,15 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, * If the device is broken and uses an alias requester ID for * DMA, iterate over that too. */ - if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) { - ret = fn(pdev, PCI_DEVID(pdev->bus->number, - pdev->dma_alias_devfn), data); - if (ret) - return ret; + if (unlikely(pdev->dma_alias_mask)) { + u8 devfn; + + for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) { + ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn), + data); + if (ret) + return ret; + } } for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 7e7019064437..5581d05f7833 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -166,8 +166,6 @@ enum pci_dev_flags { PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2), /* Flag for quirk use to store if quirk-specific ACS is enabled */ PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), - /* Flag to indicate the device uses dma_alias_devfn */ - PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4), /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */ PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5), /* Do not use bus resets for device */ @@ -273,7 +271,7 @@ struct pci_dev { u8 rom_base_reg; /* which config register controls the ROM */ u8 pin; /* which interrupt pin this device uses */ u16 pcie_flags_reg; /* cached PCIe Capabilities Register */ - u8 dma_alias_devfn;/* devfn of DMA alias, if any */ + unsigned long *dma_alias_mask;/* mask of enabled devfn aliases */ struct pci_driver *driver; /* which driver has allocated this device */ u64 dma_mask; /* Mask of the bits of bus address this @@ -1989,6 +1987,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) #endif void pci_add_dma_alias(struct pci_dev *dev, u8 devfn); +bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2); int pci_for_each_dma_alias(struct pci_dev *pdev, int (*fn)(struct pci_dev *pdev, u16 alias, void *data), void *data); -- cgit From 5ac7eace2d00eab5ae0e9fdee63e38aee6001f7c Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: KEYS: Add a facility to restrict new links into a keyring Add a facility whereby proposed new links to be added to a keyring can be vetted, permitting them to be rejected if necessary. This can be used to block public keys from which the signature cannot be verified or for which the signature verification fails. It could also be used to provide blacklisting. This affects operations like add_key(), KEYCTL_LINK and KEYCTL_INSTANTIATE. To this end: (1) A function pointer is added to the key struct that, if set, points to the vetting function. This is called as: int (*restrict_link)(struct key *keyring, const struct key_type *key_type, unsigned long key_flags, const union key_payload *key_payload), where 'keyring' will be the keyring being added to, key_type and key_payload will describe the key being added and key_flags[*] can be AND'ed with KEY_FLAG_TRUSTED. [*] This parameter will be removed in a later patch when KEY_FLAG_TRUSTED is removed. The function should return 0 to allow the link to take place or an error (typically -ENOKEY, -ENOPKG or -EKEYREJECTED) to reject the link. The pointer should not be set directly, but rather should be set through keyring_alloc(). Note that if called during add_key(), preparse is called before this method, but a key isn't actually allocated until after this function is called. (2) KEY_ALLOC_BYPASS_RESTRICTION is added. This can be passed to key_create_or_update() or key_instantiate_and_link() to bypass the restriction check. (3) KEY_FLAG_TRUSTED_ONLY is removed. The entire contents of a keyring with this restriction emplaced can be considered 'trustworthy' by virtue of being in the keyring when that keyring is consulted. (4) key_alloc() and keyring_alloc() take an extra argument that will be used to set restrict_link in the new key. This ensures that the pointer is set before the key is published, thus preventing a window of unrestrictedness. Normally this argument will be NULL. (5) As a temporary affair, keyring_restrict_trusted_only() is added. It should be passed to keyring_alloc() as the extra argument instead of setting KEY_FLAG_TRUSTED_ONLY on a keyring. This will be replaced in a later patch with functions that look in the appropriate places for authoritative keys. Signed-off-by: David Howells Reviewed-by: Mimi Zohar --- Documentation/security/keys.txt | 22 ++++++++++++ certs/system_keyring.c | 8 ++--- fs/cifs/cifsacl.c | 2 +- fs/nfs/nfs4idmap.c | 2 +- include/linux/key.h | 53 +++++++++++++++++++++++------ net/dns_resolver/dns_key.c | 2 +- net/rxrpc/ar-key.c | 4 +-- security/integrity/digsig.c | 7 ++-- security/integrity/ima/ima_mok.c | 8 ++--- security/keys/key.c | 43 +++++++++++++++++++---- security/keys/keyring.c | 73 ++++++++++++++++++++++++++++++++++++---- security/keys/persistent.c | 4 +-- security/keys/process_keys.c | 16 +++++---- security/keys/request_key.c | 4 +-- security/keys/request_key_auth.c | 2 +- 15 files changed, 198 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8c183873b2b7..a6a50b359025 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -999,6 +999,10 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), unsigned long flags, struct key *dest); @@ -1010,6 +1014,24 @@ payload contents" for more information. KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted towards the user's quota). Error ENOMEM can also be returned. + If restrict_link not NULL, it should point to a function that will be + called each time an attempt is made to link a key into the new keyring. + This function is called to check whether a key may be added into the keying + or not. Callers of key_create_or_update() within the kernel can pass + KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using + this is to manage rings of cryptographic keys that are set up when the + kernel boots where userspace is also permitted to add keys - provided they + can be verified by a key the kernel already has. + + When called, the restriction function will be passed the keyring being + added to, the key flags value and the type and payload of the key being + added. Note that when a new key is being created, this is called between + payload preparsing and actual key creation. The function should return 0 + to allow the link or an error to reject it. + + A convenience function, restrict_link_reject, exists to always return + -EPERM to in this case. + (*) To check the validity of a key, this function can be called: diff --git a/certs/system_keyring.c b/certs/system_keyring.c index dc18869ff680..417d65882870 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -36,11 +36,10 @@ static __init int system_trusted_keyring_init(void) KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); if (IS_ERR(system_trusted_keyring)) panic("Can't allocate system trusted keyring\n"); - - set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); return 0; } @@ -85,7 +84,8 @@ static __init int load_system_certificate_list(void) KEY_USR_VIEW | KEY_USR_READ), KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_TRUSTED | - KEY_ALLOC_BUILT_IN); + KEY_ALLOC_BUILT_IN | + KEY_ALLOC_BYPASS_RESTRICTION); if (IS_ERR(key)) { pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3f93125916bf..71e8a56e9479 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -360,7 +360,7 @@ init_cifs_idmap(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 5ba22c6b0ffa..c444285bb1b1 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -201,7 +201,7 @@ int nfs_idmap_init(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/include/linux/key.h b/include/linux/key.h index 5f5b1129dc92..83b603639d2e 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -174,10 +174,9 @@ struct key { #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ -#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ -#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ -#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ +#define KEY_FLAG_BUILTIN 9 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 10 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 11 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -205,6 +204,21 @@ struct key { }; int reject_error; }; + + /* This is set on a keyring to restrict the addition of a link to a key + * to it. If this method isn't provided then it is assumed that the + * keyring is open to any addition. It is ignored for non-keyring + * keys. + * + * This is intended for use with rings of trusted keys whereby addition + * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION + * overrides this, allowing the kernel to add extra keys without + * restriction. + */ + int (*restrict_link)(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload); }; extern struct key *key_alloc(struct key_type *type, @@ -212,14 +226,19 @@ extern struct key *key_alloc(struct key_type *type, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags); + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *)); -#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ -#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ -#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ -#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ -#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ +#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ +#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ +#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ +#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ +#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0010 /* Override the check on restricted keyrings */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -288,8 +307,22 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), struct key *dest); +extern int keyring_restrict_trusted_only(struct key *keyring, + const struct key_type *type, + unsigned long, + const union key_payload *payload); + +extern int restrict_link_reject(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload); + extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index c79b85eb4d4c..8737412c7b27 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -281,7 +281,7 @@ static int __init init_dns_resolver(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 3fb492eedeb9..1021b4c0bdd2 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, key = key_alloc(&key_type_rxrpc, "x", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, - KEY_ALLOC_NOT_IN_QUOTA); + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) { _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); return -ENOMEM; @@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname) key = key_alloc(&key_type_rxrpc, keyname, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA); + KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) return key; diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 8ef15118cc78..659566c2200b 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -83,10 +83,9 @@ int __init integrity_init_keyring(const unsigned int id) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); - if (!IS_ERR(keyring[id])) - set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); - else { + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", keyring_name[id], err); diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 676885e4320e..ef91248cb934 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -35,20 +35,20 @@ __init int ima_mok_init(void) (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA MOK or blacklist keyrings."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } diff --git a/security/keys/key.c b/security/keys/key.c index b28755131687..deb881754e03 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,6 +201,7 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. + * @restrict_link: Optional link restriction method for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the @@ -223,7 +224,11 @@ serial_exists: */ struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, - key_perm_t perm, unsigned long flags) + key_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *)) { struct key_user *user = NULL; struct key *key; @@ -291,6 +296,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->uid = uid; key->gid = gid; key->perm = perm; + key->restrict_link = restrict_link; if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; @@ -496,6 +502,12 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + if (keyring->restrict_link) { + ret = keyring->restrict_link(keyring, key->type, + key->flags, &prep.payload); + if (ret < 0) + goto error; + } ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) goto error; @@ -551,8 +563,12 @@ int key_reject_and_link(struct key *key, awaken = 0; ret = -EBUSY; - if (keyring) + if (keyring) { + if (keyring->restrict_link) + return -EPERM; + link_ret = __key_link_begin(keyring, &key->index_key, &edit); + } mutex_lock(&key_construction_mutex); @@ -793,6 +809,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -811,6 +831,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_check(keyring); + key_ref = ERR_PTR(-EPERM); + if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION)) + restrict_link = keyring->restrict_link; + key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) goto error_put_type; @@ -835,10 +859,15 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - key_ref = ERR_PTR(-EPERM); - if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) - goto error_free_prep; - flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; + if (restrict_link) { + unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; + ret = restrict_link(keyring, + index_key.type, kflags, &prep.payload); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + } ret = __key_link_begin(keyring, &index_key, &edit); if (ret < 0) { @@ -879,7 +908,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, - cred->fsuid, cred->fsgid, cred, perm, flags); + cred->fsuid, cred->fsgid, cred, perm, flags, NULL); if (IS_ERR(key)) { key_ref = ERR_CAST(key); goto error_link_end; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f931ccfeefb0..d2d1f3378008 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -491,13 +491,18 @@ static long keyring_read(const struct key *keyring, */ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags, struct key *dest) + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, cred, perm, flags); + uid, gid, cred, perm, flags, restrict_link); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { @@ -510,6 +515,51 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/** + * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring that point to keys that aren't + * marked as being trusted. It can be overridden by passing + * KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when adding a key + * to a keyring. + * + * This is meant to be passed as the restrict_link parameter to + * keyring_alloc(). + */ +int keyring_restrict_trusted_only(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; +} + +/** + * restrict_link_reject - Give -EPERM to restrict link + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring. It can be overridden by + * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when + * adding a key to a keyring. + * + * This is meant to be passed as the restrict_link parameter to + * keyring_alloc(). + */ +int restrict_link_reject(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return -EPERM; +} + /* * By default, we keys found by getting an exact match on their descriptions. */ @@ -1191,6 +1241,17 @@ void __key_link_end(struct key *keyring, up_write(&keyring->sem); } +/* + * Check addition of keys to restricted keyrings. + */ +static int __key_link_check_restriction(struct key *keyring, struct key *key) +{ + if (!keyring->restrict_link) + return 0; + return keyring->restrict_link(keyring, + key->type, key->flags, &key->payload); +} + /** * key_link - Link a key to a keyring * @keyring: The keyring to make the link in. @@ -1221,14 +1282,12 @@ int key_link(struct key *keyring, struct key *key) key_check(keyring); key_check(key); - if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && - !test_bit(KEY_FLAG_TRUSTED, &key->flags)) - return -EPERM; - ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret == 0) { kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); - ret = __key_link_check_live_key(keyring, key); + ret = __key_link_check_restriction(keyring, key); + if (ret == 0) + ret = __key_link_check_live_key(keyring, key); if (ret == 0) __key_link(key, &edit); __key_link_end(keyring, &key->index_key, edit); diff --git a/security/keys/persistent.c b/security/keys/persistent.c index c9fae5ea89fe..2ef45b319dd9 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns) current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, uid, INVALID_GID, current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, + KEY_ALLOC_NOT_IN_QUOTA, NULL, ns->persistent_keyring_register); if (IS_ERR(persistent)) return ERR_CAST(persistent); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index e6d50172872f..40a885239782 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -76,7 +76,8 @@ int install_user_keyrings(void) if (IS_ERR(uid_keyring)) { uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(uid_keyring)) { ret = PTR_ERR(uid_keyring); goto error; @@ -92,7 +93,8 @@ int install_user_keyrings(void) session_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error_release; @@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - flags, NULL); + flags, NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } else { @@ -785,7 +789,7 @@ long join_session_keyring(const char *name) keyring = keyring_alloc( name, old->uid, old->gid, old, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c7a117c9a8f3..a29e3554751e 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons, cred = get_current_cred(); keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL); put_cred(cred); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); @@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, key = key_alloc(ctx->index_key.type, ctx->index_key.description, ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, - perm, flags); + perm, flags, NULL); if (IS_ERR(key)) goto alloc_failed; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 4f0f112fe276..9db8b4a82787 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, authkey = key_alloc(&key_type_request_key_auth, desc, cred->fsuid, cred->fsgid, cred, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | - KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); + KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); goto error_alloc; -- cgit From 77f68bac9481ad440f4f34dda3d28c2dce6eb87b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: KEYS: Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED as they're no longer meaningful. Also we can drop the trusted flag from the preparse structure. Given this, we no longer need to pass the key flags through to restrict_link(). Further, we can now get rid of keyring_restrict_trusted_only() also. Signed-off-by: David Howells --- certs/system_keyring.c | 2 -- include/keys/system_keyring.h | 1 - include/linux/key-type.h | 1 - include/linux/key.h | 21 +++++---------------- security/integrity/digsig.c | 3 +-- security/keys/key.c | 11 ++--------- security/keys/keyring.c | 29 +---------------------------- 7 files changed, 9 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4e2fa8ab01d6..e460d00a7781 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -31,7 +31,6 @@ extern __initconst const unsigned long system_certificate_list_size; */ int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return restrict_link_by_signature(system_trusted_keyring, @@ -97,7 +96,6 @@ static __init int load_system_certificate_list(void) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), KEY_ALLOC_NOT_IN_QUOTA | - KEY_ALLOC_TRUSTED | KEY_ALLOC_BUILT_IN | KEY_ALLOC_BYPASS_RESTRICTION); if (IS_ERR(key)) { diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 93715913a0b1..c72330ae76df 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,6 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); #else diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 7463355a198b..eaee981c5558 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -45,7 +45,6 @@ struct key_preparsed_payload { size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ time_t expiry; /* Expiry time of key */ - bool trusted; /* True if key is trusted */ }; typedef int (*request_key_actor_t)(struct key_construction *key, diff --git a/include/linux/key.h b/include/linux/key.h index 83b603639d2e..722914798f37 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -173,10 +173,9 @@ struct key { #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ -#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ -#define KEY_FLAG_BUILTIN 9 /* set if key is built in to the kernel */ -#define KEY_FLAG_ROOT_CAN_INVAL 10 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 11 /* set if key should not be removed */ +#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 10 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -217,7 +216,6 @@ struct key { */ int (*restrict_link)(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); }; @@ -229,16 +227,14 @@ extern struct key *key_alloc(struct key_type *type, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *)); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ -#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ -#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ -#define KEY_ALLOC_BYPASS_RESTRICTION 0x0010 /* Override the check on restricted keyrings */ +#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -309,18 +305,11 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest); -extern int keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long, - const union key_payload *payload); - extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); extern int keyring_clear(struct key *keyring); diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index d647178c6bbd..98ee4c752cf5 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -51,12 +51,11 @@ static bool init_keyring __initdata; */ static int restrict_link_by_ima_mok(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { int ret; - ret = restrict_link_by_builtin_trusted(keyring, type, flags, payload); + ret = restrict_link_by_builtin_trusted(keyring, type, payload); if (ret != -ENOKEY) return ret; diff --git a/security/keys/key.c b/security/keys/key.c index deb881754e03..bd5a272f28a6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -227,7 +227,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, key_perm_t perm, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *)) { struct key_user *user = NULL; @@ -300,8 +299,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; - if (flags & KEY_ALLOC_TRUSTED) - key->flags |= 1 << KEY_FLAG_TRUSTED; if (flags & KEY_ALLOC_BUILT_IN) key->flags |= 1 << KEY_FLAG_BUILTIN; @@ -504,7 +501,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - key->flags, &prep.payload); + &prep.payload); if (ret < 0) goto error; } @@ -811,7 +808,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, int ret; int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel @@ -843,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.data = payload; prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; - prep.trusted = flags & KEY_ALLOC_TRUSTED; prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); @@ -860,9 +855,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; - ret = restrict_link(keyring, - index_key.type, kflags, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d2d1f3378008..c91e4e0cea08 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -494,7 +494,6 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest) { @@ -515,34 +514,10 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); -/** - * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only - * @keyring: The keyring being added to. - * @type: The type of key being added. - * @flags: The key flags. - * @payload: The payload of the key intended to be added. - * - * Reject the addition of any links to a keyring that point to keys that aren't - * marked as being trusted. It can be overridden by passing - * KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when adding a key - * to a keyring. - * - * This is meant to be passed as the restrict_link parameter to - * keyring_alloc(). - */ -int keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long flags, - const union key_payload *payload) -{ - return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; -} - /** * restrict_link_reject - Give -EPERM to restrict link * @keyring: The keyring being added to. * @type: The type of key being added. - * @flags: The key flags. * @payload: The payload of the key intended to be added. * * Reject the addition of any links to a keyring. It can be overridden by @@ -554,7 +529,6 @@ int keyring_restrict_trusted_only(struct key *keyring, */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return -EPERM; @@ -1248,8 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, - key->type, key->flags, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload); } /** -- cgit From 757d12e5849be549076901b0d33c60d5f360269c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 12 Apr 2016 21:07:06 +0530 Subject: dmaengine: ensure dmaengine helpers check valid callback dmaengine has various device callbacks and exposes helper functions to invoke these. These helpers should check if channel, device and callback is valid or not before invoking them. Reported-by: Jon Hunter Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 017433712833..30de0197263a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -804,6 +804,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( sg_dma_address(&sg) = buf; sg_dma_len(&sg) = len; + if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + return NULL; + return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags, NULL); } @@ -812,6 +815,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction dir, unsigned long flags) { + if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + return NULL; + return chan->device->device_prep_slave_sg(chan, sgl, sg_len, dir, flags, NULL); } @@ -823,6 +829,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg( enum dma_transfer_direction dir, unsigned long flags, struct rio_dma_ext *rio_ext) { + if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + return NULL; + return chan->device->device_prep_slave_sg(chan, sgl, sg_len, dir, flags, rio_ext); } @@ -833,6 +842,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction dir, unsigned long flags) { + if (!chan || !chan->device || !chan->device->device_prep_dma_cyclic) + return NULL; + return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, period_len, dir, flags); } @@ -841,6 +853,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags) { + if (!chan || !chan->device || !chan->device->device_prep_interleaved_dma) + return NULL; + return chan->device->device_prep_interleaved_dma(chan, xt, flags); } @@ -848,7 +863,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memset( struct dma_chan *chan, dma_addr_t dest, int value, size_t len, unsigned long flags) { - if (!chan || !chan->device) + if (!chan || !chan->device || !chan->device->device_prep_dma_memset) return NULL; return chan->device->device_prep_dma_memset(chan, dest, value, @@ -861,6 +876,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg( struct scatterlist *src_sg, unsigned int src_nents, unsigned long flags) { + if (!chan || !chan->device || !chan->device->device_prep_dma_sg) + return NULL; + return chan->device->device_prep_dma_sg(chan, dst_sg, dst_nents, src_sg, src_nents, flags); } -- cgit From 9fd4dcece43a53e5a9e65a973df5693702ee6401 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 22 Mar 2016 14:11:13 +0100 Subject: debugfs: prevent access to possibly dead file_operations at file open Nothing prevents a dentry found by path lookup before a return of __debugfs_remove() to actually get opened after that return. Now, after the return of __debugfs_remove(), there are no guarantees whatsoever regarding the memory the corresponding inode's file_operations object had been kept in. Since __debugfs_remove() is seldomly invoked, usually from module exit handlers only, the race is hard to trigger and the impact is very low. A discussion of the problem outlined above as well as a suggested solution can be found in the (sub-)thread rooted at http://lkml.kernel.org/g/20130401203445.GA20862@ZenIV.linux.org.uk ("Yet another pipe related oops.") Basically, Greg KH suggests to introduce an intermediate fops and Al Viro points out that a pointer to the original ones may be stored in ->d_fsdata. Follow this line of reasoning: - Add SRCU as a reverse dependency of DEBUG_FS. - Introduce a srcu_struct object for the debugfs subsystem. - In debugfs_create_file(), store a pointer to the original file_operations object in ->d_fsdata. - Make debugfs_remove() and debugfs_remove_recursive() wait for a SRCU grace period after the dentry has been delete()'d and before they return to their callers. - Introduce an intermediate file_operations object named "debugfs_open_proxy_file_operations". It's ->open() functions checks, under the protection of a SRCU read lock, whether the dentry is still alive, i.e. has not been d_delete()'d and if so, tries to acquire a reference on the owning module. On success, it sets the file object's ->f_op to the original file_operations and forwards the ongoing open() call to the original ->open(). - For clarity, rename the former debugfs_file_operations to debugfs_noop_file_operations -- they are in no way canonical. The choice of SRCU over "normal" RCU is justified by the fact, that the former may also be used to protect ->i_private data from going away during the execution of a file's readers and writers which may (and do) sleep. Finally, introduce the fs/debugfs/internal.h header containing some declarations internal to the debugfs implementation. Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++- fs/debugfs/inode.c | 13 ++++++- fs/debugfs/internal.h | 24 +++++++++++++ include/linux/debugfs.h | 3 -- lib/Kconfig.debug | 1 + 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 fs/debugfs/internal.h (limited to 'include/linux') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index d2ba12e23ed9..736ab3c988f2 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -22,6 +22,9 @@ #include #include #include +#include + +#include "internal.h" static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) @@ -35,13 +38,99 @@ static ssize_t default_write_file(struct file *file, const char __user *buf, return count; } -const struct file_operations debugfs_file_operations = { +const struct file_operations debugfs_noop_file_operations = { .read = default_read_file, .write = default_write_file, .open = simple_open, .llseek = noop_llseek, }; +/** + * debugfs_use_file_start - mark the beginning of file data access + * @dentry: the dentry object whose data is being accessed. + * @srcu_idx: a pointer to some memory to store a SRCU index in. + * + * Up to a matching call to debugfs_use_file_finish(), any + * successive call into the file removing functions debugfs_remove() + * and debugfs_remove_recursive() will block. Since associated private + * file data may only get freed after a successful return of any of + * the removal functions, you may safely access it after a successful + * call to debugfs_use_file_start() without worrying about + * lifetime issues. + * + * If -%EIO is returned, the file has already been removed and thus, + * it is not safe to access any of its data. If, on the other hand, + * it is allowed to access the file data, zero is returned. + * + * Regardless of the return code, any call to + * debugfs_use_file_start() must be followed by a matching call + * to debugfs_use_file_finish(). + */ +static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) + __acquires(&debugfs_srcu) +{ + *srcu_idx = srcu_read_lock(&debugfs_srcu); + barrier(); + if (d_unlinked(dentry)) + return -EIO; + return 0; +} + +/** + * debugfs_use_file_finish - mark the end of file data access + * @srcu_idx: the SRCU index "created" by a former call to + * debugfs_use_file_start(). + * + * Allow any ongoing concurrent call into debugfs_remove() or + * debugfs_remove_recursive() blocked by a former call to + * debugfs_use_file_start() to proceed and return to its caller. + */ +static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) +{ + srcu_read_unlock(&debugfs_srcu, srcu_idx); +} + +#define F_DENTRY(filp) ((filp)->f_path.dentry) + +#define REAL_FOPS_DEREF(dentry) \ + ((const struct file_operations *)(dentry)->d_fsdata) + +static int open_proxy_open(struct inode *inode, struct file *filp) +{ + const struct dentry *dentry = F_DENTRY(filp); + const struct file_operations *real_fops = NULL; + int srcu_idx, r; + + r = debugfs_use_file_start(dentry, &srcu_idx); + if (r) { + r = -ENOENT; + goto out; + } + + real_fops = REAL_FOPS_DEREF(dentry); + real_fops = fops_get(real_fops); + if (!real_fops) { + /* Huh? Module did not clean up after itself at exit? */ + WARN(1, "debugfs file owner did not clean up at exit: %pd", + dentry); + r = -ENXIO; + goto out; + } + replace_fops(filp, real_fops); + + if (real_fops->open) + r = real_fops->open(inode, filp); + +out: + fops_put(real_fops); + debugfs_use_file_finish(srcu_idx); + return r; +} + +const struct file_operations debugfs_open_proxy_file_operations = { + .open = open_proxy_open, +}; + static struct dentry *debugfs_create_mode(const char *name, umode_t mode, struct dentry *parent, void *value, const struct file_operations *fops, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index b1e7f35f3cd4..2905dd160575 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -27,9 +27,14 @@ #include #include #include +#include + +#include "internal.h" #define DEBUGFS_DEFAULT_MODE 0700 +DEFINE_SRCU(debugfs_srcu); + static struct vfsmount *debugfs_mount; static int debugfs_mount_count; static bool debugfs_registered; @@ -341,8 +346,12 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, return failed_creating(dentry); inode->i_mode = mode; - inode->i_fop = fops ? fops : &debugfs_file_operations; inode->i_private = data; + + inode->i_fop = fops ? &debugfs_open_proxy_file_operations + : &debugfs_noop_file_operations; + dentry->d_fsdata = (void *)fops; + d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); return end_creating(dentry); @@ -570,6 +579,7 @@ void debugfs_remove(struct dentry *dentry) inode_unlock(d_inode(parent)); if (!ret) simple_release_fs(&debugfs_mount, &debugfs_mount_count); + synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove); @@ -647,6 +657,7 @@ void debugfs_remove_recursive(struct dentry *dentry) if (!__debugfs_remove(child, parent)) simple_release_fs(&debugfs_mount, &debugfs_mount_count); inode_unlock(d_inode(parent)); + synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove_recursive); diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h new file mode 100644 index 000000000000..c7aaa5cb6685 --- /dev/null +++ b/fs/debugfs/internal.h @@ -0,0 +1,24 @@ +/* + * internal.h - declarations internal to debugfs + * + * Copyright (C) 2016 Nicolai Stange + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + */ + +#ifndef _DEBUGFS_INTERNAL_H_ +#define _DEBUGFS_INTERNAL_H_ + +struct file_operations; +struct srcu_struct; + +/* declared over in file.c */ +extern const struct file_operations debugfs_noop_file_operations; +extern const struct file_operations debugfs_open_proxy_file_operations; + +extern struct srcu_struct debugfs_srcu; + +#endif /* _DEBUGFS_INTERNAL_H_ */ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 981e53ab84e8..fcafe2d389f9 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -43,9 +43,6 @@ extern struct dentry *arch_debugfs_dir; #if defined(CONFIG_DEBUG_FS) -/* declared over in file.c */ -extern const struct file_operations debugfs_file_operations; - struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1e9a607534ca..ddb0e8337aae 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -257,6 +257,7 @@ config PAGE_OWNER config DEBUG_FS bool "Debug Filesystem" + select SRCU help debugfs is a virtual file system that kernel developers use to put debugging files into. Enable this option to be able to read and -- cgit From 49d200deaa680501f19a247b1fffb29301e51d2b Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 22 Mar 2016 14:11:14 +0100 Subject: debugfs: prevent access to removed files' private data Upon return of debugfs_remove()/debugfs_remove_recursive(), it might still be attempted to access associated private file data through previously opened struct file objects. If that data has been freed by the caller of debugfs_remove*() in the meanwhile, the reading/writing process would either encounter a fault or, if the memory address in question has been reassigned again, unrelated data structures could get overwritten. However, since debugfs files are seldomly removed, usually from module exit handlers only, the impact is very low. Currently, there are ~1000 call sites of debugfs_create_file() spread throughout the whole tree and touching all of those struct file_operations in order to make them file removal aware by means of checking the result of debugfs_use_file_start() from within their methods is unfeasible. Instead, wrap the struct file_operations by a lifetime managing proxy at file open: - In debugfs_create_file(), the original fops handed in has got stashed away in ->d_fsdata already. - In debugfs_create_file(), install a proxy file_operations factory, debugfs_full_proxy_file_operations, at ->i_fop. This proxy factory has got an ->open() method only. It carries out some lifetime checks and if successful, dynamically allocates and sets up a new struct file_operations proxy at ->f_op. Afterwards, it forwards to the ->open() of the original struct file_operations in ->d_fsdata, if any. The dynamically set up proxy at ->f_op has got a lifetime managing wrapper set for each of the methods defined in the original struct file_operations in ->d_fsdata. Its ->release()er frees the proxy again and forwards to the original ->release(), if any. In order not to mislead the VFS layer, it is strictly necessary to leave those fields blank in the proxy that have been NULL in the original struct file_operations also, i.e. aren't supported. This is why there is a need for dynamically allocated proxies. The choice made not to allocate a proxy instance for every dentry at file creation, but for every struct file object instantiated thereof is justified by the expected usage pattern of debugfs, namely that in general very few files get opened more than once at a time. The wrapper methods set in the struct file_operations implement lifetime managing by means of the SRCU protection facilities already in place for debugfs: They set up a SRCU read side critical section and check whether the dentry is still alive by means of debugfs_use_file_start(). If so, they forward the call to the original struct file_operation stored in ->d_fsdata, still under the protection of the SRCU read side critical section. This SRCU read side critical section prevents any pending debugfs_remove() and friends to return to their callers. Since a file's private data must only be freed after the return of debugfs_remove(), the ongoing proxied call is guarded against any file removal race. If, on the other hand, the initial call to debugfs_use_file_start() detects that the dentry is dead, the wrapper simply returns -EIO and does not forward the call. Note that the ->poll() wrapper is special in that its signature does not allow for the return of arbitrary -EXXX values and thus, POLLHUP is returned here. In order not to pollute debugfs with wrapper definitions that aren't ever needed, I chose not to define a wrapper for every struct file_operations method possible. Instead, a wrapper is defined only for the subset of methods which are actually set by any debugfs users. Currently, these are: ->llseek() ->read() ->write() ->unlocked_ioctl() ->poll() The ->release() wrapper is special in that it does not protect the original ->release() in any way from dead files in order not to leak resources. Thus, any ->release() handed to debugfs must implement file lifetime management manually, if needed. For only 33 out of a total of 434 releasers handed in to debugfs, it could not be verified immediately whether they access data structures that might have been freed upon a debugfs_remove() return in the meanwhile. Export debugfs_use_file_start() and debugfs_use_file_finish() in order to allow any ->release() to manually implement file lifetime management. For a set of common cases of struct file_operations implemented by the debugfs_core itself, future patches will incorporate file lifetime management directly within those in order to allow for their unproxied operation. Rename the original, non-proxying "debugfs_create_file()" to "debugfs_create_file_unsafe()" and keep it for future internal use by debugfs itself. Factor out code common to both into the new __debugfs_create_file(). Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++- fs/debugfs/inode.c | 70 ++++++++++++++------- fs/debugfs/internal.h | 6 +- include/linux/debugfs.h | 20 ++++++ 4 files changed, 226 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 736ab3c988f2..6eb58a8ed03c 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -23,9 +23,12 @@ #include #include #include +#include #include "internal.h" +struct poll_table_struct; + static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -66,7 +69,7 @@ const struct file_operations debugfs_noop_file_operations = { * debugfs_use_file_start() must be followed by a matching call * to debugfs_use_file_finish(). */ -static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) +int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) __acquires(&debugfs_srcu) { *srcu_idx = srcu_read_lock(&debugfs_srcu); @@ -75,6 +78,7 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) return -EIO; return 0; } +EXPORT_SYMBOL_GPL(debugfs_use_file_start); /** * debugfs_use_file_finish - mark the end of file data access @@ -85,10 +89,11 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) * debugfs_remove_recursive() blocked by a former call to * debugfs_use_file_start() to proceed and return to its caller. */ -static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) +void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) { srcu_read_unlock(&debugfs_srcu, srcu_idx); } +EXPORT_SYMBOL_GPL(debugfs_use_file_finish); #define F_DENTRY(filp) ((filp)->f_path.dentry) @@ -131,6 +136,154 @@ const struct file_operations debugfs_open_proxy_file_operations = { .open = open_proxy_open, }; +#define PROTO(args...) args +#define ARGS(args...) args + +#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \ +static ret_type full_proxy_ ## name(proto) \ +{ \ + const struct dentry *dentry = F_DENTRY(filp); \ + const struct file_operations *real_fops = \ + REAL_FOPS_DEREF(dentry); \ + int srcu_idx; \ + ret_type r; \ + \ + r = debugfs_use_file_start(dentry, &srcu_idx); \ + if (likely(!r)) \ + r = real_fops->name(args); \ + debugfs_use_file_finish(srcu_idx); \ + return r; \ +} + +FULL_PROXY_FUNC(llseek, loff_t, filp, + PROTO(struct file *filp, loff_t offset, int whence), + ARGS(filp, offset, whence)); + +FULL_PROXY_FUNC(read, ssize_t, filp, + PROTO(struct file *filp, char __user *buf, size_t size, + loff_t *ppos), + ARGS(filp, buf, size, ppos)); + +FULL_PROXY_FUNC(write, ssize_t, filp, + PROTO(struct file *filp, const char __user *buf, size_t size, + loff_t *ppos), + ARGS(filp, buf, size, ppos)); + +FULL_PROXY_FUNC(unlocked_ioctl, long, filp, + PROTO(struct file *filp, unsigned int cmd, unsigned long arg), + ARGS(filp, cmd, arg)); + +static unsigned int full_proxy_poll(struct file *filp, + struct poll_table_struct *wait) +{ + const struct dentry *dentry = F_DENTRY(filp); + const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry); + int srcu_idx; + unsigned int r = 0; + + if (debugfs_use_file_start(dentry, &srcu_idx)) { + debugfs_use_file_finish(srcu_idx); + return POLLHUP; + } + + r = real_fops->poll(filp, wait); + debugfs_use_file_finish(srcu_idx); + return r; +} + +static int full_proxy_release(struct inode *inode, struct file *filp) +{ + const struct dentry *dentry = F_DENTRY(filp); + const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry); + const struct file_operations *proxy_fops = filp->f_op; + int r = 0; + + /* + * We must not protect this against removal races here: the + * original releaser should be called unconditionally in order + * not to leak any resources. Releasers must not assume that + * ->i_private is still being meaningful here. + */ + if (real_fops->release) + r = real_fops->release(inode, filp); + + replace_fops(filp, d_inode(dentry)->i_fop); + kfree((void *)proxy_fops); + fops_put(real_fops); + return 0; +} + +static void __full_proxy_fops_init(struct file_operations *proxy_fops, + const struct file_operations *real_fops) +{ + proxy_fops->release = full_proxy_release; + if (real_fops->llseek) + proxy_fops->llseek = full_proxy_llseek; + if (real_fops->read) + proxy_fops->read = full_proxy_read; + if (real_fops->write) + proxy_fops->write = full_proxy_write; + if (real_fops->poll) + proxy_fops->poll = full_proxy_poll; + if (real_fops->unlocked_ioctl) + proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl; +} + +static int full_proxy_open(struct inode *inode, struct file *filp) +{ + const struct dentry *dentry = F_DENTRY(filp); + const struct file_operations *real_fops = NULL; + struct file_operations *proxy_fops = NULL; + int srcu_idx, r; + + r = debugfs_use_file_start(dentry, &srcu_idx); + if (r) { + r = -ENOENT; + goto out; + } + + real_fops = REAL_FOPS_DEREF(dentry); + real_fops = fops_get(real_fops); + if (!real_fops) { + /* Huh? Module did not cleanup after itself at exit? */ + WARN(1, "debugfs file owner did not clean up at exit: %pd", + dentry); + r = -ENXIO; + goto out; + } + + proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL); + if (!proxy_fops) { + r = -ENOMEM; + goto free_proxy; + } + __full_proxy_fops_init(proxy_fops, real_fops); + replace_fops(filp, proxy_fops); + + if (real_fops->open) { + r = real_fops->open(inode, filp); + + if (filp->f_op != proxy_fops) { + /* No protection against file removal anymore. */ + WARN(1, "debugfs file owner replaced proxy fops: %pd", + dentry); + goto free_proxy; + } + } + + goto out; +free_proxy: + kfree(proxy_fops); + fops_put(real_fops); +out: + debugfs_use_file_finish(srcu_idx); + return r; +} + +const struct file_operations debugfs_full_proxy_file_operations = { + .open = full_proxy_open, +}; + static struct dentry *debugfs_create_mode(const char *name, umode_t mode, struct dentry *parent, void *value, const struct file_operations *fops, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 2905dd160575..136f269f01de 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -300,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry) return dentry; } +static struct dentry *__debugfs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *proxy_fops, + const struct file_operations *real_fops) +{ + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + BUG_ON(!S_ISREG(mode)); + dentry = start_creating(name, parent); + + if (IS_ERR(dentry)) + return NULL; + + inode = debugfs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = mode; + inode->i_private = data; + + inode->i_fop = proxy_fops; + dentry->d_fsdata = (void *)real_fops; + + d_instantiate(dentry, inode); + fsnotify_create(d_inode(dentry->d_parent), dentry); + return end_creating(dentry); +} + /** * debugfs_create_file - create a file in the debugfs filesystem * @name: a pointer to a string containing the name of the file to create. @@ -330,33 +361,24 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { - struct dentry *dentry; - struct inode *inode; - - if (!(mode & S_IFMT)) - mode |= S_IFREG; - BUG_ON(!S_ISREG(mode)); - dentry = start_creating(name, parent); - - if (IS_ERR(dentry)) - return NULL; - - inode = debugfs_get_inode(dentry->d_sb); - if (unlikely(!inode)) - return failed_creating(dentry); - inode->i_mode = mode; - inode->i_private = data; + return __debugfs_create_file(name, mode, parent, data, + fops ? &debugfs_full_proxy_file_operations : + &debugfs_noop_file_operations, + fops); +} +EXPORT_SYMBOL_GPL(debugfs_create_file); - inode->i_fop = fops ? &debugfs_open_proxy_file_operations - : &debugfs_noop_file_operations; - dentry->d_fsdata = (void *)fops; +struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops) +{ - d_instantiate(dentry, inode); - fsnotify_create(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return __debugfs_create_file(name, mode, parent, data, + fops ? &debugfs_open_proxy_file_operations : + &debugfs_noop_file_operations, + fops); } -EXPORT_SYMBOL_GPL(debugfs_create_file); /** * debugfs_create_file_size - create a file in the debugfs filesystem @@ -579,6 +601,7 @@ void debugfs_remove(struct dentry *dentry) inode_unlock(d_inode(parent)); if (!ret) simple_release_fs(&debugfs_mount, &debugfs_mount_count); + synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove); @@ -657,6 +680,7 @@ void debugfs_remove_recursive(struct dentry *dentry) if (!__debugfs_remove(child, parent)) simple_release_fs(&debugfs_mount, &debugfs_mount_count); inode_unlock(d_inode(parent)); + synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove_recursive); diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index c7aaa5cb6685..bba52634b995 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -13,12 +13,14 @@ #define _DEBUGFS_INTERNAL_H_ struct file_operations; -struct srcu_struct; /* declared over in file.c */ extern const struct file_operations debugfs_noop_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations; +extern const struct file_operations debugfs_full_proxy_file_operations; -extern struct srcu_struct debugfs_srcu; +struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); #endif /* _DEBUGFS_INTERNAL_H_ */ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index fcafe2d389f9..a63e6ea3321c 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -19,9 +19,11 @@ #include #include +#include struct device; struct file_operations; +struct srcu_struct; struct debugfs_blob_wrapper { void *data; @@ -41,6 +43,8 @@ struct debugfs_regset32 { extern struct dentry *arch_debugfs_dir; +extern struct srcu_struct debugfs_srcu; + #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_create_file(const char *name, umode_t mode, @@ -65,6 +69,11 @@ struct dentry *debugfs_create_automount(const char *name, void debugfs_remove(struct dentry *dentry); void debugfs_remove_recursive(struct dentry *dentry); +int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) + __acquires(&debugfs_srcu); + +void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); + struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name); @@ -173,6 +182,17 @@ static inline void debugfs_remove(struct dentry *dentry) static inline void debugfs_remove_recursive(struct dentry *dentry) { } +static inline int debugfs_use_file_start(const struct dentry *dentry, + int *srcu_idx) + __acquires(&debugfs_srcu) +{ + return 0; +} + +static inline void debugfs_use_file_finish(int srcu_idx) + __releases(&debugfs_srcu) +{ } + static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, char *new_name) { -- cgit From c64688081490321f2d23a292ef24e60bb321f3f1 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 22 Mar 2016 14:11:15 +0100 Subject: debugfs: add support for self-protecting attribute file fops In order to protect them against file removal issues, debugfs_create_file() creates a lifetime managing proxy around each struct file_operations handed in. In cases where this struct file_operations is able to manage file lifetime by itself already, the proxy created by debugfs is a waste of resources. The most common class of struct file_operations given to debugfs are those defined by means of the DEFINE_SIMPLE_ATTRIBUTE() macro. Introduce a DEFINE_DEBUGFS_ATTRIBUTE() macro to allow any struct file_operations of this class to be easily made file lifetime aware and thus, to be operated unproxied. Specifically, introduce debugfs_attr_read() and debugfs_attr_write() which wrap simple_attr_read() and simple_attr_write() under the protection of a debugfs_use_file_start()/debugfs_use_file_finish() pair. Make DEFINE_DEBUGFS_ATTRIBUTE() set the defined struct file_operations' ->read() and ->write() members to these wrappers. Export debugfs_create_file_unsafe() in order to allow debugfs users to create their files in non-proxying operation mode. Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 28 ++++++++++++++++++++++++++++ fs/debugfs/inode.c | 28 ++++++++++++++++++++++++++++ include/linux/debugfs.h | 26 ++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) (limited to 'include/linux') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 6eb58a8ed03c..8ef56d9499a4 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -284,6 +284,34 @@ const struct file_operations debugfs_full_proxy_file_operations = { .open = full_proxy_open, }; +ssize_t debugfs_attr_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + int srcu_idx; + + ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); + if (likely(!ret)) + ret = simple_attr_read(file, buf, len, ppos); + debugfs_use_file_finish(srcu_idx); + return ret; +} +EXPORT_SYMBOL_GPL(debugfs_attr_read); + +ssize_t debugfs_attr_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + int srcu_idx; + + ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); + if (likely(!ret)) + ret = simple_attr_write(file, buf, len, ppos); + debugfs_use_file_finish(srcu_idx); + return ret; +} +EXPORT_SYMBOL_GPL(debugfs_attr_write); + static struct dentry *debugfs_create_mode(const char *name, umode_t mode, struct dentry *parent, void *value, const struct file_operations *fops, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 136f269f01de..41e079a8da26 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -369,6 +369,33 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, } EXPORT_SYMBOL_GPL(debugfs_create_file); +/** + * debugfs_create_file_unsafe - create a file in the debugfs filesystem + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is NULL, then the + * file will be created in the root of the debugfs filesystem. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.i_private pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + * + * debugfs_create_file_unsafe() is completely analogous to + * debugfs_create_file(), the only difference being that the fops + * handed it will not get protected against file removals by the + * debugfs core. + * + * It is your responsibility to protect your struct file_operation + * methods against file removals by means of debugfs_use_file_start() + * and debugfs_use_file_finish(). ->open() is still protected by + * debugfs though. + * + * Any struct file_operations defined by means of + * DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and + * thus, may be used here. + */ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) @@ -379,6 +406,7 @@ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, &debugfs_noop_file_operations, fops); } +EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe); /** * debugfs_create_file_size - create a file in the debugfs filesystem diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index a63e6ea3321c..1438e2322d5c 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -50,6 +50,9 @@ extern struct srcu_struct debugfs_srcu; struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); +struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); struct dentry *debugfs_create_file_size(const char *name, umode_t mode, struct dentry *parent, void *data, @@ -74,6 +77,26 @@ int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); +ssize_t debugfs_attr_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos); +ssize_t debugfs_attr_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos); + +#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ +static int __fops ## _open(struct inode *inode, struct file *file) \ +{ \ + __simple_attr_check_format(__fmt, 0ull); \ + return simple_attr_open(inode, file, __get, __set, __fmt); \ +} \ +static const struct file_operations __fops = { \ + .owner = THIS_MODULE, \ + .open = __fops ## _open, \ + .release = simple_attr_release, \ + .read = debugfs_attr_read, \ + .write = debugfs_attr_write, \ + .llseek = generic_file_llseek, \ +} + struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name); @@ -193,6 +216,9 @@ static inline void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) { } +#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ + static const struct file_operations __fops = { 0 } + static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, char *new_name) { -- cgit From c422025c185fb2bb28df65b1bbed7953480c7f87 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Mar 2016 16:24:41 +0200 Subject: dmaengine: dw: rename masters to reflect actual topology The source and destination masters are reflecting buses or their layers to where the different devices can be connected. The patch changes the master names to reflect which one is related to which independently on the transfer direction. The outcome of the change is that the memory data width is now always limited by a data width of the master which is dedicated to communicate to memory. The patch will not break anything since all current users have the same data width for all masters. Though it would be nice to revisit avr32 platforms to check what is the actual hardware topology in use there. It seems that it has one bus and two masters on it as stated by Table 8-2, that's why everything works independently on the master in use. The purpose of the sequential patch is to fix the driver for configuration of more than one bus. The change is done in the assumption that src_master and dst_master are reflecting a connection to the memory and peripheral correspondently on avr32 and otherwise on the rest. Acked-by: Hans-Christian Egtvedt Acked-by: Mark Brown Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 4 ++-- arch/avr32/mach-at32ap/at32ap700x.c | 16 ++++++++-------- drivers/ata/sata_dwc_460ex.c | 4 ++-- drivers/dma/dw/core.c | 19 +++++++++---------- drivers/dma/dw/platform.c | 12 ++++++------ drivers/dma/dw/regs.h | 4 ++-- drivers/spi/spi-pxa2xx-pci.c | 8 ++++---- drivers/tty/serial/8250/8250_pci.c | 8 ++++---- include/linux/platform_data/dma-dw.h | 8 ++++---- 9 files changed, 41 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c261598164a7..c99c1ffac199 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -47,8 +47,8 @@ The four cells in order are: 1. A phandle pointing to the DMA controller 2. The DMA request line number -3. Source master for transfers on allocated channel -4. Destination master for transfers on allocated channel +3. Memory master for transfers on allocated channel +4. Peripheral master for transfers on allocated channel Example: diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index bf445aa48282..00d6dcc1d9b6 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1365,8 +1365,8 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) slave->dma_dev = &dw_dmac0_device.dev; slave->src_id = 0; slave->dst_id = 1; - slave->src_master = 1; - slave->dst_master = 0; + slave->m_master = 1; + slave->p_master = 0; data->dma_slave = slave; data->dma_filter = at32_mci_dma_filter; @@ -2061,16 +2061,16 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data, if (flags & AC97C_CAPTURE) { rx_dws->dma_dev = &dw_dmac0_device.dev; rx_dws->src_id = 3; - rx_dws->src_master = 0; - rx_dws->dst_master = 1; + rx_dws->m_master = 0; + rx_dws->p_master = 1; } /* Check if DMA slave interface for playback should be configured. */ if (flags & AC97C_PLAYBACK) { tx_dws->dma_dev = &dw_dmac0_device.dev; tx_dws->dst_id = 4; - tx_dws->src_master = 0; - tx_dws->dst_master = 1; + tx_dws->m_master = 0; + tx_dws->p_master = 1; } if (platform_device_add_data(pdev, data, @@ -2141,8 +2141,8 @@ at32_add_device_abdac(unsigned int id, struct atmel_abdac_pdata *data) dws->dma_dev = &dw_dmac0_device.dev; dws->dst_id = 2; - dws->src_master = 0; - dws->dst_master = 1; + dws->m_master = 0; + dws->p_master = 1; if (platform_device_add_data(pdev, data, sizeof(struct atmel_abdac_pdata))) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 902034991517..80bdcabc293f 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -201,8 +201,8 @@ static struct sata_dwc_host_priv host_pvt; static struct dw_dma_slave sata_dwc_dma_dws = { .src_id = 0, .dst_id = 0, - .src_master = 0, - .dst_master = 1, + .m_master = 1, + .p_master = 0, }; /* diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 97199b3c25a2..5bd7873a02c6 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -50,8 +50,8 @@ | DWC_CTLL_SRC_MSIZE(_smsize) \ | DWC_CTLL_LLP_D_EN \ | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(_dwc->dst_master) \ - | DWC_CTLL_SMS(_dwc->src_master)); \ + | DWC_CTLL_DMS(_dwc->p_master) \ + | DWC_CTLL_SMS(_dwc->m_master)); \ }) /* @@ -709,8 +709,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction = DMA_MEM_TO_MEM; - data_width = min_t(unsigned int, dw->data_width[dwc->src_master], - dw->data_width[dwc->dst_master]); + data_width = dw->data_width[dwc->m_master]; src_width = dst_width = min_t(unsigned int, data_width, dwc_fast_ffs(src | dest | len)); @@ -802,7 +801,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); - data_width = dw->data_width[dwc->src_master]; + data_width = dw->data_width[dwc->m_master]; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -859,7 +858,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); - data_width = dw->data_width[dwc->dst_master]; + data_width = dw->data_width[dwc->m_master]; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -937,8 +936,8 @@ bool dw_dma_filter(struct dma_chan *chan, void *param) dwc->src_id = dws->src_id; dwc->dst_id = dws->dst_id; - dwc->src_master = dws->src_master; - dwc->dst_master = dws->dst_master; + dwc->m_master = dws->m_master; + dwc->p_master = dws->p_master; return true; } @@ -1227,8 +1226,8 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dwc->src_id = 0; dwc->dst_id = 0; - dwc->src_master = 0; - dwc->dst_master = 0; + dwc->m_master = 0; + dwc->p_master = 0; dwc->initialized = false; diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 26edbe3a27ac..23616c57645c 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -42,13 +42,13 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, slave.src_id = dma_spec->args[0]; slave.dst_id = dma_spec->args[0]; - slave.src_master = dma_spec->args[1]; - slave.dst_master = dma_spec->args[2]; + slave.m_master = dma_spec->args[1]; + slave.p_master = dma_spec->args[2]; if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS || slave.dst_id >= DW_DMA_MAX_NR_REQUESTS || - slave.src_master >= dw->nr_masters || - slave.dst_master >= dw->nr_masters)) + slave.m_master >= dw->nr_masters || + slave.p_master >= dw->nr_masters)) return NULL; dma_cap_zero(cap); @@ -66,8 +66,8 @@ static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) .dma_dev = dma_spec->dev, .src_id = dma_spec->slave_id, .dst_id = dma_spec->slave_id, - .src_master = 1, - .dst_master = 0, + .m_master = 0, + .p_master = 1, }; return dw_dma_filter(chan, &slave); diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 0a50c18d85b8..a63d62bbffe2 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -249,8 +249,8 @@ struct dw_dma_chan { /* custom slave configuration */ u8 src_id; u8 dst_id; - u8 src_master; - u8 dst_master; + u8 m_master; + u8 p_master; /* configuration passed via .device_config */ struct dma_slave_config dma_sconfig; diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 520ed1dd5780..4fd7f9802f1b 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -144,16 +144,16 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, struct dw_dma_slave *slave = c->tx_param; slave->dma_dev = &dma_dev->dev; - slave->src_master = 1; - slave->dst_master = 0; + slave->m_master = 0; + slave->p_master = 1; } if (c->rx_param) { struct dw_dma_slave *slave = c->rx_param; slave->dma_dev = &dma_dev->dev; - slave->src_master = 1; - slave->dst_master = 0; + slave->m_master = 0; + slave->p_master = 1; } spi_pdata.dma_filter = lpss_dma_filter; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 98862aa5bb58..5eea74d7f9f4 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1454,13 +1454,13 @@ byt_serial_setup(struct serial_private *priv, return -EINVAL; } - rx_param->src_master = 1; - rx_param->dst_master = 0; + rx_param->m_master = 0; + rx_param->p_master = 1; dma->rxconf.src_maxburst = 16; - tx_param->src_master = 1; - tx_param->dst_master = 0; + tx_param->m_master = 0; + tx_param->p_master = 1; dma->txconf.dst_maxburst = 16; diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 03b6095d3b18..b881b978e486 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -21,15 +21,15 @@ * @dma_dev: required DMA master device * @src_id: src request line * @dst_id: dst request line - * @src_master: src master for transfers on allocated channel. - * @dst_master: dest master for transfers on allocated channel. + * @m_master: memory master for transfers on allocated channel + * @p_master: peripheral master for transfers on allocated channel */ struct dw_dma_slave { struct device *dma_dev; u8 src_id; u8 dst_id; - u8 src_master; - u8 dst_master; + u8 m_master; + u8 p_master; }; /** -- cgit From d17322feecf80152303426dd724577025d1fbd7e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 9 Apr 2016 10:52:26 +0200 Subject: gpio: sx150x: move platform data into driver The sx150x has some platform data definition in but this file is only included from the driver in the whole kernel so move its contents into the driver. Cc: Wei Chen Cc: Peter Rosin Acked-by: Wolfram Sang Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sx150x.c | 60 ++++++++++++++++++++++++++++++++- include/linux/i2c/sx150x.h | 82 ---------------------------------------------- 2 files changed, 59 insertions(+), 83 deletions(-) delete mode 100644 include/linux/i2c/sx150x.h (limited to 'include/linux') diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index d57e8ad0bfd2..d4501d5f8b8e 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -85,6 +84,65 @@ struct sx150x_device_data { } pri; }; +/** + * struct sx150x_platform_data - config data for SX150x driver + * @gpio_base: The index number of the first GPIO assigned to this + * GPIO expander. The expander will create a block of + * consecutively numbered gpios beginning at the given base, + * with the size of the block depending on the model of the + * expander chip. + * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO + * instead of as an oscillator, increasing the size of the + * GP(I)O pool created by this expander by one. The + * output-only GPO pin will be added at the end of the block. + * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor + * for each IO line in the expander. Setting the bit at + * position n will enable the pull-up for the IO at + * the corresponding offset. For chips with fewer than + * 16 IO pins, high-end bits are ignored. + * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down + * resistor for each IO line in the expander. Setting the + * bit at position n will enable the pull-down for the IO at + * the corresponding offset. For chips with fewer than + * 16 IO pins, high-end bits are ignored. + * @io_open_drain_ena: A bit-mask which enables-or disables open-drain + * operation for each IO line in the expander. Setting the + * bit at position n enables open-drain operation for + * the IO at the corresponding offset. Clearing the bit + * enables regular push-pull operation for that IO. + * For chips with fewer than 16 IO pins, high-end bits + * are ignored. + * @io_polarity: A bit-mask which enables polarity inversion for each IO line + * in the expander. Setting the bit at position n inverts + * the polarity of that IO line, while clearing it results + * in normal polarity. For chips with fewer than 16 IO pins, + * high-end bits are ignored. + * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line + * is connected, via which it reports interrupt events + * across all GPIO lines. This must be a real, + * pre-existing IRQ line. + * Setting this value < 0 disables the irq_chip functionality + * of the driver. + * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based + * IRQ lines will appear. Similarly to gpio_base, the expander + * will create a block of irqs beginning at this number. + * This value is ignored if irq_summary is < 0. + * @reset_during_probe: If set to true, the driver will trigger a full + * reset of the chip at the beginning of the probe + * in order to place it in a known state. + */ +struct sx150x_platform_data { + unsigned gpio_base; + bool oscio_is_gpo; + u16 io_pullup_ena; + u16 io_pulldn_ena; + u16 io_open_drain_ena; + u16 io_polarity; + int irq_summary; + unsigned irq_base; + bool reset_during_probe; +}; + struct sx150x_chip { struct gpio_chip gpio_chip; struct i2c_client *client; diff --git a/include/linux/i2c/sx150x.h b/include/linux/i2c/sx150x.h deleted file mode 100644 index 52baa79d69a7..000000000000 --- a/include/linux/i2c/sx150x.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Driver for the Semtech SX150x I2C GPIO Expanders - * - * Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ -#ifndef __LINUX_I2C_SX150X_H -#define __LINUX_I2C_SX150X_H - -/** - * struct sx150x_platform_data - config data for SX150x driver - * @gpio_base: The index number of the first GPIO assigned to this - * GPIO expander. The expander will create a block of - * consecutively numbered gpios beginning at the given base, - * with the size of the block depending on the model of the - * expander chip. - * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO - * instead of as an oscillator, increasing the size of the - * GP(I)O pool created by this expander by one. The - * output-only GPO pin will be added at the end of the block. - * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor - * for each IO line in the expander. Setting the bit at - * position n will enable the pull-up for the IO at - * the corresponding offset. For chips with fewer than - * 16 IO pins, high-end bits are ignored. - * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down - * resistor for each IO line in the expander. Setting the - * bit at position n will enable the pull-down for the IO at - * the corresponding offset. For chips with fewer than - * 16 IO pins, high-end bits are ignored. - * @io_open_drain_ena: A bit-mask which enables-or disables open-drain - * operation for each IO line in the expander. Setting the - * bit at position n enables open-drain operation for - * the IO at the corresponding offset. Clearing the bit - * enables regular push-pull operation for that IO. - * For chips with fewer than 16 IO pins, high-end bits - * are ignored. - * @io_polarity: A bit-mask which enables polarity inversion for each IO line - * in the expander. Setting the bit at position n inverts - * the polarity of that IO line, while clearing it results - * in normal polarity. For chips with fewer than 16 IO pins, - * high-end bits are ignored. - * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line - * is connected, via which it reports interrupt events - * across all GPIO lines. This must be a real, - * pre-existing IRQ line. - * Setting this value < 0 disables the irq_chip functionality - * of the driver. - * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based - * IRQ lines will appear. Similarly to gpio_base, the expander - * will create a block of irqs beginning at this number. - * This value is ignored if irq_summary is < 0. - * @reset_during_probe: If set to true, the driver will trigger a full - * reset of the chip at the beginning of the probe - * in order to place it in a known state. - */ -struct sx150x_platform_data { - unsigned gpio_base; - bool oscio_is_gpo; - u16 io_pullup_ena; - u16 io_pulldn_ena; - u16 io_open_drain_ena; - u16 io_polarity; - int irq_summary; - unsigned irq_base; - bool reset_during_probe; -}; - -#endif /* __LINUX_I2C_SX150X_H */ -- cgit From 58bc67fc32b1c67fb045f4828a67134dc8fee631 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jul 2015 15:23:28 +0300 Subject: ARM: OMAP2+: gpmc: Add platform data Add a platform data structure for GPMC. It contains all the necessary platform information that needs to be passed from platform init code to GPMC driver. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- include/linux/omap-gpmc.h | 3 +-- include/linux/platform_data/gpmc-omap.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 include/linux/platform_data/gpmc-omap.h (limited to 'include/linux') diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index d833eb4dd446..45d9075be1e5 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -7,8 +7,7 @@ * option) any later version. */ -/* Maximum Number of Chip Selects */ -#define GPMC_CS_NUM 8 +#include #define GPMC_CONFIG_WP 0x00000005 diff --git a/include/linux/platform_data/gpmc-omap.h b/include/linux/platform_data/gpmc-omap.h new file mode 100644 index 000000000000..6804a8b387d7 --- /dev/null +++ b/include/linux/platform_data/gpmc-omap.h @@ -0,0 +1,30 @@ +/* + * OMAP GPMC Platform data + * + * Copyright (C) 2014 Texas Instruments, Inc. - http://www.ti.com + * Roger Quadros + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _GPMC_OMAP_H_ +#define _GPMC_OMAP_H_ + +/* Maximum Number of Chip Selects */ +#define GPMC_CS_NUM 8 + +/* Data for each chip select */ +struct gpmc_omap_cs_data { + bool valid; /* data is valid */ + bool is_nand; /* device within this CS is NAND */ + struct platform_device *pdev; /* device within this CS region */ + unsigned int pdata_size; +}; + +struct gpmc_omap_platform_data { + struct gpmc_omap_cs_data cs[GPMC_CS_NUM]; +}; + +#endif /* _GPMC_OMAP_H */ -- cgit From fabe7d7756d17f5da4bd80fa2373c4bd93ed39e5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jul 2015 15:23:29 +0300 Subject: ARM: OMAP2+: gpmc: Add gpmc timings and settings to platform data Add device_timings, gpmc_timings and gpmc_setting to gpmc platform data. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- include/linux/omap-gpmc.h | 139 ------------------------------- include/linux/platform_data/gpmc-omap.h | 142 ++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 139 deletions(-) (limited to 'include/linux') diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index 45d9075be1e5..2dcef1c8c8d4 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -14,145 +14,6 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 -#define GPMC_BURST_4 4 /* 4 word burst */ -#define GPMC_BURST_8 8 /* 8 word burst */ -#define GPMC_BURST_16 16 /* 16 word burst */ -#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ -#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ -#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ -#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ - -/* bool type time settings */ -struct gpmc_bool_timings { - bool cycle2cyclediffcsen; - bool cycle2cyclesamecsen; - bool we_extra_delay; - bool oe_extra_delay; - bool adv_extra_delay; - bool cs_extra_delay; - bool time_para_granularity; -}; - -/* - * Note that all values in this struct are in nanoseconds except sync_clk - * (which is in picoseconds), while the register values are in gpmc_fck cycles. - */ -struct gpmc_timings { - /* Minimum clock period for synchronous mode (in picoseconds) */ - u32 sync_clk; - - /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ - u32 cs_on; /* Assertion time */ - u32 cs_rd_off; /* Read deassertion time */ - u32 cs_wr_off; /* Write deassertion time */ - - /* ADV signal timings corresponding to GPMC_CONFIG3 */ - u32 adv_on; /* Assertion time */ - u32 adv_rd_off; /* Read deassertion time */ - u32 adv_wr_off; /* Write deassertion time */ - u32 adv_aad_mux_on; /* ADV assertion time for AAD */ - u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */ - u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */ - - /* WE signals timings corresponding to GPMC_CONFIG4 */ - u32 we_on; /* WE assertion time */ - u32 we_off; /* WE deassertion time */ - - /* OE signals timings corresponding to GPMC_CONFIG4 */ - u32 oe_on; /* OE assertion time */ - u32 oe_off; /* OE deassertion time */ - u32 oe_aad_mux_on; /* OE assertion time for AAD */ - u32 oe_aad_mux_off; /* OE deassertion time for AAD */ - - /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ - u32 page_burst_access; /* Multiple access word delay */ - u32 access; /* Start-cycle to first data valid delay */ - u32 rd_cycle; /* Total read cycle time */ - u32 wr_cycle; /* Total write cycle time */ - - u32 bus_turnaround; - u32 cycle2cycle_delay; - - u32 wait_monitoring; - u32 clk_activation; - - /* The following are only on OMAP3430 */ - u32 wr_access; /* WRACCESSTIME */ - u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ - - struct gpmc_bool_timings bool_timings; -}; - -/* Device timings in picoseconds */ -struct gpmc_device_timings { - u32 t_ceasu; /* address setup to CS valid */ - u32 t_avdasu; /* address setup to ADV valid */ - /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is - * of tusb using these timings even for sync whilst - * ideally for adv_rd/(wr)_off it should have considered - * t_avdh instead. This indirectly necessitates r/w - * variations of t_avdp as it is possible to have one - * sync & other async - */ - u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ - u32 t_avdp_w; - u32 t_aavdh; /* address hold time */ - u32 t_oeasu; /* address setup to OE valid */ - u32 t_aa; /* access time from ADV assertion */ - u32 t_iaa; /* initial access time */ - u32 t_oe; /* access time from OE assertion */ - u32 t_ce; /* access time from CS asertion */ - u32 t_rd_cycle; /* read cycle time */ - u32 t_cez_r; /* read CS deassertion to high Z */ - u32 t_cez_w; /* write CS deassertion to high Z */ - u32 t_oez; /* OE deassertion to high Z */ - u32 t_weasu; /* address setup to WE valid */ - u32 t_wpl; /* write assertion time */ - u32 t_wph; /* write deassertion time */ - u32 t_wr_cycle; /* write cycle time */ - - u32 clk; - u32 t_bacc; /* burst access valid clock to output delay */ - u32 t_ces; /* CS setup time to clk */ - u32 t_avds; /* ADV setup time to clk */ - u32 t_avdh; /* ADV hold time from clk */ - u32 t_ach; /* address hold time from clk */ - u32 t_rdyo; /* clk to ready valid */ - - u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ - u32 t_ce_avd; /* CS on to ADV on delay */ - - /* XXX: check the possibility of combining - * cyc_aavhd_oe & cyc_aavdh_we - */ - u8 cyc_aavdh_oe;/* read address hold time in cycles */ - u8 cyc_aavdh_we;/* write address hold time in cycles */ - u8 cyc_oe; /* access time from OE assertion in cycles */ - u8 cyc_wpl; /* write deassertion time in cycles */ - u32 cyc_iaa; /* initial access time in cycles */ - - /* extra delays */ - bool ce_xdelay; - bool avd_xdelay; - bool oe_xdelay; - bool we_xdelay; -}; - -struct gpmc_settings { - bool burst_wrap; /* enables wrap bursting */ - bool burst_read; /* enables read page/burst mode */ - bool burst_write; /* enables write page/burst mode */ - bool device_nand; /* device is NAND */ - bool sync_read; /* enables synchronous reads */ - bool sync_write; /* enables synchronous writes */ - bool wait_on_read; /* monitor wait on reads */ - bool wait_on_write; /* monitor wait on writes */ - u32 burst_len; /* page/burst length */ - u32 device_width; /* device bus width (8 or 16 bit) */ - u32 mux_add_data; /* multiplex address & data */ - u32 wait_pin; /* wait-pin to be used */ -}; - extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, struct gpmc_settings *gpmc_s, struct gpmc_device_timings *dev_t); diff --git a/include/linux/platform_data/gpmc-omap.h b/include/linux/platform_data/gpmc-omap.h index 6804a8b387d7..67ccdb0e1606 100644 --- a/include/linux/platform_data/gpmc-omap.h +++ b/include/linux/platform_data/gpmc-omap.h @@ -15,10 +15,152 @@ /* Maximum Number of Chip Selects */ #define GPMC_CS_NUM 8 +/* bool type time settings */ +struct gpmc_bool_timings { + bool cycle2cyclediffcsen; + bool cycle2cyclesamecsen; + bool we_extra_delay; + bool oe_extra_delay; + bool adv_extra_delay; + bool cs_extra_delay; + bool time_para_granularity; +}; + +/* + * Note that all values in this struct are in nanoseconds except sync_clk + * (which is in picoseconds), while the register values are in gpmc_fck cycles. + */ +struct gpmc_timings { + /* Minimum clock period for synchronous mode (in picoseconds) */ + u32 sync_clk; + + /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ + u32 cs_on; /* Assertion time */ + u32 cs_rd_off; /* Read deassertion time */ + u32 cs_wr_off; /* Write deassertion time */ + + /* ADV signal timings corresponding to GPMC_CONFIG3 */ + u32 adv_on; /* Assertion time */ + u32 adv_rd_off; /* Read deassertion time */ + u32 adv_wr_off; /* Write deassertion time */ + u32 adv_aad_mux_on; /* ADV assertion time for AAD */ + u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */ + u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */ + + /* WE signals timings corresponding to GPMC_CONFIG4 */ + u32 we_on; /* WE assertion time */ + u32 we_off; /* WE deassertion time */ + + /* OE signals timings corresponding to GPMC_CONFIG4 */ + u32 oe_on; /* OE assertion time */ + u32 oe_off; /* OE deassertion time */ + u32 oe_aad_mux_on; /* OE assertion time for AAD */ + u32 oe_aad_mux_off; /* OE deassertion time for AAD */ + + /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ + u32 page_burst_access; /* Multiple access word delay */ + u32 access; /* Start-cycle to first data valid delay */ + u32 rd_cycle; /* Total read cycle time */ + u32 wr_cycle; /* Total write cycle time */ + + u32 bus_turnaround; + u32 cycle2cycle_delay; + + u32 wait_monitoring; + u32 clk_activation; + + /* The following are only on OMAP3430 */ + u32 wr_access; /* WRACCESSTIME */ + u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ + + struct gpmc_bool_timings bool_timings; +}; + +/* Device timings in picoseconds */ +struct gpmc_device_timings { + u32 t_ceasu; /* address setup to CS valid */ + u32 t_avdasu; /* address setup to ADV valid */ + /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is + * of tusb using these timings even for sync whilst + * ideally for adv_rd/(wr)_off it should have considered + * t_avdh instead. This indirectly necessitates r/w + * variations of t_avdp as it is possible to have one + * sync & other async + */ + u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ + u32 t_avdp_w; + u32 t_aavdh; /* address hold time */ + u32 t_oeasu; /* address setup to OE valid */ + u32 t_aa; /* access time from ADV assertion */ + u32 t_iaa; /* initial access time */ + u32 t_oe; /* access time from OE assertion */ + u32 t_ce; /* access time from CS asertion */ + u32 t_rd_cycle; /* read cycle time */ + u32 t_cez_r; /* read CS deassertion to high Z */ + u32 t_cez_w; /* write CS deassertion to high Z */ + u32 t_oez; /* OE deassertion to high Z */ + u32 t_weasu; /* address setup to WE valid */ + u32 t_wpl; /* write assertion time */ + u32 t_wph; /* write deassertion time */ + u32 t_wr_cycle; /* write cycle time */ + + u32 clk; + u32 t_bacc; /* burst access valid clock to output delay */ + u32 t_ces; /* CS setup time to clk */ + u32 t_avds; /* ADV setup time to clk */ + u32 t_avdh; /* ADV hold time from clk */ + u32 t_ach; /* address hold time from clk */ + u32 t_rdyo; /* clk to ready valid */ + + u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ + u32 t_ce_avd; /* CS on to ADV on delay */ + + /* XXX: check the possibility of combining + * cyc_aavhd_oe & cyc_aavdh_we + */ + u8 cyc_aavdh_oe;/* read address hold time in cycles */ + u8 cyc_aavdh_we;/* write address hold time in cycles */ + u8 cyc_oe; /* access time from OE assertion in cycles */ + u8 cyc_wpl; /* write deassertion time in cycles */ + u32 cyc_iaa; /* initial access time in cycles */ + + /* extra delays */ + bool ce_xdelay; + bool avd_xdelay; + bool oe_xdelay; + bool we_xdelay; +}; + +#define GPMC_BURST_4 4 /* 4 word burst */ +#define GPMC_BURST_8 8 /* 8 word burst */ +#define GPMC_BURST_16 16 /* 16 word burst */ +#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ +#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ +#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ +#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ + +struct gpmc_settings { + bool burst_wrap; /* enables wrap bursting */ + bool burst_read; /* enables read page/burst mode */ + bool burst_write; /* enables write page/burst mode */ + bool device_nand; /* device is NAND */ + bool sync_read; /* enables synchronous reads */ + bool sync_write; /* enables synchronous writes */ + bool wait_on_read; /* monitor wait on reads */ + bool wait_on_write; /* monitor wait on writes */ + u32 burst_len; /* page/burst length */ + u32 device_width; /* device bus width (8 or 16 bit) */ + u32 mux_add_data; /* multiplex address & data */ + u32 wait_pin; /* wait-pin to be used */ +}; + /* Data for each chip select */ struct gpmc_omap_cs_data { bool valid; /* data is valid */ bool is_nand; /* device within this CS is NAND */ + struct gpmc_settings *settings; + struct gpmc_device_timings *device_timings; + struct gpmc_timings *gpmc_timings; struct platform_device *pdev; /* device within this CS region */ unsigned int pdata_size; }; -- cgit From f47fcad63f6847ea677c6c7030f30fd6438e0052 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 13:58:01 +0300 Subject: memory: omap-gpmc: Introduce GPMC to NAND interface The OMAP GPMC module has certain registers dedicated for NAND access and some NAND bits mixed with other GPMC functionality. For the NAND dedicated registers we have the struct gpmc_nand_regs. The NAND driver needs to access NAND specific bits from the following non-dedicated registers - EMPTYWRITEBUFFERSTATUS from GPMC_STATUS For accessing these bits we introduce the struct gpmc_nand_ops. Add gpmc_omap_get_nand_ops() that returns the gpmc_nand_ops along with updating the gpmc_nand_regs. This API will be called by the OMAP NAND driver to access the necessary bits in GPMC register space. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 21 +++++++++++++++++++++ include/linux/omap-gpmc.h | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 21825ddce4a3..0b62afd86f7e 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1118,6 +1118,27 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) } } +static struct gpmc_nand_ops nand_ops; + +/** + * gpmc_omap_get_nand_ops - Get the GPMC NAND interface + * @regs: the GPMC NAND register map exclusive for NAND use. + * @cs: GPMC chip select number on which the NAND sits. The + * register map returned will be specific to this chip select. + * + * Returns NULL on error e.g. invalid cs. + */ +struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs) +{ + if (cs >= gpmc_cs_num) + return NULL; + + gpmc_update_nand_reg(reg, cs); + + return &nand_ops; +} +EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops); + int gpmc_get_client_irq(unsigned irq_config) { int i; diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index 2dcef1c8c8d4..dc2ada6fb9b4 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -14,14 +14,45 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 +/** + * gpmc_nand_ops - Interface between NAND and GPMC + * @nand_write_buffer_empty: get the NAND write buffer empty status. + */ +struct gpmc_nand_ops { + bool (*nand_writebuffer_empty)(void); +}; + +struct gpmc_nand_regs; + +#if IS_ENABLED(CONFIG_OMAP_GPMC) +struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, + int cs); +#else +static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, + int cs) +{ + return NULL; +} +#endif /* CONFIG_OMAP_GPMC */ + +/*--------------------------------*/ + +/* deprecated APIs */ +#if IS_ENABLED(CONFIG_OMAP_GPMC) +void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); +#else +static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) +{ +} +#endif /* CONFIG_OMAP_GPMC */ +/*--------------------------------*/ + extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, struct gpmc_settings *gpmc_s, struct gpmc_device_timings *dev_t); -struct gpmc_nand_regs; struct device_node; -extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); extern int gpmc_get_client_irq(unsigned irq_config); extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); -- cgit From 384258f252727c67772bbd48dad3185a30ba50d3 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 30 Jul 2015 14:49:23 +0300 Subject: memory: omap-gpmc: Implement IRQ domain for NAND IRQs GPMC provides 2 interrupts for NAND use. i.e. fifoevent and termcount. Use IRQ domain for this. NAND device tree node can then get the necessary interrupts by using gpmc as the interrupt parent. Legacy boot uses gpmc_get_client_irq to get the NAND interrupts from the GPMC IRQ domain. Get rid of custom bitmasks and use IRQ domain for that as well. Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Tony Lindgren --- Documentation/devicetree/bindings/bus/ti-gpmc.txt | 8 + drivers/memory/omap-gpmc.c | 246 ++++++++++++---------- include/linux/omap-gpmc.h | 5 +- 3 files changed, 144 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt index 01683707060b..13f13786f992 100644 --- a/Documentation/devicetree/bindings/bus/ti-gpmc.txt +++ b/Documentation/devicetree/bindings/bus/ti-gpmc.txt @@ -32,6 +32,12 @@ Required properties: bootloader) are used for the physical address decoding. As this will change in the future, filling correct values here is a requirement. + - interrupt-controller: The GPMC driver implements and interrupt controller for + the NAND events "fifoevent" and "termcount". + The interrupt number mapping is as follows + 0 - NAND_fifoevent + 1 - NAND_termcount + - interrupt-cells: Must be set to 2 Timing properties for child nodes. All are optional and default to 0. @@ -130,6 +136,8 @@ Example for an AM33xx board: #address-cells = <2>; #size-cells = <1>; ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ + interrupt-controller; + #interrupt-cells = <2>; /* child nodes go here */ }; diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 90dfba5a8f55..e28d6bc2500a 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -127,7 +128,6 @@ #define GPMC_CONFIG_RDY_BSY 0x00000001 #define GPMC_CONFIG_DEV_SIZE 0x00000002 #define GPMC_CONFIG_DEV_TYPE 0x00000003 -#define GPMC_SET_IRQ_STATUS 0x00000004 #define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) #define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30) @@ -176,8 +176,6 @@ #define GPMC_CONFIG_WRITEPROTECT 0x00000010 #define WR_RD_PIN_MONITORING 0x00600000 -#define GPMC_ENABLE_IRQ 0x0000000d - /* ECC commands */ #define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */ #define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ @@ -201,11 +199,6 @@ struct gpmc_cs_data { struct resource mem; }; -struct gpmc_client_irq { - unsigned irq; - u32 bitmask; -}; - /* Structure to save gpmc cs context */ struct gpmc_cs_config { u32 config1; @@ -233,9 +226,13 @@ struct omap3_gpmc_regs { struct gpmc_cs_config cs_context[GPMC_CS_NUM]; }; -static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ]; -static struct irq_chip gpmc_irq_chip; -static int gpmc_irq_start; +struct gpmc_device { + struct device *dev; + int irq; + struct irq_chip irq_chip; +}; + +static struct irq_domain *gpmc_irq_domain; static struct resource gpmc_mem_root; static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM]; @@ -243,8 +240,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock); /* Define chip-selects as reserved by default until probe completes */ static unsigned int gpmc_cs_num = GPMC_CS_NUM; static unsigned int gpmc_nr_waitpins; -static struct device *gpmc_dev; -static int gpmc_irq; static resource_size_t phys_base, mem_size; static unsigned gpmc_capability; static void __iomem *gpmc_base; @@ -1056,14 +1051,6 @@ int gpmc_configure(int cmd, int wval) u32 regval; switch (cmd) { - case GPMC_ENABLE_IRQ: - gpmc_write_reg(GPMC_IRQENABLE, wval); - break; - - case GPMC_SET_IRQ_STATUS: - gpmc_write_reg(GPMC_IRQSTATUS, wval); - break; - case GPMC_CONFIG_WP: regval = gpmc_read_reg(GPMC_CONFIG); if (wval) @@ -1153,85 +1140,97 @@ EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops); int gpmc_get_client_irq(unsigned irq_config) { - int i; - - if (hweight32(irq_config) > 1) + if (!gpmc_irq_domain) { + pr_warn("%s called before GPMC IRQ domain available\n", + __func__); return 0; + } - for (i = 0; i < GPMC_NR_IRQ; i++) - if (gpmc_client_irq[i].bitmask & irq_config) - return gpmc_client_irq[i].irq; + if (irq_config >= GPMC_NR_IRQ) + return 0; - return 0; + return irq_create_mapping(gpmc_irq_domain, irq_config); } -static int gpmc_irq_endis(unsigned irq, bool endis) +static int gpmc_irq_endis(unsigned long hwirq, bool endis) { - int i; u32 regval; - for (i = 0; i < GPMC_NR_IRQ; i++) - if (irq == gpmc_client_irq[i].irq) { - regval = gpmc_read_reg(GPMC_IRQENABLE); - if (endis) - regval |= gpmc_client_irq[i].bitmask; - else - regval &= ~gpmc_client_irq[i].bitmask; - gpmc_write_reg(GPMC_IRQENABLE, regval); - break; - } + regval = gpmc_read_reg(GPMC_IRQENABLE); + if (endis) + regval |= BIT(hwirq); + else + regval &= ~BIT(hwirq); + gpmc_write_reg(GPMC_IRQENABLE, regval); return 0; } static void gpmc_irq_disable(struct irq_data *p) { - gpmc_irq_endis(p->irq, false); + gpmc_irq_endis(p->hwirq, false); } static void gpmc_irq_enable(struct irq_data *p) { - gpmc_irq_endis(p->irq, true); + gpmc_irq_endis(p->hwirq, true); } static void gpmc_irq_noop(struct irq_data *data) { } static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } -static int gpmc_setup_irq(void) +static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) { - int i; + struct gpmc_device *gpmc = d->host_data; + + irq_set_chip_data(virq, gpmc); + irq_set_chip_and_handler(virq, &gpmc->irq_chip, handle_simple_irq); + irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN); + + return 0; +} + +static const struct irq_domain_ops gpmc_irq_domain_ops = { + .map = gpmc_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static irqreturn_t gpmc_handle_irq(int irq, void *data) +{ + int hwirq, virq; u32 regval; + struct gpmc_device *gpmc = data; - if (!gpmc_irq) - return -EINVAL; + regval = gpmc_read_reg(GPMC_IRQSTATUS); - gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0); - if (gpmc_irq_start < 0) { - pr_err("irq_alloc_descs failed\n"); - return gpmc_irq_start; - } + if (!regval) + return IRQ_NONE; - gpmc_irq_chip.name = "gpmc"; - gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret; - gpmc_irq_chip.irq_enable = gpmc_irq_enable; - gpmc_irq_chip.irq_disable = gpmc_irq_disable; - gpmc_irq_chip.irq_shutdown = gpmc_irq_noop; - gpmc_irq_chip.irq_ack = gpmc_irq_noop; - gpmc_irq_chip.irq_mask = gpmc_irq_noop; - gpmc_irq_chip.irq_unmask = gpmc_irq_noop; - - gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE; - gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT; - - for (i = 0; i < GPMC_NR_IRQ; i++) { - gpmc_client_irq[i].irq = gpmc_irq_start + i; - irq_set_chip_and_handler(gpmc_client_irq[i].irq, - &gpmc_irq_chip, handle_simple_irq); - irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST, - IRQ_NOAUTOEN); + for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) { + if (regval & BIT(hwirq)) { + virq = irq_find_mapping(gpmc_irq_domain, hwirq); + if (!virq) { + dev_warn(gpmc->dev, + "spurious irq detected hwirq %d, virq %d\n", + hwirq, virq); + } + + generic_handle_irq(virq); + } } + gpmc_write_reg(GPMC_IRQSTATUS, regval); + + return IRQ_HANDLED; +} + +static int gpmc_setup_irq(struct gpmc_device *gpmc) +{ + u32 regval; + int rc; + /* Disable interrupts */ gpmc_write_reg(GPMC_IRQENABLE, 0); @@ -1239,22 +1238,46 @@ static int gpmc_setup_irq(void) regval = gpmc_read_reg(GPMC_IRQSTATUS); gpmc_write_reg(GPMC_IRQSTATUS, regval); - return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL); + gpmc->irq_chip.name = "gpmc"; + gpmc->irq_chip.irq_startup = gpmc_irq_noop_ret; + gpmc->irq_chip.irq_enable = gpmc_irq_enable; + gpmc->irq_chip.irq_disable = gpmc_irq_disable; + gpmc->irq_chip.irq_shutdown = gpmc_irq_noop; + gpmc->irq_chip.irq_ack = gpmc_irq_noop; + gpmc->irq_chip.irq_mask = gpmc_irq_noop; + gpmc->irq_chip.irq_unmask = gpmc_irq_noop; + + gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node, + GPMC_NR_IRQ, + &gpmc_irq_domain_ops, + gpmc); + if (!gpmc_irq_domain) { + dev_err(gpmc->dev, "IRQ domain add failed\n"); + return -ENODEV; + } + + rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc); + if (rc) { + dev_err(gpmc->dev, "failed to request irq %d: %d\n", + gpmc->irq, rc); + irq_domain_remove(gpmc_irq_domain); + gpmc_irq_domain = NULL; + } + + return rc; } -static int gpmc_free_irq(void) +static int gpmc_free_irq(struct gpmc_device *gpmc) { - int i; + int hwirq; - if (gpmc_irq) - free_irq(gpmc_irq, NULL); + free_irq(gpmc->irq, gpmc); - for (i = 0; i < GPMC_NR_IRQ; i++) { - irq_set_handler(gpmc_client_irq[i].irq, NULL); - irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip); - } + for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) + irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq)); - irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ); + irq_domain_remove(gpmc_irq_domain); + gpmc_irq_domain = NULL; return 0; } @@ -2154,6 +2177,14 @@ static int gpmc_probe(struct platform_device *pdev) int rc; u32 l; struct resource *res; + struct gpmc_device *gpmc; + + gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL); + if (!gpmc) + return -ENOMEM; + + gpmc->dev = &pdev->dev; + platform_set_drvdata(pdev, gpmc); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) @@ -2167,15 +2198,16 @@ static int gpmc_probe(struct platform_device *pdev) return PTR_ERR(gpmc_base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) - dev_warn(&pdev->dev, "Failed to get resource: irq\n"); - else - gpmc_irq = res->start; + if (!res) { + dev_err(&pdev->dev, "Failed to get resource: irq\n"); + return -ENOENT; + } + + gpmc->irq = res->start; gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(gpmc_l3_clk)) { dev_err(&pdev->dev, "Failed to get GPMC fck\n"); - gpmc_irq = 0; return PTR_ERR(gpmc_l3_clk); } @@ -2187,8 +2219,6 @@ static int gpmc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - gpmc_dev = &pdev->dev; - l = gpmc_read_reg(GPMC_REVISION); /* @@ -2207,13 +2237,16 @@ static int gpmc_probe(struct platform_device *pdev) gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS; if (GPMC_REVISION_MAJOR(l) > 0x5) gpmc_capability |= GPMC_HAS_MUX_AAD; - dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), + dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), GPMC_REVISION_MINOR(l)); gpmc_mem_init(); - if (gpmc_setup_irq() < 0) - dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); + rc = gpmc_setup_irq(gpmc); + if (rc) { + dev_err(gpmc->dev, "gpmc_setup_irq failed\n"); + goto fail; + } if (!pdev->dev.of_node) { gpmc_cs_num = GPMC_CS_NUM; @@ -2222,21 +2255,27 @@ static int gpmc_probe(struct platform_device *pdev) rc = gpmc_probe_dt(pdev); if (rc < 0) { - pm_runtime_put_sync(&pdev->dev); - dev_err(gpmc_dev, "failed to probe DT parameters\n"); - return rc; + dev_err(gpmc->dev, "failed to probe DT parameters\n"); + gpmc_free_irq(gpmc); + goto fail; } return 0; + +fail: + pm_runtime_put_sync(&pdev->dev); + return rc; } static int gpmc_remove(struct platform_device *pdev) { - gpmc_free_irq(); + struct gpmc_device *gpmc = platform_get_drvdata(pdev); + + gpmc_free_irq(gpmc); gpmc_mem_exit(); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - gpmc_dev = NULL; + return 0; } @@ -2282,25 +2321,6 @@ static __exit void gpmc_exit(void) postcore_initcall(gpmc_init); module_exit(gpmc_exit); -static irqreturn_t gpmc_handle_irq(int irq, void *dev) -{ - int i; - u32 regval; - - regval = gpmc_read_reg(GPMC_IRQSTATUS); - - if (!regval) - return IRQ_NONE; - - for (i = 0; i < GPMC_NR_IRQ; i++) - if (regval & gpmc_client_irq[i].bitmask) - generic_handle_irq(gpmc_client_irq[i].irq); - - gpmc_write_reg(GPMC_IRQSTATUS, regval); - - return IRQ_HANDLED; -} - static struct omap3_gpmc_regs gpmc_context; void omap3_gpmc_save_context(void) diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index dc2ada6fb9b4..9e9d79e8efa5 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -11,8 +11,9 @@ #define GPMC_CONFIG_WP 0x00000005 -#define GPMC_IRQ_FIFOEVENTENABLE 0x01 -#define GPMC_IRQ_COUNT_EVENT 0x02 +/* IRQ numbers in GPMC IRQ domain for legacy boot use */ +#define GPMC_IRQ_FIFOEVENTENABLE 0 +#define GPMC_IRQ_COUNT_EVENT 1 /** * gpmc_nand_ops - Interface between NAND and GPMC -- cgit From c509aefd75d026f4ef4aa306131d7a780c2eda7b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 14:01:50 +0300 Subject: mtd: nand: omap: Use gpmc_omap_get_nand_ops() to get NAND registers Deprecate nand register passing via platform data and use gpmc_omap_get_nand_ops() instead. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-nand.c | 2 -- drivers/mtd/nand/omap2.c | 9 ++++++++- include/linux/platform_data/mtd-nand-omap2.h | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index 72918c4973ea..04e6998c1529 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -121,8 +121,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, if (err < 0) goto out_free_cs; - gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs); - if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) { pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n"); err = -EINVAL; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 0749ca1a1456..cba9bf0adba1 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -28,6 +28,7 @@ #include #include +#include #include #define DRIVER_NAME "omap2-nand" @@ -168,7 +169,9 @@ struct omap_nand_info { } iomode; u_char *buf; int buf_len; + /* Interface to GPMC */ struct gpmc_nand_regs reg; + struct gpmc_nand_ops *ops; /* generated at runtime depending on ECC algorithm and layout selected */ struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ @@ -1665,9 +1668,13 @@ static int omap_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); + info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); + if (!info->ops) { + dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); + return -ENODEV; + } info->pdev = pdev; info->gpmc_cs = pdata->cs; - info->reg = pdata->reg; info->of_node = pdata->of_node; info->ecc_opt = pdata->ecc_opt; nand_chip = &info->nand; diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 090bbab0130a..a067f581e938 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -75,10 +75,12 @@ struct omap_nand_platform_data { enum nand_io xfer_type; int devsize; enum omap_ecc ecc_opt; - struct gpmc_nand_regs reg; /* for passing the partitions */ struct device_node *of_node; struct device_node *elm_of_node; + + /* deprecated */ + struct gpmc_nand_regs reg; }; #endif -- cgit From c9711ec5250b22fd94e9b34c17c095e001a90e66 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 21 May 2014 07:29:03 +0300 Subject: mtd: nand: omap: Clean up device tree support Move NAND specific device tree parsing to NAND driver. The NAND controller node must have a compatible id, register space resource and interrupt resource. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-nand.c | 5 +- drivers/memory/omap-gpmc.c | 143 +++++++-------------------- drivers/mtd/nand/omap2.c | 134 +++++++++++++++++++++---- include/linux/platform_data/mtd-nand-omap2.h | 3 +- 4 files changed, 153 insertions(+), 132 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index 04e6998c1529..f6ac027f3c3b 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -97,10 +97,7 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); memset(&s, 0, sizeof(struct gpmc_settings)); - if (gpmc_nand_data->of_node) - gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); - else - gpmc_set_legacy(gpmc_nand_data, &s); + gpmc_set_legacy(gpmc_nand_data, &s); s.device_nand = true; diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index e28d6bc2500a..8dc6e3b1c44a 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -1852,105 +1851,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, of_property_read_bool(np, "gpmc,time-para-granularity"); } -#if IS_ENABLED(CONFIG_MTD_NAND) - -static const char * const nand_xfer_types[] = { - [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", - [NAND_OMAP_POLLED] = "polled", - [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", - [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", -}; - -static int gpmc_probe_nand_child(struct platform_device *pdev, - struct device_node *child) -{ - u32 val; - const char *s; - struct gpmc_timings gpmc_t; - struct omap_nand_platform_data *gpmc_nand_data; - - if (of_property_read_u32(child, "reg", &val) < 0) { - dev_err(&pdev->dev, "%s has no 'reg' property\n", - child->full_name); - return -ENODEV; - } - - gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data), - GFP_KERNEL); - if (!gpmc_nand_data) - return -ENOMEM; - - gpmc_nand_data->cs = val; - gpmc_nand_data->of_node = child; - - /* Detect availability of ELM module */ - gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); - if (gpmc_nand_data->elm_of_node == NULL) - gpmc_nand_data->elm_of_node = - of_parse_phandle(child, "elm_id", 0); - - /* select ecc-scheme for NAND */ - if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { - pr_err("%s: ti,nand-ecc-opt not found\n", __func__); - return -ENODEV; - } - - if (!strcmp(s, "sw")) - gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW; - else if (!strcmp(s, "ham1") || - !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) - gpmc_nand_data->ecc_opt = - OMAP_ECC_HAM1_CODE_HW; - else if (!strcmp(s, "bch4")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH4_CODE_HW; - else - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; - else if (!strcmp(s, "bch8")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH8_CODE_HW; - else - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; - else if (!strcmp(s, "bch16")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH16_CODE_HW; - else - pr_err("%s: BCH16 requires ELM support\n", __func__); - else - pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__); - - /* select data transfer mode for NAND controller */ - if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) - for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++) - if (!strcasecmp(s, nand_xfer_types[val])) { - gpmc_nand_data->xfer_type = val; - break; - } - - gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child); - - val = of_get_nand_bus_width(child); - if (val == 16) - gpmc_nand_data->devsize = NAND_BUSWIDTH_16; - - gpmc_read_timings_dt(child, &gpmc_t); - gpmc_nand_init(gpmc_nand_data, &gpmc_t); - - return 0; -} -#else -static int gpmc_probe_nand_child(struct platform_device *pdev, - struct device_node *child) -{ - return 0; -} -#endif - #if IS_ENABLED(CONFIG_MTD_ONENAND) static int gpmc_probe_onenand_child(struct platform_device *pdev, struct device_node *child) @@ -2069,9 +1969,42 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, goto err; } - ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); - if (ret < 0) - goto err; + if (of_node_cmp(child->name, "nand") == 0) { + /* Warn about older DT blobs with no compatible property */ + if (!of_property_read_bool(child, "compatible")) { + dev_warn(&pdev->dev, + "Incompatible NAND node: missing compatible"); + ret = -EINVAL; + goto err; + } + } + + if (of_device_is_compatible(child, "ti,omap2-nand")) { + /* NAND specific setup */ + val = of_get_nand_bus_width(child); + switch (val) { + case 8: + gpmc_s.device_width = GPMC_DEVWIDTH_8BIT; + break; + case 16: + gpmc_s.device_width = GPMC_DEVWIDTH_16BIT; + break; + default: + dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n", + child->name); + ret = -EINVAL; + goto err; + } + + /* disable write protect */ + gpmc_configure(GPMC_CONFIG_WP, 0); + gpmc_s.device_nand = true; + } else { + ret = of_property_read_u32(child, "bank-width", + &gpmc_s.device_width); + if (ret < 0) + goto err; + } gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); ret = gpmc_cs_program_settings(cs, &gpmc_s); @@ -2155,9 +2088,7 @@ static int gpmc_probe_dt(struct platform_device *pdev) if (!child->name) continue; - if (of_node_cmp(child->name, "nand") == 0) - ret = gpmc_probe_nand_child(pdev, child); - else if (of_node_cmp(child->name, "onenand") == 0) + if (of_node_cmp(child->name, "onenand") == 0) ret = gpmc_probe_onenand_child(pdev, child); else ret = gpmc_probe_generic_child(pdev, child); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7e4e263c7d9c..35b8f3359c17 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -176,11 +177,11 @@ struct omap_nand_info { /* Interface to GPMC */ struct gpmc_nand_regs reg; struct gpmc_nand_ops *ops; + bool flash_bbt; /* generated at runtime depending on ECC algorithm and layout selected */ struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ struct device *elm_dev; - struct device_node *of_node; }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -1643,10 +1644,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, return true; } +static const char * const nand_xfer_types[] = { + [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", + [NAND_OMAP_POLLED] = "polled", + [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", + [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", +}; + +static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) +{ + struct device_node *child = dev->of_node; + int i; + const char *s; + u32 cs; + + if (of_property_read_u32(child, "reg", &cs) < 0) { + dev_err(dev, "reg not found in DT\n"); + return -EINVAL; + } + + info->gpmc_cs = cs; + + /* detect availability of ELM module. Won't be present pre-OMAP4 */ + info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); + if (!info->elm_of_node) + dev_dbg(dev, "ti,elm-id not in DT\n"); + + /* select ecc-scheme for NAND */ + if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { + dev_err(dev, "ti,nand-ecc-opt not found\n"); + return -EINVAL; + } + + if (!strcmp(s, "sw")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; + } else if (!strcmp(s, "ham1") || + !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; + } else if (!strcmp(s, "bch4")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch8")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch16")) { + info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; + } else { + dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); + return -EINVAL; + } + + /* select data transfer mode */ + if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { + for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { + if (!strcasecmp(s, nand_xfer_types[i])) { + info->xfer_type = i; + goto next; + } + } + + dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); + return -EINVAL; + } + +next: + of_get_nand_on_flash_bbt(child); + + if (of_get_nand_bus_width(child) == 16) + info->devsize = NAND_BUSWIDTH_16; + + return 0; +} + static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; - struct omap_nand_platform_data *pdata; + struct omap_nand_platform_data *pdata = NULL; struct mtd_info *mtd; struct nand_chip *nand_chip; struct nand_ecclayout *ecclayout; @@ -1656,39 +1733,47 @@ static int omap_nand_probe(struct platform_device *pdev) unsigned sig; unsigned oob_index; struct resource *res; - - pdata = dev_get_platdata(&pdev->dev); - if (pdata == NULL) { - dev_err(&pdev->dev, "platform data missing\n"); - return -ENODEV; - } + struct device *dev = &pdev->dev; info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), GFP_KERNEL); if (!info) return -ENOMEM; - platform_set_drvdata(pdev, info); + info->pdev = pdev; + if (dev->of_node) { + if (omap_get_dt_info(dev, info)) + return -EINVAL; + } else { + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "platform data missing\n"); + return -EINVAL; + } + + info->gpmc_cs = pdata->cs; + info->reg = pdata->reg; + info->ecc_opt = pdata->ecc_opt; + info->dev_ready = pdata->dev_ready; + info->xfer_type = pdata->xfer_type; + info->devsize = pdata->devsize; + info->elm_of_node = pdata->elm_of_node; + info->flash_bbt = pdata->flash_bbt; + } + + platform_set_drvdata(pdev, info); info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); if (!info->ops) { dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); return -ENODEV; } - info->pdev = pdev; - info->gpmc_cs = pdata->cs; - info->of_node = pdata->of_node; - info->ecc_opt = pdata->ecc_opt; - info->dev_ready = pdata->dev_ready; - info->xfer_type = pdata->xfer_type; - info->devsize = pdata->devsize; - info->elm_of_node = pdata->elm_of_node; nand_chip = &info->nand; mtd = nand_to_mtd(nand_chip); mtd->dev.parent = &pdev->dev; nand_chip->ecc.priv = NULL; - nand_set_flash_node(nand_chip, pdata->of_node); + nand_set_flash_node(nand_chip, dev->of_node); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); @@ -1717,7 +1802,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->chip_delay = 50; } - if (pdata->flash_bbt) + if (info->flash_bbt) nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; else nand_chip->options |= NAND_SKIP_BBTSCAN; @@ -2035,7 +2120,10 @@ scan_tail: goto return_error; } - mtd_device_register(mtd, pdata->parts, pdata->nr_parts); + if (dev->of_node) + mtd_device_register(mtd, NULL, 0); + else + mtd_device_register(mtd, pdata->parts, pdata->nr_parts); platform_set_drvdata(pdev, mtd); @@ -2066,11 +2154,17 @@ static int omap_nand_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id omap_nand_ids[] = { + { .compatible = "ti,omap2-nand", }, + {}, +}; + static struct platform_driver omap_nand_driver = { .probe = omap_nand_probe, .remove = omap_nand_remove, .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(omap_nand_ids), }, }; diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index a067f581e938..ff27e5a77e03 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -76,11 +76,10 @@ struct omap_nand_platform_data { int devsize; enum omap_ecc ecc_opt; - /* for passing the partitions */ - struct device_node *of_node; struct device_node *elm_of_node; /* deprecated */ struct gpmc_nand_regs reg; + struct device_node *of_node; }; #endif -- cgit From 9e6946215dbd9803e8b511928c9f61f3a49e2c58 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 7 Aug 2015 10:38:13 +0300 Subject: memory: omap-gpmc: Prevent GPMC_STATUS from being accessed via gpmc_regs GPMC_STATUS register is private to the GPMC module and must not be accessed directly by NAND driver through the gpmc_regs. They must use gpmc_omap_get_nand_ops() instead. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 2 +- include/linux/platform_data/mtd-nand-omap2.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index ea9c89747950..33d69b1e4c31 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1081,7 +1081,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) { int i; - reg->gpmc_status = gpmc_base + GPMC_STATUS; + reg->gpmc_status = NULL; /* deprecated */ reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index ff27e5a77e03..7f6de5377f80 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -45,7 +45,6 @@ enum omap_ecc { }; struct gpmc_nand_regs { - void __iomem *gpmc_status; void __iomem *gpmc_nand_command; void __iomem *gpmc_nand_address; void __iomem *gpmc_nand_data; @@ -64,6 +63,8 @@ struct gpmc_nand_regs { void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER]; + /* Deprecated. Do not use */ + void __iomem *gpmc_status; }; struct omap_nand_platform_data { -- cgit From 10f22ee367c4aff7841da6a83c10445d7d6328d9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 6 Aug 2015 17:39:35 +0300 Subject: mtd: nand: omap2: Implement NAND ready using gpiolib The GPMC WAIT pin status are now available over gpiolib. Update the omap_dev_ready() function to use gpio instead of directly accessing GPMC register space. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Boris Brezillon Acked-by: Tony Lindgren --- .../devicetree/bindings/mtd/gpmc-nand.txt | 2 ++ drivers/mtd/nand/omap2.c | 29 ++++++++++++++-------- include/linux/platform_data/mtd-nand-omap2.h | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt index ff3215d20343..3ee7e202657c 100644 --- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt @@ -48,6 +48,7 @@ Optional properties: locating ECC errors for BCHx algorithms. SoC devices which have ELM hardware engines should specify this device node in .dtsi Using ELM for ECC error correction frees some CPU cycles. + - rb-gpios: GPIO specifier for the ready/busy# pin. For inline partition table parsing (optional): @@ -78,6 +79,7 @@ Example for an AM33xx board: nand-bus-width = <16>; ti,nand-ecc-opt = "bch8"; ti,nand-xfer-type = "polled"; + rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */ gpmc,sync-clk-ps = <0>; gpmc,cs-on-ns = <0>; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 35b8f3359c17..e0b2b2f0fbde 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,8 @@ struct omap_nand_info { struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ struct device *elm_dev; + /* NAND ready gpio */ + struct gpio_desc *ready_gpiod; }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -1023,21 +1026,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** - * omap_dev_ready - calls the platform specific dev_ready function + * omap_dev_ready - checks the NAND Ready GPIO line * @mtd: MTD device structure + * + * Returns true if ready and false if busy. */ static int omap_dev_ready(struct mtd_info *mtd) { - unsigned int val = 0; struct omap_nand_info *info = mtd_to_omap(mtd); - val = readl(info->reg.gpmc_status); - - if ((val & 0x100) == 0x100) { - return 1; - } else { - return 0; - } + return gpiod_get_value(info->ready_gpiod); } /** @@ -1755,7 +1753,9 @@ static int omap_nand_probe(struct platform_device *pdev) info->gpmc_cs = pdata->cs; info->reg = pdata->reg; info->ecc_opt = pdata->ecc_opt; - info->dev_ready = pdata->dev_ready; + if (pdata->dev_ready) + dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n"); + info->xfer_type = pdata->xfer_type; info->devsize = pdata->devsize; info->elm_of_node = pdata->elm_of_node; @@ -1787,6 +1787,13 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; nand_chip->cmd_ctrl = omap_hwcontrol; + info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", + GPIOD_IN); + if (IS_ERR(info->ready_gpiod)) { + dev_err(dev, "failed to get ready gpio\n"); + return PTR_ERR(info->ready_gpiod); + } + /* * If RDY/BSY line is connected to OMAP then use the omap ready * function and the generic nand_wait function which reads the status @@ -1794,7 +1801,7 @@ static int omap_nand_probe(struct platform_device *pdev) * chip delay which is slightly more than tR (AC Timing) of the NAND * device and read status register until you get a failure or success */ - if (info->dev_ready) { + if (info->ready_gpiod) { nand_chip->dev_ready = omap_dev_ready; nand_chip->chip_delay = 0; } else { diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 7f6de5377f80..17d57a18bac5 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -71,7 +71,6 @@ struct omap_nand_platform_data { int cs; struct mtd_partition *parts; int nr_parts; - bool dev_ready; bool flash_bbt; enum nand_io xfer_type; int devsize; @@ -82,5 +81,6 @@ struct omap_nand_platform_data { /* deprecated */ struct gpmc_nand_regs reg; struct device_node *of_node; + bool dev_ready; }; #endif -- cgit From 675f10bde6cc3874632a8f684df2a8a2a8ace76e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:29:18 +0800 Subject: f2fs: fix to convert inline directory correctly With below serials, we will lose parts of dirents: 1) mount f2fs with inline_dentry option 2) echo 1 > /sys/fs/f2fs/sdX/dir_level 3) mkdir dir 4) touch 180 files named [1-180] in dir 5) touch 181 in dir 6) echo 3 > /proc/sys/vm/drop_caches 7) ll dir ls: cannot access 2: No such file or directory ls: cannot access 4: No such file or directory ls: cannot access 5: No such file or directory ls: cannot access 6: No such file or directory ls: cannot access 8: No such file or directory ls: cannot access 9: No such file or directory ... total 360 drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./ drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../ -rw-r--r-- 1 root root 0 Feb 19 15:12 1 -rw-r--r-- 1 root root 0 Feb 19 15:12 10 -rw-r--r-- 1 root root 0 Feb 19 15:12 100 -????????? ? ? ? ? ? 101 -????????? ? ? ? ? ? 102 -????????? ? ? ? ? ? 103 ... The reason is: when doing the inline dir conversion, we didn't consider that directory has hierarchical hash structure which can be configured through sysfs interface 'dir_level'. By default, dir_level of directory inode is 0, it means we have one bucket in hash table located in first level, all dirents will be hashed in this bucket, so it has no problem for us to do the duplication simply between inline dentry page and converted normal dentry page. However, if we configured dir_level with the value N (greater than 0), it will expand the bucket number of first level hash table by 2^N - 1, it hashs dirents into different buckets according their hash value, if we still move all dirents to first bucket, it makes incorrent locating for inline dirents, the result is, although we can iterate all dirents through ->readdir, we can't stat some of them in ->lookup which based on hash table searching. This patch fixes this issue by rehashing dirents into correct position when converting inline directory. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 87 ++++++++++++++++++++++++--------------------- fs/f2fs/f2fs.h | 4 ++- fs/f2fs/inline.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/f2fs_fs.h | 2 ++ 4 files changed, 144 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index af819571bce7..e90380d82214 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -48,7 +48,6 @@ unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { [F2FS_FT_SYMLINK] = DT_LNK, }; -#define S_SHIFT 12 static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, @@ -64,6 +63,13 @@ void set_de_type(struct f2fs_dir_entry *de, umode_t mode) de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; } +unsigned char get_de_type(struct f2fs_dir_entry *de) +{ + if (de->file_type < F2FS_FT_MAX) + return f2fs_filetype_table[de->file_type]; + return DT_UNKNOWN; +} + static unsigned long dir_block_index(unsigned int level, int dir_level, unsigned int idx) { @@ -509,11 +515,7 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, } } -/* - * Caller should grab and release a rwsem by calling f2fs_lock_op() and - * f2fs_unlock_op(). - */ -int __f2fs_add_link(struct inode *dir, const struct qstr *name, +int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, struct inode *inode, nid_t ino, umode_t mode) { unsigned int bit_pos; @@ -526,28 +528,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_ptr d; struct page *page = NULL; - struct fscrypt_name fname; - struct qstr new_name; - int slots, err; - - err = fscrypt_setup_filename(dir, name, 0, &fname); - if (err) - return err; - - new_name.name = fname_name(&fname); - new_name.len = fname_len(&fname); - - if (f2fs_has_inline_dentry(dir)) { - err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); - if (!err || err != -EAGAIN) - goto out; - else - err = 0; - } + int slots, err = 0; level = 0; - slots = GET_DENTRY_SLOTS(new_name.len); - dentry_hash = f2fs_dentry_hash(&new_name); + slots = GET_DENTRY_SLOTS(new_name->len); + dentry_hash = f2fs_dentry_hash(new_name); current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { @@ -556,10 +541,8 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, } start: - if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) { - err = -ENOSPC; - goto out; - } + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) + return -ENOSPC; /* Increase the depth, if required */ if (level == current_depth) @@ -573,10 +556,8 @@ start: for (block = bidx; block <= (bidx + nblock - 1); block++) { dentry_page = get_new_data_page(dir, NULL, block, true); - if (IS_ERR(dentry_page)) { - err = PTR_ERR(dentry_page); - goto out; - } + if (IS_ERR(dentry_page)) + return PTR_ERR(dentry_page); dentry_blk = kmap(dentry_page); bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, @@ -596,7 +577,7 @@ add_dentry: if (inode) { down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, &new_name, NULL); + page = init_inode_metadata(inode, dir, new_name, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; @@ -606,7 +587,7 @@ add_dentry: } make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); - f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos); + f2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos); set_page_dirty(dentry_page); @@ -628,7 +609,34 @@ fail: } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); -out: + + return err; +} + +/* + * Caller should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op(). + */ +int __f2fs_add_link(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode) +{ + struct fscrypt_name fname; + struct qstr new_name; + int err; + + err = fscrypt_setup_filename(dir, name, 0, &fname); + if (err) + return err; + + new_name.name = fname_name(&fname); + new_name.len = fname_len(&fname); + + err = -EAGAIN; + if (f2fs_has_inline_dentry(dir)) + err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); + if (err == -EAGAIN) + err = f2fs_add_regular_entry(dir, &new_name, inode, ino, mode); + fscrypt_free_filename(&fname); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; @@ -792,10 +800,7 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, continue; } - if (de->file_type < F2FS_FT_MAX) - d_type = f2fs_filetype_table[de->file_type]; - else - d_type = DT_UNKNOWN; + d_type = get_de_type(de); de_name.name = d->filename[bit_pos]; de_name.len = le16_to_cpu(de->name_len); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e1c07b60f301..3f1551395244 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1711,7 +1711,7 @@ struct dentry *f2fs_get_parent(struct dentry *child); */ extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; void set_de_type(struct f2fs_dir_entry *, umode_t); - +unsigned char get_de_type(struct f2fs_dir_entry *); struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, f2fs_hash_t, int *, struct f2fs_dentry_ptr *); bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, @@ -1732,6 +1732,8 @@ void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, int update_dent_inode(struct inode *, struct inode *, const struct qstr *); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, const struct qstr *, f2fs_hash_t , unsigned int); +int f2fs_add_regular_entry(struct inode *, const struct qstr *, + struct inode *, nid_t, umode_t); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, umode_t); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index a2fbe6f427d3..772056587eb9 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -355,7 +355,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, * NOTE: ipage is grabbed by caller, but if any error occurs, we should * release ipage in this function. */ -static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, +static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { struct page *page; @@ -416,6 +416,98 @@ out: return err; } +static int f2fs_add_inline_entries(struct inode *dir, + struct f2fs_inline_dentry *inline_dentry) +{ + struct f2fs_dentry_ptr d; + unsigned long bit_pos = 0; + int err = 0; + + make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + + while (bit_pos < d.max) { + struct f2fs_dir_entry *de; + struct qstr new_name; + nid_t ino; + umode_t fake_mode; + + if (!test_bit_le(bit_pos, d.bitmap)) { + bit_pos++; + continue; + } + + de = &d.dentry[bit_pos]; + new_name.name = d.filename[bit_pos]; + new_name.len = de->name_len; + + ino = le32_to_cpu(de->ino); + fake_mode = get_de_type(de) << S_SHIFT; + + err = f2fs_add_regular_entry(dir, &new_name, NULL, + ino, fake_mode); + if (err) + goto punch_dentry_pages; + + if (unlikely(!de->name_len)) + d.max = -1; + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + return 0; +punch_dentry_pages: + truncate_inode_pages(&dir->i_data, 0); + truncate_blocks(dir, 0, false); + remove_dirty_inode(dir); + return err; +} + +static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + struct f2fs_inline_dentry *backup_dentry; + int err; + + backup_dentry = kmalloc(sizeof(struct f2fs_inline_dentry), + GFP_F2FS_ZERO); + if (!backup_dentry) + return -ENOMEM; + + memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); + truncate_inline_inode(ipage, 0); + + unlock_page(ipage); + + err = f2fs_add_inline_entries(dir, backup_dentry); + if (err) + goto recover; + + lock_page(ipage); + + stat_dec_inline_dir(dir); + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + update_inode(dir, ipage); + kfree(backup_dentry); + return 0; +recover: + lock_page(ipage); + memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); + i_size_write(dir, MAX_INLINE_DATA); + update_inode(dir, ipage); + f2fs_put_page(ipage, 1); + + kfree(backup_dentry); + return err; +} + +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + if (!F2FS_I(dir)->i_dir_level) + return f2fs_move_inline_dirents(dir, ipage, inline_dentry); + else + return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry); +} + int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, struct inode *inode, nid_t ino, umode_t mode) { diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index b90e9bdbd1dd..4c02c6521fef 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -508,4 +508,6 @@ enum { F2FS_FT_MAX }; +#define S_SHIFT 12 + #endif /* _LINUX_F2FS_FS_H */ -- cgit From 522566376a3f8373fbd5ff75bb8a7a2da701c1a7 Mon Sep 17 00:00:00 2001 From: Aviya Erenfeld Date: Thu, 14 Apr 2016 11:59:31 +0200 Subject: devcoredump: add scatterlist support Add scatterlist support (dev_coredumpsg) to allow drivers to avoid vmalloc() like dev_coredumpm(), while also avoiding the module reference that the latter function requires. This internally uses dev_coredumpm() with function inside the devcoredump module, requiring removing the const (which touches the driver using it.) Signed-off-by: Aviya Erenfeld Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcoredump.c | 83 +++++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 4 +- include/linux/devcoredump.h | 86 ++++++++++++++++++++++--- 3 files changed, 154 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 1bd120a0b084..240374fd1838 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -4,6 +4,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -41,12 +42,12 @@ static bool devcd_disabled; struct devcd_entry { struct device devcd_dev; - const void *data; + void *data; size_t datalen; struct module *owner; ssize_t (*read)(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen); - void (*free)(const void *data); + void *data, size_t datalen); + void (*free)(void *data); struct delayed_work del_wk; struct device *failing_dev; }; @@ -174,7 +175,7 @@ static struct class devcd_class = { }; static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen) + void *data, size_t datalen) { if (offset > datalen) return -EINVAL; @@ -188,6 +189,11 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, return count; } +static void devcd_freev(void *data) +{ + vfree(data); +} + /** * dev_coredumpv - create device coredump with vmalloc data * @dev: the struct device for the crashed device @@ -198,10 +204,10 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, * This function takes ownership of the vmalloc'ed data and will free * it when it is no longer used. See dev_coredumpm() for more information. */ -void dev_coredumpv(struct device *dev, const void *data, size_t datalen, +void dev_coredumpv(struct device *dev, void *data, size_t datalen, gfp_t gfp) { - dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree); + dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev); } EXPORT_SYMBOL_GPL(dev_coredumpv); @@ -212,6 +218,44 @@ static int devcd_match_failing(struct device *dev, const void *failing) return devcd->failing_dev == failing; } +/** + * devcd_free_sgtable - free all the memory of the given scatterlist table + * (i.e. both pages and scatterlist instances) + * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained + * using the sg_chain function then that function should be called only once + * on the chained table + * @table: pointer to sg_table to free + */ +static void devcd_free_sgtable(void *data) +{ + _devcd_free_sgtable(data); +} + +/** + * devcd_read_from_table - copy data from sg_table to a given buffer + * and return the number of bytes read + * @buffer: the buffer to copy the data to it + * @buf_len: the length of the buffer + * @data: the scatterlist table to copy from + * @offset: start copy from @offset@ bytes from the head of the data + * in the given scatterlist + * @data_len: the length of the data in the sg_table + */ +static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset, + size_t buf_len, void *data, + size_t data_len) +{ + struct scatterlist *table = data; + + if (offset > data_len) + return -EINVAL; + + if (offset + buf_len > data_len) + buf_len = data_len - offset; + return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len, + offset); +} + /** * dev_coredumpm - create device coredump with read/free methods * @dev: the struct device for the crashed device @@ -228,10 +272,10 @@ static int devcd_match_failing(struct device *dev, const void *failing) * function will be called to free the data. */ void dev_coredumpm(struct device *dev, struct module *owner, - const void *data, size_t datalen, gfp_t gfp, + void *data, size_t datalen, gfp_t gfp, ssize_t (*read)(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen), - void (*free)(const void *data)) + void *data, size_t datalen), + void (*free)(void *data)) { static atomic_t devcd_count = ATOMIC_INIT(0); struct devcd_entry *devcd; @@ -291,6 +335,27 @@ void dev_coredumpm(struct device *dev, struct module *owner, } EXPORT_SYMBOL_GPL(dev_coredumpm); +/** + * dev_coredumpmsg - create device coredump that uses scatterlist as data + * parameter + * @dev: the struct device for the crashed device + * @table: the dump data + * @datalen: length of the data + * @gfp: allocation flags + * + * Creates a new device coredump for the given device. If a previous one hasn't + * been read yet, the new coredump is discarded. The data lifetime is determined + * by the device coredump framework and when it is no longer needed + * it will free the data. + */ +void dev_coredumpsg(struct device *dev, struct scatterlist *table, + size_t datalen, gfp_t gfp) +{ + dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable, + devcd_free_sgtable); +} +EXPORT_SYMBOL_GPL(dev_coredumpsg); + static int __init devcoredump_init(void) { return class_register(&devcd_class); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 4856eac120f6..a4b0581d2275 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -71,7 +71,7 @@ #include "iwl-csr.h" static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen) + void *data, size_t datalen) { const struct iwl_mvm_dump_ptrs *dump_ptrs = data; ssize_t bytes_read; @@ -104,7 +104,7 @@ static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, return bytes_read + bytes_read_trans; } -static void iwl_mvm_free_coredump(const void *data) +static void iwl_mvm_free_coredump(void *data) { const struct iwl_mvm_dump_ptrs *fw_error_dump = data; diff --git a/include/linux/devcoredump.h b/include/linux/devcoredump.h index c0a360e99f64..269521f143ac 100644 --- a/include/linux/devcoredump.h +++ b/include/linux/devcoredump.h @@ -1,3 +1,22 @@ +/* + * This file is provided under the GPLv2 license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + */ #ifndef __DEVCOREDUMP_H #define __DEVCOREDUMP_H @@ -5,17 +24,62 @@ #include #include +#include +#include + +/* + * _devcd_free_sgtable - free all the memory of the given scatterlist table + * (i.e. both pages and scatterlist instances) + * NOTE: if two tables allocated and chained using the sg_chain function then + * this function should be called only once on the first table + * @table: pointer to sg_table to free + */ +static inline void _devcd_free_sgtable(struct scatterlist *table) +{ + int i; + struct page *page; + struct scatterlist *iter; + struct scatterlist *delete_iter; + + /* free pages */ + iter = table; + for_each_sg(table, iter, sg_nents(table), i) { + page = sg_page(iter); + if (page) + __free_page(page); + } + + /* then free all chained tables */ + iter = table; + delete_iter = table; /* always points on a head of a table */ + while (!sg_is_last(iter)) { + iter++; + if (sg_is_chain(iter)) { + iter = sg_chain_ptr(iter); + kfree(delete_iter); + delete_iter = iter; + } + } + + /* free the last table */ + kfree(delete_iter); +} + + #ifdef CONFIG_DEV_COREDUMP -void dev_coredumpv(struct device *dev, const void *data, size_t datalen, +void dev_coredumpv(struct device *dev, void *data, size_t datalen, gfp_t gfp); void dev_coredumpm(struct device *dev, struct module *owner, - const void *data, size_t datalen, gfp_t gfp, + void *data, size_t datalen, gfp_t gfp, ssize_t (*read)(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen), - void (*free)(const void *data)); + void *data, size_t datalen), + void (*free)(void *data)); + +void dev_coredumpsg(struct device *dev, struct scatterlist *table, + size_t datalen, gfp_t gfp); #else -static inline void dev_coredumpv(struct device *dev, const void *data, +static inline void dev_coredumpv(struct device *dev, void *data, size_t datalen, gfp_t gfp) { vfree(data); @@ -23,13 +87,19 @@ static inline void dev_coredumpv(struct device *dev, const void *data, static inline void dev_coredumpm(struct device *dev, struct module *owner, - const void *data, size_t datalen, gfp_t gfp, + void *data, size_t datalen, gfp_t gfp, ssize_t (*read)(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen), - void (*free)(const void *data)) + void *data, size_t datalen), + void (*free)(void *data)) { free(data); } + +static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table, + size_t datalen, gfp_t gfp) +{ + _devcd_free_sgtable(table); +} #endif /* CONFIG_DEV_COREDUMP */ #endif /* __DEVCOREDUMP_H */ -- cgit From 9b1d6c8950021ab007608d455fc9c398ecd25476 Mon Sep 17 00:00:00 2001 From: Ming Lin Date: Mon, 4 Apr 2016 14:48:11 -0700 Subject: lib: scatterlist: move SG pool code from SCSI driver to lib/sg_pool.c Now it's ready to move the mempool based SG chained allocator code from SCSI driver to lib/sg_pool.c, which will be compiled only based on a Kconfig symbol CONFIG_SG_POOL. SCSI selects CONFIG_SG_POOL. Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lin Reviewed-by: Sagi Grimberg Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 1 + drivers/scsi/scsi_lib.c | 137 ----------------------------------- include/linux/scatterlist.h | 25 +++++++ include/scsi/scsi.h | 19 ----- lib/Kconfig | 7 ++ lib/Makefile | 1 + lib/sg_pool.c | 172 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 206 insertions(+), 156 deletions(-) create mode 100644 lib/sg_pool.c (limited to 'include/linux') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 0950567e6269..98e5d51a3346 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -17,6 +17,7 @@ config SCSI tristate "SCSI device support" depends on BLOCK select SCSI_DMA if HAS_DMA + select SG_POOL ---help--- If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or any other SCSI device under Linux, say Y and make sure that you know diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 8f776f1e95ce..b920c5dabf60 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include @@ -40,39 +38,6 @@ #include "scsi_logging.h" -#define SG_MEMPOOL_NR ARRAY_SIZE(sg_pools) -#define SG_MEMPOOL_SIZE 2 - -struct sg_pool { - size_t size; - char *name; - struct kmem_cache *slab; - mempool_t *pool; -}; - -#define SP(x) { .size = x, "sgpool-" __stringify(x) } -#if (SG_CHUNK_SIZE < 32) -#error SG_CHUNK_SIZE is too small (must be 32 or greater) -#endif -static struct sg_pool sg_pools[] = { - SP(8), - SP(16), -#if (SG_CHUNK_SIZE > 32) - SP(32), -#if (SG_CHUNK_SIZE > 64) - SP(64), -#if (SG_CHUNK_SIZE > 128) - SP(128), -#if (SG_CHUNK_SIZE > 256) -#error SG_CHUNK_SIZE is too large (256 MAX) -#endif -#endif -#endif -#endif - SP(SG_CHUNK_SIZE) -}; -#undef SP - struct kmem_cache *scsi_sdb_cache; /* @@ -553,65 +518,6 @@ void scsi_run_host_queues(struct Scsi_Host *shost) scsi_run_queue(sdev->request_queue); } -static inline unsigned int sg_pool_index(unsigned short nents) -{ - unsigned int index; - - BUG_ON(nents > SG_CHUNK_SIZE); - - if (nents <= 8) - index = 0; - else - index = get_count_order(nents) - 3; - - return index; -} - -static void sg_pool_free(struct scatterlist *sgl, unsigned int nents) -{ - struct sg_pool *sgp; - - sgp = sg_pools + sg_pool_index(nents); - mempool_free(sgl, sgp->pool); -} - -static struct scatterlist *sg_pool_alloc(unsigned int nents, gfp_t gfp_mask) -{ - struct sg_pool *sgp; - - sgp = sg_pools + sg_pool_index(nents); - return mempool_alloc(sgp->pool, gfp_mask); -} - -static void sg_free_table_chained(struct sg_table *table, bool first_chunk) -{ - if (first_chunk && table->orig_nents <= SG_CHUNK_SIZE) - return; - __sg_free_table(table, SG_CHUNK_SIZE, first_chunk, sg_pool_free); -} - -static int sg_alloc_table_chained(struct sg_table *table, int nents, - struct scatterlist *first_chunk) -{ - int ret; - - BUG_ON(!nents); - - if (first_chunk) { - if (nents <= SG_CHUNK_SIZE) { - table->nents = table->orig_nents = nents; - sg_init_table(table->sgl, nents); - return 0; - } - } - - ret = __sg_alloc_table(table, nents, SG_CHUNK_SIZE, - first_chunk, GFP_ATOMIC, sg_pool_alloc); - if (unlikely(ret)) - sg_free_table_chained(table, (bool)first_chunk); - return ret; -} - static void scsi_uninit_cmd(struct scsi_cmnd *cmd) { if (cmd->request->cmd_type == REQ_TYPE_FS) { @@ -2269,8 +2175,6 @@ EXPORT_SYMBOL(scsi_unblock_requests); int __init scsi_init_queue(void) { - int i; - scsi_sdb_cache = kmem_cache_create("scsi_data_buffer", sizeof(struct scsi_data_buffer), 0, 0, NULL); @@ -2279,53 +2183,12 @@ int __init scsi_init_queue(void) return -ENOMEM; } - for (i = 0; i < SG_MEMPOOL_NR; i++) { - struct sg_pool *sgp = sg_pools + i; - int size = sgp->size * sizeof(struct scatterlist); - - sgp->slab = kmem_cache_create(sgp->name, size, 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!sgp->slab) { - printk(KERN_ERR "SCSI: can't init sg slab %s\n", - sgp->name); - goto cleanup_sdb; - } - - sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE, - sgp->slab); - if (!sgp->pool) { - printk(KERN_ERR "SCSI: can't init sg mempool %s\n", - sgp->name); - goto cleanup_sdb; - } - } - return 0; - -cleanup_sdb: - for (i = 0; i < SG_MEMPOOL_NR; i++) { - struct sg_pool *sgp = sg_pools + i; - if (sgp->pool) - mempool_destroy(sgp->pool); - if (sgp->slab) - kmem_cache_destroy(sgp->slab); - } - kmem_cache_destroy(scsi_sdb_cache); - - return -ENOMEM; } void scsi_exit_queue(void) { - int i; - kmem_cache_destroy(scsi_sdb_cache); - - for (i = 0; i < SG_MEMPOOL_NR; i++) { - struct sg_pool *sgp = sg_pools + i; - mempool_destroy(sgp->pool); - kmem_cache_destroy(sgp->slab); - } } /** diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 556ec1ea2574..cb3c8fe6acd7 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -285,6 +285,31 @@ size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, */ #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) +/* + * The maximum number of SG segments that we will put inside a + * scatterlist (unless chaining is used). Should ideally fit inside a + * single page, to avoid a higher order allocation. We could define this + * to SG_MAX_SINGLE_ALLOC to pack correctly at the highest order. The + * minimum value is 32 + */ +#define SG_CHUNK_SIZE 128 + +/* + * Like SG_CHUNK_SIZE, but for archs that have sg chaining. This limit + * is totally arbitrary, a setting of 2048 will get you at least 8mb ios. + */ +#ifdef CONFIG_ARCH_HAS_SG_CHAIN +#define SG_MAX_SEGMENTS 2048 +#else +#define SG_MAX_SEGMENTS SG_CHUNK_SIZE +#endif + +#ifdef CONFIG_SG_POOL +void sg_free_table_chained(struct sg_table *table, bool first_chunk); +int sg_alloc_table_chained(struct sg_table *table, int nents, + struct scatterlist *first_chunk); +#endif + /* * sg page iterator * diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 74dafa75bae7..8ec7c30e35af 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -17,25 +17,6 @@ enum scsi_timeouts { SCSI_DEFAULT_EH_TIMEOUT = 10 * HZ, }; -/* - * The maximum number of SG segments that we will put inside a - * scatterlist (unless chaining is used). Should ideally fit inside a - * single page, to avoid a higher order allocation. We could define this - * to SG_MAX_SINGLE_ALLOC to pack correctly at the highest order. The - * minimum value is 32 - */ -#define SG_CHUNK_SIZE 128 - -/* - * Like SG_CHUNK_SIZE, but for archs that have sg chaining. This limit - * is totally arbitrary, a setting of 2048 will get you at least 8mb ios. - */ -#ifdef CONFIG_ARCH_HAS_SG_CHAIN -#define SG_MAX_SEGMENTS 2048 -#else -#define SG_MAX_SEGMENTS SG_CHUNK_SIZE -#endif - /* * DIX-capable adapters effectively support infinite chaining for the * protection information scatterlist diff --git a/lib/Kconfig b/lib/Kconfig index 3cca1222578e..61d55bd0ed89 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -523,6 +523,13 @@ config SG_SPLIT a scatterlist. This should be selected by a driver or an API which whishes to split a scatterlist amongst multiple DMA channels. +config SG_POOL + def_bool n + help + Provides a helper to allocate chained scatterlists. This should be + selected by a driver or an API which whishes to allocate chained + scatterlist. + # # sg chaining option # diff --git a/lib/Makefile b/lib/Makefile index 7bd6fd436c97..bf01c2673423 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -178,6 +178,7 @@ obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o obj-$(CONFIG_GENERIC_NET_UTILS) += net_utils.o obj-$(CONFIG_SG_SPLIT) += sg_split.o +obj-$(CONFIG_SG_POOL) += sg_pool.o obj-$(CONFIG_STMP_DEVICE) += stmp_device.o obj-$(CONFIG_IRQ_POLL) += irq_poll.o diff --git a/lib/sg_pool.c b/lib/sg_pool.c new file mode 100644 index 000000000000..6dd30615a201 --- /dev/null +++ b/lib/sg_pool.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include + +#define SG_MEMPOOL_NR ARRAY_SIZE(sg_pools) +#define SG_MEMPOOL_SIZE 2 + +struct sg_pool { + size_t size; + char *name; + struct kmem_cache *slab; + mempool_t *pool; +}; + +#define SP(x) { .size = x, "sgpool-" __stringify(x) } +#if (SG_CHUNK_SIZE < 32) +#error SG_CHUNK_SIZE is too small (must be 32 or greater) +#endif +static struct sg_pool sg_pools[] = { + SP(8), + SP(16), +#if (SG_CHUNK_SIZE > 32) + SP(32), +#if (SG_CHUNK_SIZE > 64) + SP(64), +#if (SG_CHUNK_SIZE > 128) + SP(128), +#if (SG_CHUNK_SIZE > 256) +#error SG_CHUNK_SIZE is too large (256 MAX) +#endif +#endif +#endif +#endif + SP(SG_CHUNK_SIZE) +}; +#undef SP + +static inline unsigned int sg_pool_index(unsigned short nents) +{ + unsigned int index; + + BUG_ON(nents > SG_CHUNK_SIZE); + + if (nents <= 8) + index = 0; + else + index = get_count_order(nents) - 3; + + return index; +} + +static void sg_pool_free(struct scatterlist *sgl, unsigned int nents) +{ + struct sg_pool *sgp; + + sgp = sg_pools + sg_pool_index(nents); + mempool_free(sgl, sgp->pool); +} + +static struct scatterlist *sg_pool_alloc(unsigned int nents, gfp_t gfp_mask) +{ + struct sg_pool *sgp; + + sgp = sg_pools + sg_pool_index(nents); + return mempool_alloc(sgp->pool, gfp_mask); +} + +/** + * sg_free_table_chained - Free a previously mapped sg table + * @table: The sg table header to use + * @first_chunk: was first_chunk not NULL in sg_alloc_table_chained? + * + * Description: + * Free an sg table previously allocated and setup with + * sg_alloc_table_chained(). + * + **/ +void sg_free_table_chained(struct sg_table *table, bool first_chunk) +{ + if (first_chunk && table->orig_nents <= SG_CHUNK_SIZE) + return; + __sg_free_table(table, SG_CHUNK_SIZE, first_chunk, sg_pool_free); +} +EXPORT_SYMBOL_GPL(sg_free_table_chained); + +/** + * sg_alloc_table_chained - Allocate and chain SGLs in an sg table + * @table: The sg table header to use + * @nents: Number of entries in sg list + * @first_chunk: first SGL + * + * Description: + * Allocate and chain SGLs in an sg table. If @nents@ is larger than + * SG_CHUNK_SIZE a chained sg table will be setup. + * + **/ +int sg_alloc_table_chained(struct sg_table *table, int nents, + struct scatterlist *first_chunk) +{ + int ret; + + BUG_ON(!nents); + + if (first_chunk) { + if (nents <= SG_CHUNK_SIZE) { + table->nents = table->orig_nents = nents; + sg_init_table(table->sgl, nents); + return 0; + } + } + + ret = __sg_alloc_table(table, nents, SG_CHUNK_SIZE, + first_chunk, GFP_ATOMIC, sg_pool_alloc); + if (unlikely(ret)) + sg_free_table_chained(table, (bool)first_chunk); + return ret; +} +EXPORT_SYMBOL_GPL(sg_alloc_table_chained); + +static __init int sg_pool_init(void) +{ + int i; + + for (i = 0; i < SG_MEMPOOL_NR; i++) { + struct sg_pool *sgp = sg_pools + i; + int size = sgp->size * sizeof(struct scatterlist); + + sgp->slab = kmem_cache_create(sgp->name, size, 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!sgp->slab) { + printk(KERN_ERR "SG_POOL: can't init sg slab %s\n", + sgp->name); + goto cleanup_sdb; + } + + sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE, + sgp->slab); + if (!sgp->pool) { + printk(KERN_ERR "SG_POOL: can't init sg mempool %s\n", + sgp->name); + goto cleanup_sdb; + } + } + + return 0; + +cleanup_sdb: + for (i = 0; i < SG_MEMPOOL_NR; i++) { + struct sg_pool *sgp = sg_pools + i; + if (sgp->pool) + mempool_destroy(sgp->pool); + if (sgp->slab) + kmem_cache_destroy(sgp->slab); + } + + return -ENOMEM; +} + +static __exit void sg_pool_exit(void) +{ + int i; + + for (i = 0; i < SG_MEMPOOL_NR; i++) { + struct sg_pool *sgp = sg_pools + i; + mempool_destroy(sgp->pool); + kmem_cache_destroy(sgp->slab); + } +} + +module_init(sg_pool_init); +module_exit(sg_pool_exit); -- cgit From c5cc2a0bc930f1ae00b198aeb752acc3bdd4d5a7 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 16 Mar 2016 21:54:55 +0200 Subject: clk: ti: dpll: add support for specifying max rate for DPLLs DPLLs typically have a maximum rate they can support, and this varies from DPLL to DPLL. Add support of the maximum rate value to the DPLL data struct, and also add check for this in the DPLL round_rate function. Signed-off-by: Tero Kristo Reviewed-by: Nishanth Menon Cc: Tomi Valkeinen Cc: Lokesh Vutla Signed-off-by: Stephen Boyd --- drivers/clk/ti/clkt_dpll.c | 3 +++ include/linux/clk/ti.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/ti/clkt_dpll.c b/drivers/clk/ti/clkt_dpll.c index 032c658a5f5e..b919fdfe8256 100644 --- a/drivers/clk/ti/clkt_dpll.c +++ b/drivers/clk/ti/clkt_dpll.c @@ -301,6 +301,9 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, dd = clk->dpll_data; + if (dd->max_rate && target_rate > dd->max_rate) + target_rate = dd->max_rate; + ref_rate = clk_hw_get_rate(dd->clk_ref); clk_name = clk_hw_get_name(hw); pr_debug("clock: %s: starting DPLL round_rate, target rate %lu\n", diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index dc5164a6df29..6110fe09ed18 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -37,6 +37,7 @@ * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate() * @min_divider: minimum valid non-bypass divider value (actual) * @max_divider: maximum valid non-bypass divider value (actual) + * @max_rate: maximum clock rate for the DPLL * @modes: possible values of @enable_mask * @autoidle_reg: register containing the DPLL autoidle mode bitfield * @idlest_reg: register containing the DPLL idle status bitfield @@ -81,6 +82,7 @@ struct dpll_data { u8 last_rounded_n; u8 min_divider; u16 max_divider; + unsigned long max_rate; u8 modes; void __iomem *autoidle_reg; void __iomem *idlest_reg; -- cgit From af8a41271b56f6d79cb4d7c7f3ca688a2d97a801 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 15 Apr 2016 16:59:38 +0200 Subject: iio:adis: Add support for manual self-test flag clear Some variants of the devices from the ADIS family don't auto-clear the self-test bit after the self-test has completed. Instead we have to manually clear. Add support for this to the ADIS library. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 7 ++++++- include/linux/iio/imu/adis.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 911255d41c1a..ad6f91d06185 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -324,7 +324,12 @@ static int adis_self_test(struct adis *adis) msleep(adis->data->startup_delay); - return adis_check_status(adis); + ret = adis_check_status(adis); + + if (adis->data->self_test_no_autoclear) + adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00); + + return ret; } /** diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index fa2d01ef8f55..360da7d18a3d 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -41,6 +41,7 @@ struct adis_data { unsigned int diag_stat_reg; unsigned int self_test_mask; + bool self_test_no_autoclear; unsigned int startup_delay; const char * const *status_error_msgs; -- cgit From 20147f0d4f50f6f0d1fbe1815fe3d4d0a6444a70 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 29 Mar 2016 17:22:26 +0800 Subject: mfd: axp20x: Add support for AXP809 PMIC The X-Powers AXP809 is a new PMIC that is paired with Allwinner's A80 SoC, along with a slave AXP806 PMIC. This PMIC is quite similar to the earlier AXP223, though the interrupts and regulator have changed a bit. This patch adds support for the interrupts and power button of the PMIC. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x-rsb.c | 1 + drivers/mfd/axp20x.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c index 28c20247c112..a407527bcd09 100644 --- a/drivers/mfd/axp20x-rsb.c +++ b/drivers/mfd/axp20x-rsb.c @@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) static const struct of_device_id axp20x_rsb_of_match[] = { { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID }, + { .compatible = "x-powers,axp809", .data = (void *)AXP809_ID }, { }, }; MODULE_DEVICE_TABLE(of, axp20x_rsb_of_match); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index a57d6e940610..1ce923277cc8 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -37,6 +37,7 @@ static const char * const axp20x_model_names[] = { "AXP221", "AXP223", "AXP288", + "AXP809", }; static const struct regmap_range axp152_writeable_ranges[] = { @@ -85,6 +86,7 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +/* AXP22x ranges are shared with the AXP809, as they cover the same range */ static const struct regmap_range axp22x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1), @@ -211,6 +213,20 @@ static struct resource axp288_fuel_gauge_resources[] = { }, }; +static struct resource axp809_pek_resources[] = { + { + .name = "PEK_DBR", + .start = AXP809_IRQ_PEK_RIS_EDGE, + .end = AXP809_IRQ_PEK_RIS_EDGE, + .flags = IORESOURCE_IRQ, + }, { + .name = "PEK_DBF", + .start = AXP809_IRQ_PEK_FAL_EDGE, + .end = AXP809_IRQ_PEK_FAL_EDGE, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct regmap_config axp152_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -378,6 +394,41 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; +static const struct regmap_irq axp809_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP809, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP809, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP809, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP809, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP809, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP809, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP809, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP809, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP809, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP809, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP809, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP809, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP809, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP809, BATT_CHG_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP809, BATT_CHG_TEMP_HIGH_END, 2, 6), + INIT_REGMAP_IRQ(AXP809, BATT_CHG_TEMP_LOW, 2, 5), + INIT_REGMAP_IRQ(AXP809, BATT_CHG_TEMP_LOW_END, 2, 4), + INIT_REGMAP_IRQ(AXP809, BATT_ACT_TEMP_HIGH, 2, 3), + INIT_REGMAP_IRQ(AXP809, BATT_ACT_TEMP_HIGH_END, 2, 2), + INIT_REGMAP_IRQ(AXP809, BATT_ACT_TEMP_LOW, 2, 1), + INIT_REGMAP_IRQ(AXP809, BATT_ACT_TEMP_LOW_END, 2, 0), + INIT_REGMAP_IRQ(AXP809, DIE_TEMP_HIGH, 3, 7), + INIT_REGMAP_IRQ(AXP809, LOW_PWR_LVL1, 3, 1), + INIT_REGMAP_IRQ(AXP809, LOW_PWR_LVL2, 3, 0), + INIT_REGMAP_IRQ(AXP809, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP809, PEK_RIS_EDGE, 4, 6), + INIT_REGMAP_IRQ(AXP809, PEK_FAL_EDGE, 4, 5), + INIT_REGMAP_IRQ(AXP809, PEK_SHORT, 4, 4), + INIT_REGMAP_IRQ(AXP809, PEK_LONG, 4, 3), + INIT_REGMAP_IRQ(AXP809, PEK_OVER_OFF, 4, 2), + INIT_REGMAP_IRQ(AXP809, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP809, GPIO0_INPUT, 4, 0), +}; + static const struct regmap_irq_chip axp152_regmap_irq_chip = { .name = "axp152_irq_chip", .status_base = AXP152_IRQ1_STATE, @@ -428,6 +479,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp809_regmap_irq_chip = { + .name = "axp809", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, + .mask_invert = true, + .init_ack_masked = true, + .irqs = axp809_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp809_regmap_irqs), + .num_regs = 5, +}; + static struct mfd_cell axp20x_cells[] = { { .name = "axp20x-pek", @@ -572,6 +635,16 @@ static struct mfd_cell axp288_cells[] = { }, }; +static struct mfd_cell axp809_cells[] = { + { + .name = "axp20x-pek", + .num_resources = ARRAY_SIZE(axp809_pek_resources), + .resources = axp809_pek_resources, + }, { + .name = "axp20x-regulator", + }, +}; + static struct axp20x_dev *axp20x_pm_power_off; static void axp20x_power_off(void) { @@ -631,6 +704,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_cfg = &axp288_regmap_config; axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; break; + case AXP809_ID: + axp20x->nr_cells = ARRAY_SIZE(axp809_cells); + axp20x->cells = axp809_cells; + axp20x->regmap_cfg = &axp22x_regmap_config; + axp20x->regmap_irq_chip = &axp809_regmap_irq_chip; + break; default: dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant); return -EINVAL; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index d82e7d51372b..0be4982f08fe 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -20,6 +20,7 @@ enum { AXP221_ID, AXP223_ID, AXP288_ID, + AXP809_ID, NR_AXP20X_VARIANTS, }; @@ -264,6 +265,29 @@ enum { AXP22X_REG_ID_MAX, }; +enum { + AXP809_DCDC1 = 0, + AXP809_DCDC2, + AXP809_DCDC3, + AXP809_DCDC4, + AXP809_DCDC5, + AXP809_DC1SW, + AXP809_DC5LDO, + AXP809_ALDO1, + AXP809_ALDO2, + AXP809_ALDO3, + AXP809_ELDO1, + AXP809_ELDO2, + AXP809_ELDO3, + AXP809_DLDO1, + AXP809_DLDO2, + AXP809_RTC_LDO, + AXP809_LDO_IO0, + AXP809_LDO_IO1, + AXP809_SW, + AXP809_REG_ID_MAX, +}; + /* IRQs */ enum { AXP152_IRQ_LDO0IN_CONNECT = 1, @@ -390,6 +414,41 @@ enum axp288_irqs { AXP288_IRQ_BC_USB_CHNG, }; +enum axp809_irqs { + AXP809_IRQ_ACIN_OVER_V = 1, + AXP809_IRQ_ACIN_PLUGIN, + AXP809_IRQ_ACIN_REMOVAL, + AXP809_IRQ_VBUS_OVER_V, + AXP809_IRQ_VBUS_PLUGIN, + AXP809_IRQ_VBUS_REMOVAL, + AXP809_IRQ_VBUS_V_LOW, + AXP809_IRQ_BATT_PLUGIN, + AXP809_IRQ_BATT_REMOVAL, + AXP809_IRQ_BATT_ENT_ACT_MODE, + AXP809_IRQ_BATT_EXIT_ACT_MODE, + AXP809_IRQ_CHARG, + AXP809_IRQ_CHARG_DONE, + AXP809_IRQ_BATT_CHG_TEMP_HIGH, + AXP809_IRQ_BATT_CHG_TEMP_HIGH_END, + AXP809_IRQ_BATT_CHG_TEMP_LOW, + AXP809_IRQ_BATT_CHG_TEMP_LOW_END, + AXP809_IRQ_BATT_ACT_TEMP_HIGH, + AXP809_IRQ_BATT_ACT_TEMP_HIGH_END, + AXP809_IRQ_BATT_ACT_TEMP_LOW, + AXP809_IRQ_BATT_ACT_TEMP_LOW_END, + AXP809_IRQ_DIE_TEMP_HIGH, + AXP809_IRQ_LOW_PWR_LVL1, + AXP809_IRQ_LOW_PWR_LVL2, + AXP809_IRQ_TIMER, + AXP809_IRQ_PEK_RIS_EDGE, + AXP809_IRQ_PEK_FAL_EDGE, + AXP809_IRQ_PEK_SHORT, + AXP809_IRQ_PEK_LONG, + AXP809_IRQ_PEK_OVER_OFF, + AXP809_IRQ_GPIO1_INPUT, + AXP809_IRQ_GPIO0_INPUT, +}; + #define AXP288_TS_ADC_H 0x58 #define AXP288_TS_ADC_L 0x59 #define AXP288_GP_ADC_H 0x5a -- cgit From a8f447be8056d9ce17bf7757d6de79426700bb8b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 8 Apr 2016 00:12:55 +0530 Subject: mfd: Add resource managed APIs for mfd_add_devices Add resource managed API devm_mfd_add_devices() for the mfd_add_devices(). This helps in reducing code in error path as it is not required to call mfd_remove_devices() explicitly to remove all child-devices. In some cases, it also helps not to implement .remove() callback which get called during driver unbind. Signed-off-by: Laxman Dewangan Signed-off-by: Lee Jones --- drivers/mfd/mfd-core.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/mfd/core.h | 4 ++++ 2 files changed, 42 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 409da01effcd..4b4c1d4f3280 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -334,6 +334,44 @@ void mfd_remove_devices(struct device *parent) } EXPORT_SYMBOL(mfd_remove_devices); +static void devm_mfd_dev_release(struct device *dev, void *res) +{ + mfd_remove_devices(dev); +} + +/** + * devm_mfd_add_devices - Resource managed version of mfd_add_devices() + * + * Returns 0 on success or an appropriate negative error number on failure. + * All child-devices of the MFD will automatically be removed when it gets + * unbinded. + */ +int devm_mfd_add_devices(struct device *dev, int id, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base, struct irq_domain *domain) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_mfd_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = mfd_add_devices(dev, id, cells, n_devs, mem_base, + irq_base, domain); + if (ret < 0) { + devres_free(ptr); + return ret; + } + + *ptr = dev; + devres_add(dev, ptr); + + return ret; +} +EXPORT_SYMBOL(devm_mfd_add_devices); + int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones) { struct mfd_cell cell_entry; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index bc6f7e00fb3d..4a0268afe546 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -131,4 +131,8 @@ static inline int mfd_add_hotplug_devices(struct device *parent, extern void mfd_remove_devices(struct device *parent); +extern int devm_mfd_add_devices(struct device *dev, int id, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base, struct irq_domain *irq_domain); #endif -- cgit From 679ca39fc670a5a95c2b40d2cc8cf2cee2486f7a Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 18 Apr 2016 16:53:39 +0900 Subject: usb: gadget: udc: core: add usb_gadget_{un}map_request_by_dev() If the following environment, the first argument of DMA API should be set to a DMAC's device structure, not a udc controller's one. - A udc controller needs an external DMAC device (like a DMA Engine). - The external DMAC enables IOMMU. So, this patch add usb_gadget_{un}map_request_by_dev() API to set a DMAC's device structure by a udc controller driver. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 24 ++++++++++++++++++------ include/linux/usb/gadget.h | 4 ++++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index c6e76465065a..6e8300d6a737 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -61,11 +61,9 @@ static int udc_bind_to_driver(struct usb_udc *udc, #ifdef CONFIG_HAS_DMA -int usb_gadget_map_request(struct usb_gadget *gadget, +int usb_gadget_map_request_by_dev(struct device *dev, struct usb_request *req, int is_in) { - struct device *dev = gadget->dev.parent; - if (req->length == 0) return 0; @@ -92,24 +90,38 @@ int usb_gadget_map_request(struct usb_gadget *gadget, return 0; } +EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); + +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); +} EXPORT_SYMBOL_GPL(usb_gadget_map_request); -void usb_gadget_unmap_request(struct usb_gadget *gadget, +void usb_gadget_unmap_request_by_dev(struct device *dev, struct usb_request *req, int is_in) { if (req->length == 0) return; if (req->num_mapped_sgs) { - dma_unmap_sg(gadget->dev.parent, req->sg, req->num_mapped_sgs, + dma_unmap_sg(dev, req->sg, req->num_mapped_sgs, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->num_mapped_sgs = 0; } else { - dma_unmap_single(gadget->dev.parent, req->dma, req->length, + dma_unmap_single(dev, req->dma, req->length, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } } +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); +} EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); #endif /* CONFIG_HAS_DMA */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 5d4e151c49bf..457651bf45b0 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -1223,9 +1223,13 @@ int usb_otg_descriptor_init(struct usb_gadget *gadget, /* utility to simplify map/unmap of usb_requests to/from DMA */ +extern int usb_gadget_map_request_by_dev(struct device *dev, + struct usb_request *req, int is_in); extern int usb_gadget_map_request(struct usb_gadget *gadget, struct usb_request *req, int is_in); +extern void usb_gadget_unmap_request_by_dev(struct device *dev, + struct usb_request *req, int is_in); extern void usb_gadget_unmap_request(struct usb_gadget *gadget, struct usb_request *req, int is_in); -- cgit From 7ef224d1d0e3a1ade02d02c01ce1dcffb736d2c3 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 3 Mar 2016 12:54:42 -0600 Subject: tracing: Add 'hist' event trigger command 'hist' triggers allow users to continually aggregate trace events, which can then be viewed afterwards by simply reading a 'hist' file containing the aggregation in a human-readable format. The basic idea is very simple and boils down to a mechanism whereby trace events, rather than being exhaustively dumped in raw form and viewed directly, are automatically 'compressed' into meaningful tables completely defined by the user. This is done strictly via single-line command-line commands and without the aid of any kind of programming language or interpreter. A surprising number of typical use cases can be accomplished by users via this simple mechanism. In fact, a large number of the tasks that users typically do using the more complicated script-based tracing tools, at least during the initial stages of an investigation, can be accomplished by simply specifying a set of keys and values to be used in the creation of a hash table. The Linux kernel trace event subsystem happens to provide an extensive list of keys and values ready-made for such a purpose in the form of the event format files associated with each trace event. By simply consulting the format file for field names of interest and by plugging them into the hist trigger command, users can create an endless number of useful aggregations to help with investigating various properties of the system. See Documentation/trace/events.txt for examples. hist triggers are implemented on top of the existing event trigger infrastructure, and as such are consistent with the existing triggers from a user's perspective as well. The basic syntax follows the existing trigger syntax. Users start an aggregation by writing a 'hist' trigger to the event of interest's trigger file: # echo hist:keys=xxx [ if filter] > event/trigger Once a hist trigger has been set up, by default it continually aggregates every matching event into a hash table using the event key and a value field named 'hitcount'. To view the aggregation at any point in time, simply read the 'hist' file in the same directory as the 'trigger' file: # cat event/hist The detailed syntax provides additional options for user control, and is described exhaustively in Documentation/trace/events.txt and in the virtual tracing/README file in the tracing subsystem. Link: http://lkml.kernel.org/r/72d263b5e1853fe9c314953b65833c3aa75479f2.1457029949.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi Tested-by: Masami Hiramatsu Reviewed-by: Namhyung Kim Signed-off-by: Steven Rostedt --- include/linux/trace_events.h | 1 + kernel/trace/Kconfig | 16 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 17 + kernel/trace/trace.h | 7 + kernel/trace/trace_events.c | 4 + kernel/trace/trace_events_hist.c | 849 ++++++++++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 1 + 8 files changed, 896 insertions(+) create mode 100644 kernel/trace/trace_events_hist.c (limited to 'include/linux') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 0810f81b6db2..404603720650 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -407,6 +407,7 @@ enum event_trigger_type { ETT_SNAPSHOT = (1 << 1), ETT_STACKTRACE = (1 << 2), ETT_EVENT_ENABLE = (1 << 3), + ETT_EVENT_HIST = (1 << 4), }; extern int filter_match_preds(struct event_filter *filter, void *rec); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index d39556fd863a..fafeaf803bd0 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -538,6 +538,22 @@ config TRACING_MAP generally used outside of that context, and is normally selected by tracers that use it. +config HIST_TRIGGERS + bool "Histogram triggers" + depends on ARCH_HAVE_NMI_SAFE_CMPXCHG + select TRACING_MAP + default n + help + Hist triggers allow one or more arbitrary trace event fields + to be aggregated into hash tables and dumped to stdout by + reading a debugfs/tracefs file. They're useful for + gathering quick and dirty (though precise) summaries of + event activity as an initial guide for further investigation + using more advanced tools. + + See Documentation/trace/events.txt. + If in doubt, say N. + config MMIOTRACE_TEST tristate "Test module for mmiotrace" depends on MMIOTRACE && m diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 4255c4057aaa..979e7bfbde7a 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o endif obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o +obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o obj-$(CONFIG_TRACEPOINTS) += power-traces.o diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0d12dbde8399..6cf8fd03b028 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3812,6 +3812,9 @@ static const char readme_msg[] = #endif #ifdef CONFIG_TRACER_SNAPSHOT "\t\t snapshot\n" +#endif +#ifdef CONFIG_HIST_TRIGGERS + "\t\t hist (see below)\n" #endif "\t example: echo traceoff > events/block/block_unplug/trigger\n" "\t echo traceoff:3 > events/block/block_unplug/trigger\n" @@ -3828,6 +3831,20 @@ static const char readme_msg[] = "\t To remove a trigger with a count:\n" "\t echo '!:0 > //trigger\n" "\t Filters can be ignored when removing a trigger.\n" +#ifdef CONFIG_HIST_TRIGGERS + " hist trigger\t- If set, event hits are aggregated into a hash table\n" + "\t Format: hist:keys=\n" + "\t [:size=#entries]\n" + "\t [if ]\n\n" + "\t When a matching event is hit, an entry is added to a hash\n" + "\t table using the key named, and the value of a sum called\n" + "\t 'hitcount' is incremented. Keys correspond to fields in the\n" + "\t event's format description. Keys can be any field. The\n" + "\t 'size' parameter can be used to specify more or fewer than\n" + "\t the default 2048 entries for the hashtable size.\n\n" + "\t Reading the 'hist' file for the event will dump the hash\n" + "\t table in its entirety to stdout." +#endif ; static ssize_t diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2525042760e6..505f8a45f426 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1162,6 +1162,13 @@ extern struct mutex event_mutex; extern struct list_head ftrace_events; extern const struct file_operations event_trigger_fops; +extern const struct file_operations event_hist_fops; + +#ifdef CONFIG_HIST_TRIGGERS +extern int register_trigger_hist_cmd(void); +#else +static inline int register_trigger_hist_cmd(void) { return 0; } +#endif extern int register_trigger_cmds(void); extern void clear_event_triggers(struct trace_array *tr); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index add81dff7520..e7cb983ee93c 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2141,6 +2141,10 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) trace_create_file("trigger", 0644, file->dir, file, &event_trigger_fops); +#ifdef CONFIG_HIST_TRIGGERS + trace_create_file("hist", 0444, file->dir, file, + &event_hist_fops); +#endif trace_create_file("format", 0444, file->dir, call, &ftrace_event_format_fops); diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c new file mode 100644 index 000000000000..23b45e462117 --- /dev/null +++ b/kernel/trace/trace_events_hist.c @@ -0,0 +1,849 @@ +/* + * trace_events_hist - trace event hist triggers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2015 Tom Zanussi + */ + +#include +#include +#include +#include +#include + +#include "tracing_map.h" +#include "trace.h" + +struct hist_field; + +typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); + +struct hist_field { + struct ftrace_event_field *field; + unsigned long flags; + hist_field_fn_t fn; + unsigned int size; +}; + +static u64 hist_field_counter(struct hist_field *field, void *event) +{ + return 1; +} + +static u64 hist_field_string(struct hist_field *hist_field, void *event) +{ + char *addr = (char *)(event + hist_field->field->offset); + + return (u64)(unsigned long)addr; +} + +#define DEFINE_HIST_FIELD_FN(type) \ +static u64 hist_field_##type(struct hist_field *hist_field, void *event)\ +{ \ + type *addr = (type *)(event + hist_field->field->offset); \ + \ + return (u64)*addr; \ +} + +DEFINE_HIST_FIELD_FN(s64); +DEFINE_HIST_FIELD_FN(u64); +DEFINE_HIST_FIELD_FN(s32); +DEFINE_HIST_FIELD_FN(u32); +DEFINE_HIST_FIELD_FN(s16); +DEFINE_HIST_FIELD_FN(u16); +DEFINE_HIST_FIELD_FN(s8); +DEFINE_HIST_FIELD_FN(u8); + +#define for_each_hist_field(i, hist_data) \ + for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) + +#define for_each_hist_val_field(i, hist_data) \ + for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) + +#define for_each_hist_key_field(i, hist_data) \ + for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) + +#define HITCOUNT_IDX 0 +#define HIST_KEY_MAX 1 +#define HIST_KEY_SIZE_MAX MAX_FILTER_STR_VAL + +enum hist_field_flags { + HIST_FIELD_FL_HITCOUNT = 1, + HIST_FIELD_FL_KEY = 2, + HIST_FIELD_FL_STRING = 4, +}; + +struct hist_trigger_attrs { + char *keys_str; + unsigned int map_bits; +}; + +struct hist_trigger_data { + struct hist_field *fields[TRACING_MAP_FIELDS_MAX]; + unsigned int n_vals; + unsigned int n_keys; + unsigned int n_fields; + unsigned int key_size; + struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; + unsigned int n_sort_keys; + struct trace_event_file *event_file; + struct hist_trigger_attrs *attrs; + struct tracing_map *map; +}; + +static hist_field_fn_t select_value_fn(int field_size, int field_is_signed) +{ + hist_field_fn_t fn = NULL; + + switch (field_size) { + case 8: + if (field_is_signed) + fn = hist_field_s64; + else + fn = hist_field_u64; + break; + case 4: + if (field_is_signed) + fn = hist_field_s32; + else + fn = hist_field_u32; + break; + case 2: + if (field_is_signed) + fn = hist_field_s16; + else + fn = hist_field_u16; + break; + case 1: + if (field_is_signed) + fn = hist_field_s8; + else + fn = hist_field_u8; + break; + } + + return fn; +} + +static int parse_map_size(char *str) +{ + unsigned long size, map_bits; + int ret; + + strsep(&str, "="); + if (!str) { + ret = -EINVAL; + goto out; + } + + ret = kstrtoul(str, 0, &size); + if (ret) + goto out; + + map_bits = ilog2(roundup_pow_of_two(size)); + if (map_bits < TRACING_MAP_BITS_MIN || + map_bits > TRACING_MAP_BITS_MAX) + ret = -EINVAL; + else + ret = map_bits; + out: + return ret; +} + +static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) +{ + if (!attrs) + return; + + kfree(attrs->keys_str); + kfree(attrs); +} + +static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) +{ + struct hist_trigger_attrs *attrs; + int ret = 0; + + attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return ERR_PTR(-ENOMEM); + + while (trigger_str) { + char *str = strsep(&trigger_str, ":"); + + if ((strncmp(str, "key=", strlen("key=")) == 0) || + (strncmp(str, "keys=", strlen("keys=")) == 0)) + attrs->keys_str = kstrdup(str, GFP_KERNEL); + else if (strncmp(str, "size=", strlen("size=")) == 0) { + int map_bits = parse_map_size(str); + + if (map_bits < 0) { + ret = map_bits; + goto free; + } + attrs->map_bits = map_bits; + } else { + ret = -EINVAL; + goto free; + } + } + + if (!attrs->keys_str) { + ret = -EINVAL; + goto free; + } + + return attrs; + free: + destroy_hist_trigger_attrs(attrs); + + return ERR_PTR(ret); +} + +static void destroy_hist_field(struct hist_field *hist_field) +{ + kfree(hist_field); +} + +static struct hist_field *create_hist_field(struct ftrace_event_field *field, + unsigned long flags) +{ + struct hist_field *hist_field; + + if (field && is_function_field(field)) + return NULL; + + hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL); + if (!hist_field) + return NULL; + + if (flags & HIST_FIELD_FL_HITCOUNT) { + hist_field->fn = hist_field_counter; + goto out; + } + + if (is_string_field(field)) { + flags |= HIST_FIELD_FL_STRING; + hist_field->fn = hist_field_string; + } else { + hist_field->fn = select_value_fn(field->size, + field->is_signed); + if (!hist_field->fn) { + destroy_hist_field(hist_field); + return NULL; + } + } + out: + hist_field->field = field; + hist_field->flags = flags; + + return hist_field; +} + +static void destroy_hist_fields(struct hist_trigger_data *hist_data) +{ + unsigned int i; + + for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { + if (hist_data->fields[i]) { + destroy_hist_field(hist_data->fields[i]); + hist_data->fields[i] = NULL; + } + } +} + +static int create_hitcount_val(struct hist_trigger_data *hist_data) +{ + hist_data->fields[HITCOUNT_IDX] = + create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT); + if (!hist_data->fields[HITCOUNT_IDX]) + return -ENOMEM; + + hist_data->n_vals++; + + if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) + return -EINVAL; + + return 0; +} + +static int create_val_fields(struct hist_trigger_data *hist_data, + struct trace_event_file *file) +{ + int ret; + + ret = create_hitcount_val(hist_data); + + return ret; +} + +static int create_key_field(struct hist_trigger_data *hist_data, + unsigned int key_idx, + struct trace_event_file *file, + char *field_str) +{ + struct ftrace_event_field *field = NULL; + unsigned long flags = 0; + unsigned int key_size; + int ret = 0; + + if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX)) + return -EINVAL; + + flags |= HIST_FIELD_FL_KEY; + + field = trace_find_event_field(file->event_call, field_str); + if (!field) { + ret = -EINVAL; + goto out; + } + + key_size = field->size; + + hist_data->fields[key_idx] = create_hist_field(field, flags); + if (!hist_data->fields[key_idx]) { + ret = -ENOMEM; + goto out; + } + + key_size = ALIGN(key_size, sizeof(u64)); + hist_data->fields[key_idx]->size = key_size; + hist_data->key_size = key_size; + if (hist_data->key_size > HIST_KEY_SIZE_MAX) { + ret = -EINVAL; + goto out; + } + + hist_data->n_keys++; + + if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX)) + return -EINVAL; + + ret = key_size; + out: + return ret; +} + +static int create_key_fields(struct hist_trigger_data *hist_data, + struct trace_event_file *file) +{ + unsigned int i, n_vals = hist_data->n_vals; + char *fields_str, *field_str; + int ret = -EINVAL; + + fields_str = hist_data->attrs->keys_str; + if (!fields_str) + goto out; + + strsep(&fields_str, "="); + if (!fields_str) + goto out; + + for (i = n_vals; i < n_vals + HIST_KEY_MAX; i++) { + field_str = strsep(&fields_str, ","); + if (!field_str) + break; + ret = create_key_field(hist_data, i, file, field_str); + if (ret < 0) + goto out; + } + if (fields_str) { + ret = -EINVAL; + goto out; + } + ret = 0; + out: + return ret; +} + +static int create_hist_fields(struct hist_trigger_data *hist_data, + struct trace_event_file *file) +{ + int ret; + + ret = create_val_fields(hist_data, file); + if (ret) + goto out; + + ret = create_key_fields(hist_data, file); + if (ret) + goto out; + + hist_data->n_fields = hist_data->n_vals + hist_data->n_keys; + out: + return ret; +} + +static int create_sort_keys(struct hist_trigger_data *hist_data) +{ + int ret = 0; + + hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */ + + return ret; +} + +static void destroy_hist_data(struct hist_trigger_data *hist_data) +{ + destroy_hist_trigger_attrs(hist_data->attrs); + destroy_hist_fields(hist_data); + tracing_map_destroy(hist_data->map); + kfree(hist_data); +} + +static int create_tracing_map_fields(struct hist_trigger_data *hist_data) +{ + struct tracing_map *map = hist_data->map; + struct ftrace_event_field *field; + struct hist_field *hist_field; + unsigned int i, idx; + + for_each_hist_field(i, hist_data) { + hist_field = hist_data->fields[i]; + if (hist_field->flags & HIST_FIELD_FL_KEY) { + tracing_map_cmp_fn_t cmp_fn; + + field = hist_field->field; + + if (is_string_field(field)) + cmp_fn = tracing_map_cmp_string; + else + cmp_fn = tracing_map_cmp_num(field->size, + field->is_signed); + idx = tracing_map_add_key_field(map, 0, cmp_fn); + } else + idx = tracing_map_add_sum_field(map); + + if (idx < 0) + return idx; + } + + return 0; +} + +static struct hist_trigger_data * +create_hist_data(unsigned int map_bits, + struct hist_trigger_attrs *attrs, + struct trace_event_file *file) +{ + struct hist_trigger_data *hist_data; + int ret = 0; + + hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL); + if (!hist_data) + return ERR_PTR(-ENOMEM); + + hist_data->attrs = attrs; + + ret = create_hist_fields(hist_data, file); + if (ret) + goto free; + + ret = create_sort_keys(hist_data); + if (ret) + goto free; + + hist_data->map = tracing_map_create(map_bits, hist_data->key_size, + NULL, hist_data); + if (IS_ERR(hist_data->map)) { + ret = PTR_ERR(hist_data->map); + hist_data->map = NULL; + goto free; + } + + ret = create_tracing_map_fields(hist_data); + if (ret) + goto free; + + ret = tracing_map_init(hist_data->map); + if (ret) + goto free; + + hist_data->event_file = file; + out: + return hist_data; + free: + hist_data->attrs = NULL; + + destroy_hist_data(hist_data); + + hist_data = ERR_PTR(ret); + + goto out; +} + +static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt, + void *rec) +{ + struct hist_field *hist_field; + unsigned int i; + u64 hist_val; + + for_each_hist_val_field(i, hist_data) { + hist_field = hist_data->fields[i]; + hist_val = hist_field->fn(hist_field, rec); + tracing_map_update_sum(elt, i, hist_val); + } +} + +static void event_hist_trigger(struct event_trigger_data *data, void *rec) +{ + struct hist_trigger_data *hist_data = data->private_data; + struct hist_field *key_field; + struct tracing_map_elt *elt; + u64 field_contents; + void *key = NULL; + unsigned int i; + + for_each_hist_key_field(i, hist_data) { + key_field = hist_data->fields[i]; + + field_contents = key_field->fn(key_field, rec); + if (key_field->flags & HIST_FIELD_FL_STRING) + key = (void *)(unsigned long)field_contents; + else + key = (void *)&field_contents; + } + + elt = tracing_map_insert(hist_data->map, key); + if (elt) + hist_trigger_elt_update(hist_data, elt, rec); +} + +static void +hist_trigger_entry_print(struct seq_file *m, + struct hist_trigger_data *hist_data, void *key, + struct tracing_map_elt *elt) +{ + struct hist_field *key_field; + unsigned int i; + u64 uval; + + seq_puts(m, "{ "); + + for_each_hist_key_field(i, hist_data) { + key_field = hist_data->fields[i]; + + if (i > hist_data->n_vals) + seq_puts(m, ", "); + + if (key_field->flags & HIST_FIELD_FL_STRING) { + seq_printf(m, "%s: %-50s", key_field->field->name, + (char *)key); + } else { + uval = *(u64 *)key; + seq_printf(m, "%s: %10llu", + key_field->field->name, uval); + } + } + + seq_puts(m, " }"); + + seq_printf(m, " hitcount: %10llu", + tracing_map_read_sum(elt, HITCOUNT_IDX)); + + seq_puts(m, "\n"); +} + +static int print_entries(struct seq_file *m, + struct hist_trigger_data *hist_data) +{ + struct tracing_map_sort_entry **sort_entries = NULL; + struct tracing_map *map = hist_data->map; + unsigned int i, n_entries; + + n_entries = tracing_map_sort_entries(map, hist_data->sort_keys, + hist_data->n_sort_keys, + &sort_entries); + if (n_entries < 0) + return n_entries; + + for (i = 0; i < n_entries; i++) + hist_trigger_entry_print(m, hist_data, + sort_entries[i]->key, + sort_entries[i]->elt); + + tracing_map_destroy_sort_entries(sort_entries, n_entries); + + return n_entries; +} + +static int hist_show(struct seq_file *m, void *v) +{ + struct event_trigger_data *test, *data = NULL; + struct trace_event_file *event_file; + struct hist_trigger_data *hist_data; + int n_entries, ret = 0; + + mutex_lock(&event_mutex); + + event_file = event_file_data(m->private); + if (unlikely(!event_file)) { + ret = -ENODEV; + goto out_unlock; + } + + list_for_each_entry_rcu(test, &event_file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + data = test; + break; + } + } + if (!data) + goto out_unlock; + + seq_puts(m, "# event histogram\n#\n# trigger info: "); + data->ops->print(m, data->ops, data); + seq_puts(m, "\n"); + + hist_data = data->private_data; + n_entries = print_entries(m, hist_data); + if (n_entries < 0) { + ret = n_entries; + n_entries = 0; + } + + seq_printf(m, "\nTotals:\n Hits: %llu\n Entries: %u\n Dropped: %llu\n", + (u64)atomic64_read(&hist_data->map->hits), + n_entries, (u64)atomic64_read(&hist_data->map->drops)); + out_unlock: + mutex_unlock(&event_mutex); + + return ret; +} + +static int event_hist_open(struct inode *inode, struct file *file) +{ + return single_open(file, hist_show, file); +} + +const struct file_operations event_hist_fops = { + .open = event_hist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) +{ + seq_printf(m, "%s", hist_field->field->name); +} + +static int event_hist_trigger_print(struct seq_file *m, + struct event_trigger_ops *ops, + struct event_trigger_data *data) +{ + struct hist_trigger_data *hist_data = data->private_data; + struct hist_field *key_field; + unsigned int i; + + seq_puts(m, "hist:keys="); + + for_each_hist_key_field(i, hist_data) { + key_field = hist_data->fields[i]; + + if (i > hist_data->n_vals) + seq_puts(m, ","); + + hist_field_print(m, key_field); + } + + seq_puts(m, ":vals="); + seq_puts(m, "hitcount"); + + seq_puts(m, ":sort="); + seq_puts(m, "hitcount"); + + seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); + + if (data->filter_str) + seq_printf(m, " if %s", data->filter_str); + + seq_puts(m, " [active]"); + + seq_putc(m, '\n'); + + return 0; +} + +static void event_hist_trigger_free(struct event_trigger_ops *ops, + struct event_trigger_data *data) +{ + struct hist_trigger_data *hist_data = data->private_data; + + if (WARN_ON_ONCE(data->ref <= 0)) + return; + + data->ref--; + if (!data->ref) { + trigger_data_free(data); + destroy_hist_data(hist_data); + } +} + +static struct event_trigger_ops event_hist_trigger_ops = { + .func = event_hist_trigger, + .print = event_hist_trigger_print, + .init = event_trigger_init, + .free = event_hist_trigger_free, +}; + +static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd, + char *param) +{ + return &event_hist_trigger_ops; +} + +static int hist_register_trigger(char *glob, struct event_trigger_ops *ops, + struct event_trigger_data *data, + struct trace_event_file *file) +{ + struct event_trigger_data *test; + int ret = 0; + + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + ret = -EEXIST; + goto out; + } + } + + if (data->ops->init) { + ret = data->ops->init(data->ops, data); + if (ret < 0) + goto out; + } + + list_add_rcu(&data->list, &file->triggers); + ret++; + + update_cond_flag(file); + if (trace_event_trigger_enable_disable(file, 1) < 0) { + list_del_rcu(&data->list); + update_cond_flag(file); + ret--; + } + out: + return ret; +} + +static int event_hist_trigger_func(struct event_command *cmd_ops, + struct trace_event_file *file, + char *glob, char *cmd, char *param) +{ + unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT; + struct event_trigger_data *trigger_data; + struct hist_trigger_attrs *attrs; + struct event_trigger_ops *trigger_ops; + struct hist_trigger_data *hist_data; + char *trigger; + int ret = 0; + + if (!param) + return -EINVAL; + + /* separate the trigger from the filter (k:v [if filter]) */ + trigger = strsep(¶m, " \t"); + if (!trigger) + return -EINVAL; + + attrs = parse_hist_trigger_attrs(trigger); + if (IS_ERR(attrs)) + return PTR_ERR(attrs); + + if (attrs->map_bits) + hist_trigger_bits = attrs->map_bits; + + hist_data = create_hist_data(hist_trigger_bits, attrs, file); + if (IS_ERR(hist_data)) { + destroy_hist_trigger_attrs(attrs); + return PTR_ERR(hist_data); + } + + trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); + + ret = -ENOMEM; + trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); + if (!trigger_data) + goto out_free; + + trigger_data->count = -1; + trigger_data->ops = trigger_ops; + trigger_data->cmd_ops = cmd_ops; + + INIT_LIST_HEAD(&trigger_data->list); + RCU_INIT_POINTER(trigger_data->filter, NULL); + + trigger_data->private_data = hist_data; + + if (glob[0] == '!') { + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); + ret = 0; + goto out_free; + } + + if (!param) /* if param is non-empty, it's supposed to be a filter */ + goto out_reg; + + if (!cmd_ops->set_filter) + goto out_reg; + + ret = cmd_ops->set_filter(param, trigger_data, file); + if (ret < 0) + goto out_free; + out_reg: + ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file); + /* + * The above returns on success the # of triggers registered, + * but if it didn't register any it returns zero. Consider no + * triggers registered a failure too. + */ + if (!ret) { + ret = -ENOENT; + goto out_free; + } else if (ret < 0) + goto out_free; + /* Just return zero, not the number of registered triggers */ + ret = 0; + out: + return ret; + out_free: + if (cmd_ops->set_filter) + cmd_ops->set_filter(NULL, trigger_data, NULL); + + kfree(trigger_data); + + destroy_hist_data(hist_data); + goto out; +} + +static struct event_command trigger_hist_cmd = { + .name = "hist", + .trigger_type = ETT_EVENT_HIST, + .flags = EVENT_CMD_FL_NEEDS_REC, + .func = event_hist_trigger_func, + .reg = hist_register_trigger, + .unreg = unregister_trigger, + .get_trigger_ops = event_hist_get_trigger_ops, + .set_filter = set_trigger_filter, +}; + +__init int register_trigger_hist_cmd(void) +{ + int ret; + + ret = register_event_command(&trigger_hist_cmd); + WARN_ON(ret < 0); + + return ret; +} diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index d67992f3bb0e..d29092afe005 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -1447,6 +1447,7 @@ __init int register_trigger_cmds(void) register_trigger_snapshot_cmd(); register_trigger_stacktrace_cmd(); register_trigger_enable_disable_cmds(); + register_trigger_hist_cmd(); return 0; } -- cgit From 97865fe41322d83dac4373fe0a0de5b1a1b318c5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 24 Mar 2016 14:18:05 +0100 Subject: iio: st_sensors: verify interrupt event to status This makes all ST sensor drivers check that they actually have new data available for the requested channel(s) before claiming an IRQ, by reading the status register (which is conveniently the same for all ST sensors) and check that the channel has new data before proceeding to read it and fill the buffer. This way sensors can share an interrupt line: it can be flaged as shared and then the sensor that did not fire will return NO_IRQ, and the sensor that fired will handle the IRQ and return IRQ_HANDLED. Cc: Giuseppe Barba Cc: Denis Ciocca Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 5 +++++ drivers/iio/common/st_sensors/st_sensors_buffer.c | 18 ++++++++++++++++++ drivers/iio/gyro/st_gyro_core.c | 3 +++ drivers/iio/magnetometer/st_magn_core.c | 1 + drivers/iio/pressure/st_pressure_core.c | 2 ++ include/linux/iio/common/st_sensors.h | 3 +++ 6 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index fee32e3d7a05..9fb6d35fce5b 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -332,6 +332,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT, .bootime = 2, @@ -397,6 +398,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, .bootime = 2, @@ -474,6 +476,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, .ig1 = { .en_addr = ST_ACCEL_3_IG1_EN_ADDR, .en_mask = ST_ACCEL_3_IG1_EN_MASK, @@ -532,6 +535,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .drdy_irq = { .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, .bootime = 2, /* guess */ @@ -583,6 +587,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT, .bootime = 2, /* guess */ diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index 2ce0d2a3f855..c55898543a47 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -58,6 +58,24 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct st_sensor_data *sdata = iio_priv(indio_dev); + /* If we have a status register, check if this IRQ came from us */ + if (sdata->sensor_settings->drdy_irq.addr_stat_drdy) { + u8 status; + + len = sdata->tf->read_byte(&sdata->tb, sdata->dev, + sdata->sensor_settings->drdy_irq.addr_stat_drdy, + &status); + if (len < 0) + dev_err(sdata->dev, "could not read channel status\n"); + + /* + * If this was not caused by any channels on this sensor, + * return IRQ_NONE + */ + if (!(status & (u8)indio_dev->active_scan_mask[0])) + return IRQ_NONE; + } + len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); if (len < 0) goto st_sensors_get_buffer_element_error; diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 110f95b6e52f..be9057e89dc3 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -190,6 +190,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, .bootime = 2, @@ -258,6 +259,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, .bootime = 2, @@ -322,6 +324,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT, .bootime = 2, diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 501f858df413..62036d2a9956 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -484,6 +484,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .mask_int1 = ST_MAGN_3_DRDY_INT_MASK, .addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR, .mask_ihl = ST_MAGN_3_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT, .bootime = 2, diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 172393ad34af..1cd37eaa4a57 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -226,6 +226,7 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR, .mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT, .bootime = 2, @@ -312,6 +313,7 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR, .mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, .bootime = 2, diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 6670c3d25c58..d8da075bfda0 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -37,6 +37,7 @@ #define ST_SENSORS_DEFAULT_AXIS_ADDR 0x20 #define ST_SENSORS_DEFAULT_AXIS_MASK 0x07 #define ST_SENSORS_DEFAULT_AXIS_N_BIT 3 +#define ST_SENSORS_DEFAULT_STAT_ADDR 0x27 #define ST_SENSORS_MAX_NAME 17 #define ST_SENSORS_MAX_4WAI 7 @@ -121,6 +122,7 @@ struct st_sensor_bdu { * @mask_int2: mask to enable/disable IRQ on INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. + * @addr_stat_drdy: address to read status of DRDY (data ready) interrupt * struct ig1 - represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. @@ -131,6 +133,7 @@ struct st_sensor_data_ready_irq { u8 mask_int2; u8 addr_ihl; u8 mask_ihl; + u8 addr_stat_drdy; struct { u8 en_addr; u8 en_mask; -- cgit From 0e6f6871a1591f4bb0971809c45bc91a991f1967 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 14 Apr 2016 10:45:21 +0200 Subject: iio: st_sensors: support open drain mode Some types of ST Sensors can be connected to the same IRQ line as other peripherals using open drain. Add a device tree binding and a sensor data property to flip the right bit in the interrupt control register to enable open drain mode on the INT line. If the line is set to be open drain, also tag on IRQF_SHARED to the IRQ flags when requesting the interrupt, as the whole point of using open drain interrupt lines is to share them with more than one peripheral (wire-or). Cc: devicetree@vger.kernel.org Cc: Giuseppe Barba Cc: Denis Ciocca Acked-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/st-sensors.txt | 4 ++++ drivers/iio/accel/st_accel_core.c | 8 ++++++++ drivers/iio/common/st_sensors/st_sensors_core.c | 20 ++++++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_trigger.c | 13 +++++++++++++ drivers/iio/pressure/st_pressure_core.c | 8 ++++++++ include/linux/iio/common/st_sensors.h | 6 ++++++ include/linux/platform_data/st_sensors_pdata.h | 2 ++ 7 files changed, 61 insertions(+) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index 71b7bdff21cd..637e283f4a8b 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -16,6 +16,10 @@ Optional properties: - st,drdy-int-pin: the pin on the package that will be used to signal "data ready" (valid values: 1 or 2). This property is not configurable on all sensors. +- drive-open-drain: the interrupt/data ready line will be configured + as open drain, which is useful if several sensors share the same + interrupt line. (This binding is taken from pinctrl/pinctrl-bindings.txt) + This is a boolean property. Sensors may also have applicable pin control settings, those use the standard bindings from pinctrl/pinctrl-bindings.txt. diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 9fb6d35fce5b..dc73f2d85e6d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -99,6 +99,8 @@ #define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10 #define ST_ACCEL_2_IHL_IRQ_ADDR 0x22 #define ST_ACCEL_2_IHL_IRQ_MASK 0x80 +#define ST_ACCEL_2_OD_IRQ_ADDR 0x22 +#define ST_ACCEL_2_OD_IRQ_MASK 0x40 #define ST_ACCEL_2_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 3 */ @@ -180,6 +182,8 @@ #define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20 #define ST_ACCEL_5_IHL_IRQ_ADDR 0x22 #define ST_ACCEL_5_IHL_IRQ_MASK 0x80 +#define ST_ACCEL_5_OD_IRQ_ADDR 0x22 +#define ST_ACCEL_5_OD_IRQ_MASK 0x40 #define ST_ACCEL_5_IG1_EN_ADDR 0x21 #define ST_ACCEL_5_IG1_EN_MASK 0x08 #define ST_ACCEL_5_MULTIREAD_BIT false @@ -398,6 +402,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK, + .addr_od = ST_ACCEL_2_OD_IRQ_ADDR, + .mask_od = ST_ACCEL_2_OD_IRQ_MASK, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, @@ -587,6 +593,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR, .mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK, + .addr_od = ST_ACCEL_5_OD_IRQ_ADDR, + .mask_od = ST_ACCEL_5_OD_IRQ_MASK, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT, diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index f5a2d445d0c0..dffe00692169 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -301,6 +301,14 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, return -EINVAL; } + if (pdata->open_drain) { + if (!sdata->sensor_settings->drdy_irq.addr_od) + dev_err(&indio_dev->dev, + "open drain requested but unsupported.\n"); + else + sdata->int_pin_open_drain = true; + } + return 0; } @@ -321,6 +329,8 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev, else pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0; + pdata->open_drain = of_property_read_bool(np, "drive-open-drain"); + return pdata; } #else @@ -374,6 +384,16 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, return err; } + if (sdata->int_pin_open_drain) { + dev_info(&indio_dev->dev, + "set interrupt line to open drain mode\n"); + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor_settings->drdy_irq.addr_od, + sdata->sensor_settings->drdy_irq.mask_od, 1); + if (err < 0) + return err; + } + err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); return err; diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 6a8c98327945..da72279fcf99 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -64,6 +64,19 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, "rising edge\n", irq_trig); irq_trig = IRQF_TRIGGER_RISING; } + + /* + * If the interrupt pin is Open Drain, by definition this + * means that the interrupt line may be shared with other + * peripherals. But to do this we also need to have a status + * register and mask to figure out if this sensor was firing + * the IRQ or not, so we can tell the interrupt handle that + * it was "our" interrupt. + */ + if (sdata->int_pin_open_drain && + sdata->sensor_settings->drdy_irq.addr_stat_drdy) + irq_trig |= IRQF_SHARED; + err = request_threaded_irq(irq, iio_trigger_generic_data_rdy_poll, NULL, diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 1cd37eaa4a57..9e9b72a8f18f 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -64,6 +64,8 @@ #define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20 #define ST_PRESS_LPS331AP_IHL_IRQ_ADDR 0x22 #define ST_PRESS_LPS331AP_IHL_IRQ_MASK 0x80 +#define ST_PRESS_LPS331AP_OD_IRQ_ADDR 0x22 +#define ST_PRESS_LPS331AP_OD_IRQ_MASK 0x40 #define ST_PRESS_LPS331AP_MULTIREAD_BIT true #define ST_PRESS_LPS331AP_TEMP_OFFSET 42500 @@ -104,6 +106,8 @@ #define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10 #define ST_PRESS_LPS25H_IHL_IRQ_ADDR 0x22 #define ST_PRESS_LPS25H_IHL_IRQ_MASK 0x80 +#define ST_PRESS_LPS25H_OD_IRQ_ADDR 0x22 +#define ST_PRESS_LPS25H_OD_IRQ_MASK 0x40 #define ST_PRESS_LPS25H_MULTIREAD_BIT true #define ST_PRESS_LPS25H_TEMP_OFFSET 42500 #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 @@ -226,6 +230,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR, .mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK, + .addr_od = ST_PRESS_LPS331AP_OD_IRQ_ADDR, + .mask_od = ST_PRESS_LPS331AP_OD_IRQ_MASK, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT, @@ -313,6 +319,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK, .addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR, .mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK, + .addr_od = ST_PRESS_LPS25H_OD_IRQ_ADDR, + .mask_od = ST_PRESS_LPS25H_OD_IRQ_MASK, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index d8da075bfda0..d029ffac0d69 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -122,6 +122,8 @@ struct st_sensor_bdu { * @mask_int2: mask to enable/disable IRQ on INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. + * @addr_od: address to enable/disable Open Drain on the INT lines. + * @mask_od: mask to enable/disable Open Drain on the INT lines. * @addr_stat_drdy: address to read status of DRDY (data ready) interrupt * struct ig1 - represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. @@ -133,6 +135,8 @@ struct st_sensor_data_ready_irq { u8 mask_int2; u8 addr_ihl; u8 mask_ihl; + u8 addr_od; + u8 mask_od; u8 addr_stat_drdy; struct { u8 en_addr; @@ -215,6 +219,7 @@ struct st_sensor_settings { * @odr: Output data rate of the sensor [Hz]. * num_data_channels: Number of data channels used in buffer. * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). + * @int_pin_open_drain: Set the interrupt/DRDY to open drain. * @get_irq_data_ready: Function to get the IRQ used for data ready signal. * @tf: Transfer function structure used by I/O operations. * @tb: Transfer buffers and mutex used by I/O operations. @@ -236,6 +241,7 @@ struct st_sensor_data { unsigned int num_data_channels; u8 drdy_int_pin; + bool int_pin_open_drain; unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h index 753839187ba0..79b0e4cdb814 100644 --- a/include/linux/platform_data/st_sensors_pdata.h +++ b/include/linux/platform_data/st_sensors_pdata.h @@ -16,9 +16,11 @@ * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). * Available only for accelerometer and pressure sensors. * Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet). + * @open_drain: set the interrupt line to be open drain if possible. */ struct st_sensors_platform_data { u8 drdy_int_pin; + bool open_drain; }; #endif /* ST_SENSORS_PDATA_H */ -- cgit From 8bf872d8d261feefcdf67027522e3f717cad2bfe Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 6 Apr 2016 16:01:06 +0530 Subject: iio: core: Add devm_ APIs for iio_channel_{get,release} Some of kernel driver uses the IIO framework to get the sensor value via ADC or IIO HW driver. The client driver get iio channel by iio_channel_get() and release it by calling iio_channel_release(). Add resource managed version (devm_*) of these APIs so that if client calls the devm_iio_channel_get() then it need not to release it explicitly, it can be done by managed device framework when driver get un-binded. This reduces the code in error path and also need of .remove callback in some cases. Signed-off-by: Laxman Dewangan Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/consumer.h | 27 +++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 2fc7928f401d..9fd8934c1887 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -356,6 +356,54 @@ void iio_channel_release(struct iio_channel *channel) } EXPORT_SYMBOL_GPL(iio_channel_release); +static void devm_iio_channel_free(struct device *dev, void *res) +{ + struct iio_channel *channel = *(struct iio_channel **)res; + + iio_channel_release(channel); +} + +static int devm_iio_channel_match(struct device *dev, void *res, void *data) +{ + struct iio_channel **r = res; + + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + return *r == data; +} + +struct iio_channel *devm_iio_channel_get(struct device *dev, + const char *channel_name) +{ + struct iio_channel **ptr, *channel; + + ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + channel = iio_channel_get(dev, channel_name); + if (IS_ERR(channel)) { + devres_free(ptr); + return channel; + } + + *ptr = channel; + devres_add(dev, ptr); + + return channel; +} +EXPORT_SYMBOL_GPL(devm_iio_channel_get); + +void devm_iio_channel_release(struct device *dev, struct iio_channel *channel) +{ + WARN_ON(devres_release(dev, devm_iio_channel_free, + devm_iio_channel_match, channel)); +} +EXPORT_SYMBOL_GPL(devm_iio_channel_release); + struct iio_channel *iio_channel_get_all(struct device *dev) { const char *name; diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index fad58671c49e..e1e033d6a81f 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -48,6 +48,33 @@ struct iio_channel *iio_channel_get(struct device *dev, */ void iio_channel_release(struct iio_channel *chan); +/** + * devm_iio_channel_get() - Resource managed version of iio_channel_get(). + * @dev: Pointer to consumer device. Device name must match + * the name of the device as provided in the iio_map + * with which the desired provider to consumer mapping + * was registered. + * @consumer_channel: Unique name to identify the channel on the consumer + * side. This typically describes the channels use within + * the consumer. E.g. 'battery_voltage' + * + * Returns a pointer to negative errno if it is not able to get the iio channel + * otherwise returns valid pointer for iio channel. + * + * The allocated iio channel is automatically released when the device is + * unbound. + */ +struct iio_channel *devm_iio_channel_get(struct device *dev, + const char *consumer_channel); +/** + * devm_iio_channel_release() - Resource managed version of + * iio_channel_release(). + * @dev: Pointer to consumer device for which resource + * is allocared. + * @chan: The channel to be released. + */ +void devm_iio_channel_release(struct device *dev, struct iio_channel *chan); + /** * iio_channel_get_all() - get all channels associated with a client * @dev: Pointer to consumer device. -- cgit From efc2c0133f198bc65593a67015af358919b0c48f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 6 Apr 2016 16:01:07 +0530 Subject: iio: core: Add devm_ APIs for iio_channel_{get,release}_all Some of kernel driver uses the IIO framework to get the sensor value via ADC or IIO HW driver. The client driver get iio channel by iio_channel_get_all() and release it by calling iio_channel_release_all(). Add resource managed version (devm_*) of these APIs so that if client calls the devm_iio_channel_get_all() then it need not to release it explicitly, it can be done by managed device framework when driver get un-binded. This reduces the code in error path and also need of .remove callback in some cases. Signed-off-by: Laxman Dewangan Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/iio/consumer.h | 26 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9fd8934c1887..c4757e6367e7 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -489,6 +489,42 @@ void iio_channel_release_all(struct iio_channel *channels) } EXPORT_SYMBOL_GPL(iio_channel_release_all); +static void devm_iio_channel_free_all(struct device *dev, void *res) +{ + struct iio_channel *channels = *(struct iio_channel **)res; + + iio_channel_release_all(channels); +} + +struct iio_channel *devm_iio_channel_get_all(struct device *dev) +{ + struct iio_channel **ptr, *channels; + + ptr = devres_alloc(devm_iio_channel_free_all, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) { + devres_free(ptr); + return channels; + } + + *ptr = channels; + devres_add(dev, ptr); + + return channels; +} +EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); + +void devm_iio_channel_release_all(struct device *dev, + struct iio_channel *channels) +{ + WARN_ON(devres_release(dev, devm_iio_channel_free_all, + devm_iio_channel_match, channels)); +} +EXPORT_SYMBOL_GPL(devm_iio_channel_release_all); + static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) { diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index e1e033d6a81f..3d672f72e7ec 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -92,6 +92,32 @@ struct iio_channel *iio_channel_get_all(struct device *dev); */ void iio_channel_release_all(struct iio_channel *chan); +/** + * devm_iio_channel_get_all() - Resource managed version of + * iio_channel_get_all(). + * @dev: Pointer to consumer device. + * + * Returns a pointer to negative errno if it is not able to get the iio channel + * otherwise returns an array of iio_channel structures terminated with one with + * null iio_dev pointer. + * + * This function is used by fairly generic consumers to get all the + * channels registered as having this consumer. + * + * The allocated iio channels are automatically released when the device is + * unbounded. + */ +struct iio_channel *devm_iio_channel_get_all(struct device *dev); + +/** + * devm_iio_channel_release_all() - Resource managed version of + * iio_channel_release_all(). + * @dev: Pointer to consumer device for which resource + * is allocared. + * @chan: Array channel to be released. + */ +void devm_iio_channel_release_all(struct device *dev, struct iio_channel *chan); + struct iio_cb_buffer; /** * iio_channel_get_all_cb() - register callback for triggered capture -- cgit From b0fcd8ab7b3c89b5da7fff5224d06ed73e7a33cc Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 23 Mar 2016 11:19:00 +0100 Subject: mtd: nand: add new enum for storing ECC algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our nand_ecc_modes_t is already a bit abused by value NAND_ECC_SOFT_BCH. This enum should store ECC mode only and putting algorithm details there is a bad idea. It would result in too many values impossible to support in a sane way. To solve this problem let's add a new enum. We'll have to modify all drivers to set it properly but once it's done it'll be possible to drop NAND_ECC_SOFT_BCH. That will result in a cleaner design and more possibilities like setting ECC algorithm for hardware ECC mode. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- include/linux/mtd/nand.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 56574ba36555..1b673e19667c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -119,6 +119,12 @@ typedef enum { NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; +enum nand_ecc_algo { + NAND_ECC_UNKNOWN, + NAND_ECC_HAMMING, + NAND_ECC_BCH, +}; + /* * Constants for Hardware ECC */ @@ -458,6 +464,7 @@ struct nand_hw_control { /** * struct nand_ecc_ctrl - Control structure for ECC * @mode: ECC mode + * @algo: ECC algorithm * @steps: number of ECC steps per page * @size: data bytes per ECC step * @bytes: ECC bytes per step @@ -508,6 +515,7 @@ struct nand_hw_control { */ struct nand_ecc_ctrl { nand_ecc_modes_t mode; + enum nand_ecc_algo algo; int steps; int size; int bytes; -- cgit From dd2dcc004230b9d8fa809102cd326e3ee4bbdb2a Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 23 Mar 2016 11:19:01 +0100 Subject: of: mtd: prepare helper reading NAND ECC algo from DT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NAND subsystem is being slightly reworked to store ECC details in separated fields. In future we'll want to add support for more DT properties as specifying every possible setup with a single "nand-ecc-mode" is a pretty bad idea. To allow this let's add a helper that will support something like "nand-ecc-algo" in future. Right now we use it for keeping backward compatibility. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/of/of_mtd.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/of_mtd.h | 6 ++++++ 2 files changed, 42 insertions(+) (limited to 'include/linux') diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index b7361ed70537..15d056e181d2 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -49,6 +49,42 @@ int of_get_nand_ecc_mode(struct device_node *np) } EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); +/** + * of_get_nand_ecc_algo - Get nand ecc algorithm for given device_node + * @np: Pointer to the given device_node + * + * The function gets ecc algorithm and returns its enum value, or errno in error + * case. + */ +int of_get_nand_ecc_algo(struct device_node *np) +{ + const char *pm; + int err; + + /* + * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo. + * It's not implemented yet as currently NAND subsystem ignores + * algorithm explicitly set this way. Once it's handled we should + * document & support new property. + */ + + /* + * For backward compatibility we also read "nand-ecc-mode" checking + * for some obsoleted values that were specifying ECC algorithm. + */ + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + if (!strcasecmp(pm, "soft")) + return NAND_ECC_HAMMING; + else if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_BCH; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_algo); + /** * of_get_nand_ecc_step_size - Get ECC step size associated to * the required ECC strength (see below). diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index e266caa36402..0f6aca5c6f2f 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -13,6 +13,7 @@ #include int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_algo(struct device_node *np); int of_get_nand_ecc_step_size(struct device_node *np); int of_get_nand_ecc_strength(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); @@ -25,6 +26,11 @@ static inline int of_get_nand_ecc_mode(struct device_node *np) return -ENOSYS; } +static inline int of_get_nand_ecc_algo(struct device_node *np) +{ + return -ENOSYS; +} + static inline int of_get_nand_ecc_step_size(struct device_node *np) { return -ENOSYS; -- cgit From 7a654172161c8c9c7d59cbd0054d9e63c7411219 Mon Sep 17 00:00:00 2001 From: Raghav Dogra Date: Wed, 17 Feb 2016 16:54:18 +0530 Subject: mtd/ifc: Add support for IFC controller version 2.0 The new IFC controller version 2.0 has a different memory map page. Upto IFC 1.4 PAGE size is 4 KB and from IFC2.0 PAGE size is 64KB. This patch segregates the IFC global and runtime registers to appropriate PAGE sizes. Signed-off-by: Jaiprakash Singh Signed-off-by: Raghav Dogra Acked-by: Li Yang Signed-off-by: Raghav Dogra Acked-by: Scott Wood Acked-by: Brian Norris Signed-off-by: Boris Brezillon --- drivers/memory/fsl_ifc.c | 36 ++++++++++----------- drivers/mtd/nand/fsl_ifc_nand.c | 72 ++++++++++++++++++++++------------------- include/linux/fsl_ifc.h | 45 +++++++++++++++++--------- 3 files changed, 87 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c index 2a691da8c1c7..904b4af5f142 100644 --- a/drivers/memory/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base) { int i = 0; - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) return -ENODEV; for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { - u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find); static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; /* * Clear all the common status and event registers @@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) irq_dispose_mapping(ctrl->nand_irq); irq_dispose_mapping(ctrl->irq); - iounmap(ctrl->regs); + iounmap(ctrl->gregs); dev_set_drvdata(&dev->dev, NULL); kfree(ctrl); @@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock); static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; unsigned long flags; u32 stat; @@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) { struct fsl_ifc_ctrl *ctrl = data; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; u32 err_axiid, err_srcid, status, cs_err, err_addr; irqreturn_t ret = IRQ_NONE; @@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) { int ret = 0; int version, banks; + void __iomem *addr; dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); @@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); /* IOMAP the entire IFC region */ - fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); - if (!fsl_ifc_ctrl_dev->regs) { + fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0); + if (!fsl_ifc_ctrl_dev->gregs) { dev_err(&dev->dev, "failed to get memory region\n"); ret = -ENODEV; goto err; } - version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) & - FSL_IFC_VERSION_MASK; - banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; - dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", - version >> 24, (version >> 16) & 0xf, banks); - - fsl_ifc_ctrl_dev->version = version; - fsl_ifc_ctrl_dev->banks = banks; - if (of_property_read_bool(dev->dev.of_node, "little-endian")) { fsl_ifc_ctrl_dev->little_endian = true; dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); @@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); } - version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) & + version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) & FSL_IFC_VERSION_MASK; + banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", version >> 24, (version >> 16) & 0xf, banks); @@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) fsl_ifc_ctrl_dev->version = version; fsl_ifc_ctrl_dev->banks = banks; + addr = fsl_ifc_ctrl_dev->gregs; + if (version >= FSL_IFC_VERSION_2_0_0) + addr += PGOFFSET_64K; + else + addr += PGOFFSET_4K; + fsl_ifc_ctrl_dev->rregs = addr; + /* get the Controller level irq */ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); if (fsl_ifc_ctrl_dev->irq == 0) { diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 43f5a3a4873f..f8a016f038cd 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -232,7 +232,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) struct nand_chip *chip = mtd_to_nand(mtd); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; int buf_num; ifc_nand_ctrl->page = page_addr; @@ -295,7 +295,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; u32 eccstat[4]; int i; @@ -371,7 +371,7 @@ static void fsl_ifc_do_read(struct nand_chip *chip, { struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ if (mtd->writesize > 512) { @@ -411,7 +411,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, struct nand_chip *chip = mtd_to_nand(mtd); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; /* clear the read buffer */ ifc_nand_ctrl->read_bytes = 0; @@ -723,7 +723,7 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; u32 nand_fsr; /* Use READ_STATUS command, but wait for the device to be ready */ @@ -825,39 +825,42 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) { struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; uint32_t csor = 0, csor_8k = 0, csor_ext = 0; uint32_t cs = priv->bank; /* Save CSOR and CSOR_ext */ - csor = ifc_in32(&ifc->csor_cs[cs].csor); - csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext); + csor = ifc_in32(&ifc_global->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); /* chage PageSize 8K and SpareSize 1K*/ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - ifc_out32(csor_8k, &ifc->csor_cs[cs].csor); - ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); + ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); /* READID */ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), + &ifc_runtime->ifc_nand.nand_fir0); ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - ifc_out32(0x0, &ifc->ifc_nand.row3); + &ifc_runtime->ifc_nand.nand_fcr0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); - ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); /* Program ROW0/COL0 */ - ifc_out32(0x0, &ifc->ifc_nand.row0); - ifc_out32(0x0, &ifc->ifc_nand.col0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); /* set the chip select for NAND Transaction */ - ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); + ifc_out32(cs << IFC_NAND_CSEL_SHIFT, + &ifc_runtime->ifc_nand.nand_csel); /* start read seq */ - ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, + &ifc_runtime->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -867,14 +870,15 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); /* Restore CSOR and CSOR_ext */ - ifc_out32(csor, &ifc->csor_cs[cs].csor); - ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor, &ifc_global->csor_cs[cs].csor); + ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); } static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) { struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; struct nand_chip *chip = &priv->chip; struct mtd_info *mtd = nand_to_mtd(&priv->chip); struct nand_ecclayout *layout; @@ -886,7 +890,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* fill in nand_chip structure */ /* set up function call table */ - if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) + & CSPR_PORT_SIZE_16) chip->read_byte = fsl_ifc_read_byte16; else chip->read_byte = fsl_ifc_read_byte; @@ -900,13 +905,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; - ifc_out32(0x0, &ifc->ifc_nand.ncfgr); + ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; chip->options = NAND_NO_SUBPAGE_WRITE; - if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) + & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; chip->options |= NAND_BUSWIDTH_16; } else { @@ -919,7 +925,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.read_page = fsl_ifc_read_page; chip->ecc.write_page = fsl_ifc_write_page; - csor = ifc_in32(&ifc->csor_cs[priv->bank].csor); + csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; @@ -1007,10 +1013,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) return 0; } -static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, +static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, phys_addr_t addr) { - u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr); + u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); if (!(cspr & CSPR_V)) return 0; @@ -1024,7 +1030,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex); static int fsl_ifc_nand_probe(struct platform_device *dev) { - struct fsl_ifc_regs __iomem *ifc; + struct fsl_ifc_runtime __iomem *ifc; struct fsl_ifc_mtd *priv; struct resource res; static const char *part_probe_types[] @@ -1034,9 +1040,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) struct device_node *node = dev->dev.of_node; struct mtd_info *mtd; - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) return -ENODEV; - ifc = fsl_ifc_ctrl_dev->regs; + ifc = fsl_ifc_ctrl_dev->rregs; /* get, allocate and map the memory resource */ ret = of_address_to_resource(node, 0, &res); @@ -1047,7 +1053,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) /* find which chip select it is connected to */ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { - if (match_bank(ifc, bank, res.start)) + if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) break; } diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h index 0023088b253b..3f9778cbc79d 100644 --- a/include/linux/fsl_ifc.h +++ b/include/linux/fsl_ifc.h @@ -39,6 +39,10 @@ #define FSL_IFC_VERSION_MASK 0x0F0F0000 #define FSL_IFC_VERSION_1_0_0 0x01000000 #define FSL_IFC_VERSION_1_1_0 0x01010000 +#define FSL_IFC_VERSION_2_0_0 0x02000000 + +#define PGOFFSET_64K (64*1024) +#define PGOFFSET_4K (4*1024) /* * CSPR - Chip Select Property Register @@ -723,20 +727,26 @@ struct fsl_ifc_nand { __be32 nand_evter_en; u32 res17[0x2]; __be32 nand_evter_intr_en; - u32 res18[0x2]; + __be32 nand_vol_addr_stat; + u32 res18; __be32 nand_erattr0; __be32 nand_erattr1; u32 res19[0x10]; __be32 nand_fsr; - u32 res20; - __be32 nand_eccstat[4]; - u32 res21[0x20]; + u32 res20[0x3]; + __be32 nand_eccstat[6]; + u32 res21[0x1c]; __be32 nanndcr; u32 res22[0x2]; __be32 nand_autoboot_trgr; u32 res23; __be32 nand_mdr; - u32 res24[0x5C]; + u32 res24[0x1C]; + __be32 nand_dll_lowcfg0; + __be32 nand_dll_lowcfg1; + u32 res25; + __be32 nand_dll_lowstat; + u32 res26[0x3c]; }; /* @@ -771,13 +781,12 @@ struct fsl_ifc_gpcm { __be32 gpcm_erattr1; __be32 gpcm_erattr2; __be32 gpcm_stat; - u32 res4[0x1F3]; }; /* * IFC Controller Registers */ -struct fsl_ifc_regs { +struct fsl_ifc_global { __be32 ifc_rev; u32 res1[0x2]; struct { @@ -803,21 +812,26 @@ struct fsl_ifc_regs { } ftim_cs[FSL_IFC_BANK_COUNT]; u32 res9[0x30]; __be32 rb_stat; - u32 res10[0x2]; + __be32 rb_map; + __be32 wb_map; __be32 ifc_gcr; - u32 res11[0x2]; + u32 res10[0x2]; __be32 cm_evter_stat; - u32 res12[0x2]; + u32 res11[0x2]; __be32 cm_evter_en; - u32 res13[0x2]; + u32 res12[0x2]; __be32 cm_evter_intr_en; - u32 res14[0x2]; + u32 res13[0x2]; __be32 cm_erattr0; __be32 cm_erattr1; - u32 res15[0x2]; + u32 res14[0x2]; __be32 ifc_ccr; __be32 ifc_csr; - u32 res16[0x2EB]; + __be32 ddr_ccr_low; +}; + + +struct fsl_ifc_runtime { struct fsl_ifc_nand ifc_nand; struct fsl_ifc_nor ifc_nor; struct fsl_ifc_gpcm ifc_gpcm; @@ -831,7 +845,8 @@ extern int fsl_ifc_find(phys_addr_t addr_base); struct fsl_ifc_ctrl { /* device info */ struct device *dev; - struct fsl_ifc_regs __iomem *regs; + struct fsl_ifc_global __iomem *gregs; + struct fsl_ifc_runtime __iomem *rregs; int irq; int nand_irq; spinlock_t lock; -- cgit From 9d02fc2a5129449581c3108c260e96377cf35f7e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 26 Aug 2015 16:08:12 +0200 Subject: mtd: nand: export default read/write oob functions Export the default read/write oob functions (for the standard and syndrome scheme), so that drivers can use them for their raw implementation and implement their own functions for the normal oob operation. This is required if your ECC engine is capable of fixing some of the OOB data. In this case you have to overload the ->read_oob() and ->write_oob(), but if you don't specify the ->read/write_oob_raw() functions they are assigned to the ->read/write_oob() implementation, which is not what you want. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 18 ++++++++++-------- include/linux/mtd/nand.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0f0c5b190316..13fcddc8a10e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1893,13 +1893,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @chip: nand chip info structure * @page: page number to read */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } +EXPORT_SYMBOL(nand_read_oob_std); /** * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC @@ -1908,8 +1908,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to read */ -static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) { int length = mtd->oobsize; int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; @@ -1937,6 +1937,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +EXPORT_SYMBOL(nand_read_oob_syndrome); /** * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function @@ -1944,8 +1945,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status = 0; const uint8_t *buf = chip->oob_poi; @@ -1960,6 +1960,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, return status & NAND_STATUS_FAIL ? -EIO : 0; } +EXPORT_SYMBOL(nand_write_oob_std); /** * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC @@ -1968,8 +1969,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) +int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) { int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size, length = mtd->oobsize; @@ -2019,6 +2020,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, return status & NAND_STATUS_FAIL ? -EIO : 0; } +EXPORT_SYMBOL(nand_write_oob_syndrome); /** * nand_do_read_oob - [INTERN] NAND read out-of-band diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1b673e19667c..7e06afb8552c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1078,4 +1078,18 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen, void *extraoob, int extraooblen, int threshold); + +/* Default write_oob implementation */ +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); + +/* Default write_oob syndrome implementation */ +int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page); + +/* Default read_oob implementation */ +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); + +/* Default read_oob syndrome implementation */ +int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page); #endif /* __LINUX_MTD_NAND_H */ -- cgit From 75eb2cec251fda33c9bb716ecc372819abb9278a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 4 Feb 2016 09:52:30 +0100 Subject: mtd: add mtd_ooblayout_xxx() helper functions In order to make the ecclayout definition completely dynamic we need to rework the way the OOB layout are defined and iterated. Create a few mtd_ooblayout_xxx() helpers to ease OOB bytes manipulation and hide ecclayout internals to their users. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 33 ++++ 2 files changed, 433 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bee180bd11e7..0290c41e44fc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1016,6 +1016,406 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, } EXPORT_SYMBOL_GPL(mtd_write_oob); +/** + * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section + * @mtd: MTD device structure + * @section: ECC section. Depending on the layout you may have all the ECC + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobecc: OOB region struct filled with the appropriate ECC position + * information + * + * This functions return ECC section information in the OOB area. I you want + * to get all the ECC bytes information, then you should call + * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; + + memset(oobecc, 0, sizeof(*oobecc)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + /* + * This logic allows us to reuse the ->ecclayout information and + * expose them as ECC regions (as done for the OOB free regions). + * + * TODO: this should be dropped as soon as we get rid of the + * ->ecclayout field. + */ + for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { + eccpos = mtd->ecclayout->eccpos[eccbyte]; + + if (eccbyte < mtd->ecclayout->eccbytes - 1) { + int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; + + if (eccpos + 1 == neccpos) { + length++; + continue; + } + } + + if (section == cursection) + break; + + length = 0; + cursection++; + } + + if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) + return -ERANGE; + + oobecc->length = length + 1; + oobecc->offset = eccpos - length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); + +/** + * mtd_ooblayout_free - Get the OOB region definition of a specific free + * section + * @mtd: MTD device structure + * @section: Free section you are interested in. Depending on the layout + * you may have all the free bytes stored in a single contiguous + * section, or one section per ECC chunk plus an extra section + * for the remaining bytes (or other funky layout). + * @oobfree: OOB region struct filled with the appropriate free position + * information + * + * This functions return free bytes position in the OOB area. I you want + * to get all the free bytes information, then you should call + * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + memset(oobfree, 0, sizeof(*oobfree)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE) + return -ERANGE; + + oobfree->offset = mtd->ecclayout->oobfree[section].offset; + oobfree->length = mtd->ecclayout->oobfree[section].length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_free); + +/** + * mtd_ooblayout_find_region - Find the region attached to a specific byte + * @mtd: mtd info structure + * @byte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: used to retrieve the ECC position + * @iter: iterator function. Should be either mtd_ooblayout_free or + * mtd_ooblayout_ecc depending on the region type you're searching for + * + * This functions returns the section id and oobregion information of a + * specific byte. For example, say you want to know where the 4th ECC byte is + * stored, you'll use: + * + * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc); + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte, + int *sectionp, struct mtd_oob_region *oobregion, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + int pos = 0, ret, section = 0; + + memset(oobregion, 0, sizeof(*oobregion)); + + while (1) { + ret = iter(mtd, section, oobregion); + if (ret) + return ret; + + if (pos + oobregion->length > byte) + break; + + pos += oobregion->length; + section++; + } + + /* + * Adjust region info to make it start at the beginning at the + * 'start' ECC byte. + */ + oobregion->offset += byte - pos; + oobregion->length -= byte - pos; + *sectionp = section; + + return 0; +} + +/** + * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific + * ECC byte + * @mtd: mtd info structure + * @eccbyte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: OOB region information + * + * Works like mtd_ooblayout_find_region() except it searches for a specific ECC + * byte. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, + int *section, + struct mtd_oob_region *oobregion) +{ + return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion); + +/** + * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer + * @mtd: mtd info structure + * @buf: destination buffer to store OOB bytes + * @oobbuf: OOB buffer + * @start: first byte to retrieve + * @nbytes: number of bytes to retrieve + * @iter: section iterator + * + * Extract bytes attached to a specific category (ECC or free) + * from the OOB buffer and copy them into buf. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, + const u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(buf, oobbuf + oobregion.offset, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer + * @mtd: mtd info structure + * @buf: source buffer to get OOB bytes from + * @oobbuf: OOB buffer + * @start: first OOB byte to set + * @nbytes: number of OOB bytes to set + * @iter: section iterator + * + * Fill the OOB buffer with data provided in buf. The category (ECC or free) + * is selected by passing the appropriate iterator. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, + u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(oobbuf + oobregion.offset, buf, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category + * @mtd: mtd info structure + * @iter: category iterator + * + * Count the number of bytes in a given category. + * + * Returns a positive value on success, a negative error code otherwise. + */ +static int mtd_ooblayout_count_bytes(struct mtd_info *mtd, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret, nbytes = 0; + + while (1) { + ret = iter(mtd, section++, &oobregion); + if (ret) { + if (ret == -ERANGE) + ret = nbytes; + break; + } + + nbytes += oobregion.length; + } + + return ret; +} + +/** + * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer + * @mtd: mtd info structure + * @eccbuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes); + +/** + * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get ECC bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes); + +/** + * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer + * @mtd: mtd info structure + * @databuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes); + +/** + * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get data bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_freebytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index ef9fea4fc400..117ca1ff581d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -108,6 +108,21 @@ struct nand_ecclayout { struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; }; +/** + * struct mtd_oob_region - oob region definition + * @offset: region offset + * @length: region length + * + * This structure describes a region of the OOB area, and is used + * to retrieve ECC or free bytes sections. + * Each section is defined by an offset within the OOB area and a + * length. + */ +struct mtd_oob_region { + u32 offset; + u32 length; +}; + struct module; /* only needed for owner field in mtd_info */ struct mtd_info { @@ -253,6 +268,24 @@ struct mtd_info { int usecount; }; +int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc); +int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, + int *section, + struct mtd_oob_region *oobregion); +int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, + const u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, + u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree); +int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, + const u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, + u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); +int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); + static inline void mtd_set_of_node(struct mtd_info *mtd, struct device_node *np) { -- cgit From 036d6543f85319ffe96afad6de73d3a220917a63 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 18:53:44 +0100 Subject: mtd: add mtd_set_ecclayout() helper function Add an mtd_set_ecclayout() helper function to avoid direct accesses to the mtd->ecclayout field. This will ease future reworks of ECC layout definition. Signed-off-by: Boris Brezillon --- include/linux/mtd/mtd.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 117ca1ff581d..e62da8462493 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -286,6 +286,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); +static inline void mtd_set_ecclayout(struct mtd_info *mtd, + struct nand_ecclayout *ecclayout) +{ + mtd->ecclayout = ecclayout; +} + static inline void mtd_set_of_node(struct mtd_info *mtd, struct device_node *np) { -- cgit From adbbc3bc827eb1f43a932d783f09ba55c8ec8379 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:01:31 +0100 Subject: mtd: create an mtd_ooblayout_ops struct to ease ECC layout definition ECC layout definitions are currently exposed using the nand_ecclayout struct which embeds oobfree and eccpos arrays with predefined size. This approach was acceptable when NAND chips were providing relatively small OOB regions, but MLC and TLC now provide OOB regions of several hundreds of bytes, which implies a non negligible overhead for everybody even those who only need to support legacy NANDs. Create an mtd_ooblayout_ops interface providing the same functionality (expose the ECC and oobfree layout) without the need for this huge structure. The mtd->ecclayout is now deprecated and should be replaced by the equivalent mtd_ooblayout_ops. In the meantime we provide a wrapper around the ->ecclayout field to ease migration to this new model. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdchar.c | 4 +- drivers/mtd/mtdconcat.c | 2 +- drivers/mtd/mtdcore.c | 165 +++++++++++++++++++++++++++++++++++------------- drivers/mtd/mtdpart.c | 23 ++++++- include/linux/mtd/mtd.h | 32 ++++++++-- 5 files changed, 174 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index cd64ab76dd7b..3fad2c7425b0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -888,7 +888,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_oobinfo oi; - if (!mtd->ecclayout) + if (!mtd->ooblayout) return -EOPNOTSUPP; ret = get_oobinfo(mtd, &oi); @@ -982,7 +982,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_ecclayout_user *usrlay; - if (!mtd->ecclayout) + if (!mtd->ooblayout) return -EOPNOTSUPP; usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 481565e5fbaa..d573606b91c2 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } - mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout); + mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout); concat->num_subdev = num_devs; concat->mtd.name = name; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0290c41e44fc..134ed2f7b919 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1035,49 +1035,15 @@ EXPORT_SYMBOL_GPL(mtd_write_oob); int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc) { - int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; - memset(oobecc, 0, sizeof(*oobecc)); if (!mtd || section < 0) return -EINVAL; - if (!mtd->ecclayout) + if (!mtd->ooblayout || !mtd->ooblayout->ecc) return -ENOTSUPP; - /* - * This logic allows us to reuse the ->ecclayout information and - * expose them as ECC regions (as done for the OOB free regions). - * - * TODO: this should be dropped as soon as we get rid of the - * ->ecclayout field. - */ - for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { - eccpos = mtd->ecclayout->eccpos[eccbyte]; - - if (eccbyte < mtd->ecclayout->eccbytes - 1) { - int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; - - if (eccpos + 1 == neccpos) { - length++; - continue; - } - } - - if (section == cursection) - break; - - length = 0; - cursection++; - } - - if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) - return -ERANGE; - - oobecc->length = length + 1; - oobecc->offset = eccpos - length; - - return 0; + return mtd->ooblayout->ecc(mtd, section, oobecc); } EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); @@ -1106,16 +1072,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section, if (!mtd || section < 0) return -EINVAL; - if (!mtd->ecclayout) + if (!mtd->ooblayout || !mtd->ooblayout->free) return -ENOTSUPP; - if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE) - return -ERANGE; - - oobfree->offset = mtd->ecclayout->oobfree[section].offset; - oobfree->length = mtd->ecclayout->oobfree[section].length; - - return 0; + return mtd->ooblayout->free(mtd, section, oobfree); } EXPORT_SYMBOL_GPL(mtd_ooblayout_free); @@ -1416,6 +1376,123 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); +/** + * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation + * @mtd: MTD device structure + * @section: ECC section. Depending on the layout you may have all the ECC + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobecc: OOB region struct filled with the appropriate ECC position + * information + * + * This function is just a wrapper around the mtd->ecclayout field and is + * here to ease the transition to the mtd_ooblayout_ops approach. + * All it does is convert the layout->eccpos information into proper oob + * region definitions. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + /* + * This logic allows us to reuse the ->ecclayout information and + * expose them as ECC regions (as done for the OOB free regions). + * + * TODO: this should be dropped as soon as we get rid of the + * ->ecclayout field. + */ + for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { + eccpos = mtd->ecclayout->eccpos[eccbyte]; + + if (eccbyte < mtd->ecclayout->eccbytes - 1) { + int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; + + if (eccpos + 1 == neccpos) { + length++; + continue; + } + } + + if (section == cursection) + break; + + length = 0; + cursection++; + } + + if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) + return -ERANGE; + + oobecc->length = length + 1; + oobecc->offset = eccpos - length; + + return 0; +} + +/** + * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation + * @mtd: MTD device structure + * @section: Free section. Depending on the layout you may have all the free + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobfree: OOB region struct filled with the appropriate free position + * information + * + * This function is just a wrapper around the mtd->ecclayout field and is + * here to ease the transition to the mtd_ooblayout_ops approach. + * All it does is convert the layout->oobfree information into proper oob + * region definitions. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ecclayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + struct nand_ecclayout *layout = mtd->ecclayout; + + if (!layout) + return -ENOTSUPP; + + if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE || + !layout->oobfree[section].length) + return -ERANGE; + + oobfree->offset = layout->oobfree[section].offset; + oobfree->length = layout->oobfree[section].length; + + return 0; +} + +static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = { + .ecc = mtd_ecclayout_ecc, + .free = mtd_ecclayout_free, +}; + +/** + * mtd_set_ecclayout - Attach an ecclayout to an MTD device + * @mtd: MTD device structure + * @ecclayout: The ecclayout to attach to the device + * + * Returns zero on success, a negative error code otherwise. + */ +void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout) +{ + if (!mtd || !ecclayout) + return; + + mtd->ecclayout = ecclayout; + mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops); +} +EXPORT_SYMBOL_GPL(mtd_set_ecclayout); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f53d9d72b23a..1f13e32556f8 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) return res; } +static int part_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct mtd_part *part = mtd_to_part(mtd); + + return mtd_ooblayout_ecc(part->master, section, oobregion); +} + +static int part_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct mtd_part *part = mtd_to_part(mtd); + + return mtd_ooblayout_free(part->master, section, oobregion); +} + +static const struct mtd_ooblayout_ops part_ooblayout_ops = { + .ecc = part_ooblayout_ecc, + .free = part_ooblayout_free, +}; + static inline void free_partition(struct mtd_part *p) { kfree(p->mtd.name); @@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, part->name); } - mtd_set_ecclayout(&slave->mtd, master->ecclayout); + mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops); slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index e62da8462493..177bf314ad70 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -101,6 +101,9 @@ struct mtd_oob_ops { * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained * for export to user-space via the ECCGETLAYOUT ioctl. * nand_ecclayout should be expandable in the future simply by the above macros. + * + * This structure is now deprecated, you should use struct nand_ecclayout_ops + * to describe your OOB layout. */ struct nand_ecclayout { __u32 eccbytes; @@ -123,6 +126,22 @@ struct mtd_oob_region { u32 length; }; +/* + * struct mtd_ooblayout_ops - NAND OOB layout operations + * @ecc: function returning an ECC region in the OOB area. + * Should return -ERANGE if %section exceeds the total number of + * ECC sections. + * @free: function returning a free region in the OOB area. + * Should return -ERANGE if %section exceeds the total number of + * free sections. + */ +struct mtd_ooblayout_ops { + int (*ecc)(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc); + int (*free)(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree); +}; + struct module; /* only needed for owner field in mtd_info */ struct mtd_info { @@ -181,9 +200,12 @@ struct mtd_info { const char *name; int index; - /* ECC layout structure pointer - read only! */ + /* [Deprecated] ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; + /* OOB layout description */ + const struct mtd_ooblayout_ops *ooblayout; + /* the ecc step size. */ unsigned int ecc_step_size; @@ -286,10 +308,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); -static inline void mtd_set_ecclayout(struct mtd_info *mtd, - struct nand_ecclayout *ecclayout) +void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout); + +static inline void mtd_set_ooblayout(struct mtd_info *mtd, + const struct mtd_ooblayout_ops *ooblayout) { - mtd->ecclayout = ecclayout; + mtd->ooblayout = ooblayout; } static inline void mtd_set_of_node(struct mtd_info *mtd, -- cgit From 41b207a70d3a86b9e2eede155e87838234c7cbd5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:06:15 +0100 Subject: mtd: nand: implement the default mtd_ooblayout_ops Replace the default nand_ecclayout definitions for large and small page devices with the equivalent mtd_ooblayout_ops. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 142 ++++++++++++++++++++++++++++--------------- include/linux/mtd/nand.h | 3 + 2 files changed, 96 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index feb1448e9dd6..e8332ea45739 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -47,54 +47,96 @@ #include #include +static int nand_get_device(struct mtd_info *mtd, int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + /* Define default oob placement schemes for large and small page devices */ -static struct nand_ecclayout nand_oob_8 = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - {.offset = 3, - .length = 2}, - {.offset = 6, - .length = 2} } -}; +static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; -static struct nand_ecclayout nand_oob_16 = { - .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = { - {.offset = 8, - . length = 8} } -}; + if (section > 1) + return -ERANGE; -static struct nand_ecclayout nand_oob_64 = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 38} } -}; + if (!section) { + oobregion->offset = 0; + oobregion->length = 4; + } else { + oobregion->offset = 6; + oobregion->length = ecc->total - 4; + } -static struct nand_ecclayout nand_oob_128 = { - .eccbytes = 48, - .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { - {.offset = 2, - .length = 78} } + return 0; +} + +static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (mtd->oobsize == 16) { + if (section) + return -ERANGE; + + oobregion->length = 8; + oobregion->offset = 8; + } else { + oobregion->length = 2; + if (!section) + oobregion->offset = 3; + else + oobregion->offset = 6; + } + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { + .ecc = nand_ooblayout_ecc_sp, + .free = nand_ooblayout_free_sp, }; +EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); -static int nand_get_device(struct mtd_info *mtd, int new_state); +static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); + if (section) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 2; + oobregion->offset = 2; + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { + .ecc = nand_ooblayout_ecc_lp, + .free = nand_ooblayout_free_lp, +}; +EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -4109,22 +4151,25 @@ int nand_scan_tail(struct mtd_info *mtd) /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; + /* + * Set the provided ECC layout. If ecc->layout is NULL, the MTD core + * will just leave mtd->ooblayout to NULL, if it's not NULL, it will + * set ->ooblayout to the default ecclayout wrapper. + */ + mtd_set_ecclayout(mtd, ecc->layout); + /* * If no default placement scheme is given, select an appropriate one. */ - if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { + if (!mtd->ooblayout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: - ecc->layout = &nand_oob_8; - break; case 16: - ecc->layout = &nand_oob_16; + mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); break; case 64: - ecc->layout = &nand_oob_64; - break; case 128: - ecc->layout = &nand_oob_128; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); break; default: WARN(1, "No oob scheme defined for oobsize %d\n", @@ -4285,7 +4330,6 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->write_oob_raw = ecc->write_oob; /* propagate ecc info to mtd_info */ - mtd_set_ecclayout(mtd, ecc->layout); mtd->ecc_strength = ecc->strength; mtd->ecc_step_size = ecc->size; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7e06afb8552c..f2ded7b1b3b8 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -748,6 +748,9 @@ struct nand_chip { void *priv; }; +extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; +extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; + static inline void nand_set_flash_node(struct nand_chip *chip, struct device_node *np) { -- cgit From 74e1fbb1375a3ede3e17da22911761ce9bc8f53f Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Apr 2016 17:49:17 +0200 Subject: of: Introduce struct of_phandle_iterator This struct carrys all necessary information to iterate over a list of phandles and extract the arguments. Add an init-function for the iterator and make use of it in __of_parse_phandle_with_args(). Signed-off-by: Joerg Roedel Signed-off-by: Rob Herring --- drivers/of/base.c | 99 +++++++++++++++++++++++++++++++++--------------------- include/linux/of.h | 33 ++++++++++++++++++ 2 files changed, 93 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index b299de2b3afa..1c6f43b5737d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1440,35 +1440,56 @@ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) printk("\n"); } +int of_phandle_iterator_init(struct of_phandle_iterator *it, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count) +{ + const __be32 *list; + int size; + + memset(it, 0, sizeof(*it)); + + list = of_get_property(np, list_name, &size); + if (!list) + return -ENOENT; + + it->cells_name = cells_name; + it->cell_count = cell_count; + it->parent = np; + it->list_end = list + size / sizeof(*list); + it->phandle_end = list; + it->cur = list; + + return 0; +} + static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int cell_count, int index, struct of_phandle_args *out_args) { - const __be32 *list, *list_end; - int rc = 0, size, cur_index = 0; - uint32_t count = 0; - struct device_node *node = NULL; - phandle phandle; + struct of_phandle_iterator it; + int rc, cur_index = 0; - /* Retrieve the phandle list property */ - list = of_get_property(np, list_name, &size); - if (!list) - return -ENOENT; - list_end = list + size / sizeof(*list); + rc = of_phandle_iterator_init(&it, np, list_name, + cells_name, cell_count); + if (rc) + return rc; /* Loop over the phandles until all the requested entry is found */ - while (list < list_end) { + while (it.cur < it.list_end) { rc = -EINVAL; - count = 0; + it.cur_count = 0; /* * If phandle is 0, then it is an empty entry with no * arguments. Skip forward to the next entry. */ - phandle = be32_to_cpup(list++); - if (phandle) { + it.phandle = be32_to_cpup(it.cur++); + if (it.phandle) { /* * Find the provider node and parse the #*-cells * property to determine the argument length. @@ -1478,34 +1499,34 @@ static int __of_parse_phandle_with_args(const struct device_node *np, * except when we're going to return the found node * below. */ - if (cells_name || cur_index == index) { - node = of_find_node_by_phandle(phandle); - if (!node) { + if (it.cells_name || cur_index == index) { + it.node = of_find_node_by_phandle(it.phandle); + if (!it.node) { pr_err("%s: could not find phandle\n", - np->full_name); + it.parent->full_name); goto err; } } - if (cells_name) { - if (of_property_read_u32(node, cells_name, - &count)) { + if (it.cells_name) { + if (of_property_read_u32(it.node, it.cells_name, + &it.cur_count)) { pr_err("%s: could not get %s for %s\n", - np->full_name, cells_name, - node->full_name); + it.parent->full_name, it.cells_name, + it.node->full_name); goto err; } } else { - count = cell_count; + it.cur_count = it.cell_count; } /* * Make sure that the arguments actually fit in the * remaining property data length */ - if (list + count > list_end) { + if (it.cur + it.cur_count > it.list_end) { pr_err("%s: arguments longer than property\n", - np->full_name); + it.parent->full_name); goto err; } } @@ -1518,28 +1539,28 @@ static int __of_parse_phandle_with_args(const struct device_node *np, */ rc = -ENOENT; if (cur_index == index) { - if (!phandle) + if (!it.phandle) goto err; if (out_args) { int i; - if (WARN_ON(count > MAX_PHANDLE_ARGS)) - count = MAX_PHANDLE_ARGS; - out_args->np = node; - out_args->args_count = count; - for (i = 0; i < count; i++) - out_args->args[i] = be32_to_cpup(list++); + if (WARN_ON(it.cur_count > MAX_PHANDLE_ARGS)) + it.cur_count = MAX_PHANDLE_ARGS; + out_args->np = it.node; + out_args->args_count = it.cur_count; + for (i = 0; i < it.cur_count; i++) + out_args->args[i] = be32_to_cpup(it.cur++); } else { - of_node_put(node); + of_node_put(it.node); } /* Found it! return success */ return 0; } - of_node_put(node); - node = NULL; - list += count; + of_node_put(it.node); + it.node = NULL; + it.cur += it.cur_count; cur_index++; } @@ -1551,8 +1572,8 @@ static int __of_parse_phandle_with_args(const struct device_node *np, */ rc = index < 0 ? cur_index : -ENOENT; err: - if (node) - of_node_put(node); + if (it.node) + of_node_put(it.node); return rc; } diff --git a/include/linux/of.h b/include/linux/of.h index 7fcb681baadf..0f187dbb890b 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -75,6 +75,23 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; +struct of_phandle_iterator { + /* Common iterator information */ + const char *cells_name; + int cell_count; + const struct device_node *parent; + + /* List size information */ + const __be32 *list_end; + const __be32 *phandle_end; + + /* Current position state */ + const __be32 *cur; + uint32_t cur_count; + phandle phandle; + struct device_node *node; +}; + struct of_reconfig_data { struct device_node *dn; struct property *prop; @@ -334,6 +351,13 @@ extern int of_parse_phandle_with_fixed_args(const struct device_node *np, extern int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name); +/* phandle iterator functions */ +extern int of_phandle_iterator_init(struct of_phandle_iterator *it, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count); + extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); @@ -608,6 +632,15 @@ static inline int of_count_phandle_with_args(struct device_node *np, return -ENOSYS; } +static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count) +{ + return -ENOSYS; +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; -- cgit From cd209b412c8a5d632b51af1e45576f0d00b8105f Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Apr 2016 17:49:18 +0200 Subject: of: Move phandle walking to of_phandle_iterator_next() Move the code to walk over the phandles out of the loop in __of_parse_phandle_with_args() to a separate function that just works with the iterator handle: of_phandle_iterator_next(). Signed-off-by: Joerg Roedel Signed-off-by: Rob Herring --- drivers/of/base.c | 130 ++++++++++++++++++++++++++++++----------------------- include/linux/of.h | 7 +++ 2 files changed, 81 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index 1c6f43b5737d..69286ec206f7 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1465,6 +1465,75 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it, return 0; } +int of_phandle_iterator_next(struct of_phandle_iterator *it) +{ + uint32_t count = 0; + + if (it->node) { + of_node_put(it->node); + it->node = NULL; + } + + if (!it->cur || it->phandle_end >= it->list_end) + return -ENOENT; + + it->cur = it->phandle_end; + + /* If phandle is 0, then it is an empty entry with no arguments. */ + it->phandle = be32_to_cpup(it->cur++); + + if (it->phandle) { + + /* + * Find the provider node and parse the #*-cells property to + * determine the argument length. + */ + it->node = of_find_node_by_phandle(it->phandle); + + if (it->cells_name) { + if (!it->node) { + pr_err("%s: could not find phandle\n", + it->parent->full_name); + goto err; + } + + if (of_property_read_u32(it->node, it->cells_name, + &count)) { + pr_err("%s: could not get %s for %s\n", + it->parent->full_name, + it->cells_name, + it->node->full_name); + goto err; + } + } else { + count = it->cell_count; + } + + /* + * Make sure that the arguments actually fit in the remaining + * property data length + */ + if (it->cur + count > it->list_end) { + pr_err("%s: arguments longer than property\n", + it->parent->full_name); + goto err; + } + } + + it->phandle_end = it->cur + count; + it->cur_count = count; + + return 0; + +err: + if (it->node) { + of_node_put(it->node); + it->node = NULL; + } + + return -EINVAL; +} + static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, @@ -1480,59 +1549,9 @@ static int __of_parse_phandle_with_args(const struct device_node *np, return rc; /* Loop over the phandles until all the requested entry is found */ - while (it.cur < it.list_end) { - rc = -EINVAL; - it.cur_count = 0; - - /* - * If phandle is 0, then it is an empty entry with no - * arguments. Skip forward to the next entry. - */ - it.phandle = be32_to_cpup(it.cur++); - if (it.phandle) { - /* - * Find the provider node and parse the #*-cells - * property to determine the argument length. - * - * This is not needed if the cell count is hard-coded - * (i.e. cells_name not set, but cell_count is set), - * except when we're going to return the found node - * below. - */ - if (it.cells_name || cur_index == index) { - it.node = of_find_node_by_phandle(it.phandle); - if (!it.node) { - pr_err("%s: could not find phandle\n", - it.parent->full_name); - goto err; - } - } - - if (it.cells_name) { - if (of_property_read_u32(it.node, it.cells_name, - &it.cur_count)) { - pr_err("%s: could not get %s for %s\n", - it.parent->full_name, it.cells_name, - it.node->full_name); - goto err; - } - } else { - it.cur_count = it.cell_count; - } - - /* - * Make sure that the arguments actually fit in the - * remaining property data length - */ - if (it.cur + it.cur_count > it.list_end) { - pr_err("%s: arguments longer than property\n", - it.parent->full_name); - goto err; - } - } - + while ((rc = of_phandle_iterator_next(&it)) == 0) { /* - * All of the error cases above bail out of the loop, so at + * All of the error cases bail out of the loop, so at * this point, the parsing is successful. If the requested * index matches, then fill the out_args structure and return, * or return -ENOENT for an empty entry. @@ -1558,9 +1577,6 @@ static int __of_parse_phandle_with_args(const struct device_node *np, return 0; } - of_node_put(it.node); - it.node = NULL; - it.cur += it.cur_count; cur_index++; } @@ -1570,7 +1586,9 @@ static int __of_parse_phandle_with_args(const struct device_node *np, * -EINVAL : parsing error on data * [1..n] : Number of phandle (count mode; when index = -1) */ - rc = index < 0 ? cur_index : -ENOENT; + if (rc == -ENOENT && index < 0) + rc = cur_index; + err: if (it.node) of_node_put(it.node); diff --git a/include/linux/of.h b/include/linux/of.h index 0f187dbb890b..1f5e108f6716 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -358,6 +358,8 @@ extern int of_phandle_iterator_init(struct of_phandle_iterator *it, const char *cells_name, int cell_count); +extern int of_phandle_iterator_next(struct of_phandle_iterator *it); + extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); @@ -641,6 +643,11 @@ static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, return -ENOSYS; } +static inline int of_phandle_iterator_next(struct of_phandle_iterator *it) +{ + return -ENOSYS; +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; -- cgit From f623ce95a51baee6a6638f0b025efc0229a9ac0d Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Apr 2016 17:49:20 +0200 Subject: of: Introduce of_for_each_phandle() helper macro With this macro any user can easily iterate over a list of phandles. The patch also converts __of_parse_phandle_with_args() to make use of the macro. The of_count_phandle_with_args() function is not converted, because the macro hides the return value of of_phandle_iterator_init(), which is needed in there. Signed-off-by: Joerg Roedel Signed-off-by: Rob Herring --- drivers/of/base.c | 7 +------ include/linux/of.h | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index fcff2b62ec10..ea5a13d3c5a5 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1543,13 +1543,8 @@ static int __of_parse_phandle_with_args(const struct device_node *np, struct of_phandle_iterator it; int rc, cur_index = 0; - rc = of_phandle_iterator_init(&it, np, list_name, - cells_name, cell_count); - if (rc) - return rc; - /* Loop over the phandles until all the requested entry is found */ - while ((rc = of_phandle_iterator_next(&it)) == 0) { + of_for_each_phandle(&it, rc, np, list_name, cells_name, cell_count) { /* * All of the error cases bail out of the loop, so at * this point, the parsing is successful. If the requested diff --git a/include/linux/of.h b/include/linux/of.h index 1f5e108f6716..b0b80716fbfb 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -908,6 +908,12 @@ static inline int of_property_read_s32(const struct device_node *np, return of_property_read_u32(np, propname, (u32*) out_value); } +#define of_for_each_phandle(it, err, np, ln, cn, cc) \ + for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)), \ + err = of_phandle_iterator_next(it); \ + err == 0; \ + err = of_phandle_iterator_next(it)) + #define of_property_for_each_u32(np, propname, prop, p, u) \ for (prop = of_find_property(np, propname, NULL), \ p = of_prop_next_u32(prop, NULL, &u); \ -- cgit From abdaa77b18480361f3565d958a2acffad268c39c Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Apr 2016 17:49:21 +0200 Subject: of: Introduce of_phandle_iterator_args() This helper function can be used to copy the arguments of a phandle to an array. Signed-off-by: Joerg Roedel Signed-off-by: Rob Herring --- drivers/of/base.c | 29 +++++++++++++++++++++++------ include/linux/of.h | 10 ++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index ea5a13d3c5a5..e87e21df19d8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1534,6 +1534,23 @@ err: return -EINVAL; } +int of_phandle_iterator_args(struct of_phandle_iterator *it, + uint32_t *args, + int size) +{ + int i, count; + + count = it->cur_count; + + if (WARN_ON(size < count)) + count = size; + + for (i = 0; i < count; i++) + args[i] = be32_to_cpup(it->cur++); + + return count; +} + static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, @@ -1557,13 +1574,13 @@ static int __of_parse_phandle_with_args(const struct device_node *np, goto err; if (out_args) { - int i; - if (WARN_ON(it.cur_count > MAX_PHANDLE_ARGS)) - it.cur_count = MAX_PHANDLE_ARGS; + int c; + + c = of_phandle_iterator_args(&it, + out_args->args, + MAX_PHANDLE_ARGS); out_args->np = it.node; - out_args->args_count = it.cur_count; - for (i = 0; i < it.cur_count; i++) - out_args->args[i] = be32_to_cpup(it.cur++); + out_args->args_count = c; } else { of_node_put(it.node); } diff --git a/include/linux/of.h b/include/linux/of.h index b0b80716fbfb..71e1c35a5960 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -359,6 +359,9 @@ extern int of_phandle_iterator_init(struct of_phandle_iterator *it, int cell_count); extern int of_phandle_iterator_next(struct of_phandle_iterator *it); +extern int of_phandle_iterator_args(struct of_phandle_iterator *it, + uint32_t *args, + int size); extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); @@ -648,6 +651,13 @@ static inline int of_phandle_iterator_next(struct of_phandle_iterator *it) return -ENOSYS; } +static inline int of_phandle_iterator_args(struct of_phandle_iterator *it, + uint32_t *args, + int size) +{ + return 0; +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; -- cgit From d0bad49bb0a094a1beb06640785f95cb256b7272 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 3 Mar 2016 12:54:55 -0600 Subject: tracing: Add enable_hist/disable_hist triggers Similar to enable_event/disable_event triggers, these triggers enable and disable the aggregation of events into maps rather than enabling and disabling their writing into the trace buffer. They can be used to automatically start and stop hist triggers based on a matching filter condition. If there's a paused hist trigger on system:event, the following would start it when the filter condition was hit: # echo enable_hist:system:event [ if filter] > event/trigger And the following would disable a running system:event hist trigger: # echo disable_hist:system:event [ if filter] > event/trigger See Documentation/trace/events.txt for real examples. Link: http://lkml.kernel.org/r/f812f086e52c8b7c8ad5443487375e03c96a601f.1457029949.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi Tested-by: Masami Hiramatsu Reviewed-by: Namhyung Kim Signed-off-by: Steven Rostedt --- include/linux/trace_events.h | 1 + kernel/trace/trace.c | 8 +++ kernel/trace/trace.h | 32 ++++++++++ kernel/trace/trace_events_hist.c | 115 ++++++++++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 71 ++++++++++++---------- 5 files changed, 196 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 404603720650..5f89a5b0c7e6 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -408,6 +408,7 @@ enum event_trigger_type { ETT_STACKTRACE = (1 << 2), ETT_EVENT_ENABLE = (1 << 3), ETT_EVENT_HIST = (1 << 4), + ETT_HIST_ENABLE = (1 << 5), }; extern int filter_match_preds(struct event_filter *filter, void *rec); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 2238bfde799b..8430145bea12 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3807,6 +3807,10 @@ static const char readme_msg[] = "\t trigger: traceon, traceoff\n" "\t enable_event::\n" "\t disable_event::\n" +#ifdef CONFIG_HIST_TRIGGERS + "\t enable_hist::\n" + "\t disable_hist::\n" +#endif #ifdef CONFIG_STACKTRACE "\t\t stacktrace\n" #endif @@ -3867,6 +3871,10 @@ static const char readme_msg[] = "\t The 'clear' parameter will clear the contents of a running\n" "\t hist trigger and leave its current paused/active state\n" "\t unchanged.\n\n" + "\t The enable_hist and disable_hist triggers can be used to\n" + "\t have one event conditionally start and stop another event's\n" + "\t already-attached hist trigger. The syntax is analagous to\n" + "\t the enable_event and disable_event triggers.\n" #endif ; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 505f8a45f426..cab1f4bfe85b 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1166,8 +1166,10 @@ extern const struct file_operations event_hist_fops; #ifdef CONFIG_HIST_TRIGGERS extern int register_trigger_hist_cmd(void); +extern int register_trigger_hist_enable_disable_cmds(void); #else static inline int register_trigger_hist_cmd(void) { return 0; } +static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; } #endif extern int register_trigger_cmds(void); @@ -1185,6 +1187,34 @@ struct event_trigger_data { struct list_head list; }; +/* Avoid typos */ +#define ENABLE_EVENT_STR "enable_event" +#define DISABLE_EVENT_STR "disable_event" +#define ENABLE_HIST_STR "enable_hist" +#define DISABLE_HIST_STR "disable_hist" + +struct enable_trigger_data { + struct trace_event_file *file; + bool enable; + bool hist; +}; + +extern int event_enable_trigger_print(struct seq_file *m, + struct event_trigger_ops *ops, + struct event_trigger_data *data); +extern void event_enable_trigger_free(struct event_trigger_ops *ops, + struct event_trigger_data *data); +extern int event_enable_trigger_func(struct event_command *cmd_ops, + struct trace_event_file *file, + char *glob, char *cmd, char *param); +extern int event_enable_register_trigger(char *glob, + struct event_trigger_ops *ops, + struct event_trigger_data *data, + struct trace_event_file *file); +extern void event_enable_unregister_trigger(char *glob, + struct event_trigger_ops *ops, + struct event_trigger_data *test, + struct trace_event_file *file); extern void trigger_data_free(struct event_trigger_data *data); extern int event_trigger_init(struct event_trigger_ops *ops, struct event_trigger_data *data); @@ -1198,6 +1228,8 @@ extern int set_trigger_filter(char *filter_str, struct event_trigger_data *trigger_data, struct trace_event_file *file); extern int register_event_command(struct event_command *cmd); +extern int unregister_event_command(struct event_command *cmd); +extern int register_trigger_hist_enable_disable_cmds(void); /** * struct event_trigger_ops - callbacks for trace event triggers diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 4f4041d76926..5d4f02792440 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -1393,3 +1393,118 @@ __init int register_trigger_hist_cmd(void) return ret; } + +static void +hist_enable_trigger(struct event_trigger_data *data, void *rec) +{ + struct enable_trigger_data *enable_data = data->private_data; + struct event_trigger_data *test; + + list_for_each_entry_rcu(test, &enable_data->file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + if (enable_data->enable) + test->paused = false; + else + test->paused = true; + break; + } + } +} + +static void +hist_enable_count_trigger(struct event_trigger_data *data, void *rec) +{ + if (!data->count) + return; + + if (data->count != -1) + (data->count)--; + + hist_enable_trigger(data, rec); +} + +static struct event_trigger_ops hist_enable_trigger_ops = { + .func = hist_enable_trigger, + .print = event_enable_trigger_print, + .init = event_trigger_init, + .free = event_enable_trigger_free, +}; + +static struct event_trigger_ops hist_enable_count_trigger_ops = { + .func = hist_enable_count_trigger, + .print = event_enable_trigger_print, + .init = event_trigger_init, + .free = event_enable_trigger_free, +}; + +static struct event_trigger_ops hist_disable_trigger_ops = { + .func = hist_enable_trigger, + .print = event_enable_trigger_print, + .init = event_trigger_init, + .free = event_enable_trigger_free, +}; + +static struct event_trigger_ops hist_disable_count_trigger_ops = { + .func = hist_enable_count_trigger, + .print = event_enable_trigger_print, + .init = event_trigger_init, + .free = event_enable_trigger_free, +}; + +static struct event_trigger_ops * +hist_enable_get_trigger_ops(char *cmd, char *param) +{ + struct event_trigger_ops *ops; + bool enable; + + enable = (strcmp(cmd, ENABLE_HIST_STR) == 0); + + if (enable) + ops = param ? &hist_enable_count_trigger_ops : + &hist_enable_trigger_ops; + else + ops = param ? &hist_disable_count_trigger_ops : + &hist_disable_trigger_ops; + + return ops; +} + +static struct event_command trigger_hist_enable_cmd = { + .name = ENABLE_HIST_STR, + .trigger_type = ETT_HIST_ENABLE, + .func = event_enable_trigger_func, + .reg = event_enable_register_trigger, + .unreg = event_enable_unregister_trigger, + .get_trigger_ops = hist_enable_get_trigger_ops, + .set_filter = set_trigger_filter, +}; + +static struct event_command trigger_hist_disable_cmd = { + .name = DISABLE_HIST_STR, + .trigger_type = ETT_HIST_ENABLE, + .func = event_enable_trigger_func, + .reg = event_enable_register_trigger, + .unreg = event_enable_unregister_trigger, + .get_trigger_ops = hist_enable_get_trigger_ops, + .set_filter = set_trigger_filter, +}; + +static __init void unregister_trigger_hist_enable_disable_cmds(void) +{ + unregister_event_command(&trigger_hist_enable_cmd); + unregister_event_command(&trigger_hist_disable_cmd); +} + +__init int register_trigger_hist_enable_disable_cmds(void) +{ + int ret; + + ret = register_event_command(&trigger_hist_enable_cmd); + if (WARN_ON(ret < 0)) + return ret; + ret = register_event_command(&trigger_hist_disable_cmd); + if (WARN_ON(ret < 0)) + unregister_trigger_hist_enable_disable_cmds(); + + return ret; +} diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index d29092afe005..d133f2094566 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -347,7 +347,7 @@ __init int register_event_command(struct event_command *cmd) * Currently we only unregister event commands from __init, so mark * this __init too. */ -static __init int unregister_event_command(struct event_command *cmd) +__init int unregister_event_command(struct event_command *cmd) { struct event_command *p, *n; int ret = -ENODEV; @@ -1062,15 +1062,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void) unregister_event_command(&trigger_traceoff_cmd); } -/* Avoid typos */ -#define ENABLE_EVENT_STR "enable_event" -#define DISABLE_EVENT_STR "disable_event" - -struct enable_trigger_data { - struct trace_event_file *file; - bool enable; -}; - static void event_enable_trigger(struct event_trigger_data *data, void *rec) { @@ -1100,14 +1091,16 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec) event_enable_trigger(data, rec); } -static int -event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, - struct event_trigger_data *data) +int event_enable_trigger_print(struct seq_file *m, + struct event_trigger_ops *ops, + struct event_trigger_data *data) { struct enable_trigger_data *enable_data = data->private_data; seq_printf(m, "%s:%s:%s", - enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR, + enable_data->hist ? + (enable_data->enable ? ENABLE_HIST_STR : DISABLE_HIST_STR) : + (enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR), enable_data->file->event_call->class->system, trace_event_name(enable_data->file->event_call)); @@ -1124,9 +1117,8 @@ event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, return 0; } -static void -event_enable_trigger_free(struct event_trigger_ops *ops, - struct event_trigger_data *data) +void event_enable_trigger_free(struct event_trigger_ops *ops, + struct event_trigger_data *data) { struct enable_trigger_data *enable_data = data->private_data; @@ -1171,10 +1163,9 @@ static struct event_trigger_ops event_disable_count_trigger_ops = { .free = event_enable_trigger_free, }; -static int -event_enable_trigger_func(struct event_command *cmd_ops, - struct trace_event_file *file, - char *glob, char *cmd, char *param) +int event_enable_trigger_func(struct event_command *cmd_ops, + struct trace_event_file *file, + char *glob, char *cmd, char *param) { struct trace_event_file *event_enable_file; struct enable_trigger_data *enable_data; @@ -1183,6 +1174,7 @@ event_enable_trigger_func(struct event_command *cmd_ops, struct trace_array *tr = file->tr; const char *system; const char *event; + bool hist = false; char *trigger; char *number; bool enable; @@ -1207,8 +1199,15 @@ event_enable_trigger_func(struct event_command *cmd_ops, if (!event_enable_file) goto out; - enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; +#ifdef CONFIG_HIST_TRIGGERS + hist = ((strcmp(cmd, ENABLE_HIST_STR) == 0) || + (strcmp(cmd, DISABLE_HIST_STR) == 0)); + enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) || + (strcmp(cmd, ENABLE_HIST_STR) == 0)); +#else + enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; +#endif trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); ret = -ENOMEM; @@ -1228,6 +1227,7 @@ event_enable_trigger_func(struct event_command *cmd_ops, INIT_LIST_HEAD(&trigger_data->list); RCU_INIT_POINTER(trigger_data->filter, NULL); + enable_data->hist = hist; enable_data->enable = enable; enable_data->file = event_enable_file; trigger_data->private_data = enable_data; @@ -1305,10 +1305,10 @@ event_enable_trigger_func(struct event_command *cmd_ops, goto out; } -static int event_enable_register_trigger(char *glob, - struct event_trigger_ops *ops, - struct event_trigger_data *data, - struct trace_event_file *file) +int event_enable_register_trigger(char *glob, + struct event_trigger_ops *ops, + struct event_trigger_data *data, + struct trace_event_file *file) { struct enable_trigger_data *enable_data = data->private_data; struct enable_trigger_data *test_enable_data; @@ -1318,6 +1318,8 @@ static int event_enable_register_trigger(char *glob, list_for_each_entry_rcu(test, &file->triggers, list) { test_enable_data = test->private_data; if (test_enable_data && + (test->cmd_ops->trigger_type == + data->cmd_ops->trigger_type) && (test_enable_data->file == enable_data->file)) { ret = -EEXIST; goto out; @@ -1343,10 +1345,10 @@ out: return ret; } -static void event_enable_unregister_trigger(char *glob, - struct event_trigger_ops *ops, - struct event_trigger_data *test, - struct trace_event_file *file) +void event_enable_unregister_trigger(char *glob, + struct event_trigger_ops *ops, + struct event_trigger_data *test, + struct trace_event_file *file) { struct enable_trigger_data *test_enable_data = test->private_data; struct enable_trigger_data *enable_data; @@ -1356,6 +1358,8 @@ static void event_enable_unregister_trigger(char *glob, list_for_each_entry_rcu(data, &file->triggers, list) { enable_data = data->private_data; if (enable_data && + (data->cmd_ops->trigger_type == + test->cmd_ops->trigger_type) && (enable_data->file == test_enable_data->file)) { unregistered = true; list_del_rcu(&data->list); @@ -1375,8 +1379,12 @@ event_enable_get_trigger_ops(char *cmd, char *param) struct event_trigger_ops *ops; bool enable; +#ifdef CONFIG_HIST_TRIGGERS + enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) || + (strcmp(cmd, ENABLE_HIST_STR) == 0)); +#else enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; - +#endif if (enable) ops = param ? &event_enable_count_trigger_ops : &event_enable_trigger_ops; @@ -1447,6 +1455,7 @@ __init int register_trigger_cmds(void) register_trigger_snapshot_cmd(); register_trigger_stacktrace_cmd(); register_trigger_enable_disable_cmds(); + register_trigger_hist_enable_disable_cmds(); register_trigger_hist_cmd(); return 0; -- cgit From c1d61c9bb163e696bf06850bcabbd26386554489 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 31 Mar 2016 16:34:32 -0600 Subject: PCI: Reverse standard ACS vs device-specific ACS enabling The original thought was that if a device implemented ACS, then surely we want to use that... well, it turns out that devices can make an ACS capability so broken that we still need to fall back to quirks. Reverse the order of ACS enabling to give quirks first shot at it. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 10 ++++------ drivers/pci/quirks.c | 6 ++++-- include/linux/pci.h | 7 +++++-- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 25e0327d4429..c98c4e2aed3c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2547,7 +2547,7 @@ void pci_request_acs(void) * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites * @dev: the PCI device */ -static int pci_std_enable_acs(struct pci_dev *dev) +static void pci_std_enable_acs(struct pci_dev *dev) { int pos; u16 cap; @@ -2555,7 +2555,7 @@ static int pci_std_enable_acs(struct pci_dev *dev) pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); if (!pos) - return -ENODEV; + return; pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); @@ -2573,8 +2573,6 @@ static int pci_std_enable_acs(struct pci_dev *dev) ctrl |= (cap & PCI_ACS_UF); pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); - - return 0; } /** @@ -2586,10 +2584,10 @@ void pci_enable_acs(struct pci_dev *dev) if (!pci_acs_enable) return; - if (!pci_std_enable_acs(dev)) + if (!pci_dev_specific_enable_acs(dev)) return; - pci_dev_specific_enable_acs(dev); + pci_std_enable_acs(dev); } static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e248c2aad000..1f5c7898a246 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4201,7 +4201,7 @@ static const struct pci_dev_enable_acs { { 0 } }; -void pci_dev_specific_enable_acs(struct pci_dev *dev) +int pci_dev_specific_enable_acs(struct pci_dev *dev) { const struct pci_dev_enable_acs *i; int ret; @@ -4213,9 +4213,11 @@ void pci_dev_specific_enable_acs(struct pci_dev *dev) i->device == (u16)PCI_ANY_ID)) { ret = i->enable_acs(dev); if (ret >= 0) - return; + return ret; } } + + return -ENOTTY; } /* diff --git a/include/linux/pci.h b/include/linux/pci.h index 004b8133417d..aaec79aee805 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1663,7 +1663,7 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); -void pci_dev_specific_enable_acs(struct pci_dev *dev); +int pci_dev_specific_enable_acs(struct pci_dev *dev); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) { } @@ -1672,7 +1672,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, { return -ENOTTY; } -static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { } +static inline int pci_dev_specific_enable_acs(struct pci_dev *dev) +{ + return -ENOTTY; +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); -- cgit From a14b9e0512404ed7d4415b888dc9f1f9785a4fa3 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 5 Feb 2016 16:40:47 -0800 Subject: clkdev: Remove clk_register_clkdevs() Now that we've converted the only caller over to another clkdev API, remove this one. Reviewed-by: Andy Shevchenko Cc: Russell King Signed-off-by: Stephen Boyd --- drivers/clk/clkdev.c | 27 --------------------------- include/linux/clkdev.h | 1 - 2 files changed, 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index eb20b941154b..ae8e40a82d34 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -402,30 +402,3 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, return cl ? 0 : -ENOMEM; } EXPORT_SYMBOL(clk_register_clkdev); - -/** - * clk_register_clkdevs - register a set of clk_lookup for a struct clk - * @clk: struct clk to associate with all clk_lookups - * @cl: array of clk_lookup structures with con_id and dev_id pre-initialized - * @num: number of clk_lookup structures to register - * - * To make things easier for mass registration, we detect error clks - * from a previous clk_register() call, and return the error code for - * those. This is to permit this function to be called immediately - * after clk_register(). - */ -int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num) -{ - unsigned i; - - if (IS_ERR(clk)) - return PTR_ERR(clk); - - for (i = 0; i < num; i++, cl++) { - cl->clk_hw = __clk_get_hw(clk); - __clkdev_add(cl); - } - - return 0; -} -EXPORT_SYMBOL(clk_register_clkdevs); diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h index c2c04f7cbe8a..e6f8eb1d585f 100644 --- a/include/linux/clkdev.h +++ b/include/linux/clkdev.h @@ -45,7 +45,6 @@ void clkdev_add_table(struct clk_lookup *, size_t); int clk_add_alias(const char *, const char *, const char *, struct device *); int clk_register_clkdev(struct clk *, const char *, const char *); -int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t); #ifdef CONFIG_COMMON_CLK int __clk_get(struct clk *clk); -- cgit From 4143804c4fdef40358c654d1fb2271a1a0f1fedf Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 5 Feb 2016 17:02:52 -0800 Subject: clk: Add {devm_}clk_hw_{register,unregister}() APIs We've largely split the clk consumer and provider APIs along struct clk and struct clk_hw, but clk_register() still returns a struct clk pointer for each struct clk_hw that's registered. Eventually we'd like to only allocate struct clks when there's a user, because struct clk is per-user now, so clk_register() needs to change. Let's add new APIs to register struct clk_hws, but this time we'll hide the struct clk from the caller by returning an int error code. Also add an unregistration API that takes the clk_hw structure that was passed to the registration API. This way provider drivers never have to deal with a struct clk pointer unless they're using the clk consumer APIs. Signed-off-by: Stephen Boyd --- Documentation/driver-model/devres.txt | 1 + drivers/clk/clk.c | 86 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 6 +++ 3 files changed, 93 insertions(+) (limited to 'include/linux') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 73b98dfbcea4..108d45553e1b 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -236,6 +236,7 @@ certainly invest a bit more effort into libata core layer). CLOCK devm_clk_get() devm_clk_put() + devm_clk_hw_register() DMA dmam_alloc_coherent() diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fb74dc1f7520..0ef919666827 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2536,6 +2536,22 @@ fail_out: } EXPORT_SYMBOL_GPL(clk_register); +/** + * clk_hw_register - register a clk_hw and return an error code + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * clk_hw_register is the primary interface for populating the clock tree with + * new clock nodes. It returns an integer equal to zero indicating success or + * less than zero indicating failure. Drivers must test for an error code after + * calling clk_hw_register(). + */ +int clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + return PTR_ERR_OR_ZERO(clk_register(dev, hw)); +} +EXPORT_SYMBOL_GPL(clk_hw_register); + /* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { @@ -2637,11 +2653,26 @@ unlock: } EXPORT_SYMBOL_GPL(clk_unregister); +/** + * clk_hw_unregister - unregister a currently registered clk_hw + * @hw: hardware-specific clock data to unregister + */ +void clk_hw_unregister(struct clk_hw *hw) +{ + clk_unregister(hw->clk); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister); + static void devm_clk_release(struct device *dev, void *res) { clk_unregister(*(struct clk **)res); } +static void devm_clk_hw_release(struct device *dev, void *res) +{ + clk_hw_unregister(*(struct clk_hw **)res); +} + /** * devm_clk_register - resource managed clk_register() * @dev: device that is registering this clock @@ -2672,6 +2703,36 @@ struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(devm_clk_register); +/** + * devm_clk_hw_register - resource managed clk_hw_register() + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * Managed clk_hw_register(). Clocks returned from this function are + * automatically clk_hw_unregister()ed on driver detach. See clk_hw_register() + * for more information. + */ +int devm_clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + struct clk_hw **hwp; + int ret; + + hwp = devres_alloc(devm_clk_hw_release, sizeof(*hwp), GFP_KERNEL); + if (!hwp) + return -ENOMEM; + + ret = clk_hw_register(dev, hw); + if (!ret) { + *hwp = hw; + devres_add(dev, hwp); + } else { + devres_free(hwp); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register); + static int devm_clk_match(struct device *dev, void *res, void *data) { struct clk *c = res; @@ -2680,6 +2741,15 @@ static int devm_clk_match(struct device *dev, void *res, void *data) return c == data; } +static int devm_clk_hw_match(struct device *dev, void *res, void *data) +{ + struct clk_hw *hw = res; + + if (WARN_ON(!hw)) + return 0; + return hw == data; +} + /** * devm_clk_unregister - resource managed clk_unregister() * @clk: clock to unregister @@ -2694,6 +2764,22 @@ void devm_clk_unregister(struct device *dev, struct clk *clk) } EXPORT_SYMBOL_GPL(devm_clk_unregister); +/** + * devm_clk_hw_unregister - resource managed clk_hw_unregister() + * @dev: device that is unregistering the hardware-specific clock data + * @hw: link to hardware-specific clock data + * + * Unregister a clk_hw registered with devm_clk_hw_register(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw) +{ + WARN_ON(devres_release(dev, devm_clk_hw_release, devm_clk_hw_match, + hw)); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_unregister); + /* * clkdev helpers */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index da95258127aa..bc6c8de1fac1 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -655,9 +655,15 @@ struct clk *clk_register_gpio_mux(struct device *dev, const char *name, struct clk *clk_register(struct device *dev, struct clk_hw *hw); struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw); +int __must_check clk_hw_register(struct device *dev, struct clk_hw *hw); +int __must_check devm_clk_hw_register(struct device *dev, struct clk_hw *hw); + void clk_unregister(struct clk *clk); void devm_clk_unregister(struct device *dev, struct clk *clk); +void clk_hw_unregister(struct clk_hw *hw); +void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw); + /* helper functions */ const char *__clk_get_name(const struct clk *clk); const char *clk_hw_get_name(const struct clk_hw *hw); -- cgit From 0861e5b8cf80038e91942f1005c8dfce79d18c38 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 5 Feb 2016 17:38:26 -0800 Subject: clk: Add clk_hw OF clk providers Now that we have a clk registration API that doesn't return struct clks, we need to have some way to hand out struct clks via the clk_get() APIs that doesn't involve associating struct clk pointers with an OF node. Currently we ask the OF provider to give us a struct clk pointer for some clkspec, turn that struct clk into a struct clk_hw and then allocate a new struct clk to return to the caller. Let's add a clk_hw based OF provider hook that returns a struct clk_hw directly, so that we skip the intermediate step of converting from struct clk to struct clk_hw. Eventually when we've converted all OF clk providers to struct clk_hw based APIs we can remove the struct clk based ones. It should also be noted that we change the onecell provider to have a flex array instead of a pointer for the array of clk_hw pointers. This allows providers to allocate one structure of the correct length in one step instead of two. Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 85 +++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-provider.h | 30 ++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0ef919666827..e813b2aabc87 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2941,6 +2941,7 @@ struct of_clk_provider { struct device_node *node; struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data); void *data; }; @@ -2957,6 +2958,12 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data) +{ + return data; +} +EXPORT_SYMBOL_GPL(of_clk_hw_simple_get); + struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) { struct clk_onecell_data *clk_data = data; @@ -2971,6 +2978,21 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) } EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); +struct clk_hw * +of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get); + /** * of_clk_add_provider() - Register a clock provider for a node * @np: Device node pointer associated with clock provider @@ -3006,6 +3028,41 @@ int of_clk_add_provider(struct device_node *np, } EXPORT_SYMBOL_GPL(of_clk_add_provider); +/** + * of_clk_add_hw_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @get: callback for decoding clk_hw + * @data: context pointer for @get callback. + */ +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct of_clk_provider *cp; + int ret; + + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = of_node_get(np); + cp->data = data; + cp->get_hw = get; + + mutex_lock(&of_clk_mutex); + list_add(&cp->link, &of_clk_providers); + mutex_unlock(&of_clk_mutex); + pr_debug("Added clk_hw provider from %s\n", np->full_name); + + ret = of_clk_set_defaults(np, true); + if (ret < 0) + of_clk_del_provider(np); + + return ret; +} +EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); + /** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider @@ -3027,11 +3084,32 @@ void of_clk_del_provider(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_del_provider); +static struct clk_hw * +__of_clk_get_hw_from_provider(struct of_clk_provider *provider, + struct of_phandle_args *clkspec) +{ + struct clk *clk; + struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); + + if (provider->get_hw) { + hw = provider->get_hw(clkspec, provider->data); + } else if (provider->get) { + clk = provider->get(clkspec, provider->data); + if (!IS_ERR(clk)) + hw = __clk_get_hw(clk); + else + hw = ERR_CAST(clk); + } + + return hw; +} + struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, const char *dev_id, const char *con_id) { struct of_clk_provider *provider; struct clk *clk = ERR_PTR(-EPROBE_DEFER); + struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); if (!clkspec) return ERR_PTR(-EINVAL); @@ -3040,10 +3118,9 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, mutex_lock(&of_clk_mutex); list_for_each_entry(provider, &of_clk_providers, link) { if (provider->node == clkspec->np) - clk = provider->get(clkspec, provider->data); - if (!IS_ERR(clk)) { - clk = __clk_create_clk(__clk_get_hw(clk), dev_id, - con_id); + hw = __of_clk_get_hw_from_provider(provider, clkspec); + if (!IS_ERR(hw)) { + clk = __clk_create_clk(hw, dev_id, con_id); if (!IS_ERR(clk) && !__clk_get(clk)) { __clk_free_clk(clk); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index bc6c8de1fac1..bf8c8bb8c2cb 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -709,6 +709,11 @@ struct clk_onecell_data { unsigned int clk_num; }; +struct clk_hw_onecell_data { + size_t num; + struct clk_hw *hws[]; +}; + extern struct of_device_id __clk_of_table; #define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn) @@ -718,10 +723,18 @@ int of_clk_add_provider(struct device_node *np, struct clk *(*clk_src_get)(struct of_phandle_args *args, void *data), void *data); +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data); void of_clk_del_provider(struct device_node *np); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); +struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, + void *data); struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); +struct clk_hw *of_clk_hw_onecell_get(struct of_phandle_args *clkspec, + void *data); unsigned int of_clk_get_parent_count(struct device_node *np); int of_clk_parent_fill(struct device_node *np, const char **parents, unsigned int size); @@ -738,17 +751,34 @@ static inline int of_clk_add_provider(struct device_node *np, { return 0; } +static inline int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return 0; +} static inline void of_clk_del_provider(struct device_node *np) {} static inline struct clk *of_clk_src_simple_get( struct of_phandle_args *clkspec, void *data) { return ERR_PTR(-ENOENT); } +static inline struct clk_hw * +of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} static inline struct clk *of_clk_src_onecell_get( struct of_phandle_args *clkspec, void *data) { return ERR_PTR(-ENOENT); } +static inline struct clk_hw * +of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} static inline int of_clk_get_parent_count(struct device_node *np) { return 0; -- cgit From e4f1b49bda6d6aa2e13730ff7eeccbe65a6271f1 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 8 Feb 2016 14:59:49 -0800 Subject: clkdev: Add clk_hw based registration APIs Now that we have a clk registration API that doesn't return struct clks, we need to have some way to hand out struct clks via the clk_get() APIs that doesn't involve associating struct clk pointers with a struct clk_lookup. Luckily, clkdev already operates on struct clk_hw pointers, except for the registration facing APIs where it converts struct clk pointers into struct clk_hw pointers almost immediately. Let's add clk_hw based registration APIs so that we can skip the conversion step and provide a way for clk provider drivers to operate exclusively on clk_hw structs. This way we clearly split the API between consumers and providers. Cc: Russell King Signed-off-by: Stephen Boyd --- drivers/clk/clkdev.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/clkdev.h | 6 +++++ 2 files changed, 70 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index ae8e40a82d34..89cc700fbc37 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -301,6 +301,20 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) } EXPORT_SYMBOL(clkdev_alloc); +struct clk_lookup * +clkdev_hw_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + va_start(ap, dev_fmt); + cl = vclkdev_alloc(hw, con_id, dev_fmt, ap); + va_end(ap); + + return cl; +} +EXPORT_SYMBOL(clkdev_hw_alloc); + /** * clkdev_create - allocate and add a clkdev lookup structure * @clk: struct clk to associate with all clk_lookups @@ -324,6 +338,29 @@ struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id, } EXPORT_SYMBOL_GPL(clkdev_create); +/** + * clkdev_hw_create - allocate and add a clkdev lookup structure + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_fmt: format string describing device name + * + * Returns a clk_lookup structure, which can be later unregistered and + * freed. + */ +struct clk_lookup *clkdev_hw_create(struct clk_hw *hw, const char *con_id, + const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + va_start(ap, dev_fmt); + cl = vclkdev_create(hw, con_id, dev_fmt, ap); + va_end(ap); + + return cl; +} +EXPORT_SYMBOL_GPL(clkdev_hw_create); + int clk_add_alias(const char *alias, const char *alias_dev_name, const char *con_id, struct device *dev) { @@ -402,3 +439,30 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, return cl ? 0 : -ENOMEM; } EXPORT_SYMBOL(clk_register_clkdev); + +/** + * clk_hw_register_clkdev - register one clock lookup for a struct clk_hw + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + */ +int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, + const char *dev_id) +{ + struct clk_lookup *cl; + + /* + * Since dev_id can be NULL, and NULL is handled specially, we must + * pass it as either a NULL format string, or with "%s". + */ + if (dev_id) + cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); + else + cl = __clk_register_clkdev(hw, con_id, NULL); + + return cl ? 0 : -ENOMEM; +} +EXPORT_SYMBOL(clk_hw_register_clkdev); diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h index e6f8eb1d585f..2eabc862abdb 100644 --- a/include/linux/clkdev.h +++ b/include/linux/clkdev.h @@ -15,6 +15,7 @@ #include struct clk; +struct clk_hw; struct device; struct clk_lookup { @@ -34,17 +35,22 @@ struct clk_lookup { struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) __printf(3, 4); +struct clk_lookup *clkdev_hw_alloc(struct clk_hw *hw, const char *con_id, + const char *dev_fmt, ...) __printf(3, 4); void clkdev_add(struct clk_lookup *cl); void clkdev_drop(struct clk_lookup *cl); struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id, const char *dev_fmt, ...) __printf(3, 4); +struct clk_lookup *clkdev_hw_create(struct clk_hw *hw, const char *con_id, + const char *dev_fmt, ...) __printf(3, 4); void clkdev_add_table(struct clk_lookup *, size_t); int clk_add_alias(const char *, const char *, const char *, struct device *); int clk_register_clkdev(struct clk *, const char *, const char *); +int clk_hw_register_clkdev(struct clk_hw *, const char *, const char *); #ifdef CONFIG_COMMON_CLK int __clk_get(struct clk *clk); -- cgit From eb7d264f3bf9ca7c093efb77bdde557c6c6e826f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 6 Feb 2016 23:26:37 -0800 Subject: clk: divider: Add hw based registration APIs Add registration APIs in the clk divider code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-divider.c | 91 ++++++++++++++++++++++++++++++++++++++++---- include/linux/clk-provider.h | 10 +++++ 2 files changed, 93 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 00e035b51c69..a0f55bc1ad3d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -426,15 +426,16 @@ const struct clk_ops clk_divider_ro_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ro_ops); -static struct clk *_register_divider(struct device *dev, const char *name, +static struct clk_hw *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock) { struct clk_divider *div; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { if (width + shift > 16) { @@ -467,12 +468,14 @@ static struct clk *_register_divider(struct device *dev, const char *name, div->table = table; /* register the clock */ - clk = clk_register(dev, &div->hw); - - if (IS_ERR(clk)) + hw = &div->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(div); + hw = ERR_PTR(ret); + } - return clk; + return hw; } /** @@ -492,11 +495,38 @@ struct clk *clk_register_divider(struct device *dev, const char *name, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock) { - return _register_divider(dev, name, parent_name, flags, reg, shift, + struct clk_hw *hw; + + hw = _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, NULL, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_divider); +/** + * clk_hw_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +struct clk_hw *clk_hw_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_divider); + /** * clk_register_divider_table - register a table based divider clock with * the clock framework @@ -517,11 +547,41 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock) { - return _register_divider(dev, name, parent_name, flags, reg, shift, + struct clk_hw *hw; + + hw = _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, table, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_divider_table); +/** + * clk_hw_register_divider_table - register a table based divider clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @table: array of divider/value pairs ending with a div set to 0 + * @lock: shared register lock for this clock + */ +struct clk_hw *clk_hw_register_divider_table(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, table, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_divider_table); + void clk_unregister_divider(struct clk *clk) { struct clk_divider *div; @@ -537,3 +597,18 @@ void clk_unregister_divider(struct clk *clk) kfree(div); } EXPORT_SYMBOL_GPL(clk_unregister_divider); + +/** + * clk_hw_unregister_divider - unregister a clk divider + * @hw: hardware-specific clock data to unregister + */ +void clk_hw_unregister_divider(struct clk_hw *hw) +{ + struct clk_divider *div; + + div = to_clk_divider(hw); + + clk_hw_unregister(hw); + kfree(div); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_divider); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index bf8c8bb8c2cb..8885d0350596 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -407,12 +407,22 @@ struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock); +struct clk_hw *clk_hw_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock); struct clk *clk_register_divider_table(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock); +struct clk_hw *clk_hw_register_divider_table(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); void clk_unregister_divider(struct clk *clk); +void clk_hw_unregister_divider(struct clk_hw *hw); /** * struct clk_mux - multiplexer clock -- cgit From e270d8cb13763f58107198e879cf396511ba2867 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 6 Feb 2016 23:54:45 -0800 Subject: clk: gate: Add hw based registration APIs Add registration APIs in the clk gate code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-gate.c | 43 ++++++++++++++++++++++++++++++++++++------- include/linux/clk-provider.h | 5 +++++ 2 files changed, 41 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index d0d8ec8e1f1b..4e691e35483a 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -110,7 +110,7 @@ const struct clk_ops clk_gate_ops = { EXPORT_SYMBOL_GPL(clk_gate_ops); /** - * clk_register_gate - register a gate clock with the clock framework + * clk_hw_register_gate - register a gate clock with the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent @@ -120,14 +120,15 @@ EXPORT_SYMBOL_GPL(clk_gate_ops); * @clk_gate_flags: gate-specific flags for this clock * @lock: shared register lock for this clock */ -struct clk *clk_register_gate(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) { struct clk_gate *gate; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { if (bit_idx > 15) { @@ -154,12 +155,29 @@ struct clk *clk_register_gate(struct device *dev, const char *name, gate->lock = lock; gate->hw.init = &init; - clk = clk_register(dev, &gate->hw); - - if (IS_ERR(clk)) + hw = &gate->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(gate); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_gate); + +struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gate(dev, name, parent_name, flags, reg, + bit_idx, clk_gate_flags, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_gate); @@ -178,3 +196,14 @@ void clk_unregister_gate(struct clk *clk) kfree(gate); } EXPORT_SYMBOL_GPL(clk_unregister_gate); + +void clk_hw_unregister_gate(struct clk_hw *hw) +{ + struct clk_gate *gate; + + gate = to_clk_gate(hw); + + clk_hw_unregister(hw); + kfree(gate); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_gate); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 8885d0350596..bf12050aadd5 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -326,7 +326,12 @@ struct clk *clk_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock); +struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); void clk_unregister_gate(struct clk *clk); +void clk_hw_unregister_gate(struct clk_hw *hw); struct clk_div_table { unsigned int val; -- cgit From 264b31719735eb1fcbed47cecdb20f517e804856 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:05:48 -0800 Subject: clk: mux: Add hw based registration APIs Add registration APIs in the clk mux code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-mux.c | 57 +++++++++++++++++++++++++++++++++++++++----- include/linux/clk-provider.h | 11 +++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 252188fd8bcd..16a3d5717f4e 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -113,16 +113,17 @@ const struct clk_ops clk_mux_ro_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ro_ops); -struct clk *clk_register_mux_table(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; u8 width = 0; + int ret; if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { width = fls(mask) - ffs(mask) + 1; @@ -157,12 +158,31 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, mux->table = table; mux->hw.init = &init; - clk = clk_register(dev, &mux->hw); - - if (IS_ERR(clk)) + hw = &mux->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(mux); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_mux_table); + +struct clk *clk_register_mux_table(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + table, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_mux_table); @@ -180,6 +200,20 @@ struct clk *clk_register_mux(struct device *dev, const char *name, } EXPORT_SYMBOL_GPL(clk_register_mux); +struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_hw_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_mux); + void clk_unregister_mux(struct clk *clk) { struct clk_mux *mux; @@ -195,3 +229,14 @@ void clk_unregister_mux(struct clk *clk) kfree(mux); } EXPORT_SYMBOL_GPL(clk_unregister_mux); + +void clk_hw_unregister_mux(struct clk_hw *hw) +{ + struct clk_mux *mux; + + mux = to_clk_mux(hw); + + clk_hw_unregister(hw); + kfree(mux); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_mux); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index bf12050aadd5..d690d99b9c1c 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -478,14 +478,25 @@ struct clk *clk_register_mux(struct device *dev, const char *name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); +struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock); struct clk *clk_register_mux_table(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock); +struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock); void clk_unregister_mux(struct clk *clk); +void clk_hw_unregister_mux(struct clk_hw *hw); void of_fixed_factor_clk_setup(struct device_node *node); -- cgit From 0759ac8a73dc2c8cc8ac697fbe5dbd8d67348d37 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:11:06 -0800 Subject: clk: fixed-factor: Add hw based registration APIs Add registration APIs in the clk fixed-factor code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-fixed-factor.c | 42 +++++++++++++++++++++++++++++++++++------- include/linux/clk-provider.h | 4 ++++ 2 files changed, 39 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 053448e2453d..75cd6c792cb8 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -68,13 +68,14 @@ const struct clk_ops clk_fixed_factor_ops = { }; EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); -struct clk *clk_register_fixed_factor(struct device *dev, const char *name, - const char *parent_name, unsigned long flags, +struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { struct clk_fixed_factor *fix; struct clk_init_data init; - struct clk *clk; + struct clk_hw *hw; + int ret; fix = kmalloc(sizeof(*fix), GFP_KERNEL); if (!fix) @@ -91,12 +92,28 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, init.parent_names = &parent_name; init.num_parents = 1; - clk = clk_register(dev, &fix->hw); - - if (IS_ERR(clk)) + hw = &fix->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fix); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk_hw *hw; - return clk; + hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult, + div); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fixed_factor); @@ -113,6 +130,17 @@ void clk_unregister_fixed_factor(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor); +void clk_hw_unregister_fixed_factor(struct clk_hw *hw) +{ + struct clk_fixed_factor *fix; + + fix = to_clk_fixed_factor(hw); + + clk_hw_unregister(hw); + kfree(fix); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor); + #ifdef CONFIG_OF /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index d690d99b9c1c..79ad1a8a6831 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -525,6 +525,10 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); void clk_unregister_fixed_factor(struct clk *clk); +struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div); +void clk_hw_unregister_fixed_factor(struct clk_hw *hw); /** * struct clk_fractional_divider - adjustable fractional divider clock -- cgit From 39b44cff4ad4af6d7abd9dd2acb288b005c26503 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:15:09 -0800 Subject: clk: fractional-divider: Add hw based registration APIs Add registration APIs in the clk fractional divider code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-fractional-divider.c | 40 +++++++++++++++++++++++++++++++----- include/linux/clk-provider.h | 5 +++++ 2 files changed, 40 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 1abcd76b4993..aab904618eb6 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -116,14 +116,15 @@ const struct clk_ops clk_fractional_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_fractional_divider_ops); -struct clk *clk_register_fractional_divider(struct device *dev, +struct clk_hw *clk_hw_register_fractional_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, u8 clk_divider_flags, spinlock_t *lock) { struct clk_fractional_divider *fd; struct clk_init_data init; - struct clk *clk; + struct clk_hw *hw; + int ret; fd = kzalloc(sizeof(*fd), GFP_KERNEL); if (!fd) @@ -146,10 +147,39 @@ struct clk *clk_register_fractional_divider(struct device *dev, fd->lock = lock; fd->hw.init = &init; - clk = clk_register(dev, &fd->hw); - if (IS_ERR(clk)) + hw = &fd->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fd); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fractional_divider); - return clk; +struct clk *clk_register_fractional_divider(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, + u8 clk_divider_flags, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fractional_divider(dev, name, parent_name, flags, + reg, mshift, mwidth, nshift, nwidth, clk_divider_flags, + lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fractional_divider); + +void clk_hw_unregister_fractional_divider(struct clk_hw *hw) +{ + struct clk_fractional_divider *fd; + + fd = to_clk_fd(hw); + + clk_hw_unregister(hw); + kfree(fd); +} diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 79ad1a8a6831..bcbaf6c95d52 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -563,6 +563,11 @@ struct clk *clk_register_fractional_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, u8 clk_divider_flags, spinlock_t *lock); +struct clk_hw *clk_hw_register_fractional_divider(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, + u8 clk_divider_flags, spinlock_t *lock); +void clk_hw_unregister_fractional_divider(struct clk_hw *hw); /** * struct clk_multiplier - adjustable multiplier clock -- cgit From 49cb392d36397a296dcd51ec57cf83585a89a94a Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:20:31 -0800 Subject: clk: composite: Add hw based registration APIs Add registration APIs in the clk composite code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-composite.c | 45 ++++++++++++++++++++++++++++++++------------ include/linux/clk-provider.h | 7 +++++++ 2 files changed, 40 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 1f903e1f86a2..463fadd5a68f 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -184,17 +184,18 @@ static void clk_composite_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } -struct clk *clk_register_composite(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags) { - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; struct clk_composite *composite; struct clk_ops *clk_composite_ops; + int ret; composite = kzalloc(sizeof(*composite), GFP_KERNEL); if (!composite) @@ -204,12 +205,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name, init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; + hw = &composite->hw; clk_composite_ops = &composite->ops; if (mux_hw && mux_ops) { if (!mux_ops->get_parent) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } @@ -224,7 +226,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, if (rate_hw && rate_ops) { if (!rate_ops->recalc_rate) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } clk_composite_ops->recalc_rate = clk_composite_recalc_rate; @@ -253,7 +255,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, if (gate_hw && gate_ops) { if (!gate_ops->is_enabled || !gate_ops->enable || !gate_ops->disable) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } @@ -267,22 +269,41 @@ struct clk *clk_register_composite(struct device *dev, const char *name, init.ops = clk_composite_ops; composite->hw.init = &init; - clk = clk_register(dev, &composite->hw); - if (IS_ERR(clk)) + ret = clk_hw_register(dev, hw); + if (ret) { + hw = ERR_PTR(ret); goto err; + } if (composite->mux_hw) - composite->mux_hw->clk = clk; + composite->mux_hw->clk = hw->clk; if (composite->rate_hw) - composite->rate_hw->clk = clk; + composite->rate_hw->clk = hw->clk; if (composite->gate_hw) - composite->gate_hw->clk = clk; + composite->gate_hw->clk = hw->clk; - return clk; + return hw; err: kfree(composite); - return clk; + return hw; +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_composite(dev, name, parent_names, num_parents, + mux_hw, mux_ops, rate_hw, rate_ops, gate_hw, gate_ops, + flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index bcbaf6c95d52..456c3ced1ac9 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -638,6 +638,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags); +struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags); +void clk_hw_unregister_composite(struct clk_hw *hw); /*** * struct clk_gpio_gate - gpio gated clock -- cgit From b120743a64a3ec68b8c5310a6009094329b4a33b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:27:55 -0800 Subject: clk: gpio: Add hw based registration APIs Add registration APIs in the clk gpio code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-gpio.c | 52 ++++++++++++++++++++++++++++++++++---------- include/linux/clk-provider.h | 8 +++++++ 2 files changed, 49 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 08f65acc5d57..86b245746a6b 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -94,13 +94,13 @@ const struct clk_ops clk_gpio_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_gpio_mux_ops); -static struct clk *clk_register_gpio(struct device *dev, const char *name, +static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned gpio, bool active_low, unsigned long flags, const struct clk_ops *clk_gpio_ops) { struct clk_gpio *clk_gpio; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init = {}; unsigned long gpio_flags; int err; @@ -141,24 +141,26 @@ static struct clk *clk_register_gpio(struct device *dev, const char *name, clk_gpio->gpiod = gpio_to_desc(gpio); clk_gpio->hw.init = &init; + hw = &clk_gpio->hw; if (dev) - clk = devm_clk_register(dev, &clk_gpio->hw); + err = devm_clk_hw_register(dev, hw); else - clk = clk_register(NULL, &clk_gpio->hw); + err = clk_hw_register(NULL, hw); - if (!IS_ERR(clk)) - return clk; + if (!err) + return hw; if (!dev) { gpiod_put(clk_gpio->gpiod); kfree(clk_gpio); } - return clk; + return ERR_PTR(err); } /** - * clk_register_gpio_gate - register a gpio clock gate with the clock framework + * clk_hw_register_gpio_gate - register a gpio clock gate with the clock + * framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent @@ -166,7 +168,7 @@ static struct clk *clk_register_gpio(struct device *dev, const char *name, * @active_low: true if gpio should be set to 0 to enable clock * @flags: clock flags */ -struct clk *clk_register_gpio_gate(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, const char *parent_name, unsigned gpio, bool active_low, unsigned long flags) { @@ -175,10 +177,24 @@ struct clk *clk_register_gpio_gate(struct device *dev, const char *name, (parent_name ? 1 : 0), gpio, active_low, flags, &clk_gpio_gate_ops); } +EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate); + +struct clk *clk_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, unsigned gpio, bool active_low, + unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpio, active_low, + flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} EXPORT_SYMBOL_GPL(clk_register_gpio_gate); /** - * clk_register_gpio_mux - register a gpio clock mux with the clock framework + * clk_hw_register_gpio_mux - register a gpio clock mux with the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_names: names of this clock's parents @@ -187,7 +203,7 @@ EXPORT_SYMBOL_GPL(clk_register_gpio_gate); * @active_low: true if gpio should be set to 0 to enable clock * @flags: clock flags */ -struct clk *clk_register_gpio_mux(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned gpio, bool active_low, unsigned long flags) { @@ -199,6 +215,20 @@ struct clk *clk_register_gpio_mux(struct device *dev, const char *name, return clk_register_gpio(dev, name, parent_names, num_parents, gpio, active_low, flags, &clk_gpio_mux_ops); } +EXPORT_SYMBOL_GPL(clk_hw_register_gpio_mux); + +struct clk *clk_register_gpio_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, unsigned gpio, + bool active_low, unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gpio_mux(dev, name, parent_names, num_parents, + gpio, active_low, flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} EXPORT_SYMBOL_GPL(clk_register_gpio_mux); static int gpio_clk_driver_probe(struct platform_device *pdev) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 456c3ced1ac9..6c36c5e8ccbe 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -667,6 +667,10 @@ extern const struct clk_ops clk_gpio_gate_ops; struct clk *clk_register_gpio_gate(struct device *dev, const char *name, const char *parent_name, unsigned gpio, bool active_low, unsigned long flags); +struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, unsigned gpio, bool active_low, + unsigned long flags); +void clk_hw_unregister_gpio_gate(struct clk_hw *hw); /** * struct clk_gpio_mux - gpio controlled clock multiplexer @@ -682,6 +686,10 @@ extern const struct clk_ops clk_gpio_mux_ops; struct clk *clk_register_gpio_mux(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned gpio, bool active_low, unsigned long flags); +struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, unsigned gpio, + bool active_low, unsigned long flags); +void clk_hw_unregister_gpio_mux(struct clk_hw *hw); /** * clk_register - allocate a new clock, register it and return an opaque cookie -- cgit From 26ef56be9e0944a9b136169eb47140f309ce745b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 7 Feb 2016 00:34:13 -0800 Subject: clk: fixed-rate: Add hw based registration APIs Add registration APIs in the clk fixed-rate code to return struct clk_hw pointers instead of struct clk pointers. This way we hide the struct clk pointer from providers unless they need to use consumer facing APIs. Signed-off-by: Stephen Boyd --- drivers/clk/clk-fixed-rate.c | 44 ++++++++++++++++++++++++++++++++++++-------- include/linux/clk-provider.h | 7 +++++++ 2 files changed, 43 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index cd9dc925b3f8..8e4453eb54e8 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -45,8 +45,8 @@ const struct clk_ops clk_fixed_rate_ops = { EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); /** - * clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the - * clock framework + * clk_hw_register_fixed_rate_with_accuracy - register fixed-rate clock with + * the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of clock's parent @@ -54,13 +54,14 @@ EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); * @fixed_rate: non-adjustable clock rate * @fixed_accuracy: non-adjustable clock rate */ -struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, +struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate, unsigned long fixed_accuracy) { struct clk_fixed_rate *fixed; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; /* allocate fixed-rate clock */ fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); @@ -79,22 +80,49 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, fixed->hw.init = &init; /* register the clock */ - clk = clk_register(dev, &fixed->hw); - if (IS_ERR(clk)) + hw = &fixed->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fixed); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_rate_with_accuracy); + +struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned long fixed_rate, unsigned long fixed_accuracy) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate_with_accuracy(dev, name, parent_name, + flags, fixed_rate, fixed_accuracy); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fixed_rate_with_accuracy); /** - * clk_register_fixed_rate - register fixed-rate clock with the clock framework + * clk_hw_register_fixed_rate - register fixed-rate clock with the clock + * framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of clock's parent * @flags: framework-specific flags * @fixed_rate: non-adjustable clock rate */ +struct clk_hw *clk_hw_register_fixed_rate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned long fixed_rate) +{ + return clk_hw_register_fixed_rate_with_accuracy(dev, name, parent_name, + flags, fixed_rate, 0); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_rate); + struct clk *clk_register_fixed_rate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 6c36c5e8ccbe..c3fc042d517c 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -282,10 +282,17 @@ extern const struct clk_ops clk_fixed_rate_ops; struct clk *clk_register_fixed_rate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate); +struct clk_hw *clk_hw_register_fixed_rate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned long fixed_rate); struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate, unsigned long fixed_accuracy); void clk_unregister_fixed_rate(struct clk *clk); +struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned long fixed_rate, unsigned long fixed_accuracy); + void of_fixed_clk_setup(struct device_node *np); /** -- cgit From 12524e348bcb2189a8cf43829e90256f7e7d4f3d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Apr 2016 11:18:06 +0200 Subject: clk: renesas: mstp: Provide dummy attach/detach_dev callbacks Provide dummy cpg_mstp_{at,de}tach_dev() PM Domain callbacks if CPG/MSTP support is not included, so the rcar-sysc driver won't have to care about this. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart --- include/linux/clk/renesas.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 095b1681daf4..ab57a298a374 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -25,7 +25,12 @@ void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); void cpg_mstp_add_clk_domain(struct device_node *np); +#ifdef CONFIG_CLK_RENESAS_CPG_MSTP int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev); void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev); +#else +#define cpg_mstp_attach_dev NULL +#define cpg_mstp_detach_dev NULL +#endif #endif -- cgit From 2066390ad47b374f3d35075a32325b47d15bf735 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 4 Mar 2016 17:03:46 +0100 Subject: clk: renesas: cpg-mssr: Export cpg_mssr_{at,de}tach_dev() The R-Car SYSC PM Domain driver has to power manage devices in power areas using clocks. To reuse code and to share knowledge of clocks suitable for power management, this is ideally done through the existing cpg_mssr_attach_dev() and cpg_mssr_detach_dev() callbacks. Hence these callbacks can no longer rely on their "domain" parameter pointing to the CPG/MSSR Clock Domain. To handle this, keep a pointer to the clock domain in a static variable. cpg_mssr_attach_dev() has to support probe deferral, as the R-Car SYSC PM Domain may be initialized, and devices may be added to it, before the CPG/MSSR Clock Domain is initialized. Dummy callbacks are provided for the case where CPG/MSTP support is not included, so the rcar-sysc driver won't have to care about this. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart --- drivers/clk/renesas/renesas-cpg-mssr.c | 18 ++++++++++++------ include/linux/clk/renesas.h | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 703bdb157528..1f2dc3629f0e 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -388,6 +389,8 @@ struct cpg_mssr_clk_domain { unsigned int core_pm_clks[0]; }; +static struct cpg_mssr_clk_domain *cpg_mssr_clk_domain; + static bool cpg_mssr_is_pm_clk(const struct of_phandle_args *clkspec, struct cpg_mssr_clk_domain *pd) { @@ -411,17 +414,20 @@ static bool cpg_mssr_is_pm_clk(const struct of_phandle_args *clkspec, } } -static int cpg_mssr_attach_dev(struct generic_pm_domain *genpd, - struct device *dev) +int cpg_mssr_attach_dev(struct generic_pm_domain *unused, struct device *dev) { - struct cpg_mssr_clk_domain *pd = - container_of(genpd, struct cpg_mssr_clk_domain, genpd); + struct cpg_mssr_clk_domain *pd = cpg_mssr_clk_domain; struct device_node *np = dev->of_node; struct of_phandle_args clkspec; struct clk *clk; int i = 0; int error; + if (!pd) { + dev_dbg(dev, "CPG/MSSR clock domain not yet available\n"); + return -EPROBE_DEFER; + } + while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, &clkspec)) { if (cpg_mssr_is_pm_clk(&clkspec, pd)) @@ -461,8 +467,7 @@ fail_put: return error; } -static void cpg_mssr_detach_dev(struct generic_pm_domain *genpd, - struct device *dev) +void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev) { if (!list_empty(&dev->power.subsys_data->clock_list)) pm_clk_destroy(dev); @@ -491,6 +496,7 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, pm_genpd_init(genpd, &simple_qos_governor, false); genpd->attach_dev = cpg_mssr_attach_dev; genpd->detach_dev = cpg_mssr_detach_dev; + cpg_mssr_clk_domain = pd; of_genpd_add_provider_simple(np, genpd); return 0; diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index ab57a298a374..ba6fa4148515 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -33,4 +33,11 @@ void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev); #define cpg_mstp_detach_dev NULL #endif +#ifdef CONFIG_CLK_RENESAS_CPG_MSSR +int cpg_mssr_attach_dev(struct generic_pm_domain *unused, struct device *dev); +void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); +#else +#define cpg_mssr_attach_dev NULL +#define cpg_mssr_detach_dev NULL +#endif #endif -- cgit From 2e4682ba2ed79d8082b78d292b3b80f54d970b7a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Mar 2016 16:30:22 +0100 Subject: KVM: add missing memory barrier in kvm_{make,check}_request kvm_make_request and kvm_check_request imply a producer-consumer relationship; add implicit memory barriers to them. There was indeed already a place that was adding an explicit smp_mb() to order between kvm_check_request and the processing of the request. That memory barrier can be removed (as an added benefit, kvm_check_request can use smp_mb__after_atomic which is free on x86). Signed-off-by: Paolo Bonzini --- arch/x86/kvm/irq_comm.c | 3 --- include/linux/kvm_host.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c index 54ead79e444b..dfb4c6476877 100644 --- a/arch/x86/kvm/irq_comm.c +++ b/arch/x86/kvm/irq_comm.c @@ -382,9 +382,6 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, u32 i, nr_ioapic_pins; int idx; - /* kvm->irq_routing must be read after clearing - * KVM_SCAN_IOAPIC. */ - smp_mb(); idx = srcu_read_lock(&kvm->irq_srcu); table = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); nr_ioapic_pins = min_t(u32, table->nr_rt_entries, diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5276fe0916fc..ad40d44784c7 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1091,6 +1091,11 @@ static inline bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return true; } static inline void kvm_make_request(int req, struct kvm_vcpu *vcpu) { + /* + * Ensure the rest of the request is published to kvm_check_request's + * caller. Paired with the smp_mb__after_atomic in kvm_check_request. + */ + smp_wmb(); set_bit(req, &vcpu->requests); } @@ -1098,6 +1103,12 @@ static inline bool kvm_check_request(int req, struct kvm_vcpu *vcpu) { if (test_bit(req, &vcpu->requests)) { clear_bit(req, &vcpu->requests); + + /* + * Ensure the rest of the request is visible to kvm_check_request's + * caller. Paired with the smp_wmb in kvm_make_request. + */ + smp_mb__after_atomic(); return true; } else { return false; -- cgit From 9257b4a206fc0229dd5f84b78e4d1ebf3f91d270 Mon Sep 17 00:00:00 2001 From: Omer Peleg Date: Wed, 20 Apr 2016 11:34:11 +0300 Subject: iommu/iova: introduce per-cpu caching to iova allocation IOVA allocation has two problems that impede high-throughput I/O. First, it can do a linear search over the allocated IOVA ranges. Second, the rbtree spinlock that serializes IOVA allocations becomes contended. Address these problems by creating an API for caching allocated IOVA ranges, so that the IOVA allocator isn't accessed frequently. This patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs without taking the rbtree spinlock. The per-CPU caches are backed by a global cache, to avoid invoking the (linear-time) IOVA allocator without needing to make the per-CPU cache size excessive. This design is based on magazines, as described in "Magazines and Vmem: Extending the Slab Allocator to Many CPUs and Arbitrary Resources" (currently available at https://www.usenix.org/legacy/event/usenix01/bonwick.html) Adding caching on top of the existing rbtree allocator maintains the property that IOVAs are densely packed in the IO virtual address space, which is important for keeping IOMMU page table usage low. To keep the cache size reasonable, we bound the IOVA space a CPU can cache by 32 MiB (we cache a bounded number of IOVA ranges, and only ranges of size <= 128 KiB). The shared global cache is bounded at 4 MiB of IOVA space. Signed-off-by: Omer Peleg [mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message] Signed-off-by: Adam Morrison Reviewed-by: Shaohua Li Reviewed-by: Ben Serebrin [dwmw2: split out VT-d part into a separate patch] Signed-off-by: David Woodhouse --- drivers/iommu/iova.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/iova.h | 23 ++- 2 files changed, 414 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index fa0adef32bd6..ba764a0835d3 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -20,6 +20,17 @@ #include #include #include +#include +#include + +static bool iova_rcache_insert(struct iova_domain *iovad, + unsigned long pfn, + unsigned long size); +static unsigned long iova_rcache_get(struct iova_domain *iovad, + unsigned long size, + unsigned long limit_pfn); +static void init_iova_rcaches(struct iova_domain *iovad); +static void free_iova_rcaches(struct iova_domain *iovad); void init_iova_domain(struct iova_domain *iovad, unsigned long granule, @@ -38,6 +49,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, iovad->granule = granule; iovad->start_pfn = start_pfn; iovad->dma_32bit_pfn = pfn_32bit; + init_iova_rcaches(iovad); } EXPORT_SYMBOL_GPL(init_iova_domain); @@ -291,33 +303,18 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, } EXPORT_SYMBOL_GPL(alloc_iova); -/** - * find_iova - find's an iova for a given pfn - * @iovad: - iova domain in question. - * @pfn: - page frame number - * This function finds and returns an iova belonging to the - * given doamin which matches the given pfn. - */ -struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn) +static struct iova * +private_find_iova(struct iova_domain *iovad, unsigned long pfn) { - unsigned long flags; - struct rb_node *node; + struct rb_node *node = iovad->rbroot.rb_node; + + assert_spin_locked(&iovad->iova_rbtree_lock); - /* Take the lock so that no other thread is manipulating the rbtree */ - spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); - node = iovad->rbroot.rb_node; while (node) { struct iova *iova = container_of(node, struct iova, node); /* If pfn falls within iova's range, return iova */ if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) { - spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); - /* We are not holding the lock while this iova - * is referenced by the caller as the same thread - * which called this function also calls __free_iova() - * and it is by design that only one thread can possibly - * reference a particular iova and hence no conflict. - */ return iova; } @@ -327,9 +324,35 @@ struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn) node = node->rb_right; } - spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return NULL; } + +static void private_free_iova(struct iova_domain *iovad, struct iova *iova) +{ + assert_spin_locked(&iovad->iova_rbtree_lock); + __cached_rbnode_delete_update(iovad, iova); + rb_erase(&iova->node, &iovad->rbroot); + free_iova_mem(iova); +} + +/** + * find_iova - finds an iova for a given pfn + * @iovad: - iova domain in question. + * @pfn: - page frame number + * This function finds and returns an iova belonging to the + * given doamin which matches the given pfn. + */ +struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn) +{ + unsigned long flags; + struct iova *iova; + + /* Take the lock so that no other thread is manipulating the rbtree */ + spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); + iova = private_find_iova(iovad, pfn); + spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); + return iova; +} EXPORT_SYMBOL_GPL(find_iova); /** @@ -344,10 +367,8 @@ __free_iova(struct iova_domain *iovad, struct iova *iova) unsigned long flags; spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); - __cached_rbnode_delete_update(iovad, iova); - rb_erase(&iova->node, &iovad->rbroot); + private_free_iova(iovad, iova); spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); - free_iova_mem(iova); } EXPORT_SYMBOL_GPL(__free_iova); @@ -369,6 +390,63 @@ free_iova(struct iova_domain *iovad, unsigned long pfn) } EXPORT_SYMBOL_GPL(free_iova); +/** + * alloc_iova_fast - allocates an iova from rcache + * @iovad: - iova domain in question + * @size: - size of page frames to allocate + * @limit_pfn: - max limit address + * This function tries to satisfy an iova allocation from the rcache, + * and falls back to regular allocation on failure. +*/ +unsigned long +alloc_iova_fast(struct iova_domain *iovad, unsigned long size, + unsigned long limit_pfn) +{ + bool flushed_rcache = false; + unsigned long iova_pfn; + struct iova *new_iova; + + iova_pfn = iova_rcache_get(iovad, size, limit_pfn); + if (iova_pfn) + return iova_pfn; + +retry: + new_iova = alloc_iova(iovad, size, limit_pfn, true); + if (!new_iova) { + unsigned int cpu; + + if (flushed_rcache) + return 0; + + /* Try replenishing IOVAs by flushing rcache. */ + flushed_rcache = true; + for_each_online_cpu(cpu) + free_cpu_cached_iovas(cpu, iovad); + goto retry; + } + + return new_iova->pfn_lo; +} +EXPORT_SYMBOL_GPL(alloc_iova_fast); + +/** + * free_iova_fast - free iova pfn range into rcache + * @iovad: - iova domain in question. + * @pfn: - pfn that is allocated previously + * @size: - # of pages in range + * This functions frees an iova range by trying to put it into the rcache, + * falling back to regular iova deallocation via free_iova() if this fails. + */ +void +free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size) +{ + if (iova_rcache_insert(iovad, pfn, size)) + return; + + free_iova(iovad, pfn); +} +EXPORT_SYMBOL_GPL(free_iova_fast); + /** * put_iova_domain - destroys the iova doamin * @iovad: - iova domain in question. @@ -379,6 +457,7 @@ void put_iova_domain(struct iova_domain *iovad) struct rb_node *node; unsigned long flags; + free_iova_rcaches(iovad); spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); node = rb_first(&iovad->rbroot); while (node) { @@ -550,5 +629,295 @@ error: return NULL; } +/* + * Magazine caches for IOVA ranges. For an introduction to magazines, + * see the USENIX 2001 paper "Magazines and Vmem: Extending the Slab + * Allocator to Many CPUs and Arbitrary Resources" by Bonwick and Adams. + * For simplicity, we use a static magazine size and don't implement the + * dynamic size tuning described in the paper. + */ + +#define IOVA_MAG_SIZE 128 + +struct iova_magazine { + unsigned long size; + unsigned long pfns[IOVA_MAG_SIZE]; +}; + +struct iova_cpu_rcache { + spinlock_t lock; + struct iova_magazine *loaded; + struct iova_magazine *prev; +}; + +static struct iova_magazine *iova_magazine_alloc(gfp_t flags) +{ + return kzalloc(sizeof(struct iova_magazine), flags); +} + +static void iova_magazine_free(struct iova_magazine *mag) +{ + kfree(mag); +} + +static void +iova_magazine_free_pfns(struct iova_magazine *mag, struct iova_domain *iovad) +{ + unsigned long flags; + int i; + + if (!mag) + return; + + spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); + + for (i = 0 ; i < mag->size; ++i) { + struct iova *iova = private_find_iova(iovad, mag->pfns[i]); + + BUG_ON(!iova); + private_free_iova(iovad, iova); + } + + spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); + + mag->size = 0; +} + +static bool iova_magazine_full(struct iova_magazine *mag) +{ + return (mag && mag->size == IOVA_MAG_SIZE); +} + +static bool iova_magazine_empty(struct iova_magazine *mag) +{ + return (!mag || mag->size == 0); +} + +static unsigned long iova_magazine_pop(struct iova_magazine *mag, + unsigned long limit_pfn) +{ + BUG_ON(iova_magazine_empty(mag)); + + if (mag->pfns[mag->size - 1] >= limit_pfn) + return 0; + + return mag->pfns[--mag->size]; +} + +static void iova_magazine_push(struct iova_magazine *mag, unsigned long pfn) +{ + BUG_ON(iova_magazine_full(mag)); + + mag->pfns[mag->size++] = pfn; +} + +static void init_iova_rcaches(struct iova_domain *iovad) +{ + struct iova_cpu_rcache *cpu_rcache; + struct iova_rcache *rcache; + unsigned int cpu; + int i; + + for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) { + rcache = &iovad->rcaches[i]; + spin_lock_init(&rcache->lock); + rcache->depot_size = 0; + rcache->cpu_rcaches = __alloc_percpu(sizeof(*cpu_rcache), cache_line_size()); + if (WARN_ON(!rcache->cpu_rcaches)) + continue; + for_each_possible_cpu(cpu) { + cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu); + spin_lock_init(&cpu_rcache->lock); + cpu_rcache->loaded = iova_magazine_alloc(GFP_KERNEL); + cpu_rcache->prev = iova_magazine_alloc(GFP_KERNEL); + } + } +} + +/* + * Try inserting IOVA range starting with 'iova_pfn' into 'rcache', and + * return true on success. Can fail if rcache is full and we can't free + * space, and free_iova() (our only caller) will then return the IOVA + * range to the rbtree instead. + */ +static bool __iova_rcache_insert(struct iova_domain *iovad, + struct iova_rcache *rcache, + unsigned long iova_pfn) +{ + struct iova_magazine *mag_to_free = NULL; + struct iova_cpu_rcache *cpu_rcache; + bool can_insert = false; + unsigned long flags; + + cpu_rcache = this_cpu_ptr(rcache->cpu_rcaches); + spin_lock_irqsave(&cpu_rcache->lock, flags); + + if (!iova_magazine_full(cpu_rcache->loaded)) { + can_insert = true; + } else if (!iova_magazine_full(cpu_rcache->prev)) { + swap(cpu_rcache->prev, cpu_rcache->loaded); + can_insert = true; + } else { + struct iova_magazine *new_mag = iova_magazine_alloc(GFP_ATOMIC); + + if (new_mag) { + spin_lock(&rcache->lock); + if (rcache->depot_size < MAX_GLOBAL_MAGS) { + rcache->depot[rcache->depot_size++] = + cpu_rcache->loaded; + } else { + mag_to_free = cpu_rcache->loaded; + } + spin_unlock(&rcache->lock); + + cpu_rcache->loaded = new_mag; + can_insert = true; + } + } + + if (can_insert) + iova_magazine_push(cpu_rcache->loaded, iova_pfn); + + spin_unlock_irqrestore(&cpu_rcache->lock, flags); + + if (mag_to_free) { + iova_magazine_free_pfns(mag_to_free, iovad); + iova_magazine_free(mag_to_free); + } + + return can_insert; +} + +static bool iova_rcache_insert(struct iova_domain *iovad, unsigned long pfn, + unsigned long size) +{ + unsigned int log_size = order_base_2(size); + + if (log_size >= IOVA_RANGE_CACHE_MAX_SIZE) + return false; + + return __iova_rcache_insert(iovad, &iovad->rcaches[log_size], pfn); +} + +/* + * Caller wants to allocate a new IOVA range from 'rcache'. If we can + * satisfy the request, return a matching non-NULL range and remove + * it from the 'rcache'. + */ +static unsigned long __iova_rcache_get(struct iova_rcache *rcache, + unsigned long limit_pfn) +{ + struct iova_cpu_rcache *cpu_rcache; + unsigned long iova_pfn = 0; + bool has_pfn = false; + unsigned long flags; + + cpu_rcache = this_cpu_ptr(rcache->cpu_rcaches); + spin_lock_irqsave(&cpu_rcache->lock, flags); + + if (!iova_magazine_empty(cpu_rcache->loaded)) { + has_pfn = true; + } else if (!iova_magazine_empty(cpu_rcache->prev)) { + swap(cpu_rcache->prev, cpu_rcache->loaded); + has_pfn = true; + } else { + spin_lock(&rcache->lock); + if (rcache->depot_size > 0) { + iova_magazine_free(cpu_rcache->loaded); + cpu_rcache->loaded = rcache->depot[--rcache->depot_size]; + has_pfn = true; + } + spin_unlock(&rcache->lock); + } + + if (has_pfn) + iova_pfn = iova_magazine_pop(cpu_rcache->loaded, limit_pfn); + + spin_unlock_irqrestore(&cpu_rcache->lock, flags); + + return iova_pfn; +} + +/* + * Try to satisfy IOVA allocation range from rcache. Fail if requested + * size is too big or the DMA limit we are given isn't satisfied by the + * top element in the magazine. + */ +static unsigned long iova_rcache_get(struct iova_domain *iovad, + unsigned long size, + unsigned long limit_pfn) +{ + unsigned int log_size = order_base_2(size); + + if (log_size >= IOVA_RANGE_CACHE_MAX_SIZE) + return 0; + + return __iova_rcache_get(&iovad->rcaches[log_size], limit_pfn); +} + +/* + * Free a cpu's rcache. + */ +static void free_cpu_iova_rcache(unsigned int cpu, struct iova_domain *iovad, + struct iova_rcache *rcache) +{ + struct iova_cpu_rcache *cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu); + unsigned long flags; + + spin_lock_irqsave(&cpu_rcache->lock, flags); + + iova_magazine_free_pfns(cpu_rcache->loaded, iovad); + iova_magazine_free(cpu_rcache->loaded); + + iova_magazine_free_pfns(cpu_rcache->prev, iovad); + iova_magazine_free(cpu_rcache->prev); + + spin_unlock_irqrestore(&cpu_rcache->lock, flags); +} + +/* + * free rcache data structures. + */ +static void free_iova_rcaches(struct iova_domain *iovad) +{ + struct iova_rcache *rcache; + unsigned long flags; + unsigned int cpu; + int i, j; + + for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) { + rcache = &iovad->rcaches[i]; + for_each_possible_cpu(cpu) + free_cpu_iova_rcache(cpu, iovad, rcache); + spin_lock_irqsave(&rcache->lock, flags); + free_percpu(rcache->cpu_rcaches); + for (j = 0; j < rcache->depot_size; ++j) { + iova_magazine_free_pfns(rcache->depot[j], iovad); + iova_magazine_free(rcache->depot[j]); + } + spin_unlock_irqrestore(&rcache->lock, flags); + } +} + +/* + * free all the IOVA ranges cached by a cpu (used when cpu is unplugged) + */ +void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad) +{ + struct iova_cpu_rcache *cpu_rcache; + struct iova_rcache *rcache; + unsigned long flags; + int i; + + for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) { + rcache = &iovad->rcaches[i]; + cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu); + spin_lock_irqsave(&cpu_rcache->lock, flags); + iova_magazine_free_pfns(cpu_rcache->loaded, iovad); + iova_magazine_free_pfns(cpu_rcache->prev, iovad); + spin_unlock_irqrestore(&cpu_rcache->lock, flags); + } +} + MODULE_AUTHOR("Anil S Keshavamurthy "); MODULE_LICENSE("GPL"); diff --git a/include/linux/iova.h b/include/linux/iova.h index 92f7177db2ce..f27bb2c62fca 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -19,8 +19,21 @@ /* iova structure */ struct iova { struct rb_node node; - unsigned long pfn_hi; /* IOMMU dish out addr hi */ - unsigned long pfn_lo; /* IOMMU dish out addr lo */ + unsigned long pfn_hi; /* Highest allocated pfn */ + unsigned long pfn_lo; /* Lowest allocated pfn */ +}; + +struct iova_magazine; +struct iova_cpu_rcache; + +#define IOVA_RANGE_CACHE_MAX_SIZE 6 /* log of max cached IOVA range size (in pages) */ +#define MAX_GLOBAL_MAGS 32 /* magazines per bin */ + +struct iova_rcache { + spinlock_t lock; + unsigned long depot_size; + struct iova_magazine *depot[MAX_GLOBAL_MAGS]; + struct iova_cpu_rcache __percpu *cpu_rcaches; }; /* holds all the iova translations for a domain */ @@ -31,6 +44,7 @@ struct iova_domain { unsigned long granule; /* pfn granularity for this domain */ unsigned long start_pfn; /* Lower limit for this domain */ unsigned long dma_32bit_pfn; + struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE]; /* IOVA range caches */ }; static inline unsigned long iova_size(struct iova *iova) @@ -78,6 +92,10 @@ void __free_iova(struct iova_domain *iovad, struct iova *iova); struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size, unsigned long limit_pfn, bool size_aligned); +void free_iova_fast(struct iova_domain *iovad, unsigned long pfn, + unsigned long size); +unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size, + unsigned long limit_pfn); struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo, unsigned long pfn_hi); void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to); @@ -87,5 +105,6 @@ struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn); void put_iova_domain(struct iova_domain *iovad); struct iova *split_and_remove_iova(struct iova_domain *iovad, struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi); +void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad); #endif -- cgit From 80e0f8d94d3090f0f7bf3faf3e6180e920ee0d22 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 24 Feb 2016 14:12:59 +0530 Subject: pinctrl: Add devm_ apis for pinctrl_{register, unregister} Add device managed APIs devm_pinctrl_register() and devm_pinctrl_unregister() for the APIs pinctrl_register() and pinctrl_unregister(). This helps in reducing code in error path and sometimes removal of .remove callback for driver unbind. Signed-off-by: Laxman Dewangan Reviewed-by: Philipp Zabel Acked-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 63 +++++++++++++++++++++++++++++++++++++++++ include/linux/pinctrl/pinctrl.h | 6 ++++ 2 files changed, 69 insertions(+) (limited to 'include/linux') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index f67a8b7a4e18..21df52e8192a 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1872,6 +1872,69 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) } EXPORT_SYMBOL_GPL(pinctrl_unregister); +static void devm_pinctrl_dev_release(struct device *dev, void *res) +{ + struct pinctrl_dev *pctldev = *(struct pinctrl_dev **)res; + + pinctrl_unregister(pctldev); +} + +static int devm_pinctrl_dev_match(struct device *dev, void *res, void *data) +{ + struct pctldev **r = res; + + if (WARN_ON(!r || !*r) + return 0; + + return *r == data; +} + +/** + * devm_pinctrl_register() - Resource managed version of pinctrl_register(). + * @dev: parent device for this pin controller + * @pctldesc: descriptor for this pin controller + * @driver_data: private pin controller data for this pin controller + * + * Returns an error pointer if pincontrol register failed. Otherwise + * it returns valid pinctrl handle. + * + * The pinctrl device will be automatically released when the device is unbound. + */ +struct pinctrl_dev *devm_pinctrl_register(struct device *dev, + struct pinctrl_desc *pctldesc, + void *driver_data) +{ + struct pinctrl_dev **ptr, *pctldev; + + ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + pctldev = pinctrl_register(pctldesc, dev, driver_data); + if (IS_ERR(pctldev)) { + devres_free(ptr); + return pctldev; + } + + *ptr = pctldev; + devres_add(dev, ptr); + + return pctldev; +} +EXPORT_SYMBOL_GPL(devm_pinctrl_register); + +/** + * devm_pinctrl_unregister() - Resource managed version of pinctrl_unregister(). + * @dev: device for which which resource was allocated + * @pctldev: the pinctrl device to unregister. + */ +void devm_pinctrl_unregister(struct device *dev, struct pinctrl_dev *pctldev) +{ + WARN_ON(devres_release(dev, devm_pinctrl_dev_release, + devm_pinctrl_dev_match, pctldev)); +} +EXPORT_SYMBOL_GPL(devm_pinctrl_unregister); + static int __init pinctrl_init(void) { pr_info("initialized pinctrl subsystem\n"); diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 9ba59fcba549..a42e57da270d 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -144,6 +144,12 @@ struct pinctrl_desc { extern struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data); extern void pinctrl_unregister(struct pinctrl_dev *pctldev); +extern struct pinctrl_dev *devm_pinctrl_register(struct device *dev, + struct pinctrl_desc *pctldesc, + void *driver_data); +extern void devm_pinctrl_unregister(struct device *dev, + struct pinctrl_dev *pctldev); + extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin); extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); -- cgit From b53f27e4fa0d0e72d897830cc4f3f83d2a25d952 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:23 -0700 Subject: string_helpers: add kstrdup_quotable Handle allocating and escaping a string safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/string_helpers.h | 2 ++ lib/string_helpers.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include/linux') diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index dabe643eb5fa..9de228af00c1 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -68,4 +68,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only); } +char *kstrdup_quotable(const char *src, gfp_t gfp); + #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 5c88204b6f1f..aa00c9f989ee 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -534,3 +535,30 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, return p - dst; } EXPORT_SYMBOL(string_escape_mem); + +/* + * Return an allocated string that has been escaped of special characters + * and double quotes, making it safe to log in quotes. + */ +char *kstrdup_quotable(const char *src, gfp_t gfp) +{ + size_t slen, dlen; + char *dst; + const int flags = ESCAPE_HEX; + const char esc[] = "\f\n\r\t\v\a\e\\\""; + + if (!src) + return NULL; + slen = strlen(src); + + dlen = string_escape_mem(src, slen, NULL, 0, flags, esc); + dst = kmalloc(dlen + 1, gfp); + if (!dst) + return NULL; + + WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen); + dst[dlen] = '\0'; + + return dst; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable); -- cgit From 0d0443288f2244d7054796086e481ddef6abdbba Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:24 -0700 Subject: string_helpers: add kstrdup_quotable_cmdline Provide an escaped (but readable: no inter-argument NULLs) commandline safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/string_helpers.h | 1 + lib/string_helpers.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include/linux') diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index 9de228af00c1..684d2695fc36 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -69,5 +69,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, } char *kstrdup_quotable(const char *src, gfp_t gfp); +char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp); #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index aa00c9f989ee..b16ee85aaf87 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -562,3 +563,36 @@ char *kstrdup_quotable(const char *src, gfp_t gfp) return dst; } EXPORT_SYMBOL_GPL(kstrdup_quotable); + +/* + * Returns allocated NULL-terminated string containing process + * command line, with inter-argument NULLs replaced with spaces, + * and other special characters escaped. + */ +char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp) +{ + char *buffer, *quoted; + int i, res; + + buffer = kmalloc(PAGE_SIZE, GFP_TEMPORARY); + if (!buffer) + return NULL; + + res = get_cmdline(task, buffer, PAGE_SIZE - 1); + buffer[res] = '\0'; + + /* Collapse trailing NULLs, leave res pointing to last non-NULL. */ + while (--res >= 0 && buffer[res] == '\0') + ; + + /* Replace inter-argument NULLs. */ + for (i = 0; i <= res; i++) + if (buffer[i] == '\0') + buffer[i] = ' '; + + /* Make sure result is printable. */ + quoted = kstrdup_quotable(buffer, gfp); + kfree(buffer); + return quoted; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline); -- cgit From 21985319add60b55fc27230d9421a3e5af7e998a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:25 -0700 Subject: string_helpers: add kstrdup_quotable_file Allocate a NULL-terminated file path with special characters escaped, safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/string_helpers.h | 3 +++ lib/string_helpers.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index 684d2695fc36..5ce9538f290e 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -3,6 +3,8 @@ #include +struct file; + /* Descriptions of the types of units to * print in */ enum string_size_units { @@ -70,5 +72,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, char *kstrdup_quotable(const char *src, gfp_t gfp); char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp); +char *kstrdup_quotable_file(struct file *file, gfp_t gfp); #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index b16ee85aaf87..ecaac2c0526f 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -596,3 +598,31 @@ char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp) return quoted; } EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline); + +/* + * Returns allocated NULL-terminated string containing pathname, + * with special characters escaped, able to be safely logged. If + * there is an error, the leading character will be "<". + */ +char *kstrdup_quotable_file(struct file *file, gfp_t gfp) +{ + char *temp, *pathname; + + if (!file) + return kstrdup("", gfp); + + /* We add 11 spaces for ' (deleted)' to be appended */ + temp = kmalloc(PATH_MAX + 11, GFP_TEMPORARY); + if (!temp) + return kstrdup("", gfp); + + pathname = file_path(file, temp, PATH_MAX + 11); + if (IS_ERR(pathname)) + pathname = kstrdup("", gfp); + else + pathname = kstrdup_quotable(pathname, gfp); + + kfree(temp); + return pathname; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable_file); -- cgit From 1284ab5b2dcb927d38e4f3fbc2e307f3d1af9262 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 20 Apr 2016 15:46:27 -0700 Subject: fs: define a string representation of the kernel_read_file_id enumeration A string representation of the kernel_read_file_id enumeration is needed for displaying messages (eg. pr_info, auditing) that can be used by multiple LSMs and the integrity subsystem. To simplify keeping the list of strings up to date with the enumeration, this patch defines two new preprocessing macros named __fid_enumify and __fid_stringify to create the enumeration and an array of strings. kernel_read_file_id_str() returns a string based on the enumeration. Signed-off-by: Mimi Zohar [kees: removed removal of my old version, constified pointer values] Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/fs.h | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 14a97194b34b..90477550b935 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2580,15 +2580,34 @@ static inline void i_readcount_inc(struct inode *inode) #endif extern int do_pipe_flags(int *, int); +#define __kernel_read_file_id(id) \ + id(UNKNOWN, unknown) \ + id(FIRMWARE, firmware) \ + id(MODULE, kernel-module) \ + id(KEXEC_IMAGE, kexec-image) \ + id(KEXEC_INITRAMFS, kexec-initramfs) \ + id(POLICY, security-policy) \ + id(MAX_ID, ) + +#define __fid_enumify(ENUM, dummy) READING_ ## ENUM, +#define __fid_stringify(dummy, str) #str, + enum kernel_read_file_id { - READING_FIRMWARE = 1, - READING_MODULE, - READING_KEXEC_IMAGE, - READING_KEXEC_INITRAMFS, - READING_POLICY, - READING_MAX_ID + __kernel_read_file_id(__fid_enumify) +}; + +static const char * const kernel_read_file_str[] = { + __kernel_read_file_id(__fid_stringify) }; +static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) +{ + if (id < 0 || id >= READING_MAX_ID) + return kernel_read_file_str[READING_UNKNOWN]; + + return kernel_read_file_str[id]; +} + extern int kernel_read(struct file *, loff_t, char *, unsigned long); extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, enum kernel_read_file_id); -- cgit From 9b091556a073a9f5f93e2ad23d118f45c4796a84 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:28 -0700 Subject: LSM: LoadPin for kernel file loading restrictions This LSM enforces that kernel-loaded files (modules, firmware, etc) must all come from the same filesystem, with the expectation that such a filesystem is backed by a read-only device such as dm-verity or CDROM. This allows systems that have a verified and/or unchangeable filesystem to enforce module and firmware loading restrictions without needing to sign the files individually. Signed-off-by: Kees Cook Acked-by: Serge Hallyn Signed-off-by: James Morris --- Documentation/security/LoadPin.txt | 17 ++++ MAINTAINERS | 6 ++ include/linux/lsm_hooks.h | 5 + security/Kconfig | 1 + security/Makefile | 2 + security/loadpin/Kconfig | 10 ++ security/loadpin/Makefile | 1 + security/loadpin/loadpin.c | 190 +++++++++++++++++++++++++++++++++++++ security/security.c | 1 + 9 files changed, 233 insertions(+) create mode 100644 Documentation/security/LoadPin.txt create mode 100644 security/loadpin/Kconfig create mode 100644 security/loadpin/Makefile create mode 100644 security/loadpin/loadpin.c (limited to 'include/linux') diff --git a/Documentation/security/LoadPin.txt b/Documentation/security/LoadPin.txt new file mode 100644 index 000000000000..e11877f5d3d4 --- /dev/null +++ b/Documentation/security/LoadPin.txt @@ -0,0 +1,17 @@ +LoadPin is a Linux Security Module that ensures all kernel-loaded files +(modules, firmware, etc) all originate from the same filesystem, with +the expectation that such a filesystem is backed by a read-only device +such as dm-verity or CDROM. This allows systems that have a verified +and/or unchangeable filesystem to enforce module and firmware loading +restrictions without needing to sign the files individually. + +The LSM is selectable at build-time with CONFIG_SECURITY_LOADPIN, and +can be controlled at boot-time with the kernel command line option +"loadpin.enabled". By default, it is enabled, but can be disabled at +boot ("loadpin.enabled=0"). + +LoadPin starts pinning when it sees the first file loaded. If the +block device backing the filesystem is not read-only, a sysctl is +created to toggle pinning: /proc/sys/kernel/loadpin/enabled. (Having +a mutable filesystem means pinning is mutable too, but having the +sysctl allows for easy testing on systems with a mutable filesystem.) diff --git a/MAINTAINERS b/MAINTAINERS index 1c32f8a3d6c4..b4b1e8179018 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9962,6 +9962,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git S: Supported F: security/apparmor/ +LOADPIN SECURITY MODULE +M: Kees Cook +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin +S: Supported +F: security/loadpin/ + YAMA SECURITY MODULE M: Kees Cook T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ae2537886177..6e466fc0666c 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1892,5 +1892,10 @@ extern void __init yama_add_hooks(void); #else static inline void __init yama_add_hooks(void) { } #endif +#ifdef CONFIG_SECURITY_LOADPIN +void __init loadpin_add_hooks(void); +#else +static inline void loadpin_add_hooks(void) { }; +#endif #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/Kconfig b/security/Kconfig index e45237897b43..176758cdfa57 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -122,6 +122,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig source security/apparmor/Kconfig +source security/loadpin/Kconfig source security/yama/Kconfig source security/integrity/Kconfig diff --git a/security/Makefile b/security/Makefile index c9bfbc84ff50..f2d71cdb8e19 100644 --- a/security/Makefile +++ b/security/Makefile @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama +subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin # always enable default capabilities obj-y += commoncap.o @@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig new file mode 100644 index 000000000000..c668ac4eda65 --- /dev/null +++ b/security/loadpin/Kconfig @@ -0,0 +1,10 @@ +config SECURITY_LOADPIN + bool "Pin load of kernel files (modules, fw, etc) to one filesystem" + depends on SECURITY && BLOCK + help + Any files read through the kernel file reading interface + (kernel modules, firmware, kexec images, security policy) will + be pinned to the first filesystem used for loading. Any files + that come from other filesystems will be rejected. This is best + used on systems without an initrd that have a root filesystem + backed by a read-only device such as dm-verity or a CDROM. diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile new file mode 100644 index 000000000000..c2d77f83037b --- /dev/null +++ b/security/loadpin/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c new file mode 100644 index 000000000000..e4debae3c4d6 --- /dev/null +++ b/security/loadpin/loadpin.c @@ -0,0 +1,190 @@ +/* + * Module and Firmware Pinning Security Module + * + * Copyright 2011-2016 Google Inc. + * + * Author: Kees Cook + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "LoadPin: " fmt + +#include +#include +#include +#include +#include +#include +#include /* current */ +#include + +static void report_load(const char *origin, struct file *file, char *operation) +{ + char *cmdline, *pathname; + + pathname = kstrdup_quotable_file(file, GFP_KERNEL); + cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL); + + pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n", + origin, operation, + (pathname && pathname[0] != '<') ? "\"" : "", + pathname, + (pathname && pathname[0] != '<') ? "\"" : "", + task_pid_nr(current), + cmdline ? "\"" : "", cmdline, cmdline ? "\"" : ""); + + kfree(cmdline); + kfree(pathname); +} + +static int enabled = 1; +static struct super_block *pinned_root; +static DEFINE_SPINLOCK(pinned_root_spinlock); + +#ifdef CONFIG_SYSCTL +static int zero; +static int one = 1; + +static struct ctl_path loadpin_sysctl_path[] = { + { .procname = "kernel", }, + { .procname = "loadpin", }, + { } +}; + +static struct ctl_table loadpin_sysctl_table[] = { + { + .procname = "enabled", + .data = &enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { } +}; + +/* + * This must be called after early kernel init, since then the rootdev + * is available. + */ +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + bool ro = false; + + /* + * If load pinning is not enforced via a read-only block + * device, allow sysctl to change modes for testing. + */ + if (mnt_sb->s_bdev) { + ro = bdev_read_only(mnt_sb->s_bdev); + pr_info("dev(%u,%u): %s\n", + MAJOR(mnt_sb->s_bdev->bd_dev), + MINOR(mnt_sb->s_bdev->bd_dev), + ro ? "read-only" : "writable"); + } else + pr_info("mnt_sb lacks block device, treating as: writable\n"); + + if (!ro) { + if (!register_sysctl_paths(loadpin_sysctl_path, + loadpin_sysctl_table)) + pr_notice("sysctl registration failed!\n"); + else + pr_info("load pinning can be disabled.\n"); + } else + pr_info("load pinning engaged.\n"); +} +#else +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + pr_info("load pinning engaged.\n"); +} +#endif + +static void loadpin_sb_free_security(struct super_block *mnt_sb) +{ + /* + * When unmounting the filesystem we were using for load + * pinning, we acknowledge the superblock release, but make sure + * no other modules or firmware can be loaded. + */ + if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) { + pinned_root = ERR_PTR(-EIO); + pr_info("umount pinned fs: refusing further loads\n"); + } +} + +static int loadpin_read_file(struct file *file, enum kernel_read_file_id id) +{ + struct super_block *load_root; + const char *origin = kernel_read_file_id_str(id); + + /* This handles the older init_module API that has a NULL file. */ + if (!file) { + if (!enabled) { + report_load(origin, NULL, "old-api-pinning-ignored"); + return 0; + } + + report_load(origin, NULL, "old-api-denied"); + return -EPERM; + } + + load_root = file->f_path.mnt->mnt_sb; + + /* First loaded module/firmware defines the root for all others. */ + spin_lock(&pinned_root_spinlock); + /* + * pinned_root is only NULL at startup. Otherwise, it is either + * a valid reference, or an ERR_PTR. + */ + if (!pinned_root) { + pinned_root = load_root; + /* + * Unlock now since it's only pinned_root we care about. + * In the worst case, we will (correctly) report pinning + * failures before we have announced that pinning is + * enabled. This would be purely cosmetic. + */ + spin_unlock(&pinned_root_spinlock); + check_pinning_enforcement(pinned_root); + report_load(origin, file, "pinned"); + } else { + spin_unlock(&pinned_root_spinlock); + } + + if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) { + if (unlikely(!enabled)) { + report_load(origin, file, "pinning-ignored"); + return 0; + } + + report_load(origin, file, "denied"); + return -EPERM; + } + + return 0; +} + +static struct security_hook_list loadpin_hooks[] = { + LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), + LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), +}; + +void __init loadpin_add_hooks(void) +{ + pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); + security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); +} + +/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ +module_param(enabled, int, 0); +MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)"); diff --git a/security/security.c b/security/security.c index 554c3fb7d4a5..e42860899f23 100644 --- a/security/security.c +++ b/security/security.c @@ -60,6 +60,7 @@ int __init security_init(void) */ capability_add_hooks(); yama_add_hooks(); + loadpin_add_hooks(); /* * Load all the remaining security modules. -- cgit From 92a39d9043ba5ff98adb1c31491f00c7bea5466e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 23 Mar 2016 17:38:24 +0100 Subject: clk: composite: Add unregister function The composite clock didn't have any unregistration function, which forced us to use clk_unregister directly on it. While it was already not great from an API point of view, it also meant that we were leaking the clk_composite structure allocated in clk_register_composite. Add a clk_unregister_composite function to fix this. Signed-off-by: Maxime Ripard Signed-off-by: Stephen Boyd --- drivers/clk/clk-composite.c | 15 +++++++++++++++ include/linux/clk-provider.h | 1 + 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 1f903e1f86a2..b0f3b84ebd13 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -286,3 +286,18 @@ err: kfree(composite); return clk; } + +void clk_unregister_composite(struct clk *clk) +{ + struct clk_composite *composite; + struct clk_hw *hw; + + hw = __clk_get_hw(clk); + if (!hw) + return; + + composite = to_clk_composite(hw); + + clk_unregister(clk); + kfree(composite); +} diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index da95258127aa..26a8c9b7be71 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -603,6 +603,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags); +void clk_unregister_composite(struct clk *clk); /*** * struct clk_gpio_gate - gpio gated clock -- cgit From be32bcbbd18213803ff24e480340d4d2cca1df4f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 Apr 2016 14:02:36 +0200 Subject: soc: renesas: Move pm-rcar to drivers/soc/renesas/rcar-sysc Move the pm-rcar driver from arch/arm/mach-shmobile/ to drivers/soc/renesas/, and its header file to include/linux/soc/renesas/, so it can be shared between arm32 (R-Car H1 and Gen2) and arm64 (R-Car Gen3). Rename it to rcar-sysc as it's really a driver for the R-Car System Controller (SYSC). Kill the intermediate PM_RCAR config symbol, as it's not user configurable anymore, and to prepare for SoC-specific make rules. Add the missing #include to rcar-sysc.h, which was exposed by different include order. Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- MAINTAINERS | 4 + arch/arm/mach-shmobile/Kconfig | 11 +-- arch/arm/mach-shmobile/Makefile | 1 - arch/arm/mach-shmobile/pm-r8a7779.c | 3 +- arch/arm/mach-shmobile/pm-rcar-gen2.c | 2 +- arch/arm/mach-shmobile/pm-rcar.c | 164 ---------------------------------- arch/arm/mach-shmobile/pm-rcar.h | 15 ---- arch/arm/mach-shmobile/smp-r8a7779.c | 2 +- arch/arm/mach-shmobile/smp-r8a7790.c | 2 +- drivers/soc/Makefile | 3 +- drivers/soc/renesas/Makefile | 5 ++ drivers/soc/renesas/rcar-sysc.c | 164 ++++++++++++++++++++++++++++++++++ include/linux/soc/renesas/rcar-sysc.h | 17 ++++ 13 files changed, 201 insertions(+), 192 deletions(-) delete mode 100644 arch/arm/mach-shmobile/pm-rcar.c delete mode 100644 arch/arm/mach-shmobile/pm-rcar.h create mode 100644 drivers/soc/renesas/Makefile create mode 100644 drivers/soc/renesas/rcar-sysc.c create mode 100644 include/linux/soc/renesas/rcar-sysc.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 03e00c7c88eb..a0e23922a90b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1491,6 +1491,8 @@ Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next S: Supported F: arch/arm64/boot/dts/renesas/ +F: drivers/soc/renesas/ +F: include/linux/soc/renesas/ ARM/RISCPC ARCHITECTURE M: Russell King @@ -1604,6 +1606,8 @@ F: arch/arm/configs/shmobile_defconfig F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: drivers/sh/ +F: drivers/soc/renesas/ +F: include/linux/soc/renesas/ ARM/SOCFPGA ARCHITECTURE M: Dinh Nguyen diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index f2bc5c353119..fe4ccb52f921 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -4,11 +4,6 @@ config ARCH_SHMOBILE config ARCH_SHMOBILE_MULTI bool -config PM_RCAR - bool - select PM - select PM_GENERIC_DOMAINS - config PM_RMOBILE bool select PM @@ -16,13 +11,15 @@ config PM_RMOBILE config ARCH_RCAR_GEN1 bool - select PM_RCAR + select PM + select PM_GENERIC_DOMAINS select RENESAS_INTC_IRQPIN select SYS_SUPPORTS_SH_TMU config ARCH_RCAR_GEN2 bool - select PM_RCAR + select PM + select PM_GENERIC_DOMAINS select RENESAS_IRQC select SYS_SUPPORTS_SH_CMT select PCI_DOMAINS if PCI diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index a65c80ac9009..ebb909c55b85 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -39,7 +39,6 @@ smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o headsmp-scu.o platsmp-scu.o # PM objects obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o -obj-$(CONFIG_PM_RCAR) += pm-rcar.o obj-$(CONFIG_PM_RMOBILE) += pm-rmobile.o obj-$(CONFIG_ARCH_RCAR_GEN2) += pm-rcar-gen2.o diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c index 14c42a1bdf1e..4174cbcbc467 100644 --- a/arch/arm/mach-shmobile/pm-r8a7779.c +++ b/arch/arm/mach-shmobile/pm-r8a7779.c @@ -9,9 +9,10 @@ * for more details. */ +#include + #include -#include "pm-rcar.h" #include "r8a7779.h" /* SYSC */ diff --git a/arch/arm/mach-shmobile/pm-rcar-gen2.c b/arch/arm/mach-shmobile/pm-rcar-gen2.c index 6815781ad116..691ac166a277 100644 --- a/arch/arm/mach-shmobile/pm-rcar-gen2.c +++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c @@ -13,9 +13,9 @@ #include #include #include +#include #include #include "common.h" -#include "pm-rcar.h" #include "rcar-gen2.h" /* RST */ diff --git a/arch/arm/mach-shmobile/pm-rcar.c b/arch/arm/mach-shmobile/pm-rcar.c deleted file mode 100644 index 0af05d288b09..000000000000 --- a/arch/arm/mach-shmobile/pm-rcar.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * R-Car SYSC Power management support - * - * Copyright (C) 2014 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include -#include -#include -#include -#include -#include "pm-rcar.h" - -/* SYSC Common */ -#define SYSCSR 0x00 /* SYSC Status Register */ -#define SYSCISR 0x04 /* Interrupt Status Register */ -#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ -#define SYSCIER 0x0c /* Interrupt Enable Register */ -#define SYSCIMR 0x10 /* Interrupt Mask Register */ - -/* SYSC Status Register */ -#define SYSCSR_PONENB 1 /* Ready for power resume requests */ -#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ - -/* - * Power Control Register Offsets inside the register block for each domain - * Note: The "CR" registers for ARM cores exist on H1 only - * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 - */ -#define PWRSR_OFFS 0x00 /* Power Status Register */ -#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ -#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ -#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ -#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ -#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ - - -#define SYSCSR_RETRIES 100 -#define SYSCSR_DELAY_US 1 - -#define PWRER_RETRIES 100 -#define PWRER_DELAY_US 1 - -#define SYSCISR_RETRIES 1000 -#define SYSCISR_DELAY_US 1 - -static void __iomem *rcar_sysc_base; -static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ - -static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int sr_bit, reg_offs; - int k; - - if (on) { - sr_bit = SYSCSR_PONENB; - reg_offs = PWRONCR_OFFS; - } else { - sr_bit = SYSCSR_POFFENB; - reg_offs = PWROFFCR_OFFS; - } - - /* Wait until SYSC is ready to accept a power request */ - for (k = 0; k < SYSCSR_RETRIES; k++) { - if (ioread32(rcar_sysc_base + SYSCSR) & BIT(sr_bit)) - break; - udelay(SYSCSR_DELAY_US); - } - - if (k == SYSCSR_RETRIES) - return -EAGAIN; - - /* Submit power shutoff or power resume request */ - iowrite32(BIT(sysc_ch->chan_bit), - rcar_sysc_base + sysc_ch->chan_offs + reg_offs); - - return 0; -} - -static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int isr_mask = BIT(sysc_ch->isr_bit); - unsigned int chan_mask = BIT(sysc_ch->chan_bit); - unsigned int status; - unsigned long flags; - int ret = 0; - int k; - - spin_lock_irqsave(&rcar_sysc_lock, flags); - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - /* Submit power shutoff or resume request until it was accepted */ - for (k = 0; k < PWRER_RETRIES; k++) { - ret = rcar_sysc_pwr_on_off(sysc_ch, on); - if (ret) - goto out; - - status = ioread32(rcar_sysc_base + - sysc_ch->chan_offs + PWRER_OFFS); - if (!(status & chan_mask)) - break; - - udelay(PWRER_DELAY_US); - } - - if (k == PWRER_RETRIES) { - ret = -EIO; - goto out; - } - - /* Wait until the power shutoff or resume request has completed * */ - for (k = 0; k < SYSCISR_RETRIES; k++) { - if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) - break; - udelay(SYSCISR_DELAY_US); - } - - if (k == SYSCISR_RETRIES) - ret = -EIO; - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - out: - spin_unlock_irqrestore(&rcar_sysc_lock, flags); - - pr_debug("sysc power domain %d: %08x -> %d\n", - sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); - return ret; -} - -int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, false); -} - -int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, true); -} - -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) -{ - unsigned int st; - - st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); - if (st & BIT(sysc_ch->chan_bit)) - return true; - - return false; -} - -void __iomem *rcar_sysc_init(phys_addr_t base) -{ - rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); - if (!rcar_sysc_base) - panic("unable to ioremap R-Car SYSC hardware block\n"); - - return rcar_sysc_base; -} diff --git a/arch/arm/mach-shmobile/pm-rcar.h b/arch/arm/mach-shmobile/pm-rcar.h deleted file mode 100644 index 1b901db4a24c..000000000000 --- a/arch/arm/mach-shmobile/pm-rcar.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef PM_RCAR_H -#define PM_RCAR_H - -struct rcar_sysc_ch { - u16 chan_offs; - u8 chan_bit; - u8 isr_bit; -}; - -int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch); -int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch); -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch); -void __iomem *rcar_sysc_init(phys_addr_t base); - -#endif /* PM_RCAR_H */ diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c index f5c31fbc10b2..c6951ee24588 100644 --- a/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -19,13 +19,13 @@ #include #include #include +#include #include #include #include #include "common.h" -#include "pm-rcar.h" #include "r8a7779.h" #define AVECR IOMEM(0xfe700040) diff --git a/arch/arm/mach-shmobile/smp-r8a7790.c b/arch/arm/mach-shmobile/smp-r8a7790.c index f6426c6fdefc..28f26d5362d8 100644 --- a/arch/arm/mach-shmobile/smp-r8a7790.c +++ b/arch/arm/mach-shmobile/smp-r8a7790.c @@ -17,12 +17,12 @@ #include #include #include +#include #include #include "common.h" #include "platsmp-apmu.h" -#include "pm-rcar.h" #include "rcar-gen2.h" #include "r8a7790.h" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 5ade71306ee1..380230f03874 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -9,7 +9,8 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_QCOM) += qcom/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_ARCH_RENESAS) += renesas/ +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile new file mode 100644 index 000000000000..2b64f6c94681 --- /dev/null +++ b/drivers/soc/renesas/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_ARCH_R8A7779) += rcar-sysc.o +obj-$(CONFIG_ARCH_R8A7790) += rcar-sysc.o +obj-$(CONFIG_ARCH_R8A7791) += rcar-sysc.o +obj-$(CONFIG_ARCH_R8A7793) += rcar-sysc.o +obj-$(CONFIG_ARCH_R8A7794) += rcar-sysc.o diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c new file mode 100644 index 000000000000..d59bcdf78f0b --- /dev/null +++ b/drivers/soc/renesas/rcar-sysc.c @@ -0,0 +1,164 @@ +/* + * R-Car SYSC Power management support + * + * Copyright (C) 2014 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include + +/* SYSC Common */ +#define SYSCSR 0x00 /* SYSC Status Register */ +#define SYSCISR 0x04 /* Interrupt Status Register */ +#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ +#define SYSCIER 0x0c /* Interrupt Enable Register */ +#define SYSCIMR 0x10 /* Interrupt Mask Register */ + +/* SYSC Status Register */ +#define SYSCSR_PONENB 1 /* Ready for power resume requests */ +#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ + +/* + * Power Control Register Offsets inside the register block for each domain + * Note: The "CR" registers for ARM cores exist on H1 only + * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 + */ +#define PWRSR_OFFS 0x00 /* Power Status Register */ +#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ +#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ +#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ +#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ +#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ + + +#define SYSCSR_RETRIES 100 +#define SYSCSR_DELAY_US 1 + +#define PWRER_RETRIES 100 +#define PWRER_DELAY_US 1 + +#define SYSCISR_RETRIES 1000 +#define SYSCISR_DELAY_US 1 + +static void __iomem *rcar_sysc_base; +static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ + +static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int sr_bit, reg_offs; + int k; + + if (on) { + sr_bit = SYSCSR_PONENB; + reg_offs = PWRONCR_OFFS; + } else { + sr_bit = SYSCSR_POFFENB; + reg_offs = PWROFFCR_OFFS; + } + + /* Wait until SYSC is ready to accept a power request */ + for (k = 0; k < SYSCSR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCSR) & BIT(sr_bit)) + break; + udelay(SYSCSR_DELAY_US); + } + + if (k == SYSCSR_RETRIES) + return -EAGAIN; + + /* Submit power shutoff or power resume request */ + iowrite32(BIT(sysc_ch->chan_bit), + rcar_sysc_base + sysc_ch->chan_offs + reg_offs); + + return 0; +} + +static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int isr_mask = BIT(sysc_ch->isr_bit); + unsigned int chan_mask = BIT(sysc_ch->chan_bit); + unsigned int status; + unsigned long flags; + int ret = 0; + int k; + + spin_lock_irqsave(&rcar_sysc_lock, flags); + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PWRER_RETRIES; k++) { + ret = rcar_sysc_pwr_on_off(sysc_ch, on); + if (ret) + goto out; + + status = ioread32(rcar_sysc_base + + sysc_ch->chan_offs + PWRER_OFFS); + if (!(status & chan_mask)) + break; + + udelay(PWRER_DELAY_US); + } + + if (k == PWRER_RETRIES) { + ret = -EIO; + goto out; + } + + /* Wait until the power shutoff or resume request has completed * */ + for (k = 0; k < SYSCISR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) + break; + udelay(SYSCISR_DELAY_US); + } + + if (k == SYSCISR_RETRIES) + ret = -EIO; + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + out: + spin_unlock_irqrestore(&rcar_sysc_lock, flags); + + pr_debug("sysc power domain %d: %08x -> %d\n", + sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); + return ret; +} + +int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_power(sysc_ch, false); +} + +int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_power(sysc_ch, true); +} + +bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) +{ + unsigned int st; + + st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); + if (st & BIT(sysc_ch->chan_bit)) + return true; + + return false; +} + +void __iomem *rcar_sysc_init(phys_addr_t base) +{ + rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); + if (!rcar_sysc_base) + panic("unable to ioremap R-Car SYSC hardware block\n"); + + return rcar_sysc_base; +} diff --git a/include/linux/soc/renesas/rcar-sysc.h b/include/linux/soc/renesas/rcar-sysc.h new file mode 100644 index 000000000000..96f30c288388 --- /dev/null +++ b/include/linux/soc/renesas/rcar-sysc.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_SOC_RENESAS_RCAR_SYSC_H__ +#define __LINUX_SOC_RENESAS_RCAR_SYSC_H__ + +#include + +struct rcar_sysc_ch { + u16 chan_offs; + u8 chan_bit; + u8 isr_bit; +}; + +int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch); +int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch); +bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch); +void __iomem *rcar_sysc_init(phys_addr_t base); + +#endif /* __LINUX_SOC_RENESAS_RCAR_SYSC_H__ */ -- cgit From 2f024cef5b064d5498fe466dfe9492d46b5b12a4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 Apr 2016 14:02:39 +0200 Subject: soc: renesas: rcar-sysc: Make rcar_sysc_power_is_off() static As of commit b12ff41658171f53 ("ARM: shmobile: r8a7779: Remove legacy PM Domain remainings"), rcar_sysc_power_is_off() is no longer used from SoC-specific code. Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 2 +- include/linux/soc/renesas/rcar-sysc.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 95f2b59cbd76..4e760d63f2ab 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -152,7 +152,7 @@ int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) return rcar_sysc_power(sysc_ch, true); } -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) +static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) { unsigned int st; diff --git a/include/linux/soc/renesas/rcar-sysc.h b/include/linux/soc/renesas/rcar-sysc.h index 96f30c288388..92fc613ab23d 100644 --- a/include/linux/soc/renesas/rcar-sysc.h +++ b/include/linux/soc/renesas/rcar-sysc.h @@ -11,7 +11,6 @@ struct rcar_sysc_ch { int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch); int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch); -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch); void __iomem *rcar_sysc_init(phys_addr_t base); #endif /* __LINUX_SOC_RENESAS_RCAR_SYSC_H__ */ -- cgit From a7ab72390b77062420fb50e4451f71c9321aae05 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:38:50 +0200 Subject: i2c: mux: add common data for every i2c-mux instance All i2c-muxes have a parent adapter and one or many child adapters. A mux also has some means of selection. Previously, this was stored per child adapter, but it is only needed to keep track of this per mux. Add an i2c mux core, that keeps track of this consistently. Also add some glue for users of the old interface, which will create one implicit mux core per child adapter. Signed-off-by: Peter Rosin Tested-by: Antti Palosaari Tested-by: Crestez Dan Leonard Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-mux.c | 175 ++++++++++++++++++++++++++++++++++++------------ include/linux/i2c-mux.h | 34 ++++++++++ 2 files changed, 168 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d4022878b2f0..5ce1b0704cb5 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,33 +28,34 @@ #include /* multiplexer per channel data */ +struct i2c_mux_priv_old { + void *mux_priv; + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); +}; + struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; - - struct i2c_adapter *parent; - struct device *mux_dev; - void *mux_priv; + struct i2c_mux_core *muxc; u32 chan_id; - - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = __i2c_transfer(parent, msgs, num); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -65,17 +66,18 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, int size, union i2c_smbus_data *data) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -84,7 +86,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, static u32 i2c_mux_functionality(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_adapter *parent = priv->muxc->parent; return parent->algo->functionality(parent); } @@ -102,6 +104,20 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); + + return priv->select(muxc->parent, priv->mux_priv, chan); +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); + + return priv->deselect(muxc->parent, priv->mux_priv, chan); +} + struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, struct device *mux_dev, void *mux_priv, u32 force_nr, u32 chan_id, @@ -111,21 +127,77 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, int (*deselect) (struct i2c_adapter *, void *, u32)) { + struct i2c_mux_core *muxc; + struct i2c_mux_priv_old *priv; + int ret; + + muxc = i2c_mux_alloc(parent, mux_dev, 1, sizeof(*priv), 0, + i2c_mux_select, i2c_mux_deselect); + if (!muxc) + return NULL; + + priv = i2c_mux_priv(muxc); + priv->select = select; + priv->deselect = deselect; + priv->mux_priv = mux_priv; + + ret = i2c_mux_add_adapter(muxc, force_nr, chan_id, class); + if (ret) { + devm_kfree(mux_dev, muxc); + return NULL; + } + + return muxc->adapter[0]; +} +EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); + +struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + struct device *dev, int max_adapters, + int sizeof_priv, u32 flags, + int (*select)(struct i2c_mux_core *, u32), + int (*deselect)(struct i2c_mux_core *, u32)) +{ + struct i2c_mux_core *muxc; + + muxc = devm_kzalloc(dev, sizeof(*muxc) + + max_adapters * sizeof(muxc->adapter[0]) + + sizeof_priv, GFP_KERNEL); + if (!muxc) + return NULL; + if (sizeof_priv) + muxc->priv = &muxc->adapter[max_adapters]; + + muxc->parent = parent; + muxc->dev = dev; + muxc->select = select; + muxc->deselect = deselect; + muxc->max_adapters = max_adapters; + + return muxc; +} +EXPORT_SYMBOL_GPL(i2c_mux_alloc); + +int i2c_mux_add_adapter(struct i2c_mux_core *muxc, + u32 force_nr, u32 chan_id, + unsigned int class) +{ + struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; char symlink_name[20]; int ret; - priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); + if (muxc->num_adapters >= muxc->max_adapters) { + dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) - return NULL; + return -ENOMEM; /* Set up private adapter data */ - priv->parent = parent; - priv->mux_dev = mux_dev; - priv->mux_priv = mux_priv; + priv->muxc = muxc; priv->chan_id = chan_id; - priv->select = select; - priv->deselect = deselect; /* Need to do algo dynamically because we don't know ahead * of time what sort of physical adapter we'll be dealing with. @@ -159,11 +231,11 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. */ - if (mux_dev->of_node) { + if (muxc->dev->of_node) { struct device_node *child; u32 reg; - for_each_child_of_node(mux_dev->of_node, child) { + for_each_child_of_node(muxc->dev->of_node, child) { ret = of_property_read_u32(child, "reg", ®); if (ret) continue; @@ -177,8 +249,9 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, /* * Associate the mux channel with an ACPI node. */ - if (has_acpi_companion(mux_dev)) - acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev), + if (has_acpi_companion(muxc->dev)) + acpi_preset_companion(&priv->adap.dev, + ACPI_COMPANION(muxc->dev), chan_id); if (force_nr) { @@ -192,33 +265,53 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, "failed to add mux-adapter (error=%d)\n", ret); kfree(priv); - return NULL; + return ret; } - WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"), - "can't create symlink to mux device\n"); + WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, + "mux_device"), + "can't create symlink to mux device\n"); snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); - WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name), - "can't create symlink for channel %u\n", chan_id); + WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, + symlink_name), + "can't create symlink for channel %u\n", chan_id); dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); - return &priv->adap; + muxc->adapter[muxc->num_adapters++] = &priv->adap; + return 0; } -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); +EXPORT_SYMBOL_GPL(i2c_mux_add_adapter); -void i2c_del_mux_adapter(struct i2c_adapter *adap) +void i2c_mux_del_adapters(struct i2c_mux_core *muxc) { - struct i2c_mux_priv *priv = adap->algo_data; char symlink_name[20]; - snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id); - sysfs_remove_link(&priv->mux_dev->kobj, symlink_name); + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; + + muxc->adapter[muxc->num_adapters] = NULL; + + snprintf(symlink_name, sizeof(symlink_name), + "channel-%u", priv->chan_id); + sysfs_remove_link(&muxc->dev->kobj, symlink_name); + + sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); + i2c_del_adapter(adap); + kfree(priv); + } +} +EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); + +void i2c_del_mux_adapter(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; - sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); - i2c_del_adapter(adap); - kfree(priv); + i2c_mux_del_adapters(muxc); + devm_kfree(muxc->dev, muxc); } EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index b5f9a007a3ab..71ac1b3f4f68 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -27,6 +27,31 @@ #ifdef __KERNEL__ +struct i2c_mux_core { + struct i2c_adapter *parent; + struct device *dev; + + void *priv; + + int (*select)(struct i2c_mux_core *, u32 chan_id); + int (*deselect)(struct i2c_mux_core *, u32 chan_id); + + int num_adapters; + int max_adapters; + struct i2c_adapter *adapter[0]; +}; + +struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + struct device *dev, int max_adapters, + int sizeof_priv, u32 flags, + int (*select)(struct i2c_mux_core *, u32), + int (*deselect)(struct i2c_mux_core *, u32)); + +static inline void *i2c_mux_priv(struct i2c_mux_core *muxc) +{ + return muxc->priv; +} + /* * Called to create a i2c bus on a multiplexed bus segment. * The mux_dev and chan_id parameters are passed to the select @@ -41,8 +66,17 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, void *mux_dev, u32 chan_id), int (*deselect) (struct i2c_adapter *, void *mux_dev, u32 chan_id)); +/* + * Called to create an i2c bus on a multiplexed bus segment. + * The chan_id parameter is passed to the select and deselect + * callback functions to perform hardware-specific mux control. + */ +int i2c_mux_add_adapter(struct i2c_mux_core *muxc, + u32 force_nr, u32 chan_id, + unsigned int class); void i2c_del_mux_adapter(struct i2c_adapter *adap); +void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- cgit From 23fe440c59b9f08afe108e7ec7b6714cb2a3b955 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 2 Mar 2016 15:14:22 +0100 Subject: i2c: mux: drop old unused i2c-mux api All i2c mux users are using an explicit i2c mux core, drop support for implicit i2c mux cores. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-mux.c | 63 ------------------------------------------------- include/linux/i2c-mux.h | 15 ------------ 2 files changed, 78 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 5ce1b0704cb5..25e9336b0e6e 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,12 +28,6 @@ #include /* multiplexer per channel data */ -struct i2c_mux_priv_old { - void *mux_priv; - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); -}; - struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; @@ -104,53 +98,6 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } -static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->select(muxc->parent, priv->mux_priv, chan); -} - -static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->deselect(muxc->parent, priv->mux_priv, chan); -} - -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *, u32), - int (*deselect) (struct i2c_adapter *, - void *, u32)) -{ - struct i2c_mux_core *muxc; - struct i2c_mux_priv_old *priv; - int ret; - - muxc = i2c_mux_alloc(parent, mux_dev, 1, sizeof(*priv), 0, - i2c_mux_select, i2c_mux_deselect); - if (!muxc) - return NULL; - - priv = i2c_mux_priv(muxc); - priv->select = select; - priv->deselect = deselect; - priv->mux_priv = mux_priv; - - ret = i2c_mux_add_adapter(muxc, force_nr, chan_id, class); - if (ret) { - devm_kfree(mux_dev, muxc); - return NULL; - } - - return muxc->adapter[0]; -} -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); - struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, struct device *dev, int max_adapters, int sizeof_priv, u32 flags, @@ -305,16 +252,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) } EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); -void i2c_del_mux_adapter(struct i2c_adapter *adap) -{ - struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_mux_core *muxc = priv->muxc; - - i2c_mux_del_adapters(muxc); - devm_kfree(muxc->dev, muxc); -} -EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); - MODULE_AUTHOR("Rodolfo Giometti "); MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 71ac1b3f4f68..2fa93fe1345e 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -52,20 +52,6 @@ static inline void *i2c_mux_priv(struct i2c_mux_core *muxc) return muxc->priv; } -/* - * Called to create a i2c bus on a multiplexed bus segment. - * The mux_dev and chan_id parameters are passed to the select - * and deselect callback functions to perform hardware-specific - * mux control. - */ -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *mux_dev, u32 chan_id), - int (*deselect) (struct i2c_adapter *, - void *mux_dev, u32 chan_id)); /* * Called to create an i2c bus on a multiplexed bus segment. * The chan_id parameter is passed to the select and deselect @@ -75,7 +61,6 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, u32 force_nr, u32 chan_id, unsigned int class); -void i2c_del_mux_adapter(struct i2c_adapter *adap); void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- cgit From 9d90725ddca347450c4ab177ad680ed76063afd4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 18 Mar 2016 11:27:36 -0700 Subject: libnvdimm, blk: move i/o infrastructure to nd_namespace_blk Consolidate the information for issuing i/o to a blk-namespace, and eliminate some pointer chasing. Reviewed-by: Johannes Thumshirn Signed-off-by: Dan Williams --- drivers/nvdimm/blk.c | 137 ++++++++++++++++++++++++++------------------------- include/linux/nd.h | 2 + 2 files changed, 71 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c index 26d039879ba2..4c14ecdc792b 100644 --- a/drivers/nvdimm/blk.c +++ b/drivers/nvdimm/blk.c @@ -21,17 +21,19 @@ #include #include "nd.h" -struct nd_blk_device { - struct nd_namespace_blk *nsblk; - struct nd_blk_region *ndbr; - size_t disk_size; - u32 sector_size; - u32 internal_lbasize; -}; +static u32 nsblk_meta_size(struct nd_namespace_blk *nsblk) +{ + return nsblk->lbasize - ((nsblk->lbasize >= 4096) ? 4096 : 512); +} -static u32 nd_blk_meta_size(struct nd_blk_device *blk_dev) +static u32 nsblk_internal_lbasize(struct nd_namespace_blk *nsblk) { - return blk_dev->nsblk->lbasize - blk_dev->sector_size; + return roundup(nsblk->lbasize, INT_LBASIZE_ALIGNMENT); +} + +static u32 nsblk_sector_size(struct nd_namespace_blk *nsblk) +{ + return nsblk->lbasize - nsblk_meta_size(nsblk); } static resource_size_t to_dev_offset(struct nd_namespace_blk *nsblk, @@ -55,20 +57,29 @@ static resource_size_t to_dev_offset(struct nd_namespace_blk *nsblk, return SIZE_MAX; } +static struct nd_blk_region *to_ndbr(struct nd_namespace_blk *nsblk) +{ + struct nd_region *nd_region; + struct device *parent; + + parent = nsblk->common.dev.parent; + nd_region = container_of(parent, struct nd_region, dev); + return container_of(nd_region, struct nd_blk_region, nd_region); +} + #ifdef CONFIG_BLK_DEV_INTEGRITY -static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev, - struct bio_integrity_payload *bip, u64 lba, - int rw) +static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk, + struct bio_integrity_payload *bip, u64 lba, int rw) { - unsigned int len = nd_blk_meta_size(blk_dev); + struct nd_blk_region *ndbr = to_ndbr(nsblk); + unsigned int len = nsblk_meta_size(nsblk); resource_size_t dev_offset, ns_offset; - struct nd_namespace_blk *nsblk; - struct nd_blk_region *ndbr; + u32 internal_lbasize, sector_size; int err = 0; - nsblk = blk_dev->nsblk; - ndbr = blk_dev->ndbr; - ns_offset = lba * blk_dev->internal_lbasize + blk_dev->sector_size; + internal_lbasize = nsblk_internal_lbasize(nsblk); + sector_size = nsblk_sector_size(nsblk); + ns_offset = lba * internal_lbasize + sector_size; dev_offset = to_dev_offset(nsblk, ns_offset, len); if (dev_offset == SIZE_MAX) return -EIO; @@ -102,25 +113,26 @@ static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev, } #else /* CONFIG_BLK_DEV_INTEGRITY */ -static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev, - struct bio_integrity_payload *bip, u64 lba, - int rw) +static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk, + struct bio_integrity_payload *bip, u64 lba, int rw) { return 0; } #endif -static int nd_blk_do_bvec(struct nd_blk_device *blk_dev, - struct bio_integrity_payload *bip, struct page *page, - unsigned int len, unsigned int off, int rw, - sector_t sector) +static int nsblk_do_bvec(struct nd_namespace_blk *nsblk, + struct bio_integrity_payload *bip, struct page *page, + unsigned int len, unsigned int off, int rw, sector_t sector) { - struct nd_blk_region *ndbr = blk_dev->ndbr; + struct nd_blk_region *ndbr = to_ndbr(nsblk); resource_size_t dev_offset, ns_offset; + u32 internal_lbasize, sector_size; int err = 0; void *iobuf; u64 lba; + internal_lbasize = nsblk_internal_lbasize(nsblk); + sector_size = nsblk_sector_size(nsblk); while (len) { unsigned int cur_len; @@ -130,11 +142,11 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev, * Block Window setup/move steps. the do_io routine is capable * of handling len <= PAGE_SIZE. */ - cur_len = bip ? min(len, blk_dev->sector_size) : len; + cur_len = bip ? min(len, sector_size) : len; - lba = div_u64(sector << SECTOR_SHIFT, blk_dev->sector_size); - ns_offset = lba * blk_dev->internal_lbasize; - dev_offset = to_dev_offset(blk_dev->nsblk, ns_offset, cur_len); + lba = div_u64(sector << SECTOR_SHIFT, sector_size); + ns_offset = lba * internal_lbasize; + dev_offset = to_dev_offset(nsblk, ns_offset, cur_len); if (dev_offset == SIZE_MAX) return -EIO; @@ -145,13 +157,13 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev, return err; if (bip) { - err = nd_blk_rw_integrity(blk_dev, bip, lba, rw); + err = nd_blk_rw_integrity(nsblk, bip, lba, rw); if (err) return err; } len -= cur_len; off += cur_len; - sector += blk_dev->sector_size >> SECTOR_SHIFT; + sector += sector_size >> SECTOR_SHIFT; } return err; @@ -160,7 +172,7 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev, static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio) { struct bio_integrity_payload *bip; - struct nd_blk_device *blk_dev; + struct nd_namespace_blk *nsblk; struct bvec_iter iter; unsigned long start; struct bio_vec bvec; @@ -179,17 +191,17 @@ static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio) } bip = bio_integrity(bio); - blk_dev = q->queuedata; + nsblk = q->queuedata; rw = bio_data_dir(bio); do_acct = nd_iostat_start(bio, &start); bio_for_each_segment(bvec, bio, iter) { unsigned int len = bvec.bv_len; BUG_ON(len > PAGE_SIZE); - err = nd_blk_do_bvec(blk_dev, bip, bvec.bv_page, len, - bvec.bv_offset, rw, iter.bi_sector); + err = nsblk_do_bvec(nsblk, bip, bvec.bv_page, len, + bvec.bv_offset, rw, iter.bi_sector); if (err) { - dev_dbg(&blk_dev->nsblk->common.dev, + dev_dbg(&nsblk->common.dev, "io error in %s sector %lld, len %d,\n", (rw == READ) ? "READ" : "WRITE", (unsigned long long) iter.bi_sector, len); @@ -205,17 +217,16 @@ static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio) return BLK_QC_T_NONE; } -static int nd_blk_rw_bytes(struct nd_namespace_common *ndns, +static int nsblk_rw_bytes(struct nd_namespace_common *ndns, resource_size_t offset, void *iobuf, size_t n, int rw) { - struct nd_blk_device *blk_dev = dev_get_drvdata(ndns->claim); - struct nd_namespace_blk *nsblk = blk_dev->nsblk; - struct nd_blk_region *ndbr = blk_dev->ndbr; + struct nd_namespace_blk *nsblk = to_nd_namespace_blk(&ndns->dev); + struct nd_blk_region *ndbr = to_ndbr(nsblk); resource_size_t dev_offset; dev_offset = to_dev_offset(nsblk, offset, n); - if (unlikely(offset + n > blk_dev->disk_size)) { + if (unlikely(offset + n > nsblk->size)) { dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); return -EFAULT; } @@ -242,16 +253,16 @@ static void nd_blk_release_disk(void *disk) put_disk(disk); } -static int nd_blk_attach_disk(struct device *dev, - struct nd_namespace_common *ndns, struct nd_blk_device *blk_dev) +static int nsblk_attach_disk(struct nd_namespace_blk *nsblk) { + struct device *dev = &nsblk->common.dev; resource_size_t available_disk_size; struct request_queue *q; struct gendisk *disk; u64 internal_nlba; - internal_nlba = div_u64(blk_dev->disk_size, blk_dev->internal_lbasize); - available_disk_size = internal_nlba * blk_dev->sector_size; + internal_nlba = div_u64(nsblk->size, nsblk_internal_lbasize(nsblk)); + available_disk_size = internal_nlba * nsblk_sector_size(nsblk); q = blk_alloc_queue(GFP_KERNEL); if (!q) @@ -264,9 +275,9 @@ static int nd_blk_attach_disk(struct device *dev, blk_queue_make_request(q, nd_blk_make_request); blk_queue_max_hw_sectors(q, UINT_MAX); blk_queue_bounce_limit(q, BLK_BOUNCE_ANY); - blk_queue_logical_block_size(q, blk_dev->sector_size); + blk_queue_logical_block_size(q, nsblk_sector_size(nsblk)); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q); - q->queuedata = blk_dev; + q->queuedata = nsblk; disk = alloc_disk(0); if (!disk) @@ -276,17 +287,17 @@ static int nd_blk_attach_disk(struct device *dev, return -ENOMEM; } - disk->driverfs_dev = &ndns->dev; + disk->driverfs_dev = dev; disk->first_minor = 0; disk->fops = &nd_blk_fops; disk->queue = q; disk->flags = GENHD_FL_EXT_DEVT; - nvdimm_namespace_disk_name(ndns, disk->disk_name); + nvdimm_namespace_disk_name(&nsblk->common, disk->disk_name); set_capacity(disk, 0); add_disk(disk); - if (nd_blk_meta_size(blk_dev)) { - int rc = nd_integrity_init(disk, nd_blk_meta_size(blk_dev)); + if (nsblk_meta_size(nsblk)) { + int rc = nd_integrity_init(disk, nsblk_meta_size(nsblk)); if (rc) return rc; @@ -301,33 +312,23 @@ static int nd_blk_probe(struct device *dev) { struct nd_namespace_common *ndns; struct nd_namespace_blk *nsblk; - struct nd_blk_device *blk_dev; ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) return PTR_ERR(ndns); - blk_dev = devm_kzalloc(dev, sizeof(*blk_dev), GFP_KERNEL); - if (!blk_dev) - return -ENOMEM; - nsblk = to_nd_namespace_blk(&ndns->dev); - blk_dev->disk_size = nvdimm_namespace_capacity(ndns); - blk_dev->ndbr = to_nd_blk_region(dev->parent); - blk_dev->nsblk = to_nd_namespace_blk(&ndns->dev); - blk_dev->internal_lbasize = roundup(nsblk->lbasize, - INT_LBASIZE_ALIGNMENT); - blk_dev->sector_size = ((nsblk->lbasize >= 4096) ? 4096 : 512); - dev_set_drvdata(dev, blk_dev); - - ndns->rw_bytes = nd_blk_rw_bytes; + nsblk->size = nvdimm_namespace_capacity(ndns); + dev_set_drvdata(dev, nsblk); + + ndns->rw_bytes = nsblk_rw_bytes; if (is_nd_btt(dev)) return nvdimm_namespace_attach_btt(ndns); - else if (nd_btt_probe(dev, ndns, blk_dev) == 0) { + else if (nd_btt_probe(dev, ndns, nsblk) == 0) { /* we'll come back as btt-blk */ return -ENXIO; } else - return nd_blk_attach_disk(dev, ndns, blk_dev); + return nsblk_attach_disk(nsblk); } static int nd_blk_remove(struct device *dev) diff --git a/include/linux/nd.h b/include/linux/nd.h index 5489ab756d1a..5ea4aec7fd63 100644 --- a/include/linux/nd.h +++ b/include/linux/nd.h @@ -82,6 +82,7 @@ struct nd_namespace_pmem { * @uuid: namespace name supplied in the dimm label * @id: ida allocated id * @lbasize: blk namespaces have a native sector size when btt not present + * @size: sum of all the resource ranges allocated to this namespace * @num_resources: number of dpa extents to claim * @res: discontiguous dpa extents for given dimm */ @@ -91,6 +92,7 @@ struct nd_namespace_blk { u8 *uuid; int id; unsigned long lbasize; + resource_size_t size; int num_resources; struct resource **res; }; -- cgit From 200c79da824c978fcf6eec1dc9c0a1e521133267 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 22 Mar 2016 00:22:16 -0700 Subject: libnvdimm, pmem, pfn: make pmem_rw_bytes generic and refactor pfn setup In preparation for providing an alternative (to block device) access mechanism to persistent memory, convert pmem_rw_bytes() to nsio_rw_bytes(). This allows ->rw_bytes() functionality without requiring a 'struct pmem_device' to be instantiated. In other words, when ->rw_bytes() is in use i/o is driven through 'struct nd_namespace_io', otherwise it is driven through 'struct pmem_device' and the block layer. This consolidates the disjoint calls to devm_exit_badblocks() and devm_memunmap() into a common devm_nsio_disable() and cleans up the init path to use a unified pmem_attach_disk() implementation. Reviewed-by: Johannes Thumshirn Signed-off-by: Dan Williams --- drivers/nvdimm/blk.c | 2 +- drivers/nvdimm/btt_devs.c | 4 +- drivers/nvdimm/claim.c | 61 ++++++++++ drivers/nvdimm/nd.h | 40 +++++-- drivers/nvdimm/pfn_devs.c | 4 +- drivers/nvdimm/pmem.c | 236 ++++++++++++++------------------------ include/linux/nd.h | 9 +- tools/testing/nvdimm/Kbuild | 1 + tools/testing/nvdimm/test/iomap.c | 27 +++-- 9 files changed, 211 insertions(+), 173 deletions(-) (limited to 'include/linux') diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c index 4c14ecdc792b..495e06d9f7e7 100644 --- a/drivers/nvdimm/blk.c +++ b/drivers/nvdimm/blk.c @@ -324,7 +324,7 @@ static int nd_blk_probe(struct device *dev) ndns->rw_bytes = nsblk_rw_bytes; if (is_nd_btt(dev)) return nvdimm_namespace_attach_btt(ndns); - else if (nd_btt_probe(dev, ndns, nsblk) == 0) { + else if (nd_btt_probe(dev, ndns) == 0) { /* we'll come back as btt-blk */ return -ENXIO; } else diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 1886171af80e..816d0dae6398 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -273,8 +273,7 @@ static int __nd_btt_probe(struct nd_btt *nd_btt, return 0; } -int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns, - void *drvdata) +int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns) { int rc; struct device *btt_dev; @@ -289,7 +288,6 @@ int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns, nvdimm_bus_unlock(&ndns->dev); if (!btt_dev) return -ENOMEM; - dev_set_drvdata(btt_dev, drvdata); btt_sb = devm_kzalloc(dev, sizeof(*btt_sb), GFP_KERNEL); rc = __nd_btt_probe(to_nd_btt(btt_dev), ndns, btt_sb); dev_dbg(dev, "%s: btt: %s\n", __func__, diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index e8f03b0e95e4..6bbd0a36994a 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -12,6 +12,7 @@ */ #include #include +#include #include "nd-core.h" #include "pfn.h" #include "btt.h" @@ -199,3 +200,63 @@ u64 nd_sb_checksum(struct nd_gen_sb *nd_gen_sb) return sum; } EXPORT_SYMBOL(nd_sb_checksum); + +static int nsio_rw_bytes(struct nd_namespace_common *ndns, + resource_size_t offset, void *buf, size_t size, int rw) +{ + struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); + + if (unlikely(offset + size > nsio->size)) { + dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); + return -EFAULT; + } + + if (rw == READ) { + unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); + + if (unlikely(is_bad_pmem(&nsio->bb, offset / 512, sz_align))) + return -EIO; + return memcpy_from_pmem(buf, nsio->addr + offset, size); + } else { + memcpy_to_pmem(nsio->addr + offset, buf, size); + wmb_pmem(); + } + + return 0; +} + +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio) +{ + struct resource *res = &nsio->res; + struct nd_namespace_common *ndns = &nsio->common; + + nsio->size = resource_size(res); + if (!devm_request_mem_region(dev, res->start, resource_size(res), + dev_name(dev))) { + dev_warn(dev, "could not reserve region %pR\n", res); + return -EBUSY; + } + + ndns->rw_bytes = nsio_rw_bytes; + if (devm_init_badblocks(dev, &nsio->bb)) + return -ENOMEM; + nvdimm_badblocks_populate(to_nd_region(ndns->dev.parent), &nsio->bb, + &nsio->res); + + nsio->addr = devm_memremap(dev, res->start, resource_size(res), + ARCH_MEMREMAP_PMEM); + if (IS_ERR(nsio->addr)) + return PTR_ERR(nsio->addr); + return 0; +} +EXPORT_SYMBOL_GPL(devm_nsio_enable); + +void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio) +{ + struct resource *res = &nsio->res; + + devm_memunmap(dev, nsio->addr); + devm_exit_badblocks(dev, &nsio->bb); + devm_release_mem_region(dev, res->start, resource_size(res)); +} +EXPORT_SYMBOL_GPL(devm_nsio_disable); diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 0fb14890ba26..10e23fe49012 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -13,6 +13,7 @@ #ifndef __ND_H__ #define __ND_H__ #include +#include #include #include #include @@ -197,13 +198,12 @@ struct nd_gen_sb { u64 nd_sb_checksum(struct nd_gen_sb *sb); #if IS_ENABLED(CONFIG_BTT) -int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns, - void *drvdata); +int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns); bool is_nd_btt(struct device *dev); struct device *nd_btt_create(struct nd_region *nd_region); #else static inline int nd_btt_probe(struct device *dev, - struct nd_namespace_common *ndns, void *drvdata) + struct nd_namespace_common *ndns) { return -ENODEV; } @@ -221,14 +221,13 @@ static inline struct device *nd_btt_create(struct nd_region *nd_region) struct nd_pfn *to_nd_pfn(struct device *dev); #if IS_ENABLED(CONFIG_NVDIMM_PFN) -int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns, - void *drvdata); +int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns); bool is_nd_pfn(struct device *dev); struct device *nd_pfn_create(struct nd_region *nd_region); int nd_pfn_validate(struct nd_pfn *nd_pfn); #else -static inline int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns, - void *drvdata) +static inline int nd_pfn_probe(struct device *dev, + struct nd_namespace_common *ndns) { return -ENODEV; } @@ -272,6 +271,20 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, char *name); void nvdimm_badblocks_populate(struct nd_region *nd_region, struct badblocks *bb, const struct resource *res); +#if IS_ENABLED(CONFIG_ND_CLAIM) +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio); +void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio); +#else +static inline int devm_nsio_enable(struct device *dev, + struct nd_namespace_io *nsio) +{ + return -ENXIO; +} +static inline void devm_nsio_disable(struct device *dev, + struct nd_namespace_io *nsio) +{ +} +#endif int nd_blk_region_init(struct nd_region *nd_region); void __nd_iostat_start(struct bio *bio, unsigned long *start); static inline bool nd_iostat_start(struct bio *bio, unsigned long *start) @@ -285,6 +298,19 @@ static inline bool nd_iostat_start(struct bio *bio, unsigned long *start) return true; } void nd_iostat_end(struct bio *bio, unsigned long start); +static inline bool is_bad_pmem(struct badblocks *bb, sector_t sector, + unsigned int len) +{ + if (bb->count) { + sector_t first_bad; + int num_bad; + + return !!badblocks_check(bb, sector, len / 512, &first_bad, + &num_bad); + } + + return false; +} resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk); const u8 *nd_dev_to_uuid(struct device *dev); bool pmem_should_map_pages(struct device *dev); diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 96aa5490c279..9df081ae96e3 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -410,8 +410,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn) } EXPORT_SYMBOL(nd_pfn_validate); -int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns, - void *drvdata) +int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) { int rc; struct nd_pfn *nd_pfn; @@ -427,7 +426,6 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns, nvdimm_bus_unlock(&ndns->dev); if (!pfn_dev) return -ENOMEM; - dev_set_drvdata(pfn_dev, drvdata); pfn_sb = devm_kzalloc(dev, sizeof(*pfn_sb), GFP_KERNEL); nd_pfn = to_nd_pfn(pfn_dev); nd_pfn->pfn_sb = pfn_sb; diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 67d48e2e8ca2..b5f81b02205c 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -49,19 +49,6 @@ struct pmem_device { struct badblocks bb; }; -static bool is_bad_pmem(struct badblocks *bb, sector_t sector, unsigned int len) -{ - if (bb->count) { - sector_t first_bad; - int num_bad; - - return !!badblocks_check(bb, sector, len / 512, &first_bad, - &num_bad); - } - - return false; -} - static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset, unsigned int len) { @@ -209,16 +196,40 @@ void pmem_release_disk(void *disk) put_disk(disk); } -static struct pmem_device *pmem_alloc(struct device *dev, - struct resource *res, int id) +static struct vmem_altmap *nvdimm_setup_pfn(struct nd_pfn *nd_pfn, + struct resource *res, struct vmem_altmap *altmap); + +static int pmem_attach_disk(struct device *dev, + struct nd_namespace_common *ndns) { + struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); + struct vmem_altmap __altmap, *altmap = NULL; + struct resource *res = &nsio->res; + struct nd_pfn *nd_pfn = NULL; + int nid = dev_to_node(dev); + struct nd_pfn_sb *pfn_sb; struct pmem_device *pmem; + struct resource pfn_res; struct request_queue *q; + struct gendisk *disk; + void *addr; + + /* while nsio_rw_bytes is active, parse a pfn info block if present */ + if (is_nd_pfn(dev)) { + nd_pfn = to_nd_pfn(dev); + altmap = nvdimm_setup_pfn(nd_pfn, &pfn_res, &__altmap); + if (IS_ERR(altmap)) + return PTR_ERR(altmap); + } + + /* we're attaching a block device, disable raw namespace access */ + devm_nsio_disable(dev, nsio); pmem = devm_kzalloc(dev, sizeof(*pmem), GFP_KERNEL); if (!pmem) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + dev_set_drvdata(dev, pmem); pmem->phys_addr = res->start; pmem->size = resource_size(res); if (!arch_has_wmb_pmem()) @@ -227,22 +238,31 @@ static struct pmem_device *pmem_alloc(struct device *dev, if (!devm_request_mem_region(dev, res->start, resource_size(res), dev_name(dev))) { dev_warn(dev, "could not reserve region %pR\n", res); - return ERR_PTR(-EBUSY); + return -EBUSY; } q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev)); if (!q) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + pmem->pmem_queue = q; pmem->pfn_flags = PFN_DEV; - if (pmem_should_map_pages(dev)) { - pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, res, + if (is_nd_pfn(dev)) { + addr = devm_memremap_pages(dev, &pfn_res, &q->q_usage_counter, + altmap); + pfn_sb = nd_pfn->pfn_sb; + pmem->data_offset = le64_to_cpu(pfn_sb->dataoff); + pmem->pfn_pad = resource_size(res) - resource_size(&pfn_res); + pmem->pfn_flags |= PFN_MAP; + res = &pfn_res; /* for badblocks populate */ + res->start += pmem->data_offset; + } else if (pmem_should_map_pages(dev)) { + addr = devm_memremap_pages(dev, &nsio->res, &q->q_usage_counter, NULL); pmem->pfn_flags |= PFN_MAP; } else - pmem->virt_addr = (void __pmem *) devm_memremap(dev, - pmem->phys_addr, pmem->size, - ARCH_MEMREMAP_PMEM); + addr = devm_memremap(dev, pmem->phys_addr, + pmem->size, ARCH_MEMREMAP_PMEM); /* * At release time the queue must be dead before @@ -250,23 +270,12 @@ static struct pmem_device *pmem_alloc(struct device *dev, */ if (devm_add_action(dev, pmem_release_queue, q)) { blk_cleanup_queue(q); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } - if (IS_ERR(pmem->virt_addr)) - return (void __force *) pmem->virt_addr; - - pmem->pmem_queue = q; - return pmem; -} - -static int pmem_attach_disk(struct device *dev, - struct nd_namespace_common *ndns, struct pmem_device *pmem) -{ - struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); - int nid = dev_to_node(dev); - struct resource bb_res; - struct gendisk *disk; + if (IS_ERR(addr)) + return PTR_ERR(addr); + pmem->virt_addr = (void __pmem *) addr; blk_queue_make_request(pmem->pmem_queue, pmem_make_request); blk_queue_physical_block_size(pmem->pmem_queue, PAGE_SIZE); @@ -291,20 +300,9 @@ static int pmem_attach_disk(struct device *dev, set_capacity(disk, (pmem->size - pmem->pfn_pad - pmem->data_offset) / 512); pmem->pmem_disk = disk; - devm_exit_badblocks(dev, &pmem->bb); if (devm_init_badblocks(dev, &pmem->bb)) return -ENOMEM; - bb_res.start = nsio->res.start + pmem->data_offset; - bb_res.end = nsio->res.end; - if (is_nd_pfn(dev)) { - struct nd_pfn *nd_pfn = to_nd_pfn(dev); - struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb; - - bb_res.start += __le32_to_cpu(pfn_sb->start_pad); - bb_res.end -= __le32_to_cpu(pfn_sb->end_trunc); - } - nvdimm_badblocks_populate(to_nd_region(dev->parent), &pmem->bb, - &bb_res); + nvdimm_badblocks_populate(to_nd_region(dev->parent), &pmem->bb, res); disk->bb = &pmem->bb; add_disk(disk); revalidate_disk(disk); @@ -312,33 +310,8 @@ static int pmem_attach_disk(struct device *dev, return 0; } -static int pmem_rw_bytes(struct nd_namespace_common *ndns, - resource_size_t offset, void *buf, size_t size, int rw) -{ - struct pmem_device *pmem = dev_get_drvdata(ndns->claim); - - if (unlikely(offset + size > pmem->size)) { - dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); - return -EFAULT; - } - - if (rw == READ) { - unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); - - if (unlikely(is_bad_pmem(&pmem->bb, offset / 512, sz_align))) - return -EIO; - return memcpy_from_pmem(buf, pmem->virt_addr + offset, size); - } else { - memcpy_to_pmem(pmem->virt_addr + offset, buf, size); - wmb_pmem(); - } - - return 0; -} - static int nd_pfn_init(struct nd_pfn *nd_pfn) { - struct pmem_device *pmem = dev_get_drvdata(&nd_pfn->dev); struct nd_namespace_common *ndns = nd_pfn->ndns; u32 start_pad = 0, end_trunc = 0; resource_size_t start, size; @@ -404,7 +377,8 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) * ->direct_access() to those that are included in the memmap. */ start += start_pad; - npfns = (pmem->size - start_pad - end_trunc - SZ_8K) / SZ_4K; + size = resource_size(&nsio->res); + npfns = (size - start_pad - end_trunc - SZ_8K) / SZ_4K; if (nd_pfn->mode == PFN_MODE_PMEM) offset = ALIGN(start + SZ_8K + 64 * npfns, nd_pfn->align) - start; @@ -413,13 +387,13 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) else return -ENXIO; - if (offset + start_pad + end_trunc >= pmem->size) { + if (offset + start_pad + end_trunc >= size) { dev_err(&nd_pfn->dev, "%s unable to satisfy requested alignment\n", dev_name(&ndns->dev)); return -ENXIO; } - npfns = (pmem->size - offset - start_pad - end_trunc) / SZ_4K; + npfns = (size - offset - start_pad - end_trunc) / SZ_4K; pfn_sb->mode = cpu_to_le32(nd_pfn->mode); pfn_sb->dataoff = cpu_to_le64(offset); pfn_sb->npfns = cpu_to_le64(npfns); @@ -456,17 +430,14 @@ static unsigned long init_altmap_reserve(resource_size_t base) return reserve; } -static int __nvdimm_namespace_attach_pfn(struct nd_pfn *nd_pfn) +static struct vmem_altmap *__nvdimm_setup_pfn(struct nd_pfn *nd_pfn, + struct resource *res, struct vmem_altmap *altmap) { - struct resource res; - struct request_queue *q; - struct pmem_device *pmem; - struct vmem_altmap *altmap; - struct device *dev = &nd_pfn->dev; struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb; - struct nd_namespace_common *ndns = nd_pfn->ndns; + u64 offset = le64_to_cpu(pfn_sb->dataoff); u32 start_pad = __le32_to_cpu(pfn_sb->start_pad); u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc); + struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); resource_size_t base = nsio->res.start + start_pad; struct vmem_altmap __altmap = { @@ -474,112 +445,75 @@ static int __nvdimm_namespace_attach_pfn(struct nd_pfn *nd_pfn) .reserve = init_altmap_reserve(base), }; - pmem = dev_get_drvdata(dev); - pmem->data_offset = le64_to_cpu(pfn_sb->dataoff); - pmem->pfn_pad = start_pad + end_trunc; + memcpy(res, &nsio->res, sizeof(*res)); + res->start += start_pad; + res->end -= end_trunc; + nd_pfn->mode = le32_to_cpu(nd_pfn->pfn_sb->mode); if (nd_pfn->mode == PFN_MODE_RAM) { - if (pmem->data_offset < SZ_8K) - return -EINVAL; + if (offset < SZ_8K) + return ERR_PTR(-EINVAL); nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns); altmap = NULL; } else if (nd_pfn->mode == PFN_MODE_PMEM) { - nd_pfn->npfns = (pmem->size - pmem->pfn_pad - pmem->data_offset) - / PAGE_SIZE; + nd_pfn->npfns = (resource_size(res) - offset) / PAGE_SIZE; if (le64_to_cpu(nd_pfn->pfn_sb->npfns) > nd_pfn->npfns) dev_info(&nd_pfn->dev, "number of pfns truncated from %lld to %ld\n", le64_to_cpu(nd_pfn->pfn_sb->npfns), nd_pfn->npfns); - altmap = & __altmap; - altmap->free = PHYS_PFN(pmem->data_offset - SZ_8K); + memcpy(altmap, &__altmap, sizeof(*altmap)); + altmap->free = PHYS_PFN(offset - SZ_8K); altmap->alloc = 0; } else - return -ENXIO; + return ERR_PTR(-ENXIO); - /* establish pfn range for lookup, and switch to direct map */ - q = pmem->pmem_queue; - memcpy(&res, &nsio->res, sizeof(res)); - res.start += start_pad; - res.end -= end_trunc; - devm_remove_action(dev, pmem_release_queue, q); - devm_memunmap(dev, (void __force *) pmem->virt_addr); - pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, &res, - &q->q_usage_counter, altmap); - pmem->pfn_flags |= PFN_MAP; - - /* - * At release time the queue must be dead before - * devm_memremap_pages is unwound - */ - if (devm_add_action(dev, pmem_release_queue, q)) { - blk_cleanup_queue(q); - return -ENOMEM; - } - if (IS_ERR(pmem->virt_addr)) - return PTR_ERR(pmem->virt_addr); - - /* attach pmem disk in "pfn-mode" */ - return pmem_attach_disk(dev, ndns, pmem); + return altmap; } -static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns) +/* + * Determine the effective resource range and vmem_altmap from an nd_pfn + * instance. + */ +static struct vmem_altmap *nvdimm_setup_pfn(struct nd_pfn *nd_pfn, + struct resource *res, struct vmem_altmap *altmap) { - struct nd_pfn *nd_pfn = to_nd_pfn(ndns->claim); int rc; if (!nd_pfn->uuid || !nd_pfn->ndns) - return -ENODEV; + return ERR_PTR(-ENODEV); rc = nd_pfn_init(nd_pfn); if (rc) - return rc; + return ERR_PTR(rc); + /* we need a valid pfn_sb before we can init a vmem_altmap */ - return __nvdimm_namespace_attach_pfn(nd_pfn); + return __nvdimm_setup_pfn(nd_pfn, res, altmap); } static int nd_pmem_probe(struct device *dev) { - struct nd_region *nd_region = to_nd_region(dev->parent); struct nd_namespace_common *ndns; - struct nd_namespace_io *nsio; - struct pmem_device *pmem; ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) return PTR_ERR(ndns); - nsio = to_nd_namespace_io(&ndns->dev); - pmem = pmem_alloc(dev, &nsio->res, nd_region->id); - if (IS_ERR(pmem)) - return PTR_ERR(pmem); - - dev_set_drvdata(dev, pmem); - ndns->rw_bytes = pmem_rw_bytes; - if (devm_init_badblocks(dev, &pmem->bb)) - return -ENOMEM; - nvdimm_badblocks_populate(nd_region, &pmem->bb, &nsio->res); + if (devm_nsio_enable(dev, to_nd_namespace_io(&ndns->dev))) + return -ENXIO; - if (is_nd_btt(dev)) { - /* btt allocates its own request_queue */ - devm_remove_action(dev, pmem_release_queue, pmem->pmem_queue); - blk_cleanup_queue(pmem->pmem_queue); + if (is_nd_btt(dev)) return nvdimm_namespace_attach_btt(ndns); - } if (is_nd_pfn(dev)) - return nvdimm_namespace_attach_pfn(ndns); + return pmem_attach_disk(dev, ndns); - if (nd_btt_probe(dev, ndns, pmem) == 0 - || nd_pfn_probe(dev, ndns, pmem) == 0) { - /* - * We'll come back as either btt-pmem, or pfn-pmem, so - * drop the queue allocation for now. - */ + /* if we find a valid info-block we'll come back as that personality */ + if (nd_btt_probe(dev, ndns) == 0 || nd_pfn_probe(dev, ndns) == 0) return -ENXIO; - } - return pmem_attach_disk(dev, ndns, pmem); + /* ...otherwise we're just a raw pmem device */ + return pmem_attach_disk(dev, ndns); } static int nd_pmem_remove(struct device *dev) diff --git a/include/linux/nd.h b/include/linux/nd.h index 5ea4aec7fd63..aee2761d294c 100644 --- a/include/linux/nd.h +++ b/include/linux/nd.h @@ -15,6 +15,7 @@ #include #include #include +#include enum nvdimm_event { NVDIMM_REVALIDATE_POISON, @@ -55,13 +56,19 @@ static inline struct nd_namespace_common *to_ndns(struct device *dev) } /** - * struct nd_namespace_io - infrastructure for loading an nd_pmem instance + * struct nd_namespace_io - device representation of a persistent memory range * @dev: namespace device created by the nd region driver * @res: struct resource conversion of a NFIT SPA table + * @size: cached resource_size(@res) for fast path size checks + * @addr: virtual address to access the namespace range + * @bb: badblocks list for the namespace range */ struct nd_namespace_io { struct nd_namespace_common common; struct resource res; + resource_size_t size; + void __pmem *addr; + struct badblocks bb; }; /** diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index a34bfd0c8928..d5bc8c080b44 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -7,6 +7,7 @@ ldflags-y += --wrap=ioremap_nocache ldflags-y += --wrap=iounmap ldflags-y += --wrap=memunmap ldflags-y += --wrap=__devm_request_region +ldflags-y += --wrap=__devm_release_region ldflags-y += --wrap=__request_region ldflags-y += --wrap=__release_region ldflags-y += --wrap=devm_memremap_pages diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index 0c1a7e65bb81..c842095f2801 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c @@ -239,13 +239,11 @@ struct resource *__wrap___devm_request_region(struct device *dev, } EXPORT_SYMBOL(__wrap___devm_request_region); -void __wrap___release_region(struct resource *parent, resource_size_t start, - resource_size_t n) +static bool nfit_test_release_region(struct resource *parent, + resource_size_t start, resource_size_t n) { - struct nfit_test_resource *nfit_res; - if (parent == &iomem_resource) { - nfit_res = get_nfit_res(start); + struct nfit_test_resource *nfit_res = get_nfit_res(start); if (nfit_res) { struct resource *res = nfit_res->res + 1; @@ -254,11 +252,26 @@ void __wrap___release_region(struct resource *parent, resource_size_t start, __func__, start, n, res); else memset(res, 0, sizeof(*res)); - return; + return true; } } - __release_region(parent, start, n); + return false; +} + +void __wrap___release_region(struct resource *parent, resource_size_t start, + resource_size_t n) +{ + if (!nfit_test_release_region(parent, start, n)) + __release_region(parent, start, n); } EXPORT_SYMBOL(__wrap___release_region); +void __wrap___devm_release_region(struct device *dev, struct resource *parent, + resource_size_t start, resource_size_t n) +{ + if (!nfit_test_release_region(parent, start, n)) + __devm_release_region(dev, parent, start, n); +} +EXPORT_SYMBOL(__wrap___devm_release_region); + MODULE_LICENSE("GPL v2"); -- cgit From dfc57732ad38f93ae6232a3b4e64fd077383a0f1 Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Wed, 20 Apr 2016 19:23:43 +0200 Subject: iio:core: mounting matrix support Expose a rotation matrix to indicate userspace the chip placement with respect to the overall hardware system. This is needed to adjust coordinates sampled from a sensor chip when its position deviates from the main hardware system. Final coordinates computation is delegated to userspace since: * computation may involve floating point arithmetics ; * it allows an application to combine adjustments with arbitrary transformations. This 3 dimentional space rotation matrix is expressed as 3x3 array of strings to support floating point numbers. It may be retrieved from a "[_][_]mount_matrix" sysfs attribute file. It is declared into a device / driver specific DTS property or platform data. Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 51 ++++++++++++++++++++ drivers/iio/industrialio-core.c | 82 +++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 31 +++++++++++++ 3 files changed, 164 insertions(+) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index f155eff910f9..ba8df69d40b0 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1512,3 +1512,54 @@ Contact: linux-iio@vger.kernel.org Description: Raw (unscaled no offset etc.) pH reading of a substance as a negative base-10 logarithm of hydrodium ions in a litre of water. + +What: /sys/bus/iio/devices/iio:deviceX/mount_matrix +What: /sys/bus/iio/devices/iio:deviceX/in_mount_matrix +What: /sys/bus/iio/devices/iio:deviceX/out_mount_matrix +KernelVersion: 4.6 +Contact: linux-iio@vger.kernel.org +Description: + Mounting matrix for IIO sensors. This is a rotation matrix which + informs userspace about sensor chip's placement relative to the + main hardware it is mounted on. + Main hardware placement is defined according to the local + reference frame related to the physical quantity the sensor + measures. + Given that the rotation matrix is defined in a board specific + way (platform data and / or device-tree), the main hardware + reference frame definition is left to the implementor's choice + (see below for a magnetometer example). + Applications should apply this rotation matrix to samples so + that when main hardware reference frame is aligned onto local + reference frame, then sensor chip reference frame is also + perfectly aligned with it. + Matrix is a 3x3 unitary matrix and typically looks like + [0, 1, 0; 1, 0, 0; 0, 0, -1]. Identity matrix + [1, 0, 0; 0, 1, 0; 0, 0, 1] means sensor chip and main hardware + are perfectly aligned with each other. + + For example, a mounting matrix for a magnetometer sensor informs + userspace about sensor chip's ORIENTATION relative to the main + hardware. + More specifically, main hardware orientation is defined with + respect to the LOCAL EARTH GEOMAGNETIC REFERENCE FRAME where : + * Y is in the ground plane and positive towards magnetic North ; + * X is in the ground plane, perpendicular to the North axis and + positive towards the East ; + * Z is perpendicular to the ground plane and positive upwards. + + An implementor might consider that for a hand-held device, a + 'natural' orientation would be 'front facing camera at the top'. + The main hardware reference frame could then be described as : + * Y is in the plane of the screen and is positive towards the + top of the screen ; + * X is in the plane of the screen, perpendicular to Y axis, and + positive towards the right hand side of the screen ; + * Z is perpendicular to the screen plane and positive out of the + screen. + Another example for a quadrotor UAV might be : + * Y is in the plane of the propellers and positive towards the + front-view camera; + * X is in the plane of the propellers, perpendicular to Y axis, + and positive towards the starboard side of the UAV ; + * Z is perpendicular to propellers plane and positive upwards. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 190a5939fd8c..e6319a9346b2 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -412,6 +412,88 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, } EXPORT_SYMBOL_GPL(iio_enum_write); +static const struct iio_mount_matrix iio_mount_idmatrix = { + .rotation = { + "1", "0", "0", + "0", "1", "0", + "0", "0", "1" + } +}; + +static int iio_setup_mount_idmatrix(const struct device *dev, + struct iio_mount_matrix *matrix) +{ + *matrix = iio_mount_idmatrix; + dev_info(dev, "mounting matrix not found: using identity...\n"); + return 0; +} + +ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv, + const struct iio_chan_spec *chan, char *buf) +{ + const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *) + priv)(indio_dev, chan); + + if (IS_ERR(mtx)) + return PTR_ERR(mtx); + + if (!mtx) + mtx = &iio_mount_idmatrix; + + return snprintf(buf, PAGE_SIZE, "%s, %s, %s; %s, %s, %s; %s, %s, %s\n", + mtx->rotation[0], mtx->rotation[1], mtx->rotation[2], + mtx->rotation[3], mtx->rotation[4], mtx->rotation[5], + mtx->rotation[6], mtx->rotation[7], mtx->rotation[8]); +} +EXPORT_SYMBOL_GPL(iio_show_mount_matrix); + +/** + * of_iio_read_mount_matrix() - retrieve iio device mounting matrix from + * device-tree "mount-matrix" property + * @dev: device the mounting matrix property is assigned to + * @propname: device specific mounting matrix property name + * @matrix: where to store retrieved matrix + * + * If device is assigned no mounting matrix property, a default 3x3 identity + * matrix will be filled in. + * + * Return: 0 if success, or a negative error code on failure. + */ +#ifdef CONFIG_OF +int of_iio_read_mount_matrix(const struct device *dev, + const char *propname, + struct iio_mount_matrix *matrix) +{ + if (dev->of_node) { + int err = of_property_read_string_array(dev->of_node, + propname, matrix->rotation, + ARRAY_SIZE(iio_mount_idmatrix.rotation)); + + if (err == ARRAY_SIZE(iio_mount_idmatrix.rotation)) + return 0; + + if (err >= 0) + /* Invalid number of matrix entries. */ + return -EINVAL; + + if (err != -EINVAL) + /* Invalid matrix declaration format. */ + return err; + } + + /* Matrix was not declared at all: fallback to identity. */ + return iio_setup_mount_idmatrix(dev, matrix); +} +#else +int of_iio_read_mount_matrix(const struct device *dev, + const char *propname, + struct iio_mount_matrix *matrix) +{ + return iio_setup_mount_idmatrix(dev, matrix); +} +#endif +EXPORT_SYMBOL(of_iio_read_mount_matrix); + /** * iio_format_value() - Formats a IIO value into its string representation * @buf: The buffer to which the formatted value gets written diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 0b2773ada0ba..7c29cb0124ae 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -147,6 +147,37 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, .private = (uintptr_t)(_e), \ } +/** + * struct iio_mount_matrix - iio mounting matrix + * @rotation: 3 dimensional space rotation matrix defining sensor alignment with + * main hardware + */ +struct iio_mount_matrix { + const char *rotation[9]; +}; + +ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv, + const struct iio_chan_spec *chan, char *buf); +int of_iio_read_mount_matrix(const struct device *dev, const char *propname, + struct iio_mount_matrix *matrix); + +typedef const struct iio_mount_matrix * + (iio_get_mount_matrix_t)(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + +/** + * IIO_MOUNT_MATRIX() - Initialize mount matrix extended channel attribute + * @_shared: Whether the attribute is shared between all channels + * @_get: Pointer to an iio_get_mount_matrix_t accessor + */ +#define IIO_MOUNT_MATRIX(_shared, _get) \ +{ \ + .name = "mount_matrix", \ + .shared = (_shared), \ + .read = iio_show_mount_matrix, \ + .private = (uintptr_t)(_get), \ +} + /** * struct iio_event_spec - specification for a channel event * @type: Type of the event -- cgit From 97eacb9166f4810368e180073dcbceeff0de34df Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Wed, 20 Apr 2016 19:23:44 +0200 Subject: iio:ak8975: add mounting matrix support Expose a rotation matrix to indicate userspace the chip orientation with respect to the overall hardware system. Matrix is retrieved from "in_mount_matrix". It is declared into ak8975 DTS entry as a "mount-matrix" property. Signed-off-by: Gregor Boirie Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/magnetometer/ak8975.txt | 10 +++++++ drivers/iio/magnetometer/ak8975.c | 34 +++++++++++++++++++--- include/linux/iio/magnetometer/ak8975.h | 16 ++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 include/linux/iio/magnetometer/ak8975.h (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/iio/magnetometer/ak8975.txt b/Documentation/devicetree/bindings/iio/magnetometer/ak8975.txt index 34a3206eefdf..e1e7dd3259f6 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/ak8975.txt +++ b/Documentation/devicetree/bindings/iio/magnetometer/ak8975.txt @@ -9,6 +9,7 @@ Optional properties: - gpios : should be device tree identifier of the magnetometer DRDY pin - vdd-supply: an optional regulator that needs to be on to provide VDD + - mount-matrix: an optional 3x3 mounting rotation matrix Example: @@ -17,4 +18,13 @@ ak8975@0c { reg = <0x0c>; gpios = <&gpj0 7 0>; vdd-supply = <&ldo_3v3_gnss>; + mount-matrix = "-0.984807753012208", /* x0 */ + "0", /* y0 */ + "-0.173648177666930", /* z0 */ + "0", /* x1 */ + "-1", /* y1 */ + "0", /* z1 */ + "-0.173648177666930", /* x2 */ + "0", /* y2 */ + "0.984807753012208"; /* z2 */ }; diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index a2aac50a0149..dbf066129a04 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -40,7 +40,8 @@ #include #include #include -#include + +#include /* * Register definitions, as well as various shifts and masks to get at the @@ -376,6 +377,7 @@ struct ak8975_data { wait_queue_head_t data_ready_queue; unsigned long flags; u8 cntl_cache; + struct iio_mount_matrix orientation; struct regulator *vdd; }; @@ -726,6 +728,18 @@ static int ak8975_read_raw(struct iio_dev *indio_dev, return -EINVAL; } +static const struct iio_mount_matrix * +ak8975_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + return &((struct ak8975_data *)iio_priv(indio_dev))->orientation; +} + +static const struct iio_chan_spec_ext_info ak8975_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8975_get_mount_matrix), + { }, +}; + #define AK8975_CHANNEL(axis, index) \ { \ .type = IIO_MAGN, \ @@ -740,7 +754,8 @@ static int ak8975_read_raw(struct iio_dev *indio_dev, .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_CPU \ - } \ + }, \ + .ext_info = ak8975_ext_info, \ } static const struct iio_chan_spec ak8975_channels[] = { @@ -837,10 +852,12 @@ static int ak8975_probe(struct i2c_client *client, int err; const char *name = NULL; enum asahi_compass_chipset chipset; + const struct ak8975_platform_data *pdata = + dev_get_platdata(&client->dev); /* Grab and set up the supplied GPIO. */ - if (client->dev.platform_data) - eoc_gpio = *(int *)(client->dev.platform_data); + if (pdata) + eoc_gpio = pdata->eoc_gpio; else if (client->dev.of_node) eoc_gpio = of_get_gpio(client->dev.of_node, 0); else @@ -874,6 +891,15 @@ static int ak8975_probe(struct i2c_client *client, data->eoc_gpio = eoc_gpio; data->eoc_irq = 0; + if (!pdata) { + err = of_iio_read_mount_matrix(&client->dev, + "mount-matrix", + &data->orientation); + if (err) + return err; + } else + data->orientation = pdata->orientation; + /* id will be NULL when enumerated via ACPI */ if (id) { chipset = (enum asahi_compass_chipset)(id->driver_data); diff --git a/include/linux/iio/magnetometer/ak8975.h b/include/linux/iio/magnetometer/ak8975.h new file mode 100644 index 000000000000..c8400959d197 --- /dev/null +++ b/include/linux/iio/magnetometer/ak8975.h @@ -0,0 +1,16 @@ +#ifndef __IIO_MAGNETOMETER_AK8975_H__ +#define __IIO_MAGNETOMETER_AK8975_H__ + +#include + +/** + * struct ak8975_platform_data - AK8975 magnetometer driver platform data + * @eoc_gpio: data ready event gpio + * @orientation: mounting matrix relative to main hardware + */ +struct ak8975_platform_data { + int eoc_gpio; + struct iio_mount_matrix orientation; +}; + +#endif -- cgit From eb3798463f71afc77abd25b2f62708be06f7173b Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Wed, 20 Apr 2016 19:23:45 +0200 Subject: iio:imu:mpu6050: enhance mounting matrix support Add a new rotation matrix sysfs attribute compliant with IIO core mounting matrix API. Matrix is retrieved from "in_anglvel_mount_matrix" and "in_accel_mount_matrix" sysfs attributes. It is declared into mpu6050 DTS entry as a "mount-matrix" property. Old interface is kept for backward userspace compatibility and may be retrieved from legacy platform_data mechanism only. Signed-off-by: Gregor Boirie Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 2 ++ .../devicetree/bindings/iio/imu/inv_mpu6050.txt | 13 ++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 36 ++++++++++++++++++++-- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 4 ++- include/linux/platform_data/invensense_mpu6050.h | 5 ++- 5 files changed, 55 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index ba8df69d40b0..df44998e7506 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1516,6 +1516,8 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/mount_matrix What: /sys/bus/iio/devices/iio:deviceX/in_mount_matrix What: /sys/bus/iio/devices/iio:deviceX/out_mount_matrix +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_mount_matrix +What: /sys/bus/iio/devices/iio:deviceX/in_accel_mount_matrix KernelVersion: 4.6 Contact: linux-iio@vger.kernel.org Description: diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index e4d8f1c52f4a..a9fc11e43b45 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -8,10 +8,23 @@ Required properties: - interrupt-parent : should be the phandle for the interrupt controller - interrupts : interrupt mapping for GPIO IRQ +Optional properties: + - mount-matrix: an optional 3x3 mounting rotation matrix + + Example: mpu6050@68 { compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpio1>; interrupts = <18 1>; + mount-matrix = "-0.984807753012208", /* x0 */ + "0", /* y0 */ + "-0.173648177666930", /* z0 */ + "0", /* x1 */ + "-1", /* y1 */ + "0", /* z1 */ + "-0.173648177666930", /* x2 */ + "0", /* y2 */ + "0.984807753012208"; /* z2 */ }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index d192953e9a38..482a2490c53a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -600,6 +600,10 @@ inv_fifo_rate_show(struct device *dev, struct device_attribute *attr, /** * inv_attr_show() - calling this function will show current * parameters. + * + * Deprecated in favor of IIO mounting matrix API. + * + * See inv_get_mount_matrix() */ static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -644,6 +648,18 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev, return 0; } +static const struct iio_mount_matrix * +inv_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + return &((struct inv_mpu6050_state *)iio_priv(indio_dev))->orientation; +} + +static const struct iio_chan_spec_ext_info inv_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, inv_get_mount_matrix), + { }, +}; + #define INV_MPU6050_CHAN(_type, _channel2, _index) \ { \ .type = _type, \ @@ -660,6 +676,7 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev, .shift = 0, \ .endianness = IIO_BE, \ }, \ + .ext_info = inv_ext_info, \ } static const struct iio_chan_spec inv_mpu_channels[] = { @@ -692,14 +709,16 @@ static IIO_CONST_ATTR(in_accel_scale_available, "0.000598 0.001196 0.002392 0.004785"); static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show, inv_mpu6050_fifo_rate_store); + +/* Deprecated: kept for userspace backward compatibility. */ static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL, ATTR_GYRO_MATRIX); static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL, ATTR_ACCL_MATRIX); static struct attribute *inv_attributes[] = { - &iio_dev_attr_in_gyro_matrix.dev_attr.attr, - &iio_dev_attr_in_accel_matrix.dev_attr.attr, + &iio_dev_attr_in_gyro_matrix.dev_attr.attr, /* deprecated */ + &iio_dev_attr_in_accel_matrix.dev_attr.attr, /* deprecated */ &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_const_attr_in_accel_scale_available.dev_attr.attr, @@ -779,9 +798,20 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, st->powerup_count = 0; st->irq = irq; st->map = regmap; + pdata = dev_get_platdata(dev); - if (pdata) + if (!pdata) { + result = of_iio_read_mount_matrix(dev, "mount-matrix", + &st->orientation); + if (result) { + dev_err(dev, "Failed to retrieve mounting matrix %d\n", + result); + return result; + } + } else { st->plat_data = *pdata; + } + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e302a49703bf..52d60cdc9f16 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -114,7 +114,8 @@ struct inv_mpu6050_hw { * @hw: Other hardware-specific information. * @chip_type: chip type. * @time_stamp_lock: spin lock to time stamp. - * @plat_data: platform data. + * @plat_data: platform data (deprecated in favor of @orientation). + * @orientation: sensor chip orientation relative to main hardware. * @timestamps: kfifo queue to store time stamp. * @map regmap pointer. * @irq interrupt number. @@ -131,6 +132,7 @@ struct inv_mpu6050_state { struct i2c_client *mux_client; unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; + struct iio_mount_matrix orientation; DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); struct regmap *map; int irq; diff --git a/include/linux/platform_data/invensense_mpu6050.h b/include/linux/platform_data/invensense_mpu6050.h index ad3aa7b95f35..554b59801aa8 100644 --- a/include/linux/platform_data/invensense_mpu6050.h +++ b/include/linux/platform_data/invensense_mpu6050.h @@ -16,13 +16,16 @@ /** * struct inv_mpu6050_platform_data - Platform data for the mpu driver - * @orientation: Orientation matrix of the chip + * @orientation: Orientation matrix of the chip (deprecated in favor of + * mounting matrix retrieved from device-tree) * * Contains platform specific information on how to configure the MPU6050 to * work on this platform. The orientation matricies are 3x3 rotation matricies * that are applied to the data to rotate from the mounting orientation to the * platform orientation. The values must be one of 0, 1, or -1 and each row and * column should have exactly 1 non-zero value. + * + * Deprecated in favor of mounting matrix retrieved from device-tree. */ struct inv_mpu6050_platform_data { __s8 orientation[9]; -- cgit From 41617e1a8dec9fe082ba5dec26bacb154eb55482 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sun, 24 Apr 2016 00:56:07 -0400 Subject: jbd2: add support for avoiding data writes during transaction commits Currently when filesystem needs to make sure data is on permanent storage before committing a transaction it adds inode to transaction's inode list. During transaction commit, jbd2 writes back all dirty buffers that have allocated underlying blocks and waits for the IO to finish. However when doing writeback for delayed allocated data, we allocate blocks and immediately submit the data. Thus asking jbd2 to write dirty pages just unnecessarily adds more work to jbd2 possibly writing back other redirtied blocks. Add support to jbd2 to allow filesystem to ask jbd2 to only wait for outstanding data writes before committing a transaction and thus avoid unnecessary writes. Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o --- fs/ext4/ext4_jbd2.h | 3 ++- fs/jbd2/commit.c | 4 ++++ fs/jbd2/journal.c | 3 ++- fs/jbd2/transaction.c | 22 ++++++++++++++++++---- fs/ocfs2/journal.h | 2 +- include/linux/jbd2.h | 13 +++++++++++-- 6 files changed, 38 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 5f5846211095..f1c940b38b30 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -362,7 +362,8 @@ static inline int ext4_journal_force_commit(journal_t *journal) static inline int ext4_jbd2_file_inode(handle_t *handle, struct inode *inode) { if (ext4_handle_valid(handle)) - return jbd2_journal_file_inode(handle, EXT4_I(inode)->jinode); + return jbd2_journal_inode_add_write(handle, + EXT4_I(inode)->jinode); return 0; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 2ad98d6e19f4..70078096117d 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -219,6 +219,8 @@ static int journal_submit_data_buffers(journal_t *journal, spin_lock(&journal->j_list_lock); list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { + if (!(jinode->i_flags & JI_WRITE_DATA)) + continue; mapping = jinode->i_vfs_inode->i_mapping; jinode->i_flags |= JI_COMMIT_RUNNING; spin_unlock(&journal->j_list_lock); @@ -256,6 +258,8 @@ static int journal_finish_inode_data_buffers(journal_t *journal, /* For locking, see the comment in journal_submit_data_buffers() */ spin_lock(&journal->j_list_lock); list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { + if (!(jinode->i_flags & JI_WAIT_DATA)) + continue; jinode->i_flags |= JI_COMMIT_RUNNING; spin_unlock(&journal->j_list_lock); err = filemap_fdatawait(jinode->i_vfs_inode->i_mapping); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 435f0b26ac20..b31852f76f46 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -94,7 +94,8 @@ EXPORT_SYMBOL(jbd2_journal_blocks_per_page); EXPORT_SYMBOL(jbd2_journal_invalidatepage); EXPORT_SYMBOL(jbd2_journal_try_to_free_buffers); EXPORT_SYMBOL(jbd2_journal_force_commit); -EXPORT_SYMBOL(jbd2_journal_file_inode); +EXPORT_SYMBOL(jbd2_journal_inode_add_write); +EXPORT_SYMBOL(jbd2_journal_inode_add_wait); EXPORT_SYMBOL(jbd2_journal_init_jbd_inode); EXPORT_SYMBOL(jbd2_journal_release_jbd_inode); EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 67c103867bf8..be56c8ca34c2 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -2462,7 +2462,8 @@ void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh) /* * File inode in the inode list of the handle's transaction */ -int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode) +static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode, + unsigned long flags) { transaction_t *transaction = handle->h_transaction; journal_t *journal; @@ -2487,12 +2488,14 @@ int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode) * and if jinode->i_next_transaction == transaction, commit code * will only file the inode where we want it. */ - if (jinode->i_transaction == transaction || - jinode->i_next_transaction == transaction) + if ((jinode->i_transaction == transaction || + jinode->i_next_transaction == transaction) && + (jinode->i_flags & flags) == flags) return 0; spin_lock(&journal->j_list_lock); - + jinode->i_flags |= flags; + /* Is inode already attached where we need it? */ if (jinode->i_transaction == transaction || jinode->i_next_transaction == transaction) goto done; @@ -2523,6 +2526,17 @@ done: return 0; } +int jbd2_journal_inode_add_write(handle_t *handle, struct jbd2_inode *jinode) +{ + return jbd2_journal_file_inode(handle, jinode, + JI_WRITE_DATA | JI_WAIT_DATA); +} + +int jbd2_journal_inode_add_wait(handle_t *handle, struct jbd2_inode *jinode) +{ + return jbd2_journal_file_inode(handle, jinode, JI_WAIT_DATA); +} + /* * File truncate and transaction commit interact with each other in a * non-trivial way. If a transaction writing data block A is diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index f4cd3c3e9fb7..497a4171ef61 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -619,7 +619,7 @@ static inline int ocfs2_calc_tree_trunc_credits(struct super_block *sb, static inline int ocfs2_jbd2_file_inode(handle_t *handle, struct inode *inode) { - return jbd2_journal_file_inode(handle, &OCFS2_I(inode)->ip_jinode); + return jbd2_journal_inode_add_write(handle, &OCFS2_I(inode)->ip_jinode); } static inline int ocfs2_begin_ordered_truncate(struct inode *inode, diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index fd1083c46c61..39511484ad10 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -403,11 +403,19 @@ static inline void jbd_unlock_bh_journal_head(struct buffer_head *bh) /* Flags in jbd_inode->i_flags */ #define __JI_COMMIT_RUNNING 0 -/* Commit of the inode data in progress. We use this flag to protect us from +#define __JI_WRITE_DATA 1 +#define __JI_WAIT_DATA 2 + +/* + * Commit of the inode data in progress. We use this flag to protect us from * concurrent deletion of inode. We cannot use reference to inode for this * since we cannot afford doing last iput() on behalf of kjournald */ #define JI_COMMIT_RUNNING (1 << __JI_COMMIT_RUNNING) +/* Write allocated dirty buffers in this inode before commit */ +#define JI_WRITE_DATA (1 << __JI_WRITE_DATA) +/* Wait for outstanding data writes for this inode before commit */ +#define JI_WAIT_DATA (1 << __JI_WAIT_DATA) /** * struct jbd_inode is the structure linking inodes in ordered mode @@ -1270,7 +1278,8 @@ extern int jbd2_journal_clear_err (journal_t *); extern int jbd2_journal_bmap(journal_t *, unsigned long, unsigned long long *); extern int jbd2_journal_force_commit(journal_t *); extern int jbd2_journal_force_commit_nested(journal_t *); -extern int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *inode); +extern int jbd2_journal_inode_add_write(handle_t *handle, struct jbd2_inode *inode); +extern int jbd2_journal_inode_add_wait(handle_t *handle, struct jbd2_inode *inode); extern int jbd2_journal_begin_ordered_truncate(journal_t *journal, struct jbd2_inode *inode, loff_t new_size); extern void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode); -- cgit From 7ba2f2757d84eae533679306f03c93c118437a87 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 23 Apr 2016 22:47:08 +0200 Subject: spi: core: add hook flash_read_supported to spi_master If hook spi_flash_read is implemented the fast flash read feature is enabled for all devices attached to the respective master. In most cases there is just one flash chip, however there are also devices with more than one flash chip, namely some WiFi routers. Then the fast flash read feature can be used for the first chip only. OpenWRT implemented an own handling of this case, using controller_data element of spi_device to hold the information whether fast flash read can be used for a device. This patch adds hook flash_read_supported to spi_master which is used to extend spi_flash_read_supported() by checking whether the fast flash read feature can be used for a specific spi_device. If the hook is not implemented the default behavior is to allow fast flash read for all devices (if spi_flash_read is implemented). Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 857a9a1d82b5..1f03483f61e5 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -372,6 +372,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @unprepare_message: undo any work done by prepare_message(). * @spi_flash_read: to support spi-controller hardwares that provide * accelerated interface to read from flash devices. + * @flash_read_supported: spi device supports flash read * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS * number. Any individual value may be -ENOENT for CS lines that * are not GPIOs (driven by the SPI controller itself). @@ -529,6 +530,7 @@ struct spi_master { struct spi_message *message); int (*spi_flash_read)(struct spi_device *spi, struct spi_flash_read_message *msg); + bool (*flash_read_supported)(struct spi_device *spi); /* * These hooks are for drivers that use a generic implementation @@ -1158,7 +1160,9 @@ struct spi_flash_read_message { /* SPI core interface for flash read support */ static inline bool spi_flash_read_supported(struct spi_device *spi) { - return spi->master->spi_flash_read ? true : false; + return spi->master->spi_flash_read && + (!spi->master->flash_read_supported || + spi->master->flash_read_supported(spi)); } int spi_flash_read(struct spi_device *spi, -- cgit From 21e8868e661ededd2c45c8ec27f6fd5354b88b71 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 20 Apr 2016 11:20:28 +0100 Subject: drivers: firmware: psci: make two helper functions static psci_power_state_loses_context() and psci_power_state_is_valid are only used internally now, so make them static. Signed-off-by: Jisheng Zhang Signed-off-by: Lorenzo Pieralisi Signed-off-by: Arnd Bergmann --- drivers/firmware/psci.c | 4 ++-- include/linux/psci.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 0ab477ba6564..04f2ac54e5ab 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -91,7 +91,7 @@ static inline bool psci_has_ext_power_state(void) PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; } -bool psci_power_state_loses_context(u32 state) +static bool psci_power_state_loses_context(u32 state) { const u32 mask = psci_has_ext_power_state() ? PSCI_1_0_EXT_POWER_STATE_TYPE_MASK : @@ -100,7 +100,7 @@ bool psci_power_state_loses_context(u32 state) return state & mask; } -bool psci_power_state_is_valid(u32 state) +static bool psci_power_state_is_valid(u32 state) { const u32 valid_mask = psci_has_ext_power_state() ? PSCI_1_0_EXT_POWER_STATE_MASK : diff --git a/include/linux/psci.h b/include/linux/psci.h index 393efe2edf9a..bdea1cb5e1db 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -21,8 +21,6 @@ #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 bool psci_tos_resident_on(int cpu); -bool psci_power_state_loses_context(u32 state); -bool psci_power_state_is_valid(u32 state); int psci_cpu_init_idle(unsigned int cpu); int psci_cpu_suspend_enter(unsigned long index); -- cgit From 4899f78a3dccda41ffdaa1a2cbf78209753e0f70 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Wed, 6 Apr 2016 12:37:37 -0500 Subject: mailbox/omap: drop legacy platform device support OMAP mailbox devices can no longer be created in legacy non-DT mode, all the relevant code has been cleaned up. The OMAP mailbox driver will only support devices created from DT going forward, so drop the legacy platform device support from the driver. Signed-off-by: Suman Anna Signed-off-by: Jassi Brar --- drivers/mailbox/omap-mailbox.c | 101 ++++++++++++----------------- include/linux/platform_data/mailbox-omap.h | 58 ----------------- 2 files changed, 41 insertions(+), 118 deletions(-) delete mode 100644 include/linux/platform_data/mailbox-omap.h (limited to 'include/linux') diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index b7f636f15cac..8754d810ef05 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -2,7 +2,7 @@ * OMAP mailbox driver * * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. - * Copyright (C) 2013-2014 Texas Instruments Inc. + * Copyright (C) 2013-2016 Texas Instruments Incorporated - http://www.ti.com * * Contact: Hiroshi DOYU * Suman Anna @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -69,6 +68,10 @@ #define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32)) #define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32)) +/* Interrupt register configuration types */ +#define MBOX_INTR_CFG_TYPE1 0 +#define MBOX_INTR_CFG_TYPE2 1 + struct omap_mbox_fifo { unsigned long msg; unsigned long fifo_stat; @@ -696,8 +699,6 @@ static int omap_mbox_probe(struct platform_device *pdev) int ret; struct mbox_chan *chnls; struct omap_mbox **list, *mbox, *mboxblk; - struct omap_mbox_pdata *pdata = pdev->dev.platform_data; - struct omap_mbox_dev_info *info = NULL; struct omap_mbox_fifo_info *finfo, *finfoblk; struct omap_mbox_device *mdev; struct omap_mbox_fifo *fifo; @@ -710,36 +711,26 @@ static int omap_mbox_probe(struct platform_device *pdev) u32 l; int i; - if (!node && (!pdata || !pdata->info_cnt || !pdata->info)) { - pr_err("%s: platform not supported\n", __func__); + if (!node) { + pr_err("%s: only DT-based devices are supported\n", __func__); return -ENODEV; } - if (node) { - match = of_match_device(omap_mailbox_of_match, &pdev->dev); - if (!match) - return -ENODEV; - intr_type = (u32)match->data; + match = of_match_device(omap_mailbox_of_match, &pdev->dev); + if (!match) + return -ENODEV; + intr_type = (u32)match->data; - if (of_property_read_u32(node, "ti,mbox-num-users", - &num_users)) - return -ENODEV; + if (of_property_read_u32(node, "ti,mbox-num-users", &num_users)) + return -ENODEV; - if (of_property_read_u32(node, "ti,mbox-num-fifos", - &num_fifos)) - return -ENODEV; + if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos)) + return -ENODEV; - info_count = of_get_available_child_count(node); - if (!info_count) { - dev_err(&pdev->dev, "no available mbox devices found\n"); - return -ENODEV; - } - } else { /* non-DT device creation */ - info_count = pdata->info_cnt; - info = pdata->info; - intr_type = pdata->intr_type; - num_users = pdata->num_users; - num_fifos = pdata->num_fifos; + info_count = of_get_available_child_count(node); + if (!info_count) { + dev_err(&pdev->dev, "no available mbox devices found\n"); + return -ENODEV; } finfoblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*finfoblk), @@ -750,38 +741,28 @@ static int omap_mbox_probe(struct platform_device *pdev) finfo = finfoblk; child = NULL; for (i = 0; i < info_count; i++, finfo++) { - if (node) { - child = of_get_next_available_child(node, child); - ret = of_property_read_u32_array(child, "ti,mbox-tx", - tmp, ARRAY_SIZE(tmp)); - if (ret) - return ret; - finfo->tx_id = tmp[0]; - finfo->tx_irq = tmp[1]; - finfo->tx_usr = tmp[2]; - - ret = of_property_read_u32_array(child, "ti,mbox-rx", - tmp, ARRAY_SIZE(tmp)); - if (ret) - return ret; - finfo->rx_id = tmp[0]; - finfo->rx_irq = tmp[1]; - finfo->rx_usr = tmp[2]; - - finfo->name = child->name; - - if (of_find_property(child, "ti,mbox-send-noirq", NULL)) - finfo->send_no_irq = true; - } else { - finfo->tx_id = info->tx_id; - finfo->rx_id = info->rx_id; - finfo->tx_usr = info->usr_id; - finfo->tx_irq = info->irq_id; - finfo->rx_usr = info->usr_id; - finfo->rx_irq = info->irq_id; - finfo->name = info->name; - info++; - } + child = of_get_next_available_child(node, child); + ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + finfo->tx_id = tmp[0]; + finfo->tx_irq = tmp[1]; + finfo->tx_usr = tmp[2]; + + ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + finfo->rx_id = tmp[0]; + finfo->rx_irq = tmp[1]; + finfo->rx_usr = tmp[2]; + + finfo->name = child->name; + + if (of_find_property(child, "ti,mbox-send-noirq", NULL)) + finfo->send_no_irq = true; + if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos || finfo->tx_usr >= num_users || finfo->rx_usr >= num_users) return -EINVAL; diff --git a/include/linux/platform_data/mailbox-omap.h b/include/linux/platform_data/mailbox-omap.h deleted file mode 100644 index 4631dbb4255e..000000000000 --- a/include/linux/platform_data/mailbox-omap.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * mailbox-omap.h - * - * Copyright (C) 2013 Texas Instruments, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _PLAT_MAILBOX_H -#define _PLAT_MAILBOX_H - -/* Interrupt register configuration types */ -#define MBOX_INTR_CFG_TYPE1 (0) -#define MBOX_INTR_CFG_TYPE2 (1) - -/** - * struct omap_mbox_dev_info - OMAP mailbox device attribute info - * @name: name of the mailbox device - * @tx_id: mailbox queue id used for transmitting messages - * @rx_id: mailbox queue id on which messages are received - * @irq_id: irq identifier number to use from the hwmod data - * @usr_id: mailbox user id for identifying the interrupt into - * the MPU interrupt controller. - */ -struct omap_mbox_dev_info { - const char *name; - u32 tx_id; - u32 rx_id; - u32 irq_id; - u32 usr_id; -}; - -/** - * struct omap_mbox_pdata - OMAP mailbox platform data - * @intr_type: type of interrupt configuration registers used - while programming mailbox queue interrupts - * @num_users: number of users (processor devices) that the mailbox - * h/w block can interrupt - * @num_fifos: number of h/w fifos within the mailbox h/w block - * @info_cnt: number of mailbox devices for the platform - * @info: array of mailbox device attributes - */ -struct omap_mbox_pdata { - u32 intr_type; - u32 num_users; - u32 num_fifos; - u32 info_cnt; - struct omap_mbox_dev_info *info; -}; - -#endif /* _PLAT_MAILBOX_H */ -- cgit From dd28216528cf67339cd4f5854166fbd4eefd7fa8 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Wed, 6 Apr 2016 18:37:20 -0500 Subject: mailbox/omap: kill omap_mbox_{save/restore}_ctx() functions The omap_mbox_save_ctx() and omap_mbox_restore_ctx() API were previously provided to OMAP mailbox clients to save and restore the mailbox context during system suspend/resume. The save and restore functionality is now implemented through System PM driver callbacks, and there is no need for these functions, so kill these API. Signed-off-by: Suman Anna Signed-off-by: Jassi Brar --- drivers/mailbox/omap-mailbox.c | 51 ------------------------------------------ include/linux/omap-mailbox.h | 2 -- 2 files changed, 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index d8d3a4bc5262..c5e8b9cb170d 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -55,13 +55,6 @@ #define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m))) #define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1)) -#define MBOX_REG_SIZE 0x120 - -#define OMAP4_MBOX_REG_SIZE 0x130 - -#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32)) -#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32)) - /* Interrupt register configuration types */ #define MBOX_INTR_CFG_TYPE1 0 #define MBOX_INTR_CFG_TYPE2 1 @@ -118,7 +111,6 @@ struct omap_mbox { struct omap_mbox_device *parent; struct omap_mbox_fifo tx_fifo; struct omap_mbox_fifo rx_fifo; - u32 ctx[OMAP4_MBOX_NR_REGS]; u32 intr_type; struct mbox_chan *chan; bool send_no_irq; @@ -209,49 +201,6 @@ static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) return (int)(enable & status & bit); } -void omap_mbox_save_ctx(struct mbox_chan *chan) -{ - int i; - int nr_regs; - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); - - if (WARN_ON(!mbox)) - return; - - if (mbox->intr_type) - nr_regs = OMAP4_MBOX_NR_REGS; - else - nr_regs = MBOX_NR_REGS; - for (i = 0; i < nr_regs; i++) { - mbox->ctx[i] = mbox_read_reg(mbox->parent, i * sizeof(u32)); - - dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__, - i, mbox->ctx[i]); - } -} -EXPORT_SYMBOL(omap_mbox_save_ctx); - -void omap_mbox_restore_ctx(struct mbox_chan *chan) -{ - int i; - int nr_regs; - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); - - if (WARN_ON(!mbox)) - return; - - if (mbox->intr_type) - nr_regs = OMAP4_MBOX_NR_REGS; - else - nr_regs = MBOX_NR_REGS; - for (i = 0; i < nr_regs; i++) { - mbox_write_reg(mbox->parent, mbox->ctx[i], i * sizeof(u32)); - dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__, - i, mbox->ctx[i]); - } -} -EXPORT_SYMBOL(omap_mbox_restore_ctx); - static void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { u32 l; diff --git a/include/linux/omap-mailbox.h b/include/linux/omap-mailbox.h index 587bbdd31f5a..c726bd833761 100644 --- a/include/linux/omap-mailbox.h +++ b/include/linux/omap-mailbox.h @@ -21,8 +21,6 @@ struct mbox_client; struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl, const char *chan_name); -void omap_mbox_save_ctx(struct mbox_chan *chan); -void omap_mbox_restore_ctx(struct mbox_chan *chan); void omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq); -- cgit From db0a6fb5d97afe01fd9c47d37c6daa82d4d4001d Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 21 Apr 2016 14:14:01 -0400 Subject: audit: add tty field to LOGIN event The tty field was missing from AUDIT_LOGIN events. Refactor code to create a new function audit_get_tty(), using it to replace the call in audit_log_task_info() and to add it to audit_log_set_loginuid(). Lock and bump the kref to protect it, adding audit_put_tty() alias to decrement it. Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore --- include/linux/audit.h | 24 ++++++++++++++++++++++++ kernel/audit.c | 18 +++++------------- kernel/auditsc.c | 8 ++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index b40ed5df5542..32cdafb312d8 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -26,6 +26,7 @@ #include #include #include +#include #define AUDIT_INO_UNSET ((unsigned long)-1) #define AUDIT_DEV_UNSET ((dev_t)-1) @@ -343,6 +344,23 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk) return tsk->sessionid; } +static inline struct tty_struct *audit_get_tty(struct task_struct *tsk) +{ + struct tty_struct *tty = NULL; + unsigned long flags; + + spin_lock_irqsave(&tsk->sighand->siglock, flags); + if (tsk->signal) + tty = tty_kref_get(tsk->signal->tty); + spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + return tty; +} + +static inline void audit_put_tty(struct tty_struct *tty) +{ + tty_kref_put(tty); +} + extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); extern void __audit_bprm(struct linux_binprm *bprm); @@ -500,6 +518,12 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk) { return -1; } +static inline struct tty_struct *audit_get_tty(struct task_struct *tsk) +{ + return NULL; +} +static inline void audit_put_tty(struct tty_struct *tty) +{ } static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { } static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, diff --git a/kernel/audit.c b/kernel/audit.c index f52fbefede09..384374a1d232 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -64,7 +64,6 @@ #include #endif #include -#include #include #include @@ -1871,21 +1870,14 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) { const struct cred *cred; char comm[sizeof(tsk->comm)]; - char *tty; + struct tty_struct *tty; if (!ab) return; /* tsk == current */ cred = current_cred(); - - spin_lock_irq(&tsk->sighand->siglock); - if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) - tty = tsk->signal->tty->name; - else - tty = "(none)"; - spin_unlock_irq(&tsk->sighand->siglock); - + tty = audit_get_tty(tsk); audit_log_format(ab, " ppid=%d pid=%d auid=%u uid=%u gid=%u" " euid=%u suid=%u fsuid=%u" @@ -1901,11 +1893,11 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) from_kgid(&init_user_ns, cred->egid), from_kgid(&init_user_ns, cred->sgid), from_kgid(&init_user_ns, cred->fsgid), - tty, audit_get_sessionid(tsk)); - + tty ? tty_name(tty) : "(none)", + audit_get_sessionid(tsk)); + audit_put_tty(tty); audit_log_format(ab, " comm="); audit_log_untrustedstring(ab, get_task_comm(comm, tsk)); - audit_log_d_path_exe(ab, tsk->mm); audit_log_task_context(ab); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 195ffaee50b9..71e14d836e69 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1980,6 +1980,7 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid, { struct audit_buffer *ab; uid_t uid, oldloginuid, loginuid; + struct tty_struct *tty; if (!audit_enabled) return; @@ -1987,14 +1988,17 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid, uid = from_kuid(&init_user_ns, task_uid(current)); oldloginuid = from_kuid(&init_user_ns, koldloginuid); loginuid = from_kuid(&init_user_ns, kloginuid), + tty = audit_get_tty(current); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); if (!ab) return; audit_log_format(ab, "pid=%d uid=%u", task_pid_nr(current), uid); audit_log_task_context(ab); - audit_log_format(ab, " old-auid=%u auid=%u old-ses=%u ses=%u res=%d", - oldloginuid, loginuid, oldsessionid, sessionid, !rc); + audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d", + oldloginuid, loginuid, tty ? tty_name(tty) : "(none)", + oldsessionid, sessionid, !rc); + audit_put_tty(tty); audit_log_end(ab); } -- cgit From dd80b54b18db3d76a43558daaa6ea3aa67f5aacd Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 20 Apr 2016 15:39:11 +0200 Subject: USB: LTM also for USB 3.1 LTM is also defined for SS+. The correct test is to check for anything slower than SS not exactly SS. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 6a9a0c28415d..29aba76017ee 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -720,7 +720,7 @@ extern void usb_enable_ltm(struct usb_device *udev); static inline bool usb_device_supports_ltm(struct usb_device *udev) { - if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + if (udev->speed < USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) return false; return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; } -- cgit From fca504f6054f2a62d966afde23a0cfbf9e3dc32f Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 20 Apr 2016 15:39:12 +0200 Subject: USB: correct intervals for SS+ SS+ also expresses intervals in units of 125ms. Testing must be for SS or faster, not SS exactly. Signed-off-by: Oliver neukum Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 29aba76017ee..7824f4557d50 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1569,7 +1569,7 @@ static inline void usb_fill_bulk_urb(struct urb *urb, * Initializes a interrupt urb with the proper information needed to submit * it to a device. * - * Note that High Speed and SuperSpeed interrupt endpoints use a logarithmic + * Note that High Speed and SuperSpeed(+) interrupt endpoints use a logarithmic * encoding of the endpoint interval, and express polling intervals in * microframes (eight per millisecond) rather than in frames (one per * millisecond). @@ -1595,7 +1595,7 @@ static inline void usb_fill_int_urb(struct urb *urb, urb->complete = complete_fn; urb->context = context; - if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) { + if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) { /* make sure interval is within allowed range */ interval = clamp(interval, 1, 16); -- cgit From dad56ee742a3abbb5d9e8108f8537d412bff3f57 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 26 Apr 2016 21:22:22 -0400 Subject: tracing: Move event_trigger_unlock_commit{_regs}() to local header The functions event_trigger_unlock_commit() and event_trigger_unlock_commit_regs() are no longer used outside the tracing system. Move them out of the generic headers and into the local one. Along with __event_trigger_test_discard() that is only used by them. Signed-off-by: Steven Rostedt --- include/linux/trace_events.h | 94 -------------------------------------------- kernel/trace/trace.h | 94 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 94 deletions(-) (limited to 'include/linux') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 5f89a5b0c7e6..70a181cb3585 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -452,100 +452,6 @@ trace_trigger_soft_disabled(struct trace_event_file *file) return false; } -/* - * Helper function for event_trigger_unlock_commit{_regs}(). - * If there are event triggers attached to this event that requires - * filtering against its fields, then they wil be called as the - * entry already holds the field information of the current event. - * - * It also checks if the event should be discarded or not. - * It is to be discarded if the event is soft disabled and the - * event was only recorded to process triggers, or if the event - * filter is active and this event did not match the filters. - * - * Returns true if the event is discarded, false otherwise. - */ -static inline bool -__event_trigger_test_discard(struct trace_event_file *file, - struct ring_buffer *buffer, - struct ring_buffer_event *event, - void *entry, - enum event_trigger_type *tt) -{ - unsigned long eflags = file->flags; - - if (eflags & EVENT_FILE_FL_TRIGGER_COND) - *tt = event_triggers_call(file, entry); - - if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags)) - ring_buffer_discard_commit(buffer, event); - else if (!filter_check_discard(file, entry, buffer, event)) - return false; - - return true; -} - -/** - * event_trigger_unlock_commit - handle triggers and finish event commit - * @file: The file pointer assoctiated to the event - * @buffer: The ring buffer that the event is being written to - * @event: The event meta data in the ring buffer - * @entry: The event itself - * @irq_flags: The state of the interrupts at the start of the event - * @pc: The state of the preempt count at the start of the event. - * - * This is a helper function to handle triggers that require data - * from the event itself. It also tests the event against filters and - * if the event is soft disabled and should be discarded. - */ -static inline void -event_trigger_unlock_commit(struct trace_event_file *file, - struct ring_buffer *buffer, - struct ring_buffer_event *event, - void *entry, unsigned long irq_flags, int pc) -{ - enum event_trigger_type tt = ETT_NONE; - - if (!__event_trigger_test_discard(file, buffer, event, entry, &tt)) - trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc); - - if (tt) - event_triggers_post_call(file, tt, entry); -} - -/** - * event_trigger_unlock_commit_regs - handle triggers and finish event commit - * @file: The file pointer assoctiated to the event - * @buffer: The ring buffer that the event is being written to - * @event: The event meta data in the ring buffer - * @entry: The event itself - * @irq_flags: The state of the interrupts at the start of the event - * @pc: The state of the preempt count at the start of the event. - * - * This is a helper function to handle triggers that require data - * from the event itself. It also tests the event against filters and - * if the event is soft disabled and should be discarded. - * - * Same as event_trigger_unlock_commit() but calls - * trace_buffer_unlock_commit_regs() instead of trace_buffer_unlock_commit(). - */ -static inline void -event_trigger_unlock_commit_regs(struct trace_event_file *file, - struct ring_buffer *buffer, - struct ring_buffer_event *event, - void *entry, unsigned long irq_flags, int pc, - struct pt_regs *regs) -{ - enum event_trigger_type tt = ETT_NONE; - - if (!__event_trigger_test_discard(file, buffer, event, entry, &tt)) - trace_buffer_unlock_commit_regs(file->tr, buffer, event, - irq_flags, pc, regs); - - if (tt) - event_triggers_post_call(file, tt, entry); -} - #ifdef CONFIG_BPF_EVENTS unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx); #else diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 727a3d28bce5..c0eac7b1e5a6 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1065,6 +1065,100 @@ struct trace_subsystem_dir { int nr_events; }; +/* + * Helper function for event_trigger_unlock_commit{_regs}(). + * If there are event triggers attached to this event that requires + * filtering against its fields, then they wil be called as the + * entry already holds the field information of the current event. + * + * It also checks if the event should be discarded or not. + * It is to be discarded if the event is soft disabled and the + * event was only recorded to process triggers, or if the event + * filter is active and this event did not match the filters. + * + * Returns true if the event is discarded, false otherwise. + */ +static inline bool +__event_trigger_test_discard(struct trace_event_file *file, + struct ring_buffer *buffer, + struct ring_buffer_event *event, + void *entry, + enum event_trigger_type *tt) +{ + unsigned long eflags = file->flags; + + if (eflags & EVENT_FILE_FL_TRIGGER_COND) + *tt = event_triggers_call(file, entry); + + if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags)) + ring_buffer_discard_commit(buffer, event); + else if (!filter_check_discard(file, entry, buffer, event)) + return false; + + return true; +} + +/** + * event_trigger_unlock_commit - handle triggers and finish event commit + * @file: The file pointer assoctiated to the event + * @buffer: The ring buffer that the event is being written to + * @event: The event meta data in the ring buffer + * @entry: The event itself + * @irq_flags: The state of the interrupts at the start of the event + * @pc: The state of the preempt count at the start of the event. + * + * This is a helper function to handle triggers that require data + * from the event itself. It also tests the event against filters and + * if the event is soft disabled and should be discarded. + */ +static inline void +event_trigger_unlock_commit(struct trace_event_file *file, + struct ring_buffer *buffer, + struct ring_buffer_event *event, + void *entry, unsigned long irq_flags, int pc) +{ + enum event_trigger_type tt = ETT_NONE; + + if (!__event_trigger_test_discard(file, buffer, event, entry, &tt)) + trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc); + + if (tt) + event_triggers_post_call(file, tt, entry); +} + +/** + * event_trigger_unlock_commit_regs - handle triggers and finish event commit + * @file: The file pointer assoctiated to the event + * @buffer: The ring buffer that the event is being written to + * @event: The event meta data in the ring buffer + * @entry: The event itself + * @irq_flags: The state of the interrupts at the start of the event + * @pc: The state of the preempt count at the start of the event. + * + * This is a helper function to handle triggers that require data + * from the event itself. It also tests the event against filters and + * if the event is soft disabled and should be discarded. + * + * Same as event_trigger_unlock_commit() but calls + * trace_buffer_unlock_commit_regs() instead of trace_buffer_unlock_commit(). + */ +static inline void +event_trigger_unlock_commit_regs(struct trace_event_file *file, + struct ring_buffer *buffer, + struct ring_buffer_event *event, + void *entry, unsigned long irq_flags, int pc, + struct pt_regs *regs) +{ + enum event_trigger_type tt = ETT_NONE; + + if (!__event_trigger_test_discard(file, buffer, event, entry, &tt)) + trace_buffer_unlock_commit_regs(file->tr, buffer, event, + irq_flags, pc, regs); + + if (tt) + event_triggers_post_call(file, tt, entry); +} + #define FILTER_PRED_INVALID ((unsigned short)-1) #define FILTER_PRED_IS_RIGHT (1 << 15) #define FILTER_PRED_FOLD (1 << 15) -- cgit From da20dfe6b50ea4c1a82797b7ee8655a370535d73 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 21 Apr 2016 12:53:29 -0700 Subject: fs: fix over-zealous use of "const" When I was fixing up const recommendations from checkpatch.pl, I went overboard. This fixes the warning (during a W=1 build): include/linux/fs.h:2627:74: warning: type qualifiers ignored on function return type [-Wignored-qualifiers] static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) Reported-by: Andy Shevchenko Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 90477550b935..9847d5c49a0e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2600,7 +2600,7 @@ static const char * const kernel_read_file_str[] = { __kernel_read_file_id(__fid_stringify) }; -static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) +static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id) { if (id < 0 || id >= READING_MAX_ID) return kernel_read_file_str[READING_UNKNOWN]; -- cgit From 65da9a0a3bf2202c2432f42d41eb908f2fa30579 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 27 Apr 2016 10:13:46 -0400 Subject: tracing: Make filter_check_discard() local Nothing outside of the tracing directory calls filter_check_discard() or check_filter_check_discard(). They should not be called by modules. Move their prototypes into the local tracing header and remove their EXPORT_SYMBOL() macros. Signed-off-by: Steven Rostedt --- include/linux/trace_events.h | 6 ------ kernel/trace/trace.c | 2 -- kernel/trace/trace.h | 6 ++++++ 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 70a181cb3585..bb383af35cc7 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -413,12 +413,6 @@ enum event_trigger_type { extern int filter_match_preds(struct event_filter *filter, void *rec); -extern int filter_check_discard(struct trace_event_file *file, void *rec, - struct ring_buffer *buffer, - struct ring_buffer_event *event); -extern int call_filter_check_discard(struct trace_event_call *call, void *rec, - struct ring_buffer *buffer, - struct ring_buffer_event *event); extern enum event_trigger_type event_triggers_call(struct trace_event_file *file, void *rec); extern void event_triggers_post_call(struct trace_event_file *file, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 46028d47d252..02f5a5f51d49 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -318,7 +318,6 @@ int filter_check_discard(struct trace_event_file *file, void *rec, return 0; } -EXPORT_SYMBOL_GPL(filter_check_discard); int call_filter_check_discard(struct trace_event_call *call, void *rec, struct ring_buffer *buffer, @@ -332,7 +331,6 @@ int call_filter_check_discard(struct trace_event_call *call, void *rec, return 0; } -EXPORT_SYMBOL_GPL(call_filter_check_discard); static cycle_t buffer_ftrace_now(struct trace_buffer *buf, int cpu) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index c0eac7b1e5a6..ee8691c66bfe 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1065,6 +1065,12 @@ struct trace_subsystem_dir { int nr_events; }; +extern int filter_check_discard(struct trace_event_file *file, void *rec, + struct ring_buffer *buffer, + struct ring_buffer_event *event); +extern int call_filter_check_discard(struct trace_event_call *call, void *rec, + struct ring_buffer *buffer, + struct ring_buffer_event *event); /* * Helper function for event_trigger_unlock_commit{_regs}(). * If there are event triggers attached to this event that requires -- cgit From 188e3c5cd2b672620291e64a21f1598fe91e40b6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 27 Apr 2016 11:56:04 +0200 Subject: tty: provide tty_name() even without CONFIG_TTY The audit subsystem just started printing the name of the tty, but that causes a build failure when CONFIG_TTY is disabled: kernel/built-in.o: In function `audit_log_task_info': memremap.c:(.text+0x5e34c): undefined reference to `tty_name' kernel/built-in.o: In function `audit_set_loginuid': memremap.c:(.text+0x63b34): undefined reference to `tty_name' This adds tty_name() to the list of functions that are provided as trivial stubs in that configuration. Signed-off-by: Arnd Bergmann Fixes: db0a6fb5d97a ("audit: add tty field to LOGIN event") Signed-off-by: Paul Moore --- include/linux/tty.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tty.h b/include/linux/tty.h index d9fb4b043f56..a93cce297832 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -371,6 +371,7 @@ extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); /* tty_io.c */ extern int __init tty_init(void); +extern const char *tty_name(const struct tty_struct *tty); #else static inline void console_init(void) { } @@ -391,6 +392,8 @@ static inline struct tty_struct *get_current_tty(void) /* tty_io.c */ static inline int __init tty_init(void) { return 0; } +static inline const char *tty_name(const struct tty_struct *tty) +{ return "(none)"; } #endif extern struct ktermios tty_std_termios; @@ -415,7 +418,6 @@ static inline struct tty_struct *tty_kref_get(struct tty_struct *tty) return tty; } -extern const char *tty_name(const struct tty_struct *tty); extern const char *tty_driver_name(const struct tty_struct *tty); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int __tty_check_change(struct tty_struct *tty, int sig); -- cgit From 3358d2d9f47af86bdd71edb24b361f72a54ec04e Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 18 Jun 2015 17:28:40 -0400 Subject: clk: tegra: Add interface to enable hardware control of SATA/XUSB PLLs On Tegra210, hardware control of the SATA and XUSB pad PLLs must be done during the UPHY enable sequence rather than the PLLE enable sequence. Export functions to do this so that hardware control can be enabled from the XUSB padctl driver. Signed-off-by: Andrew Bresticker Signed-off-by: Rhyland Klein Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-tegra210.c | 58 ++++++++++++++++++++++++++++++++++++++++ include/linux/clk/tegra.h | 5 ++++ 2 files changed, 63 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 637041fd53ad..3d0edee1f9fe 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -175,6 +175,19 @@ #define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) #define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) +#define SATA_PLL_CFG0 0x490 +#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) +#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2) +#define SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13) +#define SATA_PLL_CFG0_SEQ_ENABLE BIT(24) + +#define XUSBIO_PLL_CFG0 0x51c +#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) +#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6) +#define XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13) +#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24) + #define UTMIPLL_HW_PWRDN_CFG0 0x52c #define UTMIPLL_HW_PWRDN_CFG0_UTMIPLL_LOCK BIT(31) #define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25) @@ -416,6 +429,51 @@ static const char *mux_pllmcp_clkm[] = { #define PLLU_MISC0_WRITE_MASK 0xbfffffff #define PLLU_MISC1_WRITE_MASK 0x00000007 +void tegra210_xusb_pll_hw_control_enable(void) +{ + u32 val; + + val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0); + val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL | + XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL); + val |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET | + XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ; + writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_control_enable); + +void tegra210_xusb_pll_hw_sequence_start(void) +{ + u32 val; + + val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0); + val |= XUSBIO_PLL_CFG0_SEQ_ENABLE; + writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_sequence_start); + +void tegra210_sata_pll_hw_control_enable(void) +{ + u32 val; + + val = readl_relaxed(clk_base + SATA_PLL_CFG0); + val &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL; + val |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET | + SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ; + writel_relaxed(val, clk_base + SATA_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_control_enable); + +void tegra210_sata_pll_hw_sequence_start(void) +{ + u32 val; + + val = readl_relaxed(clk_base + SATA_PLL_CFG0); + val |= SATA_PLL_CFG0_SEQ_ENABLE; + writel_relaxed(val, clk_base + SATA_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_sequence_start); + static inline void _pll_misc_chk_default(void __iomem *base, struct tegra_clk_pll_params *params, u8 misc_num, u32 default_val, u32 mask) diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index 57bf7aab4516..7007a5f48080 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -121,4 +121,9 @@ static inline void tegra_cpu_clock_resume(void) } #endif +extern void tegra210_xusb_pll_hw_control_enable(void); +extern void tegra210_xusb_pll_hw_sequence_start(void); +extern void tegra210_sata_pll_hw_control_enable(void); +extern void tegra210_sata_pll_hw_sequence_start(void); + #endif /* __LINUX_CLK_TEGRA_H_ */ -- cgit From feb26ac31a2a5cb88d86680d9a94916a6343e9e6 Mon Sep 17 00:00:00 2001 From: Chris Bainbridge Date: Mon, 25 Apr 2016 13:48:38 +0100 Subject: usb: core: hub: hub_port_init lock controller instead of bus The XHCI controller presents two USB buses to the system - one for USB2 and one for USB3. The hub init code (hub_port_init) is reentrant but only locks one bus per thread, leading to a race condition failure when two threads attempt to simultaneously initialise a USB2 and USB3 device: [ 8.034843] xhci_hcd 0000:00:14.0: Timeout while waiting for setup device command [ 13.183701] usb 3-3: device descriptor read/all, error -110 On a test system this failure occurred on 6% of all boots. The call traces at the point of failure are: Call Trace: [] schedule+0x37/0x90 [] usb_kill_urb+0x8d/0xd0 [] ? wake_up_atomic_t+0x30/0x30 [] usb_start_wait_urb+0xbe/0x150 [] usb_control_msg+0xbc/0xf0 [] hub_port_init+0x51e/0xb70 [] hub_event+0x817/0x1570 [] process_one_work+0x1ff/0x620 [] ? process_one_work+0x15f/0x620 [] worker_thread+0x64/0x4b0 [] ? rescuer_thread+0x390/0x390 [] kthread+0x105/0x120 [] ? kthread_create_on_node+0x200/0x200 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x200/0x200 Call Trace: [] xhci_setup_device+0x53d/0xa40 [] xhci_address_device+0xe/0x10 [] hub_port_init+0x1bf/0xb70 [] ? trace_hardirqs_on+0xd/0x10 [] hub_event+0x817/0x1570 [] process_one_work+0x1ff/0x620 [] ? process_one_work+0x15f/0x620 [] worker_thread+0x64/0x4b0 [] ? rescuer_thread+0x390/0x390 [] kthread+0x105/0x120 [] ? kthread_create_on_node+0x200/0x200 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x200/0x200 Which results from the two call chains: hub_port_init usb_get_device_descriptor usb_get_descriptor usb_control_msg usb_internal_control_msg usb_start_wait_urb usb_submit_urb / wait_for_completion_timeout / usb_kill_urb hub_port_init hub_set_address xhci_address_device xhci_setup_device Mathias Nyman explains the current behaviour violates the XHCI spec: hub_port_reset() will end up moving the corresponding xhci device slot to default state. As hub_port_reset() is called several times in hub_port_init() it sounds reasonable that we could end up with two threads having their xhci device slots in default state at the same time, which according to xhci 4.5.3 specs still is a big no no: "Note: Software shall not transition more than one Device Slot to the Default State at a time" So both threads fail at their next task after this. One fails to read the descriptor, and the other fails addressing the device. Fix this in hub_port_init by locking the USB controller (instead of an individual bus) to prevent simultaneous initialisation of both buses. Fixes: 638139eb95d2 ("usb: hub: allow to process more usb hub events in parallel") Link: https://lkml.org/lkml/2016/2/8/312 Link: https://lkml.org/lkml/2016/2/4/748 Signed-off-by: Chris Bainbridge Cc: stable Acked-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 15 +++++++++++++-- drivers/usb/core/hub.c | 8 ++++---- include/linux/usb.h | 3 +-- include/linux/usb/hcd.h | 1 + 4 files changed, 19 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2ca2cef7f681..980fc5774151 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -994,7 +994,7 @@ static void usb_bus_init (struct usb_bus *bus) bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; - mutex_init(&bus->usb_address0_mutex); + mutex_init(&bus->devnum_next_mutex); } /*-------------------------------------------------------------------------*/ @@ -2521,6 +2521,14 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, return NULL; } if (primary_hcd == NULL) { + hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex), + GFP_KERNEL); + if (!hcd->address0_mutex) { + kfree(hcd); + dev_dbg(dev, "hcd address0 mutex alloc failed\n"); + return NULL; + } + mutex_init(hcd->address0_mutex); hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), GFP_KERNEL); if (!hcd->bandwidth_mutex) { @@ -2532,6 +2540,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, dev_set_drvdata(dev, hcd); } else { mutex_lock(&usb_port_peer_mutex); + hcd->address0_mutex = primary_hcd->address0_mutex; hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; @@ -2598,8 +2607,10 @@ static void hcd_release(struct kref *kref) struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); mutex_lock(&usb_port_peer_mutex); - if (usb_hcd_is_primary_hcd(hcd)) + if (usb_hcd_is_primary_hcd(hcd)) { + kfree(hcd->address0_mutex); kfree(hcd->bandwidth_mutex); + } if (hcd->shared_hcd) { struct usb_hcd *peer = hcd->shared_hcd; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c2270d8fac12..bee13517676f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2082,7 +2082,7 @@ static void choose_devnum(struct usb_device *udev) struct usb_bus *bus = udev->bus; /* be safe when more hub events are proceed in parallel */ - mutex_lock(&bus->usb_address0_mutex); + mutex_lock(&bus->devnum_next_mutex); if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); @@ -2100,7 +2100,7 @@ static void choose_devnum(struct usb_device *udev) set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } - mutex_unlock(&bus->usb_address0_mutex); + mutex_unlock(&bus->devnum_next_mutex); } static void release_devnum(struct usb_device *udev) @@ -4366,7 +4366,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; - mutex_lock(&hdev->bus->usb_address0_mutex); + mutex_lock(hcd->address0_mutex); /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ @@ -4652,7 +4652,7 @@ fail: hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } - mutex_unlock(&hdev->bus->usb_address0_mutex); + mutex_unlock(hcd->address0_mutex); return retval; } diff --git a/include/linux/usb.h b/include/linux/usb.h index 7824f4557d50..01b6c61cf9bb 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -374,13 +374,12 @@ struct usb_bus { int devnum_next; /* Next open device number in * round-robin allocation */ + struct mutex devnum_next_mutex; /* devnum_next mutex */ struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ - struct mutex usb_address0_mutex; /* unaddressed device mutex */ - int bandwidth_allocated; /* on this bus: how much of the time * reserved for periodic (intr/iso) * requests is used, on average? diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index b98f831dcda3..66fc13705ab7 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -181,6 +181,7 @@ struct usb_hcd { * bandwidth_mutex should be dropped after a successful control message * to the device, or resetting the bandwidth after a failed attempt. */ + struct mutex *address0_mutex; struct mutex *bandwidth_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd; -- cgit From 8453c5cafd32c4d6bd13ec4a62d4b639f4edb222 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 28 Apr 2016 08:21:03 -0700 Subject: ARM: OMAP2+: Add more functions to pwm pdata for ir-rx51 Before we start removing omap3 legacy booting support, let's make n900 DT booting behave the same way for ir-rx51 as the legacy booting does. For now, we need to pass pdata to the ir-rx51 driver. This means that the n900 tree can move to using DT based booting without having to carry all the legacy platform data with it when it gets dropped from the mainline tree. Note that the ir-rx51 driver is currently disabled because of the dependency to !ARCH_MULTIPLATFORM. This will get sorted out later with the help of drivers/pwm/pwm-omap-dmtimer.c. But first we need to add chained IRQ support to dmtimer code to avoid introducing new custom frameworks. So let's just pass the necessary dmtimer functions to ir-rx51 so we can get it working in the following patch. Cc: Neil Armstrong Tested-by: Ivaylo Dimitrov Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-rx51-peripherals.c | 35 ++++++++++++++++++++++++-- arch/arm/mach-omap2/pdata-quirks.c | 33 +++++++++++++++++++++++- include/linux/platform_data/media/ir-rx51.h | 1 + include/linux/platform_data/pwm_omap_dmtimer.h | 21 ++++++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index da174c0d603b..9a7073949d1d 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -30,6 +30,8 @@ #include #include +#include + #include #include "common.h" @@ -47,9 +49,8 @@ #include