Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/2] ARM: dts: stm32: enable USB OTG HS on stm32mp157c-ev1
From: Amelie Delaunay @ 2018-05-17 15:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526572052-17155-1-git-send-email-amelie.delaunay@st.com>

This patch enables USB OTG HS on stm32mp157c-ev1 in Peripheral mode.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/boot/dts/stm32mp157c-ev1.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts
index 9382d80..5a0b77e 100644
--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts
@@ -103,6 +103,13 @@
 	};
 };
 
+&usbotg_hs {
+	dr_mode = "peripheral";
+	phys = <&usbphyc_port1 0>;
+	phy-names = "usb2-phy";
+	status = "okay";
+};
+
 &usbphyc {
 	status = "okay";
 };
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/2] ARM: dts: stm32: add USB OTG HS support for stm32mp157c SoC
From: Amelie Delaunay @ 2018-05-17 15:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526572052-17155-1-git-send-email-amelie.delaunay@st.com>

This patch adds support for USB OTG HS on STM32MP157C SoC.
USB OTG HS controller is based on DWC2 controller.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/boot/dts/stm32mp157c.dtsi | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index b66f673..6c3815f 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -600,6 +600,21 @@
 			clocks = <&rcc DMAMUX>;
 		};
 
+		usbotg_hs: usb-otg at 49000000 {
+			compatible = "snps,dwc2";
+			reg = <0x49000000 0x10000>;
+			clocks = <&rcc USBO_K>;
+			clock-names = "otg";
+			resets = <&rcc USBO_R>;
+			reset-names = "dwc2";
+			interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+			g-rx-fifo-size = <256>;
+			g-np-tx-fifo-size = <32>;
+			g-tx-fifo-size = <128 128 64 64 64 64 32 32>;
+			dr_mode = "otg";
+			status = "disabled";
+		};
+
 		rcc: rcc at 50000000 {
 			compatible = "st,stm32mp1-rcc", "syscon";
 			reg = <0x50000000 0x1000>;
-- 
2.7.4

^ permalink raw reply related

* [PATCH 0/2] Add USB OTG HS support on stm32mp157c
From: Amelie Delaunay @ 2018-05-17 15:47 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset adds support for USB OTG HS on stm32mp157c SoC.
It also enables USB OTG HS on stm32mp157c-ev1 board in Peripheral mode.

Amelie Delaunay (2):
  ARM: dts: stm32: add USB OTG HS support for stm32mp157c SoC
  ARM: dts: stm32: enable USB OTG HS on stm32mp157c-ev1

 arch/arm/boot/dts/stm32mp157c-ev1.dts |  7 +++++++
 arch/arm/boot/dts/stm32mp157c.dtsi    | 15 +++++++++++++++
 2 files changed, 22 insertions(+)

-- 
2.7.4

^ permalink raw reply

* [PATCH v9 02/12] drivers: base: cacheinfo: setup DT cache properties early
From: Sudeep Holla @ 2018-05-17 15:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a64ed431-0f60-2711-4138-eeefc7ba2d8b@arm.com>

On 16/05/18 11:56, Sudeep Holla wrote:
> Hi Andy,
> 
> On 15/05/18 20:32, Andy Shevchenko wrote:
>> On Tue, May 15, 2018 at 8:15 PM, Jeremy Linton <jeremy.linton@arm.com> wrote:
>>> On 05/11/2018 06:57 PM, Jeremy Linton wrote:
>>
>>>>   -     cache_size = of_get_property(this_leaf->of_node, propname, NULL);
>>>> +       cache_size = of_get_property(np, propname, NULL);
>>>>         if (cache_size)
>>>>                 this_leaf->size = of_read_number(cache_size, 1);
>>
>> Can't you switch to of_read_property_uXX() variant here?
>>
> 
> This patch is just changing the first argument to the calls. So if we
> need to change, it has to be separate patch.
> 
> Now, we can use of_property_read_u64() but is there any particular
> reason you mention that ? One reason I can see is that we can avoid
> making explicit of_get_property call. Just wanted to the motive before I
> can write the patch.
> 

Is below patch does what you were looking for ?

Regards,
Sudeep

--
>From 71f1c10ddb5915a92fa74d38a4e2406d0ab27845 Mon Sep 17 00:00:00 2001
From: Sudeep Holla <sudeep.holla@arm.com>
Date: Wed, 16 May 2018 13:45:53 +0100
Subject: [PATCH] drivers: base: cacheinfo: use OF property_read_u64 instead of
 get_property,read_number

of_property_read_u64 searches for a property in a device node and read
a 64-bit value from it. Instead of using of_get_property to get the
property and then read 64-bit value using of_read_number, we can make
use of of_property_read_u64.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/base/cacheinfo.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 2880e2ab01f5..56715014f07b 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -74,22 +74,21 @@ static inline int get_cacheinfo_idx(enum cache_type type)
 static void cache_size(struct cacheinfo *this_leaf, struct device_node *np)
 {
 	const char *propname;
-	const __be32 *cache_size;
+	u64 cache_size;
 	int ct_idx;
 
 	ct_idx = get_cacheinfo_idx(this_leaf->type);
 	propname = cache_type_info[ct_idx].size_prop;
 
-	cache_size = of_get_property(np, propname, NULL);
-	if (cache_size)
-		this_leaf->size = of_read_number(cache_size, 1);
+	if (!of_property_read_u64(np, propname, &cache_size))
+		this_leaf->size = cache_size;
 }
 
 /* not cache_line_size() because that's a macro in include/linux/cache.h */
 static void cache_get_line_size(struct cacheinfo *this_leaf,
 				struct device_node *np)
 {
-	const __be32 *line_size;
+	u64 line_size;
 	int i, lim, ct_idx;
 
 	ct_idx = get_cacheinfo_idx(this_leaf->type);
@@ -99,27 +98,26 @@ static void cache_get_line_size(struct cacheinfo *this_leaf,
 		const char *propname;
 
 		propname = cache_type_info[ct_idx].line_size_props[i];
-		line_size = of_get_property(np, propname, NULL);
-		if (line_size)
+		line_size = of_property_read_u64(np, propname, &line_size);
+		if (line_size) {
+			this_leaf->coherency_line_size = line_size;
 			break;
+		}
 	}
 
-	if (line_size)
-		this_leaf->coherency_line_size = of_read_number(line_size, 1);
 }
 
 static void cache_nr_sets(struct cacheinfo *this_leaf, struct device_node *np)
 {
 	const char *propname;
-	const __be32 *nr_sets;
+	u64 nr_sets;
 	int ct_idx;
 
 	ct_idx = get_cacheinfo_idx(this_leaf->type);
 	propname = cache_type_info[ct_idx].nr_sets_prop;
 
-	nr_sets = of_get_property(np, propname, NULL);
-	if (nr_sets)
-		this_leaf->number_of_sets = of_read_number(nr_sets, 1);
+	if (!of_property_read_u64(np, propname, &nr_sets))
+		this_leaf->number_of_sets = nr_sets;
 }
 
 static void cache_associativity(struct cacheinfo *this_leaf)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3] arm64: signal: Report signal frame size to userspace via auxv
From: Dave Martin @ 2018-05-17 15:45 UTC (permalink / raw)
  To: linux-arm-kernel

Stateful CPU architecture extensions may require the signal frame
to grow to a size that exceeds the arch's MINSIGSTKSZ #define.
However, changing this #define is an ABI break.

To allow userspace the option of determining the signal frame size
in a more forwards-compatible way, this patch adds a new auxv entry
tagged with AT_MINSIGSTKSZ, which provides the maximum signal frame
size that the process can observe during its lifetime.

If AT_MINSIGSTKSZ is absent from the aux vector, the caller can
assume that the MINSIGSTKSZ #define is sufficient.  This allows for
a consistent interface with older kernels that do not provide
AT_MINSIGSTKSZ.

The idea is that libc could expose this via sysconf() or some
similar mechanism.

There is deliberately no AT_SIGSTKSZ.  The kernel knows nothing
about userspace's own stack overheads and should not pretend to
know.

For arm64:

The primary motivation for this interface is the Scalable Vector
Extension, which can require at least 4KB or so of extra space
in the signal frame for the largest hardware implementations.

To determine the correct value, a "Christmas tree" mode (via the
add_all argument) is added to setup_sigframe_layout(), to simulate
addition of all possible records to the signal frame at maximum
possible size.

If this procedure goes wrong somehow, resulting in a stupidly large
frame layout and hence failure of sigframe_alloc() to allocate a
record to the frame, then this is indicative of a kernel bug: the
kernel's internal SIGFRAME_MAXSZ is supposed to sanity-check
against generting frames that we consider _impossibly_ large.  In
this case, SIGSTKSZ is returned as a "reasonable guess that is at
least bigger than MINSIGSTKSZ" and we WARN().

For arm64 SVE:

The SVE context block in the signal frame needs to be considered
too when computing the maximum possible signal frame size.

Because the size of this block depends on the vector length, this
patch computes the size based not on the thread's current vector
length but instead on the maximum possible vector length: this
determines the maximum size of SVE context block that can be
observed in any signal frame for the lifetime of the process.

Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Alex Benn?e <alex.bennee@linaro.org>

---

Changes since v2:

 * Redefine AT_MINSIGSTKSZ as 51 to avoid clash with values defined by
   other architectures.

   This turns out to be a problem for glibc; also random userspace
   software does not necessary check the architecture before using
   getauxval() or otherwise parsing the aux vector, which can make
   aliased tags problematic.

   Really, the headers need cleaning up tree-wide in such away that the
   AT_* definitions do not appear to be arch-private.  To be addressed
   separately.
---
 arch/arm64/include/asm/elf.h         | 11 ++++++++
 arch/arm64/include/asm/processor.h   |  5 ++++
 arch/arm64/include/uapi/asm/auxvec.h |  3 ++-
 arch/arm64/kernel/cpufeature.c       |  1 +
 arch/arm64/kernel/signal.c           | 51 +++++++++++++++++++++++++++++++-----
 5 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index fac1c4d..dc32adb 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -24,6 +24,11 @@
 #include <asm/ptrace.h>
 #include <asm/user.h>
 
+#ifndef __ASSEMBLY__
+#include <linux/bug.h>
+#include <asm/processor.h> /* for signal_minsigstksz, used by ARCH_DLINFO */
+#endif
+
 /*
  * AArch64 static relocation types.
  */
@@ -146,8 +151,14 @@ typedef struct user_fpsimd_state elf_fpregset_t;
 /* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
 #define ARCH_DLINFO							\
 do {									\
+	int minsigstksz = signal_minsigstksz;				\
+									\
+	if (WARN_ON(minsigstksz <= 0))					\
+		minsigstksz = MINSIGSTKSZ;				\
+									\
 	NEW_AUX_ENT(AT_SYSINFO_EHDR,					\
 		    (elf_addr_t)current->mm->context.vdso);		\
+	NEW_AUX_ENT(AT_MINSIGSTKSZ, minsigstksz);			\
 } while (0)
 
 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 7675989..6f60e92 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -35,6 +35,8 @@
 #ifdef __KERNEL__
 
 #include <linux/build_bug.h>
+#include <linux/cache.h>
+#include <linux/init.h>
 #include <linux/stddef.h>
 #include <linux/string.h>
 
@@ -244,6 +246,9 @@ void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused);
 void cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused);
 void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused);
 
+extern int __ro_after_init signal_minsigstksz;	/* user signal frame size */
+extern void __init minsigstksz_setup(void);
+
 /* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
diff --git a/arch/arm64/include/uapi/asm/auxvec.h b/arch/arm64/include/uapi/asm/auxvec.h
index ec0a86d..743c0b8 100644
--- a/arch/arm64/include/uapi/asm/auxvec.h
+++ b/arch/arm64/include/uapi/asm/auxvec.h
@@ -19,7 +19,8 @@
 
 /* vDSO location */
 #define AT_SYSINFO_EHDR	33
+#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery */
 
-#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */
+#define AT_VECTOR_SIZE_ARCH 2 /* entries in ARCH_DLINFO */
 
 #endif
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9d1b06d..0e0b53d 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1619,6 +1619,7 @@ void __init setup_cpu_features(void)
 		pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n");
 
 	sve_setup();
+	minsigstksz_setup();
 
 	/* Advertise that we have computed the system capabilities */
 	set_sys_caps_initialised();
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 154b7d3..ae8d4ea 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/cache.h>
 #include <linux/compat.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -570,8 +571,15 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
 	return 0;
 }
 
-/* Determine the layout of optional records in the signal frame */
-static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
+/*
+ * Determine the layout of optional records in the signal frame
+ *
+ * add_all: if true, lays out the biggest possible signal frame for
+ *	this task; otherwise, generates a layout for the current state
+ *	of the task.
+ */
+static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
+				 bool add_all)
 {
 	int err;
 
@@ -581,7 +589,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
 		return err;
 
 	/* fault information, if valid */
-	if (current->thread.fault_code) {
+	if (add_all || current->thread.fault_code) {
 		err = sigframe_alloc(user, &user->esr_offset,
 				     sizeof(struct esr_context));
 		if (err)
@@ -591,8 +599,18 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
 	if (system_supports_sve()) {
 		unsigned int vq = 0;
 
-		if (test_thread_flag(TIF_SVE))
-			vq = sve_vq_from_vl(current->thread.sve_vl);
+		if (add_all || test_thread_flag(TIF_SVE)) {
+			int vl = sve_max_vl;
+
+			if (!add_all)
+				vl = current->thread.sve_vl;
+
+			/* Fail safe if something wasn't initialised */
+			if (WARN_ON(!sve_vl_valid(vl)))
+				vl = SVE_VL_MIN;
+
+			vq = sve_vq_from_vl(vl);
+		}
 
 		err = sigframe_alloc(user, &user->sve_offset,
 				     SVE_SIG_CONTEXT_SIZE(vq));
@@ -603,7 +621,6 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
 	return sigframe_alloc_end(user);
 }
 
-
 static int setup_sigframe(struct rt_sigframe_user_layout *user,
 			  struct pt_regs *regs, sigset_t *set)
 {
@@ -701,7 +718,7 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
 	int err;
 
 	init_user_layout(user);
-	err = setup_sigframe_layout(user);
+	err = setup_sigframe_layout(user, false);
 	if (err)
 		return err;
 
@@ -936,3 +953,23 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
 		thread_flags = READ_ONCE(current_thread_info()->flags);
 	} while (thread_flags & _TIF_WORK_MASK);
 }
+
+int __ro_after_init signal_minsigstksz;
+
+/*
+ * Determine the stack space required for guaranteed signal devliery.
+ * This function is used to populate AT_MINSIGSTKSZ at process startup.
+ */
+void __init minsigstksz_setup(void)
+{
+	struct rt_sigframe_user_layout user;
+
+	init_user_layout(&user);
+
+	if (WARN_ON(setup_sigframe_layout(&user, true)))
+		signal_minsigstksz = SIGSTKSZ;
+	else
+		signal_minsigstksz = sigframe_size(&user) +
+			round_up(sizeof(struct frame_record), 16) +
+			16; /* max alignment padding */
+}
-- 
2.1.4

^ permalink raw reply related

* [PATCH v2 07/40] iommu: Add a page fault handler
From: Jonathan Cameron @ 2018-05-17 15:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180511190641.23008-8-jean-philippe.brucker@arm.com>

On Fri, 11 May 2018 20:06:08 +0100
Jean-Philippe Brucker <jean-philippe.brucker@arm.com> wrote:

> Some systems allow devices to handle I/O Page Faults in the core mm. For
> example systems implementing the PCI PRI extension or Arm SMMU stall
> model. Infrastructure for reporting these recoverable page faults was
> recently added to the IOMMU core for SVA virtualisation. Add a page fault
> handler for host SVA.
> 
> IOMMU driver can now instantiate several fault workqueues and link them to
> IOPF-capable devices. Drivers can choose between a single global
> workqueue, one per IOMMU device, one per low-level fault queue, one per
> domain, etc.
> 
> When it receives a fault event, supposedly in an IRQ handler, the IOMMU
> driver reports the fault using iommu_report_device_fault(), which calls
> the registered handler. The page fault handler then calls the mm fault
> handler, and reports either success or failure with iommu_page_response().
> When the handler succeeded, the IOMMU retries the access.
> 
> The iopf_param pointer could be embedded into iommu_fault_param. But
> putting iopf_param into the iommu_param structure allows us not to care
> about ordering between calls to iopf_queue_add_device() and
> iommu_register_device_fault_handler().
> 
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>

Hi Jean-Phillipe,

One question below on how we would end up with partial faults left when
doing the queue remove. Code looks fine, but I'm not seeing how that
would happen without buggy hardware... + we presumably have to rely on
the hardware timing out on that request or it's dead anyway...

Jonathan

> 
> ---
> v1->v2: multiple queues registered by IOMMU driver
> ---
>  drivers/iommu/Kconfig      |   4 +
>  drivers/iommu/Makefile     |   1 +
>  drivers/iommu/io-pgfault.c | 363 +++++++++++++++++++++++++++++++++++++
>  include/linux/iommu.h      |  58 ++++++
>  4 files changed, 426 insertions(+)
>  create mode 100644 drivers/iommu/io-pgfault.c
> 
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index 38434899e283..09f13a7c4b60 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -79,6 +79,10 @@ config IOMMU_SVA
>  	select IOMMU_API
>  	select MMU_NOTIFIER
>  
> +config IOMMU_PAGE_FAULT
> +	bool
> +	select IOMMU_API
> +
>  config FSL_PAMU
>  	bool "Freescale IOMMU support"
>  	depends on PCI
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 1dbcc89ebe4c..4b744e399a1b 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -4,6 +4,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
>  obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
>  obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
>  obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
> +obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o
>  obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
>  obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
>  obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
> diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
> new file mode 100644
> index 000000000000..321c00dd3a3d
> --- /dev/null
> +++ b/drivers/iommu/io-pgfault.c
> @@ -0,0 +1,363 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Handle device page faults
> + *
> + * Copyright (C) 2018 ARM Ltd.
> + */
> +
> +#include <linux/iommu.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +
> +/**
> + * struct iopf_queue - IO Page Fault queue
> + * @wq: the fault workqueue
> + * @flush: low-level flush callback
> + * @flush_arg: flush() argument
> + * @refs: references to this structure taken by producers
> + */
> +struct iopf_queue {
> +	struct workqueue_struct		*wq;
> +	iopf_queue_flush_t		flush;
> +	void				*flush_arg;
> +	refcount_t			refs;
> +};
> +
> +/**
> + * struct iopf_device_param - IO Page Fault data attached to a device
> + * @queue: IOPF queue
> + * @partial: faults that are part of a Page Request Group for which the last
> + *           request hasn't been submitted yet.
> + */
> +struct iopf_device_param {
> +	struct iopf_queue		*queue;
> +	struct list_head		partial;
> +};
> +
> +struct iopf_context {
> +	struct device			*dev;
> +	struct iommu_fault_event	evt;
> +	struct list_head		head;
> +};
> +
> +struct iopf_group {
> +	struct iopf_context		last_fault;
> +	struct list_head		faults;
> +	struct work_struct		work;
> +};
> +
> +static int iopf_complete(struct device *dev, struct iommu_fault_event *evt,
> +			 enum page_response_code status)
> +{
> +	struct page_response_msg resp = {
> +		.addr			= evt->addr,
> +		.pasid			= evt->pasid,
> +		.pasid_present		= evt->pasid_valid,
> +		.page_req_group_id	= evt->page_req_group_id,
> +		.private_data		= evt->iommu_private,
> +		.resp_code		= status,
> +	};
> +
> +	return iommu_page_response(dev, &resp);
> +}
> +
> +static enum page_response_code
> +iopf_handle_single(struct iopf_context *fault)
> +{
> +	/* TODO */
> +	return -ENODEV;
> +}
> +
> +static void iopf_handle_group(struct work_struct *work)
> +{
> +	struct iopf_group *group;
> +	struct iopf_context *fault, *next;
> +	enum page_response_code status = IOMMU_PAGE_RESP_SUCCESS;
> +
> +	group = container_of(work, struct iopf_group, work);
> +
> +	list_for_each_entry_safe(fault, next, &group->faults, head) {
> +		struct iommu_fault_event *evt = &fault->evt;
> +		/*
> +		 * Errors are sticky: don't handle subsequent faults in the
> +		 * group if there is an error.
> +		 */
> +		if (status == IOMMU_PAGE_RESP_SUCCESS)
> +			status = iopf_handle_single(fault);
> +
> +		if (!evt->last_req)
> +			kfree(fault);
> +	}
> +
> +	iopf_complete(group->last_fault.dev, &group->last_fault.evt, status);
> +	kfree(group);
> +}
> +
> +/**
> + * iommu_queue_iopf - IO Page Fault handler
> + * @evt: fault event
> + * @cookie: struct device, passed to iommu_register_device_fault_handler.
> + *
> + * Add a fault to the device workqueue, to be handled by mm.
> + */
> +int iommu_queue_iopf(struct iommu_fault_event *evt, void *cookie)
> +{
> +	struct iopf_group *group;
> +	struct iopf_context *fault, *next;
> +	struct iopf_device_param *iopf_param;
> +
> +	struct device *dev = cookie;
> +	struct iommu_param *param = dev->iommu_param;
> +
> +	if (WARN_ON(!mutex_is_locked(&param->lock)))
> +		return -EINVAL;
> +
> +	if (evt->type != IOMMU_FAULT_PAGE_REQ)
> +		/* Not a recoverable page fault */
> +		return IOMMU_PAGE_RESP_CONTINUE;
> +
> +	/*
> +	 * As long as we're holding param->lock, the queue can't be unlinked
> +	 * from the device and therefore cannot disappear.
> +	 */
> +	iopf_param = param->iopf_param;
> +	if (!iopf_param)
> +		return -ENODEV;
> +
> +	if (!evt->last_req) {
> +		fault = kzalloc(sizeof(*fault), GFP_KERNEL);
> +		if (!fault)
> +			return -ENOMEM;
> +
> +		fault->evt = *evt;
> +		fault->dev = dev;
> +
> +		/* Non-last request of a group. Postpone until the last one */
> +		list_add(&fault->head, &iopf_param->partial);
> +
> +		return IOMMU_PAGE_RESP_HANDLED;
> +	}
> +
> +	group = kzalloc(sizeof(*group), GFP_KERNEL);
> +	if (!group)
> +		return -ENOMEM;
> +
> +	group->last_fault.evt = *evt;
> +	group->last_fault.dev = dev;
> +	INIT_LIST_HEAD(&group->faults);
> +	list_add(&group->last_fault.head, &group->faults);
> +	INIT_WORK(&group->work, iopf_handle_group);
> +
> +	/* See if we have partial faults for this group */
> +	list_for_each_entry_safe(fault, next, &iopf_param->partial, head) {
> +		if (fault->evt.page_req_group_id == evt->page_req_group_id)
> +			/* Insert *before* the last fault */
> +			list_move(&fault->head, &group->faults);
> +	}
> +
> +	queue_work(iopf_param->queue->wq, &group->work);
> +
> +	/* Postpone the fault completion */
> +	return IOMMU_PAGE_RESP_HANDLED;
> +}
> +EXPORT_SYMBOL_GPL(iommu_queue_iopf);
> +
> +/**
> + * iopf_queue_flush_dev - Ensure that all queued faults have been processed
> + * @dev: the endpoint whose faults need to be flushed.
> + *
> + * Users must call this function when releasing a PASID, to ensure that all
> + * pending faults for this PASID have been handled, and won't hit the address
> + * space of the next process that uses this PASID.
> + *
> + * Return 0 on success.
> + */
> +int iopf_queue_flush_dev(struct device *dev)
> +{
> +	int ret = 0;
> +	struct iopf_queue *queue;
> +	struct iommu_param *param = dev->iommu_param;
> +
> +	if (!param)
> +		return -ENODEV;
> +
> +	/*
> +	 * It is incredibly easy to find ourselves in a deadlock situation if
> +	 * we're not careful, because we're taking the opposite path as
> +	 * iommu_queue_iopf:
> +	 *
> +	 *   iopf_queue_flush_dev()   |  PRI queue handler
> +	 *    lock(mutex)             |   iommu_queue_iopf()
> +	 *     queue->flush()         |    lock(mutex)
> +	 *      wait PRI queue empty  |
> +	 *
> +	 * So we can't hold the device param lock while flushing. We don't have
> +	 * to, because the queue or the device won't disappear until all flush
> +	 * are finished.
> +	 */
> +	mutex_lock(&param->lock);
> +	if (param->iopf_param) {
> +		queue = param->iopf_param->queue;
> +	} else {
> +		ret = -ENODEV;
> +	}
> +	mutex_unlock(&param->lock);
> +	if (ret)
> +		return ret;
> +
> +	queue->flush(queue->flush_arg, dev);
> +
> +	/*
> +	 * No need to clear the partial list. All PRGs containing the PASID that
> +	 * needs to be decommissioned are whole (the device driver made sure of
> +	 * it before this function was called). They have been submitted to the
> +	 * queue by the above flush().
> +	 */
> +	flush_workqueue(queue->wq);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iopf_queue_flush_dev);
> +
> +/**
> + * iopf_queue_add_device - Add producer to the fault queue
> + * @queue: IOPF queue
> + * @dev: device to add
> + */
> +int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev)
> +{
> +	int ret = -EINVAL;
> +	struct iopf_device_param *iopf_param;
> +	struct iommu_param *param = dev->iommu_param;
> +
> +	if (!param)
> +		return -ENODEV;
> +
> +	iopf_param = kzalloc(sizeof(*iopf_param), GFP_KERNEL);
> +	if (!iopf_param)
> +		return -ENOMEM;
> +
> +	INIT_LIST_HEAD(&iopf_param->partial);
> +	iopf_param->queue = queue;
> +
> +	mutex_lock(&param->lock);
> +	if (!param->iopf_param) {
> +		refcount_inc(&queue->refs);
> +		param->iopf_param = iopf_param;
> +		ret = 0;
> +	}
> +	mutex_unlock(&param->lock);
> +
> +	if (ret)
> +		kfree(iopf_param);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iopf_queue_add_device);
> +
> +/**
> + * iopf_queue_remove_device - Remove producer from fault queue
> + * @dev: device to remove
> + *
> + * Caller makes sure that no more fault is reported for this device, and no more
> + * flush is scheduled for this device.
> + *
> + * Note: safe to call unconditionally on a cleanup path, even if the device
> + * isn't registered to any IOPF queue.
> + *
> + * Return 0 if the device was attached to the IOPF queue
> + */
> +int iopf_queue_remove_device(struct device *dev)
> +{
> +	struct iopf_context *fault, *next;
> +	struct iopf_device_param *iopf_param;
> +	struct iommu_param *param = dev->iommu_param;
> +
> +	if (!param)
> +		return -EINVAL;
> +
> +	mutex_lock(&param->lock);
> +	iopf_param = param->iopf_param;
> +	if (iopf_param) {
> +		refcount_dec(&iopf_param->queue->refs);
> +		param->iopf_param = NULL;
> +	}
> +	mutex_unlock(&param->lock);
> +	if (!iopf_param)
> +		return -EINVAL;
> +
> +	list_for_each_entry_safe(fault, next, &iopf_param->partial, head)
> +		kfree(fault);
> +

Why would we end up here with partials still in the list?  Buggy hardware?

> +	/*
> +	 * No more flush is scheduled, and the caller removed all bonds from
> +	 * this device. unbind() waited until any concurrent mm_exit() finished,
> +	 * therefore there is no flush() running anymore and we can free the
> +	 * param.
> +	 */
> +	kfree(iopf_param);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iopf_queue_remove_device);
> +
> +/**
> + * iopf_queue_alloc - Allocate and initialize a fault queue
> + * @name: a unique string identifying the queue (for workqueue)
> + * @flush: a callback that flushes the low-level queue
> + * @cookie: driver-private data passed to the flush callback
> + *
> + * The callback is called before the workqueue is flushed. The IOMMU driver must
> + * commit all faults that are pending in its low-level queues at the time of the
> + * call, into the IOPF queue (with iommu_report_device_fault). The callback
> + * takes a device pointer as argument, hinting what endpoint is causing the
> + * flush. When the device is NULL, all faults should be committed.
> + */
> +struct iopf_queue *
> +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void *cookie)
> +{
> +	struct iopf_queue *queue;
> +
> +	queue = kzalloc(sizeof(*queue), GFP_KERNEL);
> +	if (!queue)
> +		return NULL;
> +
> +	/*
> +	 * The WQ is unordered because the low-level handler enqueues faults by
> +	 * group. PRI requests within a group have to be ordered, but once
> +	 * that's dealt with, the high-level function can handle groups out of
> +	 * order.
> +	 */
> +	queue->wq = alloc_workqueue("iopf_queue/%s", WQ_UNBOUND, 0, name);
> +	if (!queue->wq) {
> +		kfree(queue);
> +		return NULL;
> +	}
> +
> +	queue->flush = flush;
> +	queue->flush_arg = cookie;
> +	refcount_set(&queue->refs, 1);
> +
> +	return queue;
> +}
> +EXPORT_SYMBOL_GPL(iopf_queue_alloc);
> +
> +/**
> + * iopf_queue_free - Free IOPF queue
> + * @queue: queue to free
> + *
> + * Counterpart to iopf_queue_alloc(). Caller must make sure that all producers
> + * have been removed.
> + */
> +void iopf_queue_free(struct iopf_queue *queue)
> +{
> +

Nitpick : Blank line here doesn't add anything.

> +	/* Caller should have removed all producers first */
> +	if (WARN_ON(!refcount_dec_and_test(&queue->refs)))
> +		return;
> +
> +	destroy_workqueue(queue->wq);
> +	kfree(queue);
> +}
> +EXPORT_SYMBOL_GPL(iopf_queue_free);
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index faf3390ce89d..fad3a60e1c14 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -461,11 +461,20 @@ struct iommu_fault_param {
>  	void *data;
>  };
>  
> +/**
> + * iopf_queue_flush_t - Flush low-level page fault queue
> + *
> + * Report all faults currently pending in the low-level page fault queue
> + */
> +struct iopf_queue;
> +typedef int (*iopf_queue_flush_t)(void *cookie, struct device *dev);
> +
>  /**
>   * struct iommu_param - collection of per-device IOMMU data
>   *
>   * @fault_param: IOMMU detected device fault reporting data
>   * @sva_param: SVA parameters
> + * @iopf_param: I/O Page Fault queue and data
>   *
>   * TODO: migrate other per device data pointers under iommu_dev_data, e.g.
>   *	struct iommu_group	*iommu_group;
> @@ -475,6 +484,7 @@ struct iommu_param {
>  	struct mutex lock;
>  	struct iommu_fault_param *fault_param;
>  	struct iommu_sva_param *sva_param;
> +	struct iopf_device_param *iopf_param;
>  };
>  
>  int  iommu_device_register(struct iommu_device *iommu);
> @@ -874,6 +884,12 @@ static inline int iommu_report_device_fault(struct device *dev, struct iommu_fau
>  	return 0;
>  }
>  
> +static inline int iommu_page_response(struct device *dev,
> +				      struct page_response_msg *msg)
> +{
> +	return -ENODEV;
> +}
> +
>  static inline int iommu_group_id(struct iommu_group *group)
>  {
>  	return -ENODEV;
> @@ -1038,4 +1054,46 @@ static inline struct mm_struct *iommu_sva_find(int pasid)
>  }
>  #endif /* CONFIG_IOMMU_SVA */
>  
> +#ifdef CONFIG_IOMMU_PAGE_FAULT
> +extern int iommu_queue_iopf(struct iommu_fault_event *evt, void *cookie);
> +
> +extern int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev);
> +extern int iopf_queue_remove_device(struct device *dev);
> +extern int iopf_queue_flush_dev(struct device *dev);
> +extern struct iopf_queue *
> +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void *cookie);
> +extern void iopf_queue_free(struct iopf_queue *queue);
> +#else /* CONFIG_IOMMU_PAGE_FAULT */
> +static inline int iommu_queue_iopf(struct iommu_fault_event *evt, void *cookie)
> +{
> +	return -ENODEV;
> +}
> +
> +static inline int iopf_queue_add_device(struct iopf_queue *queue,
> +					struct device *dev)
> +{
> +	return -ENODEV;
> +}
> +
> +static inline int iopf_queue_remove_device(struct device *dev)
> +{
> +	return -ENODEV;
> +}
> +
> +static inline int iopf_queue_flush_dev(struct device *dev)
> +{
> +	return -ENODEV;
> +}
> +
> +static inline struct iopf_queue *
> +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void *cookie)
> +{
> +	return NULL;
> +}
> +
> +static inline void iopf_queue_free(struct iopf_queue *queue)
> +{
> +}
> +#endif /* CONFIG_IOMMU_PAGE_FAULT */
> +
>  #endif /* __LINUX_IOMMU_H */

^ permalink raw reply

* [PATCH v10 25/27] ARM: davinci: add device tree support to timer
From: David Lechner @ 2018-05-17 15:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <8fa98514-7651-58cb-595e-6c7f713540b8@ti.com>

On 05/17/2018 09:35 AM, Sekhar Nori wrote:
> Hi David,
> 
> On Wednesday 09 May 2018 10:56 PM, David Lechner wrote:
>> This adds device tree support to the davinci timer so that when clocks
>> are moved to device tree, the timer will still work.
>>
>> Signed-off-by: David Lechner <david@lechnology.com>
>> ---
> 
>> +static int __init of_davinci_timer_init(struct device_node *np)
>> +{
>> +	struct clk *clk;
>> +
>> +	clk = of_clk_get(np, 0);
>> +	if (IS_ERR(clk)) {
>> +		struct of_phandle_args clkspec;
>> +
>> +		/*
>> +		 * Fall back to using ref_clk if the actual clock is not
>> +		 * available. There will be problems later if the real clock
>> +		 * source is disabled.
>> +		 */
>> +
>> +		pr_warn("%s: falling back to ref_clk\n", __func__);
>> +
>> +		clkspec.np = of_find_node_by_name(NULL, "ref_clk");
>> +		if (IS_ERR(clkspec.np)) {
>> +			pr_err("%s: No clock available for timer!\n", __func__);
>> +			return PTR_ERR(clkspec.np);
>> +		}
>> +		clk = of_clk_get_from_provider(&clkspec);
>> +		of_node_put(clkspec.np);
>> +	}
> 
> Do we need this error path now?
> 
> Thanks,
> Sekhar
> 

No, not really.

^ permalink raw reply

* [PATCH] drivers/perf: arm-ccn: stop spamming dmesg in event_init
From: Matt Sealey @ 2018-05-17 15:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180516192528.5519d2faf59766e0544bb2ee@arm.com>

Hi all,

> > I don't have any dog in this, but maybe if providing information to the
> > users is so essential to having a pleasant user experience, then
> > rethinking the whole way these messages are funneled is necessary
> > because the kernel log + dmesg is by no means appropriate. Take a look
> > at what the networking maintainers recently did with netlink extended
> > ack. You used to just get an "unsupported" error, and now you know
> > exactly what is wrong when extack is available. It seems to me like
> > something like this is what you might want here since you want to have
> > perf be as user friendly as possible.
>
> Thanks, Florian.

Florian, I'd love to know if you mean "implement netlink extended ack in
perf" or "do something idiomatic for perf"?

Let us assume we are semantically challenged over here. I'm going to
proceed from the latter.

> Acme & other perf people, do you foresee a problem adding netlink
> extended ack support to the perf subsystem for extended error message
> communication?
>
> If not, is struct perf_event_attr amenable to having error reporting
> bit(s) added?

I did have a think about this when Kim mentioned it in passing, and my
reasoning was that a serialized error record similar to ACPI APEI BERT/ERST
tables (without the NVRAM abstraction) would fit the need.

As soon as I wrote down my thoughts I realized it scratch more itches than
just something useful for perf.

It would conceptually be a buffer passed from userspace with the syscall,
which would be populated in the kernel with an identifying header, each
record denoting its own length. The syscall fills the buffer with records
of particular format for the syscall, and the errno returned to the
application is then the state of the error recording buffer - for instance,
if the kernel ran out of space in the buffer before reporting all the errors
it could (-ENOBUFS or -EAGAIN), if it stopped recording errors on something
that requires being contained and returned at that point (-EFAULT), and so
on (anyone who's got RAS on the brain will see where I'm coming from).

I have a distinct dislike of filling the kernel with const strings, so the
records would be strictly machine-readable and contain information about
the error for the record and the source. If userspace needs to print a
string then it can look up unique identifiers (UUIDv1, give or take, to
remove the need for any authority on numbering) in a database - be that
plaintext, gettext.po, bdb, sqlite, xml, json, C structure embedded in the
tool - one for each error source. That keeps strings, translations, string
formatting entirely outside the kernel, and keeps records from being freeform
typo-laden strings.

That'd give some generic Producer code in the kernel, and imply a companion
Consumer library in userspace (with said database backend), which could also
be responsible for logging the binary records somewhere for future reference
(perhaps bounded by capabilities or container privileges).

Pretty much every syscall that has problems returning 'just an errno'
could benefit from such a system, the only impediment I can see to it is
that it's adding a new subsystem to the kernel to produce these records,
and any syscall that needs it would have to gain either an extra parameter,
or attribute setting addition (like setsockopts) or to shoehorn the buffer
pointer into an existing parameter structure like perf_event_attr. That
would have to be locked down to a consistent method that would be
recommended (like adding a new syscall interface, not everyone has an
attribute interface or parameter structure) although there's no stopping
kernel devs adding in a way for legacy applications to easily receive
the same information through existing ABI.

In the case of perf_event_attr there is space enough to mark a bit to say
that errors could be reported in a buffer, but not enough in the reserved
space that exists to store a pointer for 64-bit systems (or 128-bit ones..)
without increasing its size. But, besides that, it would have the benefit
of simply being serialized with the syscall, and not a supplemental,
potentially non-thread-safe errno/strerror-like kernel-side implementation,
nor extra syscalls to retrieve information or arbitrary formatted or
unformatted strings.

Netlink extended ack could benefit from it simply by having been passed
an nlattr pointing to the buffer and recording extended error information
in it - the extended ack structure can report the status of the buffer
and use the cookie field to reproduce some information if necessary (which
extends the RAS error record concept further when needed).

Thoughts? Does anyone have any objections to a RAS-like error reporting
system for system calls, or any ideas on things that would benefit from
it above and beyond perf? We could always audit all the system calls and
their behaviors so we could do some worked examples but anyone who's got
a good candidate outside perf is welcome to suggest it.

Ta,
Matt




IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

^ permalink raw reply

* [PATCH] ARM64: dts: sun50i: a64: Add spi flash node for sopine
From: Emmanuel Vadot @ 2018-05-17 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

The Sopine and Pine64-LTS have a winbond w25q128 spi flash on spi0.
Add a node for it.

Signed-off-by: Emmanuel Vadot <manu@freebsd.org>
---
 arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi
index 43418bd881d8..3285aebc06d9 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi
@@ -66,6 +66,18 @@
 	};
 };
 
+&spi0  {
+	status = "okay";
+
+	flash at 0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "w25q128", "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <40000000>;
+	};
+};
+
 #include "axp803.dtsi"
 
 &reg_aldo2 {
-- 
2.17.0

^ permalink raw reply related

* [PATCH] KVM: arm/arm64: add WARN_ON if size is not PAGE_SIZE aligned in unmap_stage2_range
From: Suzuki K Poulose @ 2018-05-17 15:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <fbb269c0-e915-a9f2-da3b-5ae3a2b31396@gmail.com>

On 17/05/18 13:46, Jia He wrote:
> Hi Suzuki
> 
> On 5/17/2018 4:17 PM, Suzuki K Poulose Wrote:
>>
>> Hi Jia,
>>
>> On 17/05/18 07:11, Jia He wrote:
>>> I ever met a panic under memory pressure tests(start 20 guests and run
>>> memhog in the host).
>>
>> Please avoid using "I" in the commit description and preferably stick to
>> an objective description.
> 
> Thanks for the pointing
> 
>>
>>>
>>> The root cause might be what I fixed at [1]. But from arm kvm points of
>>> view, it would be better we caught the exception earlier and clearer.
>>>
>>> If the size is not PAGE_SIZE aligned, unmap_stage2_range might unmap the
>>> wrong(more or less) page range. Hence it caused the "BUG: Bad page
>>> state"
>>
>> I don't see why we should ever panic with a "positive" size value. Anyways,
>> the unmap requests must be in units of pages. So this check might be useful.
>>
>>
> 
> good question,
> 
> After further digging, maybe we need to harden the break condition as below?
> diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
> index 7f6a944..dac9b2e 100644
> --- a/virt/kvm/arm/mmu.c
> +++ b/virt/kvm/arm/mmu.c
> @@ -217,7 +217,7 @@ static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd,
> 
>                          put_page(virt_to_page(pte));
>                  }
> -       } while (pte++, addr += PAGE_SIZE, addr != end);
> +       } while (pte++, addr += PAGE_SIZE, addr < end);

I don't think this change is need as stage2_pgd_addr_end(addr, end) must return
the smaller of the next entry or end. Thus we can't miss "addr" == "end".

Suzuki

^ permalink raw reply

* [PATCH] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Ard Biesheuvel @ 2018-05-17 15:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180517145512.cmr3twodfyz7lzon@redhat.com>

On 17 May 2018 at 16:55, Peter Jones <pjones@redhat.com> wrote:
> On Thu, May 17, 2018 at 09:22:23AM -0400, Sinan Kaya wrote:
>> A host bridge is allowed to remap BAR addresses using _TRA attribute in
>> _CRS windows.
>>
>> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff window] (bus address [0x00100000-0x1fffffff])
>> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
>>
>> When a VGA device is behind such a host bridge and the resource is
>> translated efifb driver is trying to do ioremap against bus address
>> rather than the resource address and is failing to probe.
>>
>> efifb driver is having difficulty locating the base address from BAR
>> address when
>>
>> efifb: probing for efifb
>> efifb: cannot reserve video memory at 0x1e000000
>> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
>> efifb: mode is 800x600x32, linelength=3200, pages=1
>> efifb: scrolling: redraw
>> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
>>
>> Use the host bridge offset information to convert bus address to
>> resource address in the fixup.
>>
>> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>
> Looks reasonable to me - Ard, do you want to take this up through the
> EFI tree?
>

Sure

> Signed-off-by: Peter Jones <pjones@redhat.com>
>

I'll take that as a reviewed-by, thanks.

>> ---
>>  drivers/video/fbdev/efifb.c | 10 ++++++++++
>>  1 file changed, 10 insertions(+)
>>
>> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
>> index 46a4484..ea68d5c 100644
>> --- a/drivers/video/fbdev/efifb.c
>> +++ b/drivers/video/fbdev/efifb.c
>> @@ -428,6 +428,8 @@ static void efifb_fixup_resources(struct pci_dev *dev)
>>  {
>>       u64 base = screen_info.lfb_base;
>>       u64 size = screen_info.lfb_size;
>> +     struct pci_bus_region region;
>> +     struct resource res;
>>       int i;
>>
>>       if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
>> @@ -439,6 +441,14 @@ static void efifb_fixup_resources(struct pci_dev *dev)
>>       if (!base)
>>               return;
>>
>> +     region.start = base;
>> +     region.end = base + size - 1;
>> +     res.start = 0;
>> +     res.flags = IORESOURCE_MEM;
>> +     pcibios_bus_to_resource(dev->bus, &res, &region);
>> +     if (res.start)
>> +             base = res.start;
>> +
>>       for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
>>               struct resource *res = &dev->resource[i];
>>
>> --
>> 2.7.4
>>
>
> --
>   Peter

^ permalink raw reply

* [PATCH] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Peter Jones @ 2018-05-17 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526563343-28721-1-git-send-email-okaya@codeaurora.org>

On Thu, May 17, 2018 at 09:22:23AM -0400, Sinan Kaya wrote:
> A host bridge is allowed to remap BAR addresses using _TRA attribute in
> _CRS windows.
> 
> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff window] (bus address [0x00100000-0x1fffffff])
> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
> 
> When a VGA device is behind such a host bridge and the resource is
> translated efifb driver is trying to do ioremap against bus address
> rather than the resource address and is failing to probe.
> 
> efifb driver is having difficulty locating the base address from BAR
> address when
> 
> efifb: probing for efifb
> efifb: cannot reserve video memory at 0x1e000000
> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
> efifb: mode is 800x600x32, linelength=3200, pages=1
> efifb: scrolling: redraw
> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
> 
> Use the host bridge offset information to convert bus address to
> resource address in the fixup.
> 
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>

Looks reasonable to me - Ard, do you want to take this up through the
EFI tree?

Signed-off-by: Peter Jones <pjones@redhat.com>

> ---
>  drivers/video/fbdev/efifb.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
> index 46a4484..ea68d5c 100644
> --- a/drivers/video/fbdev/efifb.c
> +++ b/drivers/video/fbdev/efifb.c
> @@ -428,6 +428,8 @@ static void efifb_fixup_resources(struct pci_dev *dev)
>  {
>  	u64 base = screen_info.lfb_base;
>  	u64 size = screen_info.lfb_size;
> +	struct pci_bus_region region;
> +	struct resource res;
>  	int i;
>  
>  	if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> @@ -439,6 +441,14 @@ static void efifb_fixup_resources(struct pci_dev *dev)
>  	if (!base)
>  		return;
>  
> +	region.start = base;
> +	region.end = base + size - 1;
> +	res.start = 0;
> +	res.flags = IORESOURCE_MEM;
> +	pcibios_bus_to_resource(dev->bus, &res, &region);
> +	if (res.start)
> +		base = res.start;
> +
>  	for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
>  		struct resource *res = &dev->resource[i];
>  
> -- 
> 2.7.4
> 

-- 
  Peter

^ permalink raw reply

* [PATCH] ethernet: stmmac: dwmac-rk: Add GMAC support for px30
From: David Wu @ 2018-05-17 14:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <f7f67fbe-41e7-4497-cb01-a1fa2195d4b0@rock-chips.com>


Hi Shawn,

Thanks for the suggestion, the most is okay.

? 2018?05?16? 14:34, Shawn Lin ??:
> Hi David,
> 
> On 2018/5/16 11:38, David Wu wrote:
>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c 
>> b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>> index 13133b3..4b2ab71 100644
>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>> @@ -61,6 +61,7 @@ struct rk_priv_data {
>> ????? struct clk *mac_clk_tx;
>> ????? struct clk *clk_mac_ref;
>> ????? struct clk *clk_mac_refout;
>> +??? struct clk *clk_mac_speed;
> 
> No need to do anything now but it seems you could consider doing some
> cleanup by using clk bulk APIs in the future.

The use of this may seem to be less applicable because there are many 
scenarios using different clocks.

> 
>> ????? struct clk *aclk_mac;
>> ????? struct clk *pclk_mac;
>> ????? struct clk *clk_phy;
>> @@ -83,6 +84,64 @@ struct rk_priv_data {
>> ????? (((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : 
>> soc##_GMAC_TXCLK_DLY_DISABLE) | \
>> ?????? ((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : 
>> soc##_GMAC_RXCLK_DLY_DISABLE))
>> +#define PX30_GRF_GMAC_CON1??????? 0X0904
> 
> s/0X0904/0x0904 , since the other constants in this file follow the
> same format.
> 
>> +
>> +/* PX30_GRF_GMAC_CON1 */
>> +#define PX30_GMAC_PHY_INTF_SEL_RMII??? (GRF_CLR_BIT(4) | 
>> GRF_CLR_BIT(5) | \
>> +??????????????????? GRF_BIT(6))
>> +#define PX30_GMAC_SPEED_10M??????? GRF_CLR_BIT(2)
>> +#define PX30_GMAC_SPEED_100M??????? GRF_BIT(2)
>> +
>> +static void px30_set_to_rmii(struct rk_priv_data *bsp_priv)
>> +{
>> +??? struct device *dev = &bsp_priv->pdev->dev;
>> +
>> +??? if (IS_ERR(bsp_priv->grf)) {
>> +??????? dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
>> +??????? return;
>> +??? }
>> +
>> +??? regmap_write(bsp_priv->grf, PX30_GRF_GMAC_CON1,
>> +???????????? PX30_GMAC_PHY_INTF_SEL_RMII);
>> +}
>> +
>> +static void px30_set_rmii_speed(struct rk_priv_data *bsp_priv, int 
>> speed)
>> +{
>> +??? struct device *dev = &bsp_priv->pdev->dev;
>> +??? int ret;
>> +
>> +??? if (IS_ERR(bsp_priv->clk_mac_speed)) {
>> +??????? dev_err(dev, "%s: Missing clk_mac_speed clock\n", __func__);
>> +??????? return;
>> +??? }
>> +
>> +??? if (speed == 10) {
>> +??????? regmap_write(bsp_priv->grf, PX30_GRF_GMAC_CON1,
>> +???????????????? PX30_GMAC_SPEED_10M);
>> +
>> +??????? ret = clk_set_rate(bsp_priv->clk_mac_speed, 2500000);
>> +??????? if (ret)
>> +??????????? dev_err(dev, "%s: set clk_mac_speed rate 2500000 failed: 
>> %d\n",
>> +??????????????? __func__, ret);
>> +??? } else if (speed == 100) {
>> +??????? regmap_write(bsp_priv->grf, PX30_GRF_GMAC_CON1,
>> +???????????????? PX30_GMAC_SPEED_100M);
>> +
>> +??????? ret = clk_set_rate(bsp_priv->clk_mac_speed, 25000000);
>> +??????? if (ret)
>> +??????????? dev_err(dev, "%s: set clk_mac_speed rate 25000000 failed: 
>> %d\n",
>> +??????????????? __func__, ret);
> 
> I know it follows the existing examples, but IMHO it duplicates
> unnecessary code as all the difference is PX30_GMAC_SPEED_*
> 

i think the difference is the register offset and bits.

>> +
>> +??? } else {
>> +??????? dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
>> +??? }
>> +}
>> +
>> +static const struct rk_gmac_ops px30_ops = {
>> +??? .set_to_rmii = px30_set_to_rmii,
>> +??? .set_rmii_speed = px30_set_rmii_speed,
>> +};
>> +
>> ? #define RK3128_GRF_MAC_CON0??? 0x0168
>> ? #define RK3128_GRF_MAC_CON1??? 0x016c
>> @@ -1042,6 +1101,10 @@ static int rk_gmac_clk_init(struct 
>> plat_stmmacenet_data *plat)
>> ????????? }
>> ????? }
>> +??? bsp_priv->clk_mac_speed = devm_clk_get(dev, "clk_mac_speed");
> 
> Mightbe it'd be better to use "mac-speed" in DT bindings.
> 
>> +??? if (IS_ERR(bsp_priv->clk_mac_speed))
>> +??????? dev_err(dev, "cannot get clock %s\n", "clk_mac_speed");
>> +
> 
> Would you like to handle deferred probe >

No,

>> ????? if (bsp_priv->clock_input) {
>> ????????? dev_info(dev, "clock input from PHY\n");
>> ????? } else {
>> @@ -1424,6 +1487,7 @@ static int rk_gmac_resume(struct device *dev)
>> ? static SIMPLE_DEV_PM_OPS(rk_gmac_pm_ops, rk_gmac_suspend, 
>> rk_gmac_resume);
>> ? static const struct of_device_id rk_gmac_dwmac_match[] = {
>> +??? { .compatible = "rockchip,px30-gmac",??? .data = &px30_ops?? },
>> ????? { .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops },
>> ????? { .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops },
>> ????? { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
>>
> 
> 
> 

^ permalink raw reply

* [PATCH v2 4/4] ARM: PWM: add allwinner sun8i pwm support.
From: Hao Zhang @ 2018-05-17 14:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180515111711.l2g4vgsal7yr6dbr@flea>

2018-05-15 19:17 GMT+08:00 Maxime Ripard <maxime.ripard@bootlin.com>:
> Hi,
>
> On Mon, May 14, 2018 at 10:45:44PM +0800, Hao Zhang wrote:
>> 2018-02-26 17:00 GMT+08:00 Maxime Ripard <maxime.ripard@bootlin.com>:
>> > Thanks for respinning this serie. It looks mostly good, but you still
>> > have a quite significant number of checkpatch (--strict) warnings that
>> > you should address.
>>
>> Thanks for reviews :) ,i'm sorry for that, it will be fixed next
>> time.  and, besides, in what situation were the checkpatch warning
>> can be ignore?
>
> The only one that can be reasonably be ignored is the long line
> warning, and only if complying to the limit would make it less easy to
> understand.
>
>> >
>> > On Sun, Feb 25, 2018 at 09:53:08PM +0800, hao_zhang wrote:
>> >> +#define CAPTURE_IRQ_ENABLE_REG       0x0010
>> >> +#define CFIE(ch)     BIT(ch << 1 + 1)
>> >> +#define CRIE(ch)     BIT(ch << 1)
>> >
>> > You should also put your argument between parentheses here (and in all
>> > your other macros).
>>
>> Do you mean like this ?
>> #define CFIE(ch)     BIT((ch) << 1 + 1)
>> #define CRIE(ch)     BIT((ch) << 1)
>
> Yep, exactly. Otherwise, if you do something like CRIE(1 + 1), the
> result will be BIT(1 + 1 << 1), which will expand to 3, instead of 4.
>
> Also, CFIE looks a bit weird here, is it the offset that is
> incremented, or the value? You should probably have parentheses to
> make it explicit.

The vallue,
BIT(((ch) << 1) + 1) It seem not very nice...

uhmm...
In CAPTURE_IRQ_ENABLE_REG odd number is CFIE, even number is CRIE
each channel has one CFIE and CRIE.

we can also describe like this:
#define CFIE(ch)     BIT((ch) * 2 + 1)
#define CRIE(ch)     BIT((ch) * 2)

>
>> >
>> >> +static const u16 div_m_table[] = {
>> >> +     1,
>> >> +     2,
>> >> +     4,
>> >> +     8,
>> >> +     16,
>> >> +     32,
>> >> +     64,
>> >> +     128,
>> >> +     256
>> >> +};
>> >
>> > If this is just a power of two, you can use either the power of two /
>> > ilog2 to switch back and forth, instead of using that table.
>>
>> I think using table is more explicit and extended...
>
> If you didn't have a simple mapping between the register values and
> the divider value, then yeah, sure. But it's not the case here.
>
> Thanks!
> Maxime
>
> --
> Maxime Ripard, Bootlin (formerly Free Electrons)
> Embedded Linux and Kernel engineering
> https://bootlin.com

^ permalink raw reply

* [PATCH] ARM: dts: stm32: enable USB Host (USBH) EHCI controller on stm32mp157c-ev1
From: Amelie Delaunay @ 2018-05-17 14:36 UTC (permalink / raw)
  To: linux-arm-kernel

This patch enables USB Host (USBH) EHCI controller on stm32mp157c-ev1.
As a hub is used between USBH and USB connectors, no need to enable
USBH OHCI controller: all low- and full-speed traffic is managed by the
hub.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/boot/dts/stm32mp157c-ev1.dts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts
index 9382d80..933036b 100644
--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts
@@ -103,6 +103,12 @@
 	};
 };
 
+&usbh_ehci {
+	phys = <&usbphyc_port0>;
+	phy-names = "usb";
+	status = "okay";
+};
+
 &usbphyc {
 	status = "okay";
 };
-- 
2.7.4

^ permalink raw reply related

* [PATCH v10 25/27] ARM: davinci: add device tree support to timer
From: Sekhar Nori @ 2018-05-17 14:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180509172606.29387-26-david@lechnology.com>

Hi David,

On Wednesday 09 May 2018 10:56 PM, David Lechner wrote:
> This adds device tree support to the davinci timer so that when clocks
> are moved to device tree, the timer will still work.
> 
> Signed-off-by: David Lechner <david@lechnology.com>
> ---

> +static int __init of_davinci_timer_init(struct device_node *np)
> +{
> +	struct clk *clk;
> +
> +	clk = of_clk_get(np, 0);
> +	if (IS_ERR(clk)) {
> +		struct of_phandle_args clkspec;
> +
> +		/*
> +		 * Fall back to using ref_clk if the actual clock is not
> +		 * available. There will be problems later if the real clock
> +		 * source is disabled.
> +		 */
> +
> +		pr_warn("%s: falling back to ref_clk\n", __func__);
> +
> +		clkspec.np = of_find_node_by_name(NULL, "ref_clk");
> +		if (IS_ERR(clkspec.np)) {
> +			pr_err("%s: No clock available for timer!\n", __func__);
> +			return PTR_ERR(clkspec.np);
> +		}
> +		clk = of_clk_get_from_provider(&clkspec);
> +		of_node_put(clkspec.np);
> +	}

Do we need this error path now?

Thanks,
Sekhar

^ permalink raw reply

* [PATCH 0/7] i2c: clean up include/linux/i2c-*
From: Wolfram Sang @ 2018-05-17 14:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180419200015.15095-1-wsa@the-dreams.de>

On Thu, Apr 19, 2018 at 10:00:06PM +0200, Wolfram Sang wrote:
> Move all plain platform_data includes to the platform_data-dir
> (except for i2c-pnx which can be moved into the driver itself).
> 
> My preference is to take these patches via the i2c tree. I can provide an
> immutable branch if needed. But we can also discuss those going in via
> arch-trees if dependencies are against us.

All applied to for-next!

The immutable branch is here:

git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/platform_data-immutable

Thanks,

   Wolfram

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180517/41d00c49/attachment.sig>

^ permalink raw reply

* [PATCH v3 2/3] ioremap: Update pgtable free interfaces with addr
From: Kani, Toshi @ 2018-05-17 14:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180517064755.GP12670@dhcp22.suse.cz>

On Thu, 2018-05-17 at 08:47 +0200, Michal Hocko wrote:
> On Wed 16-05-18 17:32:06, Kani Toshimitsu wrote:
> > From: Chintan Pandya <cpandya@codeaurora.org>
> > 
> > This patch ("mm/vmalloc: Add interfaces to free unmapped
> > page table") adds following 2 interfaces to free the page
> > table in case we implement huge mapping.
> > 
> > pud_free_pmd_page() and pmd_free_pte_page()
> > 
> > Some architectures (like arm64) needs to do proper TLB
> > maintanance after updating pagetable entry even in map.
> > Why ? Read this,
> > https://patchwork.kernel.org/patch/10134581/
> 
> Please add that information to the changelog.

I will update the description and resend this patch.

Thanks!
-Toshi

^ permalink raw reply

* [PATCH] arm: bcm2835: Add the PMU to the devicetree.
From: Eric Anholt @ 2018-05-17 14:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.21.1805171007260.7820@macbook-air>

Vince Weaver <vincent.weaver@maine.edu> writes:

> On Thu, 17 May 2018, Eric Anholt wrote:
>
>> diff --git a/arch/arm/boot/dts/bcm2837.dtsi b/arch/arm/boot/dts/bcm2837.dtsi
>> index 7704bb029605..1f5e5c782835 100644
>> --- a/arch/arm/boot/dts/bcm2837.dtsi
>> +++ b/arch/arm/boot/dts/bcm2837.dtsi
>> @@ -17,6 +17,12 @@
>>  		};
>>  	};
>>  
>> +	arm-pmu {
>> +		compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
>> +		interrupt-parent = <&local_intc>;
>> +		interrupts = <9 IRQ_TYPE_LEVEL_HIGH>;
>> +	};
>> +
>
> why this and not
>
> arm-pmu {
> 	compatible = "arm,armv8-pmuv3";
> 	interrupt-parent = <&local_intc>;
> 	interrupts = <9>;
> };
>
> which works, though when I didn't get very far when I submitted the patch 
> to add this last August.

Is that better than a53?  I'm happy to switch to that.  The important
part to me is the a7, since basically everyone with this hw is running
arm32.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180517/78df2543/attachment.sig>

^ permalink raw reply

* [PATCH v2 05/40] iommu/sva: Track mm changes with an MMU notifier
From: Jonathan Cameron @ 2018-05-17 14:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180511190641.23008-6-jean-philippe.brucker@arm.com>

On Fri, 11 May 2018 20:06:06 +0100
Jean-Philippe Brucker <jean-philippe.brucker@arm.com> wrote:

> When creating an io_mm structure, register an MMU notifier that informs
> us when the virtual address space changes and disappears.
> 
> Add a new operation to the IOMMU driver, mm_invalidate, called when a
> range of addresses is unmapped to let the IOMMU driver send ATC
> invalidations. mm_invalidate cannot sleep.
> 
> Adding the notifier complicates io_mm release. In one case device
> drivers free the io_mm explicitly by calling unbind (or detaching the
> device from its domain). In the other case the process could crash
> before unbind, in which case the release notifier has to do all the
> work.
> 
> Allowing the device driver's mm_exit() handler to sleep adds another
> complication, but it will greatly simplify things for users. For example
> VFIO can take the IOMMU mutex and remove any trace of io_mm, instead of
> introducing complex synchronization to delicatly handle this race. But
> relaxing the user side does force unbind() to sleep and wait for all
> pending mm_exit() calls to finish.
> 
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> 
> ---
> v1->v2:
> * Unbind() waits for mm_exit to finish
> * mm_exit can sleep
> ---
>  drivers/iommu/Kconfig     |   1 +
>  drivers/iommu/iommu-sva.c | 248 +++++++++++++++++++++++++++++++++++---
>  include/linux/iommu.h     |  10 ++
>  3 files changed, 244 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index cca8e06903c7..38434899e283 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -77,6 +77,7 @@ config IOMMU_DMA
>  config IOMMU_SVA
>  	bool
>  	select IOMMU_API
> +	select MMU_NOTIFIER
>  
>  config FSL_PAMU
>  	bool "Freescale IOMMU support"
> diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
> index 0700893c679d..e9afae2537a2 100644
> --- a/drivers/iommu/iommu-sva.c
> +++ b/drivers/iommu/iommu-sva.c
> @@ -7,6 +7,7 @@
>  
>  #include <linux/idr.h>
>  #include <linux/iommu.h>
> +#include <linux/mmu_notifier.h>
>  #include <linux/sched/mm.h>
>  #include <linux/slab.h>
>  #include <linux/spinlock.h>
> @@ -106,6 +107,9 @@ struct iommu_bond {
>  	struct list_head	mm_head;
>  	struct list_head	dev_head;
>  	struct list_head	domain_head;
> +	refcount_t		refs;
> +	struct wait_queue_head	mm_exit_wq;
> +	bool			mm_exit_active;
>  
>  	void			*drvdata;
>  };
> @@ -124,6 +128,8 @@ static DEFINE_IDR(iommu_pasid_idr);
>   */
>  static DEFINE_SPINLOCK(iommu_sva_lock);
>  
> +static struct mmu_notifier_ops iommu_mmu_notifier;
> +
>  static struct io_mm *
>  io_mm_alloc(struct iommu_domain *domain, struct device *dev,
>  	    struct mm_struct *mm, unsigned long flags)
> @@ -151,6 +157,7 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev,
>  
>  	io_mm->flags		= flags;
>  	io_mm->mm		= mm;
> +	io_mm->notifier.ops	= &iommu_mmu_notifier;
>  	io_mm->release		= domain->ops->mm_free;
>  	INIT_LIST_HEAD(&io_mm->devices);
>  
> @@ -167,8 +174,29 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev,
>  		goto err_free_mm;
>  	}
>  
> -	/* TODO: keep track of mm. For the moment, abort. */
> -	ret = -ENOSYS;
> +	ret = mmu_notifier_register(&io_mm->notifier, mm);
> +	if (ret)
> +		goto err_free_pasid;
> +
> +	/*
> +	 * Now that the MMU notifier is valid, we can allow users to grab this
> +	 * io_mm by setting a valid refcount. Before that it was accessible in
> +	 * the IDR but invalid.
> +	 *
> +	 * The following barrier ensures that users, who obtain the io_mm with
> +	 * kref_get_unless_zero, don't read uninitialized fields in the
> +	 * structure.
> +	 */
> +	smp_wmb();
> +	kref_init(&io_mm->kref);
> +
> +	return io_mm;
> +
> +err_free_pasid:
> +	/*
> +	 * Even if the io_mm is accessible from the IDR at this point, kref is
> +	 * 0 so no user could get a reference to it. Free it manually.
> +	 */
>  	spin_lock(&iommu_sva_lock);
>  	idr_remove(&iommu_pasid_idr, io_mm->pasid);
>  	spin_unlock(&iommu_sva_lock);
> @@ -180,9 +208,13 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev,
>  	return ERR_PTR(ret);
>  }
>  
> -static void io_mm_free(struct io_mm *io_mm)
> +static void io_mm_free(struct rcu_head *rcu)
>  {
> -	struct mm_struct *mm = io_mm->mm;
> +	struct io_mm *io_mm;
> +	struct mm_struct *mm;
> +
> +	io_mm = container_of(rcu, struct io_mm, rcu);
> +	mm = io_mm->mm;
>  
>  	io_mm->release(io_mm);
>  	mmdrop(mm);
> @@ -197,7 +229,22 @@ static void io_mm_release(struct kref *kref)
>  
>  	idr_remove(&iommu_pasid_idr, io_mm->pasid);
>  
> -	io_mm_free(io_mm);
> +	/*
> +	 * If we're being released from mm exit, the notifier callback ->release
> +	 * has already been called. Otherwise we don't need ->release, the io_mm
> +	 * isn't attached to anything anymore. Hence no_release.
> +	 */
> +	mmu_notifier_unregister_no_release(&io_mm->notifier, io_mm->mm);
> +
> +	/*
> +	 * We can't free the structure here, because if mm exits during
> +	 * unbind(), then ->release might be attempting to grab the io_mm
> +	 * concurrently. And in the other case, if ->release is calling
> +	 * io_mm_release, then __mmu_notifier_release expects to still have a
> +	 * valid mn when returning. So free the structure when it's safe, after
> +	 * the RCU grace period elapsed.
> +	 */
> +	mmu_notifier_call_srcu(&io_mm->rcu, io_mm_free);
>  }
>  
>  /*
> @@ -206,8 +253,14 @@ static void io_mm_release(struct kref *kref)
>   */
>  static int io_mm_get_locked(struct io_mm *io_mm)
>  {
> -	if (io_mm)
> -		return kref_get_unless_zero(&io_mm->kref);
> +	if (io_mm && kref_get_unless_zero(&io_mm->kref)) {
> +		/*
> +		 * kref_get_unless_zero doesn't provide ordering for reads. This
> +		 * barrier pairs with the one in io_mm_alloc.
> +		 */
> +		smp_rmb();
> +		return 1;
> +	}
>  
>  	return 0;
>  }
> @@ -233,7 +286,8 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev,
>  	struct iommu_bond *bond, *tmp;
>  	struct iommu_sva_param *param = dev->iommu_param->sva_param;
>  
> -	if (!domain->ops->mm_attach || !domain->ops->mm_detach)
> +	if (!domain->ops->mm_attach || !domain->ops->mm_detach ||
> +	    !domain->ops->mm_invalidate)
>  		return -ENODEV;
>  
>  	if (pasid > param->max_pasid || pasid < param->min_pasid)
> @@ -247,6 +301,8 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev,
>  	bond->io_mm		= io_mm;
>  	bond->dev		= dev;
>  	bond->drvdata		= drvdata;
> +	refcount_set(&bond->refs, 1);
> +	init_waitqueue_head(&bond->mm_exit_wq);
>  
>  	spin_lock(&iommu_sva_lock);
>  	/*
> @@ -275,12 +331,37 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev,
>  	return 0;
>  }
>  
> -static void io_mm_detach_locked(struct iommu_bond *bond)
> +static void io_mm_detach_locked(struct iommu_bond *bond, bool wait)
>  {
>  	struct iommu_bond *tmp;
>  	bool detach_domain = true;
>  	struct iommu_domain *domain = bond->domain;
>  
> +	if (wait) {
> +		bool do_detach = true;
> +		/*
> +		 * If we're unbind() then we're deleting the bond no matter
> +		 * what. Tell the mm_exit thread that we're cleaning up, and
> +		 * wait until it finishes using the bond.
> +		 *
> +		 * refs is guaranteed to be one or more, otherwise it would
> +		 * already have been removed from the list. Check is someone is

Check if someone...

> +		 * already waiting, in which case we wait but do not free.
> +		 */
> +		if (refcount_read(&bond->refs) > 1)
> +			do_detach = false;
> +
> +		refcount_inc(&bond->refs);
> +		wait_event_lock_irq(bond->mm_exit_wq, !bond->mm_exit_active,
> +				    iommu_sva_lock);
> +		if (!do_detach)
> +			return;
> +
> +	} else if (!refcount_dec_and_test(&bond->refs)) {
> +		/* unbind() is waiting to free the bond */
> +		return;
> +	}
> +
>  	list_for_each_entry(tmp, &domain->mm_list, domain_head) {
>  		if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) {
>  			detach_domain = false;
> @@ -298,6 +379,129 @@ static void io_mm_detach_locked(struct iommu_bond *bond)
>  	kfree(bond);
>  }
>  
> +static int iommu_signal_mm_exit(struct iommu_bond *bond)
> +{
> +	struct device *dev = bond->dev;
> +	struct io_mm *io_mm = bond->io_mm;
> +	struct iommu_sva_param *param = dev->iommu_param->sva_param;
> +
> +	/*
> +	 * We can't hold the device's param_lock. If we did and the device
> +	 * driver used a global lock around io_mm, we would risk getting the
> +	 * following deadlock:
> +	 *
> +	 *   exit_mm()                 |  Shutdown SVA
> +	 *    mutex_lock(param->lock)  |   mutex_lock(glob lock)
> +	 *     param->mm_exit()        |    sva_device_shutdown()
> +	 *      mutex_lock(glob lock)  |     mutex_lock(param->lock)
> +	 *
> +	 * Fortunately unbind() waits for us to finish, and sva_device_shutdown
> +	 * requires that any bond is removed, so we can safely access mm_exit
> +	 * and drvdata without taking any lock.
> +	 */
> +	if (!param || !param->mm_exit)
> +		return 0;
> +
> +	return param->mm_exit(dev, io_mm->pasid, bond->drvdata);
> +}
> +
> +/* Called when the mm exits. Can race with unbind(). */
> +static void iommu_notifier_release(struct mmu_notifier *mn, struct mm_struct *mm)
> +{
> +	struct iommu_bond *bond, *next;
> +	struct io_mm *io_mm = container_of(mn, struct io_mm, notifier);
> +
> +	/*
> +	 * If the mm is exiting then devices are still bound to the io_mm.
> +	 * A few things need to be done before it is safe to release:
> +	 *
> +	 * - As the mmu notifier doesn't hold any reference to the io_mm when
> +	 *   calling ->release(), try to take a reference.
> +	 * - Tell the device driver to stop using this PASID.
> +	 * - Clear the PASID table and invalidate TLBs.
> +	 * - Drop all references to this io_mm by freeing the bonds.
> +	 */
> +	spin_lock(&iommu_sva_lock);
> +	if (!io_mm_get_locked(io_mm)) {
> +		/* Someone's already taking care of it. */
> +		spin_unlock(&iommu_sva_lock);
> +		return;
> +	}
> +
> +	list_for_each_entry_safe(bond, next, &io_mm->devices, mm_head) {
> +		/*
> +		 * Release the lock to let the handler sleep. We need to be
> +		 * careful about concurrent modifications to the list and to the
> +		 * bond. Tell unbind() not to free the bond until we're done.
> +		 */
> +		bond->mm_exit_active = true;
> +		spin_unlock(&iommu_sva_lock);
> +
> +		if (iommu_signal_mm_exit(bond))
> +			dev_WARN(bond->dev, "possible leak of PASID %u",
> +				 io_mm->pasid);
> +
> +		spin_lock(&iommu_sva_lock);
> +		next = list_next_entry(bond, mm_head);
> +
> +		/* If someone is waiting, let them delete the bond now */
> +		bond->mm_exit_active = false;
> +		wake_up_all(&bond->mm_exit_wq);
> +
> +		/* Otherwise, do it ourselves */
> +		io_mm_detach_locked(bond, false);
> +	}
> +	spin_unlock(&iommu_sva_lock);
> +
> +	/*
> +	 * We're now reasonably certain that no more fault is being handled for
> +	 * this io_mm, since we just flushed them all out of the fault queue.
> +	 * Release the last reference to free the io_mm.
> +	 */
> +	io_mm_put(io_mm);
> +}
> +
> +static void iommu_notifier_invalidate_range(struct mmu_notifier *mn,
> +					    struct mm_struct *mm,
> +					    unsigned long start,
> +					    unsigned long end)
> +{
> +	struct iommu_bond *bond;
> +	struct io_mm *io_mm = container_of(mn, struct io_mm, notifier);
> +
> +	spin_lock(&iommu_sva_lock);
> +	list_for_each_entry(bond, &io_mm->devices, mm_head) {
> +		struct iommu_domain *domain = bond->domain;
> +
> +		domain->ops->mm_invalidate(domain, bond->dev, io_mm, start,
> +					   end - start);
> +	}
> +	spin_unlock(&iommu_sva_lock);
> +}
> +
> +static int iommu_notifier_clear_flush_young(struct mmu_notifier *mn,
> +					    struct mm_struct *mm,
> +					    unsigned long start,
> +					    unsigned long end)
> +{
> +	iommu_notifier_invalidate_range(mn, mm, start, end);
> +	return 0;
> +}
> +
> +static void iommu_notifier_change_pte(struct mmu_notifier *mn,
> +				      struct mm_struct *mm,
> +				      unsigned long address, pte_t pte)
> +{
> +	iommu_notifier_invalidate_range(mn, mm, address, address + PAGE_SIZE);
> +}
> +
> +static struct mmu_notifier_ops iommu_mmu_notifier = {
> +	.release		= iommu_notifier_release,
> +	.clear_flush_young	= iommu_notifier_clear_flush_young,
> +	.change_pte		= iommu_notifier_change_pte,
> +	.invalidate_range	= iommu_notifier_invalidate_range,
> +};
> +
>  /**
>   * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device
>   * @dev: the device
> @@ -320,6 +524,12 @@ static void io_mm_detach_locked(struct iommu_bond *bond)
>   * The handler gets an opaque pointer corresponding to the drvdata passed as
>   * argument of bind().
>   *
> + * The @mm_exit handler is allowed to sleep. Be careful about the locks taken in
> + * @mm_exit, because they might lead to deadlocks if they are also held when
> + * dropping references to the mm. Consider the following call chain:
> + *   mutex_lock(A); mmput(mm) -> exit_mm() -> @mm_exit() -> mutex_lock(A)
> + * Using mmput_async() prevents this scenario.
> + *
>   * The device should not be performing any DMA while this function is running,
>   * otherwise the behavior is undefined.
>   *
> @@ -484,15 +694,16 @@ int __iommu_sva_unbind_device(struct device *dev, int pasid)
>  	if (!param || WARN_ON(!domain))
>  		return -EINVAL;
>  
> -	spin_lock(&iommu_sva_lock);
> +	/* spin_lock_irq matches the one in wait_event_lock_irq */
> +	spin_lock_irq(&iommu_sva_lock);
>  	list_for_each_entry(bond, &param->mm_list, dev_head) {
>  		if (bond->io_mm->pasid == pasid) {
> -			io_mm_detach_locked(bond);
> +			io_mm_detach_locked(bond, true);
>  			ret = 0;
>  			break;
>  		}
>  	}
> -	spin_unlock(&iommu_sva_lock);
> +	spin_unlock_irq(&iommu_sva_lock);
>  
>  	return ret;
>  }
> @@ -503,18 +714,25 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);
>   * @dev: the device
>   *
>   * When detaching @device from a domain, IOMMU drivers should use this helper.
> + * This function may sleep while waiting for bonds to be released.
>   */
>  void __iommu_sva_unbind_dev_all(struct device *dev)
>  {
>  	struct iommu_sva_param *param;
>  	struct iommu_bond *bond, *next;
>  
> +	/*
> +	 * io_mm_detach_locked might wait, so we shouldn't call it with the dev
> +	 * param lock held. It's fine to read sva_param outside the lock because
> +	 * it can only be freed by iommu_sva_device_shutdown when there are no
> +	 * more bonds in the list.
> +	 */
>  	param = dev->iommu_param->sva_param;
>  	if (param) {
> -		spin_lock(&iommu_sva_lock);
> +		spin_lock_irq(&iommu_sva_lock);
>  		list_for_each_entry_safe(bond, next, &param->mm_list, dev_head)
> -			io_mm_detach_locked(bond);
> -		spin_unlock(&iommu_sva_lock);
> +			io_mm_detach_locked(bond, true);
> +		spin_unlock_irq(&iommu_sva_lock);
>  	}
>  }
>  EXPORT_SYMBOL_GPL(__iommu_sva_unbind_dev_all);
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 439c8fffd836..caa6f79785b9 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -24,6 +24,7 @@
>  #include <linux/types.h>
>  #include <linux/errno.h>
>  #include <linux/err.h>
> +#include <linux/mmu_notifier.h>
>  #include <linux/of.h>
>  #include <uapi/linux/iommu.h>
>  
> @@ -111,10 +112,15 @@ struct io_mm {
>  	unsigned long		flags;
>  	struct list_head	devices;
>  	struct kref		kref;
> +#if defined(CONFIG_MMU_NOTIFIER)
> +	struct mmu_notifier	notifier;
> +#endif
>  	struct mm_struct	*mm;
>  
>  	/* Release callback for this mm */
>  	void (*release)(struct io_mm *io_mm);
> +	/* For postponed release */
> +	struct rcu_head		rcu;
>  };
>  
>  enum iommu_cap {
> @@ -249,6 +255,7 @@ struct iommu_sva_param {
>   * @mm_attach: attach io_mm to a device. Install PASID entry if necessary
>   * @mm_detach: detach io_mm from a device. Remove PASID entry and
>   *             flush associated TLB entries.
> + * @mm_invalidate: Invalidate a range of mappings for an mm
>   * @map: map a physically contiguous memory region to an iommu domain
>   * @unmap: unmap a physically contiguous memory region from an iommu domain
>   * @map_sg: map a scatter-gather list of physically contiguous memory chunks
> @@ -298,6 +305,9 @@ struct iommu_ops {
>  			 struct io_mm *io_mm, bool attach_domain);
>  	void (*mm_detach)(struct iommu_domain *domain, struct device *dev,
>  			  struct io_mm *io_mm, bool detach_domain);
> +	void (*mm_invalidate)(struct iommu_domain *domain, struct device *dev,
> +			      struct io_mm *io_mm, unsigned long vaddr,
> +			      size_t size);
>  	int (*map)(struct iommu_domain *domain, unsigned long iova,
>  		   phys_addr_t paddr, size_t size, int prot);
>  	size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,

^ permalink raw reply

* [PATCH v2 03/40] iommu/sva: Manage process address spaces
From: Jonathan Cameron @ 2018-05-17 14:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180511190641.23008-4-jean-philippe.brucker@arm.com>

On Fri, 11 May 2018 20:06:04 +0100
Jean-Philippe Brucker <jean-philippe.brucker@arm.com> wrote:

> Allocate IOMMU mm structures and binding them to devices. Four operations
> are added to IOMMU drivers:
> 
> * mm_alloc(): to create an io_mm structure and perform architecture-
>   specific operations required to grab the process (for instance on ARM,
>   pin down the CPU ASID so that the process doesn't get assigned a new
>   ASID on rollover).
> 
>   There is a single valid io_mm structure per Linux mm. Future extensions
>   may also use io_mm for kernel-managed address spaces, populated with
>   map()/unmap() calls instead of bound to process address spaces. This
>   patch focuses on "shared" io_mm.
> 
> * mm_attach(): attach an mm to a device. The IOMMU driver checks that the
>   device is capable of sharing an address space, and writes the PASID
>   table entry to install the pgd.
> 
>   Some IOMMU drivers will have a single PASID table per domain, for
>   convenience. Other can implement it differently but to help these
>   drivers, mm_attach and mm_detach take 'attach_domain' and
>   'detach_domain' parameters, that tell whether they need to set and clear
>   the PASID entry or only send the required TLB invalidations.
> 
> * mm_detach(): detach an mm from a device. The IOMMU driver removes the
>   PASID table entry and invalidates the IOTLBs.
> 
> * mm_free(): free a structure allocated by mm_alloc(), and let arch
>   release the process.
> 
> mm_attach and mm_detach operations are serialized with a spinlock. When
> trying to optimize this code, we should at least prevent concurrent
> attach()/detach() on the same domain (so multi-level PASID table code can
> allocate tables lazily). mm_alloc() can sleep, but mm_free must not
> (because we'll have to call it from call_srcu later on).
> 
> At the moment we use an IDR for allocating PASIDs and retrieving contexts.
> We also use a single spinlock. These can be refined and optimized later (a
> custom allocator will be needed for top-down PASID allocation).
> 
> Keeping track of address spaces requires the use of MMU notifiers.
> Handling process exit with regard to unbind() is tricky, so it is left for
> another patch and we explicitly fail mm_alloc() for the moment.
> 
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>

A few minor bits and bobs inline.  Looks good in general + nice diags!

Thanks,

Jonathan

> 
> ---
> v1->v2: sanity-check of flags
> ---
>  drivers/iommu/iommu-sva.c | 380 +++++++++++++++++++++++++++++++++++++-
>  drivers/iommu/iommu.c     |   1 +
>  include/linux/iommu.h     |  28 +++
>  3 files changed, 406 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
> index 8d98f9c09864..6ac679c48f3c 100644
> --- a/drivers/iommu/iommu-sva.c
> +++ b/drivers/iommu/iommu-sva.c
> @@ -5,8 +5,298 @@
>   * Copyright (C) 2018 ARM Ltd.
>   */
>  
> +#include <linux/idr.h>
>  #include <linux/iommu.h>
> +#include <linux/sched/mm.h>
>  #include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +/**
> + * DOC: io_mm model
> + *
> + * The io_mm keeps track of process address spaces shared between CPU and IOMMU.
> + * The following example illustrates the relation between structures
> + * iommu_domain, io_mm and iommu_bond. An iommu_bond is a link between io_mm and
> + * device. A device can have multiple io_mm and an io_mm may be bound to
> + * multiple devices.
> + *              ___________________________
> + *             |  IOMMU domain A           |
> + *             |  ________________         |
> + *             | |  IOMMU group   |        +------- io_pgtables
> + *             | |                |        |
> + *             | |   dev 00:00.0 ----+------- bond --- io_mm X
> + *             | |________________|   \    |
> + *             |                       '----- bond ---.
> + *             |___________________________|           \
> + *              ___________________________             \
> + *             |  IOMMU domain B           |           io_mm Y
> + *             |  ________________         |           / /
> + *             | |  IOMMU group   |        |          / /
> + *             | |                |        |         / /
> + *             | |   dev 00:01.0 ------------ bond -' /
> + *             | |   dev 00:01.1 ------------ bond --'
> + *             | |________________|        |
> + *             |                           +------- io_pgtables
> + *             |___________________________|
> + *
> + * In this example, device 00:00.0 is in domain A, devices 00:01.* are in domain
> + * B. All devices within the same domain access the same address spaces. Device
> + * 00:00.0 accesses address spaces X and Y, each corresponding to an mm_struct.
> + * Devices 00:01.* only access address space Y. In addition each
> + * IOMMU_DOMAIN_DMA domain has a private address space, io_pgtable, that is
> + * managed with iommu_map()/iommu_unmap(), and isn't shared with the CPU MMU.
> + *
> + * To obtain the above configuration, users would for instance issue the
> + * following calls:
> + *
> + *     iommu_sva_bind_device(dev 00:00.0, mm X, ...) -> PASID 1
> + *     iommu_sva_bind_device(dev 00:00.0, mm Y, ...) -> PASID 2
> + *     iommu_sva_bind_device(dev 00:01.0, mm Y, ...) -> PASID 2
> + *     iommu_sva_bind_device(dev 00:01.1, mm Y, ...) -> PASID 2
> + *
> + * A single Process Address Space ID (PASID) is allocated for each mm. In the
> + * example, devices use PASID 1 to read/write into address space X and PASID 2
> + * to read/write into address space Y.
> + *
> + * Hardware tables describing this configuration in the IOMMU would typically
> + * look like this:
> + *
> + *                                PASID tables
> + *                                 of domain A
> + *                              .->+--------+
> + *                             / 0 |        |-------> io_pgtable
> + *                            /    +--------+
> + *            Device tables  /   1 |        |-------> pgd X
> + *              +--------+  /      +--------+
> + *      00:00.0 |      A |-'     2 |        |--.
> + *              +--------+         +--------+   \
> + *              :        :       3 |        |    \
> + *              +--------+         +--------+     --> pgd Y
> + *      00:01.0 |      B |--.                    /
> + *              +--------+   \                  |
> + *      00:01.1 |      B |----+   PASID tables  |
> + *              +--------+     \   of domain B  |
> + *                              '->+--------+   |
> + *                               0 |        |-- | --> io_pgtable
> + *                                 +--------+   |
> + *                               1 |        |   |
> + *                                 +--------+   |
> + *                               2 |        |---'
> + *                                 +--------+
> + *                               3 |        |
> + *                                 +--------+
> + *
> + * With this model, a single call binds all devices in a given domain to an
> + * address space. Other devices in the domain will get the same bond implicitly.
> + * However, users must issue one bind() for each device, because IOMMUs may
> + * implement SVA differently. Furthermore, mandating one bind() per device
> + * allows the driver to perform sanity-checks on device capabilities.
> + *
> + * On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold
> + * non-PASID translations. In this case PASID 0 is reserved and entry 0 points
> + * to the io_pgtable base. On Intel IOMMU, the io_pgtable base would be held in
> + * the device table and PASID 0 would be available to the allocator.
> + */
> +
> +struct iommu_bond {
> +	struct io_mm		*io_mm;
> +	struct device		*dev;
> +	struct iommu_domain	*domain;
> +
> +	struct list_head	mm_head;
> +	struct list_head	dev_head;
> +	struct list_head	domain_head;
> +
> +	void			*drvdata;
> +};
> +
> +/*
> + * Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is
> + * used for returning errors). In practice implementations will use at most 20
> + * bits, which is the PCI limit.
> + */
> +static DEFINE_IDR(iommu_pasid_idr);
> +
> +/*
> + * For the moment this is an all-purpose lock. It serializes
> + * access/modifications to bonds, access/modifications to the PASID IDR, and
> + * changes to io_mm refcount as well.
> + */
> +static DEFINE_SPINLOCK(iommu_sva_lock);
> +
> +static struct io_mm *
> +io_mm_alloc(struct iommu_domain *domain, struct device *dev,
> +	    struct mm_struct *mm, unsigned long flags)
> +{
> +	int ret;
> +	int pasid;
> +	struct io_mm *io_mm;
> +	struct iommu_sva_param *param = dev->iommu_param->sva_param;
> +
> +	if (!domain->ops->mm_alloc || !domain->ops->mm_free)
> +		return ERR_PTR(-ENODEV);
> +
> +	io_mm = domain->ops->mm_alloc(domain, mm, flags);
> +	if (IS_ERR(io_mm))
> +		return io_mm;
> +	if (!io_mm)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/*
> +	 * The mm must not be freed until after the driver frees the io_mm
> +	 * (which may involve unpinning the CPU ASID for instance, requiring a
> +	 * valid mm struct.)
> +	 */
> +	mmgrab(mm);
> +
> +	io_mm->flags		= flags;
> +	io_mm->mm		= mm;
> +	io_mm->release		= domain->ops->mm_free;
> +	INIT_LIST_HEAD(&io_mm->devices);
> +
> +	idr_preload(GFP_KERNEL);
> +	spin_lock(&iommu_sva_lock);
> +	pasid = idr_alloc(&iommu_pasid_idr, io_mm, param->min_pasid,
> +			  param->max_pasid + 1, GFP_ATOMIC);

I'd expect the IDR cleanup to be in io_mm_free as that would 'match'
against io_mm_alloc but it's in io_mm_release just before the io_mm_free
call, perhaps move it or am I missing something?

Hmm. This is reworked in patch 5 to use call rcu to do the free.  I suppose
we may be burning an idr entry if we take a while to get round to the
free..  If so a comment to explain this would be great.

> +	io_mm->pasid = pasid;
> +	spin_unlock(&iommu_sva_lock);
> +	idr_preload_end();
> +
> +	if (pasid < 0) {
> +		ret = pasid;
> +		goto err_free_mm;
> +	}
> +
> +	/* TODO: keep track of mm. For the moment, abort. */

>From later patches, I can now see why we didn't init the kref
here, but perhaps a comment would make that clear rather than
people checking it is correctly used throughout?  Actually just grab
the comment from patch 5 and put it in this one and that will
do the job nicely.

> +	ret = -ENOSYS;
> +	spin_lock(&iommu_sva_lock);
> +	idr_remove(&iommu_pasid_idr, io_mm->pasid);
> +	spin_unlock(&iommu_sva_lock);
> +
> +err_free_mm:
> +	domain->ops->mm_free(io_mm);

Really minor, but you now have io_mm->release set so to keep
this obviously the same as the io_mm_free path, perhaps call
that rather than mm_free directly.

> +	mmdrop(mm);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static void io_mm_free(struct io_mm *io_mm)
> +{
> +	struct mm_struct *mm = io_mm->mm;
> +
> +	io_mm->release(io_mm);
> +	mmdrop(mm);
> +}
> +
> +static void io_mm_release(struct kref *kref)
> +{
> +	struct io_mm *io_mm;
> +
> +	io_mm = container_of(kref, struct io_mm, kref);
> +	WARN_ON(!list_empty(&io_mm->devices));
> +
> +	idr_remove(&iommu_pasid_idr, io_mm->pasid);
> +
> +	io_mm_free(io_mm);
> +}
> +
> +/*
> + * Returns non-zero if a reference to the io_mm was successfully taken.
> + * Returns zero if the io_mm is being freed and should not be used.
> + */
> +static int io_mm_get_locked(struct io_mm *io_mm)
> +{
> +	if (io_mm)
> +		return kref_get_unless_zero(&io_mm->kref);
> +
> +	return 0;
> +}
> +
> +static void io_mm_put_locked(struct io_mm *io_mm)
> +{
> +	kref_put(&io_mm->kref, io_mm_release);
> +}
> +
> +static void io_mm_put(struct io_mm *io_mm)
> +{
> +	spin_lock(&iommu_sva_lock);
> +	io_mm_put_locked(io_mm);
> +	spin_unlock(&iommu_sva_lock);
> +}
> +
> +static int io_mm_attach(struct iommu_domain *domain, struct device *dev,
> +			struct io_mm *io_mm, void *drvdata)
> +{
> +	int ret;
> +	bool attach_domain = true;
> +	int pasid = io_mm->pasid;
> +	struct iommu_bond *bond, *tmp;
> +	struct iommu_sva_param *param = dev->iommu_param->sva_param;
> +
> +	if (!domain->ops->mm_attach || !domain->ops->mm_detach)
> +		return -ENODEV;
> +
> +	if (pasid > param->max_pasid || pasid < param->min_pasid)
> +		return -ERANGE;
> +
> +	bond = kzalloc(sizeof(*bond), GFP_KERNEL);
> +	if (!bond)
> +		return -ENOMEM;
> +
> +	bond->domain		= domain;
> +	bond->io_mm		= io_mm;
> +	bond->dev		= dev;
> +	bond->drvdata		= drvdata;
> +
> +	spin_lock(&iommu_sva_lock);
> +	/*
> +	 * Check if this io_mm is already bound to the domain. In which case the
> +	 * IOMMU driver doesn't have to install the PASID table entry.
> +	 */
> +	list_for_each_entry(tmp, &domain->mm_list, domain_head) {
> +		if (tmp->io_mm == io_mm) {
> +			attach_domain = false;
> +			break;
> +		}
> +	}
> +
> +	ret = domain->ops->mm_attach(domain, dev, io_mm, attach_domain);
> +	if (ret) {
> +		kfree(bond);
> +		spin_unlock(&iommu_sva_lock);
> +		return ret;
> +	}
> +
> +	list_add(&bond->mm_head, &io_mm->devices);
> +	list_add(&bond->domain_head, &domain->mm_list);
> +	list_add(&bond->dev_head, &param->mm_list);
> +	spin_unlock(&iommu_sva_lock);
> +
> +	return 0;
> +}
> +
> +static void io_mm_detach_locked(struct iommu_bond *bond)
> +{
> +	struct iommu_bond *tmp;
> +	bool detach_domain = true;
> +	struct iommu_domain *domain = bond->domain;
> +
> +	list_for_each_entry(tmp, &domain->mm_list, domain_head) {
> +		if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) {
> +			detach_domain = false;
> +			break;
> +		}
> +	}
> +
> +	domain->ops->mm_detach(domain, bond->dev, bond->io_mm, detach_domain);
> +

I can't see an immediate reason to have a different order in her to the reverse of
the attach above.   So I think you should be detaching after the list_del calls.
If there is a reason, can we have a comment so I don't ask on v10.

> +	list_del(&bond->mm_head);
> +	list_del(&bond->domain_head);
> +	list_del(&bond->dev_head);
> +	io_mm_put_locked(bond->io_mm);
> +
> +	kfree(bond);
> +}
>  
>  /**
>   * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device
> @@ -47,6 +337,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,
>  
>  	param->features		= features;
>  	param->max_pasid	= max_pasid;
> +	INIT_LIST_HEAD(&param->mm_list);
>  
>  	/*
>  	 * IOMMU driver updates the limits depending on the IOMMU and device
> @@ -114,13 +405,87 @@ EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown);
>  int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm,
>  			    int *pasid, unsigned long flags, void *drvdata)
>  {
> -	return -ENOSYS; /* TODO */
> +	int i, ret = 0;
> +	struct io_mm *io_mm = NULL;
> +	struct iommu_domain *domain;
> +	struct iommu_bond *bond = NULL, *tmp;
> +	struct iommu_sva_param *param = dev->iommu_param->sva_param;
> +
> +	domain = iommu_get_domain_for_dev(dev);
> +	if (!domain)
> +		return -EINVAL;
> +
> +	/*
> +	 * The device driver does not call sva_device_init/shutdown and
> +	 * bind/unbind concurrently, so no need to take the param lock.
> +	 */
> +	if (WARN_ON_ONCE(!param) || (flags & ~param->features))
> +		return -EINVAL;
> +
> +	/* If an io_mm already exists, use it */
> +	spin_lock(&iommu_sva_lock);
> +	idr_for_each_entry(&iommu_pasid_idr, io_mm, i) {
> +		if (io_mm->mm == mm && io_mm_get_locked(io_mm)) {
> +			/* ... Unless it's already bound to this device */
> +			list_for_each_entry(tmp, &io_mm->devices, mm_head) {
> +				if (tmp->dev == dev) {
> +					bond = tmp;

Using bond for this is clear in a sense, but can we not just use ret
so it is obvious here that we are going to return -EEXIST?
At first glance I thought you were going to carry on with this bond
and couldn't work out why it would ever make sense to have two bonds
between a device an an io_mm (which it doesn't!)

> +					io_mm_put_locked(io_mm);
> +					break;
> +				}
> +			}
> +			break;
> +		}
> +	}
> +	spin_unlock(&iommu_sva_lock);
> +
> +	if (bond)
> +		return -EEXIST;
> +
> +	/* Require identical features within an io_mm for now */
> +	if (io_mm && (flags != io_mm->flags)) {
> +		io_mm_put(io_mm);
> +		return -EDOM;
> +	}
> +
> +	if (!io_mm) {
> +		io_mm = io_mm_alloc(domain, dev, mm, flags);
> +		if (IS_ERR(io_mm))
> +			return PTR_ERR(io_mm);
> +	}
> +
> +	ret = io_mm_attach(domain, dev, io_mm, drvdata);
> +	if (ret)
> +		io_mm_put(io_mm);
> +	else
> +		*pasid = io_mm->pasid;
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(__iommu_sva_bind_device);
>  
>  int __iommu_sva_unbind_device(struct device *dev, int pasid)
>  {
> -	return -ENOSYS; /* TODO */
> +	int ret = -ESRCH;
> +	struct iommu_domain *domain;
> +	struct iommu_bond *bond = NULL;
> +	struct iommu_sva_param *param = dev->iommu_param->sva_param;
> +
> +	domain = iommu_get_domain_for_dev(dev);
> +	if (!param || WARN_ON(!domain))
> +		return -EINVAL;
> +
> +	spin_lock(&iommu_sva_lock);
> +	list_for_each_entry(bond, &param->mm_list, dev_head) {
> +		if (bond->io_mm->pasid == pasid) {
> +			io_mm_detach_locked(bond);
> +			ret = 0;
> +			break;
> +		}
> +	}
> +	spin_unlock(&iommu_sva_lock);
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);
>  
> @@ -132,6 +497,15 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);
>   */
>  void __iommu_sva_unbind_dev_all(struct device *dev)
>  {
> -	/* TODO */
> +	struct iommu_sva_param *param;
> +	struct iommu_bond *bond, *next;
> +
> +	param = dev->iommu_param->sva_param;
> +	if (param) {
> +		spin_lock(&iommu_sva_lock);
> +		list_for_each_entry_safe(bond, next, &param->mm_list, dev_head)
> +			io_mm_detach_locked(bond);
> +		spin_unlock(&iommu_sva_lock);
> +	}
>  }
>  EXPORT_SYMBOL_GPL(__iommu_sva_unbind_dev_all);
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index bd2819deae5b..333801e1519c 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1463,6 +1463,7 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
>  	domain->type = type;
>  	/* Assume all sizes by default; the driver may override this later */
>  	domain->pgsize_bitmap  = bus->iommu_ops->pgsize_bitmap;
> +	INIT_LIST_HEAD(&domain->mm_list);
>  
>  	return domain;
>  }
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index da59c20c4f12..d5f21719a5a0 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -100,6 +100,20 @@ struct iommu_domain {
>  	void *handler_token;
>  	struct iommu_domain_geometry geometry;
>  	void *iova_cookie;
> +
> +	struct list_head mm_list;
> +};
> +
> +struct io_mm {
> +	int			pasid;
> +	/* IOMMU_SVA_FEAT_* */
> +	unsigned long		flags;
> +	struct list_head	devices;
> +	struct kref		kref;
> +	struct mm_struct	*mm;
> +
> +	/* Release callback for this mm */
> +	void (*release)(struct io_mm *io_mm);
>  };
>  
>  enum iommu_cap {
> @@ -216,6 +230,7 @@ struct iommu_sva_param {
>  	unsigned long features;
>  	unsigned int min_pasid;
>  	unsigned int max_pasid;
> +	struct list_head mm_list;
>  };
>  
>  /**
> @@ -227,6 +242,11 @@ struct iommu_sva_param {
>   * @detach_dev: detach device from an iommu domain
>   * @sva_device_init: initialize Shared Virtual Adressing for a device
>   * @sva_device_shutdown: shutdown Shared Virtual Adressing for a device
> + * @mm_alloc: allocate io_mm
> + * @mm_free: free io_mm
> + * @mm_attach: attach io_mm to a device. Install PASID entry if necessary
> + * @mm_detach: detach io_mm from a device. Remove PASID entry and
> + *             flush associated TLB entries.
>   * @map: map a physically contiguous memory region to an iommu domain
>   * @unmap: unmap a physically contiguous memory region from an iommu domain
>   * @map_sg: map a scatter-gather list of physically contiguous memory chunks
> @@ -268,6 +288,14 @@ struct iommu_ops {
>  			       struct iommu_sva_param *param);
>  	void (*sva_device_shutdown)(struct device *dev,
>  				    struct iommu_sva_param *param);
> +	struct io_mm *(*mm_alloc)(struct iommu_domain *domain,
> +				  struct mm_struct *mm,
> +				  unsigned long flags);
> +	void (*mm_free)(struct io_mm *io_mm);
> +	int (*mm_attach)(struct iommu_domain *domain, struct device *dev,
> +			 struct io_mm *io_mm, bool attach_domain);
> +	void (*mm_detach)(struct iommu_domain *domain, struct device *dev,
> +			  struct io_mm *io_mm, bool detach_domain);
>  	int (*map)(struct iommu_domain *domain, unsigned long iova,
>  		   phys_addr_t paddr, size_t size, int prot);
>  	size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,

^ permalink raw reply

* [PATCH 3/3] ARM: multi_v7_defconfig: enable STM32 SPI
From: Amelie Delaunay @ 2018-05-17 14:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526566904-24514-1-git-send-email-amelie.delaunay@st.com>

Enable the STM32 SPI driver, implemented on STM32MP1 SoC.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 89167cd..f990335 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -411,6 +411,7 @@ CONFIG_SPI_S3C64XX=m
 CONFIG_SPI_SH_MSIOF=m
 CONFIG_SPI_SH_HSPI=y
 CONFIG_SPI_SIRF=y
+CONFIG_SPI_STM32=y
 CONFIG_SPI_SUN4I=y
 CONFIG_SPI_SUN6I=y
 CONFIG_SPI_TEGRA114=y
-- 
2.7.4

^ permalink raw reply related

* [PATCH 2/3] ARM: dts: stm32: add SPI1 support on stm32mp157c-ev1
From: Amelie Delaunay @ 2018-05-17 14:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526566904-24514-1-git-send-email-amelie.delaunay@st.com>

This patch adds SPI1 support on stm32mp157c-ev1 board.
SPI1 is available on GPIO expansion connector but kept disabled
so these pins can be used as GPIOs by default.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 16 +++++++++++++++-
 arch/arm/boot/dts/stm32mp157c-ev1.dts     |  6 ++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
index 88e9133..b35f151 100644
--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
@@ -277,7 +277,6 @@
 			compatible = "st,stm32mp157-z-pinctrl";
 			ranges = <0 0x54004000 0x400>;
 			pins-are-numbered;
-			status = "disabled";
 
 			gpioz: gpio at 54004000 {
 				gpio-controller;
@@ -301,6 +300,21 @@
 					slew-rate = <0>;
 				};
 			};
+
+			spi1_pins_a: spi1-0 {
+				pins1 {
+					pinmux = <STM32_PINMUX('Z', 0, AF5)>, /* SPI1_SCK */
+						 <STM32_PINMUX('Z', 2, AF5)>; /* SPI1_MOSI */
+					bias-disable;
+					drive-push-pull;
+					slew-rate = <1>;
+				};
+
+				pins2 {
+					pinmux = <STM32_PINMUX('Z', 1, AF5)>; /* SPI1_MISO */
+					bias-disable;
+				};
+			};
 		};
 	};
 };
diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts
index 9382d80..7584e1f 100644
--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts
@@ -67,6 +67,12 @@
 	};
 };
 
+&spi1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi1_pins_a>;
+	status = "disabled";
+};
+
 &timers2 {
 	status = "disabled";
 	pwm {
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/3] ARM: dts: stm32: add STM32 SPI support on stm32mp157c
From: Amelie Delaunay @ 2018-05-17 14:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1526566904-24514-1-git-send-email-amelie.delaunay@st.com>

This patch adds all STM32 SPI instances on stm32mp157c.

Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
---
 arch/arm/boot/dts/stm32mp157c.dtsi | 84 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index b66f673..49ce7f0 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -311,6 +311,34 @@
 			};
 		};
 
+		spi2: spi at 4000b000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x4000b000 0x400>;
+			interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI2_K>;
+			resets = <&rcc SPI2_R>;
+			dmas = <&dmamux1 39 0x400 0x05>,
+			       <&dmamux1 40 0x400 0x05>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
+		spi3: spi at 4000c000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x4000c000 0x400>;
+			interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI3_K>;
+			resets = <&rcc SPI3_R>;
+			dmas = <&dmamux1 61 0x400 0x05>,
+			       <&dmamux1 62 0x400 0x05>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
 		usart2: serial at 4000e000 {
 			compatible = "st,stm32h7-uart";
 			reg = <0x4000e000 0x400>;
@@ -494,6 +522,34 @@
 			status = "disabled";
 		};
 
+		spi1: spi at 44004000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x44004000 0x400>;
+			interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI1_K>;
+			resets = <&rcc SPI1_R>;
+			dmas = <&dmamux1 37 0x400 0x05>,
+			       <&dmamux1 38 0x400 0x05>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
+		spi4: spi at 44005000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x44005000 0x400>;
+			interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI4_K>;
+			resets = <&rcc SPI4_R>;
+			dmas = <&dmamux1 83 0x400 0x05>,
+			       <&dmamux1 84 0x400 0x05>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
 		timers15: timer at 44006000 {
 			#address-cells = <1>;
 			#size-cells = <0>;
@@ -556,6 +612,20 @@
 			};
 		};
 
+		spi5: spi at 44009000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x44009000 0x400>;
+			interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI5_K>;
+			resets = <&rcc SPI5_R>;
+			dmas = <&dmamux1 85 0x400 0x05>,
+			       <&dmamux1 86 0x400 0x05>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
 		dma1: dma at 48000000 {
 			compatible = "st,stm32-dma";
 			reg = <0x48000000 0x400>;
@@ -805,6 +875,20 @@
 			status = "disabled";
 		};
 
+		spi6: spi at 5c001000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "st,stm32h7-spi";
+			reg = <0x5c001000 0x400>;
+			interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&rcc SPI6_K>;
+			resets = <&rcc SPI6_R>;
+			dmas = <&mdma1 34 0x0 0x40008 0x0 0x0 0>,
+			       <&mdma1 35 0x0 0x40002 0x0 0x0 0>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
 		i2c4: i2c at 5c002000 {
 			compatible = "st,stm32f7-i2c";
 			reg = <0x5c002000 0x400>;
-- 
2.7.4

^ permalink raw reply related

* [PATCH 0/3] Add STM32 SPI support on stm32mp157c
From: Amelie Delaunay @ 2018-05-17 14:21 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds STM32 SPI support on stm32mp157c SoC.
SPI1, available on GPIO expansion connector, is kept disabled, so these
pins can be used as GPIOs by default.

Amelie Delaunay (3):
  ARM: dts: stm32: add STM32 SPI support on stm32mp157c
  ARM: dts: stm32: add SPI1 support on stm32mp157c-ev1
  ARM: multi_v7_defconfig: enable STM32 SPI

 arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 16 +++++-
 arch/arm/boot/dts/stm32mp157c-ev1.dts     |  6 +++
 arch/arm/boot/dts/stm32mp157c.dtsi        | 84 +++++++++++++++++++++++++++++++
 arch/arm/configs/multi_v7_defconfig       |  1 +
 4 files changed, 106 insertions(+), 1 deletion(-)

-- 
2.7.4

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox