Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [LINUX PATCH] dma-mapping: Control memset operation using gfp flags
From: Dylan Yip @ 2019-09-20  2:49 UTC (permalink / raw)
  To: satishna, linux-arm-kernel, linux-kernel, iommu; +Cc: Dylan Yip

In case of 4k video buffer, the allocation from a reserved memory is
taking a long time, ~500ms. This is root caused to the memset()
operations on the allocated memory which is consuming more cpu cycles.
Due to this delay, we see that initial frames are being dropped.

To fix this, we have wrapped the default memset, done when allocating
coherent memory, under the __GFP_ZERO flag. So, we only clear
allocated memory if __GFP_ZERO flag is enabled. We believe this
should be safe as the video decoder always writes before reading.
This optimizes decoder initialization as we do not set the __GFP_ZERO
flag when allocating memory for decoder. With this optimization, we
don't see initial frame drops and decoder initialization time is
~100ms.

This patch adds plumbing through dma_alloc functions to pass gfp flag
set by user to __dma_alloc_from_coherent(). Here gfp flag is checked
for __GFP_ZERO. If present, we memset the buffer to 0 otherwise we
skip memset.

Signed-off-by: Dylan Yip <dylan.yip@xilinx.com>
---
 arch/arm/mm/dma-mapping-nommu.c |  2 +-
 include/linux/dma-mapping.h     | 11 +++++++----
 kernel/dma/coherent.c           | 15 +++++++++------
 kernel/dma/mapping.c            |  2 +-
 4 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
index 52b8255..242b2c3 100644
--- a/arch/arm/mm/dma-mapping-nommu.c
+++ b/arch/arm/mm/dma-mapping-nommu.c
@@ -35,7 +35,7 @@ static void *arm_nommu_dma_alloc(struct device *dev, size_t size,
 				 unsigned long attrs)
 
 {
-	void *ret = dma_alloc_from_global_coherent(size, dma_handle);
+	void *ret = dma_alloc_from_global_coherent(size, dma_handle, gfp);
 
 	/*
 	 * dma_alloc_from_global_coherent() may fail because:
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index f7d1eea..b715c9f 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -160,24 +160,27 @@ static inline int is_device_dma_capable(struct device *dev)
  * Don't use them in device drivers.
  */
 int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size,
-				       dma_addr_t *dma_handle, void **ret);
+				       dma_addr_t *dma_handle, void **ret,
+				       gfp_t flag);
 int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr);
 
 int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma,
 			    void *cpu_addr, size_t size, int *ret);
 
-void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle);
+void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle,
+				     gfp_t flag);
 int dma_release_from_global_coherent(int order, void *vaddr);
 int dma_mmap_from_global_coherent(struct vm_area_struct *vma, void *cpu_addr,
 				  size_t size, int *ret);
 
 #else
-#define dma_alloc_from_dev_coherent(dev, size, handle, ret) (0)
+#define dma_alloc_from_dev_coherent(dev, size, handle, ret, flag) (0)
 #define dma_release_from_dev_coherent(dev, order, vaddr) (0)
 #define dma_mmap_from_dev_coherent(dev, vma, vaddr, order, ret) (0)
 
 static inline void *dma_alloc_from_global_coherent(ssize_t size,
-						   dma_addr_t *dma_handle)
+						   dma_addr_t *dma_handle,
+						   gfp_t flag)
 {
 	return NULL;
 }
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 29fd659..d85fab5 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -136,7 +136,7 @@ void dma_release_declared_memory(struct device *dev)
 EXPORT_SYMBOL(dma_release_declared_memory);
 
 static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem,
-		ssize_t size, dma_addr_t *dma_handle)
+		ssize_t size, dma_addr_t *dma_handle, gfp_t gfp_flag)
 {
 	int order = get_order(size);
 	unsigned long flags;
@@ -158,7 +158,8 @@ static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem,
 	*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
 	ret = mem->virt_base + (pageno << PAGE_SHIFT);
 	spin_unlock_irqrestore(&mem->spinlock, flags);
-	memset(ret, 0, size);
+	if (gfp_flag & __GFP_ZERO)
+		memset(ret, 0, size);
 	return ret;
 err:
 	spin_unlock_irqrestore(&mem->spinlock, flags);
@@ -172,6 +173,7 @@ static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem,
  * @dma_handle:	This will be filled with the correct dma handle
  * @ret:	This pointer will be filled with the virtual address
  *		to allocated area.
+ * @flag:      gfp flag set by user
  *
  * This function should be only called from per-arch dma_alloc_coherent()
  * to support allocation from per-device coherent memory pools.
@@ -180,24 +182,25 @@ static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem,
  * generic memory areas, or !0 if dma_alloc_coherent should return @ret.
  */
 int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size,
-		dma_addr_t *dma_handle, void **ret)
+		dma_addr_t *dma_handle, void **ret, gfp_t flag)
 {
 	struct dma_coherent_mem *mem = dev_get_coherent_memory(dev);
 
 	if (!mem)
 		return 0;
 
-	*ret = __dma_alloc_from_coherent(mem, size, dma_handle);
+	*ret = __dma_alloc_from_coherent(mem, size, dma_handle, flag);
 	return 1;
 }
 
-void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle)
+void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle,
+				     gfp_t flag)
 {
 	if (!dma_coherent_default_memory)
 		return NULL;
 
 	return __dma_alloc_from_coherent(dma_coherent_default_memory, size,
-			dma_handle);
+			dma_handle, flag);
 }
 
 static int __dma_release_from_coherent(struct dma_coherent_mem *mem,
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index b0038ca..bfea1d2 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -272,7 +272,7 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
 
 	WARN_ON_ONCE(!dev->coherent_dma_mask);
 
-	if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr))
+	if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr, flag))
 		return cpu_addr;
 
 	/* let the implementation decide on the zone to allocate from: */
-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [RFC PATCH v1 1/1] Add support for arm64 to carry ima measurement log in kexec_file_load
From: Thiago Jung Bauermann @ 2019-09-20  3:07 UTC (permalink / raw)
  To: Prakhar Srivastava
  Cc: mark.rutland, jean-philippe, arnd, takahiro.akashi, sboyd,
	catalin.marinas, kexec, linux-kernel, zohar, yamada.masahiro,
	kristina.martsenko, duwe, allison, james.morse, linux-integrity,
	tglx, linux-arm-kernel
In-Reply-To: <20190913225009.3406-2-prsriva@linux.microsoft.com>


Hello Prakhar,

Prakhar Srivastava <prsriva@linux.microsoft.com> writes:

> During kexec_file_load, carrying forward the ima measurement log allows
> a verifying party to get the entire runtime event log since the last
> full reboot since that is when PCRs were last reset.
>
> Signed-off-by: Prakhar Srivastava <prsriva@linux.microsoft.com>
> ---
>  arch/arm64/Kconfig                     |   7 +
>  arch/arm64/include/asm/ima.h           |  29 ++++
>  arch/arm64/include/asm/kexec.h         |   5 +
>  arch/arm64/kernel/Makefile             |   3 +-
>  arch/arm64/kernel/ima_kexec.c          | 213 +++++++++++++++++++++++++
>  arch/arm64/kernel/machine_kexec_file.c |   6 +
>  6 files changed, 262 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm64/include/asm/ima.h
>  create mode 100644 arch/arm64/kernel/ima_kexec.c
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 3adcec05b1f6..f39b12dbf9e8 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -976,6 +976,13 @@ config KEXEC_VERIFY_SIG
>  	  verification for the corresponding kernel image type being
>  	  loaded in order for this to work.
>
> +config HAVE_IMA_KEXEC
> +	bool "Carry over IMA measurement log during kexec_file_load() syscall"
> +	depends on KEXEC_FILE
> +	help
> +	  Select this option to carry over IMA measurement log during
> +	  kexec_file_load.
> +
>  config KEXEC_IMAGE_VERIFY_SIG
>  	bool "Enable Image signature verification support"
>  	default y

This is not right. As it stands, HAVE_IMA_KEXEC is essentially a synonym
for IMA_KEXEC.

It's not meant to be user-visible in the config process. Instead, it's
meant to be selected by the arch Kconfig (probably by the ARM64 config
symbol) to signal to IMA's Kconfig that it can offer the IMA_KEXEC
option.

I also mentioned in my previous review that config HAVE_IMA_KEXEC should
be defined in arch/Kconfig, not separately in both arch/arm64/Kconfig
and arch/powerpc/Kconfig.

> diff --git a/arch/arm64/include/asm/ima.h b/arch/arm64/include/asm/ima.h
> new file mode 100644
> index 000000000000..e23cee84729f
> --- /dev/null
> +++ b/arch/arm64/include/asm/ima.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_ARM64_IMA_H
> +#define _ASM_ARM64_IMA_H
> +
> +struct kimage;
> +
> +int ima_get_kexec_buffer(void **addr, size_t *size);
> +int ima_free_kexec_buffer(void);
> +
> +#ifdef CONFIG_IMA
> +void remove_ima_buffer(void *fdt, int chosen_node);
> +#else
> +static inline void remove_ima_buffer(void *fdt, int chosen_node) {}
> +#endif

I mentioned in my previous review that remove_ima_buffer() should exist
even if CONFIG_IMA isn't set. Did you arrive at a different conclusion?

> +
> +#ifdef CONFIG_IMA_KEXEC
> +int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
> +			      size_t size);
> +
> +int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node);
> +#else
> +static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
> +				   int chosen_node)
> +{
> +	remove_ima_buffer(fdt, chosen_node);
> +	return 0;
> +}
> +#endif /* CONFIG_IMA_KEXEC */
> +#endif /* _ASM_ARM64_IMA_H */

> diff --git a/arch/arm64/kernel/ima_kexec.c b/arch/arm64/kernel/ima_kexec.c
> new file mode 100644
> index 000000000000..b14326d541f3
> --- /dev/null
> +++ b/arch/arm64/kernel/ima_kexec.c

In the previous patch, you took the powerpc file and made a few
modifications to fit your needs. This file is now somewhat different
than the powerpc version, but I don't understand to what purpose. It's
not different in any significant way.

Based on review comments from your previous patch, I was expecting to
see code from the powerpc file moved to an arch-independent part of the
the kernel and possibly adapted so that both arm64 and powerpc could use
it. Can you explain why you chose this approach instead? What is the
advantage of having superficially different but basically equivalent
code in the two architectures?

Actually, there's one change that is significant: instead of a single
linux,ima-kexec-buffer property holding the start address and size of
the buffer, ARM64 is now using two properties (linux,ima-kexec-buffer
and linux,ima-kexec-buffer-end) for the start and end addresses. In my
opinion, unless there's a good reason for it Linux should be consistent
accross architectures when possible.

--
Thiago Jung Bauermann
IBM Linux Technology Center

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 1/2] soc: ti: big cleanup of Kconfig file
From: Randy Dunlap @ 2019-09-20  3:29 UTC (permalink / raw)
  To: santosh.shilimkar, LKML, LAK
  Cc: Dave Gerlach, Tony Lindgren, Keerthy, Sandeep Nair,
	Santosh Shilimkar, Olof Johansson
In-Reply-To: <97a9a11e-7784-111e-c134-ef88bd6b51ec@oracle.com>

On 9/19/19 6:14 PM, santosh.shilimkar@oracle.com wrote:
> On 9/19/19 3:33 PM, Randy Dunlap wrote:
>> From: Randy Dunlap <rdunlap@infradead.org>
>>
>> Cleanup drivers/soc/ti/Kconfig:
>> - delete duplicate words
>> - end sentences with '.'
>> - fix typos/spellos
>> - Subsystem is one word
>> - capitalize acronyms
>> - reflow lines to be <= 80 columns
>>
>> Fixes: 41f93af900a2 ("soc: ti: add Keystone Navigator QMSS driver")
>> Fixes: 88139ed03058 ("soc: ti: add Keystone Navigator DMA support")
>> Fixes: afe761f8d3e9 ("soc: ti: Add pm33xx driver for basic suspend support")
>> Fixes: 5a99ae0092fe ("soc: ti: pm33xx: AM437X: Add rtc_only with ddr in self-refresh support")
>> Fixes: a869b7b30dac ("soc: ti: Add Support for AM654 SoC config option")
>> Fixes: cff377f7897a ("soc: ti: Add Support for J721E SoC config option")
>> Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
>> Cc: Olof Johansson <olof@lixom.net>
>> Cc: Santosh Shilimkar <ssantosh@kernel.org>
>> Cc: Sandeep Nair <sandeep_n@ti.com>
>> Cc: Dave Gerlach <d-gerlach@ti.com>
>> Cc: Keerthy <j-keerthy@ti.com>
>> Cc: Tony Lindgren <tony@atomide.com>
>> Cc: linux-kernel@vger.kernel.org
>> Cc: linux-arm-kernel@lists.infradead.org
>> ---
>> @Santosh: MAINTAINERS says that you maintain drivers/soc/ti/*,
>> but there is more that Keystone-related code in that subdirectory
>> now... just in case you want to update that info.
>>
> Yes am aware there more drivers and so far I have been taking
> care of everything in drivers/soc/ti/*

OK :)

>>   drivers/soc/ti/Kconfig |   20 ++++++++++----------
>>   1 file changed, 10 insertions(+), 10 deletions(-)
>>
> Patch looks fine to me. Do you want me to pick this up ?
> 

Yes, please.

-- 
~Randy

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* RE: [EXT] [PATCH v3] serial: imx: adapt rx buffer and dma periods
From: Andy Duan @ 2019-09-20  3:42 UTC (permalink / raw)
  To: Philipp Puschmann, linux-kernel@vger.kernel.org
  Cc: festevam@gmail.com, linux-serial@vger.kernel.org,
	gregkh@linuxfoundation.org, s.hauer@pengutronix.de,
	u.kleine-koenig@pengutronix.de, dl-linux-imx,
	kernel@pengutronix.de, jslaby@suse.com, Robin Gong,
	shawnguo@kernel.org, linux-arm-kernel@lists.infradead.org,
	l.stach@pengutronix.de
In-Reply-To: <20190919145114.13006-1-philipp.puschmann@emlix.com>

From: Philipp Puschmann <philipp.puschmann@emlix.com> Sent: Thursday, September 19, 2019 10:51 PM
> Using only 4 DMA periods for UART RX is very few if we have a high frequency
> of small transfers - like in our case using Bluetooth with many small packets
> via UART - causing many dma transfers but in each only filling a fraction of a
> single buffer. Such a case may lead to the situation that DMA RX transfer is
> triggered but no free buffer is available. When this happens dma channel ist
> stopped - with the patch
> "dmaengine: imx-sdma: fix dma freezes" temporarily only - with the possible
> consequences that:
> with disabled hw flow control:
>   If enough data is incoming on UART port the RX FIFO runs over and
>   characters will be lost. What then happens depends on upper layer.
> 
> with enabled hw flow control:
>   If enough data is incoming on UART port the RX FIFO reaches a level
>   where CTS is deasserted and remote device sending the data stops.
>   If it fails to stop timely the i.MX' RX FIFO may run over and data
>   get lost. Otherwise it's internal TX buffer may getting filled to
>   a point where it runs over and data is again lost. It depends on
>   the remote device how this case is handled and if it is recoverable.
> 
> Obviously we want to avoid having no free buffers available. So we decrease
> the size of the buffers and increase their number and the total buffer size.
> 
> Signed-off-by: Philipp Puschmann <philipp.puschmann@emlix.com>
> Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
> ---
> 
> Changelog v3:
>  - enhance description
> 
> Changelog v2:
>  - split this patch from series "Fix UART DMA freezes for iMX6"
>  - add Reviewed-by tag
> 
>  drivers/tty/serial/imx.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index
> 87c58f9f6390..51dc19833eab 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -1034,8 +1034,6 @@ static void imx_uart_timeout(struct timer_list *t)
>         }
>  }
> 
> -#define RX_BUF_SIZE    (PAGE_SIZE)
> -
>  /*
>   * There are two kinds of RX DMA interrupts(such as in the MX6Q):
>   *   [1] the RX DMA buffer is full.
> @@ -1118,7 +1116,8 @@ static void imx_uart_dma_rx_callback(void
> *data)  }
> 
>  /* RX DMA buffer periods */
> -#define RX_DMA_PERIODS 4
> +#define RX_DMA_PERIODS 16
> +#define RX_BUF_SIZE    (PAGE_SIZE / 4)
> 
Why to decrease the DMA RX buffer size here ?

The current DMA implementation support DMA cyclic mode, one SDMA BD receive one Bluetooth frame can
bring better performance.
As you know, for L2CAP, a maximum transmission unit (MTU) associated with the largest Baseband payload
is 341 bytes for DH5 packets.

So I suggest to increase RX_BUF_SIZE along with RX_DMA_PERIODS to feasible value.

Andy

>  static int imx_uart_start_rx_dma(struct imx_port *sport)  {
> --
> 2.23.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH] ARM: aspeed: ast2500 is ARMv6K
From: Andrew Jeffery @ 2019-09-20  3:58 UTC (permalink / raw)
  To: Arnd Bergmann, Joel Stanley; +Cc: linux-aspeed, linux-arm-kernel, linux-kernel
In-Reply-To: <20190919142654.1578823-1-arnd@arndb.de>



On Thu, 19 Sep 2019, at 23:56, Arnd Bergmann wrote:
> Linux supports both the original ARMv6 level (early ARM1136) and ARMv6K
> (later ARM1136, ARM1176 and ARM11mpcore).
> 
> ast2500 falls into the second categoy, being based on arm1176jzf-s.
> This is enabled by default when using ARCH_MULTI_V6, so we should
> not 'select CPU_V6'.
> 
> Removing this will lead to more efficient use of atomic instructions.
> 
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH V2 2/2] mm/pgtable/debug: Add test validating architecture page table helpers
From: Anshuman Khandual @ 2019-09-20  4:06 UTC (permalink / raw)
  To: Gerald Schaefer, Christophe Leroy
  Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
	Tetsuo Handa, Heiko Carstens, Michal Hocko, linux-mm, Dave Hansen,
	Paul Mackerras, sparclinux, Thomas Gleixner, linux-s390,
	Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
	Steven Price, Jason Gunthorpe, linux-arm-kernel, linux-snps-arc,
	Kees Cook, Masahiro Yamada, Mark Brown, Kirill A . Shutemov,
	Dan Williams, Vlastimil Babka, Sri Krishna chowdary,
	Ard Biesheuvel, Greg Kroah-Hartman, linux-mips, Ralf Baechle,
	linux-kernel, Paul Burton, Mike Rapoport, Vineet Gupta,
	Martin Schwidefsky, Andrew Morton, linuxppc-dev, David S. Miller
In-Reply-To: <20190918202243.37e709df@thinkpad>



On 09/18/2019 11:52 PM, Gerald Schaefer wrote:
> On Wed, 18 Sep 2019 18:26:03 +0200
> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> 
> [..] 
>> My suggestion was not to completely drop the #ifdef but to do like you 
>> did in pgd_clear_tests() for instance, ie to add the following test on 
>> top of the function:
>>
>> 	if (mm_pud_folded(mm) || is_defined(__ARCH_HAS_5LEVEL_HACK))
>> 		return;
>>
> 
> Ah, very nice, this would also fix the remaining issues for s390. Since
> we have dynamic page table folding, neither __PAGETABLE_PXX_FOLDED nor
> __ARCH_HAS_XLEVEL_HACK is defined, but mm_pxx_folded() will work.

Like Christophe mentioned earlier on the other thread, we will convert
all __PGTABLE_PXX_FOLDED checks as mm_pxx_folded() but looks like 
ARCH_HAS_[4 and 5]LEVEL_HACK macros will still be around. Will respin
the series with all agreed upon changes first and probably we can then
discuss pending issues from there.

> 
> mm_alloc() returns with a 3-level page table by default on s390, so we
> will run into issues in p4d_clear/populate_tests(), and also at the end
> with p4d/pud_free() (double free).
> 
> So, adding the mm_pud_folded() check to p4d_clear/populate_tests(),
> and also adding mm_p4d/pud_folded() checks at the end before calling> p4d/pud_free(), would make it all work on s390.

Atleast p4d_clear/populate_tests() tests will be taken care.

> 
> BTW, regarding p4d/pud_free(), I'm not sure if we should rather check
> the folding inside our s390 functions, similar to how we do it for
> p4d/pud_free_tlb(), instead of relying on not being called for folded
> p4d/pud. So far, I see no problem with this behavior, all callers of
> p4d/pud_free() should be fine because of our folding check within
> p4d/pud_present/none(). But that doesn't mean that it is correct not
> to check for the folding inside p4d/pud_free(). At least, with this
> test module we do now have a caller of p4d/pud_free() on potentially
> folded entries, so instead of adding pxx_folded() checks to this
> test module, we could add them to our p4d/pud_free() functions.
> Any thoughts on this?
Agreed, it seems better to do the check inside p4d/pud_free() functions.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [breakage] panic() does not halt arm64 systems under certain conditions
From: Jookia @ 2019-09-20  4:25 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arch, gregkh, Xogium, linux-kernel, linux, mingo, bp, tglx,
	linux-arm-kernel
In-Reply-To: <20190917104518.ovg6ivadyst7h76o@willie-the-truck>

On Tue, Sep 17, 2019 at 11:45:19AM +0100, Will Deacon wrote:
> Hi,
> 
> [Expanding CC list; original message is here:
>  https://lore.kernel.org/linux-arm-kernel/BX1W47JXPMR8.58IYW53H6M5N@dragonstone/]
> 
> On Mon, Sep 16, 2019 at 09:35:36PM -0400, Xogium wrote:
> > On arm64 in some situations userspace will continue running even after a
> > panic. This means any userspace watchdog daemon will continue pinging,
> > that service managers will keep running and displaying messages in certain
> > cases, and that it is possible to enter via ssh in the now unstable system
> > and to do almost anything except reboot/power off and etc. If
> > CONFIG_PREEMPT=n is set in the kernel's configuration, the issue is fixed.
> > I have reproduced the very same behavior with linux 4.19, 5.2 and 5.3. On
> > x86/x86_64 the issue does not seem to be present at all.
> 
> I've managed to reproduce this under both 32-bit and 64-bit ARM kernels.
> The issue is that the infinite loop at the end of panic() can run with
> preemption enabled (particularly when invoking by echoing 'c' to
> /proc/sysrq-trigger), so we end up rescheduling user tasks. On x86, this
> doesn't happen because smp_send_stop() disables the local APIC in
> native_stop_other_cpus() and so interrupts are effectively masked while
> spinning.
> 
> A straightforward fix is to disable preemption explicitly on the panic()
> path (diff below), but I've expanded the cc list to see both what others
> think, but also in case smp_send_stop() is supposed to have the side-effect
> of disabling interrupt delivery for the local CPU.
> 
> Will
> 
> --->8
> 
> diff --git a/kernel/panic.c b/kernel/panic.c
> index 057540b6eee9..02d0de31c42d 100644
> --- a/kernel/panic.c
> +++ b/kernel/panic.c
> @@ -179,6 +179,7 @@ void panic(const char *fmt, ...)
> 	 * after setting panic_cpu) from invoking panic() again.
> 	 */
> 	local_irq_disable();
> +	preempt_disable_notrace();
>  
> 	/*
> 	 * It's possible to come here directly from a panic-assertion and
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

When you run with panic=... it will send you to a loop earlier in the
panic code before local_irq_disable() is hit, working around the bug.
A patch like this would make the behaviour the same:

diff --git a/kernel/panic.c b/kernel/panic.c
index 4d9f55bf7d38..92abbb5f8d38 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -331,7 +331,6 @@ void panic(const char *fmt, ...)

        /* Do not scroll important messages printed above */
        suppress_printk = 1;
-       local_irq_enable();
        for (i = 0; ; i += PANIC_TIMER_STEP) {
                touch_softlockup_watchdog();
                if (i >= i_next) {

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [PATCH 19/23] mtd: spi-nor: Rework spansion(_no)_read_cr_quad_enable()
From: Tudor.Ambarus @ 2019-09-20  4:42 UTC (permalink / raw)
  To: vigneshr, boris.brezillon, marek.vasut, miquel.raynal, richard,
	linux-mtd
  Cc: linux-aspeed, andrew, linux-kernel, vz, linux-mediatek, joel,
	matthias.bgg, computersforpeace, dwmw2, linux-arm-kernel
In-Reply-To: <f811a9a6-4b88-e017-5cc6-ad758edbcab3@ti.com>



On 09/19/2019 08:34 PM, Vignesh Raghavendra wrote:
> 
> 
> On 17-Sep-19 9:25 PM, Tudor.Ambarus@microchip.com wrote:
>> From: Tudor Ambarus <tudor.ambarus@microchip.com>
>>
>> Merge:
>> spansion_no_read_cr_quad_enable()
>> spansion_read_cr_quad_enable()
>>
>> in spi_nor_sr2_bit1_quad_enable().
>>
>> Avoid duplication of code by using spi_nor_write_16bit_sr_and_check(),
>> the SNOR_F_NO_READ_CR case is treated there.
>>
>> We now do the Read Back test even for the old
>> spansion_no_read_cr_quad_enable() case.
>>
>> Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
>> ---
>>  drivers/mtd/spi-nor/spi-nor.c | 89 ++++++++++---------------------------------
>>  include/linux/mtd/spi-nor.h   |  4 +-
>>  2 files changed, 22 insertions(+), 71 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>> index 2f79923e7db5..8648666fb9bd 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -907,7 +907,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 status_new,
>>  		 * Write Status (01h) command is available just for the cases
>>  		 * in which the QE bit is described in SR2 at BIT(1).
>>  		 */
>> -		sr_cr[1] = CR_QUAD_EN_SPAN;
>> +		sr_cr[1] = SR2_QUAD_EN_BIT1;
>>  	} else {
>>  		sr_cr[1] = 0;
>>  	}
>> @@ -1963,81 +1963,34 @@ static int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor)
>>  }
>>  
>>  /**
>> - * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.
>> + * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status
>> + * Register 2.
>>   * @nor:	pointer to a 'struct spi_nor'
>>   *
>> - * Set the Quad Enable (QE) bit in the Configuration Register.
>> - * This function should be used with QSPI memories not supporting the Read
>> - * Configuration Register (35h) instruction.
>> - *
>> - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
>> - * memories.
>> - *
>> - * Return: 0 on success, -errno otherwise.
>> - */
>> -static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
>> -{
>> -	u8 *sr_cr = nor->bouncebuf;
>> -	int ret;
>> -
>> -	/* Keep the current value of the Status Register. */
>> -	ret = spi_nor_read_sr(nor, &sr_cr[0]);
>> -	if (ret)
>> -		return ret;
>> -
>> -	sr_cr[1] = CR_QUAD_EN_SPAN;
>> -
>> -	return spi_nor_write_sr(nor, sr_cr, 2);
>> -}
>> -
>> -/**
>> - * spansion_read_cr_quad_enable() - set QE bit in Configuration Register.
>> - * @nor:	pointer to a 'struct spi_nor'
>> - *
>> - * Set the Quad Enable (QE) bit in the Configuration Register.
>> - * This function should be used with QSPI memories supporting the Read
>> - * Configuration Register (35h) instruction.
>> - *
>> - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
>> - * memories.
>> + * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories.
>>   *
>>   * Return: 0 on success, -errno otherwise.
>>   */
>> -static int spansion_read_cr_quad_enable(struct spi_nor *nor)
>> +static int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor)
>>  {
>> -	u8 *sr_cr = nor->bouncebuf;
>>  	int ret;
>>  
>> -	/* Check current Quad Enable bit value. */
>> -	ret = spi_nor_read_cr(nor, &sr_cr[1]);
>> -	if (ret)
>> -		return ret;
>> -
>> -	if (sr_cr[1] & CR_QUAD_EN_SPAN)
>> -		return 0;
>> +	if (!(nor->flags & SNOR_F_NO_READ_CR)) {
>> +		/* Check current Quad Enable bit value. */
>> +		ret = spi_nor_read_cr(nor, &nor->bouncebuf[0]);
>> +		if (ret)
>> +			return ret;
>>  
>> -	sr_cr[1] |= CR_QUAD_EN_SPAN;
>> +		if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)
>> +			return 0;
>> +	}
>>  
>>  	/* Keep the current value of the Status Register. */
>> -	ret = spi_nor_read_sr(nor, &sr_cr[0]);
>> -	if (ret)
>> -		return ret;
>> -
>> -	ret = spi_nor_write_sr(nor, sr_cr, 2);
>> -	if (ret)
>> -		return ret;
>> -
>> -	/* Read back and check it. */
>> -	ret = spi_nor_read_cr(nor, &sr_cr[1]);
>> +	ret = spi_nor_read_sr(nor, &nor->bouncebuf[0]);
>>  	if (ret)
>>  		return ret;
>>  
>> -	if (!(sr_cr[1] & CR_QUAD_EN_SPAN)) {
>> -		dev_err(nor->dev, "Spansion Quad bit not set\n");
>> -		return -EIO;
>> -	}
>> -
>> -	return 0;
> 
> You need to set QE bit here before writing to CR register. This function
> does not do that.>
>> +	return spi_nor_write_16bit_sr_and_check(nor, nor->bouncebuf[0], 0xFF);>
> Neither does spi_nor_write_16bit_sr_and_check().

pff, you're right, I thought I did set it in spi_nor_write_16bit_sr_and_check(),
but in spi_nor_write_16bit_sr_and_check() I just read the CR, without setting
the QE bit. Will respin the entire series, thanks for catching this!

> We need a function that allows to modify SR2/CR register content as well
> so as to set QE bit right?
> 
> Regards
> Vignesh
> 
>>  }
>>  
>>  /**
>> @@ -2117,7 +2070,7 @@ static int spi_nor_clear_sr_bp(struct spi_nor *nor)
>>   *
>>   * Read-modify-write function that clears the Block Protection bits from the
>>   * Status Register without affecting other bits. The function is tightly
>> - * coupled with the spansion_read_cr_quad_enable() function. Both assume that
>> + * coupled with the spi_nor_sr2_bit1_quad_enable() function. Both assume that
>>   * the Write Register with 16 bits, together with the Read Configuration
>>   * Register (35h) instructions are supported.
>>   *
>> @@ -2138,7 +2091,7 @@ static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
>>  	 * When the configuration register Quad Enable bit is one, only the
>>  	 * Write Status (01h) command with two data bytes may be used.
>>  	 */
>> -	if (sr_cr[1] & CR_QUAD_EN_SPAN) {
>> +	if (sr_cr[1] & SR2_QUAD_EN_BIT1) {
>>  		ret = spi_nor_read_sr(nor, &sr_cr[0]);
>>  		if (ret)
>>  			return ret;
>> @@ -3642,7 +3595,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>  		 * supported.
>>  		 */
>>  		nor->flags |= SNOR_F_NO_READ_CR;
>> -		flash->quad_enable = spansion_no_read_cr_quad_enable;
>> +		flash->quad_enable = spi_nor_sr2_bit1_quad_enable;
>>  		break;
>>  
>>  	case BFPT_DWORD15_QER_SR1_BIT6:
>> @@ -3663,7 +3616,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>>  		 * assumption of a 16-bit Write Status (01h) command.
>>  		 */
>>  		nor->flags |= SNOR_F_HAS_16BIT_SR;
>> -		flash->quad_enable = spansion_read_cr_quad_enable;
>> +		flash->quad_enable = spi_nor_sr2_bit1_quad_enable;
>>  		break;
>>  
>>  	default:
>> @@ -4626,7 +4579,7 @@ static void spi_nor_info_init_flash_params(struct spi_nor *nor)
>>  	u8 i, erase_mask;
>>  
>>  	/* Initialize legacy flash parameters and settings. */
>> -	flash->quad_enable = spansion_read_cr_quad_enable;
>> +	flash->quad_enable = spi_nor_sr2_bit1_quad_enable;
>>  	flash->set_4byte = spansion_set_4byte;
>>  	flash->setup = spi_nor_default_setup;
>>  	/* Default to 16-bit Write Status (01h) Command */
>> @@ -4844,7 +4797,7 @@ static int spi_nor_init(struct spi_nor *nor)
>>  	int err;
>>  
>>  	if (nor->clear_sr_bp) {
>> -		if (nor->flash.quad_enable == spansion_read_cr_quad_enable)
>> +		if (nor->flash.quad_enable == spi_nor_sr2_bit1_quad_enable)
>>  			nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
>>  
>>  		err = nor->clear_sr_bp(nor);
>> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
>> index 3a835de90b6a..5590a36eb43e 100644
>> --- a/include/linux/mtd/spi-nor.h
>> +++ b/include/linux/mtd/spi-nor.h
>> @@ -144,10 +144,8 @@
>>  #define FSR_P_ERR		BIT(4)	/* Program operation status */
>>  #define FSR_PT_ERR		BIT(1)	/* Protection error bit */
>>  
>> -/* Configuration Register bits. */
>> -#define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>> -
>>  /* Status Register 2 bits. */
>> +#define SR2_QUAD_EN_BIT1	BIT(1)
>>  #define SR2_QUAD_EN_BIT7	BIT(7)
>>  
>>  /* Supported SPI protocols */
>>
> 
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 17/23] mtd: spi-nor: Fix clearing of QE bit on lock()/unlock()
From: Tudor.Ambarus @ 2019-09-20  5:23 UTC (permalink / raw)
  To: vigneshr, boris.brezillon, marek.vasut, miquel.raynal, richard,
	linux-mtd
  Cc: linux-aspeed, andrew, linux-kernel, vz, linux-mediatek, joel,
	matthias.bgg, computersforpeace, dwmw2, linux-arm-kernel
In-Reply-To: <dceca616-2b98-9bc8-73e4-32fb06fc753d@ti.com>

Hi, Vignesh,

On 09/19/2019 05:33 PM, Vignesh Raghavendra wrote:
> External E-Mail
> 
> 
> Hi Tudor
> 
> [...]
> 
> On 17-Sep-19 9:25 PM, Tudor.Ambarus@microchip.com wrote:
>> +static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 status_new,
>> +					    u8 mask)
>> +{
>> +	int ret;
>> +	u8 *sr_cr = nor->bouncebuf;
>> +	u8 cr_written;
>> +
>> +	/* Make sure we don't overwrite the contents of Status Register 2. */
>> +	if (!(nor->flags & SNOR_F_NO_READ_CR)) {
> 
> Assuming SNOR_F_NO_READ_CR is not set...
> 
>> +		ret = spi_nor_read_cr(nor, &sr_cr[1]);
>> +		if (ret)
>> +			return ret;
>> +	} else if (nor->flash.quad_enable) {
>> +		/*
>> +		 * If the Status Register 2 Read command (35h) is not
>> +		 * supported, we should at least be sure we don't
>> +		 * change the value of the SR2 Quad Enable bit.
>> +		 *
>> +		 * We can safely assume that when the Quad Enable method is
>> +		 * set, the value of the QE bit is one, as a consequence of the
>> +		 * nor->flash.quad_enable() call.
>> +		 *
>> +		 * We can safely assume that the Quad Enable bit is present in
>> +		 * the Status Register 2 at BIT(1). According to the JESD216
>> +		 * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit
>> +		 * Write Status (01h) command is available just for the cases
>> +		 * in which the QE bit is described in SR2 at BIT(1).
>> +		 */
>> +		sr_cr[1] = CR_QUAD_EN_SPAN;
>> +	} else {
>> +		sr_cr[1] = 0;
>> +	}
>> +
> 
> CR_QUAD_EN_SPAN will not be in sr_cr[1] when we reach here. So code
> won't enable quad mode.
> 

I get the problem now. spi_nor_write_16bit_sr_and_check() does not modify the
value of the QE bit, which is good in the lock/unlock() case. We want to
lock/unlock() without enabling or disabling the Quad Mode.

As you found, the problem comes later in spi_nor_sr2_bit1_quad_enable() because
I use there spi_nor_write_16bit_sr_and_check() which keeps the value of the QE
bit, without setting it to one, so the spi_nor_sr2_bit1_quad_enable() did not
enable the Quad Mode if not previously enabled.

What I'll do is to introduce a new argument to:
static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 status_new,
					    u8 mask, bool set_quad_enable)

and do a
if (set_quad_enable)
	sr_cr[1] |= CR_QUAD_EN_SPAN;
after initializing sr_cr[1]

The lock/unlock() methods will call the function with set_quad_enable being
false (we don't want to modify the QE value), and the
spi_nor_sr2_bit1_quad_enable() will call it with set_quad_enable being true, we
want to set QE to one (we don't care of the QE bit previous value).

We'll avoid code duplication, lock/unlock() and spi_nor_sr2_bit1_quad_enable()
calling the same method.

Cheers,
ta
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH 5/5] ARM: dts: stm32: Enable gating of the MAC TX clock during TX low-power mode on stm32mp157c
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel
In-Reply-To: <20190920053817.13754-1-christophe.roullier@st.com>

When there is no activity on ethernet phy link, the ETH_GTX_CLK is cut

Signed-off-by: Christophe Roullier <christophe.roullier@st.com>
---
 arch/arm/boot/dts/stm32mp157c.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index f51d6222a0e8..d78dfc44a1fb 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -1293,6 +1293,7 @@
 			st,syscon = <&syscfg 0x4>;
 			snps,mixed-burst;
 			snps,pbl = <2>;
+			snps,en-tx-lpi-clockgating;
 			snps,axi-config = <&stmmac_axi_config_0>;
 			snps,tso;
 			status = "disabled";
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH 2/5] net: ethernet: stmmac: fix warning when w=1 option is used during build
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel
In-Reply-To: <20190920053817.13754-1-christophe.roullier@st.com>

This patch fix the following warning:

warning: variable ‘ret’ set but not used [-Wunused-but-set-variable]
  int val, ret;

Signed-off-by: Christophe Roullier <christophe.roullier@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
index 7e6619868cc1..167a5e99960a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
@@ -184,7 +184,7 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
 {
 	struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
 	u32 reg = dwmac->mode_reg;
-	int val, ret;
+	int val;
 
 	switch (plat_dat->interface) {
 	case PHY_INTERFACE_MODE_MII:
@@ -220,8 +220,8 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
 	}
 
 	/* Need to update PMCCLRR (clear register) */
-	ret = regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET,
-			   dwmac->ops->syscfg_eth_mask);
+	regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET,
+		     dwmac->ops->syscfg_eth_mask);
 
 	/* Update PMCSETR (set register) */
 	return regmap_update_bits(dwmac->regmap, reg,
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH  0/5] net: ethernet: stmmac: some fixes and optimization
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel

Some improvements (manage syscfg as optional clock, update slew rate of
ETH_MDIO pin, Enable gating of the MAC TX clock during TX low-power mode)
Fix warning build message when W=1

Christophe Roullier (5):
  net: ethernet: stmmac: Add support for syscfg clock
  net: ethernet: stmmac: fix warning when w=1 option is used during
    build
  ARM: dts: stm32: remove syscfg clock on stm32mp157c ethernet
  ARM: dts: stm32: adjust slew rate for Ethernet
  ARM: dts: stm32: Enable gating of the MAC TX clock during TX low-power
    mode on stm32mp157c

 arch/arm/boot/dts/stm32mp157-pinctrl.dtsi     |  9 +++-
 arch/arm/boot/dts/stm32mp157c.dtsi            |  7 ++--
 .../net/ethernet/stmicro/stmmac/dwmac-stm32.c | 42 ++++++++++++-------
 3 files changed, 38 insertions(+), 20 deletions(-)

-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH  1/5] net: ethernet: stmmac: Add support for syscfg clock
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel
In-Reply-To: <20190920053817.13754-1-christophe.roullier@st.com>

Add optional support for syscfg clock in dwmac-stm32.c
Now Syscfg clock is activated automatically when syscfg
registers are used

Signed-off-by: Christophe Roullier <christophe.roullier@st.com>
---
 .../net/ethernet/stmicro/stmmac/dwmac-stm32.c | 36 +++++++++++++------
 1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
index 4ef041bdf6a1..7e6619868cc1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
@@ -152,23 +152,32 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare)
 	int ret = 0;
 
 	if (prepare) {
-		ret = clk_prepare_enable(dwmac->syscfg_clk);
-		if (ret)
-			return ret;
-
+		if (dwmac->syscfg_clk) {
+			ret = clk_prepare_enable(dwmac->syscfg_clk);
+			if (ret)
+				return ret;
+		}
 		if (dwmac->clk_eth_ck) {
 			ret = clk_prepare_enable(dwmac->clk_eth_ck);
 			if (ret) {
-				clk_disable_unprepare(dwmac->syscfg_clk);
+				if (dwmac->syscfg_clk)
+					goto unprepare_syscfg;
 				return ret;
 			}
 		}
 	} else {
-		clk_disable_unprepare(dwmac->syscfg_clk);
+		if (dwmac->syscfg_clk)
+			clk_disable_unprepare(dwmac->syscfg_clk);
+
 		if (dwmac->clk_eth_ck)
 			clk_disable_unprepare(dwmac->clk_eth_ck);
 	}
 	return ret;
+
+unprepare_syscfg:
+	clk_disable_unprepare(dwmac->syscfg_clk);
+
+	return ret;
 }
 
 static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
@@ -296,7 +305,7 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct device_node *np = dev->of_node;
-	int err = 0;
+	int err;
 
 	/* Gigabit Ethernet 125MHz clock selection. */
 	dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel");
@@ -320,13 +329,17 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
 		return PTR_ERR(dwmac->clk_ethstp);
 	}
 
-	/*  Clock for sysconfig */
+	/*  Optional Clock for sysconfig */
 	dwmac->syscfg_clk = devm_clk_get(dev, "syscfg-clk");
 	if (IS_ERR(dwmac->syscfg_clk)) {
-		dev_err(dev, "No syscfg clock provided...\n");
-		return PTR_ERR(dwmac->syscfg_clk);
+		err = PTR_ERR(dwmac->syscfg_clk);
+		if (err != -ENOENT)
+			return err;
+		dwmac->syscfg_clk = NULL;
 	}
 
+	err = 0;
+
 	/* Get IRQ information early to have an ability to ask for deferred
 	 * probe if needed before we went too far with resource allocation.
 	 */
@@ -436,7 +449,8 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac)
 		return ret;
 
 	clk_disable_unprepare(dwmac->clk_tx);
-	clk_disable_unprepare(dwmac->syscfg_clk);
+	if (dwmac->syscfg_clk)
+		clk_disable_unprepare(dwmac->syscfg_clk);
 	if (dwmac->clk_eth_ck)
 		clk_disable_unprepare(dwmac->clk_eth_ck);
 
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH  4/5] ARM: dts: stm32: adjust slew rate for Ethernet
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel
In-Reply-To: <20190920053817.13754-1-christophe.roullier@st.com>

ETH_MDIO slew-rate should be set to "0" instead of "2"

Signed-off-by: Christophe Roullier <christophe.roullier@st.com>
---
 arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
index df6470133574..7667fe758957 100644
--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
@@ -239,13 +239,18 @@
 						 <STM32_PINMUX('C', 2, AF11)>, /* ETH_RGMII_TXD2 */
 						 <STM32_PINMUX('E', 2, AF11)>, /* ETH_RGMII_TXD3 */
 						 <STM32_PINMUX('B', 11, AF11)>, /* ETH_RGMII_TX_CTL */
-						 <STM32_PINMUX('A', 2, AF11)>, /* ETH_MDIO */
 						 <STM32_PINMUX('C', 1, AF11)>; /* ETH_MDC */
 					bias-disable;
 					drive-push-pull;
-					slew-rate = <3>;
+					slew-rate = <2>;
 				};
 				pins2 {
+					pinmux = <STM32_PINMUX('A', 2, AF11)>; /* ETH_MDIO */
+					bias-disable;
+					drive-push-pull;
+					slew-rate = <0>;
+				};
+				pins3 {
 					pinmux = <STM32_PINMUX('C', 4, AF11)>, /* ETH_RGMII_RXD0 */
 						 <STM32_PINMUX('C', 5, AF11)>, /* ETH_RGMII_RXD1 */
 						 <STM32_PINMUX('B', 0, AF11)>, /* ETH_RGMII_RXD2 */
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH 3/5] ARM: dts: stm32: remove syscfg clock on stm32mp157c ethernet
From: Christophe Roullier @ 2019-09-20  5:38 UTC (permalink / raw)
  To: robh, davem, joabreu, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, peppe.cavallaro
  Cc: devicetree, andrew, netdev, linux-kernel, linux-stm32,
	christophe.roullier, linux-arm-kernel
In-Reply-To: <20190920053817.13754-1-christophe.roullier@st.com>

Syscfg is now activated automatically when syscfg registers are used

Signed-off-by: Christophe Roullier <christophe.roullier@st.com>
---
 arch/arm/boot/dts/stm32mp157c.dtsi | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index 0c4e6ebc3529..f51d6222a0e8 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -1285,13 +1285,11 @@
 			clock-names = "stmmaceth",
 				      "mac-clk-tx",
 				      "mac-clk-rx",
-				      "ethstp",
-				      "syscfg-clk";
+				      "ethstp";
 			clocks = <&rcc ETHMAC>,
 				 <&rcc ETHTX>,
 				 <&rcc ETHRX>,
-				 <&rcc ETHSTP>,
-				 <&rcc SYSCFG>;
+				 <&rcc ETHSTP>;
 			st,syscon = <&syscfg 0x4>;
 			snps,mixed-burst;
 			snps,pbl = <2>;
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [PATCH] ARM: aspeed: ast2500 is ARMv6K
From: Joel Stanley @ 2019-09-20  5:51 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Andrew Jeffery, linux-aspeed, Linux ARM,
	Linux Kernel Mailing List
In-Reply-To: <20190919142654.1578823-1-arnd@arndb.de>

On Thu, 19 Sep 2019 at 14:27, Arnd Bergmann <arnd@arndb.de> wrote:
>
> Linux supports both the original ARMv6 level (early ARM1136) and ARMv6K
> (later ARM1136, ARM1176 and ARM11mpcore).
>
> ast2500 falls into the second categoy, being based on arm1176jzf-s.
> This is enabled by default when using ARCH_MULTI_V6, so we should
> not 'select CPU_V6'.
>
> Removing this will lead to more efficient use of atomic instructions.

Wow, nice find.

>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  arch/arm/mach-aspeed/Kconfig | 1 -
>  1 file changed, 1 deletion(-)
>
> diff --git a/arch/arm/mach-aspeed/Kconfig b/arch/arm/mach-aspeed/Kconfig
> index a293137f5814..163931a03136 100644
> --- a/arch/arm/mach-aspeed/Kconfig
> +++ b/arch/arm/mach-aspeed/Kconfig
> @@ -26,7 +26,6 @@ config MACH_ASPEED_G4
>  config MACH_ASPEED_G5
>         bool "Aspeed SoC 5th Generation"
>         depends on ARCH_MULTI_V6
> -       select CPU_V6
>         select PINCTRL_ASPEED_G5 if !CC_IS_CLANG

I can't find any trees with !CC_IS_CLANG here. Is there a problem
building our pinmux driver with Clang?

I tested with this patch:

--- a/arch/arm/mach-aspeed/Kconfig
+++ b/arch/arm/mach-aspeed/Kconfig
@@ -25,8 +25,8 @@ config MACH_ASPEED_G4

 config MACH_ASPEED_G5
        bool "Aspeed SoC 5th Generation"
+       # This implies ARMv6K which covers the ARM1176
        depends on ARCH_MULTI_V6
-       select CPU_V6
        select PINCTRL_ASPEED_G5
        select FTTMR010_TIMER
        help

If you want to apply that as a fix for 5.4 I would be happy with that.

Fixes: 8c2ed9bcfbeb ("arm: Add Aspeed machine")
Reviewed-by: Joel Stanley <joel@jms.id.au>

Cheers,

Joel

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH] ARM: aspeed: ast2500 is ARMv6K
From: Andrew Jeffery @ 2019-09-20  5:54 UTC (permalink / raw)
  To: Arnd Bergmann, Joel Stanley; +Cc: linux-aspeed, linux-arm-kernel, linux-kernel
In-Reply-To: <20190919142654.1578823-1-arnd@arndb.de>



On Thu, 19 Sep 2019, at 23:56, Arnd Bergmann wrote:
> Linux supports both the original ARMv6 level (early ARM1136) and ARMv6K
> (later ARM1136, ARM1176 and ARM11mpcore).
> 
> ast2500 falls into the second categoy, being based on arm1176jzf-s.
> This is enabled by default when using ARCH_MULTI_V6, so we should
> not 'select CPU_V6'.
> 
> Removing this will lead to more efficient use of atomic instructions.
> 
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  arch/arm/mach-aspeed/Kconfig | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/arch/arm/mach-aspeed/Kconfig b/arch/arm/mach-aspeed/Kconfig
> index a293137f5814..163931a03136 100644
> --- a/arch/arm/mach-aspeed/Kconfig
> +++ b/arch/arm/mach-aspeed/Kconfig
> @@ -26,7 +26,6 @@ config MACH_ASPEED_G4
>  config MACH_ASPEED_G5
>  	bool "Aspeed SoC 5th Generation"
>  	depends on ARCH_MULTI_V6
> -	select CPU_V6
>  	select PINCTRL_ASPEED_G5 if !CC_IS_CLANG

Unrelated, but I'm intrigued by this. Looks like I should try compile it with clang
and fix the fallout.

Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 2/2] gpio: iproc-gpio: Handle interrupts for multiple instances
From: Srinath Mannam @ 2019-09-20  6:01 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Scott Branden, Rayagonda Kokatanur, Ray Jui,
	linux-kernel@vger.kernel.org, bcm-kernel-feedback-list, Linux ARM
In-Reply-To: <CACRpkdYyHMHknkrH_Gm45tgwv6dgjFxdoeg+Hj_KBWWyQqV1og@mail.gmail.com>

Hi Linus,

We have tested patch with your changes, it works fine.
Thanks a lot for all the help.

Regards,
Srinath.

On Wed, Sep 11, 2019 at 3:13 PM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Thu, Aug 29, 2019 at 5:52 AM Srinath Mannam
> <srinath.mannam@broadcom.com> wrote:
>
> > From: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> >
> > When multiple instance of iproc-gpio chips are present, a fix up
> > message[1] is printed during the probe of second and later instances.
> >
> > This issue is because driver sharing same irq_chip data structure
> > among multiple instances of driver.
> >
> > Fix this by allocating irq_chip data structure per instance of
> > iproc-gpio.
> >
> > [1] fix up message addressed by this patch
> > [  7.862208] gpio gpiochip2: (689d0000.gpio): detected irqchip that
> >    is shared with multiple gpiochips: please fix the driver.
> >
> > Fixes: 616043d58a89 ("pinctrl: Rename gpio driver from cygnus to iproc")
> > Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
>
> Patch applied, I had to rewrite it a bit to fit the new code that
> set up the irqchip when adding the gpio_chip, please check that
> the result works.
>
> Yours,
> Linus Walleij

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: mt76x2e hardware restart
From: Oleksandr Natalenko @ 2019-09-20  6:07 UTC (permalink / raw)
  To: linux-mediatek
  Cc: Ryder Lee, Stanislaw Gruszka, netdev, linux-wireless,
	linux-kernel, Matthias Brugger, linux-arm-kernel, Roy Luo,
	Lorenzo Bianconi, Lorenzo Bianconi, David S. Miller, Kalle Valo,
	Felix Fietkau
In-Reply-To: <c6d621759c190f7810d898765115f3b4@natalenko.name>

On 19.09.2019 23:22, Oleksandr Natalenko wrote:
> It checks for TX hang here:
> 
> === mt76x02_mmio.c
> 557 void mt76x02_wdt_work(struct work_struct *work)
> 558 {
> ...
> 562     mt76x02_check_tx_hang(dev);
> ===

I've commented out the watchdog here ^^, and the card is not resetted 
any more, but similarly it stops working shortly after the first client 
connects. So, indeed, it must be some hang in the HW, and wdt seems to 
do a correct job.

Is it even debuggable/fixable from the driver?

-- 
   Oleksandr Natalenko (post-factum)

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH v3 20/40] mtd: rawnand: Get rid of chip->ecc.priv
From: Maxime Ripard @ 2019-09-20  6:18 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Mason Yang, Vignesh Raghavendra, Tudor Ambarus, Julien Su,
	Richard Weinberger, Schrempf Frieder, Paul Cercueil, Marek Vasut,
	Chen-Yu Tsai, Boris Brezillon, linux-mtd, Thomas Petazzoni,
	Brian Norris, David Woodhouse, linux-arm-kernel
In-Reply-To: <20190919193141.7865-21-miquel.raynal@bootlin.com>


[-- Attachment #1.1: Type: text/plain, Size: 455 bytes --]

On Thu, Sep 19, 2019 at 09:31:20PM +0200, Miquel Raynal wrote:
> nand_ecc_ctrl embeds a private pointer which only has a meaning in the
> sunxi driver. This structure will soon be deprecated, but as this
> field is actually not needed, let's just drop it.
>
> Cc: Maxime Ripard <mripard@kernel.org>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Acked-by: Maxime Ripard <mripard@kernel.org>

Thanks!
Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH v5 2/2] drm/bridge: Add NWL MIPI DSI host controller support
From: Andrzej Hajda @ 2019-09-20  6:23 UTC (permalink / raw)
  To: Guido Günther
  Cc: Mark Rutland, devicetree, Jernej Skrabec, Pengutronix Kernel Team,
	Sam Ravnborg, Neil Armstrong, David Airlie, Fabio Estevam,
	Sascha Hauer, Jonas Karlman, linux-kernel, dri-devel, Rob Herring,
	Arnd Bergmann, NXP Linux Team, Daniel Vetter, Robert Chiras,
	Lee Jones, Shawn Guo, linux-arm-kernel, Laurent Pinchart
In-Reply-To: <20190919174313.GA5388@bogon.m.sigxcpu.org>

On 19.09.2019 19:43, Guido Günther wrote:
> Hi Andrzej,
>
> thanks for your comments!
>
> On Thu, Sep 19, 2019 at 03:56:45PM +0200, Andrzej Hajda wrote:
>> On 14.09.2019 18:11, Guido Günther wrote:
>>> Hi Andrzej,
>>> thanks for having a look!
>>>
>>> On Fri, Sep 13, 2019 at 11:31:43AM +0200, Andrzej Hajda wrote:
>>>> On 09.09.2019 04:25, Guido Günther wrote:
>>>>> This adds initial support for the NWL MIPI DSI Host controller found on
>>>>> i.MX8 SoCs.
>>>>>
>>>>> It adds support for the i.MX8MQ but the same IP can be found on
>>>>> e.g. the i.MX8QXP.
>>>>>
>>>>> It has been tested on the Librem 5 devkit using mxsfb.
>>>>>
>>>>> Signed-off-by: Guido Günther <agx@sigxcpu.org>
>>>>> Co-developed-by: Robert Chiras <robert.chiras@nxp.com>
>>>>> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
>>>>> Tested-by: Robert Chiras <robert.chiras@nxp.com>
>>>>> ---
>>>>>  drivers/gpu/drm/bridge/Kconfig           |   2 +
>>>>>  drivers/gpu/drm/bridge/Makefile          |   1 +
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/Kconfig   |  16 +
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/Makefile  |   4 +
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c | 499 ++++++++++++++++
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h |  65 +++
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c | 696 +++++++++++++++++++++++
>>>>>  drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h | 112 ++++
>>>> Why do you need separate files nwl-drv.[ch] and nwl-dsi.[ch] ? I guess
>>>> you can merge all into one file, maybe with separate file for NWL
>>>> register definitions.
>>> Idea is to have driver setup, soc specific hooks and revision specific
>>> quirks in one file and the dsi specific parts in another. If that
>>> doesn't fly I can merge into one if that's a requirement.
>>
>> One file looks saner to me :), but more importantly it follows current
>> practice.
> O.k., I'll merge things for v6 then.
>
>>
>>>>>  8 files changed, 1395 insertions(+)
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Kconfig
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Makefile
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
>>>>>  create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
>>>>>
>>>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>>>> index 1cc9f502c1f2..7980b5c2156f 100644
>>>>> --- a/drivers/gpu/drm/bridge/Kconfig
>>>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>>>> @@ -154,6 +154,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig"
>>>>>  
>>>>>  source "drivers/gpu/drm/bridge/adv7511/Kconfig"
>>>>>  
>>>>> +source "drivers/gpu/drm/bridge/nwl-dsi/Kconfig"
>>>>> +
>>>>>  source "drivers/gpu/drm/bridge/synopsys/Kconfig"
>>>>>  
>>>>>  endmenu
>>>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>>>> index 4934fcf5a6f8..d9f6c0f77592 100644
>>>>> --- a/drivers/gpu/drm/bridge/Makefile
>>>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>>>> @@ -16,4 +16,5 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
>>>>>  obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
>>>>>  obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
>>>>>  obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
>>>>> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi/
>>>>>  obj-y += synopsys/
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Kconfig b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
>>>>> new file mode 100644
>>>>> index 000000000000..7fa678e3b5e2
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
>>>>> @@ -0,0 +1,16 @@
>>>>> +config DRM_NWL_MIPI_DSI
>>>>> +	tristate "Northwest Logic MIPI DSI Host controller"
>>>>> +	depends on DRM
>>>>> +	depends on COMMON_CLK
>>>>> +	depends on OF && HAS_IOMEM
>>>>> +	select DRM_KMS_HELPER
>>>>> +	select DRM_MIPI_DSI
>>>>> +	select DRM_PANEL_BRIDGE
>>>>> +	select GENERIC_PHY_MIPI_DPHY
>>>>> +	select MFD_SYSCON
>>>>> +	select MULTIPLEXER
>>>>> +	select REGMAP_MMIO
>>>>> +	help
>>>>> +	  This enables the Northwest Logic MIPI DSI Host controller as
>>>>> +	  for example found on NXP's i.MX8 Processors.
>>>>> +
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Makefile b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
>>>>> new file mode 100644
>>>>> index 000000000000..804baf2f1916
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
>>>>> @@ -0,0 +1,4 @@
>>>>> +# SPDX-License-Identifier: GPL-2.0
>>>>> +nwl-mipi-dsi-y := nwl-drv.o nwl-dsi.o
>>>>> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-mipi-dsi.o
>>>>> +header-test-y += nwl-drv.h nwl-dsi.h
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
>>>>> new file mode 100644
>>>>> index 000000000000..9ff43d2de127
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
>>>>> @@ -0,0 +1,499 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>> +/*
>>>>> + * i.MX8 NWL MIPI DSI host driver
>>>>> + *
>>>>> + * Copyright (C) 2017 NXP
>>>>> + * Copyright (C) 2019 Purism SPC
>>>>> + */
>>>>> +
>>>>> +#include <linux/clk.h>
>>>>> +#include <linux/irq.h>
>>>>> +#include <linux/mfd/syscon.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/mux/consumer.h>
>>>>> +#include <linux/of.h>
>>>>> +#include <linux/of_platform.h>
>>>>> +#include <linux/phy/phy.h>
>>>>> +#include <linux/reset.h>
>>>>> +#include <linux/regmap.h>
>>>> Alphabetic order
>>> Fixed for v6.
>>>
>>>>> +#include <linux/sys_soc.h>
>>>>> +
>>>>> +#include <drm/drm_atomic_helper.h>
>>>>> +#include <drm/drm_of.h>
>>>>> +#include <drm/drm_print.h>
>>>>> +#include <drm/drm_probe_helper.h>
>>>>> +
>>>>> +#include "nwl-drv.h"
>>>>> +#include "nwl-dsi.h"
>>>>> +
>>>>> +#define DRV_NAME "nwl-dsi"
>>>>> +
>>>>> +/* Possible platform specific clocks */
>>>>> +#define NWL_DSI_CLK_CORE	"core"
>>>>> +
>>>>> +static const struct regmap_config nwl_dsi_regmap_config = {
>>>>> +	.reg_bits = 16,
>>>>> +	.val_bits = 32,
>>>>> +	.reg_stride = 4,
>>>>> +	.max_register = NWL_DSI_IRQ_MASK2,
>>>>> +	.name = DRV_NAME,
>>>>> +};
>>>> What is the point in using regmap here, why not simple writel/readl.
>>> For me
>>>
>>>     cat /sys/kernel/debug/regmap/30a00000.mipi_dsi-imx-nwl-dsi/registers
>>>
>>> justifies it's use to help debugging problems when e.g. having it
>>> connected to panels I don't own, so I think it's worth keeping if
>>> possible.
>>
>> It still sounds for me like a sledgehammer to crack a nut, but it seems
>> for many developers it is OK, so up to you.
>>
>>
>>>>> +
>>>>> +struct nwl_dsi_platform_data {
>>>>> +	int (*poweron)(struct nwl_dsi *dsi);
>>>>> +	int (*poweroff)(struct nwl_dsi *dsi);
>>>>> +	int (*select_input)(struct nwl_dsi *dsi);
>>>>> +	int (*deselect_input)(struct nwl_dsi *dsi);
>>>>> +	struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
>>>>> +};
>>>> Another construct which do not have justification, at least for now.
>>>> Please simplify the driver, remove callbacks/intermediate
>>>> structs/quirks
>>>>
>>>> - for now they are useless.
>>>>
>>>> Unless there is a serious reason - in such case please describe it in
>>>> comments.
>>> They're needed for i.mx 8QM SoC support (the current driver only
>>> supports the i.mx 8MQ). It will be relatively easy to add with
>>> these so I expect these to show up quickly. I'll add a comment. 
>>
>> That does not work well, it is impossible to review such code without
>> looking at it's usage.
>>
>> It would be better either to add 8QM driver in the same patchset showing
>> the value of these callbacks, either remove them and add later together
>> with 8QM driver.
>>
>> Maybe these callbacks are not needed at all, but without 8QM code it is
>> hard to decide.
> I agree that it's hard to plan in advance until patches show up but let
> me try to make a last argument for keeping them:
>
> - The dsi select into imx8mq_dsi_select_input was suggested by Laurent
>   since they're imx8 specific
> - This would only leave poweron/off which is different between imx8mq
>   and imx8qm:
>
>   https://protect2.fireeye.com/url?k=83f985e06216828d.83f80eaf-93189398e1043f9e&u=https://source.codeaurora.org/external/imx/linux-imx/tree/drivers/gpu/drm/imx/nwl_dsi-imx.c?h=imx_4.14.78_1.0.0_ga#n419
>
> if that's still not worth it, i can drop them making the driver smaller.


With bigger picture it looks better. OK lets leave it then.


>
>> Btw. naming different SoCs 8QM an 8MQ suggests i.mx engineers are quite
>> nasty :)
> It sure is.
>
>>> The quirks on the other hand only apply to some i.mx8MQ mask revisions
>>> so they need to be conditionalized. (or maybe I misunderstood you).
>>
>> I guess at the moment the driver is tested only on one platform - you
>> have not tested platforms with different set of quirks, am I correct?
> I have tested with two silicon mask revisions where one needs the quirks
> and the other doesn't and it behaved as intended.
>
>> If so, the quirk 'infrastructure' is not really tested, driver just
>> anticipates other 8MQ SoCs/platforms. Practice shows that it does not
>> work well - adding support for different revision usually require either
>> more changes either no changes at all, so this pre-mature
>> "diversification" serves nothing except unnecessary noise.
> Does the above make the diversification o.k.?


Yes, of course.


>
>>>>> +
>>>>> +static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge *bridge)
>>>>> +{
>>>>> +	return container_of(bridge, struct nwl_dsi, bridge);
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_set_platform_clocks(struct nwl_dsi *dsi, bool enable)
>>>>> +{
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	const char *id;
>>>>> +	struct clk *clk;
>>>>> +	size_t i;
>>>>> +	unsigned long rate;
>>>>> +	int ret, result = 0;
>>>>> +
>>>>> +	DRM_DEV_DEBUG_DRIVER(dev, "%s platform clocks\n",
>>>>> +			     enable ? "enabling" : "disabling");
>>>>> +	for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
>>>>> +		if (!dsi->clk_config[i].present)
>>>>> +			continue;
>>>>> +		id = dsi->clk_config[i].id;
>>>>> +		clk = dsi->clk_config[i].clk;
>>>>> +
>>>>> +		if (enable) {
>>>>> +			ret = clk_prepare_enable(clk);
>>>>> +			if (ret < 0) {
>>>>> +				DRM_DEV_ERROR(dev,
>>>>> +					      "Failed to enable %s clk: %d\n",
>>>>> +					      id, ret);
>>>>> +				result = result ?: ret;
>>>>> +			}
>>>>> +			rate = clk_get_rate(clk);
>>>>> +			DRM_DEV_DEBUG_DRIVER(dev, "Enabled %s clk @%lu Hz\n",
>>>>> +					     id, rate);
>>>>> +		} else {
>>>>> +			clk_disable_unprepare(clk);
>>>>> +			DRM_DEV_DEBUG_DRIVER(dev, "Disabled %s clk\n", id);
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	return result;
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_plat_enable(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	int ret;
>>>>> +
>>>>> +	if (dsi->pdata->select_input)
>>>>> +		dsi->pdata->select_input(dsi);
>>>>> +
>>>>> +	ret = nwl_dsi_set_platform_clocks(dsi, true);
>>>>> +	if (ret < 0)
>>>>> +		return ret;
>>>>> +
>>>>> +	ret = dsi->pdata->poweron(dsi);
>>>>> +	if (ret < 0)
>>>>> +		DRM_DEV_ERROR(dev, "Failed to power on DSI: %d\n", ret);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_plat_disable(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	dsi->pdata->poweroff(dsi);
>>>>> +	nwl_dsi_set_platform_clocks(dsi, false);
>>>>> +	if (dsi->pdata->deselect_input)
>>>>> +		dsi->pdata->deselect_input(dsi);
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = bridge_to_dsi(bridge);
>>>>> +
>>>>> +	nwl_dsi_disable(dsi);
>>>>> +	nwl_dsi_plat_disable(dsi);
>>>>> +	pm_runtime_put(dsi->dev);
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
>>>>> +				   const struct drm_display_mode *mode,
>>>>> +				   union phy_configure_opts *phy_opts)
>>>>> +{
>>>>> +	unsigned long rate;
>>>>> +	int ret;
>>>>> +
>>>>> +	if (dsi->lanes < 1 || dsi->lanes > 4)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	/*
>>>>> +	 * So far the DPHY spec minimal timings work for both mixel
>>>>> +	 * dphy and nwl dsi host
>>>>> +	 */
>>>>> +	ret = phy_mipi_dphy_get_default_config(
>>>>> +		mode->crtc_clock * 1000,
>>>>> +		mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes,
>>>>> +		&phy_opts->mipi_dphy);
>>>>> +	if (ret < 0)
>>>>> +		return ret;
>>>>> +
>>>>> +	rate = clk_get_rate(dsi->tx_esc_clk);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n", rate);
>>>>> +	phy_opts->mipi_dphy.lp_clk_rate = rate;
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
>>>>> +				      const struct drm_display_mode *mode,
>>>>> +				      struct drm_display_mode *adjusted_mode)
>>>>> +{
>>>>> +	/* At least LCDIF + NWL needs active high sync */
>>>>> +	adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
>>>>> +	adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
>>>>> +
>>>>> +	return true;
>>>>> +}
>>>>> +
>>>>> +static enum drm_mode_status
>>>>> +nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>>>>> +			  const struct drm_display_mode *mode)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = bridge_to_dsi(bridge);
>>>>> +	int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
>>>>> +
>>>>> +	if (mode->clock * bpp > 15000000 * dsi->lanes)
>>>>> +		return MODE_CLOCK_HIGH;
>>>>> +
>>>>> +	if (mode->clock * bpp < 80000 * dsi->lanes)
>>>>> +		return MODE_CLOCK_LOW;
>>>>> +
>>>>> +	return MODE_OK;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
>>>>> +			const struct drm_display_mode *mode,
>>>>> +			const struct drm_display_mode *adjusted_mode)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = bridge_to_dsi(bridge);
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	union phy_configure_opts new_cfg;
>>>>> +	unsigned long phy_ref_rate;
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
>>>>> +	if (ret < 0)
>>>>> +		return;
>>>>> +
>>>>> +	/*
>>>>> +	 * If hs clock is unchanged, we're all good - all parameters are
>>>>> +	 * derived from it atm.
>>>>> +	 */
>>>>> +	if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
>>>>> +		return;
>>>>> +
>>>>> +	phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate);
>>>>> +	/* Save the new desired phy config */
>>>>> +	memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
>>>>> +
>>>>> +	memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
>>>>> +	drm_mode_debug_printmodeline(adjusted_mode);
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = bridge_to_dsi(bridge);
>>>>> +
>>>>> +	pm_runtime_get_sync(dsi->dev);
>>>>> +	nwl_dsi_plat_enable(dsi);
>>>>> +	nwl_dsi_enable(dsi);
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_bridge_attach(struct drm_bridge *bridge)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = bridge->driver_private;
>>>>> +
>>>>> +	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
>>>>> +}
>>>>> +
>>>>> +static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
>>>>> +	.pre_enable = nwl_dsi_bridge_pre_enable,
>>>>> +	.disable    = nwl_dsi_bridge_disable,
>>>>> +	.mode_fixup = nwl_dsi_bridge_mode_fixup,
>>>>> +	.mode_set   = nwl_dsi_bridge_mode_set,
>>>>> +	.mode_valid = nwl_dsi_bridge_mode_valid,
>>>>> +	.attach	    = nwl_dsi_bridge_attach,
>>>>> +};
>>>>> +
>>>>> +static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct platform_device *pdev = to_platform_device(dsi->dev);
>>>>> +	struct clk *clk;
>>>>> +	const char *clk_id;
>>>>> +	void __iomem *base;
>>>>> +	int i, ret;
>>>>> +
>>>>> +	dsi->phy = devm_phy_get(dsi->dev, "dphy");
>>>>> +	if (IS_ERR(dsi->phy)) {
>>>>> +		ret = PTR_ERR(dsi->phy);
>>>>> +		if (ret != -EPROBE_DEFER)
>>>>> +			DRM_DEV_ERROR(dsi->dev, "Could not get PHY: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	/* Platform dependent clocks */
>>>>> +	memcpy(dsi->clk_config, dsi->pdata->clk_config,
>>>>> +	       sizeof(dsi->pdata->clk_config));
>>>>> +
>>>>> +	for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
>>>>> +		if (!dsi->clk_config[i].present)
>>>>> +			continue;
>>>>> +
>>>>> +		clk_id = dsi->clk_config[i].id;
>>>>> +		clk = devm_clk_get(dsi->dev, clk_id);
>>>>> +		if (IS_ERR(clk)) {
>>>>> +			ret = PTR_ERR(clk);
>>>>> +			DRM_DEV_ERROR(dsi->dev, "Failed to get %s clock: %d\n",
>>>>> +				      clk_id, ret);
>>>>> +			return ret;
>>>>> +		}
>>>>> +		DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setup clk %s (rate: %lu)\n",
>>>>> +				     clk_id, clk_get_rate(clk));
>>>>> +		dsi->clk_config[i].clk = clk;
>>>>> +	}
>>>>> +
>>>>> +	/* DSI clocks */
>>>>> +	clk = devm_clk_get(dsi->dev, "phy_ref");
>>>>> +	if (IS_ERR(clk)) {
>>>>> +		ret = PTR_ERR(clk);
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock: %d\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	dsi->phy_ref_clk = clk;
>>>>> +
>>>>> +	clk = devm_clk_get(dsi->dev, "rx_esc");
>>>>> +	if (IS_ERR(clk)) {
>>>>> +		ret = PTR_ERR(clk);
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock: %d\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	dsi->rx_esc_clk = clk;
>>>>> +
>>>>> +	clk = devm_clk_get(dsi->dev, "tx_esc");
>>>>> +	if (IS_ERR(clk)) {
>>>>> +		ret = PTR_ERR(clk);
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock: %d\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	dsi->tx_esc_clk = clk;
>>>>> +
>>>>> +	dsi->mux = devm_mux_control_get(dsi->dev, NULL);
>>>>> +	if (IS_ERR(dsi->mux)) {
>>>>> +		ret = PTR_ERR(dsi->mux);
>>>>> +		if (ret != -EPROBE_DEFER)
>>>>> +			DRM_DEV_ERROR(dsi->dev, "Failed to get mux: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	base = devm_platform_ioremap_resource(pdev, 0);
>>>>> +	if (IS_ERR(base))
>>>>> +		return PTR_ERR(base);
>>>>> +
>>>>> +	dsi->regmap =
>>>>> +		devm_regmap_init_mmio(dsi->dev, base, &nwl_dsi_regmap_config);
>>>>> +	if (IS_ERR(dsi->regmap)) {
>>>>> +		ret = PTR_ERR(dsi->regmap);
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI regmap: %d\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	dsi->irq = platform_get_irq(pdev, 0);
>>>>> +	if (dsi->irq < 0) {
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ: %d\n",
>>>>> +			      dsi->irq);
>>>>> +		return dsi->irq;
>>>>> +	}
>>>>> +
>>>>> +	dsi->rstc = devm_reset_control_array_get(dsi->dev, false, true);
>>>>> +	if (IS_ERR(dsi->rstc)) {
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to get resets: %ld\n",
>>>>> +			      PTR_ERR(dsi->rstc));
>>>>> +		return PTR_ERR(dsi->rstc);
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int imx8mq_dsi_select_input(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct device_node *remote;
>>>>> +	u32 use_dcss = 1;
>>>>> +	int ret;
>>>>> +
>>>>> +	remote = of_graph_get_remote_node(dsi->dev->of_node, 0, 0);
>>>>> +	if (strcmp(remote->name, "lcdif") == 0)
>>>>> +		use_dcss = 0;
>>>> Relying on node name seems to me wrong. I am not sure if whole logic for
>>>> input select should be here.
>>>>
>>>> My 1st impression is that selecting should be done rather in DCSS or
>>>> LCDIF driver, why do you want to put it here?
>>> Doing it in here keeps it at a single location where on the other hand
>>> it would need to be done in mxsfb (which handles other SoCs as well) and
>>> upcoming dcss. Also we can have in the dsi enable path which e.g. mxsfb
>>> doesn't even know about at this point.
>>
>> But as I understand mux is not a part of this IP. It is always
>> problematic what to do with such small pieces of hw.
>>
>> Let's keep it here until someone find better solution.
>>
>> Btw do you have public tree for testing platforms with this driver, I am
>> curious about this mux device.
> https://protect2.fireeye.com/url?k=05ccbdf61eba9fd2.05cd36b9-6ae2ecc63ea6128d&u=https://source.puri.sm/guido.gunther/linux-imx8/blob/forward-upstream/next-20190823/mxsfb+nwl/v4/arch/arm64/boot/dts/freescale/imx8mq.dtsi#L477
>
> it was prompted by
>
> https://protect2.fireeye.com/url?k=342f39cc74442e56.342eb283-377419f62357e53d&u=https://lists.freedesktop.org/archives/dri-devel/2019-August/230868.html


Thx for links here and above, they better prove value of your code.


Regards

Andrzej


>
> Cheers and thanks again for your comments,
>  -- Guido
>
>>
>> Regards
>>
>> Andrzej
>>
>>
>>> Cheers,
>>>  -- Guido
>>>
>>>> Regards
>>>>
>>>> Andrzej
>>>>
>>>>
>>>>> +
>>>>> +	DRM_DEV_INFO(dsi->dev, "Using %s as input source\n",
>>>>> +		     (use_dcss) ? "DCSS" : "LCDIF");
>>>>> +
>>>>> +	ret = mux_control_try_select(dsi->mux, use_dcss);
>>>>> +	if (ret < 0)
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n", ret);
>>>>> +
>>>>> +	of_node_put(remote);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static int imx8mq_dsi_deselect_input(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = mux_control_deselect(dsi->mux);
>>>>> +	if (ret < 0)
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to deselect input: %d\n", ret);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static int imx8mq_dsi_poweron(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	/* otherwise the display stays blank */
>>>>> +	usleep_range(200, 300);
>>>>> +
>>>>> +	if (dsi->rstc)
>>>>> +		ret = reset_control_deassert(dsi->rstc);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int imx8mq_dsi_poweroff(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	if (dsi->quirks & SRC_RESET_QUIRK)
>>>>> +		return 0;
>>>>> +
>>>>> +	if (dsi->rstc)
>>>>> +		ret = reset_control_assert(dsi->rstc);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static const struct drm_bridge_timings nwl_dsi_timings = {
>>>>> +	.input_bus_flags = DRM_BUS_FLAG_DE_LOW,
>>>>> +};
>>>>> +
>>>>> +static const struct nwl_dsi_platform_data imx8mq_dev = {
>>>>> +	.poweron = &imx8mq_dsi_poweron,
>>>>> +	.poweroff = &imx8mq_dsi_poweroff,
>>>>> +	.select_input = &imx8mq_dsi_select_input,
>>>>> +	.deselect_input = &imx8mq_dsi_deselect_input,
>>>>> +	.clk_config = {
>>>>> +		{ .id = NWL_DSI_CLK_CORE, .present = true },
>>>>> +	},
>>>>> +};
>>>>> +
>>>>> +static const struct of_device_id nwl_dsi_dt_ids[] = {
>>>>> +	{ .compatible = "fsl,imx8mq-nwl-dsi", .data = &imx8mq_dev, },
>>>>> +	{ /* sentinel */ }
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids);
>>>>> +
>>>>> +static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
>>>>> +	{ .soc_id = "i.MX8MQ", .revision = "2.0",
>>>>> +	  .data = (void *)(E11418_HS_MODE_QUIRK | SRC_RESET_QUIRK) },
>>>>> +	{ /* sentinel. */ },
>>>>> +};
>>>>> +
>>>>> +static int nwl_dsi_probe(struct platform_device *pdev)
>>>>> +{
>>>>> +	struct device *dev = &pdev->dev;
>>>>> +	const struct of_device_id *of_id = of_match_device(nwl_dsi_dt_ids, dev);
>>>>> +	const struct nwl_dsi_platform_data *pdata = of_id->data;
>>>>> +	const struct soc_device_attribute *attr;
>>>>> +	struct nwl_dsi *dsi;
>>>>> +	int ret;
>>>>> +
>>>>> +	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
>>>>> +	if (!dsi)
>>>>> +		return -ENOMEM;
>>>>> +
>>>>> +	dsi->dev = dev;
>>>>> +	dsi->pdata = pdata;
>>>>> +
>>>>> +	ret = nwl_dsi_parse_dt(dsi);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	ret = devm_request_irq(dev, dsi->irq, nwl_dsi_irq_handler, 0,
>>>>> +			       dev_name(dev), dsi);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n", dsi->irq,
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	dsi->dsi_host.ops = &nwl_dsi_host_ops;
>>>>> +	dsi->dsi_host.dev = dev;
>>>>> +	ret = mipi_dsi_host_register(&dsi->dsi_host);
>>>>> +	if (ret) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	attr = soc_device_match(nwl_dsi_quirks_match);
>>>>> +	if (attr)
>>>>> +		dsi->quirks = (uintptr_t)attr->data;
>>>>> +
>>>>> +	dsi->bridge.driver_private = dsi;
>>>>> +	dsi->bridge.funcs = &nwl_dsi_bridge_funcs;
>>>>> +	dsi->bridge.of_node = dev->of_node;
>>>>> +	dsi->bridge.timings = &nwl_dsi_timings;
>>>>> +
>>>>> +	dev_set_drvdata(dev, dsi);
>>>>> +	pm_runtime_enable(dev);
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = platform_get_drvdata(pdev);
>>>>> +
>>>>> +	mipi_dsi_host_unregister(&dsi->dsi_host);
>>>>> +	pm_runtime_disable(&pdev->dev);
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static struct platform_driver nwl_dsi_driver = {
>>>>> +	.probe		= nwl_dsi_probe,
>>>>> +	.remove		= nwl_dsi_remove,
>>>>> +	.driver		= {
>>>>> +		.of_match_table = nwl_dsi_dt_ids,
>>>>> +		.name	= DRV_NAME,
>>>>> +	},
>>>>> +};
>>>>> +
>>>>> +module_platform_driver(nwl_dsi_driver);
>>>>> +
>>>>> +MODULE_AUTHOR("NXP Semiconductor");
>>>>> +MODULE_AUTHOR("Purism SPC");
>>>>> +MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver");
>>>>> +MODULE_LICENSE("GPL"); /* GPLv2 or later */
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
>>>>> new file mode 100644
>>>>> index 000000000000..1e72a9221401
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
>>>>> @@ -0,0 +1,65 @@
>>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>>> +/*
>>>>> + * NWL MIPI DSI host driver
>>>>> + *
>>>>> + * Copyright (C) 2017 NXP
>>>>> + * Copyright (C) 2019 Purism SPC
>>>>> + */
>>>>> +
>>>>> +#ifndef __NWL_DRV_H__
>>>>> +#define __NWL_DRV_H__
>>>>> +
>>>>> +#include <linux/mux/consumer.h>
>>>>> +#include <linux/phy/phy.h>
>>>>> +
>>>>> +#include <drm/drm_bridge.h>
>>>>> +#include <drm/drm_mipi_dsi.h>
>>>>> +
>>>>> +struct nwl_dsi_platform_data;
>>>>> +
>>>>> +/* i.MX8 NWL quirks */
>>>>> +/* i.MX8MQ errata E11418 */
>>>>> +#define E11418_HS_MODE_QUIRK	    BIT(0)
>>>>> +/* Skip DSI bits in SRC on disable to avoid blank display on enable */
>>>>> +#define SRC_RESET_QUIRK		    BIT(1)
>>>>> +
>>>>> +#define NWL_DSI_MAX_PLATFORM_CLOCKS 1
>>>>> +struct nwl_dsi_plat_clk_config {
>>>>> +	const char *id;
>>>>> +	struct clk *clk;
>>>>> +	bool present;
>>>>> +};
>>>>> +
>>>>> +struct nwl_dsi {
>>>>> +	struct drm_bridge bridge;
>>>>> +	struct mipi_dsi_host dsi_host;
>>>>> +	struct drm_bridge *panel_bridge;
>>>>> +	struct device *dev;
>>>>> +	struct phy *phy;
>>>>> +	union phy_configure_opts phy_cfg;
>>>>> +	unsigned int quirks;
>>>>> +
>>>>> +	struct regmap *regmap;
>>>>> +	int irq;
>>>>> +	struct reset_control *rstc;
>>>>> +	struct mux_control *mux;
>>>>> +
>>>>> +	/* DSI clocks */
>>>>> +	struct clk *phy_ref_clk;
>>>>> +	struct clk *rx_esc_clk;
>>>>> +	struct clk *tx_esc_clk;
>>>>> +	/* Platform dependent clocks */
>>>>> +	struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
>>>>> +
>>>>> +	/* dsi lanes */
>>>>> +	u32 lanes;
>>>>> +	enum mipi_dsi_pixel_format format;
>>>>> +	struct drm_display_mode mode;
>>>>> +	unsigned long dsi_mode_flags;
>>>>> +
>>>>> +	struct nwl_dsi_transfer *xfer;
>>>>> +
>>>>> +	const struct nwl_dsi_platform_data *pdata;
>>>>> +};
>>>>> +
>>>>> +#endif /* __NWL_DRV_H__ */
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
>>>>> new file mode 100644
>>>>> index 000000000000..e6038cb4e849
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
>>>>> @@ -0,0 +1,696 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>> +/*
>>>>> + * NWL MIPI DSI host driver
>>>>> + *
>>>>> + * Copyright (C) 2017 NXP
>>>>> + * Copyright (C) 2019 Purism SPC
>>>>> + */
>>>>> +
>>>>> +#include <linux/bitfield.h>
>>>>> +#include <linux/clk.h>
>>>>> +#include <linux/irq.h>
>>>>> +#include <linux/math64.h>
>>>>> +#include <linux/regmap.h>
>>>>> +#include <linux/time64.h>
>>>>> +
>>>>> +#include <video/mipi_display.h>
>>>>> +#include <video/videomode.h>
>>>>> +
>>>>> +#include <drm/drm_atomic_helper.h>
>>>>> +#include <drm/drm_crtc_helper.h>
>>>>> +#include <drm/drm_of.h>
>>>>> +#include <drm/drm_panel.h>
>>>>> +#include <drm/drm_print.h>
>>>>> +
>>>>> +#include "nwl-drv.h"
>>>>> +#include "nwl-dsi.h"
>>>>> +
>>>>> +#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
>>>>> +
>>>>> +/*
>>>>> + * PKT_CONTROL format:
>>>>> + * [15: 0] - word count
>>>>> + * [17:16] - virtual channel
>>>>> + * [23:18] - data type
>>>>> + * [24]	   - LP or HS select (0 - LP, 1 - HS)
>>>>> + * [25]	   - perform BTA after packet is sent
>>>>> + * [26]	   - perform BTA only, no packet tx
>>>>> + */
>>>>> +#define NWL_DSI_WC(x)		FIELD_PREP(GENMASK(15, 0), (x))
>>>>> +#define NWL_DSI_TX_VC(x)	FIELD_PREP(GENMASK(17, 16), (x))
>>>>> +#define NWL_DSI_TX_DT(x)	FIELD_PREP(GENMASK(23, 18), (x))
>>>>> +#define NWL_DSI_HS_SEL(x)	FIELD_PREP(GENMASK(24, 24), (x))
>>>>> +#define NWL_DSI_BTA_TX(x)	FIELD_PREP(GENMASK(25, 25), (x))
>>>>> +#define NWL_DSI_BTA_NO_TX(x)	FIELD_PREP(GENMASK(26, 26), (x))
>>>>> +
>>>>> +/*
>>>>> + * RX_PKT_HEADER format:
>>>>> + * [15: 0] - word count
>>>>> + * [21:16] - data type
>>>>> + * [23:22] - virtual channel
>>>>> + */
>>>>> +#define NWL_DSI_RX_DT(x)	FIELD_GET(GENMASK(21, 16), (x))
>>>>> +#define NWL_DSI_RX_VC(x)	FIELD_GET(GENMASK(23, 22), (x))
>>>>> +
>>>>> +/* DSI Video mode */
>>>>> +#define NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES		0
>>>>> +#define NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS	BIT(0)
>>>>> +#define NWL_DSI_VM_BURST_MODE				BIT(1)
>>>>> +
>>>>> +/* * DPI color coding */
>>>>> +#define NWL_DSI_DPI_16_BIT_565_PACKED	0
>>>>> +#define NWL_DSI_DPI_16_BIT_565_ALIGNED	1
>>>>> +#define NWL_DSI_DPI_16_BIT_565_SHIFTED	2
>>>>> +#define NWL_DSI_DPI_18_BIT_PACKED	3
>>>>> +#define NWL_DSI_DPI_18_BIT_ALIGNED	4
>>>>> +#define NWL_DSI_DPI_24_BIT		5
>>>>> +
>>>>> +/* * DPI Pixel format */
>>>>> +#define NWL_DSI_PIXEL_FORMAT_16  0
>>>>> +#define NWL_DSI_PIXEL_FORMAT_18  BIT(0)
>>>>> +#define NWL_DSI_PIXEL_FORMAT_18L BIT(1)
>>>>> +#define NWL_DSI_PIXEL_FORMAT_24  (BIT(0) | BIT(1))
>>>>> +
>>>>> +enum transfer_direction {
>>>>> +	DSI_PACKET_SEND,
>>>>> +	DSI_PACKET_RECEIVE,
>>>>> +};
>>>>> +
>>>>> +struct nwl_dsi_transfer {
>>>>> +	const struct mipi_dsi_msg *msg;
>>>>> +	struct mipi_dsi_packet packet;
>>>>> +	struct completion completed;
>>>>> +
>>>>> +	int status; /* status of transmission */
>>>>> +	enum transfer_direction direction;
>>>>> +	bool need_bta;
>>>>> +	u8 cmd;
>>>>> +	u16 rx_word_count;
>>>>> +	size_t tx_len; /* in bytes */
>>>>> +	size_t rx_len; /* in bytes */
>>>>> +};
>>>>> +
>>>>> +static int nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32 val)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = regmap_write(dsi->regmap, reg, val);
>>>>> +	if (ret < 0)
>>>>> +		DRM_DEV_ERROR(dsi->dev,
>>>>> +			      "Failed to write NWL DSI reg 0x%x: %d\n", reg,
>>>>> +			      ret);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg)
>>>>> +{
>>>>> +	unsigned int val;
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = regmap_read(dsi->regmap, reg, &val);
>>>>> +	if (ret < 0)
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg 0x%x: %d\n",
>>>>> +			      reg, ret);
>>>>> +
>>>>> +	return val;
>>>>> +}
>>>>> +
>>>>> +static u32 nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format format)
>>>>> +{
>>>>> +	switch (format) {
>>>>> +	case MIPI_DSI_FMT_RGB565:
>>>>> +		return NWL_DSI_PIXEL_FORMAT_16;
>>>>> +	case MIPI_DSI_FMT_RGB666:
>>>>> +		return NWL_DSI_PIXEL_FORMAT_18L;
>>>>> +	case MIPI_DSI_FMT_RGB666_PACKED:
>>>>> +		return NWL_DSI_PIXEL_FORMAT_18;
>>>>> +	case MIPI_DSI_FMT_RGB888:
>>>>> +		return NWL_DSI_PIXEL_FORMAT_24;
>>>>> +	default:
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * ps2bc - Picoseconds to byte clock cycles
>>>>> + */
>>>>> +static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps)
>>>>> +{
>>>>> +	u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
>>>>> +
>>>>> +	return DIV64_U64_ROUND_UP(ps * dsi->mode.clock * bpp,
>>>>> +				  dsi->lanes * 8 * NSEC_PER_SEC);
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * ui2bc - UI time periods to byte clock cycles
>>>>> + */
>>>>> +static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
>>>>> +{
>>>>> +	u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
>>>>> +
>>>>> +	return DIV64_U64_ROUND_UP(ui * dsi->lanes,
>>>>> +				  dsi->mode.clock * 1000 * bpp);
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * us2bc - micro seconds to lp clock cycles
>>>>> + */
>>>>> +static u32 us2lp(u32 lp_clk_rate, unsigned long us)
>>>>> +{
>>>>> +	return DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC);
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_config_host(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	u32 cycles;
>>>>> +	struct phy_configure_opts_mipi_dphy *cfg = &dsi->phy_cfg.mipi_dphy;
>>>>> +
>>>>> +	if (dsi->lanes < 1 || dsi->lanes > 4)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n", dsi->lanes);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, dsi->lanes - 1);
>>>>> +
>>>>> +	if (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x01);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x01);
>>>>> +	} else {
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x00);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x00);
>>>>> +	}
>>>>> +
>>>>> +	/* values in byte clock cycles */
>>>>> +	cycles = ui2bc(dsi, cfg->clk_pre);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
>>>>> +	cycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg->clk_zero);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n", cycles);
>>>>> +	cycles += ui2bc(dsi, cfg->clk_pre);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_post: 0x%x\n", cycles);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
>>>>> +	cycles = ps2bc(dsi, cfg->hs_exit);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, cycles);
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, 0x01);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, 0x00);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, 0x00);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, 0x00);
>>>>> +	/* In LP clock cycles */
>>>>> +	cycles = us2lp(cfg->lp_clk_rate, cfg->wakeup);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n", cycles);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, cycles);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	u32 color_format, mode;
>>>>> +	bool burst_mode;
>>>>> +	int hfront_porch, hback_porch, vfront_porch, vback_porch;
>>>>> +	int hsync_len, vsync_len;
>>>>> +
>>>>> +	hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
>>>>> +	hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
>>>>> +	hback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
>>>>> +
>>>>> +	vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
>>>>> +	vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
>>>>> +	vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
>>>>> +
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n", hfront_porch);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n", hback_porch);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n", hsync_len);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi->mode.hdisplay);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n", vfront_porch);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n", vback_porch);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n", vsync_len);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi->mode.vdisplay);
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi->mode.clock);
>>>>> +
>>>>> +	color_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
>>>>> +	if (color_format < 0) {
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Invalid color format 0x%x\n",
>>>>> +			      dsi->format);
>>>>> +		return color_format;
>>>>> +	}
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi->format);
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING, NWL_DSI_DPI_24_BIT);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
>>>>> +	/*
>>>>> +	 * Adjusting input polarity based on the video mode results in
>>>>> +	 * a black screen so always pick active low:
>>>>> +	 */
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
>>>>> +		      NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
>>>>> +		      NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
>>>>> +
>>>>> +	burst_mode = (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_BURST) &&
>>>>> +		     !(dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
>>>>> +
>>>>> +	if (burst_mode) {
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, NWL_DSI_VM_BURST_MODE);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, 256);
>>>>> +	} else {
>>>>> +		mode = ((dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
>>>>> +				NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES :
>>>>> +				NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
>>>>> +			      dsi->mode.hdisplay);
>>>>> +	}
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi->mode.hdisplay);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	u32 irq_enable;
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
>>>>> +
>>>>> +	irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
>>>>> +			    NWL_DSI_RX_PKT_HDR_RCVD_MASK |
>>>>> +			    NWL_DSI_TX_FIFO_OVFLW_MASK |
>>>>> +			    NWL_DSI_HS_TX_TIMEOUT_MASK);
>>>>> +
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host,
>>>>> +			       struct mipi_dsi_device *device)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	struct drm_bridge *bridge;
>>>>> +	struct drm_panel *panel;
>>>>> +	int ret;
>>>>> +
>>>>> +	DRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n", device->lanes,
>>>>> +		     device->format, device->mode_flags);
>>>>> +
>>>>> +	if (device->lanes < 1 || device->lanes > 4)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	dsi->lanes = device->lanes;
>>>>> +	dsi->format = device->format;
>>>>> +	dsi->dsi_mode_flags = device->mode_flags;
>>>>> +
>>>>> +	ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0, &panel,
>>>>> +					  &bridge);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	if (panel) {
>>>>> +		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
>>>>> +		if (IS_ERR(bridge))
>>>>> +			return PTR_ERR(bridge);
>>>>> +	}
>>>>> +
>>>>> +	dsi->panel_bridge = bridge;
>>>>> +	drm_bridge_add(&dsi->bridge);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int nwl_dsi_host_detach(struct mipi_dsi_host *dsi_host,
>>>>> +			       struct mipi_dsi_device *device)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
>>>>> +
>>>>> +	drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
>>>>> +	drm_bridge_remove(&dsi->bridge);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status)
>>>>> +{
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	struct nwl_dsi_transfer *xfer = dsi->xfer;
>>>>> +	u8 *payload = xfer->msg->rx_buf;
>>>>> +	u32 val;
>>>>> +	u16 word_count;
>>>>> +	u8 channel;
>>>>> +	u8 data_type;
>>>>> +
>>>>> +	xfer->status = 0;
>>>>> +
>>>>> +	if (xfer->rx_word_count == 0) {
>>>>> +		if (!(status & NWL_DSI_RX_PKT_HDR_RCVD))
>>>>> +			return false;
>>>>> +		/* Get the RX header and parse it */
>>>>> +		val = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER);
>>>>> +		word_count = NWL_DSI_WC(val);
>>>>> +		channel = NWL_DSI_RX_VC(val);
>>>>> +		data_type = NWL_DSI_RX_DT(val);
>>>>> +
>>>>> +		if (channel != xfer->msg->channel) {
>>>>> +			DRM_DEV_ERROR(dev,
>>>>> +				      "[%02X] Channel mismatch (%u != %u)\n",
>>>>> +				      xfer->cmd, channel, xfer->msg->channel);
>>>>> +			return true;
>>>>> +		}
>>>>> +
>>>>> +		switch (data_type) {
>>>>> +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
>>>>> +			/* Fall through */
>>>>> +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
>>>>> +			if (xfer->msg->rx_len > 1) {
>>>>> +				/* read second byte */
>>>>> +				payload[1] = word_count >> 8;
>>>>> +				++xfer->rx_len;
>>>>> +			}
>>>>> +			/* Fall through */
>>>>> +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
>>>>> +			/* Fall through */
>>>>> +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
>>>>> +			if (xfer->msg->rx_len > 0) {
>>>>> +				/* read first byte */
>>>>> +				payload[0] = word_count & 0xff;
>>>>> +				++xfer->rx_len;
>>>>> +			}
>>>>> +			xfer->status = xfer->rx_len;
>>>>> +			return true;
>>>>> +		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
>>>>> +			word_count &= 0xff;
>>>>> +			DRM_DEV_ERROR(dev, "[%02X] DSI error report: 0x%02x\n",
>>>>> +				      xfer->cmd, word_count);
>>>>> +			xfer->status = -EPROTO;
>>>>> +			return true;
>>>>> +		}
>>>>> +
>>>>> +		if (word_count > xfer->msg->rx_len) {
>>>>> +			DRM_DEV_ERROR(
>>>>> +				dev,
>>>>> +				"[%02X] Receive buffer too small: %zu (< %u)\n",
>>>>> +				xfer->cmd, xfer->msg->rx_len, word_count);
>>>>> +			return true;
>>>>> +		}
>>>>> +
>>>>> +		xfer->rx_word_count = word_count;
>>>>> +	} else {
>>>>> +		/* Set word_count from previous header read */
>>>>> +		word_count = xfer->rx_word_count;
>>>>> +	}
>>>>> +
>>>>> +	/* If RX payload is not yet received, wait for it */
>>>>> +	if (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD))
>>>>> +		return false;
>>>>> +
>>>>> +	/* Read the RX payload */
>>>>> +	while (word_count >= 4) {
>>>>> +		val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
>>>>> +		payload[0] = (val >> 0) & 0xff;
>>>>> +		payload[1] = (val >> 8) & 0xff;
>>>>> +		payload[2] = (val >> 16) & 0xff;
>>>>> +		payload[3] = (val >> 24) & 0xff;
>>>>> +		payload += 4;
>>>>> +		xfer->rx_len += 4;
>>>>> +		word_count -= 4;
>>>>> +	}
>>>>> +
>>>>> +	if (word_count > 0) {
>>>>> +		val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
>>>>> +		switch (word_count) {
>>>>> +		case 3:
>>>>> +			payload[2] = (val >> 16) & 0xff;
>>>>> +			++xfer->rx_len;
>>>>> +			/* Fall through */
>>>>> +		case 2:
>>>>> +			payload[1] = (val >> 8) & 0xff;
>>>>> +			++xfer->rx_len;
>>>>> +			/* Fall through */
>>>>> +		case 1:
>>>>> +			payload[0] = (val >> 0) & 0xff;
>>>>> +			++xfer->rx_len;
>>>>> +			break;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	xfer->status = xfer->rx_len;
>>>>> +
>>>>> +	return true;
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32 status)
>>>>> +{
>>>>> +	struct nwl_dsi_transfer *xfer = dsi->xfer;
>>>>> +	bool end_packet = false;
>>>>> +
>>>>> +	if (!xfer)
>>>>> +		return;
>>>>> +
>>>>> +	if (xfer->direction == DSI_PACKET_SEND &&
>>>>> +	    status & NWL_DSI_TX_PKT_DONE) {
>>>>> +		xfer->status = xfer->tx_len;
>>>>> +		end_packet = true;
>>>>> +	} else if (status & NWL_DSI_DPHY_DIRECTION &&
>>>>> +		   ((status & (NWL_DSI_RX_PKT_HDR_RCVD |
>>>>> +			       NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) {
>>>>> +		end_packet = nwl_dsi_read_packet(dsi, status);
>>>>> +	}
>>>>> +
>>>>> +	if (end_packet)
>>>>> +		complete(&xfer->completed);
>>>>> +}
>>>>> +
>>>>> +static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct nwl_dsi_transfer *xfer = dsi->xfer;
>>>>> +	struct mipi_dsi_packet *pkt = &xfer->packet;
>>>>> +	const u8 *payload;
>>>>> +	size_t length;
>>>>> +	u16 word_count;
>>>>> +	u8 hs_mode;
>>>>> +	u32 val;
>>>>> +	u32 hs_workaround = 0;
>>>>> +
>>>>> +	/* Send the payload, if any */
>>>>> +	length = pkt->payload_length;
>>>>> +	payload = pkt->payload;
>>>>> +
>>>>> +	while (length >= 4) {
>>>>> +		val = *(u32 *)payload;
>>>>> +		hs_workaround |= !(val & 0xFFFF00);
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
>>>>> +		payload += 4;
>>>>> +		length -= 4;
>>>>> +	}
>>>>> +	/* Send the rest of the payload */
>>>>> +	val = 0;
>>>>> +	switch (length) {
>>>>> +	case 3:
>>>>> +		val |= payload[2] << 16;
>>>>> +		/* Fall through */
>>>>> +	case 2:
>>>>> +		val |= payload[1] << 8;
>>>>> +		hs_workaround |= !(val & 0xFFFF00);
>>>>> +		/* Fall through */
>>>>> +	case 1:
>>>>> +		val |= payload[0];
>>>>> +		nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
>>>>> +		break;
>>>>> +	}
>>>>> +	xfer->tx_len = pkt->payload_length;
>>>>> +
>>>>> +	/*
>>>>> +	 * Send the header
>>>>> +	 * header[0] = Virtual Channel + Data Type
>>>>> +	 * header[1] = Word Count LSB (LP) or first param (SP)
>>>>> +	 * header[2] = Word Count MSB (LP) or second param (SP)
>>>>> +	 */
>>>>> +	word_count = pkt->header[1] | (pkt->header[2] << 8);
>>>>> +	if (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) {
>>>>> +		DRM_DEV_DEBUG_DRIVER(dsi->dev,
>>>>> +				     "Using hs mode workaround for cmd 0x%x\n",
>>>>> +				     xfer->cmd);
>>>>> +		hs_mode = 1;
>>>>> +	} else {
>>>>> +		hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : 1;
>>>>> +	}
>>>>> +	val = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg->channel) |
>>>>> +	      NWL_DSI_TX_DT(xfer->msg->type) | NWL_DSI_HS_SEL(hs_mode) |
>>>>> +	      NWL_DSI_BTA_TX(xfer->need_bta);
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val);
>>>>> +
>>>>> +	/* Send packet command */
>>>>> +	nwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, 0x1);
>>>>> +}
>>>>> +
>>>>> +static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host,
>>>>> +				     const struct mipi_dsi_msg *msg)
>>>>> +{
>>>>> +	struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
>>>>> +	struct nwl_dsi_transfer xfer;
>>>>> +	ssize_t ret = 0;
>>>>> +
>>>>> +	/* Create packet to be sent */
>>>>> +	dsi->xfer = &xfer;
>>>>> +	ret = mipi_dsi_create_packet(&xfer.packet, msg);
>>>>> +	if (ret < 0) {
>>>>> +		dsi->xfer = NULL;
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	if ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM ||
>>>>> +	     msg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM ||
>>>>> +	     msg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM ||
>>>>> +	     msg->type & MIPI_DSI_DCS_READ) &&
>>>>> +	    msg->rx_len > 0 && msg->rx_buf != NULL)
>>>>> +		xfer.direction = DSI_PACKET_RECEIVE;
>>>>> +	else
>>>>> +		xfer.direction = DSI_PACKET_SEND;
>>>>> +
>>>>> +	xfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE);
>>>>> +	xfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0;
>>>>> +	xfer.msg = msg;
>>>>> +	xfer.status = -ETIMEDOUT;
>>>>> +	xfer.rx_word_count = 0;
>>>>> +	xfer.rx_len = 0;
>>>>> +	xfer.cmd = 0x00;
>>>>> +	if (msg->tx_len > 0)
>>>>> +		xfer.cmd = ((u8 *)(msg->tx_buf))[0];
>>>>> +	init_completion(&xfer.completed);
>>>>> +
>>>>> +	ret = clk_prepare_enable(dsi->rx_esc_clk);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk: %zd\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu Hz\n",
>>>>> +			     clk_get_rate(dsi->rx_esc_clk));
>>>>> +
>>>>> +	/* Initiate the DSI packet transmision */
>>>>> +	nwl_dsi_begin_transmission(dsi);
>>>>> +
>>>>> +	if (!wait_for_completion_timeout(&xfer.completed,
>>>>> +					 NWL_DSI_MIPI_FIFO_TIMEOUT)) {
>>>>> +		DRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer timed out\n",
>>>>> +			      xfer.cmd);
>>>>> +		ret = -ETIMEDOUT;
>>>>> +	} else {
>>>>> +		ret = xfer.status;
>>>>> +	}
>>>>> +
>>>>> +	clk_disable_unprepare(dsi->rx_esc_clk);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +const struct mipi_dsi_host_ops nwl_dsi_host_ops = {
>>>>> +	.attach = nwl_dsi_host_attach,
>>>>> +	.detach = nwl_dsi_host_detach,
>>>>> +	.transfer = nwl_dsi_host_transfer,
>>>>> +};
>>>>> +
>>>>> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data)
>>>>> +{
>>>>> +	u32 irq_status;
>>>>> +	struct nwl_dsi *dsi = data;
>>>>> +
>>>>> +	irq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS);
>>>>> +
>>>>> +	if (irq_status & NWL_DSI_TX_FIFO_OVFLW)
>>>>> +		DRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo overflow\n");
>>>>> +
>>>>> +	if (irq_status & NWL_DSI_HS_TX_TIMEOUT)
>>>>> +		DRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx timeout\n");
>>>>> +
>>>>> +	if (irq_status & NWL_DSI_TX_PKT_DONE ||
>>>>> +	    irq_status & NWL_DSI_RX_PKT_HDR_RCVD ||
>>>>> +	    irq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)
>>>>> +		nwl_dsi_finish_transmission(dsi, irq_status);
>>>>> +
>>>>> +	return IRQ_HANDLED;
>>>>> +}
>>>>> +
>>>>> +int nwl_dsi_enable(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct device *dev = dsi->dev;
>>>>> +	union phy_configure_opts *phy_cfg = &dsi->phy_cfg;
>>>>> +	int ret;
>>>>> +
>>>>> +	if (!dsi->lanes) {
>>>>> +		DRM_DEV_ERROR(dev, "Need DSI lanes: %d\n", dsi->lanes);
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	ret = phy_init(dsi->phy);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	ret = phy_configure(dsi->phy, phy_cfg);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	ret = clk_prepare_enable(dsi->tx_esc_clk);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk: %d\n",
>>>>> +			      ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu Hz\n",
>>>>> +			     clk_get_rate(dsi->tx_esc_clk));
>>>>> +
>>>>> +	ret = nwl_dsi_config_host(dsi);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to set up DSI: %d", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	ret = nwl_dsi_config_dpi(dsi);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to set up DPI: %d", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	ret = phy_power_on(dsi->phy);
>>>>> +	if (ret < 0) {
>>>>> +		DRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	nwl_dsi_init_interrupts(dsi);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +int nwl_dsi_disable(struct nwl_dsi *dsi)
>>>>> +{
>>>>> +	struct device *dev = dsi->dev;
>>>>> +
>>>>> +	DRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n");
>>>>> +
>>>>> +	phy_power_off(dsi->phy);
>>>>> +	phy_exit(dsi->phy);
>>>>> +
>>>>> +	/* Disabling the clock before the phy breaks enabling dsi again */
>>>>> +	clk_disable_unprepare(dsi->tx_esc_clk);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
>>>>> new file mode 100644
>>>>> index 000000000000..579b366de652
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
>>>>> @@ -0,0 +1,112 @@
>>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>>> +/*
>>>>> + * NWL MIPI DSI host driver
>>>>> + *
>>>>> + * Copyright (C) 2017 NXP
>>>>> + * Copyright (C) 2019 Purism SPC
>>>>> + */
>>>>> +#ifndef __NWL_DSI_H__
>>>>> +#define __NWL_DSI_H__
>>>>> +
>>>>> +#include <linux/irqreturn.h>
>>>>> +
>>>>> +#include <drm/drm_mipi_dsi.h>
>>>>> +
>>>>> +#include "nwl-drv.h"
>>>>> +
>>>>> +/* DSI HOST registers */
>>>>> +#define NWL_DSI_CFG_NUM_LANES			0x0
>>>>> +#define NWL_DSI_CFG_NONCONTINUOUS_CLK		0x4
>>>>> +#define NWL_DSI_CFG_T_PRE			0x8
>>>>> +#define NWL_DSI_CFG_T_POST			0xc
>>>>> +#define NWL_DSI_CFG_TX_GAP			0x10
>>>>> +#define NWL_DSI_CFG_AUTOINSERT_EOTP		0x14
>>>>> +#define NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP	0x18
>>>>> +#define NWL_DSI_CFG_HTX_TO_COUNT		0x1c
>>>>> +#define NWL_DSI_CFG_LRX_H_TO_COUNT		0x20
>>>>> +#define NWL_DSI_CFG_BTA_H_TO_COUNT		0x24
>>>>> +#define NWL_DSI_CFG_TWAKEUP			0x28
>>>>> +#define NWL_DSI_CFG_STATUS_OUT			0x2c
>>>>> +#define NWL_DSI_RX_ERROR_STATUS			0x30
>>>>> +
>>>>> +/* DSI DPI registers */
>>>>> +#define NWL_DSI_PIXEL_PAYLOAD_SIZE		0x200
>>>>> +#define NWL_DSI_PIXEL_FIFO_SEND_LEVEL		0x204
>>>>> +#define NWL_DSI_INTERFACE_COLOR_CODING		0x208
>>>>> +#define NWL_DSI_PIXEL_FORMAT			0x20c
>>>>> +#define NWL_DSI_VSYNC_POLARITY			0x210
>>>>> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW	0
>>>>> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGH	BIT(1)
>>>>> +
>>>>> +#define NWL_DSI_HSYNC_POLARITY			0x214
>>>>> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW	0
>>>>> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGH	BIT(1)
>>>>> +
>>>>> +#define NWL_DSI_VIDEO_MODE			0x218
>>>>> +#define NWL_DSI_HFP				0x21c
>>>>> +#define NWL_DSI_HBP				0x220
>>>>> +#define NWL_DSI_HSA				0x224
>>>>> +#define NWL_DSI_ENABLE_MULT_PKTS		0x228
>>>>> +#define NWL_DSI_VBP				0x22c
>>>>> +#define NWL_DSI_VFP				0x230
>>>>> +#define NWL_DSI_BLLP_MODE			0x234
>>>>> +#define NWL_DSI_USE_NULL_PKT_BLLP		0x238
>>>>> +#define NWL_DSI_VACTIVE				0x23c
>>>>> +#define NWL_DSI_VC				0x240
>>>>> +
>>>>> +/* DSI APB PKT control */
>>>>> +#define NWL_DSI_TX_PAYLOAD			0x280
>>>>> +#define NWL_DSI_PKT_CONTROL			0x284
>>>>> +#define NWL_DSI_SEND_PACKET			0x288
>>>>> +#define NWL_DSI_PKT_STATUS			0x28c
>>>>> +#define NWL_DSI_PKT_FIFO_WR_LEVEL		0x290
>>>>> +#define NWL_DSI_PKT_FIFO_RD_LEVEL		0x294
>>>>> +#define NWL_DSI_RX_PAYLOAD			0x298
>>>>> +#define NWL_DSI_RX_PKT_HEADER			0x29c
>>>>> +
>>>>> +/* DSI IRQ handling */
>>>>> +#define NWL_DSI_IRQ_STATUS			0x2a0
>>>>> +#define NWL_DSI_SM_NOT_IDLE			BIT(0)
>>>>> +#define NWL_DSI_TX_PKT_DONE			BIT(1)
>>>>> +#define NWL_DSI_DPHY_DIRECTION			BIT(2)
>>>>> +#define NWL_DSI_TX_FIFO_OVFLW			BIT(3)
>>>>> +#define NWL_DSI_TX_FIFO_UDFLW			BIT(4)
>>>>> +#define NWL_DSI_RX_FIFO_OVFLW			BIT(5)
>>>>> +#define NWL_DSI_RX_FIFO_UDFLW			BIT(6)
>>>>> +#define NWL_DSI_RX_PKT_HDR_RCVD			BIT(7)
>>>>> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD	BIT(8)
>>>>> +#define NWL_DSI_BTA_TIMEOUT			BIT(29)
>>>>> +#define NWL_DSI_LP_RX_TIMEOUT			BIT(30)
>>>>> +#define NWL_DSI_HS_TX_TIMEOUT			BIT(31)
>>>>> +
>>>>> +#define NWL_DSI_IRQ_STATUS2			0x2a4
>>>>> +#define NWL_DSI_SINGLE_BIT_ECC_ERR		BIT(0)
>>>>> +#define NWL_DSI_MULTI_BIT_ECC_ERR		BIT(1)
>>>>> +#define NWL_DSI_CRC_ERR				BIT(2)
>>>>> +
>>>>> +#define NWL_DSI_IRQ_MASK			0x2a8
>>>>> +#define NWL_DSI_SM_NOT_IDLE_MASK		BIT(0)
>>>>> +#define NWL_DSI_TX_PKT_DONE_MASK		BIT(1)
>>>>> +#define NWL_DSI_DPHY_DIRECTION_MASK		BIT(2)
>>>>> +#define NWL_DSI_TX_FIFO_OVFLW_MASK		BIT(3)
>>>>> +#define NWL_DSI_TX_FIFO_UDFLW_MASK		BIT(4)
>>>>> +#define NWL_DSI_RX_FIFO_OVFLW_MASK		BIT(5)
>>>>> +#define NWL_DSI_RX_FIFO_UDFLW_MASK		BIT(6)
>>>>> +#define NWL_DSI_RX_PKT_HDR_RCVD_MASK		BIT(7)
>>>>> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD_MASK	BIT(8)
>>>>> +#define NWL_DSI_BTA_TIMEOUT_MASK		BIT(29)
>>>>> +#define NWL_DSI_LP_RX_TIMEOUT_MASK		BIT(30)
>>>>> +#define NWL_DSI_HS_TX_TIMEOUT_MASK		BIT(31)
>>>>> +
>>>>> +#define NWL_DSI_IRQ_MASK2			0x2ac
>>>>> +#define NWL_DSI_SINGLE_BIT_ECC_ERR_MASK		BIT(0)
>>>>> +#define NWL_DSI_MULTI_BIT_ECC_ERR_MASK		BIT(1)
>>>>> +#define NWL_DSI_CRC_ERR_MASK			BIT(2)
>>>>> +
>>>>> +extern const struct mipi_dsi_host_ops nwl_dsi_host_ops;
>>>>> +
>>>>> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data);
>>>>> +int nwl_dsi_enable(struct nwl_dsi *dsi);
>>>>> +int nwl_dsi_disable(struct nwl_dsi *dsi);
>>>>> +
>>>>> +#endif /* __NWL_DSI_H__ */
>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH V3 0/2] mm/debug: Add tests for architecture exported page table helpers
From: Anshuman Khandual @ 2019-09-20  6:33 UTC (permalink / raw)
  To: linux-mm
  Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
	Tetsuo Handa, Heiko Carstens, Michal Hocko, Dave Hansen,
	Paul Mackerras, sparclinux, Thomas Gleixner, linux-s390,
	Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
	Steven Price, Jason Gunthorpe, Gerald Schaefer, linux-snps-arc,
	linux-arm-kernel, Kees Cook, Anshuman Khandual, Masahiro Yamada,
	Mark Brown, Kirill A . Shutemov, Dan Williams, Vlastimil Babka,
	Christophe Leroy, Sri Krishna chowdary, Ard Biesheuvel,
	Greg Kroah-Hartman, linux-mips, Ralf Baechle, linux-kernel,
	Paul Burton, Mike Rapoport, Vineet Gupta, Martin Schwidefsky,
	Andrew Morton, linuxppc-dev, David S. Miller, Mike Kravetz

This series adds a test validation for architecture exported page table
helpers. Patch in the series adds basic transformation tests at various
levels of the page table. Before that it exports gigantic page allocation
function from HugeTLB.

This test was originally suggested by Catalin during arm64 THP migration
RFC discussion earlier. Going forward it can include more specific tests
with respect to various generic MM functions like THP, HugeTLB etc and
platform specific tests.

https://lore.kernel.org/linux-mm/20190628102003.GA56463@arrakis.emea.arm.com/

Testing:

Successfully build and boot tested on both arm64 and x86 platforms without
any test failing. Only build tested on some other platforms. Build failed
on some platforms (known) in pud_clear_tests() as there were no available
__pgd() definitions.

- ARM32
- IA64

But I would really appreciate if folks can help validate this test on other
architectures and report back problems. All suggestions, comments and inputs
welcome. Thank you.

Changes in V3:

- Changed test trigger from module format into late_initcall()
- Marked all functions with __init to be freed after completion
- Changed all __PGTABLE_PXX_FOLDED checks as mm_pxx_folded()
- Folded in PPC32 fixes from Christophe

Changes in V2:

https://lore.kernel.org/linux-mm/1568268173-31302-1-git-send-email-anshuman.khandual@arm.com/T/#t

- Fixed small typo error in MODULE_DESCRIPTION()
- Fixed m64k build problems for lvalue concerns in pmd_xxx_tests()
- Fixed dynamic page table level folding problems on x86 as per Kirril
- Fixed second pointers during pxx_populate_tests() per Kirill and Gerald
- Allocate and free pte table with pte_alloc_one/pte_free per Kirill
- Modified pxx_clear_tests() to accommodate s390 lower 12 bits situation
- Changed RANDOM_NZVALUE value from 0xbe to 0xff
- Changed allocation, usage, free sequence for saved_ptep
- Renamed VMA_FLAGS as VMFLAGS
- Implemented a new method for random vaddr generation
- Implemented some other cleanups
- Dropped extern reference to mm_alloc()
- Created and exported new alloc_gigantic_page_order()
- Dropped the custom allocator and used new alloc_gigantic_page_order()

Changes in V1:

https://lore.kernel.org/linux-mm/1567497706-8649-1-git-send-email-anshuman.khandual@arm.com/

- Added fallback mechanism for PMD aligned memory allocation failure

Changes in RFC V2:

https://lore.kernel.org/linux-mm/1565335998-22553-1-git-send-email-anshuman.khandual@arm.com/T/#u

- Moved test module and it's config from lib/ to mm/
- Renamed config TEST_ARCH_PGTABLE as DEBUG_ARCH_PGTABLE_TEST
- Renamed file from test_arch_pgtable.c to arch_pgtable_test.c
- Added relevant MODULE_DESCRIPTION() and MODULE_AUTHOR() details
- Dropped loadable module config option
- Basic tests now use memory blocks with required size and alignment
- PUD aligned memory block gets allocated with alloc_contig_range()
- If PUD aligned memory could not be allocated it falls back on PMD aligned
  memory block from page allocator and pud_* tests are skipped
- Clear and populate tests now operate on real in memory page table entries
- Dummy mm_struct gets allocated with mm_alloc()
- Dummy page table entries get allocated with [pud|pmd|pte]_alloc_[map]()
- Simplified [p4d|pgd]_basic_tests(), now has random values in the entries

Original RFC V1:

https://lore.kernel.org/linux-mm/1564037723-26676-1-git-send-email-anshuman.khandual@arm.com/

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Steven Price <Steven.Price@arm.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Sri Krishna chowdary <schowdary@nvidia.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Russell King - ARM Linux <linux@armlinux.org.uk>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: James Hogan <jhogan@kernel.org>
Cc: Paul Burton <paul.burton@mips.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Christophe Leroy <christophe.leroy@c-s.fr>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: linux-snps-arc@lists.infradead.org
Cc: linux-mips@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-ia64@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org

Anshuman Khandual (2):
  mm/hugetlb: Make alloc_gigantic_page() available for general use
  mm/pgtable/debug: Add test validating architecture page table helpers

 arch/x86/include/asm/pgtable_64_types.h |   2 +
 include/linux/hugetlb.h                 |   9 +
 mm/Kconfig.debug                        |  14 +
 mm/Makefile                             |   1 +
 mm/arch_pgtable_test.c                  | 440 ++++++++++++++++++++++++++++++++
 mm/hugetlb.c                            |  24 +-
 6 files changed, 488 insertions(+), 2 deletions(-)
 create mode 100644 mm/arch_pgtable_test.c

-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH V3 2/2] mm/pgtable/debug: Add test validating architecture page table helpers
From: Anshuman Khandual @ 2019-09-20  6:33 UTC (permalink / raw)
  To: linux-mm
  Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
	Tetsuo Handa, Heiko Carstens, Michal Hocko, Dave Hansen,
	Paul Mackerras, sparclinux, Thomas Gleixner, linux-s390,
	Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
	Steven Price, Jason Gunthorpe, Gerald Schaefer, linux-snps-arc,
	linux-arm-kernel, Kees Cook, Anshuman Khandual, Masahiro Yamada,
	Mark Brown, Kirill A . Shutemov, Dan Williams, Vlastimil Babka,
	Christophe Leroy, Sri Krishna chowdary, Ard Biesheuvel,
	Greg Kroah-Hartman, linux-mips, Ralf Baechle, linux-kernel,
	Paul Burton, Mike Rapoport, Vineet Gupta, Martin Schwidefsky,
	Andrew Morton, linuxppc-dev, David S. Miller
In-Reply-To: <1568961203-18660-1-git-send-email-anshuman.khandual@arm.com>

This adds a test module which will validate architecture page table helpers
and accessors regarding compliance with generic MM semantics expectations.
This will help various architectures in validating changes to the existing
page table helpers or addition of new ones.

Test page table and memory pages creating it's entries at various level are
all allocated from system memory with required alignments. If memory pages
with required size and alignment could not be allocated, then all depending
individual tests are skipped.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Steven Price <Steven.Price@arm.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Sri Krishna chowdary <schowdary@nvidia.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Russell King - ARM Linux <linux@armlinux.org.uk>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: James Hogan <jhogan@kernel.org>
Cc: Paul Burton <paul.burton@mips.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Christophe Leroy <christophe.leroy@c-s.fr>
Cc: linux-snps-arc@lists.infradead.org
Cc: linux-mips@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-ia64@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org

Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Tested-by: Christophe Leroy <christophe.leroy@c-s.fr>		(PPC32)
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
 arch/x86/include/asm/pgtable_64_types.h |   2 +
 mm/Kconfig.debug                        |  14 +
 mm/Makefile                             |   1 +
 mm/arch_pgtable_test.c                  | 440 ++++++++++++++++++++++++++++++++
 4 files changed, 457 insertions(+)
 create mode 100644 mm/arch_pgtable_test.c

diff --git a/arch/x86/include/asm/pgtable_64_types.h b/arch/x86/include/asm/pgtable_64_types.h
index 52e5f5f..b882792 100644
--- a/arch/x86/include/asm/pgtable_64_types.h
+++ b/arch/x86/include/asm/pgtable_64_types.h
@@ -40,6 +40,8 @@ static inline bool pgtable_l5_enabled(void)
 #define pgtable_l5_enabled() 0
 #endif /* CONFIG_X86_5LEVEL */
 
+#define mm_p4d_folded(mm) (!pgtable_l5_enabled())
+
 extern unsigned int pgdir_shift;
 extern unsigned int ptrs_per_p4d;
 
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index 327b3eb..ce9c397 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -117,3 +117,17 @@ config DEBUG_RODATA_TEST
     depends on STRICT_KERNEL_RWX
     ---help---
       This option enables a testcase for the setting rodata read-only.
+
+config DEBUG_ARCH_PGTABLE_TEST
+	bool "Test arch page table helpers for semantics compliance"
+	depends on MMU
+	depends on DEBUG_KERNEL
+	help
+	  This options provides a kernel module which can be used to test
+	  architecture page table helper functions on various platform in
+	  verifying if they comply with expected generic MM semantics. This
+	  will help architectures code in making sure that any changes or
+	  new additions of these helpers will still conform to generic MM
+	  expected semantics.
+
+	  If unsure, say N.
diff --git a/mm/Makefile b/mm/Makefile
index d996846..bb572c5 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
 obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
 obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_DEBUG_RODATA_TEST) += rodata_test.o
+obj-$(CONFIG_DEBUG_ARCH_PGTABLE_TEST) += arch_pgtable_test.o
 obj-$(CONFIG_PAGE_OWNER) += page_owner.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
diff --git a/mm/arch_pgtable_test.c b/mm/arch_pgtable_test.c
new file mode 100644
index 0000000..2942a04
--- /dev/null
+++ b/mm/arch_pgtable_test.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This kernel module validates architecture page table helpers &
+ * accessors and helps in verifying their continued compliance with
+ * generic MM semantics.
+ *
+ * Copyright (C) 2019 ARM Ltd.
+ *
+ * Author: Anshuman Khandual <anshuman.khandual@arm.com>
+ */
+#define pr_fmt(fmt) "arch_pgtable_test: %s " fmt, __func__
+
+#include <linux/gfp.h>
+#include <linux/highmem.h>
+#include <linux/hugetlb.h>
+#include <linux/kernel.h>
+#include <linux/kconfig.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/mm_types.h>
+#include <linux/module.h>
+#include <linux/pfn_t.h>
+#include <linux/printk.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/swap.h>
+#include <linux/swapops.h>
+#include <linux/sched/mm.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+/*
+ * Basic operations
+ *
+ * mkold(entry)			= An old and not a young entry
+ * mkyoung(entry)		= A young and not an old entry
+ * mkdirty(entry)		= A dirty and not a clean entry
+ * mkclean(entry)		= A clean and not a dirty entry
+ * mkwrite(entry)		= A write and not a write protected entry
+ * wrprotect(entry)		= A write protected and not a write entry
+ * pxx_bad(entry)		= A mapped and non-table entry
+ * pxx_same(entry1, entry2)	= Both entries hold the exact same value
+ */
+#define VMFLAGS	(VM_READ|VM_WRITE|VM_EXEC)
+
+/*
+ * On s390 platform, the lower 12 bits are used to identify given page table
+ * entry type and for other arch specific requirements. But these bits might
+ * affect the ability to clear entries with pxx_clear(). So while loading up
+ * the entries skip all lower 12 bits in order to accommodate s390 platform.
+ * It does not have affect any other platform.
+ */
+#define RANDOM_ORVALUE	(0xfffffffffffff000UL)
+#define RANDOM_NZVALUE	(0xff)
+
+static bool pud_aligned __initdata;
+static bool pmd_aligned __initdata;
+
+static void __init pte_basic_tests(struct page *page, pgprot_t prot)
+{
+	pte_t pte = mk_pte(page, prot);
+
+	WARN_ON(!pte_same(pte, pte));
+	WARN_ON(!pte_young(pte_mkyoung(pte)));
+	WARN_ON(!pte_dirty(pte_mkdirty(pte)));
+	WARN_ON(!pte_write(pte_mkwrite(pte)));
+	WARN_ON(pte_young(pte_mkold(pte)));
+	WARN_ON(pte_dirty(pte_mkclean(pte)));
+	WARN_ON(pte_write(pte_wrprotect(pte)));
+}
+
+#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE
+static void __init pmd_basic_tests(struct page *page, pgprot_t prot)
+{
+	pmd_t pmd;
+
+	/*
+	 * Memory block here must be PMD_SIZE aligned. Abort this
+	 * test in case we could not allocate such a memory block.
+	 */
+	if (!pmd_aligned) {
+		pr_warn("Could not proceed with PMD tests\n");
+		return;
+	}
+
+	pmd = mk_pmd(page, prot);
+	WARN_ON(!pmd_same(pmd, pmd));
+	WARN_ON(!pmd_young(pmd_mkyoung(pmd)));
+	WARN_ON(!pmd_dirty(pmd_mkdirty(pmd)));
+	WARN_ON(!pmd_write(pmd_mkwrite(pmd)));
+	WARN_ON(pmd_young(pmd_mkold(pmd)));
+	WARN_ON(pmd_dirty(pmd_mkclean(pmd)));
+	WARN_ON(pmd_write(pmd_wrprotect(pmd)));
+	/*
+	 * A huge page does not point to next level page table
+	 * entry. Hence this must qualify as pmd_bad().
+	 */
+	WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
+}
+#else
+static void __init pmd_basic_tests(struct page *page, pgprot_t prot) { }
+#endif
+
+#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
+static void __init pud_basic_tests(struct page *page, pgprot_t prot)
+{
+	pud_t pud;
+
+	/*
+	 * Memory block here must be PUD_SIZE aligned. Abort this
+	 * test in case we could not allocate such a memory block.
+	 */
+	if (!pud_aligned) {
+		pr_warn("Could not proceed with PUD tests\n");
+		return;
+	}
+
+	pud = pfn_pud(page_to_pfn(page), prot);
+	WARN_ON(!pud_same(pud, pud));
+	WARN_ON(!pud_young(pud_mkyoung(pud)));
+	WARN_ON(!pud_write(pud_mkwrite(pud)));
+	WARN_ON(pud_write(pud_wrprotect(pud)));
+	WARN_ON(pud_young(pud_mkold(pud)));
+
+	if (mm_pmd_folded(mm) || __is_defined(ARCH_HAS_4LEVEL_HACK))
+		return;
+
+	/*
+	 * A huge page does not point to next level page table
+	 * entry. Hence this must qualify as pud_bad().
+	 */
+	WARN_ON(!pud_bad(pud_mkhuge(pud)));
+}
+#else
+static void __init pud_basic_tests(struct page *page, pgprot_t prot) { }
+#endif
+
+static void __init p4d_basic_tests(struct page *page, pgprot_t prot)
+{
+	p4d_t p4d;
+
+	memset(&p4d, RANDOM_NZVALUE, sizeof(p4d_t));
+	WARN_ON(!p4d_same(p4d, p4d));
+}
+
+static void __init pgd_basic_tests(struct page *page, pgprot_t prot)
+{
+	pgd_t pgd;
+
+	memset(&pgd, RANDOM_NZVALUE, sizeof(pgd_t));
+	WARN_ON(!pgd_same(pgd, pgd));
+}
+
+#ifndef __ARCH_HAS_4LEVEL_HACK
+static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp)
+{
+	pud_t pud = READ_ONCE(*pudp);
+
+	if (mm_pmd_folded(mm))
+		return;
+
+	pud = __pud(pud_val(pud) | RANDOM_ORVALUE);
+	WRITE_ONCE(*pudp, pud);
+	pud_clear(pudp);
+	pud = READ_ONCE(*pudp);
+	WARN_ON(!pud_none(pud));
+}
+
+static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
+				      pmd_t *pmdp)
+{
+	pud_t pud;
+
+	if (mm_pmd_folded(mm))
+		return;
+	/*
+	 * This entry points to next level page table page.
+	 * Hence this must not qualify as pud_bad().
+	 */
+	pmd_clear(pmdp);
+	pud_clear(pudp);
+	pud_populate(mm, pudp, pmdp);
+	pud = READ_ONCE(*pudp);
+	WARN_ON(pud_bad(pud));
+}
+#else
+static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp) { }
+static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
+				      pmd_t *pmdp)
+{
+}
+#endif
+
+#ifndef __ARCH_HAS_5LEVEL_HACK
+static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp)
+{
+	p4d_t p4d = READ_ONCE(*p4dp);
+
+	if (mm_pud_folded(mm))
+		return;
+
+	p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE);
+	WRITE_ONCE(*p4dp, p4d);
+	p4d_clear(p4dp);
+	p4d = READ_ONCE(*p4dp);
+	WARN_ON(!p4d_none(p4d));
+}
+
+static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
+				      pud_t *pudp)
+{
+	p4d_t p4d;
+
+	if (mm_pud_folded(mm))
+		return;
+
+	/*
+	 * This entry points to next level page table page.
+	 * Hence this must not qualify as p4d_bad().
+	 */
+	pud_clear(pudp);
+	p4d_clear(p4dp);
+	p4d_populate(mm, p4dp, pudp);
+	p4d = READ_ONCE(*p4dp);
+	WARN_ON(p4d_bad(p4d));
+}
+
+static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp)
+{
+	pgd_t pgd = READ_ONCE(*pgdp);
+
+	if (mm_p4d_folded(mm))
+		return;
+
+	pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE);
+	WRITE_ONCE(*pgdp, pgd);
+	pgd_clear(pgdp);
+	pgd = READ_ONCE(*pgdp);
+	WARN_ON(!pgd_none(pgd));
+}
+
+static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
+				      p4d_t *p4dp)
+{
+	pgd_t pgd;
+
+	if (mm_p4d_folded(mm))
+		return;
+
+	/*
+	 * This entry points to next level page table page.
+	 * Hence this must not qualify as pgd_bad().
+	 */
+	p4d_clear(p4dp);
+	pgd_clear(pgdp);
+	pgd_populate(mm, pgdp, p4dp);
+	pgd = READ_ONCE(*pgdp);
+	WARN_ON(pgd_bad(pgd));
+}
+#else
+static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp) { }
+static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) { }
+static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
+				      pud_t *pudp)
+{
+}
+static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
+				      p4d_t *p4dp)
+{
+}
+#endif
+
+static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep)
+{
+	pte_t pte = READ_ONCE(*ptep);
+
+	pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
+	WRITE_ONCE(*ptep, pte);
+	pte_clear(mm, 0, ptep);
+	pte = READ_ONCE(*ptep);
+	WARN_ON(!pte_none(pte));
+}
+
+static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
+{
+	pmd_t pmd = READ_ONCE(*pmdp);
+
+	pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
+	WRITE_ONCE(*pmdp, pmd);
+	pmd_clear(pmdp);
+	pmd = READ_ONCE(*pmdp);
+	WARN_ON(!pmd_none(pmd));
+}
+
+static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
+				      pgtable_t pgtable)
+{
+	pmd_t pmd;
+
+	/*
+	 * This entry points to next level page table page.
+	 * Hence this must not qualify as pmd_bad().
+	 */
+	pmd_clear(pmdp);
+	pmd_populate(mm, pmdp, pgtable);
+	pmd = READ_ONCE(*pmdp);
+	WARN_ON(pmd_bad(pmd));
+}
+
+static struct page * __init alloc_mapped_page(void)
+{
+	struct page *page;
+	gfp_t gfp_mask = GFP_KERNEL | __GFP_ZERO;
+
+	page = alloc_gigantic_page_order(get_order(PUD_SIZE), gfp_mask,
+				first_memory_node, &node_states[N_MEMORY]);
+	if (page) {
+		pud_aligned = true;
+		pmd_aligned = true;
+		return page;
+	}
+
+	page = alloc_pages(gfp_mask, get_order(PMD_SIZE));
+	if (page) {
+		pmd_aligned = true;
+		return page;
+	}
+	return alloc_page(gfp_mask);
+}
+
+static void __init free_mapped_page(struct page *page)
+{
+	if (pud_aligned) {
+		unsigned long pfn = page_to_pfn(page);
+
+		free_contig_range(pfn, 1ULL << get_order(PUD_SIZE));
+		return;
+	}
+
+	if (pmd_aligned) {
+		int order = get_order(PMD_SIZE);
+
+		free_pages((unsigned long)page_address(page), order);
+		return;
+	}
+	free_page((unsigned long)page_address(page));
+}
+
+static unsigned long __init get_random_vaddr(void)
+{
+	unsigned long random_vaddr, random_pages, total_user_pages;
+
+	total_user_pages = (TASK_SIZE - FIRST_USER_ADDRESS) / PAGE_SIZE;
+
+	random_pages = get_random_long() % total_user_pages;
+	random_vaddr = FIRST_USER_ADDRESS + random_pages * PAGE_SIZE;
+
+	WARN_ON(random_vaddr > TASK_SIZE);
+	WARN_ON(random_vaddr < FIRST_USER_ADDRESS);
+	return random_vaddr;
+}
+
+static int __init arch_pgtable_tests_init(void)
+{
+	struct mm_struct *mm;
+	struct page *page;
+	pgd_t *pgdp;
+	p4d_t *p4dp, *saved_p4dp;
+	pud_t *pudp, *saved_pudp;
+	pmd_t *pmdp, *saved_pmdp, pmd;
+	pte_t *ptep;
+	pgtable_t saved_ptep;
+	pgprot_t prot;
+	unsigned long vaddr;
+
+	prot = vm_get_page_prot(VMFLAGS);
+	vaddr = get_random_vaddr();
+	mm = mm_alloc();
+	if (!mm) {
+		pr_err("mm_struct allocation failed\n");
+		return 1;
+	}
+
+	page = alloc_mapped_page();
+	if (!page) {
+		pr_err("memory allocation failed\n");
+		return 1;
+	}
+
+	pgdp = pgd_offset(mm, vaddr);
+	p4dp = p4d_alloc(mm, pgdp, vaddr);
+	pudp = pud_alloc(mm, p4dp, vaddr);
+	pmdp = pmd_alloc(mm, pudp, vaddr);
+	ptep = pte_alloc_map(mm, pmdp, vaddr);
+
+	/*
+	 * Save all the page table page addresses as the page table
+	 * entries will be used for testing with random or garbage
+	 * values. These saved addresses will be used for freeing
+	 * page table pages.
+	 */
+	pmd = READ_ONCE(*pmdp);
+	saved_p4dp = p4d_offset(pgdp, 0UL);
+	saved_pudp = pud_offset(p4dp, 0UL);
+	saved_pmdp = pmd_offset(pudp, 0UL);
+	saved_ptep = pmd_pgtable(pmd);
+
+	pte_basic_tests(page, prot);
+	pmd_basic_tests(page, prot);
+	pud_basic_tests(page, prot);
+	p4d_basic_tests(page, prot);
+	pgd_basic_tests(page, prot);
+
+	pte_clear_tests(mm, ptep);
+	pmd_clear_tests(mm, pmdp);
+	pud_clear_tests(mm, pudp);
+	p4d_clear_tests(mm, p4dp);
+	pgd_clear_tests(mm, pgdp);
+
+	pte_unmap(ptep);
+
+	pmd_populate_tests(mm, pmdp, saved_ptep);
+	pud_populate_tests(mm, pudp, saved_pmdp);
+	p4d_populate_tests(mm, p4dp, saved_pudp);
+	pgd_populate_tests(mm, pgdp, saved_p4dp);
+
+	p4d_free(mm, saved_p4dp);
+	pud_free(mm, saved_pudp);
+	pmd_free(mm, saved_pmdp);
+	pte_free(mm, saved_ptep);
+
+	mm_dec_nr_puds(mm);
+	mm_dec_nr_pmds(mm);
+	mm_dec_nr_ptes(mm);
+	__mmdrop(mm);
+
+	free_mapped_page(page);
+	return 0;
+}
+late_initcall(arch_pgtable_tests_init);
-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [EXT] [PATCH v3] serial: imx: adapt rx buffer and dma periods
From: Philipp Puschmann @ 2019-09-20  7:06 UTC (permalink / raw)
  To: Andy Duan, linux-kernel@vger.kernel.org
  Cc: festevam@gmail.com, linux-serial@vger.kernel.org,
	gregkh@linuxfoundation.org, s.hauer@pengutronix.de,
	u.kleine-koenig@pengutronix.de, dl-linux-imx,
	kernel@pengutronix.de, jslaby@suse.com, Robin Gong,
	shawnguo@kernel.org, linux-arm-kernel@lists.infradead.org,
	l.stach@pengutronix.de
In-Reply-To: <VI1PR0402MB3600CA068AEBAC63D3CE6A4CFF880@VI1PR0402MB3600.eurprd04.prod.outlook.com>

Hi Andy,

Am 20.09.19 um 05:42 schrieb Andy Duan:
> From: Philipp Puschmann <philipp.puschmann@emlix.com> Sent: Thursday, September 19, 2019 10:51 PM
>> Using only 4 DMA periods for UART RX is very few if we have a high frequency
>> of small transfers - like in our case using Bluetooth with many small packets
>> via UART - causing many dma transfers but in each only filling a fraction of a
>> single buffer. Such a case may lead to the situation that DMA RX transfer is
>> triggered but no free buffer is available. When this happens dma channel ist
>> stopped - with the patch
>> "dmaengine: imx-sdma: fix dma freezes" temporarily only - with the possible
>> consequences that:
>> with disabled hw flow control:
>>   If enough data is incoming on UART port the RX FIFO runs over and
>>   characters will be lost. What then happens depends on upper layer.
>>
>> with enabled hw flow control:
>>   If enough data is incoming on UART port the RX FIFO reaches a level
>>   where CTS is deasserted and remote device sending the data stops.
>>   If it fails to stop timely the i.MX' RX FIFO may run over and data
>>   get lost. Otherwise it's internal TX buffer may getting filled to
>>   a point where it runs over and data is again lost. It depends on
>>   the remote device how this case is handled and if it is recoverable.
>>
>> Obviously we want to avoid having no free buffers available. So we decrease
>> the size of the buffers and increase their number and the total buffer size.
>>
>> Signed-off-by: Philipp Puschmann <philipp.puschmann@emlix.com>
>> Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
>> ---
>>
>> Changelog v3:
>>  - enhance description
>>
>> Changelog v2:
>>  - split this patch from series "Fix UART DMA freezes for iMX6"
>>  - add Reviewed-by tag
>>
>>  drivers/tty/serial/imx.c | 5 ++---
>>  1 file changed, 2 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index
>> 87c58f9f6390..51dc19833eab 100644
>> --- a/drivers/tty/serial/imx.c
>> +++ b/drivers/tty/serial/imx.c
>> @@ -1034,8 +1034,6 @@ static void imx_uart_timeout(struct timer_list *t)
>>         }
>>  }
>>
>> -#define RX_BUF_SIZE    (PAGE_SIZE)
>> -
>>  /*
>>   * There are two kinds of RX DMA interrupts(such as in the MX6Q):
>>   *   [1] the RX DMA buffer is full.
>> @@ -1118,7 +1116,8 @@ static void imx_uart_dma_rx_callback(void
>> *data)  }
>>
>>  /* RX DMA buffer periods */
>> -#define RX_DMA_PERIODS 4
>> +#define RX_DMA_PERIODS 16
>> +#define RX_BUF_SIZE    (PAGE_SIZE / 4)
>>
> Why to decrease the DMA RX buffer size here ?
> 
> The current DMA implementation support DMA cyclic mode, one SDMA BD receive one Bluetooth frame can
> bring better performance.
> As you know, for L2CAP, a maximum transmission unit (MTU) associated with the largest Baseband payload
> is 341 bytes for DH5 packets.
> 
> So I suggest to increase RX_BUF_SIZE along with RX_DMA_PERIODS to feasible value.

I debugged and developed this patches on a system with a 4.15 kernel. When prepared for upstream i have adapted
some details and missed a important thing here. It should say:

+#define RX_BUF_SIZE    (RX_DMA_PERIODS * PAGE_SIZE / 4)

Yes, i wanted to increase the total buffer size too, even wrote it in the description.
I will prepare a version 4, thanks for the hint.

Just for info: A single RX DMA period aka buffer can be filled with mutliple packets in regard of the upper layer, here BT.


Regards,
Philipp
> 
> Andy
> 
>>  static int imx_uart_start_rx_dma(struct imx_port *sport)  {
>> --
>> 2.23.0
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH RFC 11/14] arm64: Move the ASID allocator code in a separate file
From: Jean-Philippe Brucker @ 2019-09-20  7:18 UTC (permalink / raw)
  To: Guo Ren
  Cc: aou, Linux Kernel Mailing List, Arnd Bergmann, suzuki.poulose,
	Marc Zyngier, Catalin Marinas, Palmer Dabbelt, christoffer.dall,
	iommu, Mike Rapoport, Anup Patel, Atish Patra, Julien Grall,
	james.morse, gary, Paul Walmsley, linux-riscv, Will Deacon,
	kvmarm, linux-arm-kernel
In-Reply-To: <CAJF2gTQtk7VhBgUan6WOZgc3UaQzHL8SxMi=yiHG-8eC207BbQ@mail.gmail.com>

On Fri, Sep 20, 2019 at 08:07:38AM +0800, Guo Ren wrote:
> On Thu, Sep 19, 2019 at 11:18 PM Jean-Philippe Brucker
> <jean-philippe@linaro.org> wrote:
> 
> >
> > The SMMU does support PCI Virtual Function - an hypervisor can assign a
> > VF to a guest, and let that guest partition the VF into smaller contexts
> > by using PASID.  What it can't support is assigning partitions of a PCI
> > function (VF or PF) to multiple Virtual Machines, since there is a
> > single S2 PGD per function (in the Stream Table Entry), rather than one
> > S2 PGD per PASID context.
> >
> In my concept, the two sentences "The SMMU does support PCI Virtual
> Functio" v.s. "What it can't support is assigning partitions of a PCI
> function (VF or PF) to multiple Virtual Machines" are conflict and I
> don't want to play naming game :)

That's fine. But to prevent the spread of misinformation: Arm SMMU
supports PCI Virtual Functions.

Thanks,
Jean

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ 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