linux-sunxi.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
@ 2021-06-06  9:03 guoren
  2021-06-06  9:03 ` [RFC PATCH v2 01/11] riscv: asid: Use global mappings for kernel pages guoren
                   ` (15 more replies)
  0 siblings, 16 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:03 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren

From: Guo Ren <guoren@linux.alibaba.com>

The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
vendors define the custom properties of memory regions in PTE.

This patchset helps SOC vendors to support their own custom interconnect
coherent solution with PTE attributes.

For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906 has
two modes in MMU:
 - Compatible mode, the same as the definitions in spec.
 - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
   not mentioned in spec.

Allwinner D1 needs the enhanced mode to support the DMA type device with
non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
attribute bits in PTE.

The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been
tested on D1:
 - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
   conflict with RISC-V spec, and hope these patches soon could be
   approved.
 - pgtable: Using a image-hdr to pass vendor specific information and
   setup custom PTE attributes in a global struct variable during boot
   stage. Also it needs define custom protection_map in linux/mm.
 - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
   In this patchset, I just show you how T-HEAD C9xx work, and seems Atish
   is working for the DMA infrustructure, please let me know the idea.
 - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.

The patchset could work with linux-5.13-rc4, here is the steps for D1:
 - Download linux-5.13-rc4 and apply the patchset
 - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
 - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
 - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e 0x00200000 -n Linux -d arch/riscv/boot/Image uImage
 - Download newest opensbi [2], build with [3], and get fw_dynamic.bin
 - Copy uImage, fw_dynamic.bin, allwinner-d1-nezha-kit.dtb into boot
   partition of TF card.
 - Plugin the TF card and power on D1.

Link: https://linux-sunxi.org/D1 [1]
Link: https://github.com/riscv/opensbi branch:master [2]
Link: https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md [3]

Changes since v1:
 - Rebase on linux-5.13-rc4
 - Support defconfig for different PTE attributes
 - Support C906 icache_sync
 - Add Allwinner D1 dts & Kconfig & gmac for testing
 - Add asid optimization for D1 usage

Guo Ren (10):
  riscv: asid: Use global mappings for kernel pages
  riscv: asid: Add ASID-based tlbflushing methods
  riscv: asid: Optimize tlbflush coding convention
  riscv: pgtable: Fixup _PAGE_CHG_MASK usage
  riscv: pgtable: Add custom protection_map init
  riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  riscv: cmo: Add dma-noncoherency support
  riscv: cmo: Add vendor custom icache sync
  riscv: soc: Initial DTS for Allwinner D1 NeZha board
  riscv: soc: Add Allwinner SoC kconfig option

liush (1):
  riscv: soc: Allwinner D1 GMAC driver only for temp use

 arch/riscv/Kconfig                                 |    9 +
 arch/riscv/Kconfig.socs                            |   12 +
 arch/riscv/boot/dts/Makefile                       |    1 +
 arch/riscv/boot/dts/allwinner/Makefile             |    2 +
 .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
 arch/riscv/configs/defconfig                       |    1 +
 arch/riscv/include/asm/cacheflush.h                |   48 +-
 arch/riscv/include/asm/mmu_context.h               |    2 +
 arch/riscv/include/asm/pgtable-64.h                |    8 +-
 arch/riscv/include/asm/pgtable-bits.h              |   20 +-
 arch/riscv/include/asm/pgtable.h                   |   44 +-
 arch/riscv/include/asm/sbi.h                       |   15 +
 arch/riscv/include/asm/soc.h                       |    1 +
 arch/riscv/include/asm/tlbflush.h                  |   22 +
 arch/riscv/include/asm/vendorid_list.h             |    1 +
 arch/riscv/kernel/sbi.c                            |   19 +
 arch/riscv/kernel/soc.c                            |   22 +
 arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
 arch/riscv/mm/Makefile                             |    1 +
 arch/riscv/mm/cacheflush.c                         |    3 +-
 arch/riscv/mm/context.c                            |    2 +-
 arch/riscv/mm/dma-mapping.c                        |   53 +
 arch/riscv/mm/init.c                               |   26 +
 arch/riscv/mm/tlbflush.c                           |   57 +-
 drivers/net/ethernet/Kconfig                       |    1 +
 drivers/net/ethernet/Makefile                      |    1 +
 drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
 drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240 ++++++++++++++++++++
 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
 drivers/net/phy/realtek.c                          |    2 +-
 mm/mmap.c                                          |    4 +
 34 files changed, 3714 insertions(+), 37 deletions(-)
 create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
 create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
 create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
 create mode 100644 arch/riscv/mm/dma-mapping.c
 create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
 create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 01/11] riscv: asid: Use global mappings for kernel pages
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
@ 2021-06-06  9:03 ` guoren
  2021-06-06  9:03 ` [PATCH V5 1/3] riscv: " guoren
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:03 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren

From: Guo Ren <guoren@linux.alibaba.com>

We map kernel pages into all addresses space, so they can be marked as
global. This allows hardware to avoid flushing the kernel mappings when
moving between address spaces.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/include/asm/pgtable.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 9469f46..346a3c6 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -134,7 +134,8 @@
 				| _PAGE_WRITE \
 				| _PAGE_PRESENT \
 				| _PAGE_ACCESSED \
-				| _PAGE_DIRTY)
+				| _PAGE_DIRTY \
+				| _PAGE_GLOBAL)
 
 #define PAGE_KERNEL		__pgprot(_PAGE_KERNEL)
 #define PAGE_KERNEL_READ	__pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH V5 1/3] riscv: Use global mappings for kernel pages
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
  2021-06-06  9:03 ` [RFC PATCH v2 01/11] riscv: asid: Use global mappings for kernel pages guoren
@ 2021-06-06  9:03 ` guoren
  2021-06-06  9:03 ` [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods guoren
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:03 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren

From: Guo Ren <guoren@linux.alibaba.com>

We map kernel pages into all addresses space, so they can be marked as
global. This allows hardware to avoid flushing the kernel mappings when
moving between address spaces.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
---
 arch/riscv/include/asm/pgtable.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 9469f46..346a3c6 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -134,7 +134,8 @@
 				| _PAGE_WRITE \
 				| _PAGE_PRESENT \
 				| _PAGE_ACCESSED \
-				| _PAGE_DIRTY)
+				| _PAGE_DIRTY \
+				| _PAGE_GLOBAL)
 
 #define PAGE_KERNEL		__pgprot(_PAGE_KERNEL)
 #define PAGE_KERNEL_READ	__pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
  2021-06-06  9:03 ` [RFC PATCH v2 01/11] riscv: asid: Use global mappings for kernel pages guoren
  2021-06-06  9:03 ` [PATCH V5 1/3] riscv: " guoren
@ 2021-06-06  9:03 ` guoren
  2021-06-06 14:38   ` Christoph Hellwig
  2021-06-06  9:03 ` [RFC PATCH v2 02/11] riscv: asid: " guoren
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 52+ messages in thread
From: guoren @ 2021-06-06  9:03 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

Implement optimized version of the tlb flushing routines for systems
using ASIDs. These are behind the use_asid_allocator static branch to
not affect existing systems not using ASIDs.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Christoph Hellwig <hch@lst.de>
---
 arch/riscv/include/asm/mmu_context.h |  2 ++
 arch/riscv/include/asm/tlbflush.h    | 22 +++++++++++++++++
 arch/riscv/mm/context.c              |  2 +-
 arch/riscv/mm/tlbflush.c             | 46 +++++++++++++++++++++++++++++++++---
 4 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/include/asm/mmu_context.h b/arch/riscv/include/asm/mmu_context.h
index b065941..7030837 100644
--- a/arch/riscv/include/asm/mmu_context.h
+++ b/arch/riscv/include/asm/mmu_context.h
@@ -33,6 +33,8 @@ static inline int init_new_context(struct task_struct *tsk,
 	return 0;
 }
 
+DECLARE_STATIC_KEY_FALSE(use_asid_allocator);
+
 #include <asm-generic/mmu_context.h>
 
 #endif /* _ASM_RISCV_MMU_CONTEXT_H */
diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
index c84218a..894cf75 100644
--- a/arch/riscv/include/asm/tlbflush.h
+++ b/arch/riscv/include/asm/tlbflush.h
@@ -22,9 +22,31 @@ static inline void local_flush_tlb_page(unsigned long addr)
 {
 	ALT_FLUSH_TLB_PAGE(__asm__ __volatile__ ("sfence.vma %0" : : "r" (addr) : "memory"));
 }
+
+static inline void local_flush_tlb_all_asid(unsigned long asid)
+{
+	__asm__ __volatile__ ("sfence.vma x0, %0"
+			:
+			: "r" (asid)
+			: "memory");
+}
+
+static inline void local_flush_tlb_range_asid(unsigned long start,
+				unsigned long size, unsigned long asid)
+{
+	unsigned long tmp, end = ALIGN(start + size, PAGE_SIZE);
+
+	for (tmp = start & PAGE_MASK; tmp < end; tmp += PAGE_SIZE) {
+		__asm__ __volatile__ ("sfence.vma %0, %1"
+				:
+				: "r" (tmp), "r" (asid)
+				: "memory");
+	}
+}
 #else /* CONFIG_MMU */
 #define local_flush_tlb_all()			do { } while (0)
 #define local_flush_tlb_page(addr)		do { } while (0)
+#define local_flush_tlb_range_asid(addr)	do { } while (0)
 #endif /* CONFIG_MMU */
 
 #if defined(CONFIG_SMP) && defined(CONFIG_MMU)
diff --git a/arch/riscv/mm/context.c b/arch/riscv/mm/context.c
index 68aa312..45c1b04 100644
--- a/arch/riscv/mm/context.c
+++ b/arch/riscv/mm/context.c
@@ -18,7 +18,7 @@
 
 #ifdef CONFIG_MMU
 
-static DEFINE_STATIC_KEY_FALSE(use_asid_allocator);
+DEFINE_STATIC_KEY_FALSE(use_asid_allocator);
 
 static unsigned long asid_bits;
 static unsigned long num_asids;
diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 720b443..87b4e52 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -4,6 +4,7 @@
 #include <linux/smp.h>
 #include <linux/sched.h>
 #include <asm/sbi.h>
+#include <asm/mmu_context.h>
 
 void flush_tlb_all(void)
 {
@@ -39,18 +40,57 @@ static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start,
 	put_cpu();
 }
 
+static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
+				       unsigned long start,
+				       unsigned long size,
+				       unsigned long asid)
+{
+	struct cpumask hmask;
+	unsigned int cpuid;
+
+	if (cpumask_empty(cmask))
+		return;
+
+	cpuid = get_cpu();
+
+	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
+		if (size == -1)
+			local_flush_tlb_all_asid(asid);
+		else
+			local_flush_tlb_range_asid(start, size, asid);
+	} else {
+		riscv_cpuid_to_hartid_mask(cmask, &hmask);
+		sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
+					   start, size, asid);
+	}
+
+	put_cpu();
+}
+
 void flush_tlb_mm(struct mm_struct *mm)
 {
-	__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(mm), 0, -1,
+					   atomic_long_read(&mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
 }
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
-	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE,
+					   atomic_long_read(&vma->vm_mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
 }
 
 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 		     unsigned long end)
 {
-	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), start, end - start,
+					   atomic_long_read(&vma->vm_mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 02/11] riscv: asid: Add ASID-based tlbflushing methods
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (2 preceding siblings ...)
  2021-06-06  9:03 ` [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods guoren
@ 2021-06-06  9:03 ` guoren
  2021-06-06  9:04 ` [RFC PATCH v2 03/11] riscv: asid: Optimize tlbflush coding convention guoren
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:03 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

Implement optimized version of the tlb flushing routines for systems
using ASIDs. These are behind the use_asid_allocator static branch to
not affect existing systems not using ASIDs.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/include/asm/mmu_context.h |  2 ++
 arch/riscv/include/asm/tlbflush.h    | 22 +++++++++++++++++
 arch/riscv/mm/context.c              |  2 +-
 arch/riscv/mm/tlbflush.c             | 46 +++++++++++++++++++++++++++++++++---
 4 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/include/asm/mmu_context.h b/arch/riscv/include/asm/mmu_context.h
index b065941..7030837 100644
--- a/arch/riscv/include/asm/mmu_context.h
+++ b/arch/riscv/include/asm/mmu_context.h
@@ -33,6 +33,8 @@ static inline int init_new_context(struct task_struct *tsk,
 	return 0;
 }
 
+DECLARE_STATIC_KEY_FALSE(use_asid_allocator);
+
 #include <asm-generic/mmu_context.h>
 
 #endif /* _ASM_RISCV_MMU_CONTEXT_H */
diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
index c84218a..894cf75 100644
--- a/arch/riscv/include/asm/tlbflush.h
+++ b/arch/riscv/include/asm/tlbflush.h
@@ -22,9 +22,31 @@ static inline void local_flush_tlb_page(unsigned long addr)
 {
 	ALT_FLUSH_TLB_PAGE(__asm__ __volatile__ ("sfence.vma %0" : : "r" (addr) : "memory"));
 }
+
+static inline void local_flush_tlb_all_asid(unsigned long asid)
+{
+	__asm__ __volatile__ ("sfence.vma x0, %0"
+			:
+			: "r" (asid)
+			: "memory");
+}
+
+static inline void local_flush_tlb_range_asid(unsigned long start,
+				unsigned long size, unsigned long asid)
+{
+	unsigned long tmp, end = ALIGN(start + size, PAGE_SIZE);
+
+	for (tmp = start & PAGE_MASK; tmp < end; tmp += PAGE_SIZE) {
+		__asm__ __volatile__ ("sfence.vma %0, %1"
+				:
+				: "r" (tmp), "r" (asid)
+				: "memory");
+	}
+}
 #else /* CONFIG_MMU */
 #define local_flush_tlb_all()			do { } while (0)
 #define local_flush_tlb_page(addr)		do { } while (0)
+#define local_flush_tlb_range_asid(addr)	do { } while (0)
 #endif /* CONFIG_MMU */
 
 #if defined(CONFIG_SMP) && defined(CONFIG_MMU)
diff --git a/arch/riscv/mm/context.c b/arch/riscv/mm/context.c
index 68aa312..45c1b04 100644
--- a/arch/riscv/mm/context.c
+++ b/arch/riscv/mm/context.c
@@ -18,7 +18,7 @@
 
 #ifdef CONFIG_MMU
 
-static DEFINE_STATIC_KEY_FALSE(use_asid_allocator);
+DEFINE_STATIC_KEY_FALSE(use_asid_allocator);
 
 static unsigned long asid_bits;
 static unsigned long num_asids;
diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 720b443..87b4e52 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -4,6 +4,7 @@
 #include <linux/smp.h>
 #include <linux/sched.h>
 #include <asm/sbi.h>
+#include <asm/mmu_context.h>
 
 void flush_tlb_all(void)
 {
@@ -39,18 +40,57 @@ static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start,
 	put_cpu();
 }
 
+static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
+				       unsigned long start,
+				       unsigned long size,
+				       unsigned long asid)
+{
+	struct cpumask hmask;
+	unsigned int cpuid;
+
+	if (cpumask_empty(cmask))
+		return;
+
+	cpuid = get_cpu();
+
+	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
+		if (size == -1)
+			local_flush_tlb_all_asid(asid);
+		else
+			local_flush_tlb_range_asid(start, size, asid);
+	} else {
+		riscv_cpuid_to_hartid_mask(cmask, &hmask);
+		sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
+					   start, size, asid);
+	}
+
+	put_cpu();
+}
+
 void flush_tlb_mm(struct mm_struct *mm)
 {
-	__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(mm), 0, -1,
+					   atomic_long_read(&mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
 }
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
-	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE,
+					   atomic_long_read(&vma->vm_mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
 }
 
 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 		     unsigned long end)
 {
-	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
+	if (static_branch_unlikely(&use_asid_allocator))
+		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), start, end - start,
+					   atomic_long_read(&vma->vm_mm->context.id));
+	else
+		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 03/11] riscv: asid: Optimize tlbflush coding convention
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (3 preceding siblings ...)
  2021-06-06  9:03 ` [RFC PATCH v2 02/11] riscv: asid: " guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06  9:04 ` [PATCH V5 3/3] riscv: tlbflush: Optimize " guoren
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren, Atish Patra

From: Guo Ren <guoren@linux.alibaba.com>

Passing the mm_struct as the first argument, as we can derive both
the cpumask and asid from it instead of doing that in the callers.

But more importantly, the static branch check can be moved deeper
into the code to avoid a lot of duplication.

Also add FIXME comment on the non-ASID code switches to a global
flush once flushing more than a single page.

Link: https://lore.kernel.org/linux-riscv/CAJF2gTQpDYtEdw6ZrTVZUYqxGdhLPs25RjuUiQtz=xN2oKs2fw@mail.gmail.com/T/#m30f7e8d02361f21f709bc3357b9f6ead1d47ed43
Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Co-Developed-by: Christoph Hellwig <hch@lst.de>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Atish Patra <atish.patra@wdc.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/mm/tlbflush.c | 91 ++++++++++++++++++++++--------------------------
 1 file changed, 41 insertions(+), 50 deletions(-)

diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 87b4e52..facca6e 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -12,56 +12,59 @@ void flush_tlb_all(void)
 }
 
 /*
- * This function must not be called with cmask being null.
+ * This function must not be called with mm_cpumask(mm) being null.
  * Kernel may panic if cmask is NULL.
  */
-static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start,
+static void __sbi_tlb_flush_range(struct mm_struct *mm,
+				  unsigned long start,
 				  unsigned long size)
 {
+	struct cpumask *cmask = mm_cpumask(mm);
 	struct cpumask hmask;
 	unsigned int cpuid;
+	bool local;
 
 	if (cpumask_empty(cmask))
 		return;
 
 	cpuid = get_cpu();
 
-	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
-		/* local cpu is the only cpu present in cpumask */
-		if (size <= PAGE_SIZE)
-			local_flush_tlb_page(start);
-		else
-			local_flush_tlb_all();
-	} else {
-		riscv_cpuid_to_hartid_mask(cmask, &hmask);
-		sbi_remote_sfence_vma(cpumask_bits(&hmask), start, size);
-	}
+	/*
+	 * check if the tlbflush needs to be sent to other CPUs, local
+	 * cpu is the only cpu present in cpumask.
+	 */
+	local = !(cpumask_any_but(cmask, cpuid) < nr_cpu_ids);
 
-	put_cpu();
-}
-
-static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
-				       unsigned long start,
-				       unsigned long size,
-				       unsigned long asid)
-{
-	struct cpumask hmask;
-	unsigned int cpuid;
-
-	if (cpumask_empty(cmask))
-		return;
-
-	cpuid = get_cpu();
+	if (static_branch_likely(&use_asid_allocator)) {
+		unsigned long asid = atomic_long_read(&mm->context.id);
 
-	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
-		if (size == -1)
-			local_flush_tlb_all_asid(asid);
-		else
-			local_flush_tlb_range_asid(start, size, asid);
+		if (likely(local)) {
+			if (size == -1)
+				local_flush_tlb_all_asid(asid);
+			else
+				local_flush_tlb_range_asid(start, size, asid);
+		} else {
+			riscv_cpuid_to_hartid_mask(cmask, &hmask);
+			sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
+						   start, size, asid);
+		}
 	} else {
-		riscv_cpuid_to_hartid_mask(cmask, &hmask);
-		sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
-					   start, size, asid);
+		if (likely(local)) {
+			/*
+			 * FIXME: The non-ASID code switches to a global flush
+			 * once flushing more than a single page. It's made by
+			 * commit 6efb16b1d551 (RISC-V: Issue a tlb page flush
+			 * if possible).
+			 */
+			if (size <= PAGE_SIZE)
+				local_flush_tlb_page(start);
+			else
+				local_flush_tlb_all();
+		} else {
+			riscv_cpuid_to_hartid_mask(cmask, &hmask);
+			sbi_remote_sfence_vma(cpumask_bits(&hmask),
+					      start, size);
+		}
 	}
 
 	put_cpu();
@@ -69,28 +72,16 @@ static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
 
 void flush_tlb_mm(struct mm_struct *mm)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(mm), 0, -1,
-					   atomic_long_read(&mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
+	__sbi_tlb_flush_range(mm, 0, -1);
 }
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE,
-					   atomic_long_read(&vma->vm_mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
+	__sbi_tlb_flush_range(vma->vm_mm, addr, PAGE_SIZE);
 }
 
 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 		     unsigned long end)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), start, end - start,
-					   atomic_long_read(&vma->vm_mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
+	__sbi_tlb_flush_range(vma->vm_mm, start, end - start);
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH V5 3/3] riscv: tlbflush: Optimize coding convention
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (4 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 03/11] riscv: asid: Optimize tlbflush coding convention guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06  9:04 ` [RFC PATCH v2 04/11] riscv: pgtable: Fixup _PAGE_CHG_MASK usage guoren
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig, Atish Patra

From: Guo Ren <guoren@linux.alibaba.com>

Passing the mm_struct as the first argument, as we can derive both
the cpumask and asid from it instead of doing that in the callers.

But more importantly, the static branch check can be moved deeper
into the code to avoid a lot of duplication.

Also add FIXME comment on the non-ASID code switches to a global
flush once flushing more than a single page.

Link: https://lore.kernel.org/linux-riscv/CAJF2gTQpDYtEdw6ZrTVZUYqxGdhLPs25RjuUiQtz=xN2oKs2fw@mail.gmail.com/T/#m30f7e8d02361f21f709bc3357b9f6ead1d47ed43
Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Co-Developed-by: Christoph Hellwig <hch@lst.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Atish Patra <atish.patra@wdc.com>
---
 arch/riscv/mm/tlbflush.c | 91 ++++++++++++++++++++++--------------------------
 1 file changed, 41 insertions(+), 50 deletions(-)

diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 87b4e52..facca6e 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -12,56 +12,59 @@ void flush_tlb_all(void)
 }
 
 /*
- * This function must not be called with cmask being null.
+ * This function must not be called with mm_cpumask(mm) being null.
  * Kernel may panic if cmask is NULL.
  */
-static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start,
+static void __sbi_tlb_flush_range(struct mm_struct *mm,
+				  unsigned long start,
 				  unsigned long size)
 {
+	struct cpumask *cmask = mm_cpumask(mm);
 	struct cpumask hmask;
 	unsigned int cpuid;
+	bool local;
 
 	if (cpumask_empty(cmask))
 		return;
 
 	cpuid = get_cpu();
 
-	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
-		/* local cpu is the only cpu present in cpumask */
-		if (size <= PAGE_SIZE)
-			local_flush_tlb_page(start);
-		else
-			local_flush_tlb_all();
-	} else {
-		riscv_cpuid_to_hartid_mask(cmask, &hmask);
-		sbi_remote_sfence_vma(cpumask_bits(&hmask), start, size);
-	}
+	/*
+	 * check if the tlbflush needs to be sent to other CPUs, local
+	 * cpu is the only cpu present in cpumask.
+	 */
+	local = !(cpumask_any_but(cmask, cpuid) < nr_cpu_ids);
 
-	put_cpu();
-}
-
-static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
-				       unsigned long start,
-				       unsigned long size,
-				       unsigned long asid)
-{
-	struct cpumask hmask;
-	unsigned int cpuid;
-
-	if (cpumask_empty(cmask))
-		return;
-
-	cpuid = get_cpu();
+	if (static_branch_likely(&use_asid_allocator)) {
+		unsigned long asid = atomic_long_read(&mm->context.id);
 
-	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
-		if (size == -1)
-			local_flush_tlb_all_asid(asid);
-		else
-			local_flush_tlb_range_asid(start, size, asid);
+		if (likely(local)) {
+			if (size == -1)
+				local_flush_tlb_all_asid(asid);
+			else
+				local_flush_tlb_range_asid(start, size, asid);
+		} else {
+			riscv_cpuid_to_hartid_mask(cmask, &hmask);
+			sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
+						   start, size, asid);
+		}
 	} else {
-		riscv_cpuid_to_hartid_mask(cmask, &hmask);
-		sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
-					   start, size, asid);
+		if (likely(local)) {
+			/*
+			 * FIXME: The non-ASID code switches to a global flush
+			 * once flushing more than a single page. It's made by
+			 * commit 6efb16b1d551 (RISC-V: Issue a tlb page flush
+			 * if possible).
+			 */
+			if (size <= PAGE_SIZE)
+				local_flush_tlb_page(start);
+			else
+				local_flush_tlb_all();
+		} else {
+			riscv_cpuid_to_hartid_mask(cmask, &hmask);
+			sbi_remote_sfence_vma(cpumask_bits(&hmask),
+					      start, size);
+		}
 	}
 
 	put_cpu();
@@ -69,28 +72,16 @@ static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
 
 void flush_tlb_mm(struct mm_struct *mm)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(mm), 0, -1,
-					   atomic_long_read(&mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
+	__sbi_tlb_flush_range(mm, 0, -1);
 }
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE,
-					   atomic_long_read(&vma->vm_mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
+	__sbi_tlb_flush_range(vma->vm_mm, addr, PAGE_SIZE);
 }
 
 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 		     unsigned long end)
 {
-	if (static_branch_unlikely(&use_asid_allocator))
-		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), start, end - start,
-					   atomic_long_read(&vma->vm_mm->context.id));
-	else
-		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
+	__sbi_tlb_flush_range(vma->vm_mm, start, end - start);
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 04/11] riscv: pgtable: Fixup _PAGE_CHG_MASK usage
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (5 preceding siblings ...)
  2021-06-06  9:04 ` [PATCH V5 3/3] riscv: tlbflush: Optimize " guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06  9:04 ` [RFC PATCH v2 05/11] riscv: pgtable: Add custom protection_map init guoren
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

We should masks all attributes BITS first, and then
using '>> _PAGE_PFN_SHIFT' to get the final PFN value.

Adding '& _PAGE_CHG_MASK' makes the code semantics more accurate.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/include/asm/pgtable-64.h | 8 +++++---
 arch/riscv/include/asm/pgtable.h    | 6 +++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h
index f3b0da6..cbf9acf 100644
--- a/arch/riscv/include/asm/pgtable-64.h
+++ b/arch/riscv/include/asm/pgtable-64.h
@@ -62,12 +62,14 @@ static inline void pud_clear(pud_t *pudp)
 
 static inline unsigned long pud_page_vaddr(pud_t pud)
 {
-	return (unsigned long)pfn_to_virt(pud_val(pud) >> _PAGE_PFN_SHIFT);
+	return (unsigned long)pfn_to_virt(
+		(pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
 }
 
 static inline struct page *pud_page(pud_t pud)
 {
-	return pfn_to_page(pud_val(pud) >> _PAGE_PFN_SHIFT);
+	return pfn_to_page(
+		(pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
 }
 
 static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
@@ -77,7 +79,7 @@ static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
 
 static inline unsigned long _pmd_pfn(pmd_t pmd)
 {
-	return pmd_val(pmd) >> _PAGE_PFN_SHIFT;
+	return (pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT;
 }
 
 #define pmd_ERROR(e) \
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 346a3c6..13a79643 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -217,12 +217,12 @@ static inline unsigned long _pgd_pfn(pgd_t pgd)
 
 static inline struct page *pmd_page(pmd_t pmd)
 {
-	return pfn_to_page(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
+	return pfn_to_page((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
 }
 
 static inline unsigned long pmd_page_vaddr(pmd_t pmd)
 {
-	return (unsigned long)pfn_to_virt(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
+	return (unsigned long)pfn_to_virt((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
 }
 
 static inline pte_t pmd_pte(pmd_t pmd)
@@ -233,7 +233,7 @@ static inline pte_t pmd_pte(pmd_t pmd)
 /* Yields the page frame number (PFN) of a page table entry */
 static inline unsigned long pte_pfn(pte_t pte)
 {
-	return (pte_val(pte) >> _PAGE_PFN_SHIFT);
+	return ((pte_val(pte) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
 }
 
 #define pte_page(x)     pfn_to_page(pte_pfn(x))
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 05/11] riscv: pgtable: Add custom protection_map init
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (6 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 04/11] riscv: pgtable: Fixup _PAGE_CHG_MASK usage guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06  9:04 ` [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes guoren
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Andrew Morton, Palmer Dabbelt

From: Guo Ren <guoren@linux.alibaba.com>

Some RISC-V CPU vendors have defined their own PTE attributes to
solve non-coherent DMA bus problems. That makes _P/SXXX definitions
contain global variables which could be initialized at the early
boot stage before setup_vm.

This patch is similar to 316d097c4cd4  (x86/pti: Filter at
vma->vm_page_prot population) which give a choice for arch custom
implementation.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
---
 arch/riscv/Kconfig   |  4 ++++
 arch/riscv/mm/init.c | 22 ++++++++++++++++++++++
 mm/mmap.c            |  4 ++++
 3 files changed, 30 insertions(+)

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c5914e7..05c4976 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -25,6 +25,7 @@ config RISCV
 	select ARCH_HAS_GIGANTIC_PAGE
 	select ARCH_HAS_KCOV
 	select ARCH_HAS_MMIOWB
+	select ARCH_HAS_PROTECTION_MAP_INIT
 	select ARCH_HAS_PTE_SPECIAL
 	select ARCH_HAS_SET_DIRECT_MAP
 	select ARCH_HAS_SET_MEMORY
@@ -198,6 +199,9 @@ config GENERIC_HWEIGHT
 config FIX_EARLYCON_MEM
 	def_bool MMU
 
+config ARCH_HAS_PROTECTION_MAP_INIT
+	def_bool y
+
 config PGTABLE_LEVELS
 	int
 	default 3 if 64BIT
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 4faf8bd..4b398c6 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -496,6 +496,26 @@ static void __init create_kernel_page_table(pgd_t *pgdir, uintptr_t map_size)
 }
 #endif
 
+static void __init setup_protection_map(void)
+{
+	protection_map[0]  = __P000;
+	protection_map[1]  = __P001;
+	protection_map[2]  = __P010;
+	protection_map[3]  = __P011;
+	protection_map[4]  = __P100;
+	protection_map[5]  = __P101;
+	protection_map[6]  = __P110;
+	protection_map[7]  = __P111;
+	protection_map[8]  = __S000;
+	protection_map[9]  = __S001;
+	protection_map[10] = __S010;
+	protection_map[11] = __S011;
+	protection_map[12] = __S100;
+	protection_map[13] = __S101;
+	protection_map[14] = __S110;
+	protection_map[15] = __S111;
+}
+
 asmlinkage void __init setup_vm(uintptr_t dtb_pa)
 {
 	uintptr_t __maybe_unused pa;
@@ -504,6 +524,8 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
 	pmd_t fix_bmap_spmd, fix_bmap_epmd;
 #endif
 
+	setup_protection_map();
+
 #ifdef CONFIG_XIP_KERNEL
 	xiprom = (uintptr_t)CONFIG_XIP_PHYS_ADDR;
 	xiprom_sz = (uintptr_t)(&_exiprom) - (uintptr_t)(&_xiprom);
diff --git a/mm/mmap.c b/mm/mmap.c
index 0584e54..0a86059 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -100,10 +100,14 @@ static void unmap_region(struct mm_struct *mm,
  *								w: (no) no
  *								x: (yes) yes
  */
+#ifdef CONFIG_ARCH_HAS_PROTECTION_MAP_INIT
+pgprot_t protection_map[16] __ro_after_init;
+#else
 pgprot_t protection_map[16] __ro_after_init = {
 	__P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
 	__S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
 };
+#endif
 
 #ifndef CONFIG_ARCH_HAS_FILTER_PGPROT
 static inline pgprot_t arch_filter_pgprot(pgprot_t prot)
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (7 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 05/11] riscv: pgtable: Add custom protection_map init guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06 14:39   ` Christoph Hellwig
  2021-06-06 17:22   ` Nick Kossifidis
  2021-06-06  9:04 ` [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support guoren
                   ` (6 subsequent siblings)
  15 siblings, 2 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

The dma-noncoherent SOCs need different virtual memory mappings
with different attributes:
 - noncached + Strong Order (for IO/DMA descriptor)
 - noncached + Weak Order (for writecombine usage, eg: frame
   buffer)

All above base on PTE attributes by MMU hardware. That means
address attributes are determined by PTE entry, not PMA. RISC-V
soc vendors have defined their own custom PTE attributes for
dma-noncoherency.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Maxime Ripard <maxime@cerno.tech>
---
 arch/riscv/include/asm/pgtable-bits.h  | 20 +++++++++++++++++++-
 arch/riscv/include/asm/pgtable.h       | 11 ++++-------
 arch/riscv/include/asm/soc.h           |  1 +
 arch/riscv/include/asm/vendorid_list.h |  1 +
 arch/riscv/kernel/soc.c                | 22 ++++++++++++++++++++++
 arch/riscv/mm/init.c                   |  4 ++++
 6 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/arch/riscv/include/asm/pgtable-bits.h b/arch/riscv/include/asm/pgtable-bits.h
index bbaeb5d..080a9eb 100644
--- a/arch/riscv/include/asm/pgtable-bits.h
+++ b/arch/riscv/include/asm/pgtable-bits.h
@@ -24,6 +24,11 @@
 #define _PAGE_DIRTY     (1 << 7)    /* Set by hardware on any write */
 #define _PAGE_SOFT      (1 << 8)    /* Reserved for software */
 
+#define _PAGE_DMA_MASK	__riscv_custom_pte.mask
+#define _PAGE_DMA_CACHE	__riscv_custom_pte.cache
+#define _PAGE_DMA_IO	__riscv_custom_pte.io
+#define _PAGE_DMA_WC	__riscv_custom_pte.wc
+
 #define _PAGE_SPECIAL   _PAGE_SOFT
 #define _PAGE_TABLE     _PAGE_PRESENT
 
@@ -35,9 +40,22 @@
 
 #define _PAGE_PFN_SHIFT 10
 
+#ifndef __ASSEMBLY__
+
+struct riscv_custom_pte {
+	unsigned long cache;
+	unsigned long mask;
+	unsigned long io;
+	unsigned long wc;
+};
+
+extern struct riscv_custom_pte __riscv_custom_pte;
+
 /* Set of bits to preserve across pte_modify() */
 #define _PAGE_CHG_MASK  (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ |	\
 					  _PAGE_WRITE | _PAGE_EXEC |	\
-					  _PAGE_USER | _PAGE_GLOBAL))
+					  _PAGE_USER | _PAGE_GLOBAL |	\
+					  _PAGE_DMA_MASK))
+#endif
 
 #endif /* _ASM_RISCV_PGTABLE_BITS_H */
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 13a79643..6ddeb49 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -114,7 +114,7 @@
 #define USER_PTRS_PER_PGD   (TASK_SIZE / PGDIR_SIZE)
 
 /* Page protection bits */
-#define _PAGE_BASE	(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER)
+#define _PAGE_BASE	(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER | _PAGE_DMA_CACHE)
 
 #define PAGE_NONE		__pgprot(_PAGE_PROT_NONE)
 #define PAGE_READ		__pgprot(_PAGE_BASE | _PAGE_READ)
@@ -135,7 +135,8 @@
 				| _PAGE_PRESENT \
 				| _PAGE_ACCESSED \
 				| _PAGE_DIRTY \
-				| _PAGE_GLOBAL)
+				| _PAGE_GLOBAL \
+				| _PAGE_DMA_CACHE)
 
 #define PAGE_KERNEL		__pgprot(_PAGE_KERNEL)
 #define PAGE_KERNEL_READ	__pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
@@ -145,11 +146,7 @@
 
 #define PAGE_TABLE		__pgprot(_PAGE_TABLE)
 
-/*
- * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
- * change the properties of memory regions.
- */
-#define _PAGE_IOREMAP _PAGE_KERNEL
+#define _PAGE_IOREMAP	((_PAGE_KERNEL & ~_PAGE_DMA_MASK) | _PAGE_DMA_IO)
 
 extern pgd_t swapper_pg_dir[];
 
diff --git a/arch/riscv/include/asm/soc.h b/arch/riscv/include/asm/soc.h
index f494066..fc587d7 100644
--- a/arch/riscv/include/asm/soc.h
+++ b/arch/riscv/include/asm/soc.h
@@ -17,6 +17,7 @@
 		 = { .compatible = compat, .data = fn  }
 
 void soc_early_init(void);
+void soc_setup_vm(void);
 
 extern unsigned long __soc_early_init_table_start;
 extern unsigned long __soc_early_init_table_end;
diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
index 9d93421..c2710f3 100644
--- a/arch/riscv/include/asm/vendorid_list.h
+++ b/arch/riscv/include/asm/vendorid_list.h
@@ -6,5 +6,6 @@
 #define ASM_VENDOR_LIST_H
 
 #define SIFIVE_VENDOR_ID	0x489
+#define  THEAD_VENDOR_ID	0x401
 
 #endif
diff --git a/arch/riscv/kernel/soc.c b/arch/riscv/kernel/soc.c
index a051617..05fa764 100644
--- a/arch/riscv/kernel/soc.c
+++ b/arch/riscv/kernel/soc.c
@@ -3,8 +3,10 @@
  * Copyright (C) 2020 Western Digital Corporation or its affiliates.
  */
 #include <linux/init.h>
+#include <linux/mm.h>
 #include <linux/libfdt.h>
 #include <linux/pgtable.h>
+#include <asm/image.h>
 #include <asm/soc.h>
 
 /*
@@ -26,3 +28,23 @@ void __init soc_early_init(void)
 		}
 	}
 }
+
+static void __init thead_init(void)
+{
+	__riscv_custom_pte.cache = 0x7000000000000000;
+	__riscv_custom_pte.mask  = 0xf800000000000000;
+	__riscv_custom_pte.io    = BIT(63);
+	__riscv_custom_pte.wc    = 0;
+}
+
+void __init soc_setup_vm(void)
+{
+	unsigned long vendor_id =
+		((struct riscv_image_header *)(&_start))->res1;
+
+	switch (vendor_id) {
+	case THEAD_VENDOR_ID:
+		thead_init();
+		break;
+	}
+};
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 4b398c6..fb70c49 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -524,6 +524,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
 	pmd_t fix_bmap_spmd, fix_bmap_epmd;
 #endif
 
+	soc_setup_vm();
 	setup_protection_map();
 
 #ifdef CONFIG_XIP_KERNEL
@@ -911,3 +912,6 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
 	return vmemmap_populate_basepages(start, end, node, NULL);
 }
 #endif
+
+struct riscv_custom_pte __riscv_custom_pte __ro_after_init;
+EXPORT_SYMBOL(__riscv_custom_pte);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (8 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes guoren
@ 2021-06-06  9:04 ` guoren
  2021-10-17  9:28   ` twd2
  2021-06-06  9:04 ` [RFC PATCH v2 08/11] riscv: cmo: Add vendor custom icache sync guoren
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

To support DMA device in a non-coherent interconnect SOC system,
we need the below facilities:
 - Changing a virtual memory mapping region attributes from
   cacheable to noncache + strong order which used in DMA
   descriptors.
 - Add noncache + weakorder virtual memory attributes for dma
   mapping.
 - Syncing the cache with memory before DMA start and after DMA
   end with vendor custom CMO instructions.

This patch enables linux kernel generic dma-noncoherency
infrastructure and introduces new sbi_ecall API for dma_sync.

@@ -27,6 +27,7 @@ enum sbi_ext_id {
+       SBI_EXT_DMA = 0xAB150401,

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Maxime Ripard <maxime@cerno.tech>
---
 arch/riscv/Kconfig               |  5 ++++
 arch/riscv/include/asm/pgtable.h | 26 ++++++++++++++++++++
 arch/riscv/include/asm/sbi.h     | 15 ++++++++++++
 arch/riscv/kernel/sbi.c          | 19 ++++++++++++++
 arch/riscv/mm/Makefile           |  1 +
 arch/riscv/mm/dma-mapping.c      | 53 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 119 insertions(+)
 create mode 100644 arch/riscv/mm/dma-mapping.c

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 05c4976..817a9bb 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -20,6 +20,10 @@ config RISCV
 	select ARCH_HAS_DEBUG_VM_PGTABLE
 	select ARCH_HAS_DEBUG_VIRTUAL if MMU
 	select ARCH_HAS_DEBUG_WX
+	select ARCH_HAS_DMA_PREP_COHERENT
+	select ARCH_HAS_SYNC_DMA_FOR_CPU
+	select ARCH_HAS_SYNC_DMA_FOR_DEVICE
+	select ARCH_HAS_DMA_WRITE_COMBINE
 	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
 	select ARCH_HAS_GIGANTIC_PAGE
@@ -43,6 +47,7 @@ config RISCV
 	select CLONE_BACKWARDS
 	select CLINT_TIMER if !MMU
 	select COMMON_CLK
+	select DMA_DIRECT_REMAP
 	select EDAC_SUPPORT
 	select GENERIC_ARCH_TOPOLOGY if SMP
 	select GENERIC_ATOMIC64 if !64BIT
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 6ddeb49..e1a82b6 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -462,6 +462,32 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
 	return ptep_test_and_clear_young(vma, address, ptep);
 }
 
+#define pgprot_noncached pgprot_noncached
+static inline pgprot_t pgprot_noncached(pgprot_t _prot)
+{
+	unsigned long prot = pgprot_val(_prot);
+
+	prot &= ~_PAGE_DMA_MASK;
+	prot |= _PAGE_DMA_IO;
+
+	return __pgprot(prot);
+}
+
+#define pgprot_writecombine pgprot_writecombine
+static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
+{
+	unsigned long prot = pgprot_val(_prot);
+
+	prot &= ~_PAGE_DMA_MASK;
+	prot |= _PAGE_DMA_WC;
+
+	return __pgprot(prot);
+}
+
+#define __HAVE_PHYS_MEM_ACCESS_PROT
+extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+				     unsigned long size, pgprot_t vma_prot);
+
 /*
  * Encode and decode a swap entry
  *
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 0d42693..133e88a 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -27,6 +27,7 @@ enum sbi_ext_id {
 	SBI_EXT_IPI = 0x735049,
 	SBI_EXT_RFENCE = 0x52464E43,
 	SBI_EXT_HSM = 0x48534D,
+	SBI_EXT_DMA = 0xAB150401,
 };
 
 enum sbi_ext_base_fid {
@@ -63,6 +64,17 @@ enum sbi_ext_hsm_fid {
 	SBI_EXT_HSM_HART_STATUS,
 };
 
+enum sbi_ext_dma_fid {
+	SBI_DMA_SYNC = 0,
+};
+
+enum sbi_dma_sync_data_direction {
+	SBI_DMA_BIDIRECTIONAL = 0,
+	SBI_DMA_TO_DEVICE = 1,
+	SBI_DMA_FROM_DEVICE = 2,
+	SBI_DMA_NONE = 3,
+};
+
 enum sbi_hsm_hart_status {
 	SBI_HSM_HART_STATUS_STARTED = 0,
 	SBI_HSM_HART_STATUS_STOPPED,
@@ -128,6 +140,9 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
 				unsigned long size,
 				unsigned long asid);
 int sbi_probe_extension(int ext);
+void sbi_dma_sync(unsigned long start,
+		  unsigned long size,
+		  enum sbi_dma_sync_data_direction dir);
 
 /* Check if current SBI specification version is 0.1 or not */
 static inline int sbi_spec_is_0_1(void)
diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
index 7402a41..c936019 100644
--- a/arch/riscv/kernel/sbi.c
+++ b/arch/riscv/kernel/sbi.c
@@ -521,6 +521,25 @@ int sbi_probe_extension(int extid)
 }
 EXPORT_SYMBOL(sbi_probe_extension);
 
+void sbi_dma_sync(unsigned long start,
+		  unsigned long size,
+		  enum sbi_dma_sync_data_direction dir)
+{
+#if 0
+	sbi_ecall(SBI_EXT_DMA, SBI_DMA_SYNC, start, size, dir,
+		  0, 0, 0);
+#else
+	/* Just for try, it should be in sbi ecall and will be removed before merged */
+	register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1);
+
+	for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES)
+		__asm__ __volatile__(".long 0x02b5000b");
+
+	__asm__ __volatile__(".long 0x01b0000b");
+#endif
+}
+EXPORT_SYMBOL(sbi_dma_sync);
+
 static long __sbi_base_ecall(int fid)
 {
 	struct sbiret ret;
diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
index 7ebaef1..ca0ff90 100644
--- a/arch/riscv/mm/Makefile
+++ b/arch/riscv/mm/Makefile
@@ -13,6 +13,7 @@ obj-y += extable.o
 obj-$(CONFIG_MMU) += fault.o pageattr.o
 obj-y += cacheflush.o
 obj-y += context.o
+obj-y += dma-mapping.o
 
 ifeq ($(CONFIG_MMU),y)
 obj-$(CONFIG_SMP) += tlbflush.o
diff --git a/arch/riscv/mm/dma-mapping.c b/arch/riscv/mm/dma-mapping.c
new file mode 100644
index 00000000..4afd9dc
--- /dev/null
+++ b/arch/riscv/mm/dma-mapping.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/dma-map-ops.h>
+#include <asm/sbi.h>
+
+void arch_dma_prep_coherent(struct page *page, size_t size)
+{
+	void *ptr = page_address(page);
+
+	memset(ptr, 0, size);
+	sbi_dma_sync(page_to_phys(page), size, SBI_DMA_BIDIRECTIONAL);
+}
+
+void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
+		enum dma_data_direction dir)
+{
+	switch (dir) {
+	case DMA_TO_DEVICE:
+	case DMA_FROM_DEVICE:
+	case DMA_BIDIRECTIONAL:
+		sbi_dma_sync(paddr, size, dir);
+		break;
+	default:
+		BUG();
+	}
+}
+
+void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
+		enum dma_data_direction dir)
+{
+	switch (dir) {
+	case DMA_TO_DEVICE:
+		return;
+	case DMA_FROM_DEVICE:
+	case DMA_BIDIRECTIONAL:
+		sbi_dma_sync(paddr, size, dir);
+		break;
+	default:
+		BUG();
+	}
+}
+
+pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+			      unsigned long size, pgprot_t vma_prot)
+{
+	if (!pfn_valid(pfn))
+		return pgprot_noncached(vma_prot);
+	else if (file->f_flags & O_SYNC)
+		return pgprot_writecombine(vma_prot);
+
+	return vma_prot;
+}
+EXPORT_SYMBOL(phys_mem_access_prot);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 08/11] riscv: cmo: Add vendor custom icache sync
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (9 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06  9:04 ` [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board guoren
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren, Atish Patra

From: Guo Ren <guoren@linux.alibaba.com>

It's a draft version to show you how T-HEAD C9xx work with the
icache sync (We use hardware broadcast mechanism, and our icache
is VIPT):
 - icache.i(v/p)a will broadcast all harts' icache invalidtion
 - sync.is will broadcast all harts' pipeline flush and ensure all
   broadcasts finished.

This patch could improve the performance of OpenJDK on JIT and
reduce flush_icache_all in linux.

Epecially:
static inline void set_pte_at(struct mm_struct *mm,
        unsigned long addr, pte_t *ptep, pte_t pteval)
{
	if (pte_present(pteval) && pte_exec(pteval))
		flush_icache_pte(pteval);

	set_pte(ptep, pteval);
}

Different from sbi_dma_sync, it can't be hidden in SBI and we must
set up a framework to hold all vendors' implementations in
linux/arch/riscv.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Atish Patra <atish.patra@wdc.com>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/include/asm/cacheflush.h   | 48 ++++++++++++++++++++++++++++++++++-
 arch/riscv/kernel/vdso/flush_icache.S | 33 +++++++++++++++++++-----
 arch/riscv/mm/cacheflush.c            |  3 ++-
 3 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h
index 23ff703..2e2dba1 100644
--- a/arch/riscv/include/asm/cacheflush.h
+++ b/arch/riscv/include/asm/cacheflush.h
@@ -22,11 +22,57 @@ static inline void flush_dcache_page(struct page *page)
 }
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
 
+#define ICACHE_IPA_X5   ".long 0x0382800b"
+#define ICACHE_IVA_X5   ".long 0x0302800b"
+#define SYNC_IS         ".long 0x01b0000b"
+
+static inline void flush_icache_range(unsigned long start, unsigned long end)
+{
+	register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1));
+
+	for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) {
+		__asm__ __volatile__ (
+				ICACHE_IVA_X5
+				:
+				: "r" (tmp)
+				: "memory");
+	}
+
+	__asm__ __volatile__(SYNC_IS);
+
+	return;
+}
+
+static inline void flush_icache_range_phy(unsigned long start, unsigned long end)
+{
+	register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1));
+
+	for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) {
+		__asm__ __volatile__ (
+				ICACHE_IPA_X5
+				:
+				: "r" (tmp)
+				: "memory");
+	}
+
+	__asm__ __volatile__(SYNC_IS);
+
+	return;
+}
+
+static inline void __flush_icache_page(struct page *page) {
+	unsigned long start = PFN_PHYS(page_to_pfn(page));
+
+	flush_icache_range_phy(start, start + PAGE_SIZE);
+
+	return;
+}
+
 /*
  * RISC-V doesn't have an instruction to flush parts of the instruction cache,
  * so instead we just flush the whole thing.
  */
-#define flush_icache_range(start, end) flush_icache_all()
+#define flush_icache_range(start, end) flush_icache_range(start, end)
 #define flush_icache_user_page(vma, pg, addr, len) \
 	flush_icache_mm(vma->vm_mm, 0)
 
diff --git a/arch/riscv/kernel/vdso/flush_icache.S b/arch/riscv/kernel/vdso/flush_icache.S
index 82f97d6..efb2d2e 100644
--- a/arch/riscv/kernel/vdso/flush_icache.S
+++ b/arch/riscv/kernel/vdso/flush_icache.S
@@ -5,18 +5,39 @@
 
 #include <linux/linkage.h>
 #include <asm/unistd.h>
+#include <asm/cache.h>
+
+/*
+ * icache.ipa rs1
+ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
+ *   0000001    11000      rs1       000      00000  0001011
+ *
+ * icache.iva rs1
+ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
+ *   0000001    10000      rs1       000      00000  0001011
+ *
+ * sync.is
+ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
+ *   0000000    11011     00000      000      00000  0001011
+ */
+#define ICACHE_IPA_X5	.long 0x0382800b
+#define ICACHE_IVA_X5	.long 0x0302800b
+#define SYNC_IS		.long 0x01b0000b
 
 	.text
 /* int __vdso_flush_icache(void *start, void *end, unsigned long flags); */
 ENTRY(__vdso_flush_icache)
 	.cfi_startproc
-#ifdef CONFIG_SMP
-	li a7, __NR_riscv_flush_icache
-	ecall
-#else
-	fence.i
+	srli	t0, a0, L1_CACHE_SHIFT
+	slli	t0, t0, L1_CACHE_SHIFT
+	addi	a1, a1, (L1_CACHE_BYTES - 1)
+	srli	a1, a1, L1_CACHE_SHIFT
+	slli	a1, a1, L1_CACHE_SHIFT
+1:	ICACHE_IVA_X5
+	addi	t0, t0, L1_CACHE_BYTES
+	bne	t0, a1, 1b
+	SYNC_IS
 	li a0, 0
-#endif
 	ret
 	.cfi_endproc
 ENDPROC(__vdso_flush_icache)
diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c
index 0941186..0fb8344 100644
--- a/arch/riscv/mm/cacheflush.c
+++ b/arch/riscv/mm/cacheflush.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2017 SiFive
  */
 
+#include <linux/pfn.h>
 #include <asm/cacheflush.h>
 
 #ifdef CONFIG_SMP
@@ -84,6 +85,6 @@ void flush_icache_pte(pte_t pte)
 	struct page *page = pte_page(pte);
 
 	if (!test_and_set_bit(PG_dcache_clean, &page->flags))
-		flush_icache_all();
+		__flush_icache_page(page);
 }
 #endif /* CONFIG_MMU */
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (10 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 08/11] riscv: cmo: Add vendor custom icache sync guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06 16:26   ` Jernej Škrabec
  2021-06-07  7:24   ` Maxime Ripard
  2021-06-06  9:04 ` [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option guoren
                   ` (3 subsequent siblings)
  15 siblings, 2 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Atish Patra, Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

Add initial DTS for Allwinner D1 NeZha board having only essential
devices (uart, dummy, clock, reset, clint, plic, etc).

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Atish Patra <atish.patra@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/boot/dts/Makefile                       |  1 +
 arch/riscv/boot/dts/allwinner/Makefile             |  2 +
 .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84 ++++++++++++++++++++++
 4 files changed, 116 insertions(+)
 create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
 create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
 create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
index fe996b8..3e7b264 100644
--- a/arch/riscv/boot/dts/Makefile
+++ b/arch/riscv/boot/dts/Makefile
@@ -2,5 +2,6 @@
 subdir-y += sifive
 subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
 subdir-y += microchip
+subdir-y += allwinner
 
 obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile
new file mode 100644
index 00000000..4adbf4b
--- /dev/null
+++ b/arch/riscv/boot/dts/allwinner/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
new file mode 100644
index 00000000..cd9f7c9
--- /dev/null
+++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	model = "Allwinner D1 NeZha Kit";
+	compatible = "allwinner,d1-nezha-kit";
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &serial0;
+	};
+
+	memory@40000000 {
+		device_type = "memory";
+		reg = <0x0 0x40000000 0x0 0x20000000>;
+	};
+
+	soc {
+	};
+};
+
+&serial0 {
+	status = "okay";
+};
diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
new file mode 100644
index 00000000..11cd938
--- /dev/null
+++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+
+/dts-v1/;
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	model = "Allwinner D1 Soc";
+	compatible = "allwinner,d1-nezha-kit";
+
+	chosen {
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		timebase-frequency = <2400000>;
+		cpu@0 {
+			device_type = "cpu";
+			reg = <0>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64imafdcv";
+			mmu-type = "riscv,sv39";
+			cpu0_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+			};
+		};
+	};
+
+	soc {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		compatible = "simple-bus";
+		ranges;
+
+		reset: reset-sample {
+			compatible = "thead,reset-sample";
+			plic-delegate = <0x0 0x101ffffc>;
+		};
+
+		clint: clint@14000000 {
+			compatible = "riscv,clint0";
+			interrupts-extended = <
+				&cpu0_intc  3 &cpu0_intc  7
+				>;
+			reg = <0x0 0x14000000 0x0 0x04000000>;
+			clint,has-no-64bit-mmio;
+		};
+
+		plic: interrupt-controller@10000000 {
+			#interrupt-cells = <1>;
+			compatible = "riscv,plic0";
+			interrupt-controller;
+			interrupts-extended = <
+				&cpu0_intc  0xffffffff &cpu0_intc  9
+				>;
+			reg = <0x0 0x10000000 0x0 0x04000000>;
+			reg-names = "control";
+			riscv,max-priority = <7>;
+			riscv,ndev = <200>;
+		};
+
+		dummy_apb: apb-clock {
+			compatible = "fixed-clock";
+			clock-frequency = <24000000>;
+			clock-output-names = "dummy_apb";
+			#clock-cells = <0>;
+		};
+
+		serial0: serial@2500000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x0 0x02500000 0x0 0x400>;
+			reg-io-width = <4>;
+			reg-shift = <2>;
+			interrupt-parent = <&plic>;
+			interrupts = <18>;
+			clocks = <&dummy_apb>;
+			status = "disabled";
+		};
+	};
+};
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (11 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-07  7:19   ` Maxime Ripard
  2021-06-06  9:04 ` [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use guoren
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Atish Patra, Christoph Hellwig

From: Guo Ren <guoren@linux.alibaba.com>

Add Allwinner kconfig option which selects SoC specific and common
drivers that is required for this SoC.

Allwinner D1 uses custom PTE attributes to solve non-coherency SOC
interconnect issues for dma synchronization, so we set the default
value when SOC_SUNXI selected.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Cc: Anup Patel <anup.patel@wdc.com>
Cc: Atish Patra <atish.patra@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Drew Fustini <drew@beagleboard.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)

diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index ed96376..055fb3e 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
+config SOC_SUNXI
+	bool "Allwinner SoCs"
+	depends on MMU
+	select DWMAC_GENERIC
+	select SERIAL_8250
+	select SERIAL_8250_CONSOLE
+	select SERIAL_8250_DW
+	select SIFIVE_PLIC
+	select STMMAC_ETH
+	help
+	  This enables support for Allwinner SoC platforms like the D1.
+
 endmenu
diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index 1f2be23..9e83d14 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -15,6 +15,7 @@ CONFIG_BLK_DEV_INITRD=y
 CONFIG_EXPERT=y
 CONFIG_BPF_SYSCALL=y
 CONFIG_SOC_SIFIVE=y
+CONFIG_SOC_SUNXI=y
 CONFIG_SOC_VIRT=y
 CONFIG_SOC_MICROCHIP_POLARFIRE=y
 CONFIG_SMP=y
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (12 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option guoren
@ 2021-06-06  9:04 ` guoren
  2021-06-06 10:50   ` Andre Przywara
  2021-06-06 16:16   ` Arnd Bergmann
  2021-06-06 16:29 ` [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 Jernej Škrabec
  2021-06-07  3:44 ` Anup Patel
  15 siblings, 2 replies; 52+ messages in thread
From: guoren @ 2021-06-06  9:04 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi,
	Maxime Ripard, Corentin Labbe, Samuel Holland, Icenowy Zheng,
	LABBE Corentin, Michael Walle, Guo Ren

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 88171 bytes --]

From: liush <liush@allwinnertech.com>

This is a temporary driver, only guaranteed to work on allwinner
D1. In order to ensure the developer's demand for network usage.

It only could work at 1Gps mode.

The correct gmac driver should follow (I guess)
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c

If anyone is familiar with it and can help porting, I would be
very grateful.

Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
Tested-by: Guo Ren <guoren@kernel.org>
Signed-off-by: Guo Ren <guoren@kernel.org>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Corentin Labbe <clabbe@baylibre.com>
Cc: Samuel Holland <samuel@sholland.org>
Cc: Icenowy Zheng <icenowy@aosc.io>
Cc: LABBE Corentin <clabbe.montjoie@gmail.com>
Cc: Michael Walle <michael@walle.cc>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Wei Fu <wefu@redhat.com>
Cc: Wei Wu <lazyparser@gmail.com>
Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
---
 .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |    2 +-
 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |   16 +
 drivers/net/ethernet/Kconfig                       |    1 +
 drivers/net/ethernet/Makefile                      |    1 +
 drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
 drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240 ++++++++++++++++++++
 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
 drivers/net/phy/realtek.c                          |    2 +-
 10 files changed, 3232 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
 create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
 create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h

diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
index cd9f7c9..31b681d 100644
--- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
+++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
@@ -11,7 +11,7 @@
 	compatible = "allwinner,d1-nezha-kit";
 
 	chosen {
-		bootargs = "console=ttyS0,115200";
+		bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";
 		stdout-path = &serial0;
 	};
 
diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
index 11cd938..d317e19 100644
--- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
+++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
@@ -80,5 +80,21 @@
 			clocks = <&dummy_apb>;
 			status = "disabled";
 		};
+
+		eth@4500000 {
+			compatible = "allwinner,sunxi-gmac";
+			reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
+			interrupts-extended = <&plic 0x3e 0x04>;
+			interrupt-names = "gmacirq";
+			device_type = "gmac0";
+			phy-mode = "rgmii";
+			use_ephy25m = <0x01>;
+			tx-delay = <0x03>;
+			rx-delay = <0x03>;
+			gmac-power0;
+			gmac-power1;
+			gmac-power2;
+			status = "okay";
+		};
 	};
 };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 1cdff1d..1f8e37c 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -18,6 +18,7 @@ config MDIO
 config SUNGEM_PHY
 	tristate
 
+source "drivers/net/ethernet/allwinnertmp/Kconfig"
 source "drivers/net/ethernet/3com/Kconfig"
 source "drivers/net/ethernet/actions/Kconfig"
 source "drivers/net/ethernet/adaptec/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index cb3f908..3dacc0c 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the Linux network Ethernet device drivers.
 #
 
+obj-y += allwinnertmp/
 obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
 obj-$(CONFIG_NET_VENDOR_8390) += 8390/
 obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/
diff --git a/drivers/net/ethernet/allwinnertmp/Kconfig b/drivers/net/ethernet/allwinnertmp/Kconfig
new file mode 100644
index 00000000..4b7b378
--- /dev/null
+++ b/drivers/net/ethernet/allwinnertmp/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Allwinner device configuration
+#
+
+config SUNXI_GMAC
+	tristate "Allwinner GMAC support"
+	default y
+	depends on OF
+	select CRC32
+	select MII
+	select PHYLIB
+	help
+	  Support for Allwinner Gigabit ethernet driver.
+
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sunxi-gmac.
diff --git a/drivers/net/ethernet/allwinnertmp/Makefile b/drivers/net/ethernet/allwinnertmp/Makefile
new file mode 100644
index 00000000..1375dea
--- /dev/null
+++ b/drivers/net/ethernet/allwinnertmp/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Allwinner device drivers.
+#
+
+obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o
+sunxi_gmac-objs := sunxi-gmac.o sunxi-gmac-ops.o
diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
new file mode 100644
index 00000000..26ffd7f
--- /dev/null
+++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
@@ -0,0 +1,690 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
+ *
+ * This file is provided under a dual BSD/GPL license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/printk.h>
+#include <linux/io.h>
+#include "sunxi-gmac.h"
+
+/******************************************************************************
+ *	sun8iw6 operations
+ *****************************************************************************/
+#define GETH_BASIC_CTL0		0x00
+#define GETH_BASIC_CTL1		0x04
+#define GETH_INT_STA		0x08
+#define GETH_INT_EN		0x0C
+#define GETH_TX_CTL0		0x10
+#define GETH_TX_CTL1		0x14
+#define GETH_TX_FLOW_CTL	0x1C
+#define GETH_TX_DESC_LIST	0x20
+#define GETH_RX_CTL0		0x24
+#define GETH_RX_CTL1		0x28
+#define GETH_RX_DESC_LIST	0x34
+#define GETH_RX_FRM_FLT		0x38
+#define GETH_RX_HASH0		0x40
+#define GETH_RX_HASH1		0x44
+#define GETH_MDIO_ADDR		0x48
+#define GETH_MDIO_DATA		0x4C
+#define GETH_ADDR_HI(reg)	(0x50 + ((reg) << 3))
+#define GETH_ADDR_LO(reg)	(0x54 + ((reg) << 3))
+#define GETH_TX_DMA_STA		0xB0
+#define GETH_TX_CUR_DESC	0xB4
+#define GETH_TX_CUR_BUF		0xB8
+#define GETH_RX_DMA_STA		0xC0
+#define GETH_RX_CUR_DESC	0xC4
+#define GETH_RX_CUR_BUF		0xC8
+#define GETH_RGMII_STA		0xD0
+
+#define RGMII_IRQ		0x00000001
+
+#define	CTL0_LM			0x02
+#define CTL0_DM			0x01
+#define CTL0_SPEED		0x04
+
+#define BURST_LEN		0x3F000000
+#define RX_TX_PRI		0x02
+#define SOFT_RST		0x01
+
+#define TX_FLUSH		0x01
+#define TX_MD			0x02
+#define TX_NEXT_FRM		0x04
+#define TX_TH			0x0700
+
+#define RX_FLUSH		0x01
+#define RX_MD			0x02
+#define RX_RUNT_FRM		0x04
+#define RX_ERR_FRM		0x08
+#define RX_TH			0x0030
+
+#define TX_INT			0x00001
+#define TX_STOP_INT		0x00002
+#define TX_UA_INT		0x00004
+#define TX_TOUT_INT		0x00008
+#define TX_UNF_INT		0x00010
+#define TX_EARLY_INT		0x00020
+#define RX_INT			0x00100
+#define RX_UA_INT		0x00200
+#define RX_STOP_INT		0x00400
+#define RX_TOUT_INT		0x00800
+#define RX_OVF_INT		0x01000
+#define RX_EARLY_INT		0x02000
+#define LINK_STA_INT		0x10000
+
+#define DISCARD_FRAME	-1
+#define GOOD_FRAME	0
+#define CSUM_NONE	2
+#define LLC_SNAP	4
+
+#define SF_DMA_MODE		1
+
+/* Flow Control defines */
+#define FLOW_OFF	0
+#define FLOW_RX		1
+#define FLOW_TX		2
+#define FLOW_AUTO	(FLOW_TX | FLOW_RX)
+
+#define HASH_TABLE_SIZE 64
+#define PAUSE_TIME 0x200
+#define GMAC_MAX_UNICAST_ADDRESSES	8
+
+/* PHY address */
+#define PHY_ADDR		0x01
+#define PHY_DM			0x0010
+#define PHY_AUTO_NEG		0x0020
+#define PHY_POWERDOWN		0x0080
+#define PHY_NEG_EN		0x1000
+
+#define MII_BUSY		0x00000001
+#define MII_WRITE		0x00000002
+#define MII_PHY_MASK		0x0000FFC0
+#define MII_CR_MASK		0x0000001C
+#define MII_CLK			0x00000008
+/* bits 4 3 2 | AHB1 Clock	| MDC Clock
+ * -------------------------------------------------------
+ *      0 0 0 | 60 ~ 100 MHz	| div-42
+ *      0 0 1 | 100 ~ 150 MHz	| div-62
+ *      0 1 0 | 20 ~ 35 MHz	| div-16
+ *      0 1 1 | 35 ~ 60 MHz	| div-26
+ *      1 0 0 | 150 ~ 250 MHz	| div-102
+ *      1 0 1 | 250 ~ 300 MHz	| div-124
+ *      1 1 x | Reserved	|
+ */
+
+enum csum_insertion {
+	cic_dis		= 0, /* Checksum Insertion Control */
+	cic_ip		= 1, /* Only IP header */
+	cic_no_pse	= 2, /* IP header but not pseudoheader */
+	cic_full	= 3, /* IP header and pseudoheader */
+};
+
+struct gethdev {
+	void *iobase;
+	unsigned int ver;
+	unsigned int mdc_div;
+};
+
+static struct gethdev hwdev;
+
+/***************************************************************************
+ * External interface
+ **************************************************************************/
+/* Set a ring desc buffer */
+void desc_init_chain(struct dma_desc *desc, unsigned long addr, unsigned int size)
+{
+	/* In chained mode the desc3 points to the next element in the ring.
+	 * The latest element has to point to the head.
+	 */
+	int i;
+	struct dma_desc *p = desc;
+	unsigned long dma_phy = addr;
+
+	for (i = 0; i < (size - 1); i++) {
+		dma_phy += sizeof(struct dma_desc);
+		p->desc3 = (unsigned int)dma_phy;
+		/* Chain mode */
+		p->desc1.all |= (1 << 24);
+		p++;
+	}
+	p->desc1.all |= (1 << 24);
+	p->desc3 = (unsigned int)addr;
+}
+
+int sunxi_mdio_read(void *iobase, int phyaddr, int phyreg)
+{
+	unsigned int value = 0;
+
+	/* Mask the MDC_DIV_RATIO */
+	value |= ((hwdev.mdc_div & 0x07) << 20);
+	value |= (((phyaddr << 12) & (0x0001F000)) |
+			((phyreg << 4) & (0x000007F0)) |
+			MII_BUSY);
+
+	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
+		;
+
+	writel(value, iobase + GETH_MDIO_ADDR);
+	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
+		;
+
+	return (int)readl(iobase + GETH_MDIO_DATA);
+}
+
+int sunxi_mdio_write(void *iobase, int phyaddr, int phyreg, unsigned short data)
+{
+	unsigned int value;
+
+	value = ((0x07 << 20) & readl(iobase + GETH_MDIO_ADDR)) |
+		 (hwdev.mdc_div << 20);
+	value |= (((phyaddr << 12) & (0x0001F000)) |
+		  ((phyreg << 4) & (0x000007F0))) |
+		  MII_WRITE | MII_BUSY;
+
+	/* Wait until any existing MII operation is complete */
+	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
+		;
+
+	/* Set the MII address register to write */
+	writel(data, iobase + GETH_MDIO_DATA);
+	writel(value, iobase + GETH_MDIO_ADDR);
+
+	/* Wait until any existing MII operation is complete */
+	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
+		;
+
+	return 0;
+}
+
+int sunxi_mdio_reset(void *iobase)
+{
+	writel((4 << 2), iobase + GETH_MDIO_ADDR);
+	return 0;
+}
+
+void sunxi_set_link_mode(void *iobase, int duplex, int speed)
+{
+	unsigned int ctrl = readl(iobase + GETH_BASIC_CTL0);
+
+	if (!duplex)
+		ctrl &= ~CTL0_DM;
+	else
+		ctrl |= CTL0_DM;
+
+	switch (speed) {
+	case 1000:
+		ctrl &= ~0x0C;
+		break;
+	case 100:
+	case 10:
+	default:
+		ctrl |= 0x08;
+		if (speed == 100)
+			ctrl |= 0x04;
+		else
+			ctrl &= ~0x04;
+		break;
+	}
+
+	writel(ctrl, iobase + GETH_BASIC_CTL0);
+}
+
+void sunxi_mac_loopback(void *iobase, int enable)
+{
+	int reg;
+
+	reg = readl(iobase + GETH_BASIC_CTL0);
+	if (enable)
+		reg |= 0x02;
+	else
+		reg &= ~0x02;
+	writel(reg, iobase + GETH_BASIC_CTL0);
+}
+
+void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause)
+{
+	unsigned int flow = 0;
+
+	if (fc & FLOW_RX) {
+		flow = readl(iobase + GETH_RX_CTL0);
+		flow |= 0x10000;
+		writel(flow, iobase + GETH_RX_CTL0);
+	}
+
+	if (fc & FLOW_TX) {
+		flow = readl(iobase + GETH_TX_FLOW_CTL);
+		flow |= 0x00001;
+		writel(flow, iobase + GETH_TX_FLOW_CTL);
+	}
+
+	if (duplex) {
+		flow = readl(iobase + GETH_TX_FLOW_CTL);
+		flow |= (pause << 4);
+		writel(flow, iobase + GETH_TX_FLOW_CTL);
+	}
+}
+
+int sunxi_int_status(void *iobase, struct geth_extra_stats *x)
+{
+	int ret = 0;
+	/* read the status register (CSR5) */
+	unsigned int intr_status;
+
+	intr_status = readl(iobase + GETH_RGMII_STA);
+	if (intr_status & RGMII_IRQ)
+		readl(iobase + GETH_RGMII_STA);
+
+	intr_status = readl(iobase + GETH_INT_STA);
+
+	/* ABNORMAL interrupts */
+	if (intr_status & TX_UNF_INT) {
+		ret = tx_hard_error_bump_tc;
+		x->tx_undeflow_irq++;
+	}
+	if (intr_status & TX_TOUT_INT) {
+		x->tx_jabber_irq++;
+	}
+	if (intr_status & RX_OVF_INT) {
+		x->rx_overflow_irq++;
+	}
+	if (intr_status & RX_UA_INT) {
+		x->rx_buf_unav_irq++;
+	}
+	if (intr_status & RX_STOP_INT) {
+		x->rx_process_stopped_irq++;
+	}
+	if (intr_status & RX_TOUT_INT) {
+		x->rx_watchdog_irq++;
+	}
+	if (intr_status & TX_EARLY_INT) {
+		x->tx_early_irq++;
+	}
+	if (intr_status & TX_STOP_INT) {
+		x->tx_process_stopped_irq++;
+		ret = tx_hard_error;
+	}
+
+	/* TX/RX NORMAL interrupts */
+	if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) {
+		x->normal_irq_n++;
+		if (intr_status & (TX_INT | RX_INT))
+			ret = handle_tx_rx;
+	}
+	/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
+	writel(intr_status & 0x3FFF, iobase + GETH_INT_STA);
+
+	return ret;
+}
+
+void sunxi_start_rx(void *iobase, unsigned long rxbase)
+{
+	unsigned int value;
+
+	/* Write the base address of Rx descriptor lists into registers */
+	writel(rxbase, iobase + GETH_RX_DESC_LIST);
+
+	value = readl(iobase + GETH_RX_CTL1);
+	value |= 0x40000000;
+	writel(value, iobase + GETH_RX_CTL1);
+}
+
+void sunxi_stop_rx(void *iobase)
+{
+	unsigned int value;
+
+	value = readl(iobase + GETH_RX_CTL1);
+	value &= ~0x40000000;
+	writel(value, iobase + GETH_RX_CTL1);
+}
+
+void sunxi_start_tx(void *iobase, unsigned long txbase)
+{
+	unsigned int value;
+
+	/* Write the base address of Tx descriptor lists into registers */
+	writel(txbase, iobase + GETH_TX_DESC_LIST);
+
+	value = readl(iobase + GETH_TX_CTL1);
+	value |= 0x40000000;
+	writel(value, iobase + GETH_TX_CTL1);
+}
+
+void sunxi_stop_tx(void *iobase)
+{
+	unsigned int value = readl(iobase + GETH_TX_CTL1);
+
+	value &= ~0x40000000;
+	writel(value, iobase + GETH_TX_CTL1);
+}
+
+static int sunxi_dma_init(void *iobase)
+{
+	unsigned int value;
+
+	/* Burst should be 8 */
+	value = (8 << 24);
+
+#ifdef CONFIG_GMAC_DA
+	value |= RX_TX_PRI;	/* Rx has priority over tx */
+#endif
+	writel(value, iobase + GETH_BASIC_CTL1);
+
+	/* Mask interrupts by writing to CSR7 */
+	writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
+
+	return 0;
+}
+
+int sunxi_mac_init(void *iobase, int txmode, int rxmode)
+{
+	unsigned int value;
+
+	sunxi_dma_init(iobase);
+
+	/* Initialize the core component */
+	value = readl(iobase + GETH_TX_CTL0);
+	value |= (1 << 30);	/* Jabber Disable */
+	writel(value, iobase + GETH_TX_CTL0);
+
+	value = readl(iobase + GETH_RX_CTL0);
+	value |= (1 << 27);	/* Enable CRC & IPv4 Header Checksum */
+	value |= (1 << 28);	/* Automatic Pad/CRC Stripping */
+	value |= (1 << 29);	/* Jumbo Frame Enable */
+	writel(value, iobase + GETH_RX_CTL0);
+
+	writel((hwdev.mdc_div << 20), iobase + GETH_MDIO_ADDR); /* MDC_DIV_RATIO */
+
+	/* Set the Rx&Tx mode */
+	value = readl(iobase + GETH_TX_CTL1);
+	if (txmode == SF_DMA_MODE) {
+		/* Transmit COE type 2 cannot be done in cut-through mode. */
+		value |= TX_MD;
+		/* Operating on second frame increase the performance
+		 * especially when transmit store-and-forward is used.
+		 */
+		value |= TX_NEXT_FRM;
+	} else {
+		value &= ~TX_MD;
+		value &= ~TX_TH;
+		/* Set the transmit threshold */
+		if (txmode <= 64)
+			value |= 0x00000000;
+		else if (txmode <= 128)
+			value |= 0x00000100;
+		else if (txmode <= 192)
+			value |= 0x00000200;
+		else
+			value |= 0x00000300;
+	}
+	writel(value, iobase + GETH_TX_CTL1);
+
+	value = readl(iobase + GETH_RX_CTL1);
+	if (rxmode == SF_DMA_MODE) {
+		value |= RX_MD;
+	} else {
+		value &= ~RX_MD;
+		value &= ~RX_TH;
+		if (rxmode <= 32)
+			value |= 0x10;
+		else if (rxmode <= 64)
+			value |= 0x00;
+		else if (rxmode <= 96)
+			value |= 0x20;
+		else
+			value |= 0x30;
+	}
+
+	/* Forward frames with error and undersized good frame. */
+	value |= (RX_ERR_FRM | RX_RUNT_FRM);
+
+	writel(value, iobase + GETH_RX_CTL1);
+
+	return 0;
+}
+
+void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high)
+{
+	writel(high, iobase + GETH_RX_HASH0);
+	writel(low, iobase + GETH_RX_HASH1);
+}
+
+void sunxi_set_filter(void *iobase, unsigned long flags)
+{
+	int tmp_flags = 0;
+
+	tmp_flags |= ((flags >> 31) |
+			((flags >> 9) & 0x00000002) |
+			((flags << 1) & 0x00000010) |
+			((flags >> 3) & 0x00000060) |
+			((flags << 7) & 0x00000300) |
+			((flags << 6) & 0x00003000) |
+			((flags << 12) & 0x00030000) |
+			(flags << 31));
+
+	writel(tmp_flags, iobase + GETH_RX_FRM_FLT);
+}
+
+void sunxi_set_umac(void *iobase, unsigned char *addr, int index)
+{
+	unsigned long data;
+
+	data = (addr[5] << 8) | addr[4];
+	writel(data, iobase + GETH_ADDR_HI(index));
+	data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+	writel(data, iobase + GETH_ADDR_LO(index));
+}
+
+void sunxi_mac_enable(void *iobase)
+{
+	unsigned long value;
+
+	value = readl(iobase + GETH_TX_CTL0);
+	value |= (1 << 31);
+	writel(value, iobase + GETH_TX_CTL0);
+
+	value = readl(iobase + GETH_RX_CTL0);
+	value |= (1 << 31);
+	writel(value, iobase + GETH_RX_CTL0);
+}
+
+void sunxi_mac_disable(void *iobase)
+{
+	unsigned long value;
+
+	value = readl(iobase + GETH_TX_CTL0);
+	value &= ~(1 << 31);
+	writel(value, iobase + GETH_TX_CTL0);
+
+	value = readl(iobase + GETH_RX_CTL0);
+	value &= ~(1 << 31);
+	writel(value, iobase + GETH_RX_CTL0);
+}
+
+void sunxi_tx_poll(void *iobase)
+{
+	unsigned int value;
+
+	value = readl(iobase + GETH_TX_CTL1);
+	writel(value | 0x80000000, iobase + GETH_TX_CTL1);
+}
+
+void sunxi_rx_poll(void *iobase)
+{
+	unsigned int value;
+
+	value = readl(iobase + GETH_RX_CTL1);
+	writel(value | 0x80000000, iobase + GETH_RX_CTL1);
+}
+
+void sunxi_int_enable(void *iobase)
+{
+	writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
+}
+
+void sunxi_int_disable(void *iobase)
+{
+	writel(0, iobase + GETH_INT_EN);
+}
+
+void desc_buf_set(struct dma_desc *desc, unsigned long paddr, int size)
+{
+	desc->desc1.all &= (~((1 << 11) - 1));
+	desc->desc1.all |= (size & ((1 << 11) - 1));
+	desc->desc2 = paddr;
+}
+
+void desc_set_own(struct dma_desc *desc)
+{
+	desc->desc0.all |= 0x80000000;
+}
+
+void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert)
+{
+	struct dma_desc *desc = first;
+
+	first->desc1.tx.first_sg = 1;
+	end->desc1.tx.last_seg = 1;
+	end->desc1.tx.interrupt = 1;
+
+	if (csum_insert)
+		do {
+			desc->desc1.tx.cic = 3;
+			desc++;
+		} while (desc <= end);
+}
+
+void desc_init(struct dma_desc *desc)
+{
+	desc->desc1.all = 0;
+	desc->desc2  = 0;
+
+	desc->desc1.all |= (1 << 24);
+}
+
+int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x)
+{
+	int ret = 0;
+
+	if (desc->desc0.tx.under_err) {
+		x->tx_underflow++;
+		ret = -1;
+	}
+	if (desc->desc0.tx.no_carr) {
+		x->tx_carrier++;
+		ret = -1;
+	}
+	if (desc->desc0.tx.loss_carr) {
+		x->tx_losscarrier++;
+		ret = -1;
+	}
+
+#if 0
+	if ((desc->desc0.tx.ex_deferral) ||
+			(desc->desc0.tx.ex_coll) ||
+			(desc->desc0.tx.late_coll))
+		stats->collisions += desc->desc0.tx.coll_cnt;
+#endif
+
+	if (desc->desc0.tx.deferred)
+		x->tx_deferred++;
+
+	return ret;
+}
+
+int desc_buf_get_len(struct dma_desc *desc)
+{
+	return (desc->desc1.all & ((1 << 11) - 1));
+}
+
+int desc_buf_get_addr(struct dma_desc *desc)
+{
+	return desc->desc2;
+}
+
+int desc_rx_frame_len(struct dma_desc *desc)
+{
+	return desc->desc0.rx.frm_len;
+}
+
+int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x)
+{
+	int ret = good_frame;
+
+	if (desc->desc0.rx.last_desc == 0) {
+		return discard_frame;
+	}
+
+	if (desc->desc0.rx.err_sum) {
+		if (desc->desc0.rx.desc_err)
+			x->rx_desc++;
+
+		if (desc->desc0.rx.sou_filter)
+			x->sa_filter_fail++;
+
+		if (desc->desc0.rx.over_err)
+			x->overflow_error++;
+
+		if (desc->desc0.rx.ipch_err)
+			x->ipc_csum_error++;
+
+		if (desc->desc0.rx.late_coll)
+			x->rx_collision++;
+
+		if (desc->desc0.rx.crc_err)
+			x->rx_crc++;
+
+		ret = discard_frame;
+	}
+
+	if (desc->desc0.rx.len_err) {
+		ret = discard_frame;
+	}
+	if (desc->desc0.rx.mii_err) {
+		ret = discard_frame;
+	}
+
+	return ret;
+}
+
+int desc_get_own(struct dma_desc *desc)
+{
+	return desc->desc0.all & 0x80000000;
+}
+
+int desc_get_tx_ls(struct dma_desc *desc)
+{
+	return desc->desc1.tx.last_seg;
+}
+
+int sunxi_geth_register(void *iobase, int version, unsigned int div)
+{
+	hwdev.ver = version;
+	hwdev.iobase = iobase;
+	hwdev.mdc_div = div;
+
+	return 0;
+}
+
+int sunxi_mac_reset(void *iobase, void (*delay)(int), int n)
+{
+	unsigned int value;
+
+	/* DMA SW reset */
+	value = readl(iobase + GETH_BASIC_CTL1);
+	value |= SOFT_RST;
+	writel(value, iobase + GETH_BASIC_CTL1);
+
+	delay(n);
+
+	return !!(readl(iobase + GETH_BASIC_CTL1) & SOFT_RST);
+}
diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
new file mode 100644
index 00000000..0c67877
--- /dev/null
+++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
@@ -0,0 +1,2240 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
+ *
+ * This file is provided under a dual BSD/GPL license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ */
+//#include <linux/clk.h>
+//#include <linux/clk-provider.h>
+#include <linux/mii.h>
+#include <linux/gpio.h>
+#include <linux/crc32.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+//#include <linux/pinctrl/consumer.h>
+//#include <linux/pinctrl/pinctrl.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+//#include <linux/regulator/consumer.h>
+#include <linux/of_net.h>
+//#include <linux/of_gpio.h>
+#include <linux/io.h>
+//#include <linux/sunxi-sid.h>
+//#include <linux/sunxi-gpio.h>
+//#include <linux/reset.h>
+#include "sunxi-gmac.h"
+
+#define SUNXI_GMAC_VERSION "1.0.0"
+
+#define DMA_DESC_RX	256
+#define DMA_DESC_TX	256
+#define BUDGET		(dma_desc_rx / 4)
+#define TX_THRESH	(dma_desc_tx / 4)
+
+#define HASH_TABLE_SIZE	64
+#define MAX_BUF_SZ	(SZ_2K - 1)
+
+#define POWER_CHAN_NUM	3
+
+#undef PKT_DEBUG
+#undef DESC_PRINT
+
+#define circ_cnt(head, tail, size) (((head) > (tail)) ? \
+					((head) - (tail)) : \
+					((head) - (tail)) & ((size) - 1))
+
+#define circ_space(head, tail, size) circ_cnt((tail), ((head) + 1), (size))
+
+#define circ_inc(n, s) (((n) + 1) % (s))
+
+#define GETH_MAC_ADDRESS "00:00:00:00:00:00"
+static char *mac_str = GETH_MAC_ADDRESS;
+module_param(mac_str, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mac_str, "MAC Address String.(xx:xx:xx:xx:xx:xx)");
+
+static int rxmode = 1;
+module_param(rxmode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rxmode, "DMA threshold control value");
+
+static int txmode = 1;
+module_param(txmode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(txmode, "DMA threshold control value");
+
+static int pause = 0x400;
+module_param(pause, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pause, "Flow Control Pause Time");
+
+#define TX_TIMEO	5000
+static int watchdog = TX_TIMEO;
+module_param(watchdog, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
+
+static int dma_desc_rx = DMA_DESC_RX;
+module_param(dma_desc_rx, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(watchdog, "The number of receive's descriptors");
+
+static int dma_desc_tx = DMA_DESC_TX;
+module_param(dma_desc_tx, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(watchdog, "The number of transmit's descriptors");
+
+/* - 0: Flow Off
+ * - 1: Rx Flow
+ * - 2: Tx Flow
+ * - 3: Rx & Tx Flow
+ */
+static int flow_ctrl;
+module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(flow_ctrl, "Flow control [0: off, 1: rx, 2: tx, 3: both]");
+
+struct geth_priv {
+	struct dma_desc *dma_tx;
+	struct sk_buff **tx_sk;
+	unsigned int tx_clean;
+	unsigned int tx_dirty;
+	dma_addr_t dma_tx_phy;
+
+	unsigned long buf_sz;
+
+	struct dma_desc *dma_rx;
+	struct sk_buff **rx_sk;
+	unsigned int rx_clean;
+	unsigned int rx_dirty;
+	dma_addr_t dma_rx_phy;
+
+	struct net_device *ndev;
+	struct device *dev;
+	struct napi_struct napi;
+
+	struct geth_extra_stats xstats;
+
+	struct mii_bus *mii;
+	int link;
+	int speed;
+	int duplex;
+#define INT_PHY 0
+#define EXT_PHY 1
+	int phy_ext;
+	phy_interface_t phy_interface;
+
+	void __iomem *base;
+	void __iomem *base_phy;
+/*
+	struct clk *geth_clk;
+	struct clk *ephy_clk;
+	struct reset_control *reset;
+	struct pinctrl *pinctrl;
+*/
+	struct regulator *gmac_power[POWER_CHAN_NUM];
+	bool is_suspend;
+	int phyrst;
+	u8  rst_active_low;
+	/* definition spinlock */
+	spinlock_t lock;
+	spinlock_t tx_lock;
+
+	/* whether using ephy_clk */
+	int use_ephy_clk;
+	int phy_addr;
+
+	/* adjust transmit clock delay, value: 0~7 */
+	/* adjust receive clock delay, value: 0~31 */
+	unsigned int tx_delay;
+	unsigned int rx_delay;
+
+	/* resume work */
+	struct work_struct eth_work;
+};
+
+static u64 geth_dma_mask = DMA_BIT_MASK(32);
+
+void sunxi_udelay(int n)
+{
+	udelay(n);
+}
+
+static int geth_stop(struct net_device *ndev);
+static int geth_open(struct net_device *ndev);
+static void geth_tx_complete(struct geth_priv *priv);
+static void geth_rx_refill(struct net_device *ndev);
+
+#ifdef CONFIG_GETH_ATTRS
+static ssize_t adjust_bgs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int value = 0;
+	u32 efuse_value;
+	struct net_device *ndev = to_net_dev(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	if (priv->phy_ext == INT_PHY) {
+		value = readl(priv->base_phy) >> 28;
+		if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
+			pr_err("get PHY efuse fail!\n");
+		else
+#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
+			value = value - ((efuse_value >> 24) & 0x0F);
+#else
+			pr_warn("miss config come from efuse!\n");
+#endif
+	}
+
+	return sprintf(buf, "bgs: %d\n", value);
+}
+
+static ssize_t adjust_bgs_write(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	unsigned int out = 0;
+	struct net_device *ndev = to_net_dev(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+	u32 clk_value = readl(priv->base_phy);
+	u32 efuse_value;
+
+	out = simple_strtoul(buf, NULL, 10);
+
+	if (priv->phy_ext == INT_PHY) {
+		clk_value &= ~(0xF << 28);
+		if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
+			pr_err("get PHY efuse fail!\n");
+		else
+#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
+			clk_value |= (((efuse_value >> 24) & 0x0F) + out) << 28;
+#else
+			pr_warn("miss config come from efuse!\n");
+#endif
+	}
+
+	writel(clk_value, priv->base_phy);
+
+	return count;
+}
+
+static struct device_attribute adjust_reg[] = {
+	__ATTR(adjust_bgs, 0664, adjust_bgs_show, adjust_bgs_write),
+};
+
+static int geth_create_attrs(struct net_device *ndev)
+{
+	int j, ret;
+
+	for (j = 0; j < ARRAY_SIZE(adjust_reg); j++) {
+		ret = device_create_file(&ndev->dev, &adjust_reg[j]);
+		if (ret)
+			goto sysfs_failed;
+	}
+	goto succeed;
+
+sysfs_failed:
+	while (j--)
+		device_remove_file(&ndev->dev, &adjust_reg[j]);
+succeed:
+	return ret;
+}
+#endif
+
+#ifdef DEBUG
+static void desc_print(struct dma_desc *desc, int size)
+{
+#ifdef DESC_PRINT
+	int i;
+
+	for (i = 0; i < size; i++) {
+		u32 *x = (u32 *)(desc + i);
+
+		pr_info("\t%d [0x%08lx]: %08x %08x %08x %08x\n",
+			i, (unsigned long)(&desc[i]),
+			x[0], x[1], x[2], x[3]);
+	}
+	pr_info("\n");
+#endif
+}
+#endif
+
+static ssize_t extra_tx_stats_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	if (!dev) {
+		pr_err("Argment is invalid\n");
+		return 0;
+	}
+
+	if (!ndev) {
+		pr_err("Net device is null\n");
+		return 0;
+	}
+
+	return sprintf(buf, "tx_underflow: %lu\ntx_carrier: %lu\n"
+			"tx_losscarrier: %lu\nvlan_tag: %lu\n"
+			"tx_deferred: %lu\ntx_vlan: %lu\n"
+			"tx_jabber: %lu\ntx_frame_flushed: %lu\n"
+			"tx_payload_error: %lu\ntx_ip_header_error: %lu\n\n",
+			priv->xstats.tx_underflow, priv->xstats.tx_carrier,
+			priv->xstats.tx_losscarrier, priv->xstats.vlan_tag,
+			priv->xstats.tx_deferred, priv->xstats.tx_vlan,
+			priv->xstats.tx_jabber, priv->xstats.tx_frame_flushed,
+			priv->xstats.tx_payload_error, priv->xstats.tx_ip_header_error);
+}
+static DEVICE_ATTR(extra_tx_stats, 0444, extra_tx_stats_show, NULL);
+
+static ssize_t extra_rx_stats_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	if (!dev) {
+		pr_err("Argment is invalid\n");
+		return 0;
+	}
+
+	if (!ndev) {
+		pr_err("Net device is null\n");
+		return 0;
+	}
+
+	return sprintf(buf, "rx_desc: %lu\nsa_filter_fail: %lu\n"
+			"overflow_error: %lu\nipc_csum_error: %lu\n"
+			"rx_collision: %lu\nrx_crc: %lu\n"
+			"dribbling_bit: %lu\nrx_length: %lu\n"
+			"rx_mii: %lu\nrx_multicast: %lu\n"
+			"rx_gmac_overflow: %lu\nrx_watchdog: %lu\n"
+			"da_rx_filter_fail: %lu\nsa_rx_filter_fail: %lu\n"
+			"rx_missed_cntr: %lu\nrx_overflow_cntr: %lu\n"
+			"rx_vlan: %lu\n\n",
+			priv->xstats.rx_desc, priv->xstats.sa_filter_fail,
+			priv->xstats.overflow_error, priv->xstats.ipc_csum_error,
+			priv->xstats.rx_collision, priv->xstats.rx_crc,
+			priv->xstats.dribbling_bit, priv->xstats.rx_length,
+			priv->xstats.rx_mii, priv->xstats.rx_multicast,
+			priv->xstats.rx_gmac_overflow, priv->xstats.rx_length,
+			priv->xstats.da_rx_filter_fail, priv->xstats.sa_rx_filter_fail,
+			priv->xstats.rx_missed_cntr, priv->xstats.rx_overflow_cntr,
+			priv->xstats.rx_vlan);
+}
+static DEVICE_ATTR(extra_rx_stats, 0444, extra_rx_stats_show, NULL);
+
+static ssize_t gphy_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+
+	if (!dev) {
+		pr_err("Argment is invalid\n");
+		return 0;
+	}
+
+	if (!ndev) {
+		pr_err("Net device is null\n");
+		return 0;
+	}
+
+	return sprintf(buf, "Usage:\necho [0/1/2/3/4] > gphy_test\n"
+			"0 - Normal Mode\n"
+			"1 - Transmit Jitter Test\n"
+			"2 - Transmit Jitter Test(MASTER mode)\n"
+			"3 - Transmit Jitter Test(SLAVE mode)\n"
+			"4 - Transmit Distortion Test\n\n");
+}
+
+static ssize_t gphy_test_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+	u16 value = 0;
+	int ret = 0;
+	u16 data = 0;
+
+	if (!dev) {
+		pr_err("Argument is invalid\n");
+		return count;
+	}
+
+	if (!ndev) {
+		pr_err("Net device is null\n");
+		return count;
+	}
+
+	data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000);
+
+	ret = kstrtou16(buf, 0, &value);
+	if (ret)
+		return ret;
+
+	if (value >= 0 && value <= 4) {
+		data &= ~(0x7 << 13);
+		data |= value << 13;
+		sunxi_mdio_write(priv->base, priv->phy_addr, MII_CTRL1000, data);
+		pr_info("Set MII_CTRL1000(0x09) Reg: 0x%x\n", data);
+	} else {
+		pr_info("unknown value (%d)\n", value);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(gphy_test, 0664, gphy_test_show, gphy_test_store);
+
+static ssize_t mii_reg_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct net_device *ndev = NULL;
+	struct geth_priv *priv = NULL;
+
+	if (dev == NULL) {
+		pr_err("Argment is invalid\n");
+		return 0;
+	}
+
+	ndev = dev_get_drvdata(dev);
+	if (ndev == NULL) {
+		pr_err("Net device is null\n");
+		return 0;
+	}
+
+	priv = netdev_priv(ndev);
+	if (priv == NULL) {
+		pr_err("geth_priv is null\n");
+		return 0;
+	}
+
+	if (!netif_running(ndev)) {
+		pr_warn("eth is down!\n");
+		return 0;
+	}
+
+	return sprintf(buf,
+		"Current MII Registers:\n"
+		"BMCR[0x%02x] = 0x%04x,\t\tBMSR[0x%02x] = 0x%04x,\t\tPHYSID1[0x%02x] = 0x%04x\n"
+		"PHYSID2[0x%02x] = 0x%04x,\t\tADVERTISE[0x%02x] = 0x%04x,\tLPA[0x%02x] = 0x%04x\n"
+		"EXPANSION[0x%02x] = 0x%04x,\tCTRL1000[0x%02x] = 0x%04x,\tSTAT1000[0x%02x] = 0x%04x\n",
+		MII_BMCR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR),
+		MII_BMSR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMSR),
+		MII_PHYSID1, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID1),
+		MII_PHYSID2, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID2),
+		MII_ADVERTISE, sunxi_mdio_read(priv->base, priv->phy_addr, MII_ADVERTISE),
+		MII_LPA, sunxi_mdio_read(priv->base, priv->phy_addr, MII_LPA),
+		MII_EXPANSION, sunxi_mdio_read(priv->base, priv->phy_addr, MII_EXPANSION),
+		MII_CTRL1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000),
+		MII_STAT1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_STAT1000));
+}
+static DEVICE_ATTR(mii_reg, 0444, mii_reg_show, NULL);
+
+static ssize_t loopback_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "Usage:\necho [0/1/2] > loopback_test\n"
+			"0 - Normal Mode\n"
+			"1 - Mac loopback test mode\n"
+			"2 - Phy loopback test mode\n");
+}
+
+static ssize_t loopback_test_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct net_device *ndev = NULL;
+	struct geth_priv *priv = NULL;
+	u16 value = 0;
+	int ret = 0;
+	u16 data = 0;
+
+	if (dev == NULL) {
+		pr_err("Argment is invalid\n");
+		return count;
+	}
+
+	ndev = dev_get_drvdata(dev);
+	if (ndev == NULL) {
+		pr_err("Net device is null\n");
+		return count;
+	}
+
+	priv = netdev_priv(ndev);
+	if (priv == NULL) {
+		pr_err("geth_priv is null\n");
+		return count;
+	}
+
+	if (!netif_running(ndev)) {
+		pr_warn("eth is down!\n");
+		return count;
+	}
+
+	ret = kstrtou16(buf, 0, &value);
+	if (ret)
+		return ret;
+
+	if (value == 0) { /* normal mode */
+		/* clear mac loopback */
+		sunxi_mac_loopback(priv->base, 0);
+
+		/* clear phy loopback */
+		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
+		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
+	} else if (value == 1) { /* mac loopback test mode */
+		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
+		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
+
+		sunxi_mac_loopback(priv->base, 1);
+	} else if (value == 2) { /* phy loopback test mode */
+		sunxi_mac_loopback(priv->base, 0);
+
+		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
+		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data | BMCR_LOOPBACK);
+	} else {
+		pr_err("Undefined value (%d)\n", value);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(loopback_test, 0664, loopback_test_show, loopback_test_store);
+
+static int geth_power_on(struct geth_priv *priv)
+{
+	int value;
+
+	value = readl(priv->base_phy);
+	if (priv->phy_ext == INT_PHY) {
+		value |= (1 << 15);
+		value &= ~(1 << 16);
+		value |= (3 << 17);
+	} else {
+		value &= ~(1 << 15);
+/*
+		for (i = 0; i < POWER_CHAN_NUM; i++) {
+			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
+				continue;
+			if (regulator_enable(priv->gmac_power[i]) != 0) {
+				pr_err("gmac-power%d enable error\n", i);
+				return -EINVAL;
+			}
+		}
+*/
+	}
+
+	writel(value, priv->base_phy);
+
+	return 0;
+}
+
+static void geth_power_off(struct geth_priv *priv)
+{
+	int value;
+
+	if (priv->phy_ext == INT_PHY) {
+		value = readl(priv->base_phy);
+		value |= (1 << 16);
+		writel(value, priv->base_phy);
+	} else {
+/*
+		for (i = 0; i < POWER_CHAN_NUM; i++) {
+			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
+				continue;
+			regulator_disable(priv->gmac_power[i]);
+		}
+*/
+	}
+}
+
+/* PHY interface operations */
+static int geth_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
+{
+	struct net_device *ndev = bus->priv;
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	return (int)sunxi_mdio_read(priv->base,  phyaddr, phyreg);
+}
+
+static int geth_mdio_write(struct mii_bus *bus, int phyaddr,
+			   int phyreg, u16 data)
+{
+	struct net_device *ndev = bus->priv;
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	sunxi_mdio_write(priv->base, phyaddr, phyreg, data);
+
+	return 0;
+}
+
+static int geth_mdio_reset(struct mii_bus *bus)
+{
+	struct net_device *ndev = bus->priv;
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	return sunxi_mdio_reset(priv->base);
+}
+
+static void geth_adjust_link(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+	unsigned long flags;
+	int new_state = 0;
+
+	if (!phydev)
+		return;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (phydev->link) {
+		/* Now we make sure that we can be in full duplex mode.
+		 * If not, we operate in half-duplex mode.
+		 */
+		if (phydev->duplex != priv->duplex) {
+			new_state = 1;
+			priv->duplex = phydev->duplex;
+		}
+		/* Flow Control operation */
+		if (phydev->pause)
+			sunxi_flow_ctrl(priv->base, phydev->duplex,
+					flow_ctrl, pause);
+
+		if (phydev->speed != priv->speed) {
+			new_state = 1;
+			priv->speed = phydev->speed;
+		}
+
+		if (priv->link == 0) {
+			new_state = 1;
+			priv->link = phydev->link;
+		}
+
+		if (new_state)
+			sunxi_set_link_mode(priv->base, priv->duplex, priv->speed);
+
+#ifdef LOOPBACK_DEBUG
+		phydev->state = PHY_FORCING;
+#endif
+
+	} else if (priv->link != phydev->link) {
+		new_state = 1;
+		priv->link = 0;
+		priv->speed = 0;
+		priv->duplex = -1;
+	}
+
+	if (new_state)
+		phy_print_status(phydev);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int geth_phy_init(struct net_device *ndev)
+{
+	int value;
+	struct mii_bus *new_bus;
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+
+	/* Fixup the phy interface type */
+	if (priv->phy_ext == INT_PHY) {
+		priv->phy_interface = PHY_INTERFACE_MODE_MII;
+	} else {
+		/* If config gpio to reset the phy device, we should reset it */
+		/*
+		if (gpio_is_valid(priv->phyrst)) {
+			gpio_direction_output(priv->phyrst,
+					priv->rst_active_low);
+			msleep(50);
+			gpio_direction_output(priv->phyrst,
+					!priv->rst_active_low);
+			msleep(50);
+		}
+		*/
+	}
+
+	if (priv->is_suspend && phydev)
+		goto resume;
+
+	new_bus = mdiobus_alloc();
+	if (!new_bus) {
+		netdev_err(ndev, "Failed to alloc new mdio bus\n");
+		return -ENOMEM;
+	}
+
+	new_bus->name = dev_name(priv->dev);
+	new_bus->read = &geth_mdio_read;
+	new_bus->write = &geth_mdio_write;
+	new_bus->reset = &geth_mdio_reset;
+	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0);
+
+	new_bus->parent = priv->dev;
+	new_bus->priv = ndev;
+
+	if (mdiobus_register(new_bus)) {
+		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
+		goto reg_fail;
+	}
+
+	priv->mii = new_bus;
+
+	{
+		int addr;
+
+		for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+			struct phy_device *phydev_tmp = mdiobus_get_phy(new_bus, addr);
+
+			if (phydev_tmp && (phydev_tmp->phy_id != 0x00)) {
+				phydev = phydev_tmp;
+				priv->phy_addr = addr;
+				break;
+			}
+		}
+	}
+
+	if (!phydev) {
+		netdev_err(ndev, "No PHY found!\n");
+		goto err;
+	}
+
+	phydev->irq = PHY_POLL;
+
+	value = phy_connect_direct(ndev, phydev, &geth_adjust_link, priv->phy_interface);
+	if (value) {
+		netdev_err(ndev, "Could not attach to PHY\n");
+		goto err;
+	} else {
+		netdev_info(ndev, "%s: Type(%d) PHY ID %08x at %d IRQ %s (%s)\n",
+			    ndev->name, phydev->interface, phydev->phy_id,
+			    phydev->mdio.addr, "poll", dev_name(&phydev->mdio.dev));
+	}
+
+	//phydev->supported &= PHY_GBIT_FEATURES;
+	phydev->is_gigabit_capable = 1;
+	//phydev->advertising = phydev->supported;
+
+resume:
+	phy_write(phydev, MII_BMCR, BMCR_RESET);
+	while (BMCR_RESET & phy_read(phydev, MII_BMCR))
+		msleep(30);
+
+	value = phy_read(phydev, MII_BMCR);
+	phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
+
+	if (priv->phy_ext == INT_PHY) {
+		/* EPHY Initial */
+		phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
+		phy_write(phydev, 0x12, 0x4824); /* Disable APS */
+		phy_write(phydev, 0x1f, 0x0200); /* switchto page 2 */
+		phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
+		phy_write(phydev, 0x1f, 0x0600); /* switchto page 6 */
+		phy_write(phydev, 0x14, 0x708F); /* PHYAFE TX optimization */
+		phy_write(phydev, 0x19, 0x0000);
+		phy_write(phydev, 0x13, 0xf000); /* PHYAFE RX optimization */
+		phy_write(phydev, 0x15, 0x1530);
+		phy_write(phydev, 0x1f, 0x0800); /* switch to page 8 */
+		phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
+		phy_write(phydev, 0x1f, 0x0100); /* switchto page 1 */
+		/* reg 0x17 bit3,set 0 to disable iEEE */
+		phy_write(phydev, 0x17, phy_read(phydev, 0x17) & (~(1<<3)));
+		phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
+	}
+	if (priv->is_suspend)
+		phy_init_hw(phydev);
+
+	return 0;
+
+err:
+	mdiobus_unregister(new_bus);
+reg_fail:
+	mdiobus_free(new_bus);
+
+	return -EINVAL;
+}
+
+static int geth_phy_release(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+	int value = 0;
+
+	/* Stop and disconnect the PHY */
+	if (phydev)
+		phy_stop(phydev);
+
+	priv->link = PHY_DOWN;
+	priv->speed = 0;
+	priv->duplex = -1;
+
+	if (phydev) {
+		value = phy_read(phydev, MII_BMCR);
+		phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN));
+	}
+
+	if (priv->is_suspend)
+		return 0;
+
+	if (phydev) {
+		phy_disconnect(phydev);
+		ndev->phydev = NULL;
+	}
+
+	if (priv->mii) {
+		mdiobus_unregister(priv->mii);
+		priv->mii->priv = NULL;
+		mdiobus_free(priv->mii);
+		priv->mii = NULL;
+	}
+
+	return 0;
+}
+
+static void geth_rx_refill(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct dma_desc *desc;
+	struct sk_buff *sk = NULL;
+	dma_addr_t paddr;
+
+	while (circ_space(priv->rx_clean, priv->rx_dirty, dma_desc_rx) > 0) {
+		int entry = priv->rx_clean;
+
+		/* Find the dirty's desc and clean it */
+		desc = priv->dma_rx + entry;
+
+		if (priv->rx_sk[entry] == NULL) {
+			sk = netdev_alloc_skb_ip_align(ndev, priv->buf_sz);
+
+			if (unlikely(sk == NULL))
+				break;
+
+			priv->rx_sk[entry] = sk;
+			paddr = dma_map_single(priv->dev, sk->data,
+					       priv->buf_sz, DMA_FROM_DEVICE);
+			desc_buf_set(desc, paddr, priv->buf_sz);
+		}
+
+		/* sync memery */
+		wmb();
+		desc_set_own(desc);
+		priv->rx_clean = circ_inc(priv->rx_clean, dma_desc_rx);
+	}
+}
+
+/* geth_dma_desc_init - initialize the RX/TX descriptor list
+ * @ndev: net device structure
+ * Description: initialize the list for dma.
+ */
+static int geth_dma_desc_init(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	unsigned int buf_sz;
+
+	priv->rx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_rx,
+				GFP_KERNEL);
+	if (!priv->rx_sk)
+		return -ENOMEM;
+
+	priv->tx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_tx,
+				GFP_KERNEL);
+	if (!priv->tx_sk)
+		goto tx_sk_err;
+
+	/* Set the size of buffer depend on the MTU & max buf size */
+	buf_sz = MAX_BUF_SZ;
+
+	priv->dma_tx = dma_alloc_coherent(priv->dev,
+					dma_desc_tx *
+					sizeof(struct dma_desc),
+					&priv->dma_tx_phy,
+					GFP_KERNEL);
+	if (!priv->dma_tx)
+		goto dma_tx_err;
+
+	priv->dma_rx = dma_alloc_coherent(priv->dev,
+					dma_desc_rx *
+					sizeof(struct dma_desc),
+					&priv->dma_rx_phy,
+					GFP_KERNEL);
+	if (!priv->dma_rx)
+		goto dma_rx_err;
+
+	priv->buf_sz = buf_sz;
+
+	return 0;
+
+dma_rx_err:
+	dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
+			  priv->dma_tx, priv->dma_tx_phy);
+dma_tx_err:
+	kfree(priv->tx_sk);
+tx_sk_err:
+	kfree(priv->rx_sk);
+
+	return -ENOMEM;
+}
+
+static void geth_free_rx_sk(struct geth_priv *priv)
+{
+	int i;
+
+	for (i = 0; i < dma_desc_rx; i++) {
+		if (priv->rx_sk[i] != NULL) {
+			struct dma_desc *desc = priv->dma_rx + i;
+
+			dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
+					 desc_buf_get_len(desc),
+					 DMA_FROM_DEVICE);
+			dev_kfree_skb_any(priv->rx_sk[i]);
+			priv->rx_sk[i] = NULL;
+		}
+	}
+}
+
+static void geth_free_tx_sk(struct geth_priv *priv)
+{
+	int i;
+
+	for (i = 0; i < dma_desc_tx; i++) {
+		if (priv->tx_sk[i] != NULL) {
+			struct dma_desc *desc = priv->dma_tx + i;
+
+			if (desc_buf_get_addr(desc))
+				dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
+						 desc_buf_get_len(desc),
+						 DMA_TO_DEVICE);
+			dev_kfree_skb_any(priv->tx_sk[i]);
+			priv->tx_sk[i] = NULL;
+		}
+	}
+}
+
+static void geth_free_dma_desc(struct geth_priv *priv)
+{
+	/* Free the region of consistent memory previously allocated for the DMA */
+	dma_free_coherent(priv->dev, dma_desc_tx * sizeof(struct dma_desc),
+			  priv->dma_tx, priv->dma_tx_phy);
+	dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
+			  priv->dma_rx, priv->dma_rx_phy);
+
+	kfree(priv->rx_sk);
+	kfree(priv->tx_sk);
+}
+
+#if IS_ENABLED(CONFIG_PM)
+/*
+static int geth_select_gpio_state(struct pinctrl *pctrl, char *name)
+{
+	int ret = 0;
+	struct pinctrl_state *pctrl_state = NULL;
+
+	pctrl_state = pinctrl_lookup_state(pctrl, name);
+	if (IS_ERR(pctrl_state)) {
+		pr_err("gmac pinctrl_lookup_state(%s) failed! return %p\n",
+						name, pctrl_state);
+		return -EINVAL;
+	}
+
+	ret = pinctrl_select_state(pctrl, pctrl_state);
+	if (ret < 0)
+		pr_err("gmac pinctrl_select_state(%s) failed! return %d\n",
+						name, ret);
+
+	return ret;
+}
+*/
+static int geth_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	cancel_work_sync(&priv->eth_work);
+
+	if (!ndev || !netif_running(ndev))
+		return 0;
+
+	priv->is_suspend = true;
+
+	spin_lock(&priv->lock);
+	netif_device_detach(ndev);
+	spin_unlock(&priv->lock);
+
+	geth_stop(ndev);
+/*
+	if (priv->phy_ext == EXT_PHY)
+
+	geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP);
+*/
+	return 0;
+}
+
+static void geth_resume_work(struct work_struct *work)
+{
+	struct geth_priv *priv = container_of(work, struct geth_priv, eth_work);
+	struct net_device *ndev = priv->ndev;
+	int ret = 0;
+
+	if (!netif_running(ndev))
+		return;
+/*
+	if (priv->phy_ext == EXT_PHY)
+		geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_DEFAULT);
+*/
+	spin_lock(&priv->lock);
+	netif_device_attach(ndev);
+	spin_unlock(&priv->lock);
+
+#if IS_ENABLED(CONFIG_SUNXI_EPHY)
+	if (!ephy_is_enable()) {
+		pr_info("[geth_resume] ephy is not enable, waiting...\n");
+		msleep(2000);
+		if (!ephy_is_enable()) {
+			netdev_err(ndev, "Wait for ephy resume timeout.\n");
+			return;
+		}
+	}
+#endif
+
+	ret = geth_open(ndev);
+	if (!ret)
+		priv->is_suspend = false;
+}
+
+static void geth_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	schedule_work(&priv->eth_work);
+}
+
+static int geth_freeze(struct device *dev)
+{
+	return 0;
+}
+
+static int geth_restore(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops geth_pm_ops = {
+	.complete = geth_resume,
+	.prepare = geth_suspend,
+	.suspend = NULL,
+	.resume = NULL,
+	.freeze = geth_freeze,
+	.restore = geth_restore,
+};
+#else
+static const struct dev_pm_ops geth_pm_ops;
+#endif /* CONFIG_PM */
+
+#define sunxi_get_soc_chipid(x) {}
+static void geth_chip_hwaddr(u8 *addr)
+{
+#define MD5_SIZE	16
+#define CHIP_SIZE	16
+
+	struct crypto_ahash *tfm;
+	struct ahash_request *req;
+	struct scatterlist sg;
+	u8 result[MD5_SIZE];
+	u8 chipid[CHIP_SIZE];
+	int i = 0;
+	int ret = -1;
+
+	memset(chipid, 0, sizeof(chipid));
+	memset(result, 0, sizeof(result));
+
+	sunxi_get_soc_chipid((u8 *)chipid);
+
+	tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		pr_err("Failed to alloc md5\n");
+		return;
+	}
+
+	req = ahash_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto out;
+
+	ahash_request_set_callback(req, 0, NULL, NULL);
+
+	ret = crypto_ahash_init(req);
+	if (ret) {
+		pr_err("crypto_ahash_init() failed\n");
+		goto out;
+	}
+
+	sg_init_one(&sg, chipid, sizeof(chipid));
+	ahash_request_set_crypt(req, &sg, result, sizeof(chipid));
+	ret = crypto_ahash_update(req);
+	if (ret) {
+		pr_err("crypto_ahash_update() failed for id\n");
+		goto out;
+	}
+
+	ret = crypto_ahash_final(req);
+	if (ret) {
+		pr_err("crypto_ahash_final() failed for result\n");
+		goto out;
+	}
+
+	ahash_request_free(req);
+
+	/* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
+	for (i = 0; i < ETH_ALEN; i++)
+		addr[i] = result[2 * i];
+	addr[0] &= 0xfe; /* clear multicast bit */
+	addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
+
+out:
+	crypto_free_ahash(tfm);
+}
+
+static void geth_check_addr(struct net_device *ndev, unsigned char *mac)
+{
+	int i;
+	char *p = mac;
+
+	if (!is_valid_ether_addr(ndev->dev_addr)) {
+		for (i = 0; i < ETH_ALEN; i++, p++)
+			ndev->dev_addr[i] = simple_strtoul(p, &p, 16);
+
+		if (!is_valid_ether_addr(ndev->dev_addr))
+			geth_chip_hwaddr(ndev->dev_addr);
+
+		if (!is_valid_ether_addr(ndev->dev_addr)) {
+			random_ether_addr(ndev->dev_addr);
+			pr_warn("%s: Use random mac address\n", ndev->name);
+		}
+	}
+}
+
+static int geth_clk_enable(struct geth_priv *priv)
+{
+	int ret;
+	phy_interface_t phy_interface = 0;
+	u32 clk_value;
+	/*u32 efuse_value;*/
+/*
+	ret = reset_control_deassert(priv->reset);
+	if (ret) {
+		pr_err("deassert gmac rst failed!\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(priv->geth_clk);
+	if (ret) {
+		pr_err("try to enable geth_clk failed!\n");
+		goto assert_reset;
+	}
+
+	if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
+			&& !IS_ERR_OR_NULL(priv->ephy_clk)) {
+		ret = clk_prepare_enable(priv->ephy_clk);
+		if (ret) {
+			pr_err("try to enable ephy_clk failed!\n");
+			goto ephy_clk_disable;
+		}
+	}
+*/
+	phy_interface = priv->phy_interface;
+
+	clk_value = readl(priv->base_phy);
+	if (phy_interface == PHY_INTERFACE_MODE_RGMII)
+		clk_value |= 0x00000004;
+	else
+		clk_value &= (~0x00000004);
+
+	clk_value &= (~0x00002003);
+	if (phy_interface == PHY_INTERFACE_MODE_RGMII
+			|| phy_interface == PHY_INTERFACE_MODE_GMII)
+		clk_value |= 0x00000002;
+	else if (phy_interface == PHY_INTERFACE_MODE_RMII)
+		clk_value |= 0x00002001;
+
+	/*if (priv->phy_ext == INT_PHY) {
+		if (0 != sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value))
+			pr_err("get PHY efuse fail!\n");
+		else
+#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
+			clk_value |= (((efuse_value >> 24) & 0x0F) + 3) << 28;
+#else
+			pr_warn("miss config come from efuse!\n");
+#endif
+	}*/
+
+	/* Adjust Tx/Rx clock delay */
+	clk_value &= ~(0x07 << 10);
+	clk_value |= ((priv->tx_delay & 0x07) << 10);
+	clk_value &= ~(0x1F << 5);
+	clk_value |= ((priv->rx_delay & 0x1F) << 5);
+
+	writel(clk_value, priv->base_phy);
+
+    return 0;
+/*
+ephy_clk_disable:
+    clk_disable_unprepare(priv->ephy_clk);
+assert_reset:
+    reset_control_assert(priv->reset);
+*/
+    return ret;
+}
+
+static void geth_clk_disable(struct geth_priv *priv)
+{
+/*
+	if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
+			&& !IS_ERR_OR_NULL(priv->ephy_clk))
+		clk_disable_unprepare(priv->ephy_clk);
+
+	clk_disable_unprepare(priv->geth_clk);
+    reset_control_assert(priv->reset);
+*/
+}
+
+static void geth_tx_err(struct geth_priv *priv)
+{
+	netif_stop_queue(priv->ndev);
+
+	sunxi_stop_tx(priv->base);
+
+	geth_free_tx_sk(priv);
+	memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
+	desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
+	priv->tx_dirty = 0;
+	priv->tx_clean = 0;
+	sunxi_start_tx(priv->base, priv->dma_tx_phy);
+
+	priv->ndev->stats.tx_errors++;
+	netif_wake_queue(priv->ndev);
+}
+
+static inline void geth_schedule(struct geth_priv *priv)
+{
+	if (likely(napi_schedule_prep(&priv->napi))) {
+		sunxi_int_disable(priv->base);
+		__napi_schedule(&priv->napi);
+	}
+}
+
+static irqreturn_t geth_interrupt(int irq, void *dev_id)
+{
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct geth_priv *priv = netdev_priv(ndev);
+	int status;
+
+	if (unlikely(!ndev)) {
+		pr_err("%s: invalid ndev pointer\n", __func__);
+		return IRQ_NONE;
+	}
+
+	status = sunxi_int_status(priv->base, (void *)(&priv->xstats));
+
+	if (likely(status == handle_tx_rx))
+		geth_schedule(priv);
+	else if (unlikely(status == tx_hard_error_bump_tc))
+		netdev_info(ndev, "Do nothing for bump tc\n");
+	else if (unlikely(status == tx_hard_error))
+		geth_tx_err(priv);
+	else
+		netdev_info(ndev, "Do nothing.....\n");
+
+	return IRQ_HANDLED;
+}
+
+static int geth_open(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	int ret = 0;
+
+	ret = geth_power_on(priv);
+	if (ret) {
+		netdev_err(ndev, "Power on is failed\n");
+		ret = -EINVAL;
+	}
+
+	ret = geth_clk_enable(priv);
+	if (ret) {
+		pr_err("%s: clk enable is failed\n", __func__);
+		ret = -EINVAL;
+	}
+
+	netif_carrier_off(ndev);
+
+	ret = geth_phy_init(ndev);
+	if (ret)
+		goto err;
+
+	ret = sunxi_mac_reset((void *)priv->base, &sunxi_udelay, 10000);
+	if (ret) {
+		netdev_err(ndev, "Initialize hardware error\n");
+		goto desc_err;
+	}
+
+	sunxi_mac_init(priv->base, txmode, rxmode);
+	sunxi_set_umac(priv->base, ndev->dev_addr, 0);
+
+	if (!priv->is_suspend) {
+		ret = geth_dma_desc_init(ndev);
+		if (ret) {
+			ret = -EINVAL;
+			goto desc_err;
+		}
+	}
+
+	memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
+	memset(priv->dma_rx, 0, dma_desc_rx * sizeof(struct dma_desc));
+
+	desc_init_chain(priv->dma_rx, (unsigned long)priv->dma_rx_phy, dma_desc_rx);
+	desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
+
+	priv->rx_clean = 0;
+	priv->rx_dirty = 0;
+	priv->tx_clean = 0;
+	priv->tx_dirty = 0;
+	geth_rx_refill(ndev);
+
+	/* Extra statistics */
+	memset(&priv->xstats, 0, sizeof(struct geth_extra_stats));
+
+	if (ndev->phydev)
+		phy_start(ndev->phydev);
+
+	sunxi_start_rx(priv->base, (unsigned long)((struct dma_desc *)
+		       priv->dma_rx_phy + priv->rx_dirty));
+	sunxi_start_tx(priv->base, (unsigned long)((struct dma_desc *)
+		       priv->dma_tx_phy + priv->tx_clean));
+
+	napi_enable(&priv->napi);
+	netif_start_queue(ndev);
+
+	/* Enable the Rx/Tx */
+	sunxi_mac_enable(priv->base);
+
+	return 0;
+
+desc_err:
+	geth_phy_release(ndev);
+err:
+	geth_clk_disable(priv);
+	if (priv->is_suspend)
+		napi_enable(&priv->napi);
+
+	geth_power_off(priv);
+
+	return ret;
+}
+
+static int geth_stop(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	netif_stop_queue(ndev);
+	napi_disable(&priv->napi);
+
+	netif_carrier_off(ndev);
+
+	/* Release PHY resources */
+	geth_phy_release(ndev);
+
+	/* Disable Rx/Tx */
+	sunxi_mac_disable(priv->base);
+
+	geth_clk_disable(priv);
+	geth_power_off(priv);
+
+	netif_tx_lock_bh(ndev);
+	/* Release the DMA TX/RX socket buffers */
+	geth_free_rx_sk(priv);
+	geth_free_tx_sk(priv);
+	netif_tx_unlock_bh(ndev);
+
+	/* Ensure that hareware have been stopped */
+	if (!priv->is_suspend)
+		geth_free_dma_desc(priv);
+
+	return 0;
+}
+
+static void geth_tx_complete(struct geth_priv *priv)
+{
+	unsigned int entry = 0;
+	struct sk_buff *skb = NULL;
+	struct dma_desc *desc = NULL;
+	int tx_stat;
+
+	spin_lock(&priv->tx_lock);
+	while (circ_cnt(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > 0) {
+		entry = priv->tx_clean;
+		desc = priv->dma_tx + entry;
+
+		/* Check if the descriptor is owned by the DMA. */
+		if (desc_get_own(desc))
+			break;
+
+		/* Verify tx error by looking at the last segment */
+		if (desc_get_tx_ls(desc)) {
+			tx_stat = desc_get_tx_status(desc, (void *)(&priv->xstats));
+
+			if (likely(!tx_stat))
+				priv->ndev->stats.tx_packets++;
+			else
+				priv->ndev->stats.tx_errors++;
+		}
+
+		dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
+				 desc_buf_get_len(desc), DMA_TO_DEVICE);
+
+		skb = priv->tx_sk[entry];
+		priv->tx_sk[entry] = NULL;
+		desc_init(desc);
+
+		/* Find next dirty desc */
+		priv->tx_clean = circ_inc(entry, dma_desc_tx);
+
+		if (unlikely(skb == NULL))
+			continue;
+
+		dev_kfree_skb(skb);
+	}
+
+	if (unlikely(netif_queue_stopped(priv->ndev)) &&
+	    circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
+	    TX_THRESH) {
+		netif_wake_queue(priv->ndev);
+	}
+	spin_unlock(&priv->tx_lock);
+}
+
+static netdev_tx_t geth_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct geth_priv  *priv = netdev_priv(ndev);
+	unsigned int entry;
+	struct dma_desc *desc, *first;
+	unsigned int len, tmp_len = 0;
+	int i, csum_insert;
+	int nfrags = skb_shinfo(skb)->nr_frags;
+	dma_addr_t paddr;
+
+	spin_lock(&priv->tx_lock);
+	if (unlikely(circ_space(priv->tx_dirty, priv->tx_clean,
+	    dma_desc_tx) < (nfrags + 1))) {
+		if (!netif_queue_stopped(ndev)) {
+			netdev_err(ndev, "%s: BUG! Tx Ring full when queue awake\n", __func__);
+			netif_stop_queue(ndev);
+		}
+		spin_unlock(&priv->tx_lock);
+
+		return NETDEV_TX_BUSY;
+	}
+
+	csum_insert = (skb->ip_summed == CHECKSUM_PARTIAL);
+	entry = priv->tx_dirty;
+	first = priv->dma_tx + entry;
+	desc = priv->dma_tx + entry;
+
+	len = skb_headlen(skb);
+	priv->tx_sk[entry] = skb;
+
+#ifdef PKT_DEBUG
+	printk("======TX PKT DATA: ============\n");
+	/* dump the packet */
+	print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
+		       16, 1, skb->data, 64, true);
+#endif
+
+	/* Every desc max size is 2K */
+	while (len != 0) {
+		desc = priv->dma_tx + entry;
+		tmp_len = ((len > MAX_BUF_SZ) ?  MAX_BUF_SZ : len);
+
+		paddr = dma_map_single(priv->dev, skb->data, tmp_len, DMA_TO_DEVICE);
+		if (dma_mapping_error(priv->dev, paddr)) {
+			dev_kfree_skb(skb);
+			return -EIO;
+		}
+		desc_buf_set(desc, paddr, tmp_len);
+		/* Don't set the first's own bit, here */
+		if (first != desc) {
+			priv->tx_sk[entry] = NULL;
+			desc_set_own(desc);
+		}
+
+		entry = circ_inc(entry, dma_desc_tx);
+		len -= tmp_len;
+	}
+
+	for (i = 0; i < nfrags; i++) {
+		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+		len = skb_frag_size(frag);
+		desc = priv->dma_tx + entry;
+		paddr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
+		if (dma_mapping_error(priv->dev, paddr)) {
+			dev_kfree_skb(skb);
+			return -EIO;
+		}
+
+		desc_buf_set(desc, paddr, len);
+		desc_set_own(desc);
+		priv->tx_sk[entry] = NULL;
+		entry = circ_inc(entry, dma_desc_tx);
+	}
+
+	ndev->stats.tx_bytes += skb->len;
+	priv->tx_dirty = entry;
+	desc_tx_close(first, desc, csum_insert);
+
+	desc_set_own(first);
+	spin_unlock(&priv->tx_lock);
+
+	if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) <=
+			(MAX_SKB_FRAGS + 1)) {
+		netif_stop_queue(ndev);
+		if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
+				TX_THRESH)
+			netif_wake_queue(ndev);
+	}
+
+#ifdef DEBUG
+	printk("=======TX Descriptor DMA: 0x%08llx\n", priv->dma_tx_phy);
+	printk("Tx pointor: dirty: %d, clean: %d\n", priv->tx_dirty, priv->tx_clean);
+	desc_print(priv->dma_tx, dma_desc_tx);
+#endif
+	sunxi_tx_poll(priv->base);
+	geth_tx_complete(priv);
+
+	return NETDEV_TX_OK;
+}
+
+static int geth_rx(struct geth_priv *priv, int limit)
+{
+	unsigned int rxcount = 0;
+	unsigned int entry;
+	struct dma_desc *desc;
+	struct sk_buff *skb;
+	int status;
+	int frame_len;
+
+	while (rxcount < limit) {
+		entry = priv->rx_dirty;
+		desc = priv->dma_rx + entry;
+
+		if (desc_get_own(desc))
+			break;
+
+		rxcount++;
+		priv->rx_dirty = circ_inc(priv->rx_dirty, dma_desc_rx);
+
+		/* Get length & status from hardware */
+		frame_len = desc_rx_frame_len(desc);
+		status = desc_get_rx_status(desc, (void *)(&priv->xstats));
+
+		netdev_dbg(priv->ndev, "Rx frame size %d, status: %d\n",
+			   frame_len, status);
+
+		skb = priv->rx_sk[entry];
+		if (unlikely(!skb)) {
+			netdev_err(priv->ndev, "Skb is null\n");
+			priv->ndev->stats.rx_dropped++;
+			break;
+		}
+
+#ifdef PKT_DEBUG
+		printk("======RX PKT DATA: ============\n");
+		/* dump the packet */
+		print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
+				16, 1, skb->data, 64, true);
+#endif
+
+		if (status == discard_frame) {
+			netdev_dbg(priv->ndev, "Get error pkt\n");
+			priv->ndev->stats.rx_errors++;
+			continue;
+		}
+
+		if (unlikely(status != llc_snap))
+			frame_len -= ETH_FCS_LEN;
+
+		priv->rx_sk[entry] = NULL;
+
+		skb_put(skb, frame_len);
+		dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
+				 desc_buf_get_len(desc), DMA_FROM_DEVICE);
+
+		skb->protocol = eth_type_trans(skb, priv->ndev);
+
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		napi_gro_receive(&priv->napi, skb);
+
+		priv->ndev->stats.rx_packets++;
+		priv->ndev->stats.rx_bytes += frame_len;
+	}
+
+#ifdef DEBUG
+	if (rxcount > 0) {
+		printk("======RX Descriptor DMA: 0x%08llx=\n", priv->dma_rx_phy);
+		printk("RX pointor: dirty: %d, clean: %d\n", priv->rx_dirty, priv->rx_clean);
+		desc_print(priv->dma_rx, dma_desc_rx);
+	}
+#endif
+	geth_rx_refill(priv->ndev);
+
+	return rxcount;
+}
+
+static int geth_poll(struct napi_struct *napi, int budget)
+{
+	struct geth_priv *priv = container_of(napi, struct geth_priv, napi);
+	int work_done = 0;
+
+	geth_tx_complete(priv);
+	work_done = geth_rx(priv, budget);
+
+	if (work_done < budget) {
+		napi_complete(napi);
+		sunxi_int_enable(priv->base);
+	}
+
+	return work_done;
+}
+
+static int geth_change_mtu(struct net_device *ndev, int new_mtu)
+{
+	int max_mtu;
+
+	if (netif_running(ndev)) {
+		pr_err("%s: must be stopped to change its MTU\n", ndev->name);
+		return -EBUSY;
+	}
+
+	max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
+
+	if ((new_mtu < 46) || (new_mtu > max_mtu)) {
+		pr_err("%s: invalid MTU, max MTU is: %d\n", ndev->name, max_mtu);
+		return -EINVAL;
+	}
+
+	ndev->mtu = new_mtu;
+	netdev_update_features(ndev);
+
+	return 0;
+}
+
+static netdev_features_t geth_fix_features(struct net_device *ndev,
+					   netdev_features_t features)
+{
+	return features;
+}
+
+static void geth_set_rx_mode(struct net_device *ndev)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	unsigned int value = 0;
+
+	pr_debug("%s: # mcasts %d, # unicast %d\n",
+		 __func__, netdev_mc_count(ndev), netdev_uc_count(ndev));
+
+	spin_lock(&priv->lock);
+	if (ndev->flags & IFF_PROMISC) {
+		value = GETH_FRAME_FILTER_PR;
+	} else if ((netdev_mc_count(ndev) > HASH_TABLE_SIZE) ||
+		   (ndev->flags & IFF_ALLMULTI)) {
+		value = GETH_FRAME_FILTER_PM;	/* pass all multi */
+		sunxi_hash_filter(priv->base, ~0UL, ~0UL);
+	} else if (!netdev_mc_empty(ndev)) {
+		u32 mc_filter[2];
+		struct netdev_hw_addr *ha;
+
+		/* Hash filter for multicast */
+		value = GETH_FRAME_FILTER_HMC;
+
+		memset(mc_filter, 0, sizeof(mc_filter));
+		netdev_for_each_mc_addr(ha, ndev) {
+			/* The upper 6 bits of the calculated CRC are used to
+			 *  index the contens of the hash table
+			 */
+			int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
+			/* The most significant bit determines the register to
+			 * use (H/L) while the other 5 bits determine the bit
+			 * within the register.
+			 */
+			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+		}
+		sunxi_hash_filter(priv->base, mc_filter[0], mc_filter[1]);
+	}
+
+	/* Handle multiple unicast addresses (perfect filtering)*/
+	if (netdev_uc_count(ndev) > 16) {
+		/* Switch to promiscuous mode is more than 8 addrs are required */
+		value |= GETH_FRAME_FILTER_PR;
+	} else {
+		int reg = 1;
+		struct netdev_hw_addr *ha;
+
+		netdev_for_each_uc_addr(ha, ndev) {
+			sunxi_set_umac(priv->base, ha->addr, reg);
+			reg++;
+		}
+	}
+
+#ifdef FRAME_FILTER_DEBUG
+	/* Enable Receive all mode (to debug filtering_fail errors) */
+	value |= GETH_FRAME_FILTER_RA;
+#endif
+	sunxi_set_filter(priv->base, value);
+	spin_unlock(&priv->lock);
+}
+
+static void geth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	geth_tx_err(priv);
+}
+
+static int geth_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+	if (!netif_running(ndev))
+		return -EINVAL;
+
+	if (!ndev->phydev)
+		return -EINVAL;
+
+	return phy_mii_ioctl(ndev->phydev, rq, cmd);
+}
+
+/* Configuration changes (passed on by ifconfig) */
+static int geth_config(struct net_device *ndev, struct ifmap *map)
+{
+	if (ndev->flags & IFF_UP)	/* can't act on a running interface */
+		return -EBUSY;
+
+	/* Don't allow changing the I/O address */
+	if (map->base_addr != ndev->base_addr) {
+		pr_warn("%s: can't change I/O address\n", ndev->name);
+		return -EOPNOTSUPP;
+	}
+
+	/* Don't allow changing the IRQ */
+	if (map->irq != ndev->irq) {
+		pr_warn("%s: can't change IRQ number %d\n", ndev->name, ndev->irq);
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int geth_set_mac_address(struct net_device *ndev, void *p)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct sockaddr *addr = p;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+	sunxi_set_umac(priv->base, ndev->dev_addr, 0);
+
+	return 0;
+}
+
+int geth_set_features(struct net_device *ndev, netdev_features_t features)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	if (features & NETIF_F_LOOPBACK && netif_running(ndev))
+		sunxi_mac_loopback(priv->base, 1);
+	else
+		sunxi_mac_loopback(priv->base, 0);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
+/* Polling receive - used by NETCONSOLE and other diagnostic tools
+ * to allow network I/O with interrupts disabled.
+ */
+static void geth_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	geth_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
+static const struct net_device_ops geth_netdev_ops = {
+	.ndo_init = NULL,
+	.ndo_open = geth_open,
+	.ndo_start_xmit = geth_xmit,
+	.ndo_stop = geth_stop,
+	.ndo_change_mtu = geth_change_mtu,
+	.ndo_fix_features = geth_fix_features,
+	.ndo_set_rx_mode = geth_set_rx_mode,
+	.ndo_tx_timeout = geth_tx_timeout,
+	.ndo_do_ioctl = geth_ioctl,
+	.ndo_set_config = geth_config,
+#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
+	.ndo_poll_controller = geth_poll_controller,
+#endif
+	.ndo_set_mac_address = geth_set_mac_address,
+	.ndo_set_features = geth_set_features,
+};
+
+static int geth_check_if_running(struct net_device *ndev)
+{
+	if (!netif_running(ndev))
+		return -EBUSY;
+	return 0;
+}
+
+static int geth_get_sset_count(struct net_device *netdev, int sset)
+{
+	int len;
+
+	switch (sset) {
+	case ETH_SS_STATS:
+		len = 0;
+		return len;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/*static int geth_ethtool_getsettings(struct net_device *ndev,
+				    struct ethtool_cmd *cmd)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct phy_device *phy = ndev->phydev;
+	int rc;
+
+	if (phy == NULL) {
+		netdev_err(ndev, "%s: %s: PHY is not registered\n",
+		       __func__, ndev->name);
+		return -ENODEV;
+	}
+
+	if (!netif_running(ndev)) {
+		pr_err("%s: interface is disabled: we cannot track "
+		       "link speed / duplex setting\n", ndev->name);
+		return -EBUSY;
+	}
+
+	cmd->transceiver = XCVR_INTERNAL;
+	spin_lock_irq(&priv->lock);
+	//rc = phy_ethtool_gset(phy, cmd);
+	spin_unlock_irq(&priv->lock);
+
+	return rc;
+}
+
+static int geth_ethtool_setsettings(struct net_device *ndev,
+				    struct ethtool_cmd *cmd)
+{
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct phy_device *phy = ndev->phydev;
+	int rc;
+
+	spin_lock(&priv->lock);
+	rc = phy_ethtool_sset(phy, cmd);
+	spin_unlock(&priv->lock);
+
+	return rc;
+}*/
+
+static void geth_ethtool_getdrvinfo(struct net_device *ndev,
+				    struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, "sunxi_geth", sizeof(info->driver));
+
+#define DRV_MODULE_VERSION "SUNXI Gbgit driver V1.1"
+
+	strcpy(info->version, DRV_MODULE_VERSION);
+	info->fw_version[0] = '\0';
+}
+
+static const struct ethtool_ops geth_ethtool_ops = {
+	.begin = geth_check_if_running,
+	//.get_settings = geth_ethtool_getsettings,
+	//.set_settings = geth_ethtool_setsettings,
+	.get_link = ethtool_op_get_link,
+	.get_pauseparam = NULL,
+	.set_pauseparam = NULL,
+	.get_ethtool_stats = NULL,
+	.get_strings = NULL,
+	.get_wol = NULL,
+	.set_wol = NULL,
+	.get_sset_count = geth_get_sset_count,
+	.get_drvinfo = geth_ethtool_getdrvinfo,
+};
+
+/* config hardware resource */
+static int geth_hw_init(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct geth_priv *priv = netdev_priv(ndev);
+	struct device_node *np = pdev->dev.of_node;
+	int ret = 0;
+	struct resource *res;
+	u32 value;
+//	struct gpio_config cfg;
+//	const char *gmac_power;
+//	char power[20];
+//	int i;
+
+#if 1
+	priv->phy_ext = EXT_PHY;
+#else
+	priv->phy_ext = INT_PHY;
+#endif
+
+	/* config memery resource */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!res)) {
+		pr_err("%s: ERROR: get gmac memory failed", __func__);
+		return -ENODEV;
+	}
+
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (!priv->base) {
+		pr_err("%s: ERROR: gmac memory mapping failed", __func__);
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (unlikely(!res)) {
+		pr_err("%s: ERROR: get phy memory failed", __func__);
+		ret = -ENODEV;
+		goto mem_err;
+	}
+
+	priv->base_phy = devm_ioremap_resource(&pdev->dev, res);
+	if (unlikely(!priv->base_phy)) {
+		pr_err("%s: ERROR: phy memory mapping failed", __func__);
+		ret = -ENOMEM;
+		goto mem_err;
+	}
+
+	/* config IRQ */
+	ndev->irq = platform_get_irq_byname(pdev, "gmacirq");
+	if (ndev->irq == -ENXIO) {
+		pr_err("%s: ERROR: MAC IRQ not found\n", __func__);
+		ret = -ENXIO;
+		goto irq_err;
+	}
+
+	ret = request_irq(ndev->irq, geth_interrupt, IRQF_SHARED, dev_name(&pdev->dev), ndev);
+	if (unlikely(ret < 0)) {
+		pr_err("Could not request irq %d, error: %d\n", ndev->irq, ret);
+		goto irq_err;
+	}
+
+	/* get gmac rst handle */
+/*
+	priv->reset = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->reset)) {
+		pr_err("%s: Get gmac reset control failed!\n", __func__);
+		return PTR_ERR(priv->reset);
+	}
+*/
+	/* config clock */
+/*
+	priv->geth_clk = of_clk_get_by_name(np, "gmac");
+	if (unlikely(!priv->geth_clk || IS_ERR(priv->geth_clk))) {
+		pr_err("Get gmac clock failed!\n");
+		ret = -EINVAL;
+		goto clk_err;
+	}
+
+	if (INT_PHY == priv->phy_ext) {
+		priv->ephy_clk = of_clk_get_by_name(np, "ephy");
+		if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
+			pr_err("Get ephy clock failed!\n");
+			ret = -EINVAL;
+			goto clk_err;
+		}
+	} else {
+		if (!of_property_read_u32(np, "use-ephy25m", &(priv->use_ephy_clk))
+				&& priv->use_ephy_clk) {
+			priv->ephy_clk = of_clk_get_by_name(np, "ephy");
+			if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
+				pr_err("Get ephy clk failed!\n");
+				ret = -EINVAL;
+				goto clk_err;
+			}
+		}
+	}
+*/
+	/* config power regulator */
+/*
+	if (EXT_PHY == priv->phy_ext) {
+		for (i = 0; i < POWER_CHAN_NUM; i++) {
+			snprintf(power, 15, "gmac-power%d", i);
+			ret = of_property_read_string(np, power, &gmac_power);
+			if (ret) {
+				priv->gmac_power[i] = NULL;
+				pr_info("gmac-power%d: NULL\n", i);
+				continue;
+			}
+			priv->gmac_power[i] = regulator_get(NULL, gmac_power);
+			if (IS_ERR(priv->gmac_power[i])) {
+				pr_err("gmac-power%d get error!\n", i);
+				ret = -EINVAL;
+				goto clk_err;
+			}
+		}
+	}
+*/
+	/* config other parameters */
+	of_get_phy_mode(np, &(priv->phy_interface));
+	if (priv->phy_interface != PHY_INTERFACE_MODE_MII &&
+	    priv->phy_interface != PHY_INTERFACE_MODE_RGMII &&
+	    priv->phy_interface != PHY_INTERFACE_MODE_RMII) {
+		pr_err("Not support phy type!\n");
+		priv->phy_interface = PHY_INTERFACE_MODE_MII;
+	}
+
+	if (!of_property_read_u32(np, "tx-delay", &value))
+		priv->tx_delay = value;
+
+	if (!of_property_read_u32(np, "rx-delay", &value))
+		priv->rx_delay = value;
+
+	/* config pinctrl */
+/*
+	if (EXT_PHY == priv->phy_ext) {
+		priv->phyrst = of_get_named_gpio_flags(np, "phy-rst", 0, (enum of_gpio_flags *)&cfg);
+		priv->rst_active_low = (cfg.data == OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+
+		if (gpio_is_valid(priv->phyrst)) {
+			if (gpio_request(priv->phyrst, "phy-rst") < 0) {
+				pr_err("gmac gpio request fail!\n");
+				ret = -EINVAL;
+				goto pin_err;
+			}
+		}
+
+		priv->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+		if (IS_ERR_OR_NULL(priv->pinctrl)) {
+			pr_err("gmac pinctrl error!\n");
+			priv->pinctrl = NULL;
+			ret = -EINVAL;
+			goto pin_err;
+		}
+	}
+*/
+	return 0;
+
+//pin_err:
+/*
+	if (EXT_PHY == priv->phy_ext) {
+		for (i = 0; i < POWER_CHAN_NUM; i++) {
+			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
+				continue;
+			regulator_put(priv->gmac_power[i]);
+		}
+	}
+*/
+//clk_err:
+//	free_irq(ndev->irq, ndev);
+irq_err:
+	devm_iounmap(&pdev->dev, priv->base_phy);
+mem_err:
+	devm_iounmap(&pdev->dev, priv->base);
+
+	return ret;
+}
+
+static void geth_hw_release(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	devm_iounmap(&pdev->dev, (priv->base_phy));
+	devm_iounmap(&pdev->dev, priv->base);
+	free_irq(ndev->irq, ndev);
+/*
+	if (priv->geth_clk)
+		clk_put(priv->geth_clk);
+
+	if (EXT_PHY == priv->phy_ext) {
+		for (i = 0; i < POWER_CHAN_NUM; i++) {
+			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
+				continue;
+			regulator_put(priv->gmac_power[i]);
+		}
+
+		if (!IS_ERR_OR_NULL(priv->pinctrl))
+			devm_pinctrl_put(priv->pinctrl);
+
+		if (gpio_is_valid(priv->phyrst))
+			gpio_free(priv->phyrst);
+	}
+
+	if (!IS_ERR_OR_NULL(priv->ephy_clk))
+		clk_put(priv->ephy_clk);
+*/
+}
+
+/**
+ * geth_probe
+ * @pdev: platform device pointer
+ * Description: the driver is initialized through platform_device.
+ */
+static int geth_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct net_device *ndev = NULL;
+	struct geth_priv *priv;
+
+	pr_info("sunxi gmac driver's version: %s\n", SUNXI_GMAC_VERSION);
+
+#if IS_ENABLED(CONFIG_OF)
+	pdev->dev.dma_mask = &geth_dma_mask;
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+#endif
+
+	ndev = alloc_etherdev(sizeof(struct geth_priv));
+	if (!ndev) {
+		dev_err(&pdev->dev, "could not allocate device.\n");
+		return -ENOMEM;
+	}
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	priv = netdev_priv(ndev);
+	platform_set_drvdata(pdev, ndev);
+
+	/* Must set private data to pdev, before call it */
+	ret = geth_hw_init(pdev);
+	if (0 != ret) {
+		pr_err("geth_hw_init fail!\n");
+		goto hw_err;
+	}
+
+	/* setup the netdevice, fill the field of netdevice */
+	ether_setup(ndev);
+	ndev->netdev_ops = &geth_netdev_ops;
+	netdev_set_default_ethtool_ops(ndev, &geth_ethtool_ops);
+	ndev->base_addr = (unsigned long)priv->base;
+
+	priv->ndev = ndev;
+	priv->dev = &pdev->dev;
+
+	/* TODO: support the VLAN frames */
+	ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
+				NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
+
+	ndev->features |= ndev->hw_features;
+	ndev->hw_features |= NETIF_F_LOOPBACK;
+	ndev->priv_flags |= IFF_UNICAST_FLT;
+
+	ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+	netif_napi_add(ndev, &priv->napi, geth_poll,  BUDGET);
+
+	spin_lock_init(&priv->lock);
+	spin_lock_init(&priv->tx_lock);
+
+	/* The last val is mdc clock ratio */
+	sunxi_geth_register((void *)ndev->base_addr, HW_VERSION, 0x03);
+
+	ret = register_netdev(ndev);
+	if (ret) {
+		netif_napi_del(&priv->napi);
+		pr_err("Error: Register %s failed\n", ndev->name);
+		goto reg_err;
+	}
+
+	/* Before open the device, the mac address should be set */
+	geth_check_addr(ndev, mac_str);
+
+#ifdef CONFIG_GETH_ATTRS
+	geth_create_attrs(ndev);
+#endif
+	device_create_file(&pdev->dev, &dev_attr_gphy_test);
+	device_create_file(&pdev->dev, &dev_attr_mii_reg);
+	device_create_file(&pdev->dev, &dev_attr_loopback_test);
+	device_create_file(&pdev->dev, &dev_attr_extra_tx_stats);
+	device_create_file(&pdev->dev, &dev_attr_extra_rx_stats);
+
+	device_enable_async_suspend(&pdev->dev);
+
+#if IS_ENABLED(CONFIG_PM)
+	INIT_WORK(&priv->eth_work, geth_resume_work);
+#endif
+
+	return 0;
+
+reg_err:
+	geth_hw_release(pdev);
+hw_err:
+	platform_set_drvdata(pdev, NULL);
+	free_netdev(ndev);
+
+	return ret;
+}
+
+static int geth_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct geth_priv *priv = netdev_priv(ndev);
+
+	device_remove_file(&pdev->dev, &dev_attr_gphy_test);
+	device_remove_file(&pdev->dev, &dev_attr_mii_reg);
+	device_remove_file(&pdev->dev, &dev_attr_loopback_test);
+	device_remove_file(&pdev->dev, &dev_attr_extra_tx_stats);
+	device_remove_file(&pdev->dev, &dev_attr_extra_rx_stats);
+
+	netif_napi_del(&priv->napi);
+	unregister_netdev(ndev);
+	geth_hw_release(pdev);
+	platform_set_drvdata(pdev, NULL);
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static const struct of_device_id geth_of_match[] = {
+	{.compatible = "allwinner,sunxi-gmac",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, geth_of_match);
+
+static struct platform_driver geth_driver = {
+	.probe	= geth_probe,
+	.remove = geth_remove,
+	.driver = {
+		   .name = "sunxi-gmac",
+		   .owner = THIS_MODULE,
+		   .pm = &geth_pm_ops,
+		   .of_match_table = geth_of_match,
+	},
+};
+module_platform_driver(geth_driver);
+
+#ifdef MODULE
+static int __init set_mac_addr(char *str)
+{
+	char *p = str;
+
+	if (str && strlen(str))
+		memcpy(mac_str, p, 18);
+
+	return 0;
+}
+__setup("mac_addr=", set_mac_addr);
+#endif
+
+MODULE_DESCRIPTION("Allwinner Gigabit Ethernet driver");
+MODULE_AUTHOR("fuzhaoke <fuzhaoke@allwinnertech.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
new file mode 100644
index 00000000..ea7a6f15
--- /dev/null
+++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
@@ -0,0 +1,258 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
+ *
+ * This file is provided under a dual BSD/GPL license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ */
+#ifndef __SUNXI_GETH_H__
+#define __SUNXI_GETH_H__
+
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+/* GETH_FRAME_FILTER  register value */
+#define GETH_FRAME_FILTER_PR	0x00000001	/* Promiscuous Mode */
+#define GETH_FRAME_FILTER_HUC	0x00000002	/* Hash Unicast */
+#define GETH_FRAME_FILTER_HMC	0x00000004	/* Hash Multicast */
+#define GETH_FRAME_FILTER_DAIF	0x00000008	/* DA Inverse Filtering */
+#define GETH_FRAME_FILTER_PM	0x00000010	/* Pass all multicast */
+#define GETH_FRAME_FILTER_DBF	0x00000020	/* Disable Broadcast frames */
+#define GETH_FRAME_FILTER_SAIF	0x00000100	/* Inverse Filtering */
+#define GETH_FRAME_FILTER_SAF	0x00000200	/* Source Address Filter */
+#define GETH_FRAME_FILTER_HPF	0x00000400	/* Hash or perfect Filter */
+#define GETH_FRAME_FILTER_RA	0x80000000	/* Receive all mode */
+
+/* Default tx descriptor */
+#define TX_SINGLE_DESC0		0x80000000
+#define TX_SINGLE_DESC1		0x63000000
+
+/* Default rx descriptor */
+#define RX_SINGLE_DESC0		0x80000000
+#define RX_SINGLE_DESC1		0x83000000
+
+typedef union {
+	struct {
+		/* TDES0 */
+		unsigned int deferred:1;	/* Deferred bit (only half-duplex) */
+		unsigned int under_err:1;	/* Underflow error */
+		unsigned int ex_deferral:1;	/* Excessive deferral */
+		unsigned int coll_cnt:4;	/* Collision count */
+		unsigned int vlan_tag:1;	/* VLAN Frame */
+		unsigned int ex_coll:1;		/* Excessive collision */
+		unsigned int late_coll:1;	/* Late collision */
+		unsigned int no_carr:1;		/* No carrier */
+		unsigned int loss_carr:1;	/* Loss of collision */
+		unsigned int ipdat_err:1;	/* IP payload error */
+		unsigned int frm_flu:1;		/* Frame flushed */
+		unsigned int jab_timeout:1;	/* Jabber timeout */
+		unsigned int err_sum:1;		/* Error summary */
+		unsigned int iphead_err:1;	/* IP header error */
+		unsigned int ttss:1;		/* Transmit time stamp status */
+		unsigned int reserved0:13;
+		unsigned int own:1;		/* Own bit. CPU:0, DMA:1 */
+	} tx;
+
+	/* bits 5 7 0 | Frame status
+	 * ----------------------------------------------------------
+	 *      0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
+	 *      1 0 0 | IPv4/6 No CSUM errorS.
+	 *      1 0 1 | IPv4/6 CSUM PAYLOAD error
+	 *      1 1 0 | IPv4/6 CSUM IP HR error
+	 *      1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
+	 *      0 0 1 | IPv4/6 unsupported IP PAYLOAD
+	 *      0 1 1 | COE bypassed.. no IPv4/6 frame
+	 *      0 1 0 | Reserved.
+	 */
+	struct {
+		/* RDES0 */
+		unsigned int chsum_err:1;	/* Payload checksum error */
+		unsigned int crc_err:1;		/* CRC error */
+		unsigned int dribbling:1;	/* Dribble bit error */
+		unsigned int mii_err:1;		/* Received error (bit3) */
+		unsigned int recv_wt:1;		/* Received watchdog timeout */
+		unsigned int frm_type:1;	/* Frame type */
+		unsigned int late_coll:1;	/* Late Collision */
+		unsigned int ipch_err:1;	/* IPv header checksum error (bit7) */
+		unsigned int last_desc:1;	/* Laset descriptor */
+		unsigned int first_desc:1;	/* First descriptor */
+		unsigned int vlan_tag:1;	/* VLAN Tag */
+		unsigned int over_err:1;	/* Overflow error (bit11) */
+		unsigned int len_err:1;		/* Length error */
+		unsigned int sou_filter:1;	/* Source address filter fail */
+		unsigned int desc_err:1;	/* Descriptor error */
+		unsigned int err_sum:1;		/* Error summary (bit15) */
+		unsigned int frm_len:14;	/* Frame length */
+		unsigned int des_filter:1;	/* Destination address filter fail */
+		unsigned int own:1;		/* Own bit. CPU:0, DMA:1 */
+	#define RX_PKT_OK		0x7FFFB77C
+	#define RX_LEN			0x3FFF0000
+	} rx;
+
+	unsigned int all;
+} desc0_u;
+
+typedef union {
+	struct {
+		/* TDES1 */
+		unsigned int buf1_size:11;	/* Transmit buffer1 size */
+		unsigned int buf2_size:11;	/* Transmit buffer2 size */
+		unsigned int ttse:1;		/* Transmit time stamp enable */
+		unsigned int dis_pad:1;		/* Disable pad (bit23) */
+		unsigned int adr_chain:1;	/* Second address chained */
+		unsigned int end_ring:1;	/* Transmit end of ring */
+		unsigned int crc_dis:1;		/* Disable CRC */
+		unsigned int cic:2;		/* Checksum insertion control (bit27:28) */
+		unsigned int first_sg:1;	/* First Segment */
+		unsigned int last_seg:1;	/* Last Segment */
+		unsigned int interrupt:1;	/* Interrupt on completion */
+	} tx;
+
+	struct {
+		/* RDES1 */
+		unsigned int buf1_size:11;	/* Received buffer1 size */
+		unsigned int buf2_size:11;	/* Received buffer2 size */
+		unsigned int reserved1:2;
+		unsigned int adr_chain:1;	/* Second address chained */
+		unsigned int end_ring:1;		/* Received end of ring */
+		unsigned int reserved2:5;
+		unsigned int dis_ic:1;		/* Disable interrupt on completion */
+	} rx;
+
+	unsigned int all;
+} desc1_u;
+
+typedef struct dma_desc {
+	desc0_u desc0;
+	desc1_u desc1;
+	/* The address of buffers */
+	unsigned int	desc2;
+	/* Next desc's address */
+	unsigned int	desc3;
+} __attribute__((packed)) dma_desc_t;
+
+enum rx_frame_status { /* IPC status */
+	good_frame = 0,
+	discard_frame = 1,
+	csum_none = 2,
+	llc_snap = 4,
+};
+
+enum tx_dma_irq_status {
+	tx_hard_error = 1,
+	tx_hard_error_bump_tc = 2,
+	handle_tx_rx = 3,
+};
+
+struct geth_extra_stats {
+	/* Transmit errors */
+	unsigned long tx_underflow;
+	unsigned long tx_carrier;
+	unsigned long tx_losscarrier;
+	unsigned long vlan_tag;
+	unsigned long tx_deferred;
+	unsigned long tx_vlan;
+	unsigned long tx_jabber;
+	unsigned long tx_frame_flushed;
+	unsigned long tx_payload_error;
+	unsigned long tx_ip_header_error;
+
+	/* Receive errors */
+	unsigned long rx_desc;
+	unsigned long sa_filter_fail;
+	unsigned long overflow_error;
+	unsigned long ipc_csum_error;
+	unsigned long rx_collision;
+	unsigned long rx_crc;
+	unsigned long dribbling_bit;
+	unsigned long rx_length;
+	unsigned long rx_mii;
+	unsigned long rx_multicast;
+	unsigned long rx_gmac_overflow;
+	unsigned long rx_watchdog;
+	unsigned long da_rx_filter_fail;
+	unsigned long sa_rx_filter_fail;
+	unsigned long rx_missed_cntr;
+	unsigned long rx_overflow_cntr;
+	unsigned long rx_vlan;
+
+	/* Tx/Rx IRQ errors */
+	unsigned long tx_undeflow_irq;
+	unsigned long tx_process_stopped_irq;
+	unsigned long tx_jabber_irq;
+	unsigned long rx_overflow_irq;
+	unsigned long rx_buf_unav_irq;
+	unsigned long rx_process_stopped_irq;
+	unsigned long rx_watchdog_irq;
+	unsigned long tx_early_irq;
+	unsigned long fatal_bus_error_irq;
+
+	/* Extra info */
+	unsigned long threshold;
+	unsigned long tx_pkt_n;
+	unsigned long rx_pkt_n;
+	unsigned long poll_n;
+	unsigned long sched_timer_n;
+	unsigned long normal_irq_n;
+};
+
+int sunxi_mdio_read(void *,  int, int);
+int sunxi_mdio_write(void *, int, int, unsigned short);
+int sunxi_mdio_reset(void *);
+void sunxi_set_link_mode(void *iobase, int duplex, int speed);
+void sunxi_int_disable(void *);
+int sunxi_int_status(void *, struct geth_extra_stats *x);
+int sunxi_mac_init(void *, int txmode, int rxmode);
+void sunxi_set_umac(void *, unsigned char *, int);
+void sunxi_mac_enable(void *);
+void sunxi_mac_disable(void *);
+void sunxi_tx_poll(void *);
+void sunxi_int_enable(void *);
+void sunxi_start_rx(void *, unsigned long);
+void sunxi_start_tx(void *, unsigned long);
+void sunxi_stop_tx(void *);
+void sunxi_stop_rx(void *);
+void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high);
+void sunxi_set_filter(void *iobase, unsigned long flags);
+void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause);
+void sunxi_mac_loopback(void *iobase, int enable);
+
+void desc_buf_set(struct dma_desc *p, unsigned long paddr, int size);
+void desc_set_own(struct dma_desc *p);
+void desc_init_chain(struct dma_desc *p, unsigned long paddr,  unsigned int size);
+void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert);
+void desc_init(struct dma_desc *p);
+int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x);
+int desc_buf_get_len(struct dma_desc *desc);
+int desc_buf_get_addr(struct dma_desc *desc);
+int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x);
+int desc_get_own(struct dma_desc *desc);
+int desc_get_tx_ls(struct dma_desc *desc);
+int desc_rx_frame_len(struct dma_desc *desc);
+
+int sunxi_mac_reset(void *iobase, void (*mdelay)(int), int n);
+int sunxi_geth_register(void *iobase, int version, unsigned int div);
+
+#if IS_ENABLED(CONFIG_SUNXI_EPHY)
+extern int ephy_is_enable(void);
+#endif
+
+#if IS_ENABLED(CONFIG_ARCH_SUN8IW3) \
+	|| IS_ENABLED(CONFIG_ARCH_SUN9IW1) \
+	|| IS_ENABLED(CONFIG_ARCH_SUN7I)
+#define HW_VERSION	0
+#else
+#define HW_VERSION	1
+#endif
+
+#endif
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 821e85a..3c86c2a 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -338,7 +338,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)
 			"2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
 			val_txdly ? "enabled" : "disabled");
 	}
-
+return 0;
 	ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY,
 				       val_rxdly);
 	if (ret < 0) {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06  9:04 ` [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use guoren
@ 2021-06-06 10:50   ` Andre Przywara
  2021-06-06 15:32     ` Guo Ren
  2021-06-06 16:16   ` Arnd Bergmann
  1 sibling, 1 reply; 52+ messages in thread
From: Andre Przywara @ 2021-06-06 10:50 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, maxime, drew, liush,
	lazyparser, wefu, linux-riscv, linux-kernel, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

On Sun,  6 Jun 2021 09:04:09 +0000
guoren@kernel.org wrote:

Hi,

> From: liush <liush@allwinnertech.com>
> 
> This is a temporary driver, only guaranteed to work on allwinner
> D1. In order to ensure the developer's demand for network usage.

That looks like some Allwinner BSP driver, please don't endorse code
of this quality (just look at all that commented code and the attempt
for compile-time configuration).
 
> It only could work at 1Gps mode.
> 
> The correct gmac driver should follow (I guess)
> drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> 
> If anyone is familiar with it and can help porting, I would be
> very grateful.

Have you tried compiling and using that driver? Ideally it should just
work, Linux drivers are meant to be portable, by design. And the driver
is already enabled by COMPILE_TEST.
But I guess you need some extra care to make the non-coherent DMA work?
I haven't looked in detail, but are those new CMOs hooked into the
generic DMA framework?

Cheers,
Andre

> Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> Tested-by: Guo Ren <guoren@kernel.org>
> Signed-off-by: Guo Ren <guoren@kernel.org>
> Cc: Maxime Ripard <mripard@kernel.org>
> Cc: Corentin Labbe <clabbe@baylibre.com>
> Cc: Samuel Holland <samuel@sholland.org>
> Cc: Icenowy Zheng <icenowy@aosc.io>
> Cc: LABBE Corentin <clabbe.montjoie@gmail.com>
> Cc: Michael Walle <michael@walle.cc>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Cc: Maxime Ripard <maxime@cerno.tech>
> Cc: Wei Fu <wefu@redhat.com>
> Cc: Wei Wu <lazyparser@gmail.com>
> Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> ---
>  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |    2 +-
>  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |   16 +
>  drivers/net/ethernet/Kconfig                       |    1 +
>  drivers/net/ethernet/Makefile                      |    1 +
>  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
>  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240 ++++++++++++++++++++
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
>  drivers/net/phy/realtek.c                          |    2 +-
>  10 files changed, 3232 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
>  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> 
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> index cd9f7c9..31b681d 100644
> --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> @@ -11,7 +11,7 @@
>  	compatible = "allwinner,d1-nezha-kit";
>  
>  	chosen {
> -		bootargs = "console=ttyS0,115200";
> +		bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";
>  		stdout-path = &serial0;
>  	};
>  
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> index 11cd938..d317e19 100644
> --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> @@ -80,5 +80,21 @@
>  			clocks = <&dummy_apb>;
>  			status = "disabled";
>  		};
> +
> +		eth@4500000 {
> +			compatible = "allwinner,sunxi-gmac";
> +			reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
> +			interrupts-extended = <&plic 0x3e 0x04>;
> +			interrupt-names = "gmacirq";
> +			device_type = "gmac0";
> +			phy-mode = "rgmii";
> +			use_ephy25m = <0x01>;
> +			tx-delay = <0x03>;
> +			rx-delay = <0x03>;
> +			gmac-power0;
> +			gmac-power1;
> +			gmac-power2;
> +			status = "okay";
> +		};
>  	};
>  };
> diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> index 1cdff1d..1f8e37c 100644
> --- a/drivers/net/ethernet/Kconfig
> +++ b/drivers/net/ethernet/Kconfig
> @@ -18,6 +18,7 @@ config MDIO
>  config SUNGEM_PHY
>  	tristate
>  
> +source "drivers/net/ethernet/allwinnertmp/Kconfig"
>  source "drivers/net/ethernet/3com/Kconfig"
>  source "drivers/net/ethernet/actions/Kconfig"
>  source "drivers/net/ethernet/adaptec/Kconfig"
> diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
> index cb3f908..3dacc0c 100644
> --- a/drivers/net/ethernet/Makefile
> +++ b/drivers/net/ethernet/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the Linux network Ethernet device drivers.
>  #
>  
> +obj-y += allwinnertmp/
>  obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
>  obj-$(CONFIG_NET_VENDOR_8390) += 8390/
>  obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/
> diff --git a/drivers/net/ethernet/allwinnertmp/Kconfig b/drivers/net/ethernet/allwinnertmp/Kconfig
> new file mode 100644
> index 00000000..4b7b378
> --- /dev/null
> +++ b/drivers/net/ethernet/allwinnertmp/Kconfig
> @@ -0,0 +1,17 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Allwinner device configuration
> +#
> +
> +config SUNXI_GMAC
> +	tristate "Allwinner GMAC support"
> +	default y
> +	depends on OF
> +	select CRC32
> +	select MII
> +	select PHYLIB
> +	help
> +	  Support for Allwinner Gigabit ethernet driver.
> +
> +	  To compile this driver as a module, choose M here.  The module
> +	  will be called sunxi-gmac.
> diff --git a/drivers/net/ethernet/allwinnertmp/Makefile b/drivers/net/ethernet/allwinnertmp/Makefile
> new file mode 100644
> index 00000000..1375dea
> --- /dev/null
> +++ b/drivers/net/ethernet/allwinnertmp/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Makefile for the Allwinner device drivers.
> +#
> +
> +obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o
> +sunxi_gmac-objs := sunxi-gmac.o sunxi-gmac-ops.o
> diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> new file mode 100644
> index 00000000..26ffd7f
> --- /dev/null
> +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> @@ -0,0 +1,690 @@
> +/*
> + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> + *
> + * Copyright © 2016-2018, fuzhaoke
> + *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> + *
> + * This file is provided under a dual BSD/GPL license.  When using or
> + * redistributing this file, you may do so under either license.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/kernel.h>
> +#include <linux/ctype.h>
> +#include <linux/printk.h>
> +#include <linux/io.h>
> +#include "sunxi-gmac.h"
> +
> +/******************************************************************************
> + *	sun8iw6 operations
> + *****************************************************************************/
> +#define GETH_BASIC_CTL0		0x00
> +#define GETH_BASIC_CTL1		0x04
> +#define GETH_INT_STA		0x08
> +#define GETH_INT_EN		0x0C
> +#define GETH_TX_CTL0		0x10
> +#define GETH_TX_CTL1		0x14
> +#define GETH_TX_FLOW_CTL	0x1C
> +#define GETH_TX_DESC_LIST	0x20
> +#define GETH_RX_CTL0		0x24
> +#define GETH_RX_CTL1		0x28
> +#define GETH_RX_DESC_LIST	0x34
> +#define GETH_RX_FRM_FLT		0x38
> +#define GETH_RX_HASH0		0x40
> +#define GETH_RX_HASH1		0x44
> +#define GETH_MDIO_ADDR		0x48
> +#define GETH_MDIO_DATA		0x4C
> +#define GETH_ADDR_HI(reg)	(0x50 + ((reg) << 3))
> +#define GETH_ADDR_LO(reg)	(0x54 + ((reg) << 3))
> +#define GETH_TX_DMA_STA		0xB0
> +#define GETH_TX_CUR_DESC	0xB4
> +#define GETH_TX_CUR_BUF		0xB8
> +#define GETH_RX_DMA_STA		0xC0
> +#define GETH_RX_CUR_DESC	0xC4
> +#define GETH_RX_CUR_BUF		0xC8
> +#define GETH_RGMII_STA		0xD0
> +
> +#define RGMII_IRQ		0x00000001
> +
> +#define	CTL0_LM			0x02
> +#define CTL0_DM			0x01
> +#define CTL0_SPEED		0x04
> +
> +#define BURST_LEN		0x3F000000
> +#define RX_TX_PRI		0x02
> +#define SOFT_RST		0x01
> +
> +#define TX_FLUSH		0x01
> +#define TX_MD			0x02
> +#define TX_NEXT_FRM		0x04
> +#define TX_TH			0x0700
> +
> +#define RX_FLUSH		0x01
> +#define RX_MD			0x02
> +#define RX_RUNT_FRM		0x04
> +#define RX_ERR_FRM		0x08
> +#define RX_TH			0x0030
> +
> +#define TX_INT			0x00001
> +#define TX_STOP_INT		0x00002
> +#define TX_UA_INT		0x00004
> +#define TX_TOUT_INT		0x00008
> +#define TX_UNF_INT		0x00010
> +#define TX_EARLY_INT		0x00020
> +#define RX_INT			0x00100
> +#define RX_UA_INT		0x00200
> +#define RX_STOP_INT		0x00400
> +#define RX_TOUT_INT		0x00800
> +#define RX_OVF_INT		0x01000
> +#define RX_EARLY_INT		0x02000
> +#define LINK_STA_INT		0x10000
> +
> +#define DISCARD_FRAME	-1
> +#define GOOD_FRAME	0
> +#define CSUM_NONE	2
> +#define LLC_SNAP	4
> +
> +#define SF_DMA_MODE		1
> +
> +/* Flow Control defines */
> +#define FLOW_OFF	0
> +#define FLOW_RX		1
> +#define FLOW_TX		2
> +#define FLOW_AUTO	(FLOW_TX | FLOW_RX)
> +
> +#define HASH_TABLE_SIZE 64
> +#define PAUSE_TIME 0x200
> +#define GMAC_MAX_UNICAST_ADDRESSES	8
> +
> +/* PHY address */
> +#define PHY_ADDR		0x01
> +#define PHY_DM			0x0010
> +#define PHY_AUTO_NEG		0x0020
> +#define PHY_POWERDOWN		0x0080
> +#define PHY_NEG_EN		0x1000
> +
> +#define MII_BUSY		0x00000001
> +#define MII_WRITE		0x00000002
> +#define MII_PHY_MASK		0x0000FFC0
> +#define MII_CR_MASK		0x0000001C
> +#define MII_CLK			0x00000008
> +/* bits 4 3 2 | AHB1 Clock	| MDC Clock
> + * -------------------------------------------------------
> + *      0 0 0 | 60 ~ 100 MHz	| div-42
> + *      0 0 1 | 100 ~ 150 MHz	| div-62
> + *      0 1 0 | 20 ~ 35 MHz	| div-16
> + *      0 1 1 | 35 ~ 60 MHz	| div-26
> + *      1 0 0 | 150 ~ 250 MHz	| div-102
> + *      1 0 1 | 250 ~ 300 MHz	| div-124
> + *      1 1 x | Reserved	|
> + */
> +
> +enum csum_insertion {
> +	cic_dis		= 0, /* Checksum Insertion Control */
> +	cic_ip		= 1, /* Only IP header */
> +	cic_no_pse	= 2, /* IP header but not pseudoheader */
> +	cic_full	= 3, /* IP header and pseudoheader */
> +};
> +
> +struct gethdev {
> +	void *iobase;
> +	unsigned int ver;
> +	unsigned int mdc_div;
> +};
> +
> +static struct gethdev hwdev;
> +
> +/***************************************************************************
> + * External interface
> + **************************************************************************/
> +/* Set a ring desc buffer */
> +void desc_init_chain(struct dma_desc *desc, unsigned long addr, unsigned int size)
> +{
> +	/* In chained mode the desc3 points to the next element in the ring.
> +	 * The latest element has to point to the head.
> +	 */
> +	int i;
> +	struct dma_desc *p = desc;
> +	unsigned long dma_phy = addr;
> +
> +	for (i = 0; i < (size - 1); i++) {
> +		dma_phy += sizeof(struct dma_desc);
> +		p->desc3 = (unsigned int)dma_phy;
> +		/* Chain mode */
> +		p->desc1.all |= (1 << 24);
> +		p++;
> +	}
> +	p->desc1.all |= (1 << 24);
> +	p->desc3 = (unsigned int)addr;
> +}
> +
> +int sunxi_mdio_read(void *iobase, int phyaddr, int phyreg)
> +{
> +	unsigned int value = 0;
> +
> +	/* Mask the MDC_DIV_RATIO */
> +	value |= ((hwdev.mdc_div & 0x07) << 20);
> +	value |= (((phyaddr << 12) & (0x0001F000)) |
> +			((phyreg << 4) & (0x000007F0)) |
> +			MII_BUSY);
> +
> +	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> +		;
> +
> +	writel(value, iobase + GETH_MDIO_ADDR);
> +	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> +		;
> +
> +	return (int)readl(iobase + GETH_MDIO_DATA);
> +}
> +
> +int sunxi_mdio_write(void *iobase, int phyaddr, int phyreg, unsigned short data)
> +{
> +	unsigned int value;
> +
> +	value = ((0x07 << 20) & readl(iobase + GETH_MDIO_ADDR)) |
> +		 (hwdev.mdc_div << 20);
> +	value |= (((phyaddr << 12) & (0x0001F000)) |
> +		  ((phyreg << 4) & (0x000007F0))) |
> +		  MII_WRITE | MII_BUSY;
> +
> +	/* Wait until any existing MII operation is complete */
> +	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> +		;
> +
> +	/* Set the MII address register to write */
> +	writel(data, iobase + GETH_MDIO_DATA);
> +	writel(value, iobase + GETH_MDIO_ADDR);
> +
> +	/* Wait until any existing MII operation is complete */
> +	while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> +		;
> +
> +	return 0;
> +}
> +
> +int sunxi_mdio_reset(void *iobase)
> +{
> +	writel((4 << 2), iobase + GETH_MDIO_ADDR);
> +	return 0;
> +}
> +
> +void sunxi_set_link_mode(void *iobase, int duplex, int speed)
> +{
> +	unsigned int ctrl = readl(iobase + GETH_BASIC_CTL0);
> +
> +	if (!duplex)
> +		ctrl &= ~CTL0_DM;
> +	else
> +		ctrl |= CTL0_DM;
> +
> +	switch (speed) {
> +	case 1000:
> +		ctrl &= ~0x0C;
> +		break;
> +	case 100:
> +	case 10:
> +	default:
> +		ctrl |= 0x08;
> +		if (speed == 100)
> +			ctrl |= 0x04;
> +		else
> +			ctrl &= ~0x04;
> +		break;
> +	}
> +
> +	writel(ctrl, iobase + GETH_BASIC_CTL0);
> +}
> +
> +void sunxi_mac_loopback(void *iobase, int enable)
> +{
> +	int reg;
> +
> +	reg = readl(iobase + GETH_BASIC_CTL0);
> +	if (enable)
> +		reg |= 0x02;
> +	else
> +		reg &= ~0x02;
> +	writel(reg, iobase + GETH_BASIC_CTL0);
> +}
> +
> +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause)
> +{
> +	unsigned int flow = 0;
> +
> +	if (fc & FLOW_RX) {
> +		flow = readl(iobase + GETH_RX_CTL0);
> +		flow |= 0x10000;
> +		writel(flow, iobase + GETH_RX_CTL0);
> +	}
> +
> +	if (fc & FLOW_TX) {
> +		flow = readl(iobase + GETH_TX_FLOW_CTL);
> +		flow |= 0x00001;
> +		writel(flow, iobase + GETH_TX_FLOW_CTL);
> +	}
> +
> +	if (duplex) {
> +		flow = readl(iobase + GETH_TX_FLOW_CTL);
> +		flow |= (pause << 4);
> +		writel(flow, iobase + GETH_TX_FLOW_CTL);
> +	}
> +}
> +
> +int sunxi_int_status(void *iobase, struct geth_extra_stats *x)
> +{
> +	int ret = 0;
> +	/* read the status register (CSR5) */
> +	unsigned int intr_status;
> +
> +	intr_status = readl(iobase + GETH_RGMII_STA);
> +	if (intr_status & RGMII_IRQ)
> +		readl(iobase + GETH_RGMII_STA);
> +
> +	intr_status = readl(iobase + GETH_INT_STA);
> +
> +	/* ABNORMAL interrupts */
> +	if (intr_status & TX_UNF_INT) {
> +		ret = tx_hard_error_bump_tc;
> +		x->tx_undeflow_irq++;
> +	}
> +	if (intr_status & TX_TOUT_INT) {
> +		x->tx_jabber_irq++;
> +	}
> +	if (intr_status & RX_OVF_INT) {
> +		x->rx_overflow_irq++;
> +	}
> +	if (intr_status & RX_UA_INT) {
> +		x->rx_buf_unav_irq++;
> +	}
> +	if (intr_status & RX_STOP_INT) {
> +		x->rx_process_stopped_irq++;
> +	}
> +	if (intr_status & RX_TOUT_INT) {
> +		x->rx_watchdog_irq++;
> +	}
> +	if (intr_status & TX_EARLY_INT) {
> +		x->tx_early_irq++;
> +	}
> +	if (intr_status & TX_STOP_INT) {
> +		x->tx_process_stopped_irq++;
> +		ret = tx_hard_error;
> +	}
> +
> +	/* TX/RX NORMAL interrupts */
> +	if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) {
> +		x->normal_irq_n++;
> +		if (intr_status & (TX_INT | RX_INT))
> +			ret = handle_tx_rx;
> +	}
> +	/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
> +	writel(intr_status & 0x3FFF, iobase + GETH_INT_STA);
> +
> +	return ret;
> +}
> +
> +void sunxi_start_rx(void *iobase, unsigned long rxbase)
> +{
> +	unsigned int value;
> +
> +	/* Write the base address of Rx descriptor lists into registers */
> +	writel(rxbase, iobase + GETH_RX_DESC_LIST);
> +
> +	value = readl(iobase + GETH_RX_CTL1);
> +	value |= 0x40000000;
> +	writel(value, iobase + GETH_RX_CTL1);
> +}
> +
> +void sunxi_stop_rx(void *iobase)
> +{
> +	unsigned int value;
> +
> +	value = readl(iobase + GETH_RX_CTL1);
> +	value &= ~0x40000000;
> +	writel(value, iobase + GETH_RX_CTL1);
> +}
> +
> +void sunxi_start_tx(void *iobase, unsigned long txbase)
> +{
> +	unsigned int value;
> +
> +	/* Write the base address of Tx descriptor lists into registers */
> +	writel(txbase, iobase + GETH_TX_DESC_LIST);
> +
> +	value = readl(iobase + GETH_TX_CTL1);
> +	value |= 0x40000000;
> +	writel(value, iobase + GETH_TX_CTL1);
> +}
> +
> +void sunxi_stop_tx(void *iobase)
> +{
> +	unsigned int value = readl(iobase + GETH_TX_CTL1);
> +
> +	value &= ~0x40000000;
> +	writel(value, iobase + GETH_TX_CTL1);
> +}
> +
> +static int sunxi_dma_init(void *iobase)
> +{
> +	unsigned int value;
> +
> +	/* Burst should be 8 */
> +	value = (8 << 24);
> +
> +#ifdef CONFIG_GMAC_DA
> +	value |= RX_TX_PRI;	/* Rx has priority over tx */
> +#endif
> +	writel(value, iobase + GETH_BASIC_CTL1);
> +
> +	/* Mask interrupts by writing to CSR7 */
> +	writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> +
> +	return 0;
> +}
> +
> +int sunxi_mac_init(void *iobase, int txmode, int rxmode)
> +{
> +	unsigned int value;
> +
> +	sunxi_dma_init(iobase);
> +
> +	/* Initialize the core component */
> +	value = readl(iobase + GETH_TX_CTL0);
> +	value |= (1 << 30);	/* Jabber Disable */
> +	writel(value, iobase + GETH_TX_CTL0);
> +
> +	value = readl(iobase + GETH_RX_CTL0);
> +	value |= (1 << 27);	/* Enable CRC & IPv4 Header Checksum */
> +	value |= (1 << 28);	/* Automatic Pad/CRC Stripping */
> +	value |= (1 << 29);	/* Jumbo Frame Enable */
> +	writel(value, iobase + GETH_RX_CTL0);
> +
> +	writel((hwdev.mdc_div << 20), iobase + GETH_MDIO_ADDR); /* MDC_DIV_RATIO */
> +
> +	/* Set the Rx&Tx mode */
> +	value = readl(iobase + GETH_TX_CTL1);
> +	if (txmode == SF_DMA_MODE) {
> +		/* Transmit COE type 2 cannot be done in cut-through mode. */
> +		value |= TX_MD;
> +		/* Operating on second frame increase the performance
> +		 * especially when transmit store-and-forward is used.
> +		 */
> +		value |= TX_NEXT_FRM;
> +	} else {
> +		value &= ~TX_MD;
> +		value &= ~TX_TH;
> +		/* Set the transmit threshold */
> +		if (txmode <= 64)
> +			value |= 0x00000000;
> +		else if (txmode <= 128)
> +			value |= 0x00000100;
> +		else if (txmode <= 192)
> +			value |= 0x00000200;
> +		else
> +			value |= 0x00000300;
> +	}
> +	writel(value, iobase + GETH_TX_CTL1);
> +
> +	value = readl(iobase + GETH_RX_CTL1);
> +	if (rxmode == SF_DMA_MODE) {
> +		value |= RX_MD;
> +	} else {
> +		value &= ~RX_MD;
> +		value &= ~RX_TH;
> +		if (rxmode <= 32)
> +			value |= 0x10;
> +		else if (rxmode <= 64)
> +			value |= 0x00;
> +		else if (rxmode <= 96)
> +			value |= 0x20;
> +		else
> +			value |= 0x30;
> +	}
> +
> +	/* Forward frames with error and undersized good frame. */
> +	value |= (RX_ERR_FRM | RX_RUNT_FRM);
> +
> +	writel(value, iobase + GETH_RX_CTL1);
> +
> +	return 0;
> +}
> +
> +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high)
> +{
> +	writel(high, iobase + GETH_RX_HASH0);
> +	writel(low, iobase + GETH_RX_HASH1);
> +}
> +
> +void sunxi_set_filter(void *iobase, unsigned long flags)
> +{
> +	int tmp_flags = 0;
> +
> +	tmp_flags |= ((flags >> 31) |
> +			((flags >> 9) & 0x00000002) |
> +			((flags << 1) & 0x00000010) |
> +			((flags >> 3) & 0x00000060) |
> +			((flags << 7) & 0x00000300) |
> +			((flags << 6) & 0x00003000) |
> +			((flags << 12) & 0x00030000) |
> +			(flags << 31));
> +
> +	writel(tmp_flags, iobase + GETH_RX_FRM_FLT);
> +}
> +
> +void sunxi_set_umac(void *iobase, unsigned char *addr, int index)
> +{
> +	unsigned long data;
> +
> +	data = (addr[5] << 8) | addr[4];
> +	writel(data, iobase + GETH_ADDR_HI(index));
> +	data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
> +	writel(data, iobase + GETH_ADDR_LO(index));
> +}
> +
> +void sunxi_mac_enable(void *iobase)
> +{
> +	unsigned long value;
> +
> +	value = readl(iobase + GETH_TX_CTL0);
> +	value |= (1 << 31);
> +	writel(value, iobase + GETH_TX_CTL0);
> +
> +	value = readl(iobase + GETH_RX_CTL0);
> +	value |= (1 << 31);
> +	writel(value, iobase + GETH_RX_CTL0);
> +}
> +
> +void sunxi_mac_disable(void *iobase)
> +{
> +	unsigned long value;
> +
> +	value = readl(iobase + GETH_TX_CTL0);
> +	value &= ~(1 << 31);
> +	writel(value, iobase + GETH_TX_CTL0);
> +
> +	value = readl(iobase + GETH_RX_CTL0);
> +	value &= ~(1 << 31);
> +	writel(value, iobase + GETH_RX_CTL0);
> +}
> +
> +void sunxi_tx_poll(void *iobase)
> +{
> +	unsigned int value;
> +
> +	value = readl(iobase + GETH_TX_CTL1);
> +	writel(value | 0x80000000, iobase + GETH_TX_CTL1);
> +}
> +
> +void sunxi_rx_poll(void *iobase)
> +{
> +	unsigned int value;
> +
> +	value = readl(iobase + GETH_RX_CTL1);
> +	writel(value | 0x80000000, iobase + GETH_RX_CTL1);
> +}
> +
> +void sunxi_int_enable(void *iobase)
> +{
> +	writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> +}
> +
> +void sunxi_int_disable(void *iobase)
> +{
> +	writel(0, iobase + GETH_INT_EN);
> +}
> +
> +void desc_buf_set(struct dma_desc *desc, unsigned long paddr, int size)
> +{
> +	desc->desc1.all &= (~((1 << 11) - 1));
> +	desc->desc1.all |= (size & ((1 << 11) - 1));
> +	desc->desc2 = paddr;
> +}
> +
> +void desc_set_own(struct dma_desc *desc)
> +{
> +	desc->desc0.all |= 0x80000000;
> +}
> +
> +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert)
> +{
> +	struct dma_desc *desc = first;
> +
> +	first->desc1.tx.first_sg = 1;
> +	end->desc1.tx.last_seg = 1;
> +	end->desc1.tx.interrupt = 1;
> +
> +	if (csum_insert)
> +		do {
> +			desc->desc1.tx.cic = 3;
> +			desc++;
> +		} while (desc <= end);
> +}
> +
> +void desc_init(struct dma_desc *desc)
> +{
> +	desc->desc1.all = 0;
> +	desc->desc2  = 0;
> +
> +	desc->desc1.all |= (1 << 24);
> +}
> +
> +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> +{
> +	int ret = 0;
> +
> +	if (desc->desc0.tx.under_err) {
> +		x->tx_underflow++;
> +		ret = -1;
> +	}
> +	if (desc->desc0.tx.no_carr) {
> +		x->tx_carrier++;
> +		ret = -1;
> +	}
> +	if (desc->desc0.tx.loss_carr) {
> +		x->tx_losscarrier++;
> +		ret = -1;
> +	}
> +
> +#if 0
> +	if ((desc->desc0.tx.ex_deferral) ||
> +			(desc->desc0.tx.ex_coll) ||
> +			(desc->desc0.tx.late_coll))
> +		stats->collisions += desc->desc0.tx.coll_cnt;
> +#endif
> +
> +	if (desc->desc0.tx.deferred)
> +		x->tx_deferred++;
> +
> +	return ret;
> +}
> +
> +int desc_buf_get_len(struct dma_desc *desc)
> +{
> +	return (desc->desc1.all & ((1 << 11) - 1));
> +}
> +
> +int desc_buf_get_addr(struct dma_desc *desc)
> +{
> +	return desc->desc2;
> +}
> +
> +int desc_rx_frame_len(struct dma_desc *desc)
> +{
> +	return desc->desc0.rx.frm_len;
> +}
> +
> +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> +{
> +	int ret = good_frame;
> +
> +	if (desc->desc0.rx.last_desc == 0) {
> +		return discard_frame;
> +	}
> +
> +	if (desc->desc0.rx.err_sum) {
> +		if (desc->desc0.rx.desc_err)
> +			x->rx_desc++;
> +
> +		if (desc->desc0.rx.sou_filter)
> +			x->sa_filter_fail++;
> +
> +		if (desc->desc0.rx.over_err)
> +			x->overflow_error++;
> +
> +		if (desc->desc0.rx.ipch_err)
> +			x->ipc_csum_error++;
> +
> +		if (desc->desc0.rx.late_coll)
> +			x->rx_collision++;
> +
> +		if (desc->desc0.rx.crc_err)
> +			x->rx_crc++;
> +
> +		ret = discard_frame;
> +	}
> +
> +	if (desc->desc0.rx.len_err) {
> +		ret = discard_frame;
> +	}
> +	if (desc->desc0.rx.mii_err) {
> +		ret = discard_frame;
> +	}
> +
> +	return ret;
> +}
> +
> +int desc_get_own(struct dma_desc *desc)
> +{
> +	return desc->desc0.all & 0x80000000;
> +}
> +
> +int desc_get_tx_ls(struct dma_desc *desc)
> +{
> +	return desc->desc1.tx.last_seg;
> +}
> +
> +int sunxi_geth_register(void *iobase, int version, unsigned int div)
> +{
> +	hwdev.ver = version;
> +	hwdev.iobase = iobase;
> +	hwdev.mdc_div = div;
> +
> +	return 0;
> +}
> +
> +int sunxi_mac_reset(void *iobase, void (*delay)(int), int n)
> +{
> +	unsigned int value;
> +
> +	/* DMA SW reset */
> +	value = readl(iobase + GETH_BASIC_CTL1);
> +	value |= SOFT_RST;
> +	writel(value, iobase + GETH_BASIC_CTL1);
> +
> +	delay(n);
> +
> +	return !!(readl(iobase + GETH_BASIC_CTL1) & SOFT_RST);
> +}
> diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> new file mode 100644
> index 00000000..0c67877
> --- /dev/null
> +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> @@ -0,0 +1,2240 @@
> +/*
> + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
> + *
> + * Copyright © 2016-2018, fuzhaoke
> + *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> + *
> + * This file is provided under a dual BSD/GPL license.  When using or
> + * redistributing this file, you may do so under either license.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
> + * GNU General Public License for more details.
> + */
> +//#include <linux/clk.h>
> +//#include <linux/clk-provider.h>
> +#include <linux/mii.h>
> +#include <linux/gpio.h>
> +#include <linux/crc32.h>
> +#include <linux/skbuff.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +//#include <linux/pinctrl/consumer.h>
> +//#include <linux/pinctrl/pinctrl.h>
> +#include <linux/crypto.h>
> +#include <crypto/algapi.h>
> +#include <crypto/hash.h>
> +#include <linux/err.h>
> +#include <linux/scatterlist.h>
> +//#include <linux/regulator/consumer.h>
> +#include <linux/of_net.h>
> +//#include <linux/of_gpio.h>
> +#include <linux/io.h>
> +//#include <linux/sunxi-sid.h>
> +//#include <linux/sunxi-gpio.h>
> +//#include <linux/reset.h>
> +#include "sunxi-gmac.h"
> +
> +#define SUNXI_GMAC_VERSION "1.0.0"
> +
> +#define DMA_DESC_RX	256
> +#define DMA_DESC_TX	256
> +#define BUDGET		(dma_desc_rx / 4)
> +#define TX_THRESH	(dma_desc_tx / 4)
> +
> +#define HASH_TABLE_SIZE	64
> +#define MAX_BUF_SZ	(SZ_2K - 1)
> +
> +#define POWER_CHAN_NUM	3
> +
> +#undef PKT_DEBUG
> +#undef DESC_PRINT
> +
> +#define circ_cnt(head, tail, size) (((head) > (tail)) ? \
> +					((head) - (tail)) : \
> +					((head) - (tail)) & ((size) - 1))
> +
> +#define circ_space(head, tail, size) circ_cnt((tail), ((head) + 1), (size))
> +
> +#define circ_inc(n, s) (((n) + 1) % (s))
> +
> +#define GETH_MAC_ADDRESS "00:00:00:00:00:00"
> +static char *mac_str = GETH_MAC_ADDRESS;
> +module_param(mac_str, charp, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(mac_str, "MAC Address String.(xx:xx:xx:xx:xx:xx)");
> +
> +static int rxmode = 1;
> +module_param(rxmode, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(rxmode, "DMA threshold control value");
> +
> +static int txmode = 1;
> +module_param(txmode, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(txmode, "DMA threshold control value");
> +
> +static int pause = 0x400;
> +module_param(pause, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(pause, "Flow Control Pause Time");
> +
> +#define TX_TIMEO	5000
> +static int watchdog = TX_TIMEO;
> +module_param(watchdog, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
> +
> +static int dma_desc_rx = DMA_DESC_RX;
> +module_param(dma_desc_rx, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(watchdog, "The number of receive's descriptors");
> +
> +static int dma_desc_tx = DMA_DESC_TX;
> +module_param(dma_desc_tx, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(watchdog, "The number of transmit's descriptors");
> +
> +/* - 0: Flow Off
> + * - 1: Rx Flow
> + * - 2: Tx Flow
> + * - 3: Rx & Tx Flow
> + */
> +static int flow_ctrl;
> +module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(flow_ctrl, "Flow control [0: off, 1: rx, 2: tx, 3: both]");
> +
> +struct geth_priv {
> +	struct dma_desc *dma_tx;
> +	struct sk_buff **tx_sk;
> +	unsigned int tx_clean;
> +	unsigned int tx_dirty;
> +	dma_addr_t dma_tx_phy;
> +
> +	unsigned long buf_sz;
> +
> +	struct dma_desc *dma_rx;
> +	struct sk_buff **rx_sk;
> +	unsigned int rx_clean;
> +	unsigned int rx_dirty;
> +	dma_addr_t dma_rx_phy;
> +
> +	struct net_device *ndev;
> +	struct device *dev;
> +	struct napi_struct napi;
> +
> +	struct geth_extra_stats xstats;
> +
> +	struct mii_bus *mii;
> +	int link;
> +	int speed;
> +	int duplex;
> +#define INT_PHY 0
> +#define EXT_PHY 1
> +	int phy_ext;
> +	phy_interface_t phy_interface;
> +
> +	void __iomem *base;
> +	void __iomem *base_phy;
> +/*
> +	struct clk *geth_clk;
> +	struct clk *ephy_clk;
> +	struct reset_control *reset;
> +	struct pinctrl *pinctrl;
> +*/
> +	struct regulator *gmac_power[POWER_CHAN_NUM];
> +	bool is_suspend;
> +	int phyrst;
> +	u8  rst_active_low;
> +	/* definition spinlock */
> +	spinlock_t lock;
> +	spinlock_t tx_lock;
> +
> +	/* whether using ephy_clk */
> +	int use_ephy_clk;
> +	int phy_addr;
> +
> +	/* adjust transmit clock delay, value: 0~7 */
> +	/* adjust receive clock delay, value: 0~31 */
> +	unsigned int tx_delay;
> +	unsigned int rx_delay;
> +
> +	/* resume work */
> +	struct work_struct eth_work;
> +};
> +
> +static u64 geth_dma_mask = DMA_BIT_MASK(32);
> +
> +void sunxi_udelay(int n)
> +{
> +	udelay(n);
> +}
> +
> +static int geth_stop(struct net_device *ndev);
> +static int geth_open(struct net_device *ndev);
> +static void geth_tx_complete(struct geth_priv *priv);
> +static void geth_rx_refill(struct net_device *ndev);
> +
> +#ifdef CONFIG_GETH_ATTRS
> +static ssize_t adjust_bgs_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	int value = 0;
> +	u32 efuse_value;
> +	struct net_device *ndev = to_net_dev(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	if (priv->phy_ext == INT_PHY) {
> +		value = readl(priv->base_phy) >> 28;
> +		if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> +			pr_err("get PHY efuse fail!\n");
> +		else
> +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> +			value = value - ((efuse_value >> 24) & 0x0F);
> +#else
> +			pr_warn("miss config come from efuse!\n");
> +#endif
> +	}
> +
> +	return sprintf(buf, "bgs: %d\n", value);
> +}
> +
> +static ssize_t adjust_bgs_write(struct device *dev, struct device_attribute *attr,
> +				const char *buf, size_t count)
> +{
> +	unsigned int out = 0;
> +	struct net_device *ndev = to_net_dev(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	u32 clk_value = readl(priv->base_phy);
> +	u32 efuse_value;
> +
> +	out = simple_strtoul(buf, NULL, 10);
> +
> +	if (priv->phy_ext == INT_PHY) {
> +		clk_value &= ~(0xF << 28);
> +		if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> +			pr_err("get PHY efuse fail!\n");
> +		else
> +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> +			clk_value |= (((efuse_value >> 24) & 0x0F) + out) << 28;
> +#else
> +			pr_warn("miss config come from efuse!\n");
> +#endif
> +	}
> +
> +	writel(clk_value, priv->base_phy);
> +
> +	return count;
> +}
> +
> +static struct device_attribute adjust_reg[] = {
> +	__ATTR(adjust_bgs, 0664, adjust_bgs_show, adjust_bgs_write),
> +};
> +
> +static int geth_create_attrs(struct net_device *ndev)
> +{
> +	int j, ret;
> +
> +	for (j = 0; j < ARRAY_SIZE(adjust_reg); j++) {
> +		ret = device_create_file(&ndev->dev, &adjust_reg[j]);
> +		if (ret)
> +			goto sysfs_failed;
> +	}
> +	goto succeed;
> +
> +sysfs_failed:
> +	while (j--)
> +		device_remove_file(&ndev->dev, &adjust_reg[j]);
> +succeed:
> +	return ret;
> +}
> +#endif
> +
> +#ifdef DEBUG
> +static void desc_print(struct dma_desc *desc, int size)
> +{
> +#ifdef DESC_PRINT
> +	int i;
> +
> +	for (i = 0; i < size; i++) {
> +		u32 *x = (u32 *)(desc + i);
> +
> +		pr_info("\t%d [0x%08lx]: %08x %08x %08x %08x\n",
> +			i, (unsigned long)(&desc[i]),
> +			x[0], x[1], x[2], x[3]);
> +	}
> +	pr_info("\n");
> +#endif
> +}
> +#endif
> +
> +static ssize_t extra_tx_stats_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	if (!dev) {
> +		pr_err("Argment is invalid\n");
> +		return 0;
> +	}
> +
> +	if (!ndev) {
> +		pr_err("Net device is null\n");
> +		return 0;
> +	}
> +
> +	return sprintf(buf, "tx_underflow: %lu\ntx_carrier: %lu\n"
> +			"tx_losscarrier: %lu\nvlan_tag: %lu\n"
> +			"tx_deferred: %lu\ntx_vlan: %lu\n"
> +			"tx_jabber: %lu\ntx_frame_flushed: %lu\n"
> +			"tx_payload_error: %lu\ntx_ip_header_error: %lu\n\n",
> +			priv->xstats.tx_underflow, priv->xstats.tx_carrier,
> +			priv->xstats.tx_losscarrier, priv->xstats.vlan_tag,
> +			priv->xstats.tx_deferred, priv->xstats.tx_vlan,
> +			priv->xstats.tx_jabber, priv->xstats.tx_frame_flushed,
> +			priv->xstats.tx_payload_error, priv->xstats.tx_ip_header_error);
> +}
> +static DEVICE_ATTR(extra_tx_stats, 0444, extra_tx_stats_show, NULL);
> +
> +static ssize_t extra_rx_stats_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	if (!dev) {
> +		pr_err("Argment is invalid\n");
> +		return 0;
> +	}
> +
> +	if (!ndev) {
> +		pr_err("Net device is null\n");
> +		return 0;
> +	}
> +
> +	return sprintf(buf, "rx_desc: %lu\nsa_filter_fail: %lu\n"
> +			"overflow_error: %lu\nipc_csum_error: %lu\n"
> +			"rx_collision: %lu\nrx_crc: %lu\n"
> +			"dribbling_bit: %lu\nrx_length: %lu\n"
> +			"rx_mii: %lu\nrx_multicast: %lu\n"
> +			"rx_gmac_overflow: %lu\nrx_watchdog: %lu\n"
> +			"da_rx_filter_fail: %lu\nsa_rx_filter_fail: %lu\n"
> +			"rx_missed_cntr: %lu\nrx_overflow_cntr: %lu\n"
> +			"rx_vlan: %lu\n\n",
> +			priv->xstats.rx_desc, priv->xstats.sa_filter_fail,
> +			priv->xstats.overflow_error, priv->xstats.ipc_csum_error,
> +			priv->xstats.rx_collision, priv->xstats.rx_crc,
> +			priv->xstats.dribbling_bit, priv->xstats.rx_length,
> +			priv->xstats.rx_mii, priv->xstats.rx_multicast,
> +			priv->xstats.rx_gmac_overflow, priv->xstats.rx_length,
> +			priv->xstats.da_rx_filter_fail, priv->xstats.sa_rx_filter_fail,
> +			priv->xstats.rx_missed_cntr, priv->xstats.rx_overflow_cntr,
> +			priv->xstats.rx_vlan);
> +}
> +static DEVICE_ATTR(extra_rx_stats, 0444, extra_rx_stats_show, NULL);
> +
> +static ssize_t gphy_test_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +
> +	if (!dev) {
> +		pr_err("Argment is invalid\n");
> +		return 0;
> +	}
> +
> +	if (!ndev) {
> +		pr_err("Net device is null\n");
> +		return 0;
> +	}
> +
> +	return sprintf(buf, "Usage:\necho [0/1/2/3/4] > gphy_test\n"
> +			"0 - Normal Mode\n"
> +			"1 - Transmit Jitter Test\n"
> +			"2 - Transmit Jitter Test(MASTER mode)\n"
> +			"3 - Transmit Jitter Test(SLAVE mode)\n"
> +			"4 - Transmit Distortion Test\n\n");
> +}
> +
> +static ssize_t gphy_test_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	u16 value = 0;
> +	int ret = 0;
> +	u16 data = 0;
> +
> +	if (!dev) {
> +		pr_err("Argument is invalid\n");
> +		return count;
> +	}
> +
> +	if (!ndev) {
> +		pr_err("Net device is null\n");
> +		return count;
> +	}
> +
> +	data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000);
> +
> +	ret = kstrtou16(buf, 0, &value);
> +	if (ret)
> +		return ret;
> +
> +	if (value >= 0 && value <= 4) {
> +		data &= ~(0x7 << 13);
> +		data |= value << 13;
> +		sunxi_mdio_write(priv->base, priv->phy_addr, MII_CTRL1000, data);
> +		pr_info("Set MII_CTRL1000(0x09) Reg: 0x%x\n", data);
> +	} else {
> +		pr_info("unknown value (%d)\n", value);
> +	}
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(gphy_test, 0664, gphy_test_show, gphy_test_store);
> +
> +static ssize_t mii_reg_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct net_device *ndev = NULL;
> +	struct geth_priv *priv = NULL;
> +
> +	if (dev == NULL) {
> +		pr_err("Argment is invalid\n");
> +		return 0;
> +	}
> +
> +	ndev = dev_get_drvdata(dev);
> +	if (ndev == NULL) {
> +		pr_err("Net device is null\n");
> +		return 0;
> +	}
> +
> +	priv = netdev_priv(ndev);
> +	if (priv == NULL) {
> +		pr_err("geth_priv is null\n");
> +		return 0;
> +	}
> +
> +	if (!netif_running(ndev)) {
> +		pr_warn("eth is down!\n");
> +		return 0;
> +	}
> +
> +	return sprintf(buf,
> +		"Current MII Registers:\n"
> +		"BMCR[0x%02x] = 0x%04x,\t\tBMSR[0x%02x] = 0x%04x,\t\tPHYSID1[0x%02x] = 0x%04x\n"
> +		"PHYSID2[0x%02x] = 0x%04x,\t\tADVERTISE[0x%02x] = 0x%04x,\tLPA[0x%02x] = 0x%04x\n"
> +		"EXPANSION[0x%02x] = 0x%04x,\tCTRL1000[0x%02x] = 0x%04x,\tSTAT1000[0x%02x] = 0x%04x\n",
> +		MII_BMCR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR),
> +		MII_BMSR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMSR),
> +		MII_PHYSID1, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID1),
> +		MII_PHYSID2, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID2),
> +		MII_ADVERTISE, sunxi_mdio_read(priv->base, priv->phy_addr, MII_ADVERTISE),
> +		MII_LPA, sunxi_mdio_read(priv->base, priv->phy_addr, MII_LPA),
> +		MII_EXPANSION, sunxi_mdio_read(priv->base, priv->phy_addr, MII_EXPANSION),
> +		MII_CTRL1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000),
> +		MII_STAT1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_STAT1000));
> +}
> +static DEVICE_ATTR(mii_reg, 0444, mii_reg_show, NULL);
> +
> +static ssize_t loopback_test_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "Usage:\necho [0/1/2] > loopback_test\n"
> +			"0 - Normal Mode\n"
> +			"1 - Mac loopback test mode\n"
> +			"2 - Phy loopback test mode\n");
> +}
> +
> +static ssize_t loopback_test_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct net_device *ndev = NULL;
> +	struct geth_priv *priv = NULL;
> +	u16 value = 0;
> +	int ret = 0;
> +	u16 data = 0;
> +
> +	if (dev == NULL) {
> +		pr_err("Argment is invalid\n");
> +		return count;
> +	}
> +
> +	ndev = dev_get_drvdata(dev);
> +	if (ndev == NULL) {
> +		pr_err("Net device is null\n");
> +		return count;
> +	}
> +
> +	priv = netdev_priv(ndev);
> +	if (priv == NULL) {
> +		pr_err("geth_priv is null\n");
> +		return count;
> +	}
> +
> +	if (!netif_running(ndev)) {
> +		pr_warn("eth is down!\n");
> +		return count;
> +	}
> +
> +	ret = kstrtou16(buf, 0, &value);
> +	if (ret)
> +		return ret;
> +
> +	if (value == 0) { /* normal mode */
> +		/* clear mac loopback */
> +		sunxi_mac_loopback(priv->base, 0);
> +
> +		/* clear phy loopback */
> +		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> +		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> +	} else if (value == 1) { /* mac loopback test mode */
> +		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> +		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> +
> +		sunxi_mac_loopback(priv->base, 1);
> +	} else if (value == 2) { /* phy loopback test mode */
> +		sunxi_mac_loopback(priv->base, 0);
> +
> +		data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> +		sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data | BMCR_LOOPBACK);
> +	} else {
> +		pr_err("Undefined value (%d)\n", value);
> +	}
> +
> +	return count;
> +}
> +static DEVICE_ATTR(loopback_test, 0664, loopback_test_show, loopback_test_store);
> +
> +static int geth_power_on(struct geth_priv *priv)
> +{
> +	int value;
> +
> +	value = readl(priv->base_phy);
> +	if (priv->phy_ext == INT_PHY) {
> +		value |= (1 << 15);
> +		value &= ~(1 << 16);
> +		value |= (3 << 17);
> +	} else {
> +		value &= ~(1 << 15);
> +/*
> +		for (i = 0; i < POWER_CHAN_NUM; i++) {
> +			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> +				continue;
> +			if (regulator_enable(priv->gmac_power[i]) != 0) {
> +				pr_err("gmac-power%d enable error\n", i);
> +				return -EINVAL;
> +			}
> +		}
> +*/
> +	}
> +
> +	writel(value, priv->base_phy);
> +
> +	return 0;
> +}
> +
> +static void geth_power_off(struct geth_priv *priv)
> +{
> +	int value;
> +
> +	if (priv->phy_ext == INT_PHY) {
> +		value = readl(priv->base_phy);
> +		value |= (1 << 16);
> +		writel(value, priv->base_phy);
> +	} else {
> +/*
> +		for (i = 0; i < POWER_CHAN_NUM; i++) {
> +			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> +				continue;
> +			regulator_disable(priv->gmac_power[i]);
> +		}
> +*/
> +	}
> +}
> +
> +/* PHY interface operations */
> +static int geth_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
> +{
> +	struct net_device *ndev = bus->priv;
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	return (int)sunxi_mdio_read(priv->base,  phyaddr, phyreg);
> +}
> +
> +static int geth_mdio_write(struct mii_bus *bus, int phyaddr,
> +			   int phyreg, u16 data)
> +{
> +	struct net_device *ndev = bus->priv;
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	sunxi_mdio_write(priv->base, phyaddr, phyreg, data);
> +
> +	return 0;
> +}
> +
> +static int geth_mdio_reset(struct mii_bus *bus)
> +{
> +	struct net_device *ndev = bus->priv;
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	return sunxi_mdio_reset(priv->base);
> +}
> +
> +static void geth_adjust_link(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct phy_device *phydev = ndev->phydev;
> +	unsigned long flags;
> +	int new_state = 0;
> +
> +	if (!phydev)
> +		return;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	if (phydev->link) {
> +		/* Now we make sure that we can be in full duplex mode.
> +		 * If not, we operate in half-duplex mode.
> +		 */
> +		if (phydev->duplex != priv->duplex) {
> +			new_state = 1;
> +			priv->duplex = phydev->duplex;
> +		}
> +		/* Flow Control operation */
> +		if (phydev->pause)
> +			sunxi_flow_ctrl(priv->base, phydev->duplex,
> +					flow_ctrl, pause);
> +
> +		if (phydev->speed != priv->speed) {
> +			new_state = 1;
> +			priv->speed = phydev->speed;
> +		}
> +
> +		if (priv->link == 0) {
> +			new_state = 1;
> +			priv->link = phydev->link;
> +		}
> +
> +		if (new_state)
> +			sunxi_set_link_mode(priv->base, priv->duplex, priv->speed);
> +
> +#ifdef LOOPBACK_DEBUG
> +		phydev->state = PHY_FORCING;
> +#endif
> +
> +	} else if (priv->link != phydev->link) {
> +		new_state = 1;
> +		priv->link = 0;
> +		priv->speed = 0;
> +		priv->duplex = -1;
> +	}
> +
> +	if (new_state)
> +		phy_print_status(phydev);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +}
> +
> +static int geth_phy_init(struct net_device *ndev)
> +{
> +	int value;
> +	struct mii_bus *new_bus;
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct phy_device *phydev = ndev->phydev;
> +
> +	/* Fixup the phy interface type */
> +	if (priv->phy_ext == INT_PHY) {
> +		priv->phy_interface = PHY_INTERFACE_MODE_MII;
> +	} else {
> +		/* If config gpio to reset the phy device, we should reset it */
> +		/*
> +		if (gpio_is_valid(priv->phyrst)) {
> +			gpio_direction_output(priv->phyrst,
> +					priv->rst_active_low);
> +			msleep(50);
> +			gpio_direction_output(priv->phyrst,
> +					!priv->rst_active_low);
> +			msleep(50);
> +		}
> +		*/
> +	}
> +
> +	if (priv->is_suspend && phydev)
> +		goto resume;
> +
> +	new_bus = mdiobus_alloc();
> +	if (!new_bus) {
> +		netdev_err(ndev, "Failed to alloc new mdio bus\n");
> +		return -ENOMEM;
> +	}
> +
> +	new_bus->name = dev_name(priv->dev);
> +	new_bus->read = &geth_mdio_read;
> +	new_bus->write = &geth_mdio_write;
> +	new_bus->reset = &geth_mdio_reset;
> +	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0);
> +
> +	new_bus->parent = priv->dev;
> +	new_bus->priv = ndev;
> +
> +	if (mdiobus_register(new_bus)) {
> +		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
> +		goto reg_fail;
> +	}
> +
> +	priv->mii = new_bus;
> +
> +	{
> +		int addr;
> +
> +		for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
> +			struct phy_device *phydev_tmp = mdiobus_get_phy(new_bus, addr);
> +
> +			if (phydev_tmp && (phydev_tmp->phy_id != 0x00)) {
> +				phydev = phydev_tmp;
> +				priv->phy_addr = addr;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (!phydev) {
> +		netdev_err(ndev, "No PHY found!\n");
> +		goto err;
> +	}
> +
> +	phydev->irq = PHY_POLL;
> +
> +	value = phy_connect_direct(ndev, phydev, &geth_adjust_link, priv->phy_interface);
> +	if (value) {
> +		netdev_err(ndev, "Could not attach to PHY\n");
> +		goto err;
> +	} else {
> +		netdev_info(ndev, "%s: Type(%d) PHY ID %08x at %d IRQ %s (%s)\n",
> +			    ndev->name, phydev->interface, phydev->phy_id,
> +			    phydev->mdio.addr, "poll", dev_name(&phydev->mdio.dev));
> +	}
> +
> +	//phydev->supported &= PHY_GBIT_FEATURES;
> +	phydev->is_gigabit_capable = 1;
> +	//phydev->advertising = phydev->supported;
> +
> +resume:
> +	phy_write(phydev, MII_BMCR, BMCR_RESET);
> +	while (BMCR_RESET & phy_read(phydev, MII_BMCR))
> +		msleep(30);
> +
> +	value = phy_read(phydev, MII_BMCR);
> +	phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
> +
> +	if (priv->phy_ext == INT_PHY) {
> +		/* EPHY Initial */
> +		phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
> +		phy_write(phydev, 0x12, 0x4824); /* Disable APS */
> +		phy_write(phydev, 0x1f, 0x0200); /* switchto page 2 */
> +		phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
> +		phy_write(phydev, 0x1f, 0x0600); /* switchto page 6 */
> +		phy_write(phydev, 0x14, 0x708F); /* PHYAFE TX optimization */
> +		phy_write(phydev, 0x19, 0x0000);
> +		phy_write(phydev, 0x13, 0xf000); /* PHYAFE RX optimization */
> +		phy_write(phydev, 0x15, 0x1530);
> +		phy_write(phydev, 0x1f, 0x0800); /* switch to page 8 */
> +		phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
> +		phy_write(phydev, 0x1f, 0x0100); /* switchto page 1 */
> +		/* reg 0x17 bit3,set 0 to disable iEEE */
> +		phy_write(phydev, 0x17, phy_read(phydev, 0x17) & (~(1<<3)));
> +		phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
> +	}
> +	if (priv->is_suspend)
> +		phy_init_hw(phydev);
> +
> +	return 0;
> +
> +err:
> +	mdiobus_unregister(new_bus);
> +reg_fail:
> +	mdiobus_free(new_bus);
> +
> +	return -EINVAL;
> +}
> +
> +static int geth_phy_release(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct phy_device *phydev = ndev->phydev;
> +	int value = 0;
> +
> +	/* Stop and disconnect the PHY */
> +	if (phydev)
> +		phy_stop(phydev);
> +
> +	priv->link = PHY_DOWN;
> +	priv->speed = 0;
> +	priv->duplex = -1;
> +
> +	if (phydev) {
> +		value = phy_read(phydev, MII_BMCR);
> +		phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN));
> +	}
> +
> +	if (priv->is_suspend)
> +		return 0;
> +
> +	if (phydev) {
> +		phy_disconnect(phydev);
> +		ndev->phydev = NULL;
> +	}
> +
> +	if (priv->mii) {
> +		mdiobus_unregister(priv->mii);
> +		priv->mii->priv = NULL;
> +		mdiobus_free(priv->mii);
> +		priv->mii = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void geth_rx_refill(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct dma_desc *desc;
> +	struct sk_buff *sk = NULL;
> +	dma_addr_t paddr;
> +
> +	while (circ_space(priv->rx_clean, priv->rx_dirty, dma_desc_rx) > 0) {
> +		int entry = priv->rx_clean;
> +
> +		/* Find the dirty's desc and clean it */
> +		desc = priv->dma_rx + entry;
> +
> +		if (priv->rx_sk[entry] == NULL) {
> +			sk = netdev_alloc_skb_ip_align(ndev, priv->buf_sz);
> +
> +			if (unlikely(sk == NULL))
> +				break;
> +
> +			priv->rx_sk[entry] = sk;
> +			paddr = dma_map_single(priv->dev, sk->data,
> +					       priv->buf_sz, DMA_FROM_DEVICE);
> +			desc_buf_set(desc, paddr, priv->buf_sz);
> +		}
> +
> +		/* sync memery */
> +		wmb();
> +		desc_set_own(desc);
> +		priv->rx_clean = circ_inc(priv->rx_clean, dma_desc_rx);
> +	}
> +}
> +
> +/* geth_dma_desc_init - initialize the RX/TX descriptor list
> + * @ndev: net device structure
> + * Description: initialize the list for dma.
> + */
> +static int geth_dma_desc_init(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	unsigned int buf_sz;
> +
> +	priv->rx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_rx,
> +				GFP_KERNEL);
> +	if (!priv->rx_sk)
> +		return -ENOMEM;
> +
> +	priv->tx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_tx,
> +				GFP_KERNEL);
> +	if (!priv->tx_sk)
> +		goto tx_sk_err;
> +
> +	/* Set the size of buffer depend on the MTU & max buf size */
> +	buf_sz = MAX_BUF_SZ;
> +
> +	priv->dma_tx = dma_alloc_coherent(priv->dev,
> +					dma_desc_tx *
> +					sizeof(struct dma_desc),
> +					&priv->dma_tx_phy,
> +					GFP_KERNEL);
> +	if (!priv->dma_tx)
> +		goto dma_tx_err;
> +
> +	priv->dma_rx = dma_alloc_coherent(priv->dev,
> +					dma_desc_rx *
> +					sizeof(struct dma_desc),
> +					&priv->dma_rx_phy,
> +					GFP_KERNEL);
> +	if (!priv->dma_rx)
> +		goto dma_rx_err;
> +
> +	priv->buf_sz = buf_sz;
> +
> +	return 0;
> +
> +dma_rx_err:
> +	dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> +			  priv->dma_tx, priv->dma_tx_phy);
> +dma_tx_err:
> +	kfree(priv->tx_sk);
> +tx_sk_err:
> +	kfree(priv->rx_sk);
> +
> +	return -ENOMEM;
> +}
> +
> +static void geth_free_rx_sk(struct geth_priv *priv)
> +{
> +	int i;
> +
> +	for (i = 0; i < dma_desc_rx; i++) {
> +		if (priv->rx_sk[i] != NULL) {
> +			struct dma_desc *desc = priv->dma_rx + i;
> +
> +			dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> +					 desc_buf_get_len(desc),
> +					 DMA_FROM_DEVICE);
> +			dev_kfree_skb_any(priv->rx_sk[i]);
> +			priv->rx_sk[i] = NULL;
> +		}
> +	}
> +}
> +
> +static void geth_free_tx_sk(struct geth_priv *priv)
> +{
> +	int i;
> +
> +	for (i = 0; i < dma_desc_tx; i++) {
> +		if (priv->tx_sk[i] != NULL) {
> +			struct dma_desc *desc = priv->dma_tx + i;
> +
> +			if (desc_buf_get_addr(desc))
> +				dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> +						 desc_buf_get_len(desc),
> +						 DMA_TO_DEVICE);
> +			dev_kfree_skb_any(priv->tx_sk[i]);
> +			priv->tx_sk[i] = NULL;
> +		}
> +	}
> +}
> +
> +static void geth_free_dma_desc(struct geth_priv *priv)
> +{
> +	/* Free the region of consistent memory previously allocated for the DMA */
> +	dma_free_coherent(priv->dev, dma_desc_tx * sizeof(struct dma_desc),
> +			  priv->dma_tx, priv->dma_tx_phy);
> +	dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> +			  priv->dma_rx, priv->dma_rx_phy);
> +
> +	kfree(priv->rx_sk);
> +	kfree(priv->tx_sk);
> +}
> +
> +#if IS_ENABLED(CONFIG_PM)
> +/*
> +static int geth_select_gpio_state(struct pinctrl *pctrl, char *name)
> +{
> +	int ret = 0;
> +	struct pinctrl_state *pctrl_state = NULL;
> +
> +	pctrl_state = pinctrl_lookup_state(pctrl, name);
> +	if (IS_ERR(pctrl_state)) {
> +		pr_err("gmac pinctrl_lookup_state(%s) failed! return %p\n",
> +						name, pctrl_state);
> +		return -EINVAL;
> +	}
> +
> +	ret = pinctrl_select_state(pctrl, pctrl_state);
> +	if (ret < 0)
> +		pr_err("gmac pinctrl_select_state(%s) failed! return %d\n",
> +						name, ret);
> +
> +	return ret;
> +}
> +*/
> +static int geth_suspend(struct device *dev)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	cancel_work_sync(&priv->eth_work);
> +
> +	if (!ndev || !netif_running(ndev))
> +		return 0;
> +
> +	priv->is_suspend = true;
> +
> +	spin_lock(&priv->lock);
> +	netif_device_detach(ndev);
> +	spin_unlock(&priv->lock);
> +
> +	geth_stop(ndev);
> +/*
> +	if (priv->phy_ext == EXT_PHY)
> +
> +	geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP);
> +*/
> +	return 0;
> +}
> +
> +static void geth_resume_work(struct work_struct *work)
> +{
> +	struct geth_priv *priv = container_of(work, struct geth_priv, eth_work);
> +	struct net_device *ndev = priv->ndev;
> +	int ret = 0;
> +
> +	if (!netif_running(ndev))
> +		return;
> +/*
> +	if (priv->phy_ext == EXT_PHY)
> +		geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_DEFAULT);
> +*/
> +	spin_lock(&priv->lock);
> +	netif_device_attach(ndev);
> +	spin_unlock(&priv->lock);
> +
> +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> +	if (!ephy_is_enable()) {
> +		pr_info("[geth_resume] ephy is not enable, waiting...\n");
> +		msleep(2000);
> +		if (!ephy_is_enable()) {
> +			netdev_err(ndev, "Wait for ephy resume timeout.\n");
> +			return;
> +		}
> +	}
> +#endif
> +
> +	ret = geth_open(ndev);
> +	if (!ret)
> +		priv->is_suspend = false;
> +}
> +
> +static void geth_resume(struct device *dev)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	schedule_work(&priv->eth_work);
> +}
> +
> +static int geth_freeze(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int geth_restore(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops geth_pm_ops = {
> +	.complete = geth_resume,
> +	.prepare = geth_suspend,
> +	.suspend = NULL,
> +	.resume = NULL,
> +	.freeze = geth_freeze,
> +	.restore = geth_restore,
> +};
> +#else
> +static const struct dev_pm_ops geth_pm_ops;
> +#endif /* CONFIG_PM */
> +
> +#define sunxi_get_soc_chipid(x) {}
> +static void geth_chip_hwaddr(u8 *addr)
> +{
> +#define MD5_SIZE	16
> +#define CHIP_SIZE	16
> +
> +	struct crypto_ahash *tfm;
> +	struct ahash_request *req;
> +	struct scatterlist sg;
> +	u8 result[MD5_SIZE];
> +	u8 chipid[CHIP_SIZE];
> +	int i = 0;
> +	int ret = -1;
> +
> +	memset(chipid, 0, sizeof(chipid));
> +	memset(result, 0, sizeof(result));
> +
> +	sunxi_get_soc_chipid((u8 *)chipid);
> +
> +	tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
> +	if (IS_ERR(tfm)) {
> +		pr_err("Failed to alloc md5\n");
> +		return;
> +	}
> +
> +	req = ahash_request_alloc(tfm, GFP_KERNEL);
> +	if (!req)
> +		goto out;
> +
> +	ahash_request_set_callback(req, 0, NULL, NULL);
> +
> +	ret = crypto_ahash_init(req);
> +	if (ret) {
> +		pr_err("crypto_ahash_init() failed\n");
> +		goto out;
> +	}
> +
> +	sg_init_one(&sg, chipid, sizeof(chipid));
> +	ahash_request_set_crypt(req, &sg, result, sizeof(chipid));
> +	ret = crypto_ahash_update(req);
> +	if (ret) {
> +		pr_err("crypto_ahash_update() failed for id\n");
> +		goto out;
> +	}
> +
> +	ret = crypto_ahash_final(req);
> +	if (ret) {
> +		pr_err("crypto_ahash_final() failed for result\n");
> +		goto out;
> +	}
> +
> +	ahash_request_free(req);
> +
> +	/* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
> +	for (i = 0; i < ETH_ALEN; i++)
> +		addr[i] = result[2 * i];
> +	addr[0] &= 0xfe; /* clear multicast bit */
> +	addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
> +
> +out:
> +	crypto_free_ahash(tfm);
> +}
> +
> +static void geth_check_addr(struct net_device *ndev, unsigned char *mac)
> +{
> +	int i;
> +	char *p = mac;
> +
> +	if (!is_valid_ether_addr(ndev->dev_addr)) {
> +		for (i = 0; i < ETH_ALEN; i++, p++)
> +			ndev->dev_addr[i] = simple_strtoul(p, &p, 16);
> +
> +		if (!is_valid_ether_addr(ndev->dev_addr))
> +			geth_chip_hwaddr(ndev->dev_addr);
> +
> +		if (!is_valid_ether_addr(ndev->dev_addr)) {
> +			random_ether_addr(ndev->dev_addr);
> +			pr_warn("%s: Use random mac address\n", ndev->name);
> +		}
> +	}
> +}
> +
> +static int geth_clk_enable(struct geth_priv *priv)
> +{
> +	int ret;
> +	phy_interface_t phy_interface = 0;
> +	u32 clk_value;
> +	/*u32 efuse_value;*/
> +/*
> +	ret = reset_control_deassert(priv->reset);
> +	if (ret) {
> +		pr_err("deassert gmac rst failed!\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(priv->geth_clk);
> +	if (ret) {
> +		pr_err("try to enable geth_clk failed!\n");
> +		goto assert_reset;
> +	}
> +
> +	if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> +			&& !IS_ERR_OR_NULL(priv->ephy_clk)) {
> +		ret = clk_prepare_enable(priv->ephy_clk);
> +		if (ret) {
> +			pr_err("try to enable ephy_clk failed!\n");
> +			goto ephy_clk_disable;
> +		}
> +	}
> +*/
> +	phy_interface = priv->phy_interface;
> +
> +	clk_value = readl(priv->base_phy);
> +	if (phy_interface == PHY_INTERFACE_MODE_RGMII)
> +		clk_value |= 0x00000004;
> +	else
> +		clk_value &= (~0x00000004);
> +
> +	clk_value &= (~0x00002003);
> +	if (phy_interface == PHY_INTERFACE_MODE_RGMII
> +			|| phy_interface == PHY_INTERFACE_MODE_GMII)
> +		clk_value |= 0x00000002;
> +	else if (phy_interface == PHY_INTERFACE_MODE_RMII)
> +		clk_value |= 0x00002001;
> +
> +	/*if (priv->phy_ext == INT_PHY) {
> +		if (0 != sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value))
> +			pr_err("get PHY efuse fail!\n");
> +		else
> +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> +			clk_value |= (((efuse_value >> 24) & 0x0F) + 3) << 28;
> +#else
> +			pr_warn("miss config come from efuse!\n");
> +#endif
> +	}*/
> +
> +	/* Adjust Tx/Rx clock delay */
> +	clk_value &= ~(0x07 << 10);
> +	clk_value |= ((priv->tx_delay & 0x07) << 10);
> +	clk_value &= ~(0x1F << 5);
> +	clk_value |= ((priv->rx_delay & 0x1F) << 5);
> +
> +	writel(clk_value, priv->base_phy);
> +
> +    return 0;
> +/*
> +ephy_clk_disable:
> +    clk_disable_unprepare(priv->ephy_clk);
> +assert_reset:
> +    reset_control_assert(priv->reset);
> +*/
> +    return ret;
> +}
> +
> +static void geth_clk_disable(struct geth_priv *priv)
> +{
> +/*
> +	if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> +			&& !IS_ERR_OR_NULL(priv->ephy_clk))
> +		clk_disable_unprepare(priv->ephy_clk);
> +
> +	clk_disable_unprepare(priv->geth_clk);
> +    reset_control_assert(priv->reset);
> +*/
> +}
> +
> +static void geth_tx_err(struct geth_priv *priv)
> +{
> +	netif_stop_queue(priv->ndev);
> +
> +	sunxi_stop_tx(priv->base);
> +
> +	geth_free_tx_sk(priv);
> +	memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> +	desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> +	priv->tx_dirty = 0;
> +	priv->tx_clean = 0;
> +	sunxi_start_tx(priv->base, priv->dma_tx_phy);
> +
> +	priv->ndev->stats.tx_errors++;
> +	netif_wake_queue(priv->ndev);
> +}
> +
> +static inline void geth_schedule(struct geth_priv *priv)
> +{
> +	if (likely(napi_schedule_prep(&priv->napi))) {
> +		sunxi_int_disable(priv->base);
> +		__napi_schedule(&priv->napi);
> +	}
> +}
> +
> +static irqreturn_t geth_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *ndev = (struct net_device *)dev_id;
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	int status;
> +
> +	if (unlikely(!ndev)) {
> +		pr_err("%s: invalid ndev pointer\n", __func__);
> +		return IRQ_NONE;
> +	}
> +
> +	status = sunxi_int_status(priv->base, (void *)(&priv->xstats));
> +
> +	if (likely(status == handle_tx_rx))
> +		geth_schedule(priv);
> +	else if (unlikely(status == tx_hard_error_bump_tc))
> +		netdev_info(ndev, "Do nothing for bump tc\n");
> +	else if (unlikely(status == tx_hard_error))
> +		geth_tx_err(priv);
> +	else
> +		netdev_info(ndev, "Do nothing.....\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int geth_open(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	int ret = 0;
> +
> +	ret = geth_power_on(priv);
> +	if (ret) {
> +		netdev_err(ndev, "Power on is failed\n");
> +		ret = -EINVAL;
> +	}
> +
> +	ret = geth_clk_enable(priv);
> +	if (ret) {
> +		pr_err("%s: clk enable is failed\n", __func__);
> +		ret = -EINVAL;
> +	}
> +
> +	netif_carrier_off(ndev);
> +
> +	ret = geth_phy_init(ndev);
> +	if (ret)
> +		goto err;
> +
> +	ret = sunxi_mac_reset((void *)priv->base, &sunxi_udelay, 10000);
> +	if (ret) {
> +		netdev_err(ndev, "Initialize hardware error\n");
> +		goto desc_err;
> +	}
> +
> +	sunxi_mac_init(priv->base, txmode, rxmode);
> +	sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> +
> +	if (!priv->is_suspend) {
> +		ret = geth_dma_desc_init(ndev);
> +		if (ret) {
> +			ret = -EINVAL;
> +			goto desc_err;
> +		}
> +	}
> +
> +	memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> +	memset(priv->dma_rx, 0, dma_desc_rx * sizeof(struct dma_desc));
> +
> +	desc_init_chain(priv->dma_rx, (unsigned long)priv->dma_rx_phy, dma_desc_rx);
> +	desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> +
> +	priv->rx_clean = 0;
> +	priv->rx_dirty = 0;
> +	priv->tx_clean = 0;
> +	priv->tx_dirty = 0;
> +	geth_rx_refill(ndev);
> +
> +	/* Extra statistics */
> +	memset(&priv->xstats, 0, sizeof(struct geth_extra_stats));
> +
> +	if (ndev->phydev)
> +		phy_start(ndev->phydev);
> +
> +	sunxi_start_rx(priv->base, (unsigned long)((struct dma_desc *)
> +		       priv->dma_rx_phy + priv->rx_dirty));
> +	sunxi_start_tx(priv->base, (unsigned long)((struct dma_desc *)
> +		       priv->dma_tx_phy + priv->tx_clean));
> +
> +	napi_enable(&priv->napi);
> +	netif_start_queue(ndev);
> +
> +	/* Enable the Rx/Tx */
> +	sunxi_mac_enable(priv->base);
> +
> +	return 0;
> +
> +desc_err:
> +	geth_phy_release(ndev);
> +err:
> +	geth_clk_disable(priv);
> +	if (priv->is_suspend)
> +		napi_enable(&priv->napi);
> +
> +	geth_power_off(priv);
> +
> +	return ret;
> +}
> +
> +static int geth_stop(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	netif_stop_queue(ndev);
> +	napi_disable(&priv->napi);
> +
> +	netif_carrier_off(ndev);
> +
> +	/* Release PHY resources */
> +	geth_phy_release(ndev);
> +
> +	/* Disable Rx/Tx */
> +	sunxi_mac_disable(priv->base);
> +
> +	geth_clk_disable(priv);
> +	geth_power_off(priv);
> +
> +	netif_tx_lock_bh(ndev);
> +	/* Release the DMA TX/RX socket buffers */
> +	geth_free_rx_sk(priv);
> +	geth_free_tx_sk(priv);
> +	netif_tx_unlock_bh(ndev);
> +
> +	/* Ensure that hareware have been stopped */
> +	if (!priv->is_suspend)
> +		geth_free_dma_desc(priv);
> +
> +	return 0;
> +}
> +
> +static void geth_tx_complete(struct geth_priv *priv)
> +{
> +	unsigned int entry = 0;
> +	struct sk_buff *skb = NULL;
> +	struct dma_desc *desc = NULL;
> +	int tx_stat;
> +
> +	spin_lock(&priv->tx_lock);
> +	while (circ_cnt(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > 0) {
> +		entry = priv->tx_clean;
> +		desc = priv->dma_tx + entry;
> +
> +		/* Check if the descriptor is owned by the DMA. */
> +		if (desc_get_own(desc))
> +			break;
> +
> +		/* Verify tx error by looking at the last segment */
> +		if (desc_get_tx_ls(desc)) {
> +			tx_stat = desc_get_tx_status(desc, (void *)(&priv->xstats));
> +
> +			if (likely(!tx_stat))
> +				priv->ndev->stats.tx_packets++;
> +			else
> +				priv->ndev->stats.tx_errors++;
> +		}
> +
> +		dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> +				 desc_buf_get_len(desc), DMA_TO_DEVICE);
> +
> +		skb = priv->tx_sk[entry];
> +		priv->tx_sk[entry] = NULL;
> +		desc_init(desc);
> +
> +		/* Find next dirty desc */
> +		priv->tx_clean = circ_inc(entry, dma_desc_tx);
> +
> +		if (unlikely(skb == NULL))
> +			continue;
> +
> +		dev_kfree_skb(skb);
> +	}
> +
> +	if (unlikely(netif_queue_stopped(priv->ndev)) &&
> +	    circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> +	    TX_THRESH) {
> +		netif_wake_queue(priv->ndev);
> +	}
> +	spin_unlock(&priv->tx_lock);
> +}
> +
> +static netdev_tx_t geth_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> +	struct geth_priv  *priv = netdev_priv(ndev);
> +	unsigned int entry;
> +	struct dma_desc *desc, *first;
> +	unsigned int len, tmp_len = 0;
> +	int i, csum_insert;
> +	int nfrags = skb_shinfo(skb)->nr_frags;
> +	dma_addr_t paddr;
> +
> +	spin_lock(&priv->tx_lock);
> +	if (unlikely(circ_space(priv->tx_dirty, priv->tx_clean,
> +	    dma_desc_tx) < (nfrags + 1))) {
> +		if (!netif_queue_stopped(ndev)) {
> +			netdev_err(ndev, "%s: BUG! Tx Ring full when queue awake\n", __func__);
> +			netif_stop_queue(ndev);
> +		}
> +		spin_unlock(&priv->tx_lock);
> +
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	csum_insert = (skb->ip_summed == CHECKSUM_PARTIAL);
> +	entry = priv->tx_dirty;
> +	first = priv->dma_tx + entry;
> +	desc = priv->dma_tx + entry;
> +
> +	len = skb_headlen(skb);
> +	priv->tx_sk[entry] = skb;
> +
> +#ifdef PKT_DEBUG
> +	printk("======TX PKT DATA: ============\n");
> +	/* dump the packet */
> +	print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> +		       16, 1, skb->data, 64, true);
> +#endif
> +
> +	/* Every desc max size is 2K */
> +	while (len != 0) {
> +		desc = priv->dma_tx + entry;
> +		tmp_len = ((len > MAX_BUF_SZ) ?  MAX_BUF_SZ : len);
> +
> +		paddr = dma_map_single(priv->dev, skb->data, tmp_len, DMA_TO_DEVICE);
> +		if (dma_mapping_error(priv->dev, paddr)) {
> +			dev_kfree_skb(skb);
> +			return -EIO;
> +		}
> +		desc_buf_set(desc, paddr, tmp_len);
> +		/* Don't set the first's own bit, here */
> +		if (first != desc) {
> +			priv->tx_sk[entry] = NULL;
> +			desc_set_own(desc);
> +		}
> +
> +		entry = circ_inc(entry, dma_desc_tx);
> +		len -= tmp_len;
> +	}
> +
> +	for (i = 0; i < nfrags; i++) {
> +		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +
> +		len = skb_frag_size(frag);
> +		desc = priv->dma_tx + entry;
> +		paddr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
> +		if (dma_mapping_error(priv->dev, paddr)) {
> +			dev_kfree_skb(skb);
> +			return -EIO;
> +		}
> +
> +		desc_buf_set(desc, paddr, len);
> +		desc_set_own(desc);
> +		priv->tx_sk[entry] = NULL;
> +		entry = circ_inc(entry, dma_desc_tx);
> +	}
> +
> +	ndev->stats.tx_bytes += skb->len;
> +	priv->tx_dirty = entry;
> +	desc_tx_close(first, desc, csum_insert);
> +
> +	desc_set_own(first);
> +	spin_unlock(&priv->tx_lock);
> +
> +	if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) <=
> +			(MAX_SKB_FRAGS + 1)) {
> +		netif_stop_queue(ndev);
> +		if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> +				TX_THRESH)
> +			netif_wake_queue(ndev);
> +	}
> +
> +#ifdef DEBUG
> +	printk("=======TX Descriptor DMA: 0x%08llx\n", priv->dma_tx_phy);
> +	printk("Tx pointor: dirty: %d, clean: %d\n", priv->tx_dirty, priv->tx_clean);
> +	desc_print(priv->dma_tx, dma_desc_tx);
> +#endif
> +	sunxi_tx_poll(priv->base);
> +	geth_tx_complete(priv);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +static int geth_rx(struct geth_priv *priv, int limit)
> +{
> +	unsigned int rxcount = 0;
> +	unsigned int entry;
> +	struct dma_desc *desc;
> +	struct sk_buff *skb;
> +	int status;
> +	int frame_len;
> +
> +	while (rxcount < limit) {
> +		entry = priv->rx_dirty;
> +		desc = priv->dma_rx + entry;
> +
> +		if (desc_get_own(desc))
> +			break;
> +
> +		rxcount++;
> +		priv->rx_dirty = circ_inc(priv->rx_dirty, dma_desc_rx);
> +
> +		/* Get length & status from hardware */
> +		frame_len = desc_rx_frame_len(desc);
> +		status = desc_get_rx_status(desc, (void *)(&priv->xstats));
> +
> +		netdev_dbg(priv->ndev, "Rx frame size %d, status: %d\n",
> +			   frame_len, status);
> +
> +		skb = priv->rx_sk[entry];
> +		if (unlikely(!skb)) {
> +			netdev_err(priv->ndev, "Skb is null\n");
> +			priv->ndev->stats.rx_dropped++;
> +			break;
> +		}
> +
> +#ifdef PKT_DEBUG
> +		printk("======RX PKT DATA: ============\n");
> +		/* dump the packet */
> +		print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> +				16, 1, skb->data, 64, true);
> +#endif
> +
> +		if (status == discard_frame) {
> +			netdev_dbg(priv->ndev, "Get error pkt\n");
> +			priv->ndev->stats.rx_errors++;
> +			continue;
> +		}
> +
> +		if (unlikely(status != llc_snap))
> +			frame_len -= ETH_FCS_LEN;
> +
> +		priv->rx_sk[entry] = NULL;
> +
> +		skb_put(skb, frame_len);
> +		dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> +				 desc_buf_get_len(desc), DMA_FROM_DEVICE);
> +
> +		skb->protocol = eth_type_trans(skb, priv->ndev);
> +
> +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> +		napi_gro_receive(&priv->napi, skb);
> +
> +		priv->ndev->stats.rx_packets++;
> +		priv->ndev->stats.rx_bytes += frame_len;
> +	}
> +
> +#ifdef DEBUG
> +	if (rxcount > 0) {
> +		printk("======RX Descriptor DMA: 0x%08llx=\n", priv->dma_rx_phy);
> +		printk("RX pointor: dirty: %d, clean: %d\n", priv->rx_dirty, priv->rx_clean);
> +		desc_print(priv->dma_rx, dma_desc_rx);
> +	}
> +#endif
> +	geth_rx_refill(priv->ndev);
> +
> +	return rxcount;
> +}
> +
> +static int geth_poll(struct napi_struct *napi, int budget)
> +{
> +	struct geth_priv *priv = container_of(napi, struct geth_priv, napi);
> +	int work_done = 0;
> +
> +	geth_tx_complete(priv);
> +	work_done = geth_rx(priv, budget);
> +
> +	if (work_done < budget) {
> +		napi_complete(napi);
> +		sunxi_int_enable(priv->base);
> +	}
> +
> +	return work_done;
> +}
> +
> +static int geth_change_mtu(struct net_device *ndev, int new_mtu)
> +{
> +	int max_mtu;
> +
> +	if (netif_running(ndev)) {
> +		pr_err("%s: must be stopped to change its MTU\n", ndev->name);
> +		return -EBUSY;
> +	}
> +
> +	max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
> +
> +	if ((new_mtu < 46) || (new_mtu > max_mtu)) {
> +		pr_err("%s: invalid MTU, max MTU is: %d\n", ndev->name, max_mtu);
> +		return -EINVAL;
> +	}
> +
> +	ndev->mtu = new_mtu;
> +	netdev_update_features(ndev);
> +
> +	return 0;
> +}
> +
> +static netdev_features_t geth_fix_features(struct net_device *ndev,
> +					   netdev_features_t features)
> +{
> +	return features;
> +}
> +
> +static void geth_set_rx_mode(struct net_device *ndev)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	unsigned int value = 0;
> +
> +	pr_debug("%s: # mcasts %d, # unicast %d\n",
> +		 __func__, netdev_mc_count(ndev), netdev_uc_count(ndev));
> +
> +	spin_lock(&priv->lock);
> +	if (ndev->flags & IFF_PROMISC) {
> +		value = GETH_FRAME_FILTER_PR;
> +	} else if ((netdev_mc_count(ndev) > HASH_TABLE_SIZE) ||
> +		   (ndev->flags & IFF_ALLMULTI)) {
> +		value = GETH_FRAME_FILTER_PM;	/* pass all multi */
> +		sunxi_hash_filter(priv->base, ~0UL, ~0UL);
> +	} else if (!netdev_mc_empty(ndev)) {
> +		u32 mc_filter[2];
> +		struct netdev_hw_addr *ha;
> +
> +		/* Hash filter for multicast */
> +		value = GETH_FRAME_FILTER_HMC;
> +
> +		memset(mc_filter, 0, sizeof(mc_filter));
> +		netdev_for_each_mc_addr(ha, ndev) {
> +			/* The upper 6 bits of the calculated CRC are used to
> +			 *  index the contens of the hash table
> +			 */
> +			int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
> +			/* The most significant bit determines the register to
> +			 * use (H/L) while the other 5 bits determine the bit
> +			 * within the register.
> +			 */
> +			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
> +		}
> +		sunxi_hash_filter(priv->base, mc_filter[0], mc_filter[1]);
> +	}
> +
> +	/* Handle multiple unicast addresses (perfect filtering)*/
> +	if (netdev_uc_count(ndev) > 16) {
> +		/* Switch to promiscuous mode is more than 8 addrs are required */
> +		value |= GETH_FRAME_FILTER_PR;
> +	} else {
> +		int reg = 1;
> +		struct netdev_hw_addr *ha;
> +
> +		netdev_for_each_uc_addr(ha, ndev) {
> +			sunxi_set_umac(priv->base, ha->addr, reg);
> +			reg++;
> +		}
> +	}
> +
> +#ifdef FRAME_FILTER_DEBUG
> +	/* Enable Receive all mode (to debug filtering_fail errors) */
> +	value |= GETH_FRAME_FILTER_RA;
> +#endif
> +	sunxi_set_filter(priv->base, value);
> +	spin_unlock(&priv->lock);
> +}
> +
> +static void geth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	geth_tx_err(priv);
> +}
> +
> +static int geth_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
> +{
> +	if (!netif_running(ndev))
> +		return -EINVAL;
> +
> +	if (!ndev->phydev)
> +		return -EINVAL;
> +
> +	return phy_mii_ioctl(ndev->phydev, rq, cmd);
> +}
> +
> +/* Configuration changes (passed on by ifconfig) */
> +static int geth_config(struct net_device *ndev, struct ifmap *map)
> +{
> +	if (ndev->flags & IFF_UP)	/* can't act on a running interface */
> +		return -EBUSY;
> +
> +	/* Don't allow changing the I/O address */
> +	if (map->base_addr != ndev->base_addr) {
> +		pr_warn("%s: can't change I/O address\n", ndev->name);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* Don't allow changing the IRQ */
> +	if (map->irq != ndev->irq) {
> +		pr_warn("%s: can't change IRQ number %d\n", ndev->name, ndev->irq);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static int geth_set_mac_address(struct net_device *ndev, void *p)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct sockaddr *addr = p;
> +
> +	if (!is_valid_ether_addr(addr->sa_data))
> +		return -EADDRNOTAVAIL;
> +
> +	memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
> +	sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> +
> +	return 0;
> +}
> +
> +int geth_set_features(struct net_device *ndev, netdev_features_t features)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	if (features & NETIF_F_LOOPBACK && netif_running(ndev))
> +		sunxi_mac_loopback(priv->base, 1);
> +	else
> +		sunxi_mac_loopback(priv->base, 0);
> +
> +	return 0;
> +}
> +
> +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> +/* Polling receive - used by NETCONSOLE and other diagnostic tools
> + * to allow network I/O with interrupts disabled.
> + */
> +static void geth_poll_controller(struct net_device *dev)
> +{
> +	disable_irq(dev->irq);
> +	geth_interrupt(dev->irq, dev);
> +	enable_irq(dev->irq);
> +}
> +#endif
> +
> +static const struct net_device_ops geth_netdev_ops = {
> +	.ndo_init = NULL,
> +	.ndo_open = geth_open,
> +	.ndo_start_xmit = geth_xmit,
> +	.ndo_stop = geth_stop,
> +	.ndo_change_mtu = geth_change_mtu,
> +	.ndo_fix_features = geth_fix_features,
> +	.ndo_set_rx_mode = geth_set_rx_mode,
> +	.ndo_tx_timeout = geth_tx_timeout,
> +	.ndo_do_ioctl = geth_ioctl,
> +	.ndo_set_config = geth_config,
> +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> +	.ndo_poll_controller = geth_poll_controller,
> +#endif
> +	.ndo_set_mac_address = geth_set_mac_address,
> +	.ndo_set_features = geth_set_features,
> +};
> +
> +static int geth_check_if_running(struct net_device *ndev)
> +{
> +	if (!netif_running(ndev))
> +		return -EBUSY;
> +	return 0;
> +}
> +
> +static int geth_get_sset_count(struct net_device *netdev, int sset)
> +{
> +	int len;
> +
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		len = 0;
> +		return len;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +/*static int geth_ethtool_getsettings(struct net_device *ndev,
> +				    struct ethtool_cmd *cmd)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct phy_device *phy = ndev->phydev;
> +	int rc;
> +
> +	if (phy == NULL) {
> +		netdev_err(ndev, "%s: %s: PHY is not registered\n",
> +		       __func__, ndev->name);
> +		return -ENODEV;
> +	}
> +
> +	if (!netif_running(ndev)) {
> +		pr_err("%s: interface is disabled: we cannot track "
> +		       "link speed / duplex setting\n", ndev->name);
> +		return -EBUSY;
> +	}
> +
> +	cmd->transceiver = XCVR_INTERNAL;
> +	spin_lock_irq(&priv->lock);
> +	//rc = phy_ethtool_gset(phy, cmd);
> +	spin_unlock_irq(&priv->lock);
> +
> +	return rc;
> +}
> +
> +static int geth_ethtool_setsettings(struct net_device *ndev,
> +				    struct ethtool_cmd *cmd)
> +{
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct phy_device *phy = ndev->phydev;
> +	int rc;
> +
> +	spin_lock(&priv->lock);
> +	rc = phy_ethtool_sset(phy, cmd);
> +	spin_unlock(&priv->lock);
> +
> +	return rc;
> +}*/
> +
> +static void geth_ethtool_getdrvinfo(struct net_device *ndev,
> +				    struct ethtool_drvinfo *info)
> +{
> +	strlcpy(info->driver, "sunxi_geth", sizeof(info->driver));
> +
> +#define DRV_MODULE_VERSION "SUNXI Gbgit driver V1.1"
> +
> +	strcpy(info->version, DRV_MODULE_VERSION);
> +	info->fw_version[0] = '\0';
> +}
> +
> +static const struct ethtool_ops geth_ethtool_ops = {
> +	.begin = geth_check_if_running,
> +	//.get_settings = geth_ethtool_getsettings,
> +	//.set_settings = geth_ethtool_setsettings,
> +	.get_link = ethtool_op_get_link,
> +	.get_pauseparam = NULL,
> +	.set_pauseparam = NULL,
> +	.get_ethtool_stats = NULL,
> +	.get_strings = NULL,
> +	.get_wol = NULL,
> +	.set_wol = NULL,
> +	.get_sset_count = geth_get_sset_count,
> +	.get_drvinfo = geth_ethtool_getdrvinfo,
> +};
> +
> +/* config hardware resource */
> +static int geth_hw_init(struct platform_device *pdev)
> +{
> +	struct net_device *ndev = platform_get_drvdata(pdev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +	struct device_node *np = pdev->dev.of_node;
> +	int ret = 0;
> +	struct resource *res;
> +	u32 value;
> +//	struct gpio_config cfg;
> +//	const char *gmac_power;
> +//	char power[20];
> +//	int i;
> +
> +#if 1
> +	priv->phy_ext = EXT_PHY;
> +#else
> +	priv->phy_ext = INT_PHY;
> +#endif
> +
> +	/* config memery resource */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (unlikely(!res)) {
> +		pr_err("%s: ERROR: get gmac memory failed", __func__);
> +		return -ENODEV;
> +	}
> +
> +	priv->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (!priv->base) {
> +		pr_err("%s: ERROR: gmac memory mapping failed", __func__);
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (unlikely(!res)) {
> +		pr_err("%s: ERROR: get phy memory failed", __func__);
> +		ret = -ENODEV;
> +		goto mem_err;
> +	}
> +
> +	priv->base_phy = devm_ioremap_resource(&pdev->dev, res);
> +	if (unlikely(!priv->base_phy)) {
> +		pr_err("%s: ERROR: phy memory mapping failed", __func__);
> +		ret = -ENOMEM;
> +		goto mem_err;
> +	}
> +
> +	/* config IRQ */
> +	ndev->irq = platform_get_irq_byname(pdev, "gmacirq");
> +	if (ndev->irq == -ENXIO) {
> +		pr_err("%s: ERROR: MAC IRQ not found\n", __func__);
> +		ret = -ENXIO;
> +		goto irq_err;
> +	}
> +
> +	ret = request_irq(ndev->irq, geth_interrupt, IRQF_SHARED, dev_name(&pdev->dev), ndev);
> +	if (unlikely(ret < 0)) {
> +		pr_err("Could not request irq %d, error: %d\n", ndev->irq, ret);
> +		goto irq_err;
> +	}
> +
> +	/* get gmac rst handle */
> +/*
> +	priv->reset = devm_reset_control_get(&pdev->dev, NULL);
> +	if (IS_ERR(priv->reset)) {
> +		pr_err("%s: Get gmac reset control failed!\n", __func__);
> +		return PTR_ERR(priv->reset);
> +	}
> +*/
> +	/* config clock */
> +/*
> +	priv->geth_clk = of_clk_get_by_name(np, "gmac");
> +	if (unlikely(!priv->geth_clk || IS_ERR(priv->geth_clk))) {
> +		pr_err("Get gmac clock failed!\n");
> +		ret = -EINVAL;
> +		goto clk_err;
> +	}
> +
> +	if (INT_PHY == priv->phy_ext) {
> +		priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> +		if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> +			pr_err("Get ephy clock failed!\n");
> +			ret = -EINVAL;
> +			goto clk_err;
> +		}
> +	} else {
> +		if (!of_property_read_u32(np, "use-ephy25m", &(priv->use_ephy_clk))
> +				&& priv->use_ephy_clk) {
> +			priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> +			if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> +				pr_err("Get ephy clk failed!\n");
> +				ret = -EINVAL;
> +				goto clk_err;
> +			}
> +		}
> +	}
> +*/
> +	/* config power regulator */
> +/*
> +	if (EXT_PHY == priv->phy_ext) {
> +		for (i = 0; i < POWER_CHAN_NUM; i++) {
> +			snprintf(power, 15, "gmac-power%d", i);
> +			ret = of_property_read_string(np, power, &gmac_power);
> +			if (ret) {
> +				priv->gmac_power[i] = NULL;
> +				pr_info("gmac-power%d: NULL\n", i);
> +				continue;
> +			}
> +			priv->gmac_power[i] = regulator_get(NULL, gmac_power);
> +			if (IS_ERR(priv->gmac_power[i])) {
> +				pr_err("gmac-power%d get error!\n", i);
> +				ret = -EINVAL;
> +				goto clk_err;
> +			}
> +		}
> +	}
> +*/
> +	/* config other parameters */
> +	of_get_phy_mode(np, &(priv->phy_interface));
> +	if (priv->phy_interface != PHY_INTERFACE_MODE_MII &&
> +	    priv->phy_interface != PHY_INTERFACE_MODE_RGMII &&
> +	    priv->phy_interface != PHY_INTERFACE_MODE_RMII) {
> +		pr_err("Not support phy type!\n");
> +		priv->phy_interface = PHY_INTERFACE_MODE_MII;
> +	}
> +
> +	if (!of_property_read_u32(np, "tx-delay", &value))
> +		priv->tx_delay = value;
> +
> +	if (!of_property_read_u32(np, "rx-delay", &value))
> +		priv->rx_delay = value;
> +
> +	/* config pinctrl */
> +/*
> +	if (EXT_PHY == priv->phy_ext) {
> +		priv->phyrst = of_get_named_gpio_flags(np, "phy-rst", 0, (enum of_gpio_flags *)&cfg);
> +		priv->rst_active_low = (cfg.data == OF_GPIO_ACTIVE_LOW) ? 1 : 0;
> +
> +		if (gpio_is_valid(priv->phyrst)) {
> +			if (gpio_request(priv->phyrst, "phy-rst") < 0) {
> +				pr_err("gmac gpio request fail!\n");
> +				ret = -EINVAL;
> +				goto pin_err;
> +			}
> +		}
> +
> +		priv->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> +		if (IS_ERR_OR_NULL(priv->pinctrl)) {
> +			pr_err("gmac pinctrl error!\n");
> +			priv->pinctrl = NULL;
> +			ret = -EINVAL;
> +			goto pin_err;
> +		}
> +	}
> +*/
> +	return 0;
> +
> +//pin_err:
> +/*
> +	if (EXT_PHY == priv->phy_ext) {
> +		for (i = 0; i < POWER_CHAN_NUM; i++) {
> +			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> +				continue;
> +			regulator_put(priv->gmac_power[i]);
> +		}
> +	}
> +*/
> +//clk_err:
> +//	free_irq(ndev->irq, ndev);
> +irq_err:
> +	devm_iounmap(&pdev->dev, priv->base_phy);
> +mem_err:
> +	devm_iounmap(&pdev->dev, priv->base);
> +
> +	return ret;
> +}
> +
> +static void geth_hw_release(struct platform_device *pdev)
> +{
> +	struct net_device *ndev = platform_get_drvdata(pdev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	devm_iounmap(&pdev->dev, (priv->base_phy));
> +	devm_iounmap(&pdev->dev, priv->base);
> +	free_irq(ndev->irq, ndev);
> +/*
> +	if (priv->geth_clk)
> +		clk_put(priv->geth_clk);
> +
> +	if (EXT_PHY == priv->phy_ext) {
> +		for (i = 0; i < POWER_CHAN_NUM; i++) {
> +			if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> +				continue;
> +			regulator_put(priv->gmac_power[i]);
> +		}
> +
> +		if (!IS_ERR_OR_NULL(priv->pinctrl))
> +			devm_pinctrl_put(priv->pinctrl);
> +
> +		if (gpio_is_valid(priv->phyrst))
> +			gpio_free(priv->phyrst);
> +	}
> +
> +	if (!IS_ERR_OR_NULL(priv->ephy_clk))
> +		clk_put(priv->ephy_clk);
> +*/
> +}
> +
> +/**
> + * geth_probe
> + * @pdev: platform device pointer
> + * Description: the driver is initialized through platform_device.
> + */
> +static int geth_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct net_device *ndev = NULL;
> +	struct geth_priv *priv;
> +
> +	pr_info("sunxi gmac driver's version: %s\n", SUNXI_GMAC_VERSION);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +	pdev->dev.dma_mask = &geth_dma_mask;
> +	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +#endif
> +
> +	ndev = alloc_etherdev(sizeof(struct geth_priv));
> +	if (!ndev) {
> +		dev_err(&pdev->dev, "could not allocate device.\n");
> +		return -ENOMEM;
> +	}
> +	SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> +	priv = netdev_priv(ndev);
> +	platform_set_drvdata(pdev, ndev);
> +
> +	/* Must set private data to pdev, before call it */
> +	ret = geth_hw_init(pdev);
> +	if (0 != ret) {
> +		pr_err("geth_hw_init fail!\n");
> +		goto hw_err;
> +	}
> +
> +	/* setup the netdevice, fill the field of netdevice */
> +	ether_setup(ndev);
> +	ndev->netdev_ops = &geth_netdev_ops;
> +	netdev_set_default_ethtool_ops(ndev, &geth_ethtool_ops);
> +	ndev->base_addr = (unsigned long)priv->base;
> +
> +	priv->ndev = ndev;
> +	priv->dev = &pdev->dev;
> +
> +	/* TODO: support the VLAN frames */
> +	ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
> +				NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
> +
> +	ndev->features |= ndev->hw_features;
> +	ndev->hw_features |= NETIF_F_LOOPBACK;
> +	ndev->priv_flags |= IFF_UNICAST_FLT;
> +
> +	ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
> +
> +	netif_napi_add(ndev, &priv->napi, geth_poll,  BUDGET);
> +
> +	spin_lock_init(&priv->lock);
> +	spin_lock_init(&priv->tx_lock);
> +
> +	/* The last val is mdc clock ratio */
> +	sunxi_geth_register((void *)ndev->base_addr, HW_VERSION, 0x03);
> +
> +	ret = register_netdev(ndev);
> +	if (ret) {
> +		netif_napi_del(&priv->napi);
> +		pr_err("Error: Register %s failed\n", ndev->name);
> +		goto reg_err;
> +	}
> +
> +	/* Before open the device, the mac address should be set */
> +	geth_check_addr(ndev, mac_str);
> +
> +#ifdef CONFIG_GETH_ATTRS
> +	geth_create_attrs(ndev);
> +#endif
> +	device_create_file(&pdev->dev, &dev_attr_gphy_test);
> +	device_create_file(&pdev->dev, &dev_attr_mii_reg);
> +	device_create_file(&pdev->dev, &dev_attr_loopback_test);
> +	device_create_file(&pdev->dev, &dev_attr_extra_tx_stats);
> +	device_create_file(&pdev->dev, &dev_attr_extra_rx_stats);
> +
> +	device_enable_async_suspend(&pdev->dev);
> +
> +#if IS_ENABLED(CONFIG_PM)
> +	INIT_WORK(&priv->eth_work, geth_resume_work);
> +#endif
> +
> +	return 0;
> +
> +reg_err:
> +	geth_hw_release(pdev);
> +hw_err:
> +	platform_set_drvdata(pdev, NULL);
> +	free_netdev(ndev);
> +
> +	return ret;
> +}
> +
> +static int geth_remove(struct platform_device *pdev)
> +{
> +	struct net_device *ndev = platform_get_drvdata(pdev);
> +	struct geth_priv *priv = netdev_priv(ndev);
> +
> +	device_remove_file(&pdev->dev, &dev_attr_gphy_test);
> +	device_remove_file(&pdev->dev, &dev_attr_mii_reg);
> +	device_remove_file(&pdev->dev, &dev_attr_loopback_test);
> +	device_remove_file(&pdev->dev, &dev_attr_extra_tx_stats);
> +	device_remove_file(&pdev->dev, &dev_attr_extra_rx_stats);
> +
> +	netif_napi_del(&priv->napi);
> +	unregister_netdev(ndev);
> +	geth_hw_release(pdev);
> +	platform_set_drvdata(pdev, NULL);
> +	free_netdev(ndev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id geth_of_match[] = {
> +	{.compatible = "allwinner,sunxi-gmac",},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, geth_of_match);
> +
> +static struct platform_driver geth_driver = {
> +	.probe	= geth_probe,
> +	.remove = geth_remove,
> +	.driver = {
> +		   .name = "sunxi-gmac",
> +		   .owner = THIS_MODULE,
> +		   .pm = &geth_pm_ops,
> +		   .of_match_table = geth_of_match,
> +	},
> +};
> +module_platform_driver(geth_driver);
> +
> +#ifdef MODULE
> +static int __init set_mac_addr(char *str)
> +{
> +	char *p = str;
> +
> +	if (str && strlen(str))
> +		memcpy(mac_str, p, 18);
> +
> +	return 0;
> +}
> +__setup("mac_addr=", set_mac_addr);
> +#endif
> +
> +MODULE_DESCRIPTION("Allwinner Gigabit Ethernet driver");
> +MODULE_AUTHOR("fuzhaoke <fuzhaoke@allwinnertech.com>");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> new file mode 100644
> index 00000000..ea7a6f15
> --- /dev/null
> +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> @@ -0,0 +1,258 @@
> +/*
> + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
> + *
> + * Copyright © 2016-2018, fuzhaoke
> + *		Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> + *
> + * This file is provided under a dual BSD/GPL license.  When using or
> + * redistributing this file, you may do so under either license.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
> + * GNU General Public License for more details.
> + */
> +#ifndef __SUNXI_GETH_H__
> +#define __SUNXI_GETH_H__
> +
> +#include <linux/etherdevice.h>
> +#include <linux/netdevice.h>
> +#include <linux/phy.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +
> +/* GETH_FRAME_FILTER  register value */
> +#define GETH_FRAME_FILTER_PR	0x00000001	/* Promiscuous Mode */
> +#define GETH_FRAME_FILTER_HUC	0x00000002	/* Hash Unicast */
> +#define GETH_FRAME_FILTER_HMC	0x00000004	/* Hash Multicast */
> +#define GETH_FRAME_FILTER_DAIF	0x00000008	/* DA Inverse Filtering */
> +#define GETH_FRAME_FILTER_PM	0x00000010	/* Pass all multicast */
> +#define GETH_FRAME_FILTER_DBF	0x00000020	/* Disable Broadcast frames */
> +#define GETH_FRAME_FILTER_SAIF	0x00000100	/* Inverse Filtering */
> +#define GETH_FRAME_FILTER_SAF	0x00000200	/* Source Address Filter */
> +#define GETH_FRAME_FILTER_HPF	0x00000400	/* Hash or perfect Filter */
> +#define GETH_FRAME_FILTER_RA	0x80000000	/* Receive all mode */
> +
> +/* Default tx descriptor */
> +#define TX_SINGLE_DESC0		0x80000000
> +#define TX_SINGLE_DESC1		0x63000000
> +
> +/* Default rx descriptor */
> +#define RX_SINGLE_DESC0		0x80000000
> +#define RX_SINGLE_DESC1		0x83000000
> +
> +typedef union {
> +	struct {
> +		/* TDES0 */
> +		unsigned int deferred:1;	/* Deferred bit (only half-duplex) */
> +		unsigned int under_err:1;	/* Underflow error */
> +		unsigned int ex_deferral:1;	/* Excessive deferral */
> +		unsigned int coll_cnt:4;	/* Collision count */
> +		unsigned int vlan_tag:1;	/* VLAN Frame */
> +		unsigned int ex_coll:1;		/* Excessive collision */
> +		unsigned int late_coll:1;	/* Late collision */
> +		unsigned int no_carr:1;		/* No carrier */
> +		unsigned int loss_carr:1;	/* Loss of collision */
> +		unsigned int ipdat_err:1;	/* IP payload error */
> +		unsigned int frm_flu:1;		/* Frame flushed */
> +		unsigned int jab_timeout:1;	/* Jabber timeout */
> +		unsigned int err_sum:1;		/* Error summary */
> +		unsigned int iphead_err:1;	/* IP header error */
> +		unsigned int ttss:1;		/* Transmit time stamp status */
> +		unsigned int reserved0:13;
> +		unsigned int own:1;		/* Own bit. CPU:0, DMA:1 */
> +	} tx;
> +
> +	/* bits 5 7 0 | Frame status
> +	 * ----------------------------------------------------------
> +	 *      0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
> +	 *      1 0 0 | IPv4/6 No CSUM errorS.
> +	 *      1 0 1 | IPv4/6 CSUM PAYLOAD error
> +	 *      1 1 0 | IPv4/6 CSUM IP HR error
> +	 *      1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
> +	 *      0 0 1 | IPv4/6 unsupported IP PAYLOAD
> +	 *      0 1 1 | COE bypassed.. no IPv4/6 frame
> +	 *      0 1 0 | Reserved.
> +	 */
> +	struct {
> +		/* RDES0 */
> +		unsigned int chsum_err:1;	/* Payload checksum error */
> +		unsigned int crc_err:1;		/* CRC error */
> +		unsigned int dribbling:1;	/* Dribble bit error */
> +		unsigned int mii_err:1;		/* Received error (bit3) */
> +		unsigned int recv_wt:1;		/* Received watchdog timeout */
> +		unsigned int frm_type:1;	/* Frame type */
> +		unsigned int late_coll:1;	/* Late Collision */
> +		unsigned int ipch_err:1;	/* IPv header checksum error (bit7) */
> +		unsigned int last_desc:1;	/* Laset descriptor */
> +		unsigned int first_desc:1;	/* First descriptor */
> +		unsigned int vlan_tag:1;	/* VLAN Tag */
> +		unsigned int over_err:1;	/* Overflow error (bit11) */
> +		unsigned int len_err:1;		/* Length error */
> +		unsigned int sou_filter:1;	/* Source address filter fail */
> +		unsigned int desc_err:1;	/* Descriptor error */
> +		unsigned int err_sum:1;		/* Error summary (bit15) */
> +		unsigned int frm_len:14;	/* Frame length */
> +		unsigned int des_filter:1;	/* Destination address filter fail */
> +		unsigned int own:1;		/* Own bit. CPU:0, DMA:1 */
> +	#define RX_PKT_OK		0x7FFFB77C
> +	#define RX_LEN			0x3FFF0000
> +	} rx;
> +
> +	unsigned int all;
> +} desc0_u;
> +
> +typedef union {
> +	struct {
> +		/* TDES1 */
> +		unsigned int buf1_size:11;	/* Transmit buffer1 size */
> +		unsigned int buf2_size:11;	/* Transmit buffer2 size */
> +		unsigned int ttse:1;		/* Transmit time stamp enable */
> +		unsigned int dis_pad:1;		/* Disable pad (bit23) */
> +		unsigned int adr_chain:1;	/* Second address chained */
> +		unsigned int end_ring:1;	/* Transmit end of ring */
> +		unsigned int crc_dis:1;		/* Disable CRC */
> +		unsigned int cic:2;		/* Checksum insertion control (bit27:28) */
> +		unsigned int first_sg:1;	/* First Segment */
> +		unsigned int last_seg:1;	/* Last Segment */
> +		unsigned int interrupt:1;	/* Interrupt on completion */
> +	} tx;
> +
> +	struct {
> +		/* RDES1 */
> +		unsigned int buf1_size:11;	/* Received buffer1 size */
> +		unsigned int buf2_size:11;	/* Received buffer2 size */
> +		unsigned int reserved1:2;
> +		unsigned int adr_chain:1;	/* Second address chained */
> +		unsigned int end_ring:1;		/* Received end of ring */
> +		unsigned int reserved2:5;
> +		unsigned int dis_ic:1;		/* Disable interrupt on completion */
> +	} rx;
> +
> +	unsigned int all;
> +} desc1_u;
> +
> +typedef struct dma_desc {
> +	desc0_u desc0;
> +	desc1_u desc1;
> +	/* The address of buffers */
> +	unsigned int	desc2;
> +	/* Next desc's address */
> +	unsigned int	desc3;
> +} __attribute__((packed)) dma_desc_t;
> +
> +enum rx_frame_status { /* IPC status */
> +	good_frame = 0,
> +	discard_frame = 1,
> +	csum_none = 2,
> +	llc_snap = 4,
> +};
> +
> +enum tx_dma_irq_status {
> +	tx_hard_error = 1,
> +	tx_hard_error_bump_tc = 2,
> +	handle_tx_rx = 3,
> +};
> +
> +struct geth_extra_stats {
> +	/* Transmit errors */
> +	unsigned long tx_underflow;
> +	unsigned long tx_carrier;
> +	unsigned long tx_losscarrier;
> +	unsigned long vlan_tag;
> +	unsigned long tx_deferred;
> +	unsigned long tx_vlan;
> +	unsigned long tx_jabber;
> +	unsigned long tx_frame_flushed;
> +	unsigned long tx_payload_error;
> +	unsigned long tx_ip_header_error;
> +
> +	/* Receive errors */
> +	unsigned long rx_desc;
> +	unsigned long sa_filter_fail;
> +	unsigned long overflow_error;
> +	unsigned long ipc_csum_error;
> +	unsigned long rx_collision;
> +	unsigned long rx_crc;
> +	unsigned long dribbling_bit;
> +	unsigned long rx_length;
> +	unsigned long rx_mii;
> +	unsigned long rx_multicast;
> +	unsigned long rx_gmac_overflow;
> +	unsigned long rx_watchdog;
> +	unsigned long da_rx_filter_fail;
> +	unsigned long sa_rx_filter_fail;
> +	unsigned long rx_missed_cntr;
> +	unsigned long rx_overflow_cntr;
> +	unsigned long rx_vlan;
> +
> +	/* Tx/Rx IRQ errors */
> +	unsigned long tx_undeflow_irq;
> +	unsigned long tx_process_stopped_irq;
> +	unsigned long tx_jabber_irq;
> +	unsigned long rx_overflow_irq;
> +	unsigned long rx_buf_unav_irq;
> +	unsigned long rx_process_stopped_irq;
> +	unsigned long rx_watchdog_irq;
> +	unsigned long tx_early_irq;
> +	unsigned long fatal_bus_error_irq;
> +
> +	/* Extra info */
> +	unsigned long threshold;
> +	unsigned long tx_pkt_n;
> +	unsigned long rx_pkt_n;
> +	unsigned long poll_n;
> +	unsigned long sched_timer_n;
> +	unsigned long normal_irq_n;
> +};
> +
> +int sunxi_mdio_read(void *,  int, int);
> +int sunxi_mdio_write(void *, int, int, unsigned short);
> +int sunxi_mdio_reset(void *);
> +void sunxi_set_link_mode(void *iobase, int duplex, int speed);
> +void sunxi_int_disable(void *);
> +int sunxi_int_status(void *, struct geth_extra_stats *x);
> +int sunxi_mac_init(void *, int txmode, int rxmode);
> +void sunxi_set_umac(void *, unsigned char *, int);
> +void sunxi_mac_enable(void *);
> +void sunxi_mac_disable(void *);
> +void sunxi_tx_poll(void *);
> +void sunxi_int_enable(void *);
> +void sunxi_start_rx(void *, unsigned long);
> +void sunxi_start_tx(void *, unsigned long);
> +void sunxi_stop_tx(void *);
> +void sunxi_stop_rx(void *);
> +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high);
> +void sunxi_set_filter(void *iobase, unsigned long flags);
> +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause);
> +void sunxi_mac_loopback(void *iobase, int enable);
> +
> +void desc_buf_set(struct dma_desc *p, unsigned long paddr, int size);
> +void desc_set_own(struct dma_desc *p);
> +void desc_init_chain(struct dma_desc *p, unsigned long paddr,  unsigned int size);
> +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert);
> +void desc_init(struct dma_desc *p);
> +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> +int desc_buf_get_len(struct dma_desc *desc);
> +int desc_buf_get_addr(struct dma_desc *desc);
> +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> +int desc_get_own(struct dma_desc *desc);
> +int desc_get_tx_ls(struct dma_desc *desc);
> +int desc_rx_frame_len(struct dma_desc *desc);
> +
> +int sunxi_mac_reset(void *iobase, void (*mdelay)(int), int n);
> +int sunxi_geth_register(void *iobase, int version, unsigned int div);
> +
> +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> +extern int ephy_is_enable(void);
> +#endif
> +
> +#if IS_ENABLED(CONFIG_ARCH_SUN8IW3) \
> +	|| IS_ENABLED(CONFIG_ARCH_SUN9IW1) \
> +	|| IS_ENABLED(CONFIG_ARCH_SUN7I)
> +#define HW_VERSION	0
> +#else
> +#define HW_VERSION	1
> +#endif
> +
> +#endif
> diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
> index 821e85a..3c86c2a 100644
> --- a/drivers/net/phy/realtek.c
> +++ b/drivers/net/phy/realtek.c
> @@ -338,7 +338,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)
>  			"2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
>  			val_txdly ? "enabled" : "disabled");
>  	}
> -
> +return 0;
>  	ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY,
>  				       val_rxdly);
>  	if (ret < 0) {


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods
  2021-06-06  9:03 ` [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods guoren
@ 2021-06-06 14:38   ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2021-06-06 14:38 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, maxime, drew, liush,
	lazyparser, wefu, linux-riscv, linux-kernel, linux-arch,
	linux-sunxi, Guo Ren, Christoph Hellwig

On Sun, Jun 06, 2021 at 09:03:58AM +0000, guoren@kernel.org wrote:
> +static inline void local_flush_tlb_all_asid(unsigned long asid)
> +{
> +	__asm__ __volatile__ ("sfence.vma x0, %0"
> +			:
> +			: "r" (asid)
> +			: "memory");
> +}
> +
> +static inline void local_flush_tlb_range_asid(unsigned long start,
> +				unsigned long size, unsigned long asid)
> +{
> +	unsigned long tmp, end = ALIGN(start + size, PAGE_SIZE);
> +
> +	for (tmp = start & PAGE_MASK; tmp < end; tmp += PAGE_SIZE) {
> +		__asm__ __volatile__ ("sfence.vma %0, %1"
> +				:
> +				: "r" (tmp), "r" (asid)
> +				: "memory");
> +	}

No need to expose these in a header.

> +static void __sbi_tlb_flush_range_asid(struct cpumask *cmask,
> +				       unsigned long start,
> +				       unsigned long size,
> +				       unsigned long asid)
> +{
> +	struct cpumask hmask;
> +	unsigned int cpuid;
> +
> +	if (cpumask_empty(cmask))
> +		return;
> +
> +	cpuid = get_cpu();
> +
> +	if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
> +		if (size == -1)
> +			local_flush_tlb_all_asid(asid);
> +		else
> +			local_flush_tlb_range_asid(start, size, asid);
> +	} else {
> +		riscv_cpuid_to_hartid_mask(cmask, &hmask);
> +		sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),
> +					   start, size, asid);
> +	}
> +
> +	put_cpu();
> +}

Still no need to duplicate most of this logic.  Also please document
why this uses a different tradeoff for the flush all logic compared
to the non-ASID path.

> +
>  void flush_tlb_mm(struct mm_struct *mm)
>  {
> -	__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
> +	if (static_branch_unlikely(&use_asid_allocator))
> +		__sbi_tlb_flush_range_asid(mm_cpumask(mm), 0, -1,
> +					   atomic_long_read(&mm->context.id));
> +	else
> +		__sbi_tlb_flush_range(mm_cpumask(mm), 0, -1);
>  }
>  
>  void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
>  {
> -	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
> +	if (static_branch_unlikely(&use_asid_allocator))
> +		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE,
> +					   atomic_long_read(&vma->vm_mm->context.id));
> +	else
> +		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
>  }
>  
>  void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
>  		     unsigned long end)
>  {
> -	__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
> +	if (static_branch_unlikely(&use_asid_allocator))
> +		__sbi_tlb_flush_range_asid(mm_cpumask(vma->vm_mm), start, end - start,
> +					   atomic_long_read(&vma->vm_mm->context.id));
> +	else
> +		__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);

Various overly long lines (which are trivially avoided when doing the
right thing from the beginning).

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  2021-06-06  9:04 ` [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes guoren
@ 2021-06-06 14:39   ` Christoph Hellwig
  2021-06-06 15:08     ` Guo Ren
  2021-06-06 17:22   ` Nick Kossifidis
  1 sibling, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2021-06-06 14:39 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, maxime, drew, liush,
	lazyparser, wefu, linux-riscv, linux-kernel, linux-arch,
	linux-sunxi, Guo Ren, Christoph Hellwig

NAK, no SOC must ever mess with pagetable attributes.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  2021-06-06 14:39   ` Christoph Hellwig
@ 2021-06-06 15:08     ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 15:08 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren

On Sun, Jun 6, 2021 at 10:39 PM Christoph Hellwig <hch@lst.de> wrote:
>
> NAK, no SOC must ever mess with pagetable attributes.

I don't think it messes with page table attributes. _PAGE_BASE
contains variable won't cause any problem.

-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 10:50   ` Andre Przywara
@ 2021-06-06 15:32     ` Guo Ren
  2021-06-06 15:39       ` Jernej Škrabec
  0 siblings, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-06 15:32 UTC (permalink / raw)
  To: Andre Przywara
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

 ,

On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> On Sun,  6 Jun 2021 09:04:09 +0000
> guoren@kernel.org wrote:
>
> Hi,
>
> > From: liush <liush@allwinnertech.com>
> >
> > This is a temporary driver, only guaranteed to work on allwinner
> > D1. In order to ensure the developer's demand for network usage.
>
> That looks like some Allwinner BSP driver, please don't endorse code
> of this quality (just look at all that commented code and the attempt
> for compile-time configuration).
>
> > It only could work at 1Gps mode.
> >
> > The correct gmac driver should follow (I guess)
> > drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> >
> > If anyone is familiar with it and can help porting, I would be
> > very grateful.
>
> Have you tried compiling and using that driver? Ideally it should just
> work, Linux drivers are meant to be portable, by design. And the driver
> is already enabled by COMPILE_TEST.
It still needs some work with dwmac-sun8i.c glue layer, eg:
tx/rx-delay setting, clk & pinmux drivers.

The patch is just to help people using D1 with GMAC temporarily with
network function.

> But I guess you need some extra care to make the non-coherent DMA work?
> I haven't looked in detail, but are those new CMOs hooked into the
> generic DMA framework?
Yes, we have the simliar principle with arm & csky for non-coherent:
 - Using PTE attributes setting Using PTE attributes to support
_PAGE_IOREMAP & _PAGE_WRITECOMBINE
 - Using CMO instructions deal SYNC_DMA_FOR_CPU/DEVICE.

>
> Cheers,
> Andre
>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Tested-by: Guo Ren <guoren@kernel.org>
> > Signed-off-by: Guo Ren <guoren@kernel.org>
> > Cc: Maxime Ripard <mripard@kernel.org>
> > Cc: Corentin Labbe <clabbe@baylibre.com>
> > Cc: Samuel Holland <samuel@sholland.org>
> > Cc: Icenowy Zheng <icenowy@aosc.io>
> > Cc: LABBE Corentin <clabbe.montjoie@gmail.com>
> > Cc: Michael Walle <michael@walle.cc>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > ---
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |    2 +-
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |   16 +
> >  drivers/net/ethernet/Kconfig                       |    1 +
> >  drivers/net/ethernet/Makefile                      |    1 +
> >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240 ++++++++++++++++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
> >  drivers/net/phy/realtek.c                          |    2 +-
> >  10 files changed, 3232 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> >
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > index cd9f7c9..31b681d 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > @@ -11,7 +11,7 @@
> >       compatible = "allwinner,d1-nezha-kit";
> >
> >       chosen {
> > -             bootargs = "console=ttyS0,115200";
> > +             bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";
> >               stdout-path = &serial0;
> >       };
> >
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > index 11cd938..d317e19 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > @@ -80,5 +80,21 @@
> >                       clocks = <&dummy_apb>;
> >                       status = "disabled";
> >               };
> > +
> > +             eth@4500000 {
> > +                     compatible = "allwinner,sunxi-gmac";
> > +                     reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
> > +                     interrupts-extended = <&plic 0x3e 0x04>;
> > +                     interrupt-names = "gmacirq";
> > +                     device_type = "gmac0";
> > +                     phy-mode = "rgmii";
> > +                     use_ephy25m = <0x01>;
> > +                     tx-delay = <0x03>;
> > +                     rx-delay = <0x03>;
> > +                     gmac-power0;
> > +                     gmac-power1;
> > +                     gmac-power2;
> > +                     status = "okay";
> > +             };
> >       };
> >  };
> > diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> > index 1cdff1d..1f8e37c 100644
> > --- a/drivers/net/ethernet/Kconfig
> > +++ b/drivers/net/ethernet/Kconfig
> > @@ -18,6 +18,7 @@ config MDIO
> >  config SUNGEM_PHY
> >       tristate
> >
> > +source "drivers/net/ethernet/allwinnertmp/Kconfig"
> >  source "drivers/net/ethernet/3com/Kconfig"
> >  source "drivers/net/ethernet/actions/Kconfig"
> >  source "drivers/net/ethernet/adaptec/Kconfig"
> > diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
> > index cb3f908..3dacc0c 100644
> > --- a/drivers/net/ethernet/Makefile
> > +++ b/drivers/net/ethernet/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the Linux network Ethernet device drivers.
> >  #
> >
> > +obj-y += allwinnertmp/
> >  obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
> >  obj-$(CONFIG_NET_VENDOR_8390) += 8390/
> >  obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/
> > diff --git a/drivers/net/ethernet/allwinnertmp/Kconfig b/drivers/net/ethernet/allwinnertmp/Kconfig
> > new file mode 100644
> > index 00000000..4b7b378
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/Kconfig
> > @@ -0,0 +1,17 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +#
> > +# Allwinner device configuration
> > +#
> > +
> > +config SUNXI_GMAC
> > +     tristate "Allwinner GMAC support"
> > +     default y
> > +     depends on OF
> > +     select CRC32
> > +     select MII
> > +     select PHYLIB
> > +     help
> > +       Support for Allwinner Gigabit ethernet driver.
> > +
> > +       To compile this driver as a module, choose M here.  The module
> > +       will be called sunxi-gmac.
> > diff --git a/drivers/net/ethernet/allwinnertmp/Makefile b/drivers/net/ethernet/allwinnertmp/Makefile
> > new file mode 100644
> > index 00000000..1375dea
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/Makefile
> > @@ -0,0 +1,7 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +#
> > +# Makefile for the Allwinner device drivers.
> > +#
> > +
> > +obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o
> > +sunxi_gmac-objs := sunxi-gmac.o sunxi-gmac-ops.o
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > new file mode 100644
> > index 00000000..26ffd7f
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > @@ -0,0 +1,690 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.       See the
> > + * GNU General Public License for more details.
> > + */
> > +#include <linux/kernel.h>
> > +#include <linux/ctype.h>
> > +#include <linux/printk.h>
> > +#include <linux/io.h>
> > +#include "sunxi-gmac.h"
> > +
> > +/******************************************************************************
> > + *   sun8iw6 operations
> > + *****************************************************************************/
> > +#define GETH_BASIC_CTL0              0x00
> > +#define GETH_BASIC_CTL1              0x04
> > +#define GETH_INT_STA         0x08
> > +#define GETH_INT_EN          0x0C
> > +#define GETH_TX_CTL0         0x10
> > +#define GETH_TX_CTL1         0x14
> > +#define GETH_TX_FLOW_CTL     0x1C
> > +#define GETH_TX_DESC_LIST    0x20
> > +#define GETH_RX_CTL0         0x24
> > +#define GETH_RX_CTL1         0x28
> > +#define GETH_RX_DESC_LIST    0x34
> > +#define GETH_RX_FRM_FLT              0x38
> > +#define GETH_RX_HASH0                0x40
> > +#define GETH_RX_HASH1                0x44
> > +#define GETH_MDIO_ADDR               0x48
> > +#define GETH_MDIO_DATA               0x4C
> > +#define GETH_ADDR_HI(reg)    (0x50 + ((reg) << 3))
> > +#define GETH_ADDR_LO(reg)    (0x54 + ((reg) << 3))
> > +#define GETH_TX_DMA_STA              0xB0
> > +#define GETH_TX_CUR_DESC     0xB4
> > +#define GETH_TX_CUR_BUF              0xB8
> > +#define GETH_RX_DMA_STA              0xC0
> > +#define GETH_RX_CUR_DESC     0xC4
> > +#define GETH_RX_CUR_BUF              0xC8
> > +#define GETH_RGMII_STA               0xD0
> > +
> > +#define RGMII_IRQ            0x00000001
> > +
> > +#define      CTL0_LM                 0x02
> > +#define CTL0_DM                      0x01
> > +#define CTL0_SPEED           0x04
> > +
> > +#define BURST_LEN            0x3F000000
> > +#define RX_TX_PRI            0x02
> > +#define SOFT_RST             0x01
> > +
> > +#define TX_FLUSH             0x01
> > +#define TX_MD                        0x02
> > +#define TX_NEXT_FRM          0x04
> > +#define TX_TH                        0x0700
> > +
> > +#define RX_FLUSH             0x01
> > +#define RX_MD                        0x02
> > +#define RX_RUNT_FRM          0x04
> > +#define RX_ERR_FRM           0x08
> > +#define RX_TH                        0x0030
> > +
> > +#define TX_INT                       0x00001
> > +#define TX_STOP_INT          0x00002
> > +#define TX_UA_INT            0x00004
> > +#define TX_TOUT_INT          0x00008
> > +#define TX_UNF_INT           0x00010
> > +#define TX_EARLY_INT         0x00020
> > +#define RX_INT                       0x00100
> > +#define RX_UA_INT            0x00200
> > +#define RX_STOP_INT          0x00400
> > +#define RX_TOUT_INT          0x00800
> > +#define RX_OVF_INT           0x01000
> > +#define RX_EARLY_INT         0x02000
> > +#define LINK_STA_INT         0x10000
> > +
> > +#define DISCARD_FRAME        -1
> > +#define GOOD_FRAME   0
> > +#define CSUM_NONE    2
> > +#define LLC_SNAP     4
> > +
> > +#define SF_DMA_MODE          1
> > +
> > +/* Flow Control defines */
> > +#define FLOW_OFF     0
> > +#define FLOW_RX              1
> > +#define FLOW_TX              2
> > +#define FLOW_AUTO    (FLOW_TX | FLOW_RX)
> > +
> > +#define HASH_TABLE_SIZE 64
> > +#define PAUSE_TIME 0x200
> > +#define GMAC_MAX_UNICAST_ADDRESSES   8
> > +
> > +/* PHY address */
> > +#define PHY_ADDR             0x01
> > +#define PHY_DM                       0x0010
> > +#define PHY_AUTO_NEG         0x0020
> > +#define PHY_POWERDOWN                0x0080
> > +#define PHY_NEG_EN           0x1000
> > +
> > +#define MII_BUSY             0x00000001
> > +#define MII_WRITE            0x00000002
> > +#define MII_PHY_MASK         0x0000FFC0
> > +#define MII_CR_MASK          0x0000001C
> > +#define MII_CLK                      0x00000008
> > +/* bits 4 3 2 | AHB1 Clock   | MDC Clock
> > + * -------------------------------------------------------
> > + *      0 0 0 | 60 ~ 100 MHz | div-42
> > + *      0 0 1 | 100 ~ 150 MHz        | div-62
> > + *      0 1 0 | 20 ~ 35 MHz  | div-16
> > + *      0 1 1 | 35 ~ 60 MHz  | div-26
> > + *      1 0 0 | 150 ~ 250 MHz        | div-102
> > + *      1 0 1 | 250 ~ 300 MHz        | div-124
> > + *      1 1 x | Reserved     |
> > + */
> > +
> > +enum csum_insertion {
> > +     cic_dis         = 0, /* Checksum Insertion Control */
> > +     cic_ip          = 1, /* Only IP header */
> > +     cic_no_pse      = 2, /* IP header but not pseudoheader */
> > +     cic_full        = 3, /* IP header and pseudoheader */
> > +};
> > +
> > +struct gethdev {
> > +     void *iobase;
> > +     unsigned int ver;
> > +     unsigned int mdc_div;
> > +};
> > +
> > +static struct gethdev hwdev;
> > +
> > +/***************************************************************************
> > + * External interface
> > + **************************************************************************/
> > +/* Set a ring desc buffer */
> > +void desc_init_chain(struct dma_desc *desc, unsigned long addr, unsigned int size)
> > +{
> > +     /* In chained mode the desc3 points to the next element in the ring.
> > +      * The latest element has to point to the head.
> > +      */
> > +     int i;
> > +     struct dma_desc *p = desc;
> > +     unsigned long dma_phy = addr;
> > +
> > +     for (i = 0; i < (size - 1); i++) {
> > +             dma_phy += sizeof(struct dma_desc);
> > +             p->desc3 = (unsigned int)dma_phy;
> > +             /* Chain mode */
> > +             p->desc1.all |= (1 << 24);
> > +             p++;
> > +     }
> > +     p->desc1.all |= (1 << 24);
> > +     p->desc3 = (unsigned int)addr;
> > +}
> > +
> > +int sunxi_mdio_read(void *iobase, int phyaddr, int phyreg)
> > +{
> > +     unsigned int value = 0;
> > +
> > +     /* Mask the MDC_DIV_RATIO */
> > +     value |= ((hwdev.mdc_div & 0x07) << 20);
> > +     value |= (((phyaddr << 12) & (0x0001F000)) |
> > +                     ((phyreg << 4) & (0x000007F0)) |
> > +                     MII_BUSY);
> > +
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     writel(value, iobase + GETH_MDIO_ADDR);
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     return (int)readl(iobase + GETH_MDIO_DATA);
> > +}
> > +
> > +int sunxi_mdio_write(void *iobase, int phyaddr, int phyreg, unsigned short data)
> > +{
> > +     unsigned int value;
> > +
> > +     value = ((0x07 << 20) & readl(iobase + GETH_MDIO_ADDR)) |
> > +              (hwdev.mdc_div << 20);
> > +     value |= (((phyaddr << 12) & (0x0001F000)) |
> > +               ((phyreg << 4) & (0x000007F0))) |
> > +               MII_WRITE | MII_BUSY;
> > +
> > +     /* Wait until any existing MII operation is complete */
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     /* Set the MII address register to write */
> > +     writel(data, iobase + GETH_MDIO_DATA);
> > +     writel(value, iobase + GETH_MDIO_ADDR);
> > +
> > +     /* Wait until any existing MII operation is complete */
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mdio_reset(void *iobase)
> > +{
> > +     writel((4 << 2), iobase + GETH_MDIO_ADDR);
> > +     return 0;
> > +}
> > +
> > +void sunxi_set_link_mode(void *iobase, int duplex, int speed)
> > +{
> > +     unsigned int ctrl = readl(iobase + GETH_BASIC_CTL0);
> > +
> > +     if (!duplex)
> > +             ctrl &= ~CTL0_DM;
> > +     else
> > +             ctrl |= CTL0_DM;
> > +
> > +     switch (speed) {
> > +     case 1000:
> > +             ctrl &= ~0x0C;
> > +             break;
> > +     case 100:
> > +     case 10:
> > +     default:
> > +             ctrl |= 0x08;
> > +             if (speed == 100)
> > +                     ctrl |= 0x04;
> > +             else
> > +                     ctrl &= ~0x04;
> > +             break;
> > +     }
> > +
> > +     writel(ctrl, iobase + GETH_BASIC_CTL0);
> > +}
> > +
> > +void sunxi_mac_loopback(void *iobase, int enable)
> > +{
> > +     int reg;
> > +
> > +     reg = readl(iobase + GETH_BASIC_CTL0);
> > +     if (enable)
> > +             reg |= 0x02;
> > +     else
> > +             reg &= ~0x02;
> > +     writel(reg, iobase + GETH_BASIC_CTL0);
> > +}
> > +
> > +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause)
> > +{
> > +     unsigned int flow = 0;
> > +
> > +     if (fc & FLOW_RX) {
> > +             flow = readl(iobase + GETH_RX_CTL0);
> > +             flow |= 0x10000;
> > +             writel(flow, iobase + GETH_RX_CTL0);
> > +     }
> > +
> > +     if (fc & FLOW_TX) {
> > +             flow = readl(iobase + GETH_TX_FLOW_CTL);
> > +             flow |= 0x00001;
> > +             writel(flow, iobase + GETH_TX_FLOW_CTL);
> > +     }
> > +
> > +     if (duplex) {
> > +             flow = readl(iobase + GETH_TX_FLOW_CTL);
> > +             flow |= (pause << 4);
> > +             writel(flow, iobase + GETH_TX_FLOW_CTL);
> > +     }
> > +}
> > +
> > +int sunxi_int_status(void *iobase, struct geth_extra_stats *x)
> > +{
> > +     int ret = 0;
> > +     /* read the status register (CSR5) */
> > +     unsigned int intr_status;
> > +
> > +     intr_status = readl(iobase + GETH_RGMII_STA);
> > +     if (intr_status & RGMII_IRQ)
> > +             readl(iobase + GETH_RGMII_STA);
> > +
> > +     intr_status = readl(iobase + GETH_INT_STA);
> > +
> > +     /* ABNORMAL interrupts */
> > +     if (intr_status & TX_UNF_INT) {
> > +             ret = tx_hard_error_bump_tc;
> > +             x->tx_undeflow_irq++;
> > +     }
> > +     if (intr_status & TX_TOUT_INT) {
> > +             x->tx_jabber_irq++;
> > +     }
> > +     if (intr_status & RX_OVF_INT) {
> > +             x->rx_overflow_irq++;
> > +     }
> > +     if (intr_status & RX_UA_INT) {
> > +             x->rx_buf_unav_irq++;
> > +     }
> > +     if (intr_status & RX_STOP_INT) {
> > +             x->rx_process_stopped_irq++;
> > +     }
> > +     if (intr_status & RX_TOUT_INT) {
> > +             x->rx_watchdog_irq++;
> > +     }
> > +     if (intr_status & TX_EARLY_INT) {
> > +             x->tx_early_irq++;
> > +     }
> > +     if (intr_status & TX_STOP_INT) {
> > +             x->tx_process_stopped_irq++;
> > +             ret = tx_hard_error;
> > +     }
> > +
> > +     /* TX/RX NORMAL interrupts */
> > +     if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) {
> > +             x->normal_irq_n++;
> > +             if (intr_status & (TX_INT | RX_INT))
> > +                     ret = handle_tx_rx;
> > +     }
> > +     /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
> > +     writel(intr_status & 0x3FFF, iobase + GETH_INT_STA);
> > +
> > +     return ret;
> > +}
> > +
> > +void sunxi_start_rx(void *iobase, unsigned long rxbase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Write the base address of Rx descriptor lists into registers */
> > +     writel(rxbase, iobase + GETH_RX_DESC_LIST);
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     value |= 0x40000000;
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_stop_rx(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     value &= ~0x40000000;
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_start_tx(void *iobase, unsigned long txbase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Write the base address of Tx descriptor lists into registers */
> > +     writel(txbase, iobase + GETH_TX_DESC_LIST);
> > +
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     value |= 0x40000000;
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +void sunxi_stop_tx(void *iobase)
> > +{
> > +     unsigned int value = readl(iobase + GETH_TX_CTL1);
> > +
> > +     value &= ~0x40000000;
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +static int sunxi_dma_init(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Burst should be 8 */
> > +     value = (8 << 24);
> > +
> > +#ifdef CONFIG_GMAC_DA
> > +     value |= RX_TX_PRI;     /* Rx has priority over tx */
> > +#endif
> > +     writel(value, iobase + GETH_BASIC_CTL1);
> > +
> > +     /* Mask interrupts by writing to CSR7 */
> > +     writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mac_init(void *iobase, int txmode, int rxmode)
> > +{
> > +     unsigned int value;
> > +
> > +     sunxi_dma_init(iobase);
> > +
> > +     /* Initialize the core component */
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value |= (1 << 30);     /* Jabber Disable */
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value |= (1 << 27);     /* Enable CRC & IPv4 Header Checksum */
> > +     value |= (1 << 28);     /* Automatic Pad/CRC Stripping */
> > +     value |= (1 << 29);     /* Jumbo Frame Enable */
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +
> > +     writel((hwdev.mdc_div << 20), iobase + GETH_MDIO_ADDR); /* MDC_DIV_RATIO */
> > +
> > +     /* Set the Rx&Tx mode */
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     if (txmode == SF_DMA_MODE) {
> > +             /* Transmit COE type 2 cannot be done in cut-through mode. */
> > +             value |= TX_MD;
> > +             /* Operating on second frame increase the performance
> > +              * especially when transmit store-and-forward is used.
> > +              */
> > +             value |= TX_NEXT_FRM;
> > +     } else {
> > +             value &= ~TX_MD;
> > +             value &= ~TX_TH;
> > +             /* Set the transmit threshold */
> > +             if (txmode <= 64)
> > +                     value |= 0x00000000;
> > +             else if (txmode <= 128)
> > +                     value |= 0x00000100;
> > +             else if (txmode <= 192)
> > +                     value |= 0x00000200;
> > +             else
> > +                     value |= 0x00000300;
> > +     }
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     if (rxmode == SF_DMA_MODE) {
> > +             value |= RX_MD;
> > +     } else {
> > +             value &= ~RX_MD;
> > +             value &= ~RX_TH;
> > +             if (rxmode <= 32)
> > +                     value |= 0x10;
> > +             else if (rxmode <= 64)
> > +                     value |= 0x00;
> > +             else if (rxmode <= 96)
> > +                     value |= 0x20;
> > +             else
> > +                     value |= 0x30;
> > +     }
> > +
> > +     /* Forward frames with error and undersized good frame. */
> > +     value |= (RX_ERR_FRM | RX_RUNT_FRM);
> > +
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +
> > +     return 0;
> > +}
> > +
> > +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high)
> > +{
> > +     writel(high, iobase + GETH_RX_HASH0);
> > +     writel(low, iobase + GETH_RX_HASH1);
> > +}
> > +
> > +void sunxi_set_filter(void *iobase, unsigned long flags)
> > +{
> > +     int tmp_flags = 0;
> > +
> > +     tmp_flags |= ((flags >> 31) |
> > +                     ((flags >> 9) & 0x00000002) |
> > +                     ((flags << 1) & 0x00000010) |
> > +                     ((flags >> 3) & 0x00000060) |
> > +                     ((flags << 7) & 0x00000300) |
> > +                     ((flags << 6) & 0x00003000) |
> > +                     ((flags << 12) & 0x00030000) |
> > +                     (flags << 31));
> > +
> > +     writel(tmp_flags, iobase + GETH_RX_FRM_FLT);
> > +}
> > +
> > +void sunxi_set_umac(void *iobase, unsigned char *addr, int index)
> > +{
> > +     unsigned long data;
> > +
> > +     data = (addr[5] << 8) | addr[4];
> > +     writel(data, iobase + GETH_ADDR_HI(index));
> > +     data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
> > +     writel(data, iobase + GETH_ADDR_LO(index));
> > +}
> > +
> > +void sunxi_mac_enable(void *iobase)
> > +{
> > +     unsigned long value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value |= (1 << 31);
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value |= (1 << 31);
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +}
> > +
> > +void sunxi_mac_disable(void *iobase)
> > +{
> > +     unsigned long value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value &= ~(1 << 31);
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value &= ~(1 << 31);
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +}
> > +
> > +void sunxi_tx_poll(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     writel(value | 0x80000000, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +void sunxi_rx_poll(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     writel(value | 0x80000000, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_int_enable(void *iobase)
> > +{
> > +     writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> > +}
> > +
> > +void sunxi_int_disable(void *iobase)
> > +{
> > +     writel(0, iobase + GETH_INT_EN);
> > +}
> > +
> > +void desc_buf_set(struct dma_desc *desc, unsigned long paddr, int size)
> > +{
> > +     desc->desc1.all &= (~((1 << 11) - 1));
> > +     desc->desc1.all |= (size & ((1 << 11) - 1));
> > +     desc->desc2 = paddr;
> > +}
> > +
> > +void desc_set_own(struct dma_desc *desc)
> > +{
> > +     desc->desc0.all |= 0x80000000;
> > +}
> > +
> > +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert)
> > +{
> > +     struct dma_desc *desc = first;
> > +
> > +     first->desc1.tx.first_sg = 1;
> > +     end->desc1.tx.last_seg = 1;
> > +     end->desc1.tx.interrupt = 1;
> > +
> > +     if (csum_insert)
> > +             do {
> > +                     desc->desc1.tx.cic = 3;
> > +                     desc++;
> > +             } while (desc <= end);
> > +}
> > +
> > +void desc_init(struct dma_desc *desc)
> > +{
> > +     desc->desc1.all = 0;
> > +     desc->desc2  = 0;
> > +
> > +     desc->desc1.all |= (1 << 24);
> > +}
> > +
> > +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> > +{
> > +     int ret = 0;
> > +
> > +     if (desc->desc0.tx.under_err) {
> > +             x->tx_underflow++;
> > +             ret = -1;
> > +     }
> > +     if (desc->desc0.tx.no_carr) {
> > +             x->tx_carrier++;
> > +             ret = -1;
> > +     }
> > +     if (desc->desc0.tx.loss_carr) {
> > +             x->tx_losscarrier++;
> > +             ret = -1;
> > +     }
> > +
> > +#if 0
> > +     if ((desc->desc0.tx.ex_deferral) ||
> > +                     (desc->desc0.tx.ex_coll) ||
> > +                     (desc->desc0.tx.late_coll))
> > +             stats->collisions += desc->desc0.tx.coll_cnt;
> > +#endif
> > +
> > +     if (desc->desc0.tx.deferred)
> > +             x->tx_deferred++;
> > +
> > +     return ret;
> > +}
> > +
> > +int desc_buf_get_len(struct dma_desc *desc)
> > +{
> > +     return (desc->desc1.all & ((1 << 11) - 1));
> > +}
> > +
> > +int desc_buf_get_addr(struct dma_desc *desc)
> > +{
> > +     return desc->desc2;
> > +}
> > +
> > +int desc_rx_frame_len(struct dma_desc *desc)
> > +{
> > +     return desc->desc0.rx.frm_len;
> > +}
> > +
> > +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> > +{
> > +     int ret = good_frame;
> > +
> > +     if (desc->desc0.rx.last_desc == 0) {
> > +             return discard_frame;
> > +     }
> > +
> > +     if (desc->desc0.rx.err_sum) {
> > +             if (desc->desc0.rx.desc_err)
> > +                     x->rx_desc++;
> > +
> > +             if (desc->desc0.rx.sou_filter)
> > +                     x->sa_filter_fail++;
> > +
> > +             if (desc->desc0.rx.over_err)
> > +                     x->overflow_error++;
> > +
> > +             if (desc->desc0.rx.ipch_err)
> > +                     x->ipc_csum_error++;
> > +
> > +             if (desc->desc0.rx.late_coll)
> > +                     x->rx_collision++;
> > +
> > +             if (desc->desc0.rx.crc_err)
> > +                     x->rx_crc++;
> > +
> > +             ret = discard_frame;
> > +     }
> > +
> > +     if (desc->desc0.rx.len_err) {
> > +             ret = discard_frame;
> > +     }
> > +     if (desc->desc0.rx.mii_err) {
> > +             ret = discard_frame;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +int desc_get_own(struct dma_desc *desc)
> > +{
> > +     return desc->desc0.all & 0x80000000;
> > +}
> > +
> > +int desc_get_tx_ls(struct dma_desc *desc)
> > +{
> > +     return desc->desc1.tx.last_seg;
> > +}
> > +
> > +int sunxi_geth_register(void *iobase, int version, unsigned int div)
> > +{
> > +     hwdev.ver = version;
> > +     hwdev.iobase = iobase;
> > +     hwdev.mdc_div = div;
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mac_reset(void *iobase, void (*delay)(int), int n)
> > +{
> > +     unsigned int value;
> > +
> > +     /* DMA SW reset */
> > +     value = readl(iobase + GETH_BASIC_CTL1);
> > +     value |= SOFT_RST;
> > +     writel(value, iobase + GETH_BASIC_CTL1);
> > +
> > +     delay(n);
> > +
> > +     return !!(readl(iobase + GETH_BASIC_CTL1) & SOFT_RST);
> > +}
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > new file mode 100644
> > index 00000000..0c67877
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > @@ -0,0 +1,2240 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.       See the
> > + * GNU General Public License for more details.
> > + */
> > +//#include <linux/clk.h>
> > +//#include <linux/clk-provider.h>
> > +#include <linux/mii.h>
> > +#include <linux/gpio.h>
> > +#include <linux/crc32.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/platform_device.h>
> > +//#include <linux/pinctrl/consumer.h>
> > +//#include <linux/pinctrl/pinctrl.h>
> > +#include <linux/crypto.h>
> > +#include <crypto/algapi.h>
> > +#include <crypto/hash.h>
> > +#include <linux/err.h>
> > +#include <linux/scatterlist.h>
> > +//#include <linux/regulator/consumer.h>
> > +#include <linux/of_net.h>
> > +//#include <linux/of_gpio.h>
> > +#include <linux/io.h>
> > +//#include <linux/sunxi-sid.h>
> > +//#include <linux/sunxi-gpio.h>
> > +//#include <linux/reset.h>
> > +#include "sunxi-gmac.h"
> > +
> > +#define SUNXI_GMAC_VERSION "1.0.0"
> > +
> > +#define DMA_DESC_RX  256
> > +#define DMA_DESC_TX  256
> > +#define BUDGET               (dma_desc_rx / 4)
> > +#define TX_THRESH    (dma_desc_tx / 4)
> > +
> > +#define HASH_TABLE_SIZE      64
> > +#define MAX_BUF_SZ   (SZ_2K - 1)
> > +
> > +#define POWER_CHAN_NUM       3
> > +
> > +#undef PKT_DEBUG
> > +#undef DESC_PRINT
> > +
> > +#define circ_cnt(head, tail, size) (((head) > (tail)) ? \
> > +                                     ((head) - (tail)) : \
> > +                                     ((head) - (tail)) & ((size) - 1))
> > +
> > +#define circ_space(head, tail, size) circ_cnt((tail), ((head) + 1), (size))
> > +
> > +#define circ_inc(n, s) (((n) + 1) % (s))
> > +
> > +#define GETH_MAC_ADDRESS "00:00:00:00:00:00"
> > +static char *mac_str = GETH_MAC_ADDRESS;
> > +module_param(mac_str, charp, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(mac_str, "MAC Address String.(xx:xx:xx:xx:xx:xx)");
> > +
> > +static int rxmode = 1;
> > +module_param(rxmode, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(rxmode, "DMA threshold control value");
> > +
> > +static int txmode = 1;
> > +module_param(txmode, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(txmode, "DMA threshold control value");
> > +
> > +static int pause = 0x400;
> > +module_param(pause, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(pause, "Flow Control Pause Time");
> > +
> > +#define TX_TIMEO     5000
> > +static int watchdog = TX_TIMEO;
> > +module_param(watchdog, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
> > +
> > +static int dma_desc_rx = DMA_DESC_RX;
> > +module_param(dma_desc_rx, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "The number of receive's descriptors");
> > +
> > +static int dma_desc_tx = DMA_DESC_TX;
> > +module_param(dma_desc_tx, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "The number of transmit's descriptors");
> > +
> > +/* - 0: Flow Off
> > + * - 1: Rx Flow
> > + * - 2: Tx Flow
> > + * - 3: Rx & Tx Flow
> > + */
> > +static int flow_ctrl;
> > +module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(flow_ctrl, "Flow control [0: off, 1: rx, 2: tx, 3: both]");
> > +
> > +struct geth_priv {
> > +     struct dma_desc *dma_tx;
> > +     struct sk_buff **tx_sk;
> > +     unsigned int tx_clean;
> > +     unsigned int tx_dirty;
> > +     dma_addr_t dma_tx_phy;
> > +
> > +     unsigned long buf_sz;
> > +
> > +     struct dma_desc *dma_rx;
> > +     struct sk_buff **rx_sk;
> > +     unsigned int rx_clean;
> > +     unsigned int rx_dirty;
> > +     dma_addr_t dma_rx_phy;
> > +
> > +     struct net_device *ndev;
> > +     struct device *dev;
> > +     struct napi_struct napi;
> > +
> > +     struct geth_extra_stats xstats;
> > +
> > +     struct mii_bus *mii;
> > +     int link;
> > +     int speed;
> > +     int duplex;
> > +#define INT_PHY 0
> > +#define EXT_PHY 1
> > +     int phy_ext;
> > +     phy_interface_t phy_interface;
> > +
> > +     void __iomem *base;
> > +     void __iomem *base_phy;
> > +/*
> > +     struct clk *geth_clk;
> > +     struct clk *ephy_clk;
> > +     struct reset_control *reset;
> > +     struct pinctrl *pinctrl;
> > +*/
> > +     struct regulator *gmac_power[POWER_CHAN_NUM];
> > +     bool is_suspend;
> > +     int phyrst;
> > +     u8  rst_active_low;
> > +     /* definition spinlock */
> > +     spinlock_t lock;
> > +     spinlock_t tx_lock;
> > +
> > +     /* whether using ephy_clk */
> > +     int use_ephy_clk;
> > +     int phy_addr;
> > +
> > +     /* adjust transmit clock delay, value: 0~7 */
> > +     /* adjust receive clock delay, value: 0~31 */
> > +     unsigned int tx_delay;
> > +     unsigned int rx_delay;
> > +
> > +     /* resume work */
> > +     struct work_struct eth_work;
> > +};
> > +
> > +static u64 geth_dma_mask = DMA_BIT_MASK(32);
> > +
> > +void sunxi_udelay(int n)
> > +{
> > +     udelay(n);
> > +}
> > +
> > +static int geth_stop(struct net_device *ndev);
> > +static int geth_open(struct net_device *ndev);
> > +static void geth_tx_complete(struct geth_priv *priv);
> > +static void geth_rx_refill(struct net_device *ndev);
> > +
> > +#ifdef CONFIG_GETH_ATTRS
> > +static ssize_t adjust_bgs_show(struct device *dev, struct device_attribute *attr, char *buf)
> > +{
> > +     int value = 0;
> > +     u32 efuse_value;
> > +     struct net_device *ndev = to_net_dev(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value = readl(priv->base_phy) >> 28;
> > +             if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     value = value - ((efuse_value >> 24) & 0x0F);
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }
> > +
> > +     return sprintf(buf, "bgs: %d\n", value);
> > +}
> > +
> > +static ssize_t adjust_bgs_write(struct device *dev, struct device_attribute *attr,
> > +                             const char *buf, size_t count)
> > +{
> > +     unsigned int out = 0;
> > +     struct net_device *ndev = to_net_dev(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     u32 clk_value = readl(priv->base_phy);
> > +     u32 efuse_value;
> > +
> > +     out = simple_strtoul(buf, NULL, 10);
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             clk_value &= ~(0xF << 28);
> > +             if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     clk_value |= (((efuse_value >> 24) & 0x0F) + out) << 28;
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }
> > +
> > +     writel(clk_value, priv->base_phy);
> > +
> > +     return count;
> > +}
> > +
> > +static struct device_attribute adjust_reg[] = {
> > +     __ATTR(adjust_bgs, 0664, adjust_bgs_show, adjust_bgs_write),
> > +};
> > +
> > +static int geth_create_attrs(struct net_device *ndev)
> > +{
> > +     int j, ret;
> > +
> > +     for (j = 0; j < ARRAY_SIZE(adjust_reg); j++) {
> > +             ret = device_create_file(&ndev->dev, &adjust_reg[j]);
> > +             if (ret)
> > +                     goto sysfs_failed;
> > +     }
> > +     goto succeed;
> > +
> > +sysfs_failed:
> > +     while (j--)
> > +             device_remove_file(&ndev->dev, &adjust_reg[j]);
> > +succeed:
> > +     return ret;
> > +}
> > +#endif
> > +
> > +#ifdef DEBUG
> > +static void desc_print(struct dma_desc *desc, int size)
> > +{
> > +#ifdef DESC_PRINT
> > +     int i;
> > +
> > +     for (i = 0; i < size; i++) {
> > +             u32 *x = (u32 *)(desc + i);
> > +
> > +             pr_info("\t%d [0x%08lx]: %08x %08x %08x %08x\n",
> > +                     i, (unsigned long)(&desc[i]),
> > +                     x[0], x[1], x[2], x[3]);
> > +     }
> > +     pr_info("\n");
> > +#endif
> > +}
> > +#endif
> > +
> > +static ssize_t extra_tx_stats_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "tx_underflow: %lu\ntx_carrier: %lu\n"
> > +                     "tx_losscarrier: %lu\nvlan_tag: %lu\n"
> > +                     "tx_deferred: %lu\ntx_vlan: %lu\n"
> > +                     "tx_jabber: %lu\ntx_frame_flushed: %lu\n"
> > +                     "tx_payload_error: %lu\ntx_ip_header_error: %lu\n\n",
> > +                     priv->xstats.tx_underflow, priv->xstats.tx_carrier,
> > +                     priv->xstats.tx_losscarrier, priv->xstats.vlan_tag,
> > +                     priv->xstats.tx_deferred, priv->xstats.tx_vlan,
> > +                     priv->xstats.tx_jabber, priv->xstats.tx_frame_flushed,
> > +                     priv->xstats.tx_payload_error, priv->xstats.tx_ip_header_error);
> > +}
> > +static DEVICE_ATTR(extra_tx_stats, 0444, extra_tx_stats_show, NULL);
> > +
> > +static ssize_t extra_rx_stats_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "rx_desc: %lu\nsa_filter_fail: %lu\n"
> > +                     "overflow_error: %lu\nipc_csum_error: %lu\n"
> > +                     "rx_collision: %lu\nrx_crc: %lu\n"
> > +                     "dribbling_bit: %lu\nrx_length: %lu\n"
> > +                     "rx_mii: %lu\nrx_multicast: %lu\n"
> > +                     "rx_gmac_overflow: %lu\nrx_watchdog: %lu\n"
> > +                     "da_rx_filter_fail: %lu\nsa_rx_filter_fail: %lu\n"
> > +                     "rx_missed_cntr: %lu\nrx_overflow_cntr: %lu\n"
> > +                     "rx_vlan: %lu\n\n",
> > +                     priv->xstats.rx_desc, priv->xstats.sa_filter_fail,
> > +                     priv->xstats.overflow_error, priv->xstats.ipc_csum_error,
> > +                     priv->xstats.rx_collision, priv->xstats.rx_crc,
> > +                     priv->xstats.dribbling_bit, priv->xstats.rx_length,
> > +                     priv->xstats.rx_mii, priv->xstats.rx_multicast,
> > +                     priv->xstats.rx_gmac_overflow, priv->xstats.rx_length,
> > +                     priv->xstats.da_rx_filter_fail, priv->xstats.sa_rx_filter_fail,
> > +                     priv->xstats.rx_missed_cntr, priv->xstats.rx_overflow_cntr,
> > +                     priv->xstats.rx_vlan);
> > +}
> > +static DEVICE_ATTR(extra_rx_stats, 0444, extra_rx_stats_show, NULL);
> > +
> > +static ssize_t gphy_test_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "Usage:\necho [0/1/2/3/4] > gphy_test\n"
> > +                     "0 - Normal Mode\n"
> > +                     "1 - Transmit Jitter Test\n"
> > +                     "2 - Transmit Jitter Test(MASTER mode)\n"
> > +                     "3 - Transmit Jitter Test(SLAVE mode)\n"
> > +                     "4 - Transmit Distortion Test\n\n");
> > +}
> > +
> > +static ssize_t gphy_test_store(struct device *dev,
> > +             struct device_attribute *attr, const char *buf, size_t count)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     u16 value = 0;
> > +     int ret = 0;
> > +     u16 data = 0;
> > +
> > +     if (!dev) {
> > +             pr_err("Argument is invalid\n");
> > +             return count;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return count;
> > +     }
> > +
> > +     data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000);
> > +
> > +     ret = kstrtou16(buf, 0, &value);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (value >= 0 && value <= 4) {
> > +             data &= ~(0x7 << 13);
> > +             data |= value << 13;
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_CTRL1000, data);
> > +             pr_info("Set MII_CTRL1000(0x09) Reg: 0x%x\n", data);
> > +     } else {
> > +             pr_info("unknown value (%d)\n", value);
> > +     }
> > +
> > +     return count;
> > +}
> > +
> > +static DEVICE_ATTR(gphy_test, 0664, gphy_test_show, gphy_test_store);
> > +
> > +static ssize_t mii_reg_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv = NULL;
> > +
> > +     if (dev == NULL) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     ndev = dev_get_drvdata(dev);
> > +     if (ndev == NULL) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     priv = netdev_priv(ndev);
> > +     if (priv == NULL) {
> > +             pr_err("geth_priv is null\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_warn("eth is down!\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf,
> > +             "Current MII Registers:\n"
> > +             "BMCR[0x%02x] = 0x%04x,\t\tBMSR[0x%02x] = 0x%04x,\t\tPHYSID1[0x%02x] = 0x%04x\n"
> > +             "PHYSID2[0x%02x] = 0x%04x,\t\tADVERTISE[0x%02x] = 0x%04x,\tLPA[0x%02x] = 0x%04x\n"
> > +             "EXPANSION[0x%02x] = 0x%04x,\tCTRL1000[0x%02x] = 0x%04x,\tSTAT1000[0x%02x] = 0x%04x\n",
> > +             MII_BMCR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR),
> > +             MII_BMSR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMSR),
> > +             MII_PHYSID1, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID1),
> > +             MII_PHYSID2, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID2),
> > +             MII_ADVERTISE, sunxi_mdio_read(priv->base, priv->phy_addr, MII_ADVERTISE),
> > +             MII_LPA, sunxi_mdio_read(priv->base, priv->phy_addr, MII_LPA),
> > +             MII_EXPANSION, sunxi_mdio_read(priv->base, priv->phy_addr, MII_EXPANSION),
> > +             MII_CTRL1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000),
> > +             MII_STAT1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_STAT1000));
> > +}
> > +static DEVICE_ATTR(mii_reg, 0444, mii_reg_show, NULL);
> > +
> > +static ssize_t loopback_test_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     return sprintf(buf, "Usage:\necho [0/1/2] > loopback_test\n"
> > +                     "0 - Normal Mode\n"
> > +                     "1 - Mac loopback test mode\n"
> > +                     "2 - Phy loopback test mode\n");
> > +}
> > +
> > +static ssize_t loopback_test_store(struct device *dev,
> > +             struct device_attribute *attr, const char *buf, size_t count)
> > +{
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv = NULL;
> > +     u16 value = 0;
> > +     int ret = 0;
> > +     u16 data = 0;
> > +
> > +     if (dev == NULL) {
> > +             pr_err("Argment is invalid\n");
> > +             return count;
> > +     }
> > +
> > +     ndev = dev_get_drvdata(dev);
> > +     if (ndev == NULL) {
> > +             pr_err("Net device is null\n");
> > +             return count;
> > +     }
> > +
> > +     priv = netdev_priv(ndev);
> > +     if (priv == NULL) {
> > +             pr_err("geth_priv is null\n");
> > +             return count;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_warn("eth is down!\n");
> > +             return count;
> > +     }
> > +
> > +     ret = kstrtou16(buf, 0, &value);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (value == 0) { /* normal mode */
> > +             /* clear mac loopback */
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +             /* clear phy loopback */
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> > +     } else if (value == 1) { /* mac loopback test mode */
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> > +
> > +             sunxi_mac_loopback(priv->base, 1);
> > +     } else if (value == 2) { /* phy loopback test mode */
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data | BMCR_LOOPBACK);
> > +     } else {
> > +             pr_err("Undefined value (%d)\n", value);
> > +     }
> > +
> > +     return count;
> > +}
> > +static DEVICE_ATTR(loopback_test, 0664, loopback_test_show, loopback_test_store);
> > +
> > +static int geth_power_on(struct geth_priv *priv)
> > +{
> > +     int value;
> > +
> > +     value = readl(priv->base_phy);
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value |= (1 << 15);
> > +             value &= ~(1 << 16);
> > +             value |= (3 << 17);
> > +     } else {
> > +             value &= ~(1 << 15);
> > +/*
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     if (regulator_enable(priv->gmac_power[i]) != 0) {
> > +                             pr_err("gmac-power%d enable error\n", i);
> > +                             return -EINVAL;
> > +                     }
> > +             }
> > +*/
> > +     }
> > +
> > +     writel(value, priv->base_phy);
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_power_off(struct geth_priv *priv)
> > +{
> > +     int value;
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value = readl(priv->base_phy);
> > +             value |= (1 << 16);
> > +             writel(value, priv->base_phy);
> > +     } else {
> > +/*
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_disable(priv->gmac_power[i]);
> > +             }
> > +*/
> > +     }
> > +}
> > +
> > +/* PHY interface operations */
> > +static int geth_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     return (int)sunxi_mdio_read(priv->base,  phyaddr, phyreg);
> > +}
> > +
> > +static int geth_mdio_write(struct mii_bus *bus, int phyaddr,
> > +                        int phyreg, u16 data)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     sunxi_mdio_write(priv->base, phyaddr, phyreg, data);
> > +
> > +     return 0;
> > +}
> > +
> > +static int geth_mdio_reset(struct mii_bus *bus)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     return sunxi_mdio_reset(priv->base);
> > +}
> > +
> > +static void geth_adjust_link(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +     unsigned long flags;
> > +     int new_state = 0;
> > +
> > +     if (!phydev)
> > +             return;
> > +
> > +     spin_lock_irqsave(&priv->lock, flags);
> > +     if (phydev->link) {
> > +             /* Now we make sure that we can be in full duplex mode.
> > +              * If not, we operate in half-duplex mode.
> > +              */
> > +             if (phydev->duplex != priv->duplex) {
> > +                     new_state = 1;
> > +                     priv->duplex = phydev->duplex;
> > +             }
> > +             /* Flow Control operation */
> > +             if (phydev->pause)
> > +                     sunxi_flow_ctrl(priv->base, phydev->duplex,
> > +                                     flow_ctrl, pause);
> > +
> > +             if (phydev->speed != priv->speed) {
> > +                     new_state = 1;
> > +                     priv->speed = phydev->speed;
> > +             }
> > +
> > +             if (priv->link == 0) {
> > +                     new_state = 1;
> > +                     priv->link = phydev->link;
> > +             }
> > +
> > +             if (new_state)
> > +                     sunxi_set_link_mode(priv->base, priv->duplex, priv->speed);
> > +
> > +#ifdef LOOPBACK_DEBUG
> > +             phydev->state = PHY_FORCING;
> > +#endif
> > +
> > +     } else if (priv->link != phydev->link) {
> > +             new_state = 1;
> > +             priv->link = 0;
> > +             priv->speed = 0;
> > +             priv->duplex = -1;
> > +     }
> > +
> > +     if (new_state)
> > +             phy_print_status(phydev);
> > +
> > +     spin_unlock_irqrestore(&priv->lock, flags);
> > +}
> > +
> > +static int geth_phy_init(struct net_device *ndev)
> > +{
> > +     int value;
> > +     struct mii_bus *new_bus;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +
> > +     /* Fixup the phy interface type */
> > +     if (priv->phy_ext == INT_PHY) {
> > +             priv->phy_interface = PHY_INTERFACE_MODE_MII;
> > +     } else {
> > +             /* If config gpio to reset the phy device, we should reset it */
> > +             /*
> > +             if (gpio_is_valid(priv->phyrst)) {
> > +                     gpio_direction_output(priv->phyrst,
> > +                                     priv->rst_active_low);
> > +                     msleep(50);
> > +                     gpio_direction_output(priv->phyrst,
> > +                                     !priv->rst_active_low);
> > +                     msleep(50);
> > +             }
> > +             */
> > +     }
> > +
> > +     if (priv->is_suspend && phydev)
> > +             goto resume;
> > +
> > +     new_bus = mdiobus_alloc();
> > +     if (!new_bus) {
> > +             netdev_err(ndev, "Failed to alloc new mdio bus\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     new_bus->name = dev_name(priv->dev);
> > +     new_bus->read = &geth_mdio_read;
> > +     new_bus->write = &geth_mdio_write;
> > +     new_bus->reset = &geth_mdio_reset;
> > +     snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0);
> > +
> > +     new_bus->parent = priv->dev;
> > +     new_bus->priv = ndev;
> > +
> > +     if (mdiobus_register(new_bus)) {
> > +             pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
> > +             goto reg_fail;
> > +     }
> > +
> > +     priv->mii = new_bus;
> > +
> > +     {
> > +             int addr;
> > +
> > +             for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
> > +                     struct phy_device *phydev_tmp = mdiobus_get_phy(new_bus, addr);
> > +
> > +                     if (phydev_tmp && (phydev_tmp->phy_id != 0x00)) {
> > +                             phydev = phydev_tmp;
> > +                             priv->phy_addr = addr;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +
> > +     if (!phydev) {
> > +             netdev_err(ndev, "No PHY found!\n");
> > +             goto err;
> > +     }
> > +
> > +     phydev->irq = PHY_POLL;
> > +
> > +     value = phy_connect_direct(ndev, phydev, &geth_adjust_link, priv->phy_interface);
> > +     if (value) {
> > +             netdev_err(ndev, "Could not attach to PHY\n");
> > +             goto err;
> > +     } else {
> > +             netdev_info(ndev, "%s: Type(%d) PHY ID %08x at %d IRQ %s (%s)\n",
> > +                         ndev->name, phydev->interface, phydev->phy_id,
> > +                         phydev->mdio.addr, "poll", dev_name(&phydev->mdio.dev));
> > +     }
> > +
> > +     //phydev->supported &= PHY_GBIT_FEATURES;
> > +     phydev->is_gigabit_capable = 1;
> > +     //phydev->advertising = phydev->supported;
> > +
> > +resume:
> > +     phy_write(phydev, MII_BMCR, BMCR_RESET);
> > +     while (BMCR_RESET & phy_read(phydev, MII_BMCR))
> > +             msleep(30);
> > +
> > +     value = phy_read(phydev, MII_BMCR);
> > +     phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             /* EPHY Initial */
> > +             phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
> > +             phy_write(phydev, 0x12, 0x4824); /* Disable APS */
> > +             phy_write(phydev, 0x1f, 0x0200); /* switchto page 2 */
> > +             phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
> > +             phy_write(phydev, 0x1f, 0x0600); /* switchto page 6 */
> > +             phy_write(phydev, 0x14, 0x708F); /* PHYAFE TX optimization */
> > +             phy_write(phydev, 0x19, 0x0000);
> > +             phy_write(phydev, 0x13, 0xf000); /* PHYAFE RX optimization */
> > +             phy_write(phydev, 0x15, 0x1530);
> > +             phy_write(phydev, 0x1f, 0x0800); /* switch to page 8 */
> > +             phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
> > +             phy_write(phydev, 0x1f, 0x0100); /* switchto page 1 */
> > +             /* reg 0x17 bit3,set 0 to disable iEEE */
> > +             phy_write(phydev, 0x17, phy_read(phydev, 0x17) & (~(1<<3)));
> > +             phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
> > +     }
> > +     if (priv->is_suspend)
> > +             phy_init_hw(phydev);
> > +
> > +     return 0;
> > +
> > +err:
> > +     mdiobus_unregister(new_bus);
> > +reg_fail:
> > +     mdiobus_free(new_bus);
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int geth_phy_release(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +     int value = 0;
> > +
> > +     /* Stop and disconnect the PHY */
> > +     if (phydev)
> > +             phy_stop(phydev);
> > +
> > +     priv->link = PHY_DOWN;
> > +     priv->speed = 0;
> > +     priv->duplex = -1;
> > +
> > +     if (phydev) {
> > +             value = phy_read(phydev, MII_BMCR);
> > +             phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN));
> > +     }
> > +
> > +     if (priv->is_suspend)
> > +             return 0;
> > +
> > +     if (phydev) {
> > +             phy_disconnect(phydev);
> > +             ndev->phydev = NULL;
> > +     }
> > +
> > +     if (priv->mii) {
> > +             mdiobus_unregister(priv->mii);
> > +             priv->mii->priv = NULL;
> > +             mdiobus_free(priv->mii);
> > +             priv->mii = NULL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_rx_refill(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct dma_desc *desc;
> > +     struct sk_buff *sk = NULL;
> > +     dma_addr_t paddr;
> > +
> > +     while (circ_space(priv->rx_clean, priv->rx_dirty, dma_desc_rx) > 0) {
> > +             int entry = priv->rx_clean;
> > +
> > +             /* Find the dirty's desc and clean it */
> > +             desc = priv->dma_rx + entry;
> > +
> > +             if (priv->rx_sk[entry] == NULL) {
> > +                     sk = netdev_alloc_skb_ip_align(ndev, priv->buf_sz);
> > +
> > +                     if (unlikely(sk == NULL))
> > +                             break;
> > +
> > +                     priv->rx_sk[entry] = sk;
> > +                     paddr = dma_map_single(priv->dev, sk->data,
> > +                                            priv->buf_sz, DMA_FROM_DEVICE);
> > +                     desc_buf_set(desc, paddr, priv->buf_sz);
> > +             }
> > +
> > +             /* sync memery */
> > +             wmb();
> > +             desc_set_own(desc);
> > +             priv->rx_clean = circ_inc(priv->rx_clean, dma_desc_rx);
> > +     }
> > +}
> > +
> > +/* geth_dma_desc_init - initialize the RX/TX descriptor list
> > + * @ndev: net device structure
> > + * Description: initialize the list for dma.
> > + */
> > +static int geth_dma_desc_init(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     unsigned int buf_sz;
> > +
> > +     priv->rx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_rx,
> > +                             GFP_KERNEL);
> > +     if (!priv->rx_sk)
> > +             return -ENOMEM;
> > +
> > +     priv->tx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_tx,
> > +                             GFP_KERNEL);
> > +     if (!priv->tx_sk)
> > +             goto tx_sk_err;
> > +
> > +     /* Set the size of buffer depend on the MTU & max buf size */
> > +     buf_sz = MAX_BUF_SZ;
> > +
> > +     priv->dma_tx = dma_alloc_coherent(priv->dev,
> > +                                     dma_desc_tx *
> > +                                     sizeof(struct dma_desc),
> > +                                     &priv->dma_tx_phy,
> > +                                     GFP_KERNEL);
> > +     if (!priv->dma_tx)
> > +             goto dma_tx_err;
> > +
> > +     priv->dma_rx = dma_alloc_coherent(priv->dev,
> > +                                     dma_desc_rx *
> > +                                     sizeof(struct dma_desc),
> > +                                     &priv->dma_rx_phy,
> > +                                     GFP_KERNEL);
> > +     if (!priv->dma_rx)
> > +             goto dma_rx_err;
> > +
> > +     priv->buf_sz = buf_sz;
> > +
> > +     return 0;
> > +
> > +dma_rx_err:
> > +     dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> > +                       priv->dma_tx, priv->dma_tx_phy);
> > +dma_tx_err:
> > +     kfree(priv->tx_sk);
> > +tx_sk_err:
> > +     kfree(priv->rx_sk);
> > +
> > +     return -ENOMEM;
> > +}
> > +
> > +static void geth_free_rx_sk(struct geth_priv *priv)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < dma_desc_rx; i++) {
> > +             if (priv->rx_sk[i] != NULL) {
> > +                     struct dma_desc *desc = priv->dma_rx + i;
> > +
> > +                     dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                                      desc_buf_get_len(desc),
> > +                                      DMA_FROM_DEVICE);
> > +                     dev_kfree_skb_any(priv->rx_sk[i]);
> > +                     priv->rx_sk[i] = NULL;
> > +             }
> > +     }
> > +}
> > +
> > +static void geth_free_tx_sk(struct geth_priv *priv)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < dma_desc_tx; i++) {
> > +             if (priv->tx_sk[i] != NULL) {
> > +                     struct dma_desc *desc = priv->dma_tx + i;
> > +
> > +                     if (desc_buf_get_addr(desc))
> > +                             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                                              desc_buf_get_len(desc),
> > +                                              DMA_TO_DEVICE);
> > +                     dev_kfree_skb_any(priv->tx_sk[i]);
> > +                     priv->tx_sk[i] = NULL;
> > +             }
> > +     }
> > +}
> > +
> > +static void geth_free_dma_desc(struct geth_priv *priv)
> > +{
> > +     /* Free the region of consistent memory previously allocated for the DMA */
> > +     dma_free_coherent(priv->dev, dma_desc_tx * sizeof(struct dma_desc),
> > +                       priv->dma_tx, priv->dma_tx_phy);
> > +     dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> > +                       priv->dma_rx, priv->dma_rx_phy);
> > +
> > +     kfree(priv->rx_sk);
> > +     kfree(priv->tx_sk);
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_PM)
> > +/*
> > +static int geth_select_gpio_state(struct pinctrl *pctrl, char *name)
> > +{
> > +     int ret = 0;
> > +     struct pinctrl_state *pctrl_state = NULL;
> > +
> > +     pctrl_state = pinctrl_lookup_state(pctrl, name);
> > +     if (IS_ERR(pctrl_state)) {
> > +             pr_err("gmac pinctrl_lookup_state(%s) failed! return %p\n",
> > +                                             name, pctrl_state);
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = pinctrl_select_state(pctrl, pctrl_state);
> > +     if (ret < 0)
> > +             pr_err("gmac pinctrl_select_state(%s) failed! return %d\n",
> > +                                             name, ret);
> > +
> > +     return ret;
> > +}
> > +*/
> > +static int geth_suspend(struct device *dev)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     cancel_work_sync(&priv->eth_work);
> > +
> > +     if (!ndev || !netif_running(ndev))
> > +             return 0;
> > +
> > +     priv->is_suspend = true;
> > +
> > +     spin_lock(&priv->lock);
> > +     netif_device_detach(ndev);
> > +     spin_unlock(&priv->lock);
> > +
> > +     geth_stop(ndev);
> > +/*
> > +     if (priv->phy_ext == EXT_PHY)
> > +
> > +     geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP);
> > +*/
> > +     return 0;
> > +}
> > +
> > +static void geth_resume_work(struct work_struct *work)
> > +{
> > +     struct geth_priv *priv = container_of(work, struct geth_priv, eth_work);
> > +     struct net_device *ndev = priv->ndev;
> > +     int ret = 0;
> > +
> > +     if (!netif_running(ndev))
> > +             return;
> > +/*
> > +     if (priv->phy_ext == EXT_PHY)
> > +             geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_DEFAULT);
> > +*/
> > +     spin_lock(&priv->lock);
> > +     netif_device_attach(ndev);
> > +     spin_unlock(&priv->lock);
> > +
> > +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> > +     if (!ephy_is_enable()) {
> > +             pr_info("[geth_resume] ephy is not enable, waiting...\n");
> > +             msleep(2000);
> > +             if (!ephy_is_enable()) {
> > +                     netdev_err(ndev, "Wait for ephy resume timeout.\n");
> > +                     return;
> > +             }
> > +     }
> > +#endif
> > +
> > +     ret = geth_open(ndev);
> > +     if (!ret)
> > +             priv->is_suspend = false;
> > +}
> > +
> > +static void geth_resume(struct device *dev)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     schedule_work(&priv->eth_work);
> > +}
> > +
> > +static int geth_freeze(struct device *dev)
> > +{
> > +     return 0;
> > +}
> > +
> > +static int geth_restore(struct device *dev)
> > +{
> > +     return 0;
> > +}
> > +
> > +static const struct dev_pm_ops geth_pm_ops = {
> > +     .complete = geth_resume,
> > +     .prepare = geth_suspend,
> > +     .suspend = NULL,
> > +     .resume = NULL,
> > +     .freeze = geth_freeze,
> > +     .restore = geth_restore,
> > +};
> > +#else
> > +static const struct dev_pm_ops geth_pm_ops;
> > +#endif /* CONFIG_PM */
> > +
> > +#define sunxi_get_soc_chipid(x) {}
> > +static void geth_chip_hwaddr(u8 *addr)
> > +{
> > +#define MD5_SIZE     16
> > +#define CHIP_SIZE    16
> > +
> > +     struct crypto_ahash *tfm;
> > +     struct ahash_request *req;
> > +     struct scatterlist sg;
> > +     u8 result[MD5_SIZE];
> > +     u8 chipid[CHIP_SIZE];
> > +     int i = 0;
> > +     int ret = -1;
> > +
> > +     memset(chipid, 0, sizeof(chipid));
> > +     memset(result, 0, sizeof(result));
> > +
> > +     sunxi_get_soc_chipid((u8 *)chipid);
> > +
> > +     tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
> > +     if (IS_ERR(tfm)) {
> > +             pr_err("Failed to alloc md5\n");
> > +             return;
> > +     }
> > +
> > +     req = ahash_request_alloc(tfm, GFP_KERNEL);
> > +     if (!req)
> > +             goto out;
> > +
> > +     ahash_request_set_callback(req, 0, NULL, NULL);
> > +
> > +     ret = crypto_ahash_init(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_init() failed\n");
> > +             goto out;
> > +     }
> > +
> > +     sg_init_one(&sg, chipid, sizeof(chipid));
> > +     ahash_request_set_crypt(req, &sg, result, sizeof(chipid));
> > +     ret = crypto_ahash_update(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_update() failed for id\n");
> > +             goto out;
> > +     }
> > +
> > +     ret = crypto_ahash_final(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_final() failed for result\n");
> > +             goto out;
> > +     }
> > +
> > +     ahash_request_free(req);
> > +
> > +     /* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
> > +     for (i = 0; i < ETH_ALEN; i++)
> > +             addr[i] = result[2 * i];
> > +     addr[0] &= 0xfe; /* clear multicast bit */
> > +     addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
> > +
> > +out:
> > +     crypto_free_ahash(tfm);
> > +}
> > +
> > +static void geth_check_addr(struct net_device *ndev, unsigned char *mac)
> > +{
> > +     int i;
> > +     char *p = mac;
> > +
> > +     if (!is_valid_ether_addr(ndev->dev_addr)) {
> > +             for (i = 0; i < ETH_ALEN; i++, p++)
> > +                     ndev->dev_addr[i] = simple_strtoul(p, &p, 16);
> > +
> > +             if (!is_valid_ether_addr(ndev->dev_addr))
> > +                     geth_chip_hwaddr(ndev->dev_addr);
> > +
> > +             if (!is_valid_ether_addr(ndev->dev_addr)) {
> > +                     random_ether_addr(ndev->dev_addr);
> > +                     pr_warn("%s: Use random mac address\n", ndev->name);
> > +             }
> > +     }
> > +}
> > +
> > +static int geth_clk_enable(struct geth_priv *priv)
> > +{
> > +     int ret;
> > +     phy_interface_t phy_interface = 0;
> > +     u32 clk_value;
> > +     /*u32 efuse_value;*/
> > +/*
> > +     ret = reset_control_deassert(priv->reset);
> > +     if (ret) {
> > +             pr_err("deassert gmac rst failed!\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(priv->geth_clk);
> > +     if (ret) {
> > +             pr_err("try to enable geth_clk failed!\n");
> > +             goto assert_reset;
> > +     }
> > +
> > +     if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> > +                     && !IS_ERR_OR_NULL(priv->ephy_clk)) {
> > +             ret = clk_prepare_enable(priv->ephy_clk);
> > +             if (ret) {
> > +                     pr_err("try to enable ephy_clk failed!\n");
> > +                     goto ephy_clk_disable;
> > +             }
> > +     }
> > +*/
> > +     phy_interface = priv->phy_interface;
> > +
> > +     clk_value = readl(priv->base_phy);
> > +     if (phy_interface == PHY_INTERFACE_MODE_RGMII)
> > +             clk_value |= 0x00000004;
> > +     else
> > +             clk_value &= (~0x00000004);
> > +
> > +     clk_value &= (~0x00002003);
> > +     if (phy_interface == PHY_INTERFACE_MODE_RGMII
> > +                     || phy_interface == PHY_INTERFACE_MODE_GMII)
> > +             clk_value |= 0x00000002;
> > +     else if (phy_interface == PHY_INTERFACE_MODE_RMII)
> > +             clk_value |= 0x00002001;
> > +
> > +     /*if (priv->phy_ext == INT_PHY) {
> > +             if (0 != sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value))
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     clk_value |= (((efuse_value >> 24) & 0x0F) + 3) << 28;
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }*/
> > +
> > +     /* Adjust Tx/Rx clock delay */
> > +     clk_value &= ~(0x07 << 10);
> > +     clk_value |= ((priv->tx_delay & 0x07) << 10);
> > +     clk_value &= ~(0x1F << 5);
> > +     clk_value |= ((priv->rx_delay & 0x1F) << 5);
> > +
> > +     writel(clk_value, priv->base_phy);
> > +
> > +    return 0;
> > +/*
> > +ephy_clk_disable:
> > +    clk_disable_unprepare(priv->ephy_clk);
> > +assert_reset:
> > +    reset_control_assert(priv->reset);
> > +*/
> > +    return ret;
> > +}
> > +
> > +static void geth_clk_disable(struct geth_priv *priv)
> > +{
> > +/*
> > +     if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> > +                     && !IS_ERR_OR_NULL(priv->ephy_clk))
> > +             clk_disable_unprepare(priv->ephy_clk);
> > +
> > +     clk_disable_unprepare(priv->geth_clk);
> > +    reset_control_assert(priv->reset);
> > +*/
> > +}
> > +
> > +static void geth_tx_err(struct geth_priv *priv)
> > +{
> > +     netif_stop_queue(priv->ndev);
> > +
> > +     sunxi_stop_tx(priv->base);
> > +
> > +     geth_free_tx_sk(priv);
> > +     memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> > +     desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> > +     priv->tx_dirty = 0;
> > +     priv->tx_clean = 0;
> > +     sunxi_start_tx(priv->base, priv->dma_tx_phy);
> > +
> > +     priv->ndev->stats.tx_errors++;
> > +     netif_wake_queue(priv->ndev);
> > +}
> > +
> > +static inline void geth_schedule(struct geth_priv *priv)
> > +{
> > +     if (likely(napi_schedule_prep(&priv->napi))) {
> > +             sunxi_int_disable(priv->base);
> > +             __napi_schedule(&priv->napi);
> > +     }
> > +}
> > +
> > +static irqreturn_t geth_interrupt(int irq, void *dev_id)
> > +{
> > +     struct net_device *ndev = (struct net_device *)dev_id;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     int status;
> > +
> > +     if (unlikely(!ndev)) {
> > +             pr_err("%s: invalid ndev pointer\n", __func__);
> > +             return IRQ_NONE;
> > +     }
> > +
> > +     status = sunxi_int_status(priv->base, (void *)(&priv->xstats));
> > +
> > +     if (likely(status == handle_tx_rx))
> > +             geth_schedule(priv);
> > +     else if (unlikely(status == tx_hard_error_bump_tc))
> > +             netdev_info(ndev, "Do nothing for bump tc\n");
> > +     else if (unlikely(status == tx_hard_error))
> > +             geth_tx_err(priv);
> > +     else
> > +             netdev_info(ndev, "Do nothing.....\n");
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int geth_open(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     int ret = 0;
> > +
> > +     ret = geth_power_on(priv);
> > +     if (ret) {
> > +             netdev_err(ndev, "Power on is failed\n");
> > +             ret = -EINVAL;
> > +     }
> > +
> > +     ret = geth_clk_enable(priv);
> > +     if (ret) {
> > +             pr_err("%s: clk enable is failed\n", __func__);
> > +             ret = -EINVAL;
> > +     }
> > +
> > +     netif_carrier_off(ndev);
> > +
> > +     ret = geth_phy_init(ndev);
> > +     if (ret)
> > +             goto err;
> > +
> > +     ret = sunxi_mac_reset((void *)priv->base, &sunxi_udelay, 10000);
> > +     if (ret) {
> > +             netdev_err(ndev, "Initialize hardware error\n");
> > +             goto desc_err;
> > +     }
> > +
> > +     sunxi_mac_init(priv->base, txmode, rxmode);
> > +     sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> > +
> > +     if (!priv->is_suspend) {
> > +             ret = geth_dma_desc_init(ndev);
> > +             if (ret) {
> > +                     ret = -EINVAL;
> > +                     goto desc_err;
> > +             }
> > +     }
> > +
> > +     memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> > +     memset(priv->dma_rx, 0, dma_desc_rx * sizeof(struct dma_desc));
> > +
> > +     desc_init_chain(priv->dma_rx, (unsigned long)priv->dma_rx_phy, dma_desc_rx);
> > +     desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> > +
> > +     priv->rx_clean = 0;
> > +     priv->rx_dirty = 0;
> > +     priv->tx_clean = 0;
> > +     priv->tx_dirty = 0;
> > +     geth_rx_refill(ndev);
> > +
> > +     /* Extra statistics */
> > +     memset(&priv->xstats, 0, sizeof(struct geth_extra_stats));
> > +
> > +     if (ndev->phydev)
> > +             phy_start(ndev->phydev);
> > +
> > +     sunxi_start_rx(priv->base, (unsigned long)((struct dma_desc *)
> > +                    priv->dma_rx_phy + priv->rx_dirty));
> > +     sunxi_start_tx(priv->base, (unsigned long)((struct dma_desc *)
> > +                    priv->dma_tx_phy + priv->tx_clean));
> > +
> > +     napi_enable(&priv->napi);
> > +     netif_start_queue(ndev);
> > +
> > +     /* Enable the Rx/Tx */
> > +     sunxi_mac_enable(priv->base);
> > +
> > +     return 0;
> > +
> > +desc_err:
> > +     geth_phy_release(ndev);
> > +err:
> > +     geth_clk_disable(priv);
> > +     if (priv->is_suspend)
> > +             napi_enable(&priv->napi);
> > +
> > +     geth_power_off(priv);
> > +
> > +     return ret;
> > +}
> > +
> > +static int geth_stop(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     netif_stop_queue(ndev);
> > +     napi_disable(&priv->napi);
> > +
> > +     netif_carrier_off(ndev);
> > +
> > +     /* Release PHY resources */
> > +     geth_phy_release(ndev);
> > +
> > +     /* Disable Rx/Tx */
> > +     sunxi_mac_disable(priv->base);
> > +
> > +     geth_clk_disable(priv);
> > +     geth_power_off(priv);
> > +
> > +     netif_tx_lock_bh(ndev);
> > +     /* Release the DMA TX/RX socket buffers */
> > +     geth_free_rx_sk(priv);
> > +     geth_free_tx_sk(priv);
> > +     netif_tx_unlock_bh(ndev);
> > +
> > +     /* Ensure that hareware have been stopped */
> > +     if (!priv->is_suspend)
> > +             geth_free_dma_desc(priv);
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_tx_complete(struct geth_priv *priv)
> > +{
> > +     unsigned int entry = 0;
> > +     struct sk_buff *skb = NULL;
> > +     struct dma_desc *desc = NULL;
> > +     int tx_stat;
> > +
> > +     spin_lock(&priv->tx_lock);
> > +     while (circ_cnt(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > 0) {
> > +             entry = priv->tx_clean;
> > +             desc = priv->dma_tx + entry;
> > +
> > +             /* Check if the descriptor is owned by the DMA. */
> > +             if (desc_get_own(desc))
> > +                     break;
> > +
> > +             /* Verify tx error by looking at the last segment */
> > +             if (desc_get_tx_ls(desc)) {
> > +                     tx_stat = desc_get_tx_status(desc, (void *)(&priv->xstats));
> > +
> > +                     if (likely(!tx_stat))
> > +                             priv->ndev->stats.tx_packets++;
> > +                     else
> > +                             priv->ndev->stats.tx_errors++;
> > +             }
> > +
> > +             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                              desc_buf_get_len(desc), DMA_TO_DEVICE);
> > +
> > +             skb = priv->tx_sk[entry];
> > +             priv->tx_sk[entry] = NULL;
> > +             desc_init(desc);
> > +
> > +             /* Find next dirty desc */
> > +             priv->tx_clean = circ_inc(entry, dma_desc_tx);
> > +
> > +             if (unlikely(skb == NULL))
> > +                     continue;
> > +
> > +             dev_kfree_skb(skb);
> > +     }
> > +
> > +     if (unlikely(netif_queue_stopped(priv->ndev)) &&
> > +         circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> > +         TX_THRESH) {
> > +             netif_wake_queue(priv->ndev);
> > +     }
> > +     spin_unlock(&priv->tx_lock);
> > +}
> > +
> > +static netdev_tx_t geth_xmit(struct sk_buff *skb, struct net_device *ndev)
> > +{
> > +     struct geth_priv  *priv = netdev_priv(ndev);
> > +     unsigned int entry;
> > +     struct dma_desc *desc, *first;
> > +     unsigned int len, tmp_len = 0;
> > +     int i, csum_insert;
> > +     int nfrags = skb_shinfo(skb)->nr_frags;
> > +     dma_addr_t paddr;
> > +
> > +     spin_lock(&priv->tx_lock);
> > +     if (unlikely(circ_space(priv->tx_dirty, priv->tx_clean,
> > +         dma_desc_tx) < (nfrags + 1))) {
> > +             if (!netif_queue_stopped(ndev)) {
> > +                     netdev_err(ndev, "%s: BUG! Tx Ring full when queue awake\n", __func__);
> > +                     netif_stop_queue(ndev);
> > +             }
> > +             spin_unlock(&priv->tx_lock);
> > +
> > +             return NETDEV_TX_BUSY;
> > +     }
> > +
> > +     csum_insert = (skb->ip_summed == CHECKSUM_PARTIAL);
> > +     entry = priv->tx_dirty;
> > +     first = priv->dma_tx + entry;
> > +     desc = priv->dma_tx + entry;
> > +
> > +     len = skb_headlen(skb);
> > +     priv->tx_sk[entry] = skb;
> > +
> > +#ifdef PKT_DEBUG
> > +     printk("======TX PKT DATA: ============\n");
> > +     /* dump the packet */
> > +     print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> > +                    16, 1, skb->data, 64, true);
> > +#endif
> > +
> > +     /* Every desc max size is 2K */
> > +     while (len != 0) {
> > +             desc = priv->dma_tx + entry;
> > +             tmp_len = ((len > MAX_BUF_SZ) ?  MAX_BUF_SZ : len);
> > +
> > +             paddr = dma_map_single(priv->dev, skb->data, tmp_len, DMA_TO_DEVICE);
> > +             if (dma_mapping_error(priv->dev, paddr)) {
> > +                     dev_kfree_skb(skb);
> > +                     return -EIO;
> > +             }
> > +             desc_buf_set(desc, paddr, tmp_len);
> > +             /* Don't set the first's own bit, here */
> > +             if (first != desc) {
> > +                     priv->tx_sk[entry] = NULL;
> > +                     desc_set_own(desc);
> > +             }
> > +
> > +             entry = circ_inc(entry, dma_desc_tx);
> > +             len -= tmp_len;
> > +     }
> > +
> > +     for (i = 0; i < nfrags; i++) {
> > +             const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> > +
> > +             len = skb_frag_size(frag);
> > +             desc = priv->dma_tx + entry;
> > +             paddr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
> > +             if (dma_mapping_error(priv->dev, paddr)) {
> > +                     dev_kfree_skb(skb);
> > +                     return -EIO;
> > +             }
> > +
> > +             desc_buf_set(desc, paddr, len);
> > +             desc_set_own(desc);
> > +             priv->tx_sk[entry] = NULL;
> > +             entry = circ_inc(entry, dma_desc_tx);
> > +     }
> > +
> > +     ndev->stats.tx_bytes += skb->len;
> > +     priv->tx_dirty = entry;
> > +     desc_tx_close(first, desc, csum_insert);
> > +
> > +     desc_set_own(first);
> > +     spin_unlock(&priv->tx_lock);
> > +
> > +     if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) <=
> > +                     (MAX_SKB_FRAGS + 1)) {
> > +             netif_stop_queue(ndev);
> > +             if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> > +                             TX_THRESH)
> > +                     netif_wake_queue(ndev);
> > +     }
> > +
> > +#ifdef DEBUG
> > +     printk("=======TX Descriptor DMA: 0x%08llx\n", priv->dma_tx_phy);
> > +     printk("Tx pointor: dirty: %d, clean: %d\n", priv->tx_dirty, priv->tx_clean);
> > +     desc_print(priv->dma_tx, dma_desc_tx);
> > +#endif
> > +     sunxi_tx_poll(priv->base);
> > +     geth_tx_complete(priv);
> > +
> > +     return NETDEV_TX_OK;
> > +}
> > +
> > +static int geth_rx(struct geth_priv *priv, int limit)
> > +{
> > +     unsigned int rxcount = 0;
> > +     unsigned int entry;
> > +     struct dma_desc *desc;
> > +     struct sk_buff *skb;
> > +     int status;
> > +     int frame_len;
> > +
> > +     while (rxcount < limit) {
> > +             entry = priv->rx_dirty;
> > +             desc = priv->dma_rx + entry;
> > +
> > +             if (desc_get_own(desc))
> > +                     break;
> > +
> > +             rxcount++;
> > +             priv->rx_dirty = circ_inc(priv->rx_dirty, dma_desc_rx);
> > +
> > +             /* Get length & status from hardware */
> > +             frame_len = desc_rx_frame_len(desc);
> > +             status = desc_get_rx_status(desc, (void *)(&priv->xstats));
> > +
> > +             netdev_dbg(priv->ndev, "Rx frame size %d, status: %d\n",
> > +                        frame_len, status);
> > +
> > +             skb = priv->rx_sk[entry];
> > +             if (unlikely(!skb)) {
> > +                     netdev_err(priv->ndev, "Skb is null\n");
> > +                     priv->ndev->stats.rx_dropped++;
> > +                     break;
> > +             }
> > +
> > +#ifdef PKT_DEBUG
> > +             printk("======RX PKT DATA: ============\n");
> > +             /* dump the packet */
> > +             print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> > +                             16, 1, skb->data, 64, true);
> > +#endif
> > +
> > +             if (status == discard_frame) {
> > +                     netdev_dbg(priv->ndev, "Get error pkt\n");
> > +                     priv->ndev->stats.rx_errors++;
> > +                     continue;
> > +             }
> > +
> > +             if (unlikely(status != llc_snap))
> > +                     frame_len -= ETH_FCS_LEN;
> > +
> > +             priv->rx_sk[entry] = NULL;
> > +
> > +             skb_put(skb, frame_len);
> > +             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                              desc_buf_get_len(desc), DMA_FROM_DEVICE);
> > +
> > +             skb->protocol = eth_type_trans(skb, priv->ndev);
> > +
> > +             skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +             napi_gro_receive(&priv->napi, skb);
> > +
> > +             priv->ndev->stats.rx_packets++;
> > +             priv->ndev->stats.rx_bytes += frame_len;
> > +     }
> > +
> > +#ifdef DEBUG
> > +     if (rxcount > 0) {
> > +             printk("======RX Descriptor DMA: 0x%08llx=\n", priv->dma_rx_phy);
> > +             printk("RX pointor: dirty: %d, clean: %d\n", priv->rx_dirty, priv->rx_clean);
> > +             desc_print(priv->dma_rx, dma_desc_rx);
> > +     }
> > +#endif
> > +     geth_rx_refill(priv->ndev);
> > +
> > +     return rxcount;
> > +}
> > +
> > +static int geth_poll(struct napi_struct *napi, int budget)
> > +{
> > +     struct geth_priv *priv = container_of(napi, struct geth_priv, napi);
> > +     int work_done = 0;
> > +
> > +     geth_tx_complete(priv);
> > +     work_done = geth_rx(priv, budget);
> > +
> > +     if (work_done < budget) {
> > +             napi_complete(napi);
> > +             sunxi_int_enable(priv->base);
> > +     }
> > +
> > +     return work_done;
> > +}
> > +
> > +static int geth_change_mtu(struct net_device *ndev, int new_mtu)
> > +{
> > +     int max_mtu;
> > +
> > +     if (netif_running(ndev)) {
> > +             pr_err("%s: must be stopped to change its MTU\n", ndev->name);
> > +             return -EBUSY;
> > +     }
> > +
> > +     max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
> > +
> > +     if ((new_mtu < 46) || (new_mtu > max_mtu)) {
> > +             pr_err("%s: invalid MTU, max MTU is: %d\n", ndev->name, max_mtu);
> > +             return -EINVAL;
> > +     }
> > +
> > +     ndev->mtu = new_mtu;
> > +     netdev_update_features(ndev);
> > +
> > +     return 0;
> > +}
> > +
> > +static netdev_features_t geth_fix_features(struct net_device *ndev,
> > +                                        netdev_features_t features)
> > +{
> > +     return features;
> > +}
> > +
> > +static void geth_set_rx_mode(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     unsigned int value = 0;
> > +
> > +     pr_debug("%s: # mcasts %d, # unicast %d\n",
> > +              __func__, netdev_mc_count(ndev), netdev_uc_count(ndev));
> > +
> > +     spin_lock(&priv->lock);
> > +     if (ndev->flags & IFF_PROMISC) {
> > +             value = GETH_FRAME_FILTER_PR;
> > +     } else if ((netdev_mc_count(ndev) > HASH_TABLE_SIZE) ||
> > +                (ndev->flags & IFF_ALLMULTI)) {
> > +             value = GETH_FRAME_FILTER_PM;   /* pass all multi */
> > +             sunxi_hash_filter(priv->base, ~0UL, ~0UL);
> > +     } else if (!netdev_mc_empty(ndev)) {
> > +             u32 mc_filter[2];
> > +             struct netdev_hw_addr *ha;
> > +
> > +             /* Hash filter for multicast */
> > +             value = GETH_FRAME_FILTER_HMC;
> > +
> > +             memset(mc_filter, 0, sizeof(mc_filter));
> > +             netdev_for_each_mc_addr(ha, ndev) {
> > +                     /* The upper 6 bits of the calculated CRC are used to
> > +                      *  index the contens of the hash table
> > +                      */
> > +                     int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
> > +                     /* The most significant bit determines the register to
> > +                      * use (H/L) while the other 5 bits determine the bit
> > +                      * within the register.
> > +                      */
> > +                     mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
> > +             }
> > +             sunxi_hash_filter(priv->base, mc_filter[0], mc_filter[1]);
> > +     }
> > +
> > +     /* Handle multiple unicast addresses (perfect filtering)*/
> > +     if (netdev_uc_count(ndev) > 16) {
> > +             /* Switch to promiscuous mode is more than 8 addrs are required */
> > +             value |= GETH_FRAME_FILTER_PR;
> > +     } else {
> > +             int reg = 1;
> > +             struct netdev_hw_addr *ha;
> > +
> > +             netdev_for_each_uc_addr(ha, ndev) {
> > +                     sunxi_set_umac(priv->base, ha->addr, reg);
> > +                     reg++;
> > +             }
> > +     }
> > +
> > +#ifdef FRAME_FILTER_DEBUG
> > +     /* Enable Receive all mode (to debug filtering_fail errors) */
> > +     value |= GETH_FRAME_FILTER_RA;
> > +#endif
> > +     sunxi_set_filter(priv->base, value);
> > +     spin_unlock(&priv->lock);
> > +}
> > +
> > +static void geth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     geth_tx_err(priv);
> > +}
> > +
> > +static int geth_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
> > +{
> > +     if (!netif_running(ndev))
> > +             return -EINVAL;
> > +
> > +     if (!ndev->phydev)
> > +             return -EINVAL;
> > +
> > +     return phy_mii_ioctl(ndev->phydev, rq, cmd);
> > +}
> > +
> > +/* Configuration changes (passed on by ifconfig) */
> > +static int geth_config(struct net_device *ndev, struct ifmap *map)
> > +{
> > +     if (ndev->flags & IFF_UP)       /* can't act on a running interface */
> > +             return -EBUSY;
> > +
> > +     /* Don't allow changing the I/O address */
> > +     if (map->base_addr != ndev->base_addr) {
> > +             pr_warn("%s: can't change I/O address\n", ndev->name);
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     /* Don't allow changing the IRQ */
> > +     if (map->irq != ndev->irq) {
> > +             pr_warn("%s: can't change IRQ number %d\n", ndev->name, ndev->irq);
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int geth_set_mac_address(struct net_device *ndev, void *p)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct sockaddr *addr = p;
> > +
> > +     if (!is_valid_ether_addr(addr->sa_data))
> > +             return -EADDRNOTAVAIL;
> > +
> > +     memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
> > +     sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> > +
> > +     return 0;
> > +}
> > +
> > +int geth_set_features(struct net_device *ndev, netdev_features_t features)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (features & NETIF_F_LOOPBACK && netif_running(ndev))
> > +             sunxi_mac_loopback(priv->base, 1);
> > +     else
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +     return 0;
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> > +/* Polling receive - used by NETCONSOLE and other diagnostic tools
> > + * to allow network I/O with interrupts disabled.
> > + */
> > +static void geth_poll_controller(struct net_device *dev)
> > +{
> > +     disable_irq(dev->irq);
> > +     geth_interrupt(dev->irq, dev);
> > +     enable_irq(dev->irq);
> > +}
> > +#endif
> > +
> > +static const struct net_device_ops geth_netdev_ops = {
> > +     .ndo_init = NULL,
> > +     .ndo_open = geth_open,
> > +     .ndo_start_xmit = geth_xmit,
> > +     .ndo_stop = geth_stop,
> > +     .ndo_change_mtu = geth_change_mtu,
> > +     .ndo_fix_features = geth_fix_features,
> > +     .ndo_set_rx_mode = geth_set_rx_mode,
> > +     .ndo_tx_timeout = geth_tx_timeout,
> > +     .ndo_do_ioctl = geth_ioctl,
> > +     .ndo_set_config = geth_config,
> > +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> > +     .ndo_poll_controller = geth_poll_controller,
> > +#endif
> > +     .ndo_set_mac_address = geth_set_mac_address,
> > +     .ndo_set_features = geth_set_features,
> > +};
> > +
> > +static int geth_check_if_running(struct net_device *ndev)
> > +{
> > +     if (!netif_running(ndev))
> > +             return -EBUSY;
> > +     return 0;
> > +}
> > +
> > +static int geth_get_sset_count(struct net_device *netdev, int sset)
> > +{
> > +     int len;
> > +
> > +     switch (sset) {
> > +     case ETH_SS_STATS:
> > +             len = 0;
> > +             return len;
> > +     default:
> > +             return -EOPNOTSUPP;
> > +     }
> > +}
> > +
> > +/*static int geth_ethtool_getsettings(struct net_device *ndev,
> > +                                 struct ethtool_cmd *cmd)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phy = ndev->phydev;
> > +     int rc;
> > +
> > +     if (phy == NULL) {
> > +             netdev_err(ndev, "%s: %s: PHY is not registered\n",
> > +                    __func__, ndev->name);
> > +             return -ENODEV;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_err("%s: interface is disabled: we cannot track "
> > +                    "link speed / duplex setting\n", ndev->name);
> > +             return -EBUSY;
> > +     }
> > +
> > +     cmd->transceiver = XCVR_INTERNAL;
> > +     spin_lock_irq(&priv->lock);
> > +     //rc = phy_ethtool_gset(phy, cmd);
> > +     spin_unlock_irq(&priv->lock);
> > +
> > +     return rc;
> > +}
> > +
> > +static int geth_ethtool_setsettings(struct net_device *ndev,
> > +                                 struct ethtool_cmd *cmd)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phy = ndev->phydev;
> > +     int rc;
> > +
> > +     spin_lock(&priv->lock);
> > +     rc = phy_ethtool_sset(phy, cmd);
> > +     spin_unlock(&priv->lock);
> > +
> > +     return rc;
> > +}*/
> > +
> > +static void geth_ethtool_getdrvinfo(struct net_device *ndev,
> > +                                 struct ethtool_drvinfo *info)
> > +{
> > +     strlcpy(info->driver, "sunxi_geth", sizeof(info->driver));
> > +
> > +#define DRV_MODULE_VERSION "SUNXI Gbgit driver V1.1"
> > +
> > +     strcpy(info->version, DRV_MODULE_VERSION);
> > +     info->fw_version[0] = '\0';
> > +}
> > +
> > +static const struct ethtool_ops geth_ethtool_ops = {
> > +     .begin = geth_check_if_running,
> > +     //.get_settings = geth_ethtool_getsettings,
> > +     //.set_settings = geth_ethtool_setsettings,
> > +     .get_link = ethtool_op_get_link,
> > +     .get_pauseparam = NULL,
> > +     .set_pauseparam = NULL,
> > +     .get_ethtool_stats = NULL,
> > +     .get_strings = NULL,
> > +     .get_wol = NULL,
> > +     .set_wol = NULL,
> > +     .get_sset_count = geth_get_sset_count,
> > +     .get_drvinfo = geth_ethtool_getdrvinfo,
> > +};
> > +
> > +/* config hardware resource */
> > +static int geth_hw_init(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct device_node *np = pdev->dev.of_node;
> > +     int ret = 0;
> > +     struct resource *res;
> > +     u32 value;
> > +//   struct gpio_config cfg;
> > +//   const char *gmac_power;
> > +//   char power[20];
> > +//   int i;
> > +
> > +#if 1
> > +     priv->phy_ext = EXT_PHY;
> > +#else
> > +     priv->phy_ext = INT_PHY;
> > +#endif
> > +
> > +     /* config memery resource */
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     if (unlikely(!res)) {
> > +             pr_err("%s: ERROR: get gmac memory failed", __func__);
> > +             return -ENODEV;
> > +     }
> > +
> > +     priv->base = devm_ioremap_resource(&pdev->dev, res);
> > +     if (!priv->base) {
> > +             pr_err("%s: ERROR: gmac memory mapping failed", __func__);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +     if (unlikely(!res)) {
> > +             pr_err("%s: ERROR: get phy memory failed", __func__);
> > +             ret = -ENODEV;
> > +             goto mem_err;
> > +     }
> > +
> > +     priv->base_phy = devm_ioremap_resource(&pdev->dev, res);
> > +     if (unlikely(!priv->base_phy)) {
> > +             pr_err("%s: ERROR: phy memory mapping failed", __func__);
> > +             ret = -ENOMEM;
> > +             goto mem_err;
> > +     }
> > +
> > +     /* config IRQ */
> > +     ndev->irq = platform_get_irq_byname(pdev, "gmacirq");
> > +     if (ndev->irq == -ENXIO) {
> > +             pr_err("%s: ERROR: MAC IRQ not found\n", __func__);
> > +             ret = -ENXIO;
> > +             goto irq_err;
> > +     }
> > +
> > +     ret = request_irq(ndev->irq, geth_interrupt, IRQF_SHARED, dev_name(&pdev->dev), ndev);
> > +     if (unlikely(ret < 0)) {
> > +             pr_err("Could not request irq %d, error: %d\n", ndev->irq, ret);
> > +             goto irq_err;
> > +     }
> > +
> > +     /* get gmac rst handle */
> > +/*
> > +     priv->reset = devm_reset_control_get(&pdev->dev, NULL);
> > +     if (IS_ERR(priv->reset)) {
> > +             pr_err("%s: Get gmac reset control failed!\n", __func__);
> > +             return PTR_ERR(priv->reset);
> > +     }
> > +*/
> > +     /* config clock */
> > +/*
> > +     priv->geth_clk = of_clk_get_by_name(np, "gmac");
> > +     if (unlikely(!priv->geth_clk || IS_ERR(priv->geth_clk))) {
> > +             pr_err("Get gmac clock failed!\n");
> > +             ret = -EINVAL;
> > +             goto clk_err;
> > +     }
> > +
> > +     if (INT_PHY == priv->phy_ext) {
> > +             priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> > +             if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> > +                     pr_err("Get ephy clock failed!\n");
> > +                     ret = -EINVAL;
> > +                     goto clk_err;
> > +             }
> > +     } else {
> > +             if (!of_property_read_u32(np, "use-ephy25m", &(priv->use_ephy_clk))
> > +                             && priv->use_ephy_clk) {
> > +                     priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> > +                     if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> > +                             pr_err("Get ephy clk failed!\n");
> > +                             ret = -EINVAL;
> > +                             goto clk_err;
> > +                     }
> > +             }
> > +     }
> > +*/
> > +     /* config power regulator */
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     snprintf(power, 15, "gmac-power%d", i);
> > +                     ret = of_property_read_string(np, power, &gmac_power);
> > +                     if (ret) {
> > +                             priv->gmac_power[i] = NULL;
> > +                             pr_info("gmac-power%d: NULL\n", i);
> > +                             continue;
> > +                     }
> > +                     priv->gmac_power[i] = regulator_get(NULL, gmac_power);
> > +                     if (IS_ERR(priv->gmac_power[i])) {
> > +                             pr_err("gmac-power%d get error!\n", i);
> > +                             ret = -EINVAL;
> > +                             goto clk_err;
> > +                     }
> > +             }
> > +     }
> > +*/
> > +     /* config other parameters */
> > +     of_get_phy_mode(np, &(priv->phy_interface));
> > +     if (priv->phy_interface != PHY_INTERFACE_MODE_MII &&
> > +         priv->phy_interface != PHY_INTERFACE_MODE_RGMII &&
> > +         priv->phy_interface != PHY_INTERFACE_MODE_RMII) {
> > +             pr_err("Not support phy type!\n");
> > +             priv->phy_interface = PHY_INTERFACE_MODE_MII;
> > +     }
> > +
> > +     if (!of_property_read_u32(np, "tx-delay", &value))
> > +             priv->tx_delay = value;
> > +
> > +     if (!of_property_read_u32(np, "rx-delay", &value))
> > +             priv->rx_delay = value;
> > +
> > +     /* config pinctrl */
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             priv->phyrst = of_get_named_gpio_flags(np, "phy-rst", 0, (enum of_gpio_flags *)&cfg);
> > +             priv->rst_active_low = (cfg.data == OF_GPIO_ACTIVE_LOW) ? 1 : 0;
> > +
> > +             if (gpio_is_valid(priv->phyrst)) {
> > +                     if (gpio_request(priv->phyrst, "phy-rst") < 0) {
> > +                             pr_err("gmac gpio request fail!\n");
> > +                             ret = -EINVAL;
> > +                             goto pin_err;
> > +                     }
> > +             }
> > +
> > +             priv->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> > +             if (IS_ERR_OR_NULL(priv->pinctrl)) {
> > +                     pr_err("gmac pinctrl error!\n");
> > +                     priv->pinctrl = NULL;
> > +                     ret = -EINVAL;
> > +                     goto pin_err;
> > +             }
> > +     }
> > +*/
> > +     return 0;
> > +
> > +//pin_err:
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_put(priv->gmac_power[i]);
> > +             }
> > +     }
> > +*/
> > +//clk_err:
> > +//   free_irq(ndev->irq, ndev);
> > +irq_err:
> > +     devm_iounmap(&pdev->dev, priv->base_phy);
> > +mem_err:
> > +     devm_iounmap(&pdev->dev, priv->base);
> > +
> > +     return ret;
> > +}
> > +
> > +static void geth_hw_release(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     devm_iounmap(&pdev->dev, (priv->base_phy));
> > +     devm_iounmap(&pdev->dev, priv->base);
> > +     free_irq(ndev->irq, ndev);
> > +/*
> > +     if (priv->geth_clk)
> > +             clk_put(priv->geth_clk);
> > +
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_put(priv->gmac_power[i]);
> > +             }
> > +
> > +             if (!IS_ERR_OR_NULL(priv->pinctrl))
> > +                     devm_pinctrl_put(priv->pinctrl);
> > +
> > +             if (gpio_is_valid(priv->phyrst))
> > +                     gpio_free(priv->phyrst);
> > +     }
> > +
> > +     if (!IS_ERR_OR_NULL(priv->ephy_clk))
> > +             clk_put(priv->ephy_clk);
> > +*/
> > +}
> > +
> > +/**
> > + * geth_probe
> > + * @pdev: platform device pointer
> > + * Description: the driver is initialized through platform_device.
> > + */
> > +static int geth_probe(struct platform_device *pdev)
> > +{
> > +     int ret = 0;
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv;
> > +
> > +     pr_info("sunxi gmac driver's version: %s\n", SUNXI_GMAC_VERSION);
> > +
> > +#if IS_ENABLED(CONFIG_OF)
> > +     pdev->dev.dma_mask = &geth_dma_mask;
> > +     pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> > +#endif
> > +
> > +     ndev = alloc_etherdev(sizeof(struct geth_priv));
> > +     if (!ndev) {
> > +             dev_err(&pdev->dev, "could not allocate device.\n");
> > +             return -ENOMEM;
> > +     }
> > +     SET_NETDEV_DEV(ndev, &pdev->dev);
> > +
> > +     priv = netdev_priv(ndev);
> > +     platform_set_drvdata(pdev, ndev);
> > +
> > +     /* Must set private data to pdev, before call it */
> > +     ret = geth_hw_init(pdev);
> > +     if (0 != ret) {
> > +             pr_err("geth_hw_init fail!\n");
> > +             goto hw_err;
> > +     }
> > +
> > +     /* setup the netdevice, fill the field of netdevice */
> > +     ether_setup(ndev);
> > +     ndev->netdev_ops = &geth_netdev_ops;
> > +     netdev_set_default_ethtool_ops(ndev, &geth_ethtool_ops);
> > +     ndev->base_addr = (unsigned long)priv->base;
> > +
> > +     priv->ndev = ndev;
> > +     priv->dev = &pdev->dev;
> > +
> > +     /* TODO: support the VLAN frames */
> > +     ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
> > +                             NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
> > +
> > +     ndev->features |= ndev->hw_features;
> > +     ndev->hw_features |= NETIF_F_LOOPBACK;
> > +     ndev->priv_flags |= IFF_UNICAST_FLT;
> > +
> > +     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
> > +
> > +     netif_napi_add(ndev, &priv->napi, geth_poll,  BUDGET);
> > +
> > +     spin_lock_init(&priv->lock);
> > +     spin_lock_init(&priv->tx_lock);
> > +
> > +     /* The last val is mdc clock ratio */
> > +     sunxi_geth_register((void *)ndev->base_addr, HW_VERSION, 0x03);
> > +
> > +     ret = register_netdev(ndev);
> > +     if (ret) {
> > +             netif_napi_del(&priv->napi);
> > +             pr_err("Error: Register %s failed\n", ndev->name);
> > +             goto reg_err;
> > +     }
> > +
> > +     /* Before open the device, the mac address should be set */
> > +     geth_check_addr(ndev, mac_str);
> > +
> > +#ifdef CONFIG_GETH_ATTRS
> > +     geth_create_attrs(ndev);
> > +#endif
> > +     device_create_file(&pdev->dev, &dev_attr_gphy_test);
> > +     device_create_file(&pdev->dev, &dev_attr_mii_reg);
> > +     device_create_file(&pdev->dev, &dev_attr_loopback_test);
> > +     device_create_file(&pdev->dev, &dev_attr_extra_tx_stats);
> > +     device_create_file(&pdev->dev, &dev_attr_extra_rx_stats);
> > +
> > +     device_enable_async_suspend(&pdev->dev);
> > +
> > +#if IS_ENABLED(CONFIG_PM)
> > +     INIT_WORK(&priv->eth_work, geth_resume_work);
> > +#endif
> > +
> > +     return 0;
> > +
> > +reg_err:
> > +     geth_hw_release(pdev);
> > +hw_err:
> > +     platform_set_drvdata(pdev, NULL);
> > +     free_netdev(ndev);
> > +
> > +     return ret;
> > +}
> > +
> > +static int geth_remove(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     device_remove_file(&pdev->dev, &dev_attr_gphy_test);
> > +     device_remove_file(&pdev->dev, &dev_attr_mii_reg);
> > +     device_remove_file(&pdev->dev, &dev_attr_loopback_test);
> > +     device_remove_file(&pdev->dev, &dev_attr_extra_tx_stats);
> > +     device_remove_file(&pdev->dev, &dev_attr_extra_rx_stats);
> > +
> > +     netif_napi_del(&priv->napi);
> > +     unregister_netdev(ndev);
> > +     geth_hw_release(pdev);
> > +     platform_set_drvdata(pdev, NULL);
> > +     free_netdev(ndev);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id geth_of_match[] = {
> > +     {.compatible = "allwinner,sunxi-gmac",},
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, geth_of_match);
> > +
> > +static struct platform_driver geth_driver = {
> > +     .probe  = geth_probe,
> > +     .remove = geth_remove,
> > +     .driver = {
> > +                .name = "sunxi-gmac",
> > +                .owner = THIS_MODULE,
> > +                .pm = &geth_pm_ops,
> > +                .of_match_table = geth_of_match,
> > +     },
> > +};
> > +module_platform_driver(geth_driver);
> > +
> > +#ifdef MODULE
> > +static int __init set_mac_addr(char *str)
> > +{
> > +     char *p = str;
> > +
> > +     if (str && strlen(str))
> > +             memcpy(mac_str, p, 18);
> > +
> > +     return 0;
> > +}
> > +__setup("mac_addr=", set_mac_addr);
> > +#endif
> > +
> > +MODULE_DESCRIPTION("Allwinner Gigabit Ethernet driver");
> > +MODULE_AUTHOR("fuzhaoke <fuzhaoke@allwinnertech.com>");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> > new file mode 100644
> > index 00000000..ea7a6f15
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> > @@ -0,0 +1,258 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.       See the
> > + * GNU General Public License for more details.
> > + */
> > +#ifndef __SUNXI_GETH_H__
> > +#define __SUNXI_GETH_H__
> > +
> > +#include <linux/etherdevice.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/phy.h>
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +
> > +/* GETH_FRAME_FILTER  register value */
> > +#define GETH_FRAME_FILTER_PR 0x00000001      /* Promiscuous Mode */
> > +#define GETH_FRAME_FILTER_HUC        0x00000002      /* Hash Unicast */
> > +#define GETH_FRAME_FILTER_HMC        0x00000004      /* Hash Multicast */
> > +#define GETH_FRAME_FILTER_DAIF       0x00000008      /* DA Inverse Filtering */
> > +#define GETH_FRAME_FILTER_PM 0x00000010      /* Pass all multicast */
> > +#define GETH_FRAME_FILTER_DBF        0x00000020      /* Disable Broadcast frames */
> > +#define GETH_FRAME_FILTER_SAIF       0x00000100      /* Inverse Filtering */
> > +#define GETH_FRAME_FILTER_SAF        0x00000200      /* Source Address Filter */
> > +#define GETH_FRAME_FILTER_HPF        0x00000400      /* Hash or perfect Filter */
> > +#define GETH_FRAME_FILTER_RA 0x80000000      /* Receive all mode */
> > +
> > +/* Default tx descriptor */
> > +#define TX_SINGLE_DESC0              0x80000000
> > +#define TX_SINGLE_DESC1              0x63000000
> > +
> > +/* Default rx descriptor */
> > +#define RX_SINGLE_DESC0              0x80000000
> > +#define RX_SINGLE_DESC1              0x83000000
> > +
> > +typedef union {
> > +     struct {
> > +             /* TDES0 */
> > +             unsigned int deferred:1;        /* Deferred bit (only half-duplex) */
> > +             unsigned int under_err:1;       /* Underflow error */
> > +             unsigned int ex_deferral:1;     /* Excessive deferral */
> > +             unsigned int coll_cnt:4;        /* Collision count */
> > +             unsigned int vlan_tag:1;        /* VLAN Frame */
> > +             unsigned int ex_coll:1;         /* Excessive collision */
> > +             unsigned int late_coll:1;       /* Late collision */
> > +             unsigned int no_carr:1;         /* No carrier */
> > +             unsigned int loss_carr:1;       /* Loss of collision */
> > +             unsigned int ipdat_err:1;       /* IP payload error */
> > +             unsigned int frm_flu:1;         /* Frame flushed */
> > +             unsigned int jab_timeout:1;     /* Jabber timeout */
> > +             unsigned int err_sum:1;         /* Error summary */
> > +             unsigned int iphead_err:1;      /* IP header error */
> > +             unsigned int ttss:1;            /* Transmit time stamp status */
> > +             unsigned int reserved0:13;
> > +             unsigned int own:1;             /* Own bit. CPU:0, DMA:1 */
> > +     } tx;
> > +
> > +     /* bits 5 7 0 | Frame status
> > +      * ----------------------------------------------------------
> > +      *      0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
> > +      *      1 0 0 | IPv4/6 No CSUM errorS.
> > +      *      1 0 1 | IPv4/6 CSUM PAYLOAD error
> > +      *      1 1 0 | IPv4/6 CSUM IP HR error
> > +      *      1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
> > +      *      0 0 1 | IPv4/6 unsupported IP PAYLOAD
> > +      *      0 1 1 | COE bypassed.. no IPv4/6 frame
> > +      *      0 1 0 | Reserved.
> > +      */
> > +     struct {
> > +             /* RDES0 */
> > +             unsigned int chsum_err:1;       /* Payload checksum error */
> > +             unsigned int crc_err:1;         /* CRC error */
> > +             unsigned int dribbling:1;       /* Dribble bit error */
> > +             unsigned int mii_err:1;         /* Received error (bit3) */
> > +             unsigned int recv_wt:1;         /* Received watchdog timeout */
> > +             unsigned int frm_type:1;        /* Frame type */
> > +             unsigned int late_coll:1;       /* Late Collision */
> > +             unsigned int ipch_err:1;        /* IPv header checksum error (bit7) */
> > +             unsigned int last_desc:1;       /* Laset descriptor */
> > +             unsigned int first_desc:1;      /* First descriptor */
> > +             unsigned int vlan_tag:1;        /* VLAN Tag */
> > +             unsigned int over_err:1;        /* Overflow error (bit11) */
> > +             unsigned int len_err:1;         /* Length error */
> > +             unsigned int sou_filter:1;      /* Source address filter fail */
> > +             unsigned int desc_err:1;        /* Descriptor error */
> > +             unsigned int err_sum:1;         /* Error summary (bit15) */
> > +             unsigned int frm_len:14;        /* Frame length */
> > +             unsigned int des_filter:1;      /* Destination address filter fail */
> > +             unsigned int own:1;             /* Own bit. CPU:0, DMA:1 */
> > +     #define RX_PKT_OK               0x7FFFB77C
> > +     #define RX_LEN                  0x3FFF0000
> > +     } rx;
> > +
> > +     unsigned int all;
> > +} desc0_u;
> > +
> > +typedef union {
> > +     struct {
> > +             /* TDES1 */
> > +             unsigned int buf1_size:11;      /* Transmit buffer1 size */
> > +             unsigned int buf2_size:11;      /* Transmit buffer2 size */
> > +             unsigned int ttse:1;            /* Transmit time stamp enable */
> > +             unsigned int dis_pad:1;         /* Disable pad (bit23) */
> > +             unsigned int adr_chain:1;       /* Second address chained */
> > +             unsigned int end_ring:1;        /* Transmit end of ring */
> > +             unsigned int crc_dis:1;         /* Disable CRC */
> > +             unsigned int cic:2;             /* Checksum insertion control (bit27:28) */
> > +             unsigned int first_sg:1;        /* First Segment */
> > +             unsigned int last_seg:1;        /* Last Segment */
> > +             unsigned int interrupt:1;       /* Interrupt on completion */
> > +     } tx;
> > +
> > +     struct {
> > +             /* RDES1 */
> > +             unsigned int buf1_size:11;      /* Received buffer1 size */
> > +             unsigned int buf2_size:11;      /* Received buffer2 size */
> > +             unsigned int reserved1:2;
> > +             unsigned int adr_chain:1;       /* Second address chained */
> > +             unsigned int end_ring:1;                /* Received end of ring */
> > +             unsigned int reserved2:5;
> > +             unsigned int dis_ic:1;          /* Disable interrupt on completion */
> > +     } rx;
> > +
> > +     unsigned int all;
> > +} desc1_u;
> > +
> > +typedef struct dma_desc {
> > +     desc0_u desc0;
> > +     desc1_u desc1;
> > +     /* The address of buffers */
> > +     unsigned int    desc2;
> > +     /* Next desc's address */
> > +     unsigned int    desc3;
> > +} __attribute__((packed)) dma_desc_t;
> > +
> > +enum rx_frame_status { /* IPC status */
> > +     good_frame = 0,
> > +     discard_frame = 1,
> > +     csum_none = 2,
> > +     llc_snap = 4,
> > +};
> > +
> > +enum tx_dma_irq_status {
> > +     tx_hard_error = 1,
> > +     tx_hard_error_bump_tc = 2,
> > +     handle_tx_rx = 3,
> > +};
> > +
> > +struct geth_extra_stats {
> > +     /* Transmit errors */
> > +     unsigned long tx_underflow;
> > +     unsigned long tx_carrier;
> > +     unsigned long tx_losscarrier;
> > +     unsigned long vlan_tag;
> > +     unsigned long tx_deferred;
> > +     unsigned long tx_vlan;
> > +     unsigned long tx_jabber;
> > +     unsigned long tx_frame_flushed;
> > +     unsigned long tx_payload_error;
> > +     unsigned long tx_ip_header_error;
> > +
> > +     /* Receive errors */
> > +     unsigned long rx_desc;
> > +     unsigned long sa_filter_fail;
> > +     unsigned long overflow_error;
> > +     unsigned long ipc_csum_error;
> > +     unsigned long rx_collision;
> > +     unsigned long rx_crc;
> > +     unsigned long dribbling_bit;
> > +     unsigned long rx_length;
> > +     unsigned long rx_mii;
> > +     unsigned long rx_multicast;
> > +     unsigned long rx_gmac_overflow;
> > +     unsigned long rx_watchdog;
> > +     unsigned long da_rx_filter_fail;
> > +     unsigned long sa_rx_filter_fail;
> > +     unsigned long rx_missed_cntr;
> > +     unsigned long rx_overflow_cntr;
> > +     unsigned long rx_vlan;
> > +
> > +     /* Tx/Rx IRQ errors */
> > +     unsigned long tx_undeflow_irq;
> > +     unsigned long tx_process_stopped_irq;
> > +     unsigned long tx_jabber_irq;
> > +     unsigned long rx_overflow_irq;
> > +     unsigned long rx_buf_unav_irq;
> > +     unsigned long rx_process_stopped_irq;
> > +     unsigned long rx_watchdog_irq;
> > +     unsigned long tx_early_irq;
> > +     unsigned long fatal_bus_error_irq;
> > +
> > +     /* Extra info */
> > +     unsigned long threshold;
> > +     unsigned long tx_pkt_n;
> > +     unsigned long rx_pkt_n;
> > +     unsigned long poll_n;
> > +     unsigned long sched_timer_n;
> > +     unsigned long normal_irq_n;
> > +};
> > +
> > +int sunxi_mdio_read(void *,  int, int);
> > +int sunxi_mdio_write(void *, int, int, unsigned short);
> > +int sunxi_mdio_reset(void *);
> > +void sunxi_set_link_mode(void *iobase, int duplex, int speed);
> > +void sunxi_int_disable(void *);
> > +int sunxi_int_status(void *, struct geth_extra_stats *x);
> > +int sunxi_mac_init(void *, int txmode, int rxmode);
> > +void sunxi_set_umac(void *, unsigned char *, int);
> > +void sunxi_mac_enable(void *);
> > +void sunxi_mac_disable(void *);
> > +void sunxi_tx_poll(void *);
> > +void sunxi_int_enable(void *);
> > +void sunxi_start_rx(void *, unsigned long);
> > +void sunxi_start_tx(void *, unsigned long);
> > +void sunxi_stop_tx(void *);
> > +void sunxi_stop_rx(void *);
> > +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high);
> > +void sunxi_set_filter(void *iobase, unsigned long flags);
> > +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause);
> > +void sunxi_mac_loopback(void *iobase, int enable);
> > +
> > +void desc_buf_set(struct dma_desc *p, unsigned long paddr, int size);
> > +void desc_set_own(struct dma_desc *p);
> > +void desc_init_chain(struct dma_desc *p, unsigned long paddr,  unsigned int size);
> > +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert);
> > +void desc_init(struct dma_desc *p);
> > +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> > +int desc_buf_get_len(struct dma_desc *desc);
> > +int desc_buf_get_addr(struct dma_desc *desc);
> > +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> > +int desc_get_own(struct dma_desc *desc);
> > +int desc_get_tx_ls(struct dma_desc *desc);
> > +int desc_rx_frame_len(struct dma_desc *desc);
> > +
> > +int sunxi_mac_reset(void *iobase, void (*mdelay)(int), int n);
> > +int sunxi_geth_register(void *iobase, int version, unsigned int div);
> > +
> > +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> > +extern int ephy_is_enable(void);
> > +#endif
> > +
> > +#if IS_ENABLED(CONFIG_ARCH_SUN8IW3) \
> > +     || IS_ENABLED(CONFIG_ARCH_SUN9IW1) \
> > +     || IS_ENABLED(CONFIG_ARCH_SUN7I)
> > +#define HW_VERSION   0
> > +#else
> > +#define HW_VERSION   1
> > +#endif
> > +
> > +#endif
> > diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
> > index 821e85a..3c86c2a 100644
> > --- a/drivers/net/phy/realtek.c
> > +++ b/drivers/net/phy/realtek.c
> > @@ -338,7 +338,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)
> >                       "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
> >                       val_txdly ? "enabled" : "disabled");
> >       }
> > -
> > +return 0;
> >       ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY,
> >                                      val_rxdly);
> >       if (ret < 0) {
>



--
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 15:32     ` Guo Ren
@ 2021-06-06 15:39       ` Jernej Škrabec
  2021-06-06 15:41         ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-06 15:39 UTC (permalink / raw)
  To: Andre Przywara, Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

Hi!

Dne nedelja, 06. junij 2021 ob 17:32:22 CEST je Guo Ren napisal(a):
>  ,
> 
> On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <andre.przywara@arm.com> 
wrote:
> > On Sun,  6 Jun 2021 09:04:09 +0000
> > guoren@kernel.org wrote:
> > 
> > Hi,
> > 
> > > From: liush <liush@allwinnertech.com>
> > > 
> > > This is a temporary driver, only guaranteed to work on allwinner
> > > D1. In order to ensure the developer's demand for network usage.
> > 
> > That looks like some Allwinner BSP driver, please don't endorse code
> > of this quality (just look at all that commented code and the attempt
> > for compile-time configuration).
> > 
> > > It only could work at 1Gps mode.
> > > 
> > > The correct gmac driver should follow (I guess)
> > > drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> > > 
> > > If anyone is familiar with it and can help porting, I would be
> > > very grateful.
> > 
> > Have you tried compiling and using that driver? Ideally it should just
> > work, Linux drivers are meant to be portable, by design. And the driver
> > is already enabled by COMPILE_TEST.
> 
> It still needs some work with dwmac-sun8i.c glue layer, eg:
> tx/rx-delay setting, clk & pinmux drivers.
> 
> The patch is just to help people using D1 with GMAC temporarily with
> network function.

It should be marked "DO NOT MERGE" or similar then.

Best regards,
Jernej

> 
> > But I guess you need some extra care to make the non-coherent DMA work?
> > I haven't looked in detail, but are those new CMOs hooked into the
> > generic DMA framework?
> 
> Yes, we have the simliar principle with arm & csky for non-coherent:
>  - Using PTE attributes setting Using PTE attributes to support
> _PAGE_IOREMAP & _PAGE_WRITECOMBINE
>  - Using CMO instructions deal SYNC_DMA_FOR_CPU/DEVICE.
> 
> > Cheers,
> > Andre
> > 
> > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > Tested-by: Guo Ren <guoren@kernel.org>
> > > Signed-off-by: Guo Ren <guoren@kernel.org>
> > > Cc: Maxime Ripard <mripard@kernel.org>
> > > Cc: Corentin Labbe <clabbe@baylibre.com>
> > > Cc: Samuel Holland <samuel@sholland.org>
> > > Cc: Icenowy Zheng <icenowy@aosc.io>
> > > Cc: LABBE Corentin <clabbe.montjoie@gmail.com>
> > > Cc: Michael Walle <michael@walle.cc>
> > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > Cc: Wei Fu <wefu@redhat.com>
> > > Cc: Wei Wu <lazyparser@gmail.com>
> > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 15:39       ` Jernej Škrabec
@ 2021-06-06 15:41         ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 15:41 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Andre Przywara, Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens,
	maxime, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

On Sun, Jun 6, 2021 at 11:39 PM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Hi!
>
> Dne nedelja, 06. junij 2021 ob 17:32:22 CEST je Guo Ren napisal(a):
> >  ,
> >
> > On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <andre.przywara@arm.com>
> wrote:
> > > On Sun,  6 Jun 2021 09:04:09 +0000
> > > guoren@kernel.org wrote:
> > >
> > > Hi,
> > >
> > > > From: liush <liush@allwinnertech.com>
> > > >
> > > > This is a temporary driver, only guaranteed to work on allwinner
> > > > D1. In order to ensure the developer's demand for network usage.
> > >
> > > That looks like some Allwinner BSP driver, please don't endorse code
> > > of this quality (just look at all that commented code and the attempt
> > > for compile-time configuration).
> > >
> > > > It only could work at 1Gps mode.
> > > >
> > > > The correct gmac driver should follow (I guess)
> > > > drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> > > >
> > > > If anyone is familiar with it and can help porting, I would be
> > > > very grateful.
> > >
> > > Have you tried compiling and using that driver? Ideally it should just
> > > work, Linux drivers are meant to be portable, by design. And the driver
> > > is already enabled by COMPILE_TEST.
> >
> > It still needs some work with dwmac-sun8i.c glue layer, eg:
> > tx/rx-delay setting, clk & pinmux drivers.
> >
> > The patch is just to help people using D1 with GMAC temporarily with
> > network function.
>
> It should be marked "DO NOT MERGE" or similar then.
Yes, thx for reminding. I'll fix it next time.

>
> Best regards,
> Jernej
>
> >
> > > But I guess you need some extra care to make the non-coherent DMA work?
> > > I haven't looked in detail, but are those new CMOs hooked into the
> > > generic DMA framework?
> >
> > Yes, we have the simliar principle with arm & csky for non-coherent:
> >  - Using PTE attributes setting Using PTE attributes to support
> > _PAGE_IOREMAP & _PAGE_WRITECOMBINE
> >  - Using CMO instructions deal SYNC_DMA_FOR_CPU/DEVICE.
> >
> > > Cheers,
> > > Andre
> > >
> > > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > > Tested-by: Guo Ren <guoren@kernel.org>
> > > > Signed-off-by: Guo Ren <guoren@kernel.org>
> > > > Cc: Maxime Ripard <mripard@kernel.org>
> > > > Cc: Corentin Labbe <clabbe@baylibre.com>
> > > > Cc: Samuel Holland <samuel@sholland.org>
> > > > Cc: Icenowy Zheng <icenowy@aosc.io>
> > > > Cc: LABBE Corentin <clabbe.montjoie@gmail.com>
> > > > Cc: Michael Walle <michael@walle.cc>
> > > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > > Cc: Wei Fu <wefu@redhat.com>
> > > > Cc: Wei Wu <lazyparser@gmail.com>
> > > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06  9:04 ` [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use guoren
  2021-06-06 10:50   ` Andre Przywara
@ 2021-06-06 16:16   ` Arnd Bergmann
  2021-06-06 16:32     ` Jernej Škrabec
  2021-06-06 16:53     ` Guo Ren
  1 sibling, 2 replies; 52+ messages in thread
From: Arnd Bergmann @ 2021-06-06 16:16 UTC (permalink / raw)
  To: Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Chen-Yu Tsai, Maxime Ripard,
	Drew Fustini, liush, lazyparser, wefu, linux-riscv,
	Linux Kernel Mailing List, linux-arch, linux-sunxi,
	Maxime Ripard, Corentin Labbe, Samuel Holland, Icenowy Zheng,
	LABBE Corentin, Michael Walle, Guo Ren

On Sun, Jun 6, 2021 at 11:04 AM <guoren@kernel.org> wrote:

> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> index cd9f7c9..31b681d 100644
> --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> @@ -11,7 +11,7 @@
>         compatible = "allwinner,d1-nezha-kit";
>
>         chosen {
> -               bootargs = "console=ttyS0,115200";
> +               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";

These are not board specific options, they should be set by the bootloader
according to the network environment. It clearly doens't belong
into this patch .

>                 stdout-path = &serial0;
>         };
>
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> index 11cd938..d317e19 100644
> --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> @@ -80,5 +80,21 @@
>                         clocks = <&dummy_apb>;
>                         status = "disabled";
>                 };
> +
> +               eth@4500000 {
> +                       compatible = "allwinner,sunxi-gmac";
> +                       reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
> +                       interrupts-extended = <&plic 0x3e 0x04>;
> +                       interrupt-names = "gmacirq";
> +                       device_type = "gmac0";
> +                       phy-mode = "rgmii";
> +                       use_ephy25m = <0x01>;
> +                       tx-delay = <0x03>;
> +                       rx-delay = <0x03>;
> +                       gmac-power0;
> +                       gmac-power1;
> +                       gmac-power2;
> +                       status = "okay";
> +               };

Before you add this in the dts file, the properties need to be documented in
the binding file. The "allwinner,sunxi-gmac" identifier does not appear to
be specific enough here, and the properties don't match what dwmac uses,
which would make it unnecessarily hard to change to the other driver
later on without breaking compatibility to old dtb files.

> +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> @@ -0,0 +1,690 @@
> +/*
> + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> + *
> + * Copyright © 2016-2018, fuzhaoke
> + *             Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> + *
> + * This file is provided under a dual BSD/GPL license.  When using or
> + * redistributing this file, you may do so under either license.

Are you sure this is the correct copyright information and "fuzhaoke" is
the copyright holder for this file? If this is derived from either the
designware
code or the Linux stmmac driver, the authors should be mentioned,
and the license be compatible with the original license terms.

Andre already commented on the driver quality and code duplication, those are
also show-stoppers, but the unclear license terms and dt binding compatibility
are even stronger reasons to not get anywhere close to this driver.

        Arnd

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-06  9:04 ` [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board guoren
@ 2021-06-06 16:26   ` Jernej Škrabec
  2021-06-06 17:05     ` Guo Ren
  2021-06-07  3:44     ` Guo Ren
  2021-06-07  7:24   ` Maxime Ripard
  1 sibling, 2 replies; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-06 16:26 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu, guoren
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Atish Patra, Christoph Hellwig

Hi!

I didn't go through all details. After you fix all comments below, you should 
run "make dtbs_check" and fix all reported warnings too.

Dne nedelja, 06. junij 2021 ob 11:04:07 CEST je guoren@kernel.org napisal(a):
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> Add initial DTS for Allwinner D1 NeZha board having only essential
> devices (uart, dummy, clock, reset, clint, plic, etc).
> 
> Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> Cc: Anup Patel <anup.patel@wdc.com>
> Cc: Atish Patra <atish.patra@wdc.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Cc: Drew Fustini <drew@beagleboard.org>
> Cc: Maxime Ripard <maxime@cerno.tech>
> Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> Cc: Wei Fu <wefu@redhat.com>
> Cc: Wei Wu <lazyparser@gmail.com>
> ---
>  arch/riscv/boot/dts/Makefile                       |  1 +
>  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
>  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
>  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84
> ++++++++++++++++++++++ 4 files changed, 116 insertions(+)
>  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> 
> diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> index fe996b8..3e7b264 100644
> --- a/arch/riscv/boot/dts/Makefile
> +++ b/arch/riscv/boot/dts/Makefile
> @@ -2,5 +2,6 @@
>  subdir-y += sifive
>  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
>  subdir-y += microchip
> +subdir-y += allwinner
> 
>  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> diff --git a/arch/riscv/boot/dts/allwinner/Makefile
> b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644
> index 00000000..4adbf4b
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode
> 100644
> index 00000000..cd9f7c9
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Board DT names are comprised of soc name and board name, in this case it would 
be "sun20i-d1-nezha-kit.dts"

> @@ -0,0 +1,29 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

Usually copyrights are added below spdx id.

> +
> +/dts-v1/;
> +
> +#include "allwinner-d1.dtsi"
> +
> +/ {
> +	#address-cells = <2>;
> +	#size-cells = <2>;

This should be part of SoC level DTSI.

> +	model = "Allwinner D1 NeZha Kit";
> +	compatible = "allwinner,d1-nezha-kit";

Board specific compatible string should be followed with SoC compatible, in 
this case "allwinner,sun20i-d1".  You should document it too.

> +
> +	chosen {
> +		bootargs = "console=ttyS0,115200";

Above line doesn't belong here. If anything, it should be added dynamically by 
bootloader.

> +		stdout-path = &serial0;
> +	};
> +
> +	memory@40000000 {
> +		device_type = "memory";
> +		reg = <0x0 0x40000000 0x0 0x20000000>;
> +	};

Ditto for whole memory node.

> +
> +	soc {
> +	};

There is no point having empty nodes.

> +};
> +
> +&serial0 {
> +	status = "okay";
> +};
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644
> index 00000000..11cd938
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Current naming approach for Allwinner SoC level DTSI is "sun20i-d1.dtsi".

> @@ -0,0 +1,84 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)

> +
> +/dts-v1/;
> +
> +/ {
> +	#address-cells = <2>;
> +	#size-cells = <2>;

Since all peripherals and memory are below 4 GiB, why have 64-bit addresses 
and sizes? It just clutters DT.

> +	model = "Allwinner D1 Soc";
> +	compatible = "allwinner,d1-nezha-kit";

Compatible and model don't belong to SoC level DTSI.

> +
> +	chosen {
> +	};

Remove empty node.

> +
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		timebase-frequency = <2400000>;
> +		cpu@0 {
> +			device_type = "cpu";
> +			reg = <0>;
> +			status = "okay";
> +			compatible = "riscv";
> +			riscv,isa = "rv64imafdcv";
> +			mmu-type = "riscv,sv39";
> +			cpu0_intc: interrupt-controller {
> +				#interrupt-cells = <1>;
> +				compatible = "riscv,cpu-intc";
> +				interrupt-controller;
> +			};
> +		};
> +	};
> +
> +	soc {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		compatible = "simple-bus";
> +		ranges;
> +
> +		reset: reset-sample {
> +			compatible = "thead,reset-sample";
> +			plic-delegate = <0x0 0x101ffffc>;
> +		};
> +
> +		clint: clint@14000000 {
> +			compatible = "riscv,clint0";
> +			interrupts-extended = <
> +				&cpu0_intc  3 &cpu0_intc  7
> +				>;
> +			reg = <0x0 0x14000000 0x0 0x04000000>;
> +			clint,has-no-64bit-mmio;
> +		};
> +
> +		plic: interrupt-controller@10000000 {
> +			#interrupt-cells = <1>;
> +			compatible = "riscv,plic0";
> +			interrupt-controller;
> +			interrupts-extended = <
> +				&cpu0_intc  0xffffffff &cpu0_intc  9
> +				>;
> +			reg = <0x0 0x10000000 0x0 0x04000000>;
> +			reg-names = "control";
> +			riscv,max-priority = <7>;
> +			riscv,ndev = <200>;
> +		};
> +
> +		dummy_apb: apb-clock {
> +			compatible = "fixed-clock";
> +			clock-frequency = <24000000>;
> +			clock-output-names = "dummy_apb";
> +			#clock-cells = <0>;
> +		};
> +
> +		serial0: serial@2500000 {

This should be uart0 and board should have alias for it. Check ARM based 
Allwinner DTs.

Best regards,
Jernej

> +			compatible = "snps,dw-apb-uart";
> +			reg = <0x0 0x02500000 0x0 0x400>;
> +			reg-io-width = <4>;
> +			reg-shift = <2>;
> +			interrupt-parent = <&plic>;
> +			interrupts = <18>;
> +			clocks = <&dummy_apb>;
> +			status = "disabled";
> +		};
> +	};
> +};





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (13 preceding siblings ...)
  2021-06-06  9:04 ` [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use guoren
@ 2021-06-06 16:29 ` Jernej Škrabec
  2021-06-06 16:54   ` Guo Ren
  2021-06-07  3:44 ` Anup Patel
  15 siblings, 1 reply; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-06 16:29 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu, guoren
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren

Hi!

Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je guoren@kernel.org napisal(a):
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> vendors define the custom properties of memory regions in PTE.
> 
> This patchset helps SOC vendors to support their own custom interconnect
> coherent solution with PTE attributes.
> 
> For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906 has
> two modes in MMU:
>  - Compatible mode, the same as the definitions in spec.
>  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
>    not mentioned in spec.
> 
> Allwinner D1 needs the enhanced mode to support the DMA type device with
> non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> attribute bits in PTE.
> 
> The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been
> tested on D1:
>  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
>    conflict with RISC-V spec, and hope these patches soon could be
>    approved.
>  - pgtable: Using a image-hdr to pass vendor specific information and
>    setup custom PTE attributes in a global struct variable during boot
>    stage. Also it needs define custom protection_map in linux/mm.
>  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
>    In this patchset, I just show you how T-HEAD C9xx work, and seems Atish
>    is working for the DMA infrustructure, please let me know the idea.
>  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> 
> The patchset could work with linux-5.13-rc4, here is the steps for D1:
>  - Download linux-5.13-rc4 and apply the patchset
>  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
>  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
>  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e 0x00200000
> -n Linux -d arch/riscv/boot/Image uImage - Download newest opensbi [2],
> build with [3], and get fw_dynamic.bin - Copy uImage, fw_dynamic.bin,
> allwinner-d1-nezha-kit.dtb into boot partition of TF card.
>  - Plugin the TF card and power on D1.
> 
> Link: https://linux-sunxi.org/D1 [1]
> Link: https://github.com/riscv/opensbi branch:master [2]
> Link:
> https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md
> [3]
> 

Some patches are marked with v2 and some V5. It's very confusing. Mark them 
with same version in next revision.

Best regards,
Jernej

> Changes since v1:
>  - Rebase on linux-5.13-rc4
>  - Support defconfig for different PTE attributes
>  - Support C906 icache_sync
>  - Add Allwinner D1 dts & Kconfig & gmac for testing
>  - Add asid optimization for D1 usage
> 
> Guo Ren (10):
>   riscv: asid: Use global mappings for kernel pages
>   riscv: asid: Add ASID-based tlbflushing methods
>   riscv: asid: Optimize tlbflush coding convention
>   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
>   riscv: pgtable: Add custom protection_map init
>   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
>   riscv: cmo: Add dma-noncoherency support
>   riscv: cmo: Add vendor custom icache sync
>   riscv: soc: Initial DTS for Allwinner D1 NeZha board
>   riscv: soc: Add Allwinner SoC kconfig option
> 
> liush (1):
>   riscv: soc: Allwinner D1 GMAC driver only for temp use
> 
>  arch/riscv/Kconfig                                 |    9 +
>  arch/riscv/Kconfig.socs                            |   12 +
>  arch/riscv/boot/dts/Makefile                       |    1 +
>  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
>  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
>  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
>  arch/riscv/configs/defconfig                       |    1 +
>  arch/riscv/include/asm/cacheflush.h                |   48 +-
>  arch/riscv/include/asm/mmu_context.h               |    2 +
>  arch/riscv/include/asm/pgtable-64.h                |    8 +-
>  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
>  arch/riscv/include/asm/pgtable.h                   |   44 +-
>  arch/riscv/include/asm/sbi.h                       |   15 +
>  arch/riscv/include/asm/soc.h                       |    1 +
>  arch/riscv/include/asm/tlbflush.h                  |   22 +
>  arch/riscv/include/asm/vendorid_list.h             |    1 +
>  arch/riscv/kernel/sbi.c                            |   19 +
>  arch/riscv/kernel/soc.c                            |   22 +
>  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
>  arch/riscv/mm/Makefile                             |    1 +
>  arch/riscv/mm/cacheflush.c                         |    3 +-
>  arch/riscv/mm/context.c                            |    2 +-
>  arch/riscv/mm/dma-mapping.c                        |   53 +
>  arch/riscv/mm/init.c                               |   26 +
>  arch/riscv/mm/tlbflush.c                           |   57 +-
>  drivers/net/ethernet/Kconfig                       |    1 +
>  drivers/net/ethernet/Makefile                      |    1 +
>  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
>  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> ++++++++++++++++++++ drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     | 
> 258 +++
>  drivers/net/phy/realtek.c                          |    2 +-
>  mm/mmap.c                                          |    4 +
>  34 files changed, 3714 insertions(+), 37 deletions(-)
>  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi create
> mode 100644 arch/riscv/mm/dma-mapping.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
>  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 16:16   ` Arnd Bergmann
@ 2021-06-06 16:32     ` Jernej Škrabec
  2021-06-06 16:53       ` Guo Ren
  2021-06-06 16:53     ` Guo Ren
  1 sibling, 1 reply; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-06 16:32 UTC (permalink / raw)
  To: Guo Ren, Arnd Bergmann
  Cc: Anup Patel, Palmer Dabbelt, Chen-Yu Tsai, Maxime Ripard,
	Drew Fustini, liush, lazyparser, wefu, linux-riscv,
	Linux Kernel Mailing List, linux-arch, linux-sunxi,
	Maxime Ripard, Corentin Labbe, Samuel Holland, Icenowy Zheng,
	LABBE Corentin, Michael Walle, Guo Ren

Dne nedelja, 06. junij 2021 ob 18:16:44 CEST je Arnd Bergmann napisal(a):
> On Sun, Jun 6, 2021 at 11:04 AM <guoren@kernel.org> wrote:
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts index
> > cd9f7c9..31b681d 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > @@ -11,7 +11,7 @@
> > 
> >         compatible = "allwinner,d1-nezha-kit";
> >         
> >         chosen {
> > 
> > -               bootargs = "console=ttyS0,115200";
> > +               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init
> > root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock
> > ip=192.168.101.23";
> These are not board specific options, they should be set by the bootloader
> according to the network environment. It clearly doens't belong
> into this patch .
> 
> >                 stdout-path = &serial0;
> >         
> >         };
> > 
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi index 11cd938..d317e19
> > 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > @@ -80,5 +80,21 @@
> > 
> >                         clocks = <&dummy_apb>;
> >                         status = "disabled";
> >                 
> >                 };
> > 
> > +
> > +               eth@4500000 {
> > +                       compatible = "allwinner,sunxi-gmac";
> > +                       reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030
> > 0x00 0x04>; +                       interrupts-extended = <&plic 0x3e
> > 0x04>;
> > +                       interrupt-names = "gmacirq";
> > +                       device_type = "gmac0";
> > +                       phy-mode = "rgmii";
> > +                       use_ephy25m = <0x01>;
> > +                       tx-delay = <0x03>;
> > +                       rx-delay = <0x03>;
> > +                       gmac-power0;
> > +                       gmac-power1;
> > +                       gmac-power2;
> > +                       status = "okay";
> > +               };
> 
> Before you add this in the dts file, the properties need to be documented in
> the binding file. The "allwinner,sunxi-gmac" identifier does not appear to
> be specific enough here, and the properties don't match what dwmac uses,
> which would make it unnecessarily hard to change to the other driver later
> on without breaking compatibility to old dtb files.
> 
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > @@ -0,0 +1,690 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *             Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> 
> Are you sure this is the correct copyright information and "fuzhaoke" is
> the copyright holder for this file? If this is derived from either the
> designware
> code or the Linux stmmac driver, the authors should be mentioned,
> and the license be compatible with the original license terms.
> 
> Andre already commented on the driver quality and code duplication, those
> are also show-stoppers, but the unclear license terms and dt binding
> compatibility are even stronger reasons to not get anywhere close to this
> driver.

I got impression that this patch is not meant to be merged and it's forward 
ported from vendor kernel as a stop gap measure for developers until proper 
mainline ethernet driver is developed.

Best regards,
Jernej





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 16:16   ` Arnd Bergmann
  2021-06-06 16:32     ` Jernej Škrabec
@ 2021-06-06 16:53     ` Guo Ren
  1 sibling, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 16:53 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Anup Patel, Palmer Dabbelt, Chen-Yu Tsai, Maxime Ripard,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

Sorry, wast your time reviewing the patch. It's not ready to merge,
just for the test.

On Mon, Jun 7, 2021 at 12:19 AM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Sun, Jun 6, 2021 at 11:04 AM <guoren@kernel.org> wrote:
>
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > index cd9f7c9..31b681d 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > @@ -11,7 +11,7 @@
> >         compatible = "allwinner,d1-nezha-kit";
> >
> >         chosen {
> > -               bootargs = "console=ttyS0,115200";
> > +               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";
>
> These are not board specific options, they should be set by the bootloader
> according to the network environment. It clearly doens't belong
> into this patch .
>
> >                 stdout-path = &serial0;
> >         };
> >
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > index 11cd938..d317e19 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > @@ -80,5 +80,21 @@
> >                         clocks = <&dummy_apb>;
> >                         status = "disabled";
> >                 };
> > +
> > +               eth@4500000 {
> > +                       compatible = "allwinner,sunxi-gmac";
> > +                       reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
> > +                       interrupts-extended = <&plic 0x3e 0x04>;
> > +                       interrupt-names = "gmacirq";
> > +                       device_type = "gmac0";
> > +                       phy-mode = "rgmii";
> > +                       use_ephy25m = <0x01>;
> > +                       tx-delay = <0x03>;
> > +                       rx-delay = <0x03>;
> > +                       gmac-power0;
> > +                       gmac-power1;
> > +                       gmac-power2;
> > +                       status = "okay";
> > +               };
>
> Before you add this in the dts file, the properties need to be documented in
> the binding file. The "allwinner,sunxi-gmac" identifier does not appear to
> be specific enough here, and the properties don't match what dwmac uses,
> which would make it unnecessarily hard to change to the other driver
> later on without breaking compatibility to old dtb files.
>
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > @@ -0,0 +1,690 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *             Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
>
> Are you sure this is the correct copyright information and "fuzhaoke" is
> the copyright holder for this file? If this is derived from either the
> designware
> code or the Linux stmmac driver, the authors should be mentioned,
> and the license be compatible with the original license terms.
>
> Andre already commented on the driver quality and code duplication, those are
> also show-stoppers, but the unclear license terms and dt binding compatibility
> are even stronger reasons to not get anywhere close to this driver.
>
>         Arnd



-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use
  2021-06-06 16:32     ` Jernej Škrabec
@ 2021-06-06 16:53       ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 16:53 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Arnd Bergmann, Anup Patel, Palmer Dabbelt, Chen-Yu Tsai,
	Maxime Ripard, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Maxime Ripard, Corentin Labbe, Samuel Holland,
	Icenowy Zheng, LABBE Corentin, Michael Walle, Guo Ren

On Mon, Jun 7, 2021 at 12:32 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Dne nedelja, 06. junij 2021 ob 18:16:44 CEST je Arnd Bergmann napisal(a):
> > On Sun, Jun 6, 2021 at 11:04 AM <guoren@kernel.org> wrote:
> > > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts index
> > > cd9f7c9..31b681d 100644
> > > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > @@ -11,7 +11,7 @@
> > >
> > >         compatible = "allwinner,d1-nezha-kit";
> > >
> > >         chosen {
> > >
> > > -               bootargs = "console=ttyS0,115200";
> > > +               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init
> > > root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock
> > > ip=192.168.101.23";
> > These are not board specific options, they should be set by the bootloader
> > according to the network environment. It clearly doens't belong
> > into this patch .
> >
> > >                 stdout-path = &serial0;
> > >
> > >         };
> > >
> > > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi index 11cd938..d317e19
> > > 100644
> > > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > @@ -80,5 +80,21 @@
> > >
> > >                         clocks = <&dummy_apb>;
> > >                         status = "disabled";
> > >
> > >                 };
> > >
> > > +
> > > +               eth@4500000 {
> > > +                       compatible = "allwinner,sunxi-gmac";
> > > +                       reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030
> > > 0x00 0x04>; +                       interrupts-extended = <&plic 0x3e
> > > 0x04>;
> > > +                       interrupt-names = "gmacirq";
> > > +                       device_type = "gmac0";
> > > +                       phy-mode = "rgmii";
> > > +                       use_ephy25m = <0x01>;
> > > +                       tx-delay = <0x03>;
> > > +                       rx-delay = <0x03>;
> > > +                       gmac-power0;
> > > +                       gmac-power1;
> > > +                       gmac-power2;
> > > +                       status = "okay";
> > > +               };
> >
> > Before you add this in the dts file, the properties need to be documented in
> > the binding file. The "allwinner,sunxi-gmac" identifier does not appear to
> > be specific enough here, and the properties don't match what dwmac uses,
> > which would make it unnecessarily hard to change to the other driver later
> > on without breaking compatibility to old dtb files.
> >
> > > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > > @@ -0,0 +1,690 @@
> > > +/*
> > > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> > > + *
> > > + * Copyright © 2016-2018, fuzhaoke
> > > + *             Author: fuzhaoke <fuzhaoke@allwinnertech.com>
> > > + *
> > > + * This file is provided under a dual BSD/GPL license.  When using or
> > > + * redistributing this file, you may do so under either license.
> >
> > Are you sure this is the correct copyright information and "fuzhaoke" is
> > the copyright holder for this file? If this is derived from either the
> > designware
> > code or the Linux stmmac driver, the authors should be mentioned,
> > and the license be compatible with the original license terms.
> >
> > Andre already commented on the driver quality and code duplication, those
> > are also show-stoppers, but the unclear license terms and dt binding
> > compatibility are even stronger reasons to not get anywhere close to this
> > driver.
>
> I got impression that this patch is not meant to be merged and it's forward
> ported from vendor kernel as a stop gap measure for developers until proper
> mainline ethernet driver is developed.
Yes

>
> Best regards,
> Jernej
>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-06 16:29 ` [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 Jernej Škrabec
@ 2021-06-06 16:54   ` Guo Ren
  2021-06-06 17:14     ` Jernej Škrabec
  0 siblings, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-06 16:54 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren

V5 is not related to the patch series.

On Mon, Jun 7, 2021 at 12:29 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Hi!
>
> Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je guoren@kernel.org napisal(a):
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> > vendors define the custom properties of memory regions in PTE.
> >
> > This patchset helps SOC vendors to support their own custom interconnect
> > coherent solution with PTE attributes.
> >
> > For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906 has
> > two modes in MMU:
> >  - Compatible mode, the same as the definitions in spec.
> >  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
> >    not mentioned in spec.
> >
> > Allwinner D1 needs the enhanced mode to support the DMA type device with
> > non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> > attribute bits in PTE.
> >
> > The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been
> > tested on D1:
> >  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
> >    conflict with RISC-V spec, and hope these patches soon could be
> >    approved.
> >  - pgtable: Using a image-hdr to pass vendor specific information and
> >    setup custom PTE attributes in a global struct variable during boot
> >    stage. Also it needs define custom protection_map in linux/mm.
> >  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
> >    In this patchset, I just show you how T-HEAD C9xx work, and seems Atish
> >    is working for the DMA infrustructure, please let me know the idea.
> >  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> >
> > The patchset could work with linux-5.13-rc4, here is the steps for D1:
> >  - Download linux-5.13-rc4 and apply the patchset
> >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
> >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
> >  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e 0x00200000
> > -n Linux -d arch/riscv/boot/Image uImage - Download newest opensbi [2],
> > build with [3], and get fw_dynamic.bin - Copy uImage, fw_dynamic.bin,
> > allwinner-d1-nezha-kit.dtb into boot partition of TF card.
> >  - Plugin the TF card and power on D1.
> >
> > Link: https://linux-sunxi.org/D1 [1]
> > Link: https://github.com/riscv/opensbi branch:master [2]
> > Link:
> > https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md
> > [3]
> >
>
> Some patches are marked with v2 and some V5. It's very confusing. Mark them
> with same version in next revision.
>
> Best regards,
> Jernej
>
> > Changes since v1:
> >  - Rebase on linux-5.13-rc4
> >  - Support defconfig for different PTE attributes
> >  - Support C906 icache_sync
> >  - Add Allwinner D1 dts & Kconfig & gmac for testing
> >  - Add asid optimization for D1 usage
> >
> > Guo Ren (10):
> >   riscv: asid: Use global mappings for kernel pages
> >   riscv: asid: Add ASID-based tlbflushing methods
> >   riscv: asid: Optimize tlbflush coding convention
> >   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
> >   riscv: pgtable: Add custom protection_map init
> >   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
> >   riscv: cmo: Add dma-noncoherency support
> >   riscv: cmo: Add vendor custom icache sync
> >   riscv: soc: Initial DTS for Allwinner D1 NeZha board
> >   riscv: soc: Add Allwinner SoC kconfig option
> >
> > liush (1):
> >   riscv: soc: Allwinner D1 GMAC driver only for temp use
> >
> >  arch/riscv/Kconfig                                 |    9 +
> >  arch/riscv/Kconfig.socs                            |   12 +
> >  arch/riscv/boot/dts/Makefile                       |    1 +
> >  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
> >  arch/riscv/configs/defconfig                       |    1 +
> >  arch/riscv/include/asm/cacheflush.h                |   48 +-
> >  arch/riscv/include/asm/mmu_context.h               |    2 +
> >  arch/riscv/include/asm/pgtable-64.h                |    8 +-
> >  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
> >  arch/riscv/include/asm/pgtable.h                   |   44 +-
> >  arch/riscv/include/asm/sbi.h                       |   15 +
> >  arch/riscv/include/asm/soc.h                       |    1 +
> >  arch/riscv/include/asm/tlbflush.h                  |   22 +
> >  arch/riscv/include/asm/vendorid_list.h             |    1 +
> >  arch/riscv/kernel/sbi.c                            |   19 +
> >  arch/riscv/kernel/soc.c                            |   22 +
> >  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
> >  arch/riscv/mm/Makefile                             |    1 +
> >  arch/riscv/mm/cacheflush.c                         |    3 +-
> >  arch/riscv/mm/context.c                            |    2 +-
> >  arch/riscv/mm/dma-mapping.c                        |   53 +
> >  arch/riscv/mm/init.c                               |   26 +
> >  arch/riscv/mm/tlbflush.c                           |   57 +-
> >  drivers/net/ethernet/Kconfig                       |    1 +
> >  drivers/net/ethernet/Makefile                      |    1 +
> >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> > ++++++++++++++++++++ drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |
> > 258 +++
> >  drivers/net/phy/realtek.c                          |    2 +-
> >  mm/mmap.c                                          |    4 +
> >  34 files changed, 3714 insertions(+), 37 deletions(-)
> >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi create
> > mode 100644 arch/riscv/mm/dma-mapping.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-06 16:26   ` Jernej Škrabec
@ 2021-06-06 17:05     ` Guo Ren
  2021-06-07  3:44     ` Guo Ren
  1 sibling, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 17:05 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 12:26 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Hi!
>
> I didn't go through all details. After you fix all comments below, you should
> run "make dtbs_check" and fix all reported warnings too.
>
> Dne nedelja, 06. junij 2021 ob 11:04:07 CEST je guoren@kernel.org napisal(a):
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > Add initial DTS for Allwinner D1 NeZha board having only essential
> > devices (uart, dummy, clock, reset, clint, plic, etc).
> >
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Cc: Anup Patel <anup.patel@wdc.com>
> > Cc: Atish Patra <atish.patra@wdc.com>
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Drew Fustini <drew@beagleboard.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > ---
> >  arch/riscv/boot/dts/Makefile                       |  1 +
> >  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84
> > ++++++++++++++++++++++ 4 files changed, 116 insertions(+)
> >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> >
> > diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> > index fe996b8..3e7b264 100644
> > --- a/arch/riscv/boot/dts/Makefile
> > +++ b/arch/riscv/boot/dts/Makefile
> > @@ -2,5 +2,6 @@
> >  subdir-y += sifive
> >  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
> >  subdir-y += microchip
> > +subdir-y += allwinner
> >
> >  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> > diff --git a/arch/riscv/boot/dts/allwinner/Makefile
> > b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644
> > index 00000000..4adbf4b
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode
> > 100644
> > index 00000000..cd9f7c9
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
>
> Board DT names are comprised of soc name and board name, in this case it would
> be "sun20i-d1-nezha-kit.dts"
>
> > @@ -0,0 +1,29 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>
> Usually copyrights are added below spdx id.
No certain copyrights, maybe let allwinner added later.

>
> > +
> > +/dts-v1/;
> > +
> > +#include "allwinner-d1.dtsi"
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
>
> This should be part of SoC level DTSI.
>
> > +     model = "Allwinner D1 NeZha Kit";
> > +     compatible = "allwinner,d1-nezha-kit";
>
> Board specific compatible string should be followed with SoC compatible, in
> this case "allwinner,sun20i-d1".  You should document it too.
Okay

>
> > +
> > +     chosen {
> > +             bootargs = "console=ttyS0,115200";
>
> Above line doesn't belong here. If anything, it should be added dynamically by
> bootloader.
Okay

>
> > +             stdout-path = &serial0;
> > +     };
> > +
> > +     memory@40000000 {
> > +             device_type = "memory";
> > +             reg = <0x0 0x40000000 0x0 0x20000000>;
> > +     };
>
> Ditto for whole memory node.
Okay remove it and let u-boot append.

>
> > +
> > +     soc {
> > +     };
>
> There is no point having empty nodes.
Okay.

>
> > +};
> > +
> > +&serial0 {
> > +     status = "okay";
> > +};
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644
> > index 00000000..11cd938
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
>
> Current naming approach for Allwinner SoC level DTSI is "sun20i-d1.dtsi".
>
> > @@ -0,0 +1,84 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>
> > +
> > +/dts-v1/;
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
>
> Since all peripherals and memory are below 4 GiB, why have 64-bit addresses
> and sizes? It just clutters DT.
>
> > +     model = "Allwinner D1 Soc";
> > +     compatible = "allwinner,d1-nezha-kit";
>
> Compatible and model don't belong to SoC level DTSI.
>
> > +
> > +     chosen {
> > +     };
>
> Remove empty node.
Okay.

>
> > +
> > +     cpus {
> > +             #address-cells = <1>;
> > +             #size-cells = <0>;
> > +             timebase-frequency = <2400000>;
> > +             cpu@0 {
> > +                     device_type = "cpu";
> > +                     reg = <0>;
> > +                     status = "okay";
> > +                     compatible = "riscv";
> > +                     riscv,isa = "rv64imafdcv";
> > +                     mmu-type = "riscv,sv39";
> > +                     cpu0_intc: interrupt-controller {
> > +                             #interrupt-cells = <1>;
> > +                             compatible = "riscv,cpu-intc";
> > +                             interrupt-controller;
> > +                     };
> > +             };
> > +     };
> > +
> > +     soc {
> > +             #address-cells = <2>;
> > +             #size-cells = <2>;
> > +             compatible = "simple-bus";
> > +             ranges;
> > +
> > +             reset: reset-sample {
> > +                     compatible = "thead,reset-sample";
> > +                     plic-delegate = <0x0 0x101ffffc>;
> > +             };
> > +
> > +             clint: clint@14000000 {
> > +                     compatible = "riscv,clint0";
> > +                     interrupts-extended = <
> > +                             &cpu0_intc  3 &cpu0_intc  7
> > +                             >;
> > +                     reg = <0x0 0x14000000 0x0 0x04000000>;
> > +                     clint,has-no-64bit-mmio;
> > +             };
> > +
> > +             plic: interrupt-controller@10000000 {
> > +                     #interrupt-cells = <1>;
> > +                     compatible = "riscv,plic0";
> > +                     interrupt-controller;
> > +                     interrupts-extended = <
> > +                             &cpu0_intc  0xffffffff &cpu0_intc  9
> > +                             >;
> > +                     reg = <0x0 0x10000000 0x0 0x04000000>;
> > +                     reg-names = "control";
> > +                     riscv,max-priority = <7>;
> > +                     riscv,ndev = <200>;
> > +             };
> > +
> > +             dummy_apb: apb-clock {
> > +                     compatible = "fixed-clock";
> > +                     clock-frequency = <24000000>;
> > +                     clock-output-names = "dummy_apb";
> > +                     #clock-cells = <0>;
> > +             };
> > +
> > +             serial0: serial@2500000 {
>
> This should be uart0 and board should have alias for it. Check ARM based
> Allwinner DTs.
Okay, use the uart0 alias.

>
> Best regards,
> Jernej
>
> > +                     compatible = "snps,dw-apb-uart";
> > +                     reg = <0x0 0x02500000 0x0 0x400>;
> > +                     reg-io-width = <4>;
> > +                     reg-shift = <2>;
> > +                     interrupt-parent = <&plic>;
> > +                     interrupts = <18>;
> > +                     clocks = <&dummy_apb>;
> > +                     status = "disabled";
> > +             };
> > +     };
> > +};
>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-06 16:54   ` Guo Ren
@ 2021-06-06 17:14     ` Jernej Škrabec
  2021-06-06 23:42       ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-06 17:14 UTC (permalink / raw)
  To: Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren

Dne nedelja, 06. junij 2021 ob 18:54:05 CEST je Guo Ren napisal(a):
> V5 is not related to the patch series.

Don't top post.

If that's the case, then your e-mail client messed things up. I got V5 patches 
in the same thread as board enablement patches and so did mailing list [1].

Best regards,
Jernej

[1] https://lore.kernel.org/linux-sunxi/
CAJF2gTQ1yvSNthJ2yJkqaYGaJ6OYmf0vbG=hm4ocgnw4DiZbOw@mail.gmail.com/T/#t

> 
> On Mon, Jun 7, 2021 at 12:29 AM Jernej Škrabec <jernej.skrabec@gmail.com> 
wrote:
> > Hi!
> > 
> > Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je guoren@kernel.org 
napisal(a):
> > > From: Guo Ren <guoren@linux.alibaba.com>
> > > 
> > > The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> > > vendors define the custom properties of memory regions in PTE.
> > > 
> > > This patchset helps SOC vendors to support their own custom interconnect
> > > coherent solution with PTE attributes.
> > > 
> > > For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906
> > > has
> > > 
> > > two modes in MMU:
> > >  - Compatible mode, the same as the definitions in spec.
> > >  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
> > >  
> > >    not mentioned in spec.
> > > 
> > > Allwinner D1 needs the enhanced mode to support the DMA type device with
> > > non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> > > attribute bits in PTE.
> > > 
> > > The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been
> > > 
> > > tested on D1:
> > >  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
> > >  
> > >    conflict with RISC-V spec, and hope these patches soon could be
> > >    approved.
> > >  
> > >  - pgtable: Using a image-hdr to pass vendor specific information and
> > >  
> > >    setup custom PTE attributes in a global struct variable during boot
> > >    stage. Also it needs define custom protection_map in linux/mm.
> > >  
> > >  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
> > >  
> > >    In this patchset, I just show you how T-HEAD C9xx work, and seems
> > >    Atish
> > >    is working for the DMA infrustructure, please let me know the idea.
> > >  
> > >  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> > > 
> > > The patchset could work with linux-5.13-rc4, here is the steps for D1:
> > >  - Download linux-5.13-rc4 and apply the patchset
> > >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
> > >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
> > >  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e
> > >  0x00200000
> > > 
> > > -n Linux -d arch/riscv/boot/Image uImage - Download newest opensbi [2],
> > > build with [3], and get fw_dynamic.bin - Copy uImage, fw_dynamic.bin,
> > > allwinner-d1-nezha-kit.dtb into boot partition of TF card.
> > > 
> > >  - Plugin the TF card and power on D1.
> > > 
> > > Link: https://linux-sunxi.org/D1 [1]
> > > Link: https://github.com/riscv/opensbi branch:master [2]
> > > Link:
> > > https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md
> > > [3]
> > 
> > Some patches are marked with v2 and some V5. It's very confusing. Mark
> > them
> > with same version in next revision.
> > 
> > Best regards,
> > Jernej
> > 
> > > Changes since v1:
> > >  - Rebase on linux-5.13-rc4
> > >  - Support defconfig for different PTE attributes
> > >  - Support C906 icache_sync
> > >  - Add Allwinner D1 dts & Kconfig & gmac for testing
> > >  - Add asid optimization for D1 usage
> > > 
> > > Guo Ren (10):
> > >   riscv: asid: Use global mappings for kernel pages
> > >   riscv: asid: Add ASID-based tlbflushing methods
> > >   riscv: asid: Optimize tlbflush coding convention
> > >   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
> > >   riscv: pgtable: Add custom protection_map init
> > >   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
> > >   riscv: cmo: Add dma-noncoherency support
> > >   riscv: cmo: Add vendor custom icache sync
> > >   riscv: soc: Initial DTS for Allwinner D1 NeZha board
> > >   riscv: soc: Add Allwinner SoC kconfig option
> > > 
> > > liush (1):
> > >   riscv: soc: Allwinner D1 GMAC driver only for temp use
> > >  
> > >  arch/riscv/Kconfig                                 |    9 +
> > >  arch/riscv/Kconfig.socs                            |   12 +
> > >  arch/riscv/boot/dts/Makefile                       |    1 +
> > >  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
> > >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
> > >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
> > >  arch/riscv/configs/defconfig                       |    1 +
> > >  arch/riscv/include/asm/cacheflush.h                |   48 +-
> > >  arch/riscv/include/asm/mmu_context.h               |    2 +
> > >  arch/riscv/include/asm/pgtable-64.h                |    8 +-
> > >  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
> > >  arch/riscv/include/asm/pgtable.h                   |   44 +-
> > >  arch/riscv/include/asm/sbi.h                       |   15 +
> > >  arch/riscv/include/asm/soc.h                       |    1 +
> > >  arch/riscv/include/asm/tlbflush.h                  |   22 +
> > >  arch/riscv/include/asm/vendorid_list.h             |    1 +
> > >  arch/riscv/kernel/sbi.c                            |   19 +
> > >  arch/riscv/kernel/soc.c                            |   22 +
> > >  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
> > >  arch/riscv/mm/Makefile                             |    1 +
> > >  arch/riscv/mm/cacheflush.c                         |    3 +-
> > >  arch/riscv/mm/context.c                            |    2 +-
> > >  arch/riscv/mm/dma-mapping.c                        |   53 +
> > >  arch/riscv/mm/init.c                               |   26 +
> > >  arch/riscv/mm/tlbflush.c                           |   57 +-
> > >  drivers/net/ethernet/Kconfig                       |    1 +
> > >  drivers/net/ethernet/Makefile                      |    1 +
> > >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> > >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> > >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> > >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> > > 
> > > ++++++++++++++++++++ drivers/net/ethernet/allwinnertmp/sunxi-gmac.h    
> > > |
> > > 258 +++
> > > 
> > >  drivers/net/phy/realtek.c                          |    2 +-
> > >  mm/mmap.c                                          |    4 +
> > >  34 files changed, 3714 insertions(+), 37 deletions(-)
> > >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> > >  create mode 100644
> > >  arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > 
> > > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > create
> > > mode 100644 arch/riscv/mm/dma-mapping.c
> > > 
> > >  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
> > >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  2021-06-06  9:04 ` [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes guoren
  2021-06-06 14:39   ` Christoph Hellwig
@ 2021-06-06 17:22   ` Nick Kossifidis
  2021-06-07  6:19     ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: Nick Kossifidis @ 2021-06-06 17:22 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, maxime, drew, liush,
	lazyparser, wefu, linux-riscv, linux-kernel, linux-arch,
	linux-sunxi, Guo Ren, Christoph Hellwig

Στις 2021-06-06 12:04, guoren@kernel.org έγραψε:
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> The dma-noncoherent SOCs need different virtual memory mappings
> with different attributes:
>  - noncached + Strong Order (for IO/DMA descriptor)
>  - noncached + Weak Order (for writecombine usage, eg: frame
>    buffer)
> 
> All above base on PTE attributes by MMU hardware. That means
> address attributes are determined by PTE entry, not PMA. RISC-V
> soc vendors have defined their own custom PTE attributes for
> dma-noncoherency.
> 

This patch violates the Privilege Spec section 4.4.1 that clearly 
states:

"Bits63–54 are reserved for future standard use and must be zeroed by 
software for forward compatibility"

Standard use means that valid values can only be defined by the Priv. 
Spec, not by the vendor (otherwise they'd be marked as "custom use" or 
"platform use"), and since they "must" be zeroed by software we 'll be 
violating the Privilege Spec if we do otherwise.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-06 17:14     ` Jernej Škrabec
@ 2021-06-06 23:42       ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-06 23:42 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, wens, maxime,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren

On Mon, Jun 7, 2021 at 1:14 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Dne nedelja, 06. junij 2021 ob 18:54:05 CEST je Guo Ren napisal(a):
> > V5 is not related to the patch series.
>
> Don't top post.
>
> If that's the case, then your e-mail client messed things up. I got V5 patches
> in the same thread as board enablement patches and so did mailing list [1].
I made mistake that make two patchset mixed in my linux directory.

git format-patch -11
git send-email *.patch

But there are still some last posted patches in *.patch.

I'll prevent it in the next patchset version.

>
> Best regards,
> Jernej
>
> [1] https://lore.kernel.org/linux-sunxi/
> CAJF2gTQ1yvSNthJ2yJkqaYGaJ6OYmf0vbG=hm4ocgnw4DiZbOw@mail.gmail.com/T/#t
>
> >
> > On Mon, Jun 7, 2021 at 12:29 AM Jernej Škrabec <jernej.skrabec@gmail.com>
> wrote:
> > > Hi!
> > >
> > > Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je guoren@kernel.org
> napisal(a):
> > > > From: Guo Ren <guoren@linux.alibaba.com>
> > > >
> > > > The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> > > > vendors define the custom properties of memory regions in PTE.
> > > >
> > > > This patchset helps SOC vendors to support their own custom interconnect
> > > > coherent solution with PTE attributes.
> > > >
> > > > For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906
> > > > has
> > > >
> > > > two modes in MMU:
> > > >  - Compatible mode, the same as the definitions in spec.
> > > >  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
> > > >
> > > >    not mentioned in spec.
> > > >
> > > > Allwinner D1 needs the enhanced mode to support the DMA type device with
> > > > non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> > > > attribute bits in PTE.
> > > >
> > > > The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been
> > > >
> > > > tested on D1:
> > > >  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
> > > >
> > > >    conflict with RISC-V spec, and hope these patches soon could be
> > > >    approved.
> > > >
> > > >  - pgtable: Using a image-hdr to pass vendor specific information and
> > > >
> > > >    setup custom PTE attributes in a global struct variable during boot
> > > >    stage. Also it needs define custom protection_map in linux/mm.
> > > >
> > > >  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
> > > >
> > > >    In this patchset, I just show you how T-HEAD C9xx work, and seems
> > > >    Atish
> > > >    is working for the DMA infrustructure, please let me know the idea.
> > > >
> > > >  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> > > >
> > > > The patchset could work with linux-5.13-rc4, here is the steps for D1:
> > > >  - Download linux-5.13-rc4 and apply the patchset
> > > >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
> > > >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
> > > >  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e
> > > >  0x00200000
> > > >
> > > > -n Linux -d arch/riscv/boot/Image uImage - Download newest opensbi [2],
> > > > build with [3], and get fw_dynamic.bin - Copy uImage, fw_dynamic.bin,
> > > > allwinner-d1-nezha-kit.dtb into boot partition of TF card.
> > > >
> > > >  - Plugin the TF card and power on D1.
> > > >
> > > > Link: https://linux-sunxi.org/D1 [1]
> > > > Link: https://github.com/riscv/opensbi branch:master [2]
> > > > Link:
> > > > https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md
> > > > [3]
> > >
> > > Some patches are marked with v2 and some V5. It's very confusing. Mark
> > > them
> > > with same version in next revision.
> > >
> > > Best regards,
> > > Jernej
> > >
> > > > Changes since v1:
> > > >  - Rebase on linux-5.13-rc4
> > > >  - Support defconfig for different PTE attributes
> > > >  - Support C906 icache_sync
> > > >  - Add Allwinner D1 dts & Kconfig & gmac for testing
> > > >  - Add asid optimization for D1 usage
> > > >
> > > > Guo Ren (10):
> > > >   riscv: asid: Use global mappings for kernel pages
> > > >   riscv: asid: Add ASID-based tlbflushing methods
> > > >   riscv: asid: Optimize tlbflush coding convention
> > > >   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
> > > >   riscv: pgtable: Add custom protection_map init
> > > >   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
> > > >   riscv: cmo: Add dma-noncoherency support
> > > >   riscv: cmo: Add vendor custom icache sync
> > > >   riscv: soc: Initial DTS for Allwinner D1 NeZha board
> > > >   riscv: soc: Add Allwinner SoC kconfig option
> > > >
> > > > liush (1):
> > > >   riscv: soc: Allwinner D1 GMAC driver only for temp use
> > > >
> > > >  arch/riscv/Kconfig                                 |    9 +
> > > >  arch/riscv/Kconfig.socs                            |   12 +
> > > >  arch/riscv/boot/dts/Makefile                       |    1 +
> > > >  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
> > > >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
> > > >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
> > > >  arch/riscv/configs/defconfig                       |    1 +
> > > >  arch/riscv/include/asm/cacheflush.h                |   48 +-
> > > >  arch/riscv/include/asm/mmu_context.h               |    2 +
> > > >  arch/riscv/include/asm/pgtable-64.h                |    8 +-
> > > >  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
> > > >  arch/riscv/include/asm/pgtable.h                   |   44 +-
> > > >  arch/riscv/include/asm/sbi.h                       |   15 +
> > > >  arch/riscv/include/asm/soc.h                       |    1 +
> > > >  arch/riscv/include/asm/tlbflush.h                  |   22 +
> > > >  arch/riscv/include/asm/vendorid_list.h             |    1 +
> > > >  arch/riscv/kernel/sbi.c                            |   19 +
> > > >  arch/riscv/kernel/soc.c                            |   22 +
> > > >  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
> > > >  arch/riscv/mm/Makefile                             |    1 +
> > > >  arch/riscv/mm/cacheflush.c                         |    3 +-
> > > >  arch/riscv/mm/context.c                            |    2 +-
> > > >  arch/riscv/mm/dma-mapping.c                        |   53 +
> > > >  arch/riscv/mm/init.c                               |   26 +
> > > >  arch/riscv/mm/tlbflush.c                           |   57 +-
> > > >  drivers/net/ethernet/Kconfig                       |    1 +
> > > >  drivers/net/ethernet/Makefile                      |    1 +
> > > >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> > > >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> > > >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> > > >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> > > >
> > > > ++++++++++++++++++++ drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> > > > |
> > > > 258 +++
> > > >
> > > >  drivers/net/phy/realtek.c                          |    2 +-
> > > >  mm/mmap.c                                          |    4 +
> > > >  34 files changed, 3714 insertions(+), 37 deletions(-)
> > > >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> > > >  create mode 100644
> > > >  arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > >
> > > > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > > create
> > > > mode 100644 arch/riscv/mm/dma-mapping.c
> > > >
> > > >  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
> > > >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> > > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > > >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-06 16:26   ` Jernej Škrabec
  2021-06-06 17:05     ` Guo Ren
@ 2021-06-07  3:44     ` Guo Ren
  2021-06-07  7:27       ` Maxime Ripard
  1 sibling, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-07  3:44 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Maxime Ripard, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 12:26 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
>
> Hi!
>
> I didn't go through all details. After you fix all comments below, you should
> run "make dtbs_check" and fix all reported warnings too.
>
> Dne nedelja, 06. junij 2021 ob 11:04:07 CEST je guoren@kernel.org napisal(a):
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > Add initial DTS for Allwinner D1 NeZha board having only essential
> > devices (uart, dummy, clock, reset, clint, plic, etc).
> >
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Cc: Anup Patel <anup.patel@wdc.com>
> > Cc: Atish Patra <atish.patra@wdc.com>
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Drew Fustini <drew@beagleboard.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > ---
> >  arch/riscv/boot/dts/Makefile                       |  1 +
> >  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84
> > ++++++++++++++++++++++ 4 files changed, 116 insertions(+)
> >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> >
> > diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> > index fe996b8..3e7b264 100644
> > --- a/arch/riscv/boot/dts/Makefile
> > +++ b/arch/riscv/boot/dts/Makefile
> > @@ -2,5 +2,6 @@
> >  subdir-y += sifive
> >  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
> >  subdir-y += microchip
> > +subdir-y += allwinner
> >
> >  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> > diff --git a/arch/riscv/boot/dts/allwinner/Makefile
> > b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644
> > index 00000000..4adbf4b
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode
> > 100644
> > index 00000000..cd9f7c9
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
>
> Board DT names are comprised of soc name and board name, in this case it would
> be "sun20i-d1-nezha-kit.dts"
>
> > @@ -0,0 +1,29 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>
> Usually copyrights are added below spdx id.
>
> > +
> > +/dts-v1/;
> > +
> > +#include "allwinner-d1.dtsi"
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
>
> This should be part of SoC level DTSI.
>
> > +     model = "Allwinner D1 NeZha Kit";
> > +     compatible = "allwinner,d1-nezha-kit";
>
> Board specific compatible string should be followed with SoC compatible, in
> this case "allwinner,sun20i-d1".  You should document it too.
>
> > +
> > +     chosen {
> > +             bootargs = "console=ttyS0,115200";
>
> Above line doesn't belong here. If anything, it should be added dynamically by
> bootloader.
After discussion, we still want to keep a default value here.
Sometimes we could boot with jtag and parse dtb is hard for gdbinit
script.

>
> > +             stdout-path = &serial0;
> > +     };
> > +
> > +     memory@40000000 {
> > +             device_type = "memory";
> > +             reg = <0x0 0x40000000 0x0 0x20000000>;
> > +     };
>
> Ditto for whole memory node.
Ditto

>
> > +
> > +     soc {
> > +     };
>
> There is no point having empty nodes.
>
> > +};
> > +
> > +&serial0 {
> > +     status = "okay";
> > +};
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644
> > index 00000000..11cd938
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
>
> Current naming approach for Allwinner SoC level DTSI is "sun20i-d1.dtsi".
>
> > @@ -0,0 +1,84 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>
> > +
> > +/dts-v1/;
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
>
> Since all peripherals and memory are below 4 GiB, why have 64-bit addresses
> and sizes? It just clutters DT.
>
> > +     model = "Allwinner D1 Soc";
> > +     compatible = "allwinner,d1-nezha-kit";
>
> Compatible and model don't belong to SoC level DTSI.
>
> > +
> > +     chosen {
> > +     };
>
> Remove empty node.
>
> > +
> > +     cpus {
> > +             #address-cells = <1>;
> > +             #size-cells = <0>;
> > +             timebase-frequency = <2400000>;
> > +             cpu@0 {
> > +                     device_type = "cpu";
> > +                     reg = <0>;
> > +                     status = "okay";
> > +                     compatible = "riscv";
> > +                     riscv,isa = "rv64imafdcv";
> > +                     mmu-type = "riscv,sv39";
> > +                     cpu0_intc: interrupt-controller {
> > +                             #interrupt-cells = <1>;
> > +                             compatible = "riscv,cpu-intc";
> > +                             interrupt-controller;
> > +                     };
> > +             };
> > +     };
> > +
> > +     soc {
> > +             #address-cells = <2>;
> > +             #size-cells = <2>;
> > +             compatible = "simple-bus";
> > +             ranges;
> > +
> > +             reset: reset-sample {
> > +                     compatible = "thead,reset-sample";
> > +                     plic-delegate = <0x0 0x101ffffc>;
> > +             };
> > +
> > +             clint: clint@14000000 {
> > +                     compatible = "riscv,clint0";
> > +                     interrupts-extended = <
> > +                             &cpu0_intc  3 &cpu0_intc  7
> > +                             >;
> > +                     reg = <0x0 0x14000000 0x0 0x04000000>;
> > +                     clint,has-no-64bit-mmio;
> > +             };
> > +
> > +             plic: interrupt-controller@10000000 {
> > +                     #interrupt-cells = <1>;
> > +                     compatible = "riscv,plic0";
> > +                     interrupt-controller;
> > +                     interrupts-extended = <
> > +                             &cpu0_intc  0xffffffff &cpu0_intc  9
> > +                             >;
> > +                     reg = <0x0 0x10000000 0x0 0x04000000>;
> > +                     reg-names = "control";
> > +                     riscv,max-priority = <7>;
> > +                     riscv,ndev = <200>;
> > +             };
> > +
> > +             dummy_apb: apb-clock {
> > +                     compatible = "fixed-clock";
> > +                     clock-frequency = <24000000>;
> > +                     clock-output-names = "dummy_apb";
> > +                     #clock-cells = <0>;
> > +             };
> > +
> > +             serial0: serial@2500000 {
>
> This should be uart0 and board should have alias for it. Check ARM based
> Allwinner DTs.
>
> Best regards,
> Jernej
>
> > +                     compatible = "snps,dw-apb-uart";
> > +                     reg = <0x0 0x02500000 0x0 0x400>;
> > +                     reg-io-width = <4>;
> > +                     reg-shift = <2>;
> > +                     interrupt-parent = <&plic>;
> > +                     interrupts = <18>;
> > +                     clocks = <&dummy_apb>;
> > +                     status = "disabled";
> > +             };
> > +     };
> > +};
>
>
>
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* RE: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
                   ` (14 preceding siblings ...)
  2021-06-06 16:29 ` [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 Jernej Škrabec
@ 2021-06-07  3:44 ` Anup Patel
  2021-06-07  4:36   ` Guo Ren
  15 siblings, 1 reply; 52+ messages in thread
From: Anup Patel @ 2021-06-07  3:44 UTC (permalink / raw)
  To: guoren, palmerdabbelt, arnd, wens, maxime, drew, liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren



> -----Original Message-----
> From: guoren@kernel.org <guoren@kernel.org>
> Sent: 06 June 2021 14:34
> To: guoren@kernel.org; Anup Patel <Anup.Patel@wdc.com>;
> palmerdabbelt@google.com; arnd@arndb.de; wens@csie.org;
> maxime@cerno.tech; drew@beagleboard.org; liush@allwinnertech.com;
> lazyparser@gmail.com; wefu@redhat.com
> Cc: linux-riscv@lists.infradead.org; linux-kernel@vger.kernel.org; linux-
> arch@vger.kernel.org; linux-sunxi@lists.linux.dev; Guo Ren
> <guoren@linux.alibaba.com>
> Subject: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for
> Allwinner D1
> 
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> vendors define the custom properties of memory regions in PTE.
> 
> This patchset helps SOC vendors to support their own custom interconnect
> coherent solution with PTE attributes.
> 
> For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906 has
> two modes in MMU:
>  - Compatible mode, the same as the definitions in spec.
>  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
>    not mentioned in spec.
> 
> Allwinner D1 needs the enhanced mode to support the DMA type device with
> non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> attribute bits in PTE.
> 
> The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been tested
> on D1:
>  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
>    conflict with RISC-V spec, and hope these patches soon could be
>    approved.
>  - pgtable: Using a image-hdr to pass vendor specific information and
>    setup custom PTE attributes in a global struct variable during boot
>    stage. Also it needs define custom protection_map in linux/mm.
>  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
>    In this patchset, I just show you how T-HEAD C9xx work, and seems Atish
>    is working for the DMA infrustructure, please let me know the idea.
>  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> 
> The patchset could work with linux-5.13-rc4, here is the steps for D1:
>  - Download linux-5.13-rc4 and apply the patchset
>  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
>  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
>  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e 0x00200000 -
> n Linux -d arch/riscv/boot/Image uImage
>  - Download newest opensbi [2], build with [3], and get fw_dynamic.bin
>  - Copy uImage, fw_dynamic.bin, allwinner-d1-nezha-kit.dtb into boot
>    partition of TF card.
>  - Plugin the TF card and power on D1.
> 
> Link: https://linux-sunxi.org/D1 [1]
> Link: https://github.com/riscv/opensbi branch:master [2]
> Link: https://github.com/riscv/opensbi/blob/master/docs/platform/thead-
> c9xx.md [3]
> 
> Changes since v1:
>  - Rebase on linux-5.13-rc4
>  - Support defconfig for different PTE attributes
>  - Support C906 icache_sync
>  - Add Allwinner D1 dts & Kconfig & gmac for testing
>  - Add asid optimization for D1 usage
> 
> Guo Ren (10):
>   riscv: asid: Use global mappings for kernel pages
>   riscv: asid: Add ASID-based tlbflushing methods
>   riscv: asid: Optimize tlbflush coding convention
>   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
>   riscv: pgtable: Add custom protection_map init
>   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
>   riscv: cmo: Add dma-noncoherency support
>   riscv: cmo: Add vendor custom icache sync
>   riscv: soc: Initial DTS for Allwinner D1 NeZha board
>   riscv: soc: Add Allwinner SoC kconfig option

The series cover letter says DMA_COHERENT support but
it is doing lot of stuff not related to DMA.

Please keep the first three patches separate. They belong
to your ASID series.

I also dislike the fact that you are continuously sending
SBI DMA sync patches without any discussion on the
UnixPlatform mailing list for SBI spec changes.

Regards,
Anup

> 
> liush (1):
>   riscv: soc: Allwinner D1 GMAC driver only for temp use
> 
>  arch/riscv/Kconfig                                 |    9 +
>  arch/riscv/Kconfig.socs                            |   12 +
>  arch/riscv/boot/dts/Makefile                       |    1 +
>  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
>  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
>  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
>  arch/riscv/configs/defconfig                       |    1 +
>  arch/riscv/include/asm/cacheflush.h                |   48 +-
>  arch/riscv/include/asm/mmu_context.h               |    2 +
>  arch/riscv/include/asm/pgtable-64.h                |    8 +-
>  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
>  arch/riscv/include/asm/pgtable.h                   |   44 +-
>  arch/riscv/include/asm/sbi.h                       |   15 +
>  arch/riscv/include/asm/soc.h                       |    1 +
>  arch/riscv/include/asm/tlbflush.h                  |   22 +
>  arch/riscv/include/asm/vendorid_list.h             |    1 +
>  arch/riscv/kernel/sbi.c                            |   19 +
>  arch/riscv/kernel/soc.c                            |   22 +
>  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
>  arch/riscv/mm/Makefile                             |    1 +
>  arch/riscv/mm/cacheflush.c                         |    3 +-
>  arch/riscv/mm/context.c                            |    2 +-
>  arch/riscv/mm/dma-mapping.c                        |   53 +
>  arch/riscv/mm/init.c                               |   26 +
>  arch/riscv/mm/tlbflush.c                           |   57 +-
>  drivers/net/ethernet/Kconfig                       |    1 +
>  drivers/net/ethernet/Makefile                      |    1 +
>  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
>  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> ++++++++++++++++++++
>  drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
>  drivers/net/phy/realtek.c                          |    2 +-
>  mm/mmap.c                                          |    4 +
>  34 files changed, 3714 insertions(+), 37 deletions(-)  create mode 100644
> arch/riscv/boot/dts/allwinner/Makefile
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-
> kit.dts
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
>  create mode 100644 arch/riscv/mm/dma-mapping.c  create mode 100644
> drivers/net/ethernet/allwinnertmp/Kconfig
>  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
>  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> 
> --
> 2.7.4


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1
  2021-06-07  3:44 ` Anup Patel
@ 2021-06-07  4:36   ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-07  4:36 UTC (permalink / raw)
  To: Anup Patel
  Cc: palmerdabbelt, arnd, wens, maxime, drew, liush, lazyparser, wefu,
	linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren

Hi Anup,

On Mon, Jun 7, 2021 at 11:44 AM Anup Patel <Anup.Patel@wdc.com> wrote:
>
>
>
> > -----Original Message-----
> > From: guoren@kernel.org <guoren@kernel.org>
> > Sent: 06 June 2021 14:34
> > To: guoren@kernel.org; Anup Patel <Anup.Patel@wdc.com>;
> > palmerdabbelt@google.com; arnd@arndb.de; wens@csie.org;
> > maxime@cerno.tech; drew@beagleboard.org; liush@allwinnertech.com;
> > lazyparser@gmail.com; wefu@redhat.com
> > Cc: linux-riscv@lists.infradead.org; linux-kernel@vger.kernel.org; linux-
> > arch@vger.kernel.org; linux-sunxi@lists.linux.dev; Guo Ren
> > <guoren@linux.alibaba.com>
> > Subject: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for
> > Allwinner D1
> >
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > The RISC-V ISA doesn't yet specify how to query or modify PMAs, so let
> > vendors define the custom properties of memory regions in PTE.
> >
> > This patchset helps SOC vendors to support their own custom interconnect
> > coherent solution with PTE attributes.
> >
> > For example, allwinner D1[1] uses T-HEAD C906 as main processor, C906 has
> > two modes in MMU:
> >  - Compatible mode, the same as the definitions in spec.
> >  - Enhanced mode, add custom DMA_COHERENT attribute bits in PTE which
> >    not mentioned in spec.
> >
> > Allwinner D1 needs the enhanced mode to support the DMA type device with
> > non-coherent interconnect in its SOC. C906 uses BITS(63 - 59) as custom
> > attribute bits in PTE.
> >
> > The patchset contain 4 parts (asid, pgtable, cmo, soc) which have been tested
> > on D1:
> >  - asid: T-HEAD C906 of D1 contains full asid hw facilities which has no
> >    conflict with RISC-V spec, and hope these patches soon could be
> >    approved.
> >  - pgtable: Using a image-hdr to pass vendor specific information and
> >    setup custom PTE attributes in a global struct variable during boot
> >    stage. Also it needs define custom protection_map in linux/mm.
> >  - cmo: We need deal with dma_sync & icache_sync & __vdso_icache_sync.
> >    In this patchset, I just show you how T-HEAD C9xx work, and seems Atish
> >    is working for the DMA infrustructure, please let me know the idea.
> >  - soc: Add allwinner gmac driver & dts & Kconfig for sunxi test.
> >
> > The patchset could work with linux-5.13-rc4, here is the steps for D1:
> >  - Download linux-5.13-rc4 and apply the patchset
> >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- defconfig
> >  - make ARCH=riscv CROSS_COMPILE=riscv64-linux- Image modules dtbs
> >  - mkimage -A riscv -O linux -T kernel -C none -a 0x00200000 -e 0x00200000 -
> > n Linux -d arch/riscv/boot/Image uImage
> >  - Download newest opensbi [2], build with [3], and get fw_dynamic.bin
> >  - Copy uImage, fw_dynamic.bin, allwinner-d1-nezha-kit.dtb into boot
> >    partition of TF card.
> >  - Plugin the TF card and power on D1.
> >
> > Link: https://linux-sunxi.org/D1 [1]
> > Link: https://github.com/riscv/opensbi branch:master [2]
> > Link: https://github.com/riscv/opensbi/blob/master/docs/platform/thead-
> > c9xx.md [3]
> >
> > Changes since v1:
> >  - Rebase on linux-5.13-rc4
> >  - Support defconfig for different PTE attributes
> >  - Support C906 icache_sync
> >  - Add Allwinner D1 dts & Kconfig & gmac for testing
> >  - Add asid optimization for D1 usage
> >
> > Guo Ren (10):
> >   riscv: asid: Use global mappings for kernel pages
> >   riscv: asid: Add ASID-based tlbflushing methods
> >   riscv: asid: Optimize tlbflush coding convention
> >   riscv: pgtable: Fixup _PAGE_CHG_MASK usage
> >   riscv: pgtable: Add custom protection_map init
> >   riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
> >   riscv: cmo: Add dma-noncoherency support
> >   riscv: cmo: Add vendor custom icache sync
> >   riscv: soc: Initial DTS for Allwinner D1 NeZha board
> >   riscv: soc: Add Allwinner SoC kconfig option
>
> The series cover letter says DMA_COHERENT support but
> it is doing lot of stuff not related to DMA.
>
> Please keep the first three patches separate. They belong
> to your ASID series.
I just want to give out a whole view of how allwinner D1 works, which
could help soc folks run their mini system.

Christoph Hellwig has helped to update the ASID series, and I've given
the tested-by on my hardware 4*910 SMP & 906 D1
https://lore.kernel.org/linux-riscv/20210606152050.636038-1-hch@lst.de/T/#t

Ok, you won't see it in the next version patchset.

>
> I also dislike the fact that you are continuously sending
> SBI DMA sync patches without any discussion on the
> UnixPlatform mailing list for SBI spec changes.
SBI DMA sync is not critical for us, I could follow your definition
when you are ready. Even CBO trap emulation is okay for me.

>
> Regards,
> Anup
>
> >
> > liush (1):
> >   riscv: soc: Allwinner D1 GMAC driver only for temp use
> >
> >  arch/riscv/Kconfig                                 |    9 +
> >  arch/riscv/Kconfig.socs                            |   12 +
> >  arch/riscv/boot/dts/Makefile                       |    1 +
> >  arch/riscv/boot/dts/allwinner/Makefile             |    2 +
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |   29 +
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |  100 +
> >  arch/riscv/configs/defconfig                       |    1 +
> >  arch/riscv/include/asm/cacheflush.h                |   48 +-
> >  arch/riscv/include/asm/mmu_context.h               |    2 +
> >  arch/riscv/include/asm/pgtable-64.h                |    8 +-
> >  arch/riscv/include/asm/pgtable-bits.h              |   20 +-
> >  arch/riscv/include/asm/pgtable.h                   |   44 +-
> >  arch/riscv/include/asm/sbi.h                       |   15 +
> >  arch/riscv/include/asm/soc.h                       |    1 +
> >  arch/riscv/include/asm/tlbflush.h                  |   22 +
> >  arch/riscv/include/asm/vendorid_list.h             |    1 +
> >  arch/riscv/kernel/sbi.c                            |   19 +
> >  arch/riscv/kernel/soc.c                            |   22 +
> >  arch/riscv/kernel/vdso/flush_icache.S              |   33 +-
> >  arch/riscv/mm/Makefile                             |    1 +
> >  arch/riscv/mm/cacheflush.c                         |    3 +-
> >  arch/riscv/mm/context.c                            |    2 +-
> >  arch/riscv/mm/dma-mapping.c                        |   53 +
> >  arch/riscv/mm/init.c                               |   26 +
> >  arch/riscv/mm/tlbflush.c                           |   57 +-
> >  drivers/net/ethernet/Kconfig                       |    1 +
> >  drivers/net/ethernet/Makefile                      |    1 +
> >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240
> > ++++++++++++++++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
> >  drivers/net/phy/realtek.c                          |    2 +-
> >  mm/mmap.c                                          |    4 +
> >  34 files changed, 3714 insertions(+), 37 deletions(-)  create mode 100644
> > arch/riscv/boot/dts/allwinner/Makefile
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-
> > kit.dts
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> >  create mode 100644 arch/riscv/mm/dma-mapping.c  create mode 100644
> > drivers/net/ethernet/allwinnertmp/Kconfig
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> >
> > --
> > 2.7.4
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes
  2021-06-06 17:22   ` Nick Kossifidis
@ 2021-06-07  6:19     ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2021-06-07  6:19 UTC (permalink / raw)
  To: Nick Kossifidis
  Cc: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu, linux-riscv, linux-kernel, linux-arch,
	linux-sunxi, Guo Ren, Christoph Hellwig

On Sun, Jun 06, 2021 at 08:22:14PM +0300, Nick Kossifidis wrote:
> This patch violates the Privilege Spec section 4.4.1 that clearly states:
>
> "Bits63–54 are reserved for future standard use and must be zeroed by 
> software for forward compatibility"
>
> Standard use means that valid values can only be defined by the Priv. Spec, 
> not by the vendor (otherwise they'd be marked as "custom use" or "platform 
> use"), and since they "must" be zeroed by software we 'll be violating the 
> Privilege Spec if we do otherwise.

Yes, that is why I think it is a no-go.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-06  9:04 ` [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option guoren
@ 2021-06-07  7:19   ` Maxime Ripard
  2021-06-07  7:27     ` Arnd Bergmann
  2021-06-07  7:43     ` Guo Ren
  0 siblings, 2 replies; 52+ messages in thread
From: Maxime Ripard @ 2021-06-07  7:19 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, drew, liush, lazyparser,
	wefu, linux-riscv, linux-kernel, linux-arch, linux-sunxi,
	Guo Ren, Atish Patra, Christoph Hellwig

[-- Attachment #1: Type: text/plain, Size: 1738 bytes --]

Hi,

On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> Add Allwinner kconfig option which selects SoC specific and common
> drivers that is required for this SoC.
> 
> Allwinner D1 uses custom PTE attributes to solve non-coherency SOC
> interconnect issues for dma synchronization, so we set the default
> value when SOC_SUNXI selected.
> 
> Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> Cc: Anup Patel <anup.patel@wdc.com>
> Cc: Atish Patra <atish.patra@wdc.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Cc: Drew Fustini <drew@beagleboard.org>
> Cc: Maxime Ripard <maxime@cerno.tech>
> Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> Cc: Wei Fu <wefu@redhat.com>
> Cc: Wei Wu <lazyparser@gmail.com>
> ---
>  arch/riscv/Kconfig.socs      | 12 ++++++++++++
>  arch/riscv/configs/defconfig |  1 +
>  2 files changed, 13 insertions(+)
> 
> diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> index ed96376..055fb3e 100644
> --- a/arch/riscv/Kconfig.socs
> +++ b/arch/riscv/Kconfig.socs
> @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
>  
>  endif
>  
> +config SOC_SUNXI
> +	bool "Allwinner SoCs"
> +	depends on MMU
> +	select DWMAC_GENERIC
> +	select SERIAL_8250
> +	select SERIAL_8250_CONSOLE
> +	select SERIAL_8250_DW
> +	select SIFIVE_PLIC
> +	select STMMAC_ETH
> +	help
> +	  This enables support for Allwinner SoC platforms like the D1.
> +

We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
looks good otherwise.

Maxime

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-06  9:04 ` [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board guoren
  2021-06-06 16:26   ` Jernej Škrabec
@ 2021-06-07  7:24   ` Maxime Ripard
  2021-06-07  8:07     ` Guo Ren
  1 sibling, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2021-06-07  7:24 UTC (permalink / raw)
  To: guoren
  Cc: anup.patel, palmerdabbelt, arnd, wens, drew, liush, lazyparser,
	wefu, linux-riscv, linux-kernel, linux-arch, linux-sunxi,
	Guo Ren, Atish Patra, Christoph Hellwig

[-- Attachment #1: Type: text/plain, Size: 4269 bytes --]

Hi,

Thanks for the patches

On Sun, Jun 06, 2021 at 09:04:07AM +0000, guoren@kernel.org wrote:
> From: Guo Ren <guoren@linux.alibaba.com>
> 
> Add initial DTS for Allwinner D1 NeZha board having only essential
> devices (uart, dummy, clock, reset, clint, plic, etc).
> 
> Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> Cc: Anup Patel <anup.patel@wdc.com>
> Cc: Atish Patra <atish.patra@wdc.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Cc: Drew Fustini <drew@beagleboard.org>
> Cc: Maxime Ripard <maxime@cerno.tech>
> Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> Cc: Wei Fu <wefu@redhat.com>
> Cc: Wei Wu <lazyparser@gmail.com>
> ---
>  arch/riscv/boot/dts/Makefile                       |  1 +
>  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
>  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
>  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84 ++++++++++++++++++++++

Can you add the riscv folder to our MAINTAINERS entry as well?

>  4 files changed, 116 insertions(+)
>  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
>  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> 
> diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> index fe996b8..3e7b264 100644
> --- a/arch/riscv/boot/dts/Makefile
> +++ b/arch/riscv/boot/dts/Makefile
> @@ -2,5 +2,6 @@
>  subdir-y += sifive
>  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
>  subdir-y += microchip
> +subdir-y += allwinner

I assume they should be ordered alphabetically?

>  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile
> new file mode 100644
> index 00000000..4adbf4b
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> new file mode 100644
> index 00000000..cd9f7c9
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> @@ -0,0 +1,29 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +
> +/dts-v1/;
> +
> +#include "allwinner-d1.dtsi"
> +
> +/ {
> +	#address-cells = <2>;
> +	#size-cells = <2>;
> +	model = "Allwinner D1 NeZha Kit";
> +	compatible = "allwinner,d1-nezha-kit";
> +
> +	chosen {
> +		bootargs = "console=ttyS0,115200";
> +		stdout-path = &serial0;
> +	};
> +
> +	memory@40000000 {
> +		device_type = "memory";
> +		reg = <0x0 0x40000000 0x0 0x20000000>;
> +	};
> +
> +	soc {
> +	};
> +};
> +
> +&serial0 {
> +	status = "okay";
> +};
> diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> new file mode 100644
> index 00000000..11cd938
> --- /dev/null
> +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> @@ -0,0 +1,84 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +
> +/dts-v1/;
> +
> +/ {
> +	#address-cells = <2>;
> +	#size-cells = <2>;
> +	model = "Allwinner D1 Soc";
> +	compatible = "allwinner,d1-nezha-kit";
> +
> +	chosen {
> +	};
> +
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		timebase-frequency = <2400000>;
> +		cpu@0 {
> +			device_type = "cpu";
> +			reg = <0>;
> +			status = "okay";
> +			compatible = "riscv";
> +			riscv,isa = "rv64imafdcv";
> +			mmu-type = "riscv,sv39";
> +			cpu0_intc: interrupt-controller {
> +				#interrupt-cells = <1>;
> +				compatible = "riscv,cpu-intc";
> +				interrupt-controller;
> +			};
> +		};
> +	};
> +
> +	soc {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		compatible = "simple-bus";
> +		ranges;
> +
> +		reset: reset-sample {
> +			compatible = "thead,reset-sample";
> +			plic-delegate = <0x0 0x101ffffc>;
> +		};

This compatible is not documented anywhere?

Maxime

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-07  3:44     ` Guo Ren
@ 2021-06-07  7:27       ` Maxime Ripard
  2021-06-07  7:53         ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2021-06-07  7:27 UTC (permalink / raw)
  To: Guo Ren
  Cc: Jernej Škrabec, Anup Patel, Palmer Dabbelt, Arnd Bergmann,
	Chen-Yu Tsai, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

[-- Attachment #1: Type: text/plain, Size: 4676 bytes --]

On Mon, Jun 07, 2021 at 11:44:03AM +0800, Guo Ren wrote:
> On Mon, Jun 7, 2021 at 12:26 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
> >
> > Hi!
> >
> > I didn't go through all details. After you fix all comments below, you should
> > run "make dtbs_check" and fix all reported warnings too.
> >
> > Dne nedelja, 06. junij 2021 ob 11:04:07 CEST je guoren@kernel.org napisal(a):
> > > From: Guo Ren <guoren@linux.alibaba.com>
> > >
> > > Add initial DTS for Allwinner D1 NeZha board having only essential
> > > devices (uart, dummy, clock, reset, clint, plic, etc).
> > >
> > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > Cc: Anup Patel <anup.patel@wdc.com>
> > > Cc: Atish Patra <atish.patra@wdc.com>
> > > Cc: Christoph Hellwig <hch@lst.de>
> > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > Cc: Drew Fustini <drew@beagleboard.org>
> > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > > Cc: Wei Fu <wefu@redhat.com>
> > > Cc: Wei Wu <lazyparser@gmail.com>
> > > ---
> > >  arch/riscv/boot/dts/Makefile                       |  1 +
> > >  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
> > >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
> > >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84
> > > ++++++++++++++++++++++ 4 files changed, 116 insertions(+)
> > >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> > >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > >
> > > diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> > > index fe996b8..3e7b264 100644
> > > --- a/arch/riscv/boot/dts/Makefile
> > > +++ b/arch/riscv/boot/dts/Makefile
> > > @@ -2,5 +2,6 @@
> > >  subdir-y += sifive
> > >  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
> > >  subdir-y += microchip
> > > +subdir-y += allwinner
> > >
> > >  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> > > diff --git a/arch/riscv/boot/dts/allwinner/Makefile
> > > b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644
> > > index 00000000..4adbf4b
> > > --- /dev/null
> > > +++ b/arch/riscv/boot/dts/allwinner/Makefile
> > > @@ -0,0 +1,2 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> > > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode
> > > 100644
> > > index 00000000..cd9f7c9
> > > --- /dev/null
> > > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> >
> > Board DT names are comprised of soc name and board name, in this case it would
> > be "sun20i-d1-nezha-kit.dts"
> >
> > > @@ -0,0 +1,29 @@
> > > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> >
> > Usually copyrights are added below spdx id.
> >
> > > +
> > > +/dts-v1/;
> > > +
> > > +#include "allwinner-d1.dtsi"
> > > +
> > > +/ {
> > > +     #address-cells = <2>;
> > > +     #size-cells = <2>;
> >
> > This should be part of SoC level DTSI.
> >
> > > +     model = "Allwinner D1 NeZha Kit";
> > > +     compatible = "allwinner,d1-nezha-kit";
> >
> > Board specific compatible string should be followed with SoC compatible, in
> > this case "allwinner,sun20i-d1".  You should document it too.
> >
> > > +
> > > +     chosen {
> > > +             bootargs = "console=ttyS0,115200";
> >
> > Above line doesn't belong here. If anything, it should be added dynamically by
> > bootloader.
>
> After discussion, we still want to keep a default value here.
> Sometimes we could boot with jtag and parse dtb is hard for gdbinit
> script.
>
> >
> > > +             stdout-path = &serial0;
> > > +     };
> > > +
> > > +     memory@40000000 {
> > > +             device_type = "memory";
> > > +             reg = <0x0 0x40000000 0x0 0x20000000>;
> > > +     };
> >
> > Ditto for whole memory node.
>
> Ditto

The thing is that there's never a good value for a default. Let's take
the memory node here: what would be a good default? If we want to make
it work everywhere it's going to be the lowest amount of memory
available on the D1 boards. It's going to be hard to maintain and very
likely to be overlooked, resulting in broken boards anyway.

If someone is savvy enough to use JTAG, it's not really difficult to
modify the DT for their board when they need it.

Maxime

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-07  7:19   ` Maxime Ripard
@ 2021-06-07  7:27     ` Arnd Bergmann
  2021-06-07  7:45       ` Guo Ren
  2021-06-07  7:43     ` Guo Ren
  1 sibling, 1 reply; 52+ messages in thread
From: Arnd Bergmann @ 2021-06-07  7:27 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Guo Ren, Anup Patel, Palmer Dabbelt, Chen-Yu Tsai, Drew Fustini,
	liush, lazyparser, wefu, linux-riscv, Linux Kernel Mailing List,
	linux-arch, linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 9:20 AM Maxime Ripard <maxime@cerno.tech> wrote:
> On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:

> > diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> > index ed96376..055fb3e 100644
> > --- a/arch/riscv/Kconfig.socs
> > +++ b/arch/riscv/Kconfig.socs
> > @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
> >
> >  endif
> >
> > +config SOC_SUNXI
> > +     bool "Allwinner SoCs"
> > +     depends on MMU
> > +     select DWMAC_GENERIC
> > +     select SERIAL_8250
> > +     select SERIAL_8250_CONSOLE
> > +     select SERIAL_8250_DW
> > +     select SIFIVE_PLIC
> > +     select STMMAC_ETH
> > +     help
> > +       This enables support for Allwinner SoC platforms like the D1.
> > +
>
> We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
> looks good otherwise.

Correct: those subsystems may be completely disabled, which would lead to a
build failure, and a platform should not force-enable drivers or
subsystems unless
those are build time dependencies.

       Arnd

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-07  7:19   ` Maxime Ripard
  2021-06-07  7:27     ` Arnd Bergmann
@ 2021-06-07  7:43     ` Guo Ren
  2021-06-07 12:12       ` Maxime Ripard
  1 sibling, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-07  7:43 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 3:19 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> Hi,
>
> On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > Add Allwinner kconfig option which selects SoC specific and common
> > drivers that is required for this SoC.
> >
> > Allwinner D1 uses custom PTE attributes to solve non-coherency SOC
> > interconnect issues for dma synchronization, so we set the default
> > value when SOC_SUNXI selected.
> >
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Cc: Anup Patel <anup.patel@wdc.com>
> > Cc: Atish Patra <atish.patra@wdc.com>
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Drew Fustini <drew@beagleboard.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > ---
> >  arch/riscv/Kconfig.socs      | 12 ++++++++++++
> >  arch/riscv/configs/defconfig |  1 +
> >  2 files changed, 13 insertions(+)
> >
> > diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> > index ed96376..055fb3e 100644
> > --- a/arch/riscv/Kconfig.socs
> > +++ b/arch/riscv/Kconfig.socs
> > @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
> >
> >  endif
> >
> > +config SOC_SUNXI
> > +     bool "Allwinner SoCs"
> > +     depends on MMU
> > +     select DWMAC_GENERIC
> > +     select SERIAL_8250
> > +     select SERIAL_8250_CONSOLE
> > +     select SERIAL_8250_DW
> > +     select SIFIVE_PLIC
> > +     select STMMAC_ETH
> > +     help
> > +       This enables support for Allwinner SoC platforms like the D1.
> > +
>
> We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
> looks good otherwise.
>
> Maxime
Remove DWMAC, STMMAC_ETH is okay.

But I think we still need:
select SERIAL_8250_DW if SERIAL_8250
-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-07  7:27     ` Arnd Bergmann
@ 2021-06-07  7:45       ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-07  7:45 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Maxime Ripard, Anup Patel, Palmer Dabbelt, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 3:29 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Mon, Jun 7, 2021 at 9:20 AM Maxime Ripard <maxime@cerno.tech> wrote:
> > On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:
>
> > > diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> > > index ed96376..055fb3e 100644
> > > --- a/arch/riscv/Kconfig.socs
> > > +++ b/arch/riscv/Kconfig.socs
> > > @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
> > >
> > >  endif
> > >
> > > +config SOC_SUNXI
> > > +     bool "Allwinner SoCs"
> > > +     depends on MMU
> > > +     select DWMAC_GENERIC
> > > +     select SERIAL_8250
> > > +     select SERIAL_8250_CONSOLE
> > > +     select SERIAL_8250_DW
> > > +     select SIFIVE_PLIC
> > > +     select STMMAC_ETH
> > > +     help
> > > +       This enables support for Allwinner SoC platforms like the D1.
> > > +
> >
> > We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
> > looks good otherwise.
>
> Correct: those subsystems may be completely disabled, which would lead to a
> build failure, and a platform should not force-enable drivers or
> subsystems unless
> those are build time dependencies.
>
>        Arnd

I see, thx. how about just leave. I think the user would make mistakes
and waste time here.
select SERIAL_8250_DW if SERIAL_8250

-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-07  7:27       ` Maxime Ripard
@ 2021-06-07  7:53         ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-07  7:53 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Jernej Škrabec, Anup Patel, Palmer Dabbelt, Arnd Bergmann,
	Chen-Yu Tsai, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

Thx for the clarification.

On Mon, Jun 7, 2021 at 3:27 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> On Mon, Jun 07, 2021 at 11:44:03AM +0800, Guo Ren wrote:
> > On Mon, Jun 7, 2021 at 12:26 AM Jernej Škrabec <jernej.skrabec@gmail.com> wrote:
> > >
> > > Hi!
> > >
> > > I didn't go through all details. After you fix all comments below, you should
> > > run "make dtbs_check" and fix all reported warnings too.
> > >
> > > Dne nedelja, 06. junij 2021 ob 11:04:07 CEST je guoren@kernel.org napisal(a):
> > > > From: Guo Ren <guoren@linux.alibaba.com>
> > > >
> > > > Add initial DTS for Allwinner D1 NeZha board having only essential
> > > > devices (uart, dummy, clock, reset, clint, plic, etc).
> > > >
> > > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > > > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > > Cc: Anup Patel <anup.patel@wdc.com>
> > > > Cc: Atish Patra <atish.patra@wdc.com>
> > > > Cc: Christoph Hellwig <hch@lst.de>
> > > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > > Cc: Drew Fustini <drew@beagleboard.org>
> > > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > > > Cc: Wei Fu <wefu@redhat.com>
> > > > Cc: Wei Wu <lazyparser@gmail.com>
> > > > ---
> > > >  arch/riscv/boot/dts/Makefile                       |  1 +
> > > >  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
> > > >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
> > > >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84
> > > > ++++++++++++++++++++++ 4 files changed, 116 insertions(+)
> > > >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> > > >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > > create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > > >
> > > > diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> > > > index fe996b8..3e7b264 100644
> > > > --- a/arch/riscv/boot/dts/Makefile
> > > > +++ b/arch/riscv/boot/dts/Makefile
> > > > @@ -2,5 +2,6 @@
> > > >  subdir-y += sifive
> > > >  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
> > > >  subdir-y += microchip
> > > > +subdir-y += allwinner
> > > >
> > > >  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> > > > diff --git a/arch/riscv/boot/dts/allwinner/Makefile
> > > > b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644
> > > > index 00000000..4adbf4b
> > > > --- /dev/null
> > > > +++ b/arch/riscv/boot/dts/allwinner/Makefile
> > > > @@ -0,0 +1,2 @@
> > > > +# SPDX-License-Identifier: GPL-2.0
> > > > +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> > > > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > > > b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode
> > > > 100644
> > > > index 00000000..cd9f7c9
> > > > --- /dev/null
> > > > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > >
> > > Board DT names are comprised of soc name and board name, in this case it would
> > > be "sun20i-d1-nezha-kit.dts"
> > >
> > > > @@ -0,0 +1,29 @@
> > > > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > >
> > > Usually copyrights are added below spdx id.
> > >
> > > > +
> > > > +/dts-v1/;
> > > > +
> > > > +#include "allwinner-d1.dtsi"
> > > > +
> > > > +/ {
> > > > +     #address-cells = <2>;
> > > > +     #size-cells = <2>;
> > >
> > > This should be part of SoC level DTSI.
> > >
> > > > +     model = "Allwinner D1 NeZha Kit";
> > > > +     compatible = "allwinner,d1-nezha-kit";
> > >
> > > Board specific compatible string should be followed with SoC compatible, in
> > > this case "allwinner,sun20i-d1".  You should document it too.
> > >
> > > > +
> > > > +     chosen {
> > > > +             bootargs = "console=ttyS0,115200";
> > >
> > > Above line doesn't belong here. If anything, it should be added dynamically by
> > > bootloader.
> >
> > After discussion, we still want to keep a default value here.
> > Sometimes we could boot with jtag and parse dtb is hard for gdbinit
> > script.
> >
> > >
> > > > +             stdout-path = &serial0;
> > > > +     };
> > > > +
> > > > +     memory@40000000 {
> > > > +             device_type = "memory";
> > > > +             reg = <0x0 0x40000000 0x0 0x20000000>;
> > > > +     };
> > >
> > > Ditto for whole memory node.
> >
> > Ditto
>
> The thing is that there's never a good value for a default. Let's take
> the memory node here: what would be a good default? If we want to make
> it work everywhere it's going to be the lowest amount of memory
> available on the D1 boards. It's going to be hard to maintain and very
> likely to be overlooked, resulting in broken boards anyway.
>
> If someone is savvy enough to use JTAG, it's not really difficult to
> modify the DT for their board when they need it.
okay, I see. I'll follow the rule in the next version of the patchset.

>
> Maxime



-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-07  7:24   ` Maxime Ripard
@ 2021-06-07  8:07     ` Guo Ren
  2021-06-14 15:33       ` Maxime Ripard
  0 siblings, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-07  8:07 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 3:24 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> Hi,
>
> Thanks for the patches
>
> On Sun, Jun 06, 2021 at 09:04:07AM +0000, guoren@kernel.org wrote:
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > Add initial DTS for Allwinner D1 NeZha board having only essential
> > devices (uart, dummy, clock, reset, clint, plic, etc).
> >
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Cc: Anup Patel <anup.patel@wdc.com>
> > Cc: Atish Patra <atish.patra@wdc.com>
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Drew Fustini <drew@beagleboard.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > ---
> >  arch/riscv/boot/dts/Makefile                       |  1 +
> >  arch/riscv/boot/dts/allwinner/Makefile             |  2 +
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  | 29 ++++++++
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    | 84 ++++++++++++++++++++++
>
> Can you add the riscv folder to our MAINTAINERS entry as well?
Yes

>
> >  4 files changed, 116 insertions(+)
> >  create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> >  create mode 100644 arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> >
> > diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
> > index fe996b8..3e7b264 100644
> > --- a/arch/riscv/boot/dts/Makefile
> > +++ b/arch/riscv/boot/dts/Makefile
> > @@ -2,5 +2,6 @@
> >  subdir-y += sifive
> >  subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
> >  subdir-y += microchip
> > +subdir-y += allwinner
>
> I assume they should be ordered alphabetically?
Thx for pointing it out.

>
> >  obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
> > diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile
> > new file mode 100644
> > index 00000000..4adbf4b
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > new file mode 100644
> > index 00000000..cd9f7c9
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > @@ -0,0 +1,29 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +
> > +/dts-v1/;
> > +
> > +#include "allwinner-d1.dtsi"
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
> > +     model = "Allwinner D1 NeZha Kit";
> > +     compatible = "allwinner,d1-nezha-kit";
> > +
> > +     chosen {
> > +             bootargs = "console=ttyS0,115200";
> > +             stdout-path = &serial0;
> > +     };
> > +
> > +     memory@40000000 {
> > +             device_type = "memory";
> > +             reg = <0x0 0x40000000 0x0 0x20000000>;
> > +     };
> > +
> > +     soc {
> > +     };
> > +};
> > +
> > +&serial0 {
> > +     status = "okay";
> > +};
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > new file mode 100644
> > index 00000000..11cd938
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > @@ -0,0 +1,84 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > +
> > +/dts-v1/;
> > +
> > +/ {
> > +     #address-cells = <2>;
> > +     #size-cells = <2>;
> > +     model = "Allwinner D1 Soc";
> > +     compatible = "allwinner,d1-nezha-kit";
> > +
> > +     chosen {
> > +     };
> > +
> > +     cpus {
> > +             #address-cells = <1>;
> > +             #size-cells = <0>;
> > +             timebase-frequency = <2400000>;
> > +             cpu@0 {
> > +                     device_type = "cpu";
> > +                     reg = <0>;
> > +                     status = "okay";
> > +                     compatible = "riscv";
> > +                     riscv,isa = "rv64imafdcv";
> > +                     mmu-type = "riscv,sv39";
> > +                     cpu0_intc: interrupt-controller {
> > +                             #interrupt-cells = <1>;
> > +                             compatible = "riscv,cpu-intc";
> > +                             interrupt-controller;
> > +                     };
> > +             };
> > +     };
> > +
> > +     soc {
> > +             #address-cells = <2>;
> > +             #size-cells = <2>;
> > +             compatible = "simple-bus";
> > +             ranges;
> > +
> > +             reset: reset-sample {
> > +                     compatible = "thead,reset-sample";
> > +                     plic-delegate = <0x0 0x101ffffc>;
> > +             };
>
> This compatible is not documented anywhere?
It used by opensbi (riscv runtime firmware), not in Linux. But I think
we should keep it.

>
> Maxime



-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-07  7:43     ` Guo Ren
@ 2021-06-07 12:12       ` Maxime Ripard
  2021-06-07 12:39         ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2021-06-07 12:12 UTC (permalink / raw)
  To: Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

[-- Attachment #1: Type: text/plain, Size: 2312 bytes --]

On Mon, Jun 07, 2021 at 03:43:03PM +0800, Guo Ren wrote:
> On Mon, Jun 7, 2021 at 3:19 PM Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > Hi,
> >
> > On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:
> > > From: Guo Ren <guoren@linux.alibaba.com>
> > >
> > > Add Allwinner kconfig option which selects SoC specific and common
> > > drivers that is required for this SoC.
> > >
> > > Allwinner D1 uses custom PTE attributes to solve non-coherency SOC
> > > interconnect issues for dma synchronization, so we set the default
> > > value when SOC_SUNXI selected.
> > >
> > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > Cc: Anup Patel <anup.patel@wdc.com>
> > > Cc: Atish Patra <atish.patra@wdc.com>
> > > Cc: Christoph Hellwig <hch@lst.de>
> > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > Cc: Drew Fustini <drew@beagleboard.org>
> > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > > Cc: Wei Fu <wefu@redhat.com>
> > > Cc: Wei Wu <lazyparser@gmail.com>
> > > ---
> > >  arch/riscv/Kconfig.socs      | 12 ++++++++++++
> > >  arch/riscv/configs/defconfig |  1 +
> > >  2 files changed, 13 insertions(+)
> > >
> > > diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> > > index ed96376..055fb3e 100644
> > > --- a/arch/riscv/Kconfig.socs
> > > +++ b/arch/riscv/Kconfig.socs
> > > @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
> > >
> > >  endif
> > >
> > > +config SOC_SUNXI
> > > +     bool "Allwinner SoCs"
> > > +     depends on MMU
> > > +     select DWMAC_GENERIC
> > > +     select SERIAL_8250
> > > +     select SERIAL_8250_CONSOLE
> > > +     select SERIAL_8250_DW
> > > +     select SIFIVE_PLIC
> > > +     select STMMAC_ETH
> > > +     help
> > > +       This enables support for Allwinner SoC platforms like the D1.
> > > +
> >
> > We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
> > looks good otherwise.
> >
> > Maxime
> Remove DWMAC, STMMAC_ETH is okay.
> 
> But I think we still need:
> select SERIAL_8250_DW if SERIAL_8250

Well, even the UART is optional. Just enable them in the defconfig

Maxime

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option
  2021-06-07 12:12       ` Maxime Ripard
@ 2021-06-07 12:39         ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-06-07 12:39 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 7, 2021 at 8:12 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> On Mon, Jun 07, 2021 at 03:43:03PM +0800, Guo Ren wrote:
> > On Mon, Jun 7, 2021 at 3:19 PM Maxime Ripard <maxime@cerno.tech> wrote:
> > >
> > > Hi,
> > >
> > > On Sun, Jun 06, 2021 at 09:04:08AM +0000, guoren@kernel.org wrote:
> > > > From: Guo Ren <guoren@linux.alibaba.com>
> > > >
> > > > Add Allwinner kconfig option which selects SoC specific and common
> > > > drivers that is required for this SoC.
> > > >
> > > > Allwinner D1 uses custom PTE attributes to solve non-coherency SOC
> > > > interconnect issues for dma synchronization, so we set the default
> > > > value when SOC_SUNXI selected.
> > > >
> > > > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > > > Co-Developed-by: Liu Shaohua <liush@allwinnertech.com>
> > > > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > > > Cc: Anup Patel <anup.patel@wdc.com>
> > > > Cc: Atish Patra <atish.patra@wdc.com>
> > > > Cc: Christoph Hellwig <hch@lst.de>
> > > > Cc: Chen-Yu Tsai <wens@csie.org>
> > > > Cc: Drew Fustini <drew@beagleboard.org>
> > > > Cc: Maxime Ripard <maxime@cerno.tech>
> > > > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > > > Cc: Wei Fu <wefu@redhat.com>
> > > > Cc: Wei Wu <lazyparser@gmail.com>
> > > > ---
> > > >  arch/riscv/Kconfig.socs      | 12 ++++++++++++
> > > >  arch/riscv/configs/defconfig |  1 +
> > > >  2 files changed, 13 insertions(+)
> > > >
> > > > diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> > > > index ed96376..055fb3e 100644
> > > > --- a/arch/riscv/Kconfig.socs
> > > > +++ b/arch/riscv/Kconfig.socs
> > > > @@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
> > > >
> > > >  endif
> > > >
> > > > +config SOC_SUNXI
> > > > +     bool "Allwinner SoCs"
> > > > +     depends on MMU
> > > > +     select DWMAC_GENERIC
> > > > +     select SERIAL_8250
> > > > +     select SERIAL_8250_CONSOLE
> > > > +     select SERIAL_8250_DW
> > > > +     select SIFIVE_PLIC
> > > > +     select STMMAC_ETH
> > > > +     help
> > > > +       This enables support for Allwinner SoC platforms like the D1.
> > > > +
> > >
> > > We probably don't want to select DWMAC, STMMAC_ETH and the 8250 options,
> > > looks good otherwise.
> > >
> > > Maxime
> > Remove DWMAC, STMMAC_ETH is okay.
> >
> > But I think we still need:
> > select SERIAL_8250_DW if SERIAL_8250
>
> Well, even the UART is optional. Just enable them in the defconfig
Okay


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-07  8:07     ` Guo Ren
@ 2021-06-14 15:33       ` Maxime Ripard
  2021-06-14 16:28         ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2021-06-14 15:33 UTC (permalink / raw)
  To: Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

[-- Attachment #1: Type: text/plain, Size: 518 bytes --]

On Mon, Jun 07, 2021 at 04:07:37PM +0800, Guo Ren wrote:
> On Mon, Jun 7, 2021 at 3:24 PM Maxime Ripard <maxime@cerno.tech> wrote:
> > > +             reset: reset-sample {
> > > +                     compatible = "thead,reset-sample";
> > > +                     plic-delegate = <0x0 0x101ffffc>;
> > > +             };
> >
> > This compatible is not documented anywhere?
>
> It used by opensbi (riscv runtime firmware), not in Linux. But I think
> we should keep it.

This should have a documentation still.

Maxime

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-14 15:33       ` Maxime Ripard
@ 2021-06-14 16:28         ` Guo Ren
  2021-06-14 16:31           ` Jernej Škrabec
  0 siblings, 1 reply; 52+ messages in thread
From: Guo Ren @ 2021-06-14 16:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

On Mon, Jun 14, 2021 at 11:33 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> On Mon, Jun 07, 2021 at 04:07:37PM +0800, Guo Ren wrote:
> > On Mon, Jun 7, 2021 at 3:24 PM Maxime Ripard <maxime@cerno.tech> wrote:
> > > > +             reset: reset-sample {
> > > > +                     compatible = "thead,reset-sample";
> > > > +                     plic-delegate = <0x0 0x101ffffc>;
> > > > +             };
> > >
> > > This compatible is not documented anywhere?
> >
> > It used by opensbi (riscv runtime firmware), not in Linux. But I think
> > we should keep it.
>
> This should have a documentation still.

Could we detail the above in [1]?

1: https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md

>
> Maxime



-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board
  2021-06-14 16:28         ` Guo Ren
@ 2021-06-14 16:31           ` Jernej Škrabec
  0 siblings, 0 replies; 52+ messages in thread
From: Jernej Škrabec @ 2021-06-14 16:31 UTC (permalink / raw)
  To: Maxime Ripard, Guo Ren
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Atish Patra, Christoph Hellwig

Dne ponedeljek, 14. junij 2021 ob 18:28:28 CEST je Guo Ren napisal(a):
> On Mon, Jun 14, 2021 at 11:33 PM Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > On Mon, Jun 07, 2021 at 04:07:37PM +0800, Guo Ren wrote:
> > > On Mon, Jun 7, 2021 at 3:24 PM Maxime Ripard <maxime@cerno.tech> wrote:
> > > > > +             reset: reset-sample {
> > > > > +                     compatible = "thead,reset-sample";
> > > > > +                     plic-delegate = <0x0 0x101ffffc>;
> > > > > +             };
> > > >
> > > > This compatible is not documented anywhere?
> > >
> > > It used by opensbi (riscv runtime firmware), not in Linux. But I think
> > > we should keep it.
> >
> > This should have a documentation still.
> 
> Could we detail the above in [1]?

All DT nodes must be documented in
Documentation/devicetree/bindings folder in yaml format, so DTs can be machine 
verified.

Best regards,
Jernej

> 
> 1: https://github.com/riscv/opensbi/blob/master/docs/platform/thead-c9xx.md
> 
> >
> > Maxime
> 
> 
> 
> -- 
> Best Regards
>  Guo Ren
> 
> ML: https://lore.kernel.org/linux-csky/
> 
> 



^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support
  2021-06-06  9:04 ` [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support guoren
@ 2021-10-17  9:28   ` twd2
  2021-10-20  8:11     ` Guo Ren
  0 siblings, 1 reply; 52+ messages in thread
From: twd2 @ 2021-10-17  9:28 UTC (permalink / raw)
  To: guoren, anup.patel, palmerdabbelt, arnd, wens, maxime, drew,
	liush, lazyparser, wefu
  Cc: linux-riscv, linux-kernel, linux-arch, linux-sunxi, Guo Ren,
	Christoph Hellwig


On 2021/6/6 17:04, guoren@kernel.org wrote:
> From: Guo Ren <guoren@linux.alibaba.com>
>
> To support DMA device in a non-coherent interconnect SOC system,
> we need the below facilities:
>  - Changing a virtual memory mapping region attributes from
>    cacheable to noncache + strong order which used in DMA
>    descriptors.
>  - Add noncache + weakorder virtual memory attributes for dma
>    mapping.
>  - Syncing the cache with memory before DMA start and after DMA
>    end with vendor custom CMO instructions.
>
> This patch enables linux kernel generic dma-noncoherency
> infrastructure and introduces new sbi_ecall API for dma_sync.
>
> @@ -27,6 +27,7 @@ enum sbi_ext_id {
> +       SBI_EXT_DMA = 0xAB150401,
>
> Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Anup Patel <anup.patel@wdc.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Drew Fustini <drew@beagleboard.org>
> Cc: Wei Fu <wefu@redhat.com>
> Cc: Wei Wu <lazyparser@gmail.com>
> Cc: Chen-Yu Tsai <wens@csie.org>
> Cc: Maxime Ripard <maxime@cerno.tech>
> ---
>  arch/riscv/Kconfig               |  5 ++++
>  arch/riscv/include/asm/pgtable.h | 26 ++++++++++++++++++++
>  arch/riscv/include/asm/sbi.h     | 15 ++++++++++++
>  arch/riscv/kernel/sbi.c          | 19 ++++++++++++++
>  arch/riscv/mm/Makefile           |  1 +
>  arch/riscv/mm/dma-mapping.c      | 53 ++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 119 insertions(+)
>  create mode 100644 arch/riscv/mm/dma-mapping.c
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 05c4976..817a9bb 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -20,6 +20,10 @@ config RISCV
>  	select ARCH_HAS_DEBUG_VM_PGTABLE
>  	select ARCH_HAS_DEBUG_VIRTUAL if MMU
>  	select ARCH_HAS_DEBUG_WX
> +	select ARCH_HAS_DMA_PREP_COHERENT
> +	select ARCH_HAS_SYNC_DMA_FOR_CPU
> +	select ARCH_HAS_SYNC_DMA_FOR_DEVICE
> +	select ARCH_HAS_DMA_WRITE_COMBINE
>  	select ARCH_HAS_FORTIFY_SOURCE
>  	select ARCH_HAS_GCOV_PROFILE_ALL
>  	select ARCH_HAS_GIGANTIC_PAGE
> @@ -43,6 +47,7 @@ config RISCV
>  	select CLONE_BACKWARDS
>  	select CLINT_TIMER if !MMU
>  	select COMMON_CLK
> +	select DMA_DIRECT_REMAP
>  	select EDAC_SUPPORT
>  	select GENERIC_ARCH_TOPOLOGY if SMP
>  	select GENERIC_ATOMIC64 if !64BIT
> diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> index 6ddeb49..e1a82b6 100644
> --- a/arch/riscv/include/asm/pgtable.h
> +++ b/arch/riscv/include/asm/pgtable.h
> @@ -462,6 +462,32 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
>  	return ptep_test_and_clear_young(vma, address, ptep);
>  }
>  
> +#define pgprot_noncached pgprot_noncached
> +static inline pgprot_t pgprot_noncached(pgprot_t _prot)
> +{
> +	unsigned long prot = pgprot_val(_prot);
> +
> +	prot &= ~_PAGE_DMA_MASK;
> +	prot |= _PAGE_DMA_IO;
> +
> +	return __pgprot(prot);
> +}
> +
> +#define pgprot_writecombine pgprot_writecombine
> +static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> +{
> +	unsigned long prot = pgprot_val(_prot);
> +
> +	prot &= ~_PAGE_DMA_MASK;
> +	prot |= _PAGE_DMA_WC;
> +
> +	return __pgprot(prot);
> +}
> +
> +#define __HAVE_PHYS_MEM_ACCESS_PROT
> +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
> +				     unsigned long size, pgprot_t vma_prot);
> +
>  /*
>   * Encode and decode a swap entry
>   *
> diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
> index 0d42693..133e88a 100644
> --- a/arch/riscv/include/asm/sbi.h
> +++ b/arch/riscv/include/asm/sbi.h
> @@ -27,6 +27,7 @@ enum sbi_ext_id {
>  	SBI_EXT_IPI = 0x735049,
>  	SBI_EXT_RFENCE = 0x52464E43,
>  	SBI_EXT_HSM = 0x48534D,
> +	SBI_EXT_DMA = 0xAB150401,
>  };
>  
>  enum sbi_ext_base_fid {
> @@ -63,6 +64,17 @@ enum sbi_ext_hsm_fid {
>  	SBI_EXT_HSM_HART_STATUS,
>  };
>  
> +enum sbi_ext_dma_fid {
> +	SBI_DMA_SYNC = 0,
> +};
> +
> +enum sbi_dma_sync_data_direction {
> +	SBI_DMA_BIDIRECTIONAL = 0,
> +	SBI_DMA_TO_DEVICE = 1,
> +	SBI_DMA_FROM_DEVICE = 2,
> +	SBI_DMA_NONE = 3,
> +};
> +
>  enum sbi_hsm_hart_status {
>  	SBI_HSM_HART_STATUS_STARTED = 0,
>  	SBI_HSM_HART_STATUS_STOPPED,
> @@ -128,6 +140,9 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
>  				unsigned long size,
>  				unsigned long asid);
>  int sbi_probe_extension(int ext);
> +void sbi_dma_sync(unsigned long start,
> +		  unsigned long size,
> +		  enum sbi_dma_sync_data_direction dir);
>  
>  /* Check if current SBI specification version is 0.1 or not */
>  static inline int sbi_spec_is_0_1(void)
> diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
> index 7402a41..c936019 100644
> --- a/arch/riscv/kernel/sbi.c
> +++ b/arch/riscv/kernel/sbi.c
> @@ -521,6 +521,25 @@ int sbi_probe_extension(int extid)
>  }
>  EXPORT_SYMBOL(sbi_probe_extension);
>  
> +void sbi_dma_sync(unsigned long start,
> +		  unsigned long size,
> +		  enum sbi_dma_sync_data_direction dir)
> +{
> +#if 0
> +	sbi_ecall(SBI_EXT_DMA, SBI_DMA_SYNC, start, size, dir,
> +		  0, 0, 0);
> +#else
> +	/* Just for try, it should be in sbi ecall and will be removed before merged */
> +	register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1);
> +
> +	for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES)
> +		__asm__ __volatile__(".long 0x02b5000b");
> +


Hi, I'm trying to use this patch for my D1 board.

Though the above code will be removed, I notice that the use of the inline assembly is wrong and `i` (i.e. `a0`) might not be correctly passed to the assembly code when we are using some other compilers.


It should be `__asm__ __volatile__(".long 0x02b5000b" : : "r"(i))`.


Thanks,
Wende


> +	__asm__ __volatile__(".long 0x01b0000b");
> +#endif
> +}
> +EXPORT_SYMBOL(sbi_dma_sync);
> +
>  static long __sbi_base_ecall(int fid)
>  {
>  	struct sbiret ret;
> diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
> index 7ebaef1..ca0ff90 100644
> --- a/arch/riscv/mm/Makefile
> +++ b/arch/riscv/mm/Makefile
> @@ -13,6 +13,7 @@ obj-y += extable.o
>  obj-$(CONFIG_MMU) += fault.o pageattr.o
>  obj-y += cacheflush.o
>  obj-y += context.o
> +obj-y += dma-mapping.o
>  
>  ifeq ($(CONFIG_MMU),y)
>  obj-$(CONFIG_SMP) += tlbflush.o
> diff --git a/arch/riscv/mm/dma-mapping.c b/arch/riscv/mm/dma-mapping.c
> new file mode 100644
> index 00000000..4afd9dc
> --- /dev/null
> +++ b/arch/riscv/mm/dma-mapping.c
> @@ -0,0 +1,53 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/dma-map-ops.h>
> +#include <asm/sbi.h>
> +
> +void arch_dma_prep_coherent(struct page *page, size_t size)
> +{
> +	void *ptr = page_address(page);
> +
> +	memset(ptr, 0, size);
> +	sbi_dma_sync(page_to_phys(page), size, SBI_DMA_BIDIRECTIONAL);
> +}
> +
> +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
> +		enum dma_data_direction dir)
> +{
> +	switch (dir) {
> +	case DMA_TO_DEVICE:
> +	case DMA_FROM_DEVICE:
> +	case DMA_BIDIRECTIONAL:
> +		sbi_dma_sync(paddr, size, dir);
> +		break;
> +	default:
> +		BUG();
> +	}
> +}
> +
> +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
> +		enum dma_data_direction dir)
> +{
> +	switch (dir) {
> +	case DMA_TO_DEVICE:
> +		return;
> +	case DMA_FROM_DEVICE:
> +	case DMA_BIDIRECTIONAL:
> +		sbi_dma_sync(paddr, size, dir);
> +		break;
> +	default:
> +		BUG();
> +	}
> +}
> +
> +pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
> +			      unsigned long size, pgprot_t vma_prot)
> +{
> +	if (!pfn_valid(pfn))
> +		return pgprot_noncached(vma_prot);
> +	else if (file->f_flags & O_SYNC)
> +		return pgprot_writecombine(vma_prot);
> +
> +	return vma_prot;
> +}
> +EXPORT_SYMBOL(phys_mem_access_prot);

-- 
Best wishes!
Sincerely,
twd2
https://twd2.me


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support
  2021-10-17  9:28   ` twd2
@ 2021-10-20  8:11     ` Guo Ren
  0 siblings, 0 replies; 52+ messages in thread
From: Guo Ren @ 2021-10-20  8:11 UTC (permalink / raw)
  To: twd2
  Cc: Anup Patel, Palmer Dabbelt, Arnd Bergmann, Chen-Yu Tsai,
	Maxime Ripard, Drew Fustini, liush, Wei Wu (吴伟),
	wefu, linux-riscv, Linux Kernel Mailing List, linux-arch,
	linux-sunxi, Guo Ren, Christoph Hellwig

On Sun, Oct 17, 2021 at 5:28 PM twd2 <twd2.me@gmail.com> wrote:
>
>
> On 2021/6/6 17:04, guoren@kernel.org wrote:
> > From: Guo Ren <guoren@linux.alibaba.com>
> >
> > To support DMA device in a non-coherent interconnect SOC system,
> > we need the below facilities:
> >  - Changing a virtual memory mapping region attributes from
> >    cacheable to noncache + strong order which used in DMA
> >    descriptors.
> >  - Add noncache + weakorder virtual memory attributes for dma
> >    mapping.
> >  - Syncing the cache with memory before DMA start and after DMA
> >    end with vendor custom CMO instructions.
> >
> > This patch enables linux kernel generic dma-noncoherency
> > infrastructure and introduces new sbi_ecall API for dma_sync.
> >
> > @@ -27,6 +27,7 @@ enum sbi_ext_id {
> > +       SBI_EXT_DMA = 0xAB150401,
> >
> > Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
> > Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
> > Cc: Palmer Dabbelt <palmerdabbelt@google.com>
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Anup Patel <anup.patel@wdc.com>
> > Cc: Arnd Bergmann <arnd@arndb.de>
> > Cc: Drew Fustini <drew@beagleboard.org>
> > Cc: Wei Fu <wefu@redhat.com>
> > Cc: Wei Wu <lazyparser@gmail.com>
> > Cc: Chen-Yu Tsai <wens@csie.org>
> > Cc: Maxime Ripard <maxime@cerno.tech>
> > ---
> >  arch/riscv/Kconfig               |  5 ++++
> >  arch/riscv/include/asm/pgtable.h | 26 ++++++++++++++++++++
> >  arch/riscv/include/asm/sbi.h     | 15 ++++++++++++
> >  arch/riscv/kernel/sbi.c          | 19 ++++++++++++++
> >  arch/riscv/mm/Makefile           |  1 +
> >  arch/riscv/mm/dma-mapping.c      | 53 ++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 119 insertions(+)
> >  create mode 100644 arch/riscv/mm/dma-mapping.c
> >
> > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> > index 05c4976..817a9bb 100644
> > --- a/arch/riscv/Kconfig
> > +++ b/arch/riscv/Kconfig
> > @@ -20,6 +20,10 @@ config RISCV
> >       select ARCH_HAS_DEBUG_VM_PGTABLE
> >       select ARCH_HAS_DEBUG_VIRTUAL if MMU
> >       select ARCH_HAS_DEBUG_WX
> > +     select ARCH_HAS_DMA_PREP_COHERENT
> > +     select ARCH_HAS_SYNC_DMA_FOR_CPU
> > +     select ARCH_HAS_SYNC_DMA_FOR_DEVICE
> > +     select ARCH_HAS_DMA_WRITE_COMBINE
> >       select ARCH_HAS_FORTIFY_SOURCE
> >       select ARCH_HAS_GCOV_PROFILE_ALL
> >       select ARCH_HAS_GIGANTIC_PAGE
> > @@ -43,6 +47,7 @@ config RISCV
> >       select CLONE_BACKWARDS
> >       select CLINT_TIMER if !MMU
> >       select COMMON_CLK
> > +     select DMA_DIRECT_REMAP
> >       select EDAC_SUPPORT
> >       select GENERIC_ARCH_TOPOLOGY if SMP
> >       select GENERIC_ATOMIC64 if !64BIT
> > diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> > index 6ddeb49..e1a82b6 100644
> > --- a/arch/riscv/include/asm/pgtable.h
> > +++ b/arch/riscv/include/asm/pgtable.h
> > @@ -462,6 +462,32 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
> >       return ptep_test_and_clear_young(vma, address, ptep);
> >  }
> >
> > +#define pgprot_noncached pgprot_noncached
> > +static inline pgprot_t pgprot_noncached(pgprot_t _prot)
> > +{
> > +     unsigned long prot = pgprot_val(_prot);
> > +
> > +     prot &= ~_PAGE_DMA_MASK;
> > +     prot |= _PAGE_DMA_IO;
> > +
> > +     return __pgprot(prot);
> > +}
> > +
> > +#define pgprot_writecombine pgprot_writecombine
> > +static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> > +{
> > +     unsigned long prot = pgprot_val(_prot);
> > +
> > +     prot &= ~_PAGE_DMA_MASK;
> > +     prot |= _PAGE_DMA_WC;
> > +
> > +     return __pgprot(prot);
> > +}
> > +
> > +#define __HAVE_PHYS_MEM_ACCESS_PROT
> > +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
> > +                                  unsigned long size, pgprot_t vma_prot);
> > +
> >  /*
> >   * Encode and decode a swap entry
> >   *
> > diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
> > index 0d42693..133e88a 100644
> > --- a/arch/riscv/include/asm/sbi.h
> > +++ b/arch/riscv/include/asm/sbi.h
> > @@ -27,6 +27,7 @@ enum sbi_ext_id {
> >       SBI_EXT_IPI = 0x735049,
> >       SBI_EXT_RFENCE = 0x52464E43,
> >       SBI_EXT_HSM = 0x48534D,
> > +     SBI_EXT_DMA = 0xAB150401,
> >  };
> >
> >  enum sbi_ext_base_fid {
> > @@ -63,6 +64,17 @@ enum sbi_ext_hsm_fid {
> >       SBI_EXT_HSM_HART_STATUS,
> >  };
> >
> > +enum sbi_ext_dma_fid {
> > +     SBI_DMA_SYNC = 0,
> > +};
> > +
> > +enum sbi_dma_sync_data_direction {
> > +     SBI_DMA_BIDIRECTIONAL = 0,
> > +     SBI_DMA_TO_DEVICE = 1,
> > +     SBI_DMA_FROM_DEVICE = 2,
> > +     SBI_DMA_NONE = 3,
> > +};
> > +
> >  enum sbi_hsm_hart_status {
> >       SBI_HSM_HART_STATUS_STARTED = 0,
> >       SBI_HSM_HART_STATUS_STOPPED,
> > @@ -128,6 +140,9 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
> >                               unsigned long size,
> >                               unsigned long asid);
> >  int sbi_probe_extension(int ext);
> > +void sbi_dma_sync(unsigned long start,
> > +               unsigned long size,
> > +               enum sbi_dma_sync_data_direction dir);
> >
> >  /* Check if current SBI specification version is 0.1 or not */
> >  static inline int sbi_spec_is_0_1(void)
> > diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
> > index 7402a41..c936019 100644
> > --- a/arch/riscv/kernel/sbi.c
> > +++ b/arch/riscv/kernel/sbi.c
> > @@ -521,6 +521,25 @@ int sbi_probe_extension(int extid)
> >  }
> >  EXPORT_SYMBOL(sbi_probe_extension);
> >
> > +void sbi_dma_sync(unsigned long start,
> > +               unsigned long size,
> > +               enum sbi_dma_sync_data_direction dir)
> > +{
> > +#if 0
> > +     sbi_ecall(SBI_EXT_DMA, SBI_DMA_SYNC, start, size, dir,
> > +               0, 0, 0);
> > +#else
> > +     /* Just for try, it should be in sbi ecall and will be removed before merged */
> > +     register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1);
> > +
> > +     for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES)
> > +             __asm__ __volatile__(".long 0x02b5000b");
> > +
>
>
> Hi, I'm trying to use this patch for my D1 board.
>
> Though the above code will be removed, I notice that the use of the inline assembly is wrong and `i` (i.e. `a0`) might not be correctly passed to the assembly code when we are using some other compilers.
>
>
> It should be `__asm__ __volatile__(".long 0x02b5000b" : : "r"(i))`.
Yes, I should keep "r"(I).

Thx

>
>
> Thanks,
> Wende
>
>
> > +     __asm__ __volatile__(".long 0x01b0000b");
> > +#endif
> > +}
> > +EXPORT_SYMBOL(sbi_dma_sync);
> > +
> >  static long __sbi_base_ecall(int fid)
> >  {
> >       struct sbiret ret;
> > diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
> > index 7ebaef1..ca0ff90 100644
> > --- a/arch/riscv/mm/Makefile
> > +++ b/arch/riscv/mm/Makefile
> > @@ -13,6 +13,7 @@ obj-y += extable.o
> >  obj-$(CONFIG_MMU) += fault.o pageattr.o
> >  obj-y += cacheflush.o
> >  obj-y += context.o
> > +obj-y += dma-mapping.o
> >
> >  ifeq ($(CONFIG_MMU),y)
> >  obj-$(CONFIG_SMP) += tlbflush.o
> > diff --git a/arch/riscv/mm/dma-mapping.c b/arch/riscv/mm/dma-mapping.c
> > new file mode 100644
> > index 00000000..4afd9dc
> > --- /dev/null
> > +++ b/arch/riscv/mm/dma-mapping.c
> > @@ -0,0 +1,53 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <linux/dma-map-ops.h>
> > +#include <asm/sbi.h>
> > +
> > +void arch_dma_prep_coherent(struct page *page, size_t size)
> > +{
> > +     void *ptr = page_address(page);
> > +
> > +     memset(ptr, 0, size);
> > +     sbi_dma_sync(page_to_phys(page), size, SBI_DMA_BIDIRECTIONAL);
> > +}
> > +
> > +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
> > +             enum dma_data_direction dir)
> > +{
> > +     switch (dir) {
> > +     case DMA_TO_DEVICE:
> > +     case DMA_FROM_DEVICE:
> > +     case DMA_BIDIRECTIONAL:
> > +             sbi_dma_sync(paddr, size, dir);
> > +             break;
> > +     default:
> > +             BUG();
> > +     }
> > +}
> > +
> > +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
> > +             enum dma_data_direction dir)
> > +{
> > +     switch (dir) {
> > +     case DMA_TO_DEVICE:
> > +             return;
> > +     case DMA_FROM_DEVICE:
> > +     case DMA_BIDIRECTIONAL:
> > +             sbi_dma_sync(paddr, size, dir);
> > +             break;
> > +     default:
> > +             BUG();
> > +     }
> > +}
> > +
> > +pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
> > +                           unsigned long size, pgprot_t vma_prot)
> > +{
> > +     if (!pfn_valid(pfn))
> > +             return pgprot_noncached(vma_prot);
> > +     else if (file->f_flags & O_SYNC)
> > +             return pgprot_writecombine(vma_prot);
> > +
> > +     return vma_prot;
> > +}
> > +EXPORT_SYMBOL(phys_mem_access_prot);
>
> --
> Best wishes!
> Sincerely,
> twd2
> https://twd2.me
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2021-10-20  8:11 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-06  9:03 [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 guoren
2021-06-06  9:03 ` [RFC PATCH v2 01/11] riscv: asid: Use global mappings for kernel pages guoren
2021-06-06  9:03 ` [PATCH V5 1/3] riscv: " guoren
2021-06-06  9:03 ` [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods guoren
2021-06-06 14:38   ` Christoph Hellwig
2021-06-06  9:03 ` [RFC PATCH v2 02/11] riscv: asid: " guoren
2021-06-06  9:04 ` [RFC PATCH v2 03/11] riscv: asid: Optimize tlbflush coding convention guoren
2021-06-06  9:04 ` [PATCH V5 3/3] riscv: tlbflush: Optimize " guoren
2021-06-06  9:04 ` [RFC PATCH v2 04/11] riscv: pgtable: Fixup _PAGE_CHG_MASK usage guoren
2021-06-06  9:04 ` [RFC PATCH v2 05/11] riscv: pgtable: Add custom protection_map init guoren
2021-06-06  9:04 ` [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes guoren
2021-06-06 14:39   ` Christoph Hellwig
2021-06-06 15:08     ` Guo Ren
2021-06-06 17:22   ` Nick Kossifidis
2021-06-07  6:19     ` Christoph Hellwig
2021-06-06  9:04 ` [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support guoren
2021-10-17  9:28   ` twd2
2021-10-20  8:11     ` Guo Ren
2021-06-06  9:04 ` [RFC PATCH v2 08/11] riscv: cmo: Add vendor custom icache sync guoren
2021-06-06  9:04 ` [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board guoren
2021-06-06 16:26   ` Jernej Škrabec
2021-06-06 17:05     ` Guo Ren
2021-06-07  3:44     ` Guo Ren
2021-06-07  7:27       ` Maxime Ripard
2021-06-07  7:53         ` Guo Ren
2021-06-07  7:24   ` Maxime Ripard
2021-06-07  8:07     ` Guo Ren
2021-06-14 15:33       ` Maxime Ripard
2021-06-14 16:28         ` Guo Ren
2021-06-14 16:31           ` Jernej Škrabec
2021-06-06  9:04 ` [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option guoren
2021-06-07  7:19   ` Maxime Ripard
2021-06-07  7:27     ` Arnd Bergmann
2021-06-07  7:45       ` Guo Ren
2021-06-07  7:43     ` Guo Ren
2021-06-07 12:12       ` Maxime Ripard
2021-06-07 12:39         ` Guo Ren
2021-06-06  9:04 ` [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use guoren
2021-06-06 10:50   ` Andre Przywara
2021-06-06 15:32     ` Guo Ren
2021-06-06 15:39       ` Jernej Škrabec
2021-06-06 15:41         ` Guo Ren
2021-06-06 16:16   ` Arnd Bergmann
2021-06-06 16:32     ` Jernej Škrabec
2021-06-06 16:53       ` Guo Ren
2021-06-06 16:53     ` Guo Ren
2021-06-06 16:29 ` [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1 Jernej Škrabec
2021-06-06 16:54   ` Guo Ren
2021-06-06 17:14     ` Jernej Škrabec
2021-06-06 23:42       ` Guo Ren
2021-06-07  3:44 ` Anup Patel
2021-06-07  4:36   ` Guo Ren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).