Skip to contentSkip to conversation

riscv: Add DMA_COHERENT support for Allwinner D1

RFC PATCH Superseded [email protected] 9 participants 52 messages Jun 6, 2021 – Oct 20, 2021

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

v2 cover riscv: Add DMA_COHERENT support for Allwinner D1

From: Guo Ren <[email protected]>

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.

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
Signature · 1 line
2.7.4
#1 lore raw

RFC PATCH v2 1/11 riscv: asid: Use global mappings for kernel pages

From: Guo Ren <[email protected]>

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.

---
 arch/riscv/include/asm/pgtable.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/riscv/include/asm/pgtable.h +2 -1
index 9469f46..346a3c6 100644
@@ -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)
Signature · 1 line
2.7.4
#2 lore raw

PATCH v5 1/3 riscv: Use global mappings for kernel pages

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

---
 arch/riscv/include/asm/pgtable.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/riscv/include/asm/pgtable.h +2 -1
index 9469f46..346a3c6 100644
@@ -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)
Signature · 1 line
2.7.4
#3 lore raw

PATCH v5 2/3 riscv: Add ASID-based tlbflushing methods

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Palmer Dabbelt <[email protected]>
Cc Christoph Hellwig <[email protected]>
---
 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(-)
arch/riscv/include/asm/mmu_context.h +2 -0
index b065941..7030837 100644
@@ -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 */
arch/riscv/include/asm/tlbflush.h +22 -0
index c84218a..894cf75 100644
@@ -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)
arch/riscv/mm/context.c +1 -1
index 68aa312..45c1b04 100644
@@ -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;
arch/riscv/mm/tlbflush.c +43 -3

67 lines not on this page · raw message

Signature · 1 line
2.7.4
#4 lore raw

Re: [PATCH V5 2/3] riscv: Add ASID-based tlbflushing methods

Quoted 9 lines · On Sun, Jun 06, 2021 at 09:03:58AM +0000, [email protected] wrote:
arch/riscv/include/asm/tlbflush.h +9 -0
+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,

arch/riscv/include/asm/tlbflush.h static inline void local_flush_tlb_page(unsigned long addr) · line 44

Quoted 10 lines
arch/riscv/include/asm/tlbflush.h +10 -0
+				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.

Quoted 16 lines
arch/riscv/mm/tlbflush.c +16 -0
+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);

arch/riscv/mm/tlbflush.c static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start, · line 68

Quoted 10 lines
arch/riscv/mm/tlbflush.c +10 -0
+		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.

Quoted 20 lines
arch/riscv/mm/tlbflush.c +11 -2
+
 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);
 }

arch/riscv/mm/tlbflush.c static void __sbi_tlb_flush_range(struct cpumask *cmask, unsigned long start, · line 95

Quoted 10 lines
arch/riscv/mm/tlbflush.c +5 -1
 
 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).

#5 lore raw

RFC PATCH v2 2/11 riscv: asid: Add ASID-based tlbflushing methods

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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(-)
arch/riscv/include/asm/mmu_context.h +2 -0
index b065941..7030837 100644
@@ -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 */
arch/riscv/include/asm/tlbflush.h +22 -0
index c84218a..894cf75 100644
@@ -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)
arch/riscv/mm/context.c +1 -1
index 68aa312..45c1b04 100644
@@ -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;
arch/riscv/mm/tlbflush.c +43 -3

67 lines not on this page · raw message

Signature · 1 line
2.7.4
#6 lore raw

RFC PATCH v2 3/11 riscv: asid: Optimize tlbflush coding convention

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

---
 arch/riscv/mm/tlbflush.c | 91 ++++++++++++++++++++++--------------------------
 1 file changed, 41 insertions(+), 50 deletions(-)
arch/riscv/mm/tlbflush.c +41 -50
index 87b4e52..facca6e 100644
@@ -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);
 }
Signature · 1 line
2.7.4
#7 lore raw

PATCH v5 3/3 riscv: tlbflush: Optimize coding convention

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

---
 arch/riscv/mm/tlbflush.c | 91 ++++++++++++++++++++++--------------------------
 1 file changed, 41 insertions(+), 50 deletions(-)
arch/riscv/mm/tlbflush.c +41 -50
index 87b4e52..facca6e 100644
@@ -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);
 }
Signature · 1 line
2.7.4
#8 lore raw

RFC PATCH v2 4/11 riscv: pgtable: Fixup _PAGE_CHG_MASK usage

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/include/asm/pgtable-64.h | 8 +++++---
 arch/riscv/include/asm/pgtable.h    | 6 +++---
 2 files changed, 8 insertions(+), 6 deletions(-)
arch/riscv/include/asm/pgtable-64.h +5 -3
index f3b0da6..cbf9acf 100644
@@ -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) \
arch/riscv/include/asm/pgtable.h +3 -3
index 346a3c6..13a79643 100644
@@ -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))
Signature · 1 line
2.7.4
#9 lore raw

RFC PATCH v2 5/11 riscv: pgtable: Add custom protection_map init

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Andrew Morton <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Palmer Dabbelt <[email protected]>
---
 arch/riscv/Kconfig   |  4 ++++
 arch/riscv/mm/init.c | 22 ++++++++++++++++++++++
 mm/mmap.c            |  4 ++++
 3 files changed, 30 insertions(+)
arch/riscv/Kconfig +4 -0
index c5914e7..05c4976 100644
@@ -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
arch/riscv/mm/init.c +22 -0
index 4faf8bd..4b398c6 100644
@@ -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);
mm/mmap.c +4 -0
index 0584e54..0a86059 100644
@@ -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)
Signature · 1 line
2.7.4
#10 lore raw

RFC PATCH v2 6/11 riscv: pgtable: Add DMA_COHERENT with custom PTE attributes

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Palmer Dabbelt <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Anup Patel <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
---
 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(-)
arch/riscv/include/asm/pgtable-bits.h +19 -1
index bbaeb5d..080a9eb 100644
@@ -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 */
arch/riscv/include/asm/pgtable.h +4 -7
index 13a79643..6ddeb49 100644
@@ -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[];
 
arch/riscv/include/asm/soc.h +1 -0
index f494066..fc587d7 100644
@@ -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;
arch/riscv/include/asm/vendorid_list.h +1 -0
index 9d93421..c2710f3 100644
@@ -6,5 +6,6 @@
 #define ASM_VENDOR_LIST_H
 
 #define SIFIVE_VENDOR_ID	0x489
+#define  THEAD_VENDOR_ID	0x401
 
 #endif
arch/riscv/kernel/soc.c +22 -0
index a051617..05fa764 100644
@@ -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;
+	}
+};
arch/riscv/mm/init.c +4 -0
index 4b398c6..fb70c49 100644
@@ -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);
Signature · 1 line
2.7.4
#11 lore raw

Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes

NAK, no SOC must ever mess with pagetable attributes.

author
#12 lore raw

Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes

Quoted 2 lines · Christoph Hellwig

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.

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#13 lore raw

Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes

In reply to [email protected]

Στις 2021-06-06 12:04, [email protected] έγραψε:

Quoted 8 lines

From: Guo Ren <[email protected]>

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)

Quoted 5 lines

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.

#14 lore raw

Re: [RFC PATCH v2 06/11] riscv: pgtable: Add DMA_COHERENT with custom PTE attributes

Quoted 9 lines · Nick Kossifidis

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.

#15 lore raw

RFC PATCH v2 7/11 riscv: cmo: Add dma-noncoherency support

In reply to [email protected]

From: Guo Ren <[email protected]>

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,

Cc Palmer Dabbelt <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Anup Patel <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
---
 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
arch/riscv/Kconfig +5 -0
index 05c4976..817a9bb 100644
@@ -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
arch/riscv/include/asm/pgtable.h +26 -0
index 6ddeb49..e1a82b6 100644
@@ -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
  *
arch/riscv/include/asm/sbi.h +15 -0
index 0d42693..133e88a 100644
@@ -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)
arch/riscv/kernel/sbi.c +19 -0
index 7402a41..c936019 100644
@@ -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;
arch/riscv/mm/Makefile +1 -0
index 7ebaef1..ca0ff90 100644
@@ -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
arch/riscv/mm/dma-mapping.c new file +53 -0

53 lines not on this page · raw message

Signature · 1 line
2.7.4
#16 lore raw

Re: [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support

Quoted 151 lines · On 2021/6/6 17:04, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

arch/riscv/include/asm/sbi.h +1 -0
@@ -27,6 +27,7 @@ enum sbi_ext_id {
+       SBI_EXT_DMA = 0xAB150401,
 
Cc Palmer Dabbelt <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Anup Patel <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
---
 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
arch/riscv/Kconfig +5 -0
index 05c4976..817a9bb 100644
@@ -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
arch/riscv/include/asm/pgtable.h +26 -0
index 6ddeb49..e1a82b6 100644
@@ -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
  *
arch/riscv/include/asm/sbi.h +15 -0
index 0d42693..133e88a 100644
@@ -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)
arch/riscv/kernel/sbi.c +4 -0
index 7402a41..c936019 100644
@@ -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)
+{

arch/riscv/kernel/sbi.c int sbi_probe_extension(int extid) · line 537

Quoted 10 lines
arch/riscv/kernel/sbi.c +10 -0
+#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

Quoted 79 lines
arch/riscv/kernel/sbi.c +5 -0
+	__asm__ __volatile__(".long 0x01b0000b");
+#endif
+}
+EXPORT_SYMBOL(sbi_dma_sync);
+
 static long __sbi_base_ecall(int fid)
 {
 	struct sbiret ret;
arch/riscv/mm/Makefile +1 -0
index 7ebaef1..ca0ff90 100644
@@ -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
arch/riscv/mm/dma-mapping.c new file +53 -0
new file mode 100644
index 00000000..4afd9dc
@@ -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);
Signature · 4 lines
Best wishes!
Sincerely,
twd2
https://twd2.me
author
#17 lore raw

Re: [RFC PATCH v2 07/11] riscv: cmo: Add dma-noncoherency support

Quoted 168 lines · On Sun, Oct 17, 2021 at 5:28 PM twd2 <[email protected]> wrote:
Quoted 151 lines · On 2021/6/6 17:04, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

arch/riscv/include/asm/sbi.h +1 -0
@@ -27,6 +27,7 @@ enum sbi_ext_id {
+       SBI_EXT_DMA = 0xAB150401,
 
Cc Palmer Dabbelt <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Anup Patel <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
---
 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
arch/riscv/Kconfig +5 -0
index 05c4976..817a9bb 100644
@@ -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
arch/riscv/include/asm/pgtable.h +26 -0
index 6ddeb49..e1a82b6 100644
@@ -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
  *
arch/riscv/include/asm/sbi.h +15 -0
index 0d42693..133e88a 100644
@@ -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)
arch/riscv/kernel/sbi.c +4 -0
index 7402a41..c936019 100644
@@ -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)
+{

arch/riscv/kernel/sbi.c int sbi_probe_extension(int extid) · line 537

Quoted 10 lines
arch/riscv/kernel/sbi.c +10 -0
+#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.

Quoted 4 lines

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

Quoted 92 lines

Thanks,
Wende

Quoted 69 lines
arch/riscv/kernel/sbi.c +5 -0
+     __asm__ __volatile__(".long 0x01b0000b");
+#endif
+}
+EXPORT_SYMBOL(sbi_dma_sync);
+
 static long __sbi_base_ecall(int fid)
 {
      struct sbiret ret;
arch/riscv/mm/Makefile +1 -0
index 7ebaef1..ca0ff90 100644
@@ -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
arch/riscv/mm/dma-mapping.c new file +43 -0
new file mode 100644
index 00000000..4afd9dc
@@ -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,

arch/riscv/mm/dma-mapping.c line 53

Quoted 10 lines
arch/riscv/mm/dma-mapping.c new file +10 -0
+                           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

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#18 lore raw

RFC PATCH v2 8/11 riscv: cmo: Add vendor custom icache sync

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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(-)
arch/riscv/include/asm/cacheflush.h +47 -1
index 23ff703..2e2dba1 100644
@@ -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)
 
arch/riscv/kernel/vdso/flush_icache.S +27 -6
index 82f97d6..efb2d2e 100644
@@ -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)
arch/riscv/mm/cacheflush.c +2 -1
index 0941186..0fb8344 100644
@@ -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 */
Signature · 1 line
2.7.4
#19 lore raw

RFC PATCH v2 9/11 riscv: soc: Initial DTS for Allwinner D1 NeZha board

In reply to [email protected]

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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
arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
new file mode 100644
index 00000000..4adbf4b
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +29 -0
new file mode 100644
index 00000000..cd9f7c9
@@ -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";
+};
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +84 -0

84 lines not on this page · raw message

Signature · 1 line
2.7.4
maintainer
#20 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

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 [email protected] napisal(a):

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Quoted 52 lines

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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

arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000..4adbf4b

arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode 100644
index 00000000..cd9f7c9

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 1

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +1 -0
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

Usually copyrights are added below spdx id.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 9

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +8 -0
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;

This should be part of SoC level DTSI.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 11

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +2 -0
+	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.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+	chosen {
+		bootargs = "console=ttyS0,115200";

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

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 21

Quoted 7 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +7 -0
+		stdout-path = &serial0;
+	};
+
+	memory@40000000 {
+		device_type = "memory";
+		reg = <0x0 0x40000000 0x0 0x20000000>;
+	};

Ditto for whole memory node.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 24

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+	soc {
+	};

There is no point having empty nodes.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +5 -0
+};
+
+&serial0 {
+	status = "okay";
+};
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644 index 00000000..11cd938

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 7

Quoted 9 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +7 -0
@@ -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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 9

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +2 -0
+	model = "Allwinner D1 Soc";
+	compatible = "allwinner,d1-nezha-kit";

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +3 -0
+
+	chosen {
+	};

Remove empty node.

Quoted 51 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +51 -0
+
+	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>;

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 73

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +10 -0
+		};
+
+		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

Quoted 11 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +11 -0
+			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";
+		};
+	};
+};
author
#21 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Quoted 63 lines · Jernej Škrabec

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 [email protected] napisal(a):

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Quoted 52 lines

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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

arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000..4adbf4b

arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode 100644
index 00000000..cd9f7c9

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +0 -0

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

Quoted 4 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 1

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +1 -0
@@ -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.

Quoted 12 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 9

Quoted 9 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +8 -0
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+     #address-cells = <2>;
+     #size-cells = <2>;

This should be part of SoC level DTSI.

Quoted 5 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 11

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +2 -0
+     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

Quoted 7 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     chosen {
+             bootargs = "console=ttyS0,115200";

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

Okay

Quoted 10 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 21

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +7 -0
+             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.

Quoted 6 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 24

Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     soc {
+     };

There is no point having empty nodes.

Okay.

Quoted 30 lines

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 11 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +5 -0
+};
+
+&serial0 {
+     status = "okay";
+};
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644 index 00000000..11cd938

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 7

Quoted 9 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +7 -0
@@ -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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 9

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +2 -0
+     model = "Allwinner D1 Soc";
+     compatible = "allwinner,d1-nezha-kit";
Quoted 7 lines

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +3 -0
+
+     chosen {
+     };

Remove empty node.

Okay.

Quoted 57 lines
Quoted 57 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +56 -0
+
+     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>;
Quoted 8 lines

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 73

Quoted 5 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +5 -0
+                     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.

Quoted 19 lines

Best regards,
Jernej

Quoted 11 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +11 -0
+                     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";
+             };
+     };
+};
Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
author
#22 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

In reply to Jernej Škrabec
Quoted 85 lines · Jernej Škrabec

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 [email protected] napisal(a):

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Quoted 52 lines

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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

arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000..4adbf4b

arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode 100644
index 00000000..cd9f7c9

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 1

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +1 -0
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

Usually copyrights are added below spdx id.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 9

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +8 -0
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+     #address-cells = <2>;
+     #size-cells = <2>;

This should be part of SoC level DTSI.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 11

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +2 -0
+     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.

Quoted 6 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     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.

Quoted 10 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 21

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +7 -0
+             stdout-path = &serial0;
+     };
+
+     memory@40000000 {
+             device_type = "memory";
+             reg = <0x0 0x40000000 0x0 0x20000000>;
+     };

Ditto for whole memory node.

Ditto

Quoted 127 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 24

Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     soc {
+     };

There is no point having empty nodes.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +5 -0
+};
+
+&serial0 {
+     status = "okay";
+};
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file mode 100644 index 00000000..11cd938

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 7

Quoted 9 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +7 -0
@@ -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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 9

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +2 -0
+     model = "Allwinner D1 Soc";
+     compatible = "allwinner,d1-nezha-kit";

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

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +3 -0
+
+     chosen {
+     };

Remove empty node.

Quoted 51 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +51 -0
+
+     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>;

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 73

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +10 -0
+             };
+
+             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

Quoted 11 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +11 -0
+                     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";
+             };
+     };
+};
Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#23 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Quoted 106 lines · Guo Ren
Quoted 85 lines · Jernej Škrabec

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 [email protected] napisal(a):

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Quoted 52 lines

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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

arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000..4adbf4b

arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode 100644
index 00000000..cd9f7c9

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 1

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +1 -0
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

Usually copyrights are added below spdx id.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 9

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +8 -0
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+     #address-cells = <2>;
+     #size-cells = <2>;

This should be part of SoC level DTSI.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 11

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +2 -0
+     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.

Quoted 6 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     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.

Quoted 9 lines
Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +7 -0
+             stdout-path = &serial0;
+     };
+
+     memory@40000000 {
+             device_type = "memory";
+             reg = <0x0 0x40000000 0x0 0x20000000>;
+     };
Quoted 3 lines
Quoted 1 line

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

author
#24 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Thx for the clarification.

Quoted 112 lines · Maxime Ripard
Quoted 106 lines · Guo Ren
Quoted 85 lines · Jernej Škrabec

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 [email protected] napisal(a):

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts

Quoted 52 lines

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 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

arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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))
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000..4adbf4b

arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file mode 100644
index 00000000..cd9f7c9

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +0 -0

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

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 1

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +1 -0
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)

Usually copyrights are added below spdx id.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 9

Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +8 -0
+
+/dts-v1/;
+
+#include "allwinner-d1.dtsi"
+
+/ {
+     #address-cells = <2>;
+     #size-cells = <2>;

This should be part of SoC level DTSI.

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 11

Quoted 2 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +2 -0
+     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.

Quoted 6 lines

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 3 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +3 -0
+
+     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.

Quoted 9 lines
Quoted 8 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +7 -0
+             stdout-path = &serial0;
+     };
+
+     memory@40000000 {
+             device_type = "memory";
+             reg = <0x0 0x40000000 0x0 0x20000000>;
+     };
Quoted 3 lines
Quoted 1 line

Ditto for whole memory node.

Ditto

Quoted 8 lines

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.

Quoted 2 lines

Maxime

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#25 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

In reply to [email protected]

Hi,

Thanks for the patches

Quoted 17 lines · On Sun, Jun 06, 2021 at 09:04:07AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Quoted 5 lines
---
 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?

Quoted 10 lines
 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
arch/riscv/boot/dts/Makefile +0 -0
index fe996b8..3e7b264 100644

arch/riscv/boot/dts/Makefile line 5

Quoted 4 lines
arch/riscv/boot/dts/Makefile +1 -0
@@ -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?

Quoted 82 lines
arch/riscv/boot/dts/Makefile +0 -0
 obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
new file mode 100644
index 00000000..4adbf4b
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +29 -0
new file mode 100644
index 00000000..cd9f7c9
@@ -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";
+};
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +32 -0
new file mode 100644
index 00000000..11cd938
@@ -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;
+			};
+		};
+	};
+

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 42

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +10 -0
+	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

author
#26 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Quoted 29 lines · Maxime Ripard

Hi,

Thanks for the patches

Quoted 17 lines · On Sun, Jun 06, 2021 at 09:04:07AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Quoted 5 lines
---
 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 ++++++++++++++++++++++
Quoted 1 line

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

Yes

Quoted 6 lines
Quoted 6 lines
 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
Quoted 11 lines

arch/riscv/boot/dts/Makefile line 5

Quoted 9 lines
arch/riscv/boot/dts/Makefile +1 -0
index fe996b8..3e7b264 100644
@@ -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.

Quoted 87 lines
Quoted 87 lines
arch/riscv/boot/dts/Makefile +0 -0
 obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
arch/riscv/boot/dts/allwinner/Makefile new file +2 -0
new file mode 100644
index 00000000..4adbf4b
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_SOC_SUNXI) += allwinner-d1-nezha-kit.dtb
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts new file +29 -0
new file mode 100644
index 00000000..cd9f7c9
@@ -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";
+};
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +36 -0
new file mode 100644
index 00000000..11cd938
@@ -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";
Quoted 8 lines

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 42

Quoted 6 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +6 -0
+             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.

Quoted 2 lines

Maxime

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#27 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Quoted 10 lines · Guo Ren
Quoted 6 lines · Maxime Ripard

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 42

Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +4 -0
+             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

author
#28 lore raw

Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Quoted 8 lines · Maxime Ripard
Quoted 6 lines · Guo Ren
Quoted 5 lines · Maxime Ripard
Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +4 -0
+             reset: reset-sample {
+                     compatible = "thead,reset-sample";
+                     plic-delegate = <0x0 0x101ffffc>;
+             };
Quoted 6 lines
Quoted 4 lines
Quoted 1 line

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

Quoted 2 lines

Maxime

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
maintainer
#29 lore raw

Re: Re: [RFC PATCH v2 09/11] riscv: soc: Initial DTS for Allwinner D1 NeZha board

Dne ponedeljek, 14. junij 2021 ob 18:28:28 CEST je Guo Ren napisal(a):

Quoted 9 lines · Maxime Ripard
Quoted 8 lines · Guo Ren
Quoted 6 lines · Maxime Ripard
Quoted 5 lines
Quoted 4 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi new file +4 -0
+             reset: reset-sample {
+                     compatible = "thead,reset-sample";
+                     plic-delegate = <0x0 0x101ffffc>;
+             };
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Quoted 1 line

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

Quoted 15 lines

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

Quoted 2 lines

Maxime

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#30 lore raw

RFC PATCH v2 10/11 riscv: soc: Add Allwinner SoC kconfig option

In reply to [email protected]

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)
arch/riscv/Kconfig.socs +12 -0
index ed96376..055fb3e 100644
@@ -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
arch/riscv/configs/defconfig +1 -0
index 1f2be23..9e83d14 100644
@@ -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
Signature · 1 line
2.7.4
#31 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

Hi,

Quoted 36 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)
arch/riscv/Kconfig.socs +2 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
+config SOC_SUNXI
+	bool "Allwinner SoCs"

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 10 lines
arch/riscv/Kconfig.socs +10 -0
+	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

maintainer
#32 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

Quoted 10 lines · Maxime Ripard
Quoted 8 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:
arch/riscv/Kconfig.socs +0 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
Quoted 15 lines

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 12 lines
arch/riscv/Kconfig.socs +12 -0
+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

author
#33 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

Quoted 28 lines · Arnd Bergmann
Quoted 10 lines · Maxime Ripard
Quoted 8 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:
arch/riscv/Kconfig.socs +0 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
Quoted 15 lines

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 12 lines
arch/riscv/Kconfig.socs +12 -0
+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.

Quoted 6 lines

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

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
author
#34 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

In reply to Maxime Ripard
Quoted 54 lines · Maxime Ripard

Hi,

Quoted 36 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)
arch/riscv/Kconfig.socs +2 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
+config SOC_SUNXI
+     bool "Allwinner SoCs"

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 10 lines
arch/riscv/Kconfig.socs +10 -0
+     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.

Quoted 1 line

Maxime

Remove DWMAC, STMMAC_ETH is okay.

But I think we still need:
select SERIAL_8250_DW if SERIAL_8250

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#35 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

Quoted 55 lines · Guo Ren
Quoted 54 lines · Maxime Ripard

Hi,

Quoted 36 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)
arch/riscv/Kconfig.socs +2 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
+config SOC_SUNXI
+     bool "Allwinner SoCs"

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 10 lines
arch/riscv/Kconfig.socs +10 -0
+     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.

Quoted 5 lines
Quoted 1 line

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

author
#36 lore raw

Re: [RFC PATCH v2 10/11] riscv: soc: Add Allwinner SoC kconfig option

Quoted 57 lines · Maxime Ripard
Quoted 55 lines · Guo Ren
Quoted 54 lines · Maxime Ripard

Hi,

Quoted 36 lines · On Sun, Jun 06, 2021 at 09:04:08AM +0000, [email protected] wrote:

From: Guo Ren <[email protected]>

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.

Cc Anup Patel <[email protected]>
Cc Atish Patra <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Drew Fustini <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 arch/riscv/Kconfig.socs      | 12 ++++++++++++
 arch/riscv/configs/defconfig |  1 +
 2 files changed, 13 insertions(+)
arch/riscv/Kconfig.socs +2 -0
index ed96376..055fb3e 100644
@@ -69,4 +69,16 @@ config SOC_CANAAN_K210_DTB_SOURCE
 
 endif
 
+config SOC_SUNXI
+     bool "Allwinner SoCs"

arch/riscv/Kconfig.socs config SOC_CANAAN_K210_DTB_SOURCE · line 83

Quoted 10 lines
arch/riscv/Kconfig.socs +10 -0
+     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.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

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

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#37 lore raw

RFC PATCH v2 11/11 riscv: soc: Allwinner D1 GMAC driver only for temp use

In reply to [email protected]

From: liush <[email protected]>

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.

Cc Maxime Ripard <[email protected]>
Cc Corentin Labbe <[email protected]>
Cc Samuel Holland <[email protected]>
Cc Icenowy Zheng <[email protected]>
Cc LABBE Corentin <[email protected]>
Cc Michael Walle <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 .../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
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
index cd9f7c9..31b681d 100644
@@ -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;
 	};
 
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +16 -0
index 11cd938..d317e19 100644
@@ -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";
+		};
 	};
 };
drivers/net/ethernet/Kconfig +1 -0
index 1cdff1d..1f8e37c 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index cb3f908..3dacc0c 100644
@@ -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/
drivers/net/ethernet/allwinnertmp/Kconfig new file +17 -0
new file mode 100644
index 00000000..4b7b378
@@ -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.
drivers/net/ethernet/allwinnertmp/Makefile new file +7 -0
new file mode 100644
index 00000000..1375dea
@@ -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
drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c new file +690 -0

690 lines not on this page · raw message

drivers/net/ethernet/allwinnertmp/sunxi-gmac.c new file +2240 -0

2,240 lines not on this page · raw message

drivers/net/ethernet/allwinnertmp/sunxi-gmac.h new file +258 -0

3 lines not on this page · raw message

drivers/net/phy/realtek.c +1 -1
index 821e85a..3c86c2a 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.7.4

Part of the diff is not shown here. View the raw message.

#38 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

On Sun, 6 Jun 2021 09:04:09 +0000
[email protected] wrote:

Hi,

Quoted 4 lines

From: liush <[email protected]>

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).

Quoted 7 lines

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

Quoted 3,350 lines
Cc Maxime Ripard <[email protected]>
Cc Corentin Labbe <[email protected]>
Cc Samuel Holland <[email protected]>
Cc Icenowy Zheng <[email protected]>
Cc LABBE Corentin <[email protected]>
Cc Michael Walle <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 .../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
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
index cd9f7c9..31b681d 100644
@@ -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;
 	};
 
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +16 -0
index 11cd938..d317e19 100644
@@ -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";
+		};
 	};
 };
drivers/net/ethernet/Kconfig +1 -0
index 1cdff1d..1f8e37c 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index cb3f908..3dacc0c 100644
@@ -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/
drivers/net/ethernet/allwinnertmp/Kconfig new file +17 -0
new file mode 100644
index 00000000..4b7b378
@@ -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.
drivers/net/ethernet/allwinnertmp/Makefile new file +7 -0
new file mode 100644
index 00000000..1375dea
@@ -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
drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c new file +690 -0
new file mode 100644
index 00000000..26ffd7f
@@ -0,0 +1,690 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *		Author: fuzhaoke <[email protected]>
+ *
+ * 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);
+}
drivers/net/ethernet/allwinnertmp/sunxi-gmac.c new file +2240 -0
new file mode 100644
index 00000000..0c67877
@@ -0,0 +1,2240 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *		Author: fuzhaoke <[email protected]>
+ *
+ * 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 <[email protected]>");
+MODULE_LICENSE("Dual BSD/GPL");
drivers/net/ethernet/allwinnertmp/sunxi-gmac.h new file +258 -0
new file mode 100644
index 00000000..ea7a6f15
@@ -0,0 +1,258 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
+ *
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

drivers/net/phy/realtek.c +1 -1
index 821e85a..3c86c2a 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Part of the diff is not shown here. View the raw message.

author
#39 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

,

Quoted 20 lines · Andre Przywara

On Sun, 6 Jun 2021 09:04:09 +0000
[email protected] wrote:

Hi,

Quoted 4 lines

From: liush <[email protected]>

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).

Quoted 5 lines

It only could work at 1Gps mode.

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

Quoted 6 lines
Quoted 2 lines

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.

Quoted 3 lines

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.

Quoted 3,346 lines

Cheers,
Andre

Quoted 3,342 lines
Cc Maxime Ripard <[email protected]>
Cc Corentin Labbe <[email protected]>
Cc Samuel Holland <[email protected]>
Cc Icenowy Zheng <[email protected]>
Cc LABBE Corentin <[email protected]>
Cc Michael Walle <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
---
 .../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
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
index cd9f7c9..31b681d 100644
@@ -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;
      };
 
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +16 -0
index 11cd938..d317e19 100644
@@ -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";
+             };
      };
 };
drivers/net/ethernet/Kconfig +1 -0
index 1cdff1d..1f8e37c 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index cb3f908..3dacc0c 100644
@@ -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/
drivers/net/ethernet/allwinnertmp/Kconfig new file +17 -0
new file mode 100644
index 00000000..4b7b378
@@ -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.
drivers/net/ethernet/allwinnertmp/Makefile new file +7 -0
new file mode 100644
index 00000000..1375dea
@@ -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
drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c new file +690 -0
new file mode 100644
index 00000000..26ffd7f
@@ -0,0 +1,690 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *           Author: fuzhaoke <[email protected]>
+ *
+ * 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);
+}
drivers/net/ethernet/allwinnertmp/sunxi-gmac.c new file +2240 -0
new file mode 100644
index 00000000..0c67877
@@ -0,0 +1,2240 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *           Author: fuzhaoke <[email protected]>
+ *
+ * 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 <[email protected]>");
+MODULE_LICENSE("Dual BSD/GPL");
drivers/net/ethernet/allwinnertmp/sunxi-gmac.h new file +258 -0
new file mode 100644
index 00000000..ea7a6f15
@@ -0,0 +1,258 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
+ *
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

drivers/net/phy/realtek.c +0 -0
index 821e85a..3c86c2a 100644
@@ -338,7 +338,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)

The rest of this diff is not shown. View the raw message.

Quoted 9 lines

drivers/net/phy/realtek.c

Quoted 8 lines
drivers/net/phy/realtek.c +1 -1

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

--
Best Regards
Guo Ren

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

Part of the diff is not shown here. View the raw message.

maintainer
#40 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

Hi!

Dne nedelja, 06. junij 2021 ob 17:32:22 CEST je Guo Ren napisal(a):

Quoted 3 lines

,

On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <[email protected]>

Quoted 26 lines · wrote:
Quoted 19 lines

On Sun, 6 Jun 2021 09:04:09 +0000
[email protected] wrote:

Hi,

Quoted 4 lines

From: liush <[email protected]>

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).

Quoted 5 lines

It only could work at 1Gps mode.

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

Quoted 6 lines
Quoted 2 lines

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.

Quoted 5 lines

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

Quoted 27 lines
Quoted 4 lines

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.

Quoted 17 lines

Cheers,
Andre

Quoted 14 lines
Cc Maxime Ripard <[email protected]>
Cc Corentin Labbe <[email protected]>
Cc Samuel Holland <[email protected]>
Cc Icenowy Zheng <[email protected]>
Cc LABBE Corentin <[email protected]>
Cc Michael Walle <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
author
#41 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

Quoted 34 lines · Jernej Škrabec

Hi!

Dne nedelja, 06. junij 2021 ob 17:32:22 CEST je Guo Ren napisal(a):

Quoted 3 lines

,

On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <[email protected]>

Quoted 26 lines · wrote:
Quoted 25 lines

On Sun, 6 Jun 2021 09:04:09 +0000
[email protected] wrote:

Hi,

Quoted 4 lines

From: liush <[email protected]>

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).

Quoted 7 lines

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.

Quoted 7 lines
Quoted 5 lines

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.

Quoted 34 lines

Best regards,
Jernej

Quoted 27 lines
Quoted 4 lines

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.

Quoted 17 lines

Cheers,
Andre

Quoted 14 lines
Cc Maxime Ripard <[email protected]>
Cc Corentin Labbe <[email protected]>
Cc Samuel Holland <[email protected]>
Cc Icenowy Zheng <[email protected]>
Cc LABBE Corentin <[email protected]>
Cc Michael Walle <[email protected]>
Cc Chen-Yu Tsai <[email protected]>
Cc Maxime Ripard <[email protected]>
Cc Wei Fu <[email protected]>
Cc Wei Wu <[email protected]>
Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
maintainer
#42 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

In reply to [email protected]

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 10 lines · On Sun, Jun 6, 2021 at 11:04 AM <[email protected]> wrote:
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
index cd9f7c9..31b681d 100644
@@ -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 .

Quoted 17 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +0 -0
                stdout-path = &serial0;
        };
 
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +6 -0
index 11cd938..d317e19 100644
@@ -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";

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 98

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +10 -0
+                       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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 8

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +9 -0
+++ 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 <[email protected]>
+ *
+ * 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

maintainer
#43 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

Dne nedelja, 06. junij 2021 ob 18:16:44 CEST je Arnd Bergmann napisal(a):

Quoted 77 lines · On Sun, Jun 6, 2021 at 11:04 AM <[email protected]> wrote:
Quoted 13 lines
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts index cd9f7c9..31b681d 100644

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
@@ -11,7 +11,7 @@
 
        compatible = "allwinner,d1-nezha-kit";
 
        chosen {
 
-               bootargs = "console=ttyS0,115200";
+               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init
Quoted 2 lines

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 .

Quoted 23 lines
                stdout-path = &serial0;
        
        };
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi index 11cd938..d317e19 100644

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +4 -0
@@ -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>;

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +1 -0
+                       interrupt-names = "gmacirq";

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 98

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +10 -0
+                       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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 8

Quoted 10 lines

+++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +8 -0
@@ -0,0 +1,690 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *             Author: fuzhaoke <[email protected]>
+ *
+ * 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.

Quoted 4 lines

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

author
#44 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

Quoted 79 lines · Jernej Škrabec

Dne nedelja, 06. junij 2021 ob 18:16:44 CEST je Arnd Bergmann napisal(a):

Quoted 77 lines · On Sun, Jun 6, 2021 at 11:04 AM <[email protected]> wrote:
Quoted 13 lines
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts index cd9f7c9..31b681d 100644

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
@@ -11,7 +11,7 @@
 
        compatible = "allwinner,d1-nezha-kit";
 
        chosen {
 
-               bootargs = "console=ttyS0,115200";
+               bootargs = "console=ttyS0,115200 rootwait init=/sbin/init
Quoted 2 lines

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 .

Quoted 23 lines
                stdout-path = &serial0;

        };
(quoted diff) +0 -0

b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi index 11cd938..d317e19 100644

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +4 -0
@@ -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>;

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +1 -0
+                       interrupt-names = "gmacirq";

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 98

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +10 -0
+                       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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 8

Quoted 10 lines

+++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +8 -0
@@ -0,0 +1,690 @@
+/*
+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
+ *
+ * Copyright © 2016-2018, fuzhaoke
+ *             Author: fuzhaoke <[email protected]>
+ *
+ * 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.

Quoted 8 lines
Quoted 4 lines

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

Quoted 7 lines

Best regards,
Jernej

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
author
#45 lore raw

Re: [RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

In reply to Arnd Bergmann

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

Quoted 74 lines · Arnd Bergmann

arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts line 14

Quoted 10 lines · On Sun, Jun 6, 2021 at 11:04 AM <[email protected]> wrote:
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +1 -1
index cd9f7c9..31b681d 100644
@@ -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 .

Quoted 17 lines
arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts +0 -0
                stdout-path = &serial0;
        };
 
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +6 -0
index 11cd938..d317e19 100644
@@ -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";

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 98

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +10 -0
+                       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.

arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi line 8

Quoted 10 lines
arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi +9 -0
+++ 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 <[email protected]>
+ *
+ * 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

Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
maintainer
#46 lore raw
In reply to [email protected]

Hi!

Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je [email protected] napisal(a):

Quoted 41 lines

From: Guo Ren <[email protected]>

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.

Quoted 6 lines

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

Quoted 67 lines

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
author
#47 lore raw

V5 is not related to the patch series.

Quoted 129 lines · Jernej Škrabec

Hi!

Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je [email protected] napisal(a):

Quoted 41 lines

From: Guo Ren <[email protected]>

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.

Quoted 6 lines

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

Quoted 67 lines

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
Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
maintainer
#48 lore raw

Dne nedelja, 06. junij 2021 ob 18:54:05 CEST je Guo Ren napisal(a):

Quoted 1 line

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

Quoted 2 lines

On Mon, Jun 7, 2021 at 12:29 AM Jernej Škrabec <[email protected]>

Quoted 3 lines · wrote:
Quoted 3 lines

Hi!

Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je [email protected]

napisal(a):

Quoted 142 lines
Quoted 142 lines
Quoted 53 lines

From: Guo Ren <[email protected]>

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.

Quoted 7 lines

- Plugin the TF card and power on D1.

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

Quoted 74 lines

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
author
#49 lore raw
Quoted 8 lines · Jernej Škrabec

Dne nedelja, 06. junij 2021 ob 18:54:05 CEST je Guo Ren napisal(a):

Quoted 1 line

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.

Quoted 160 lines

Best regards,
Jernej

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

Quoted 2 lines

On Mon, Jun 7, 2021 at 12:29 AM Jernej Škrabec <[email protected]>

Quoted 3 lines · wrote:
Quoted 3 lines

Hi!

Dne nedelja, 06. junij 2021 ob 11:03:55 CEST je [email protected]

napisal(a):

Quoted 142 lines
Quoted 142 lines
Quoted 53 lines

From: Guo Ren <[email protected]>

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.

Quoted 7 lines

- Plugin the TF card and power on D1.

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

Quoted 74 lines

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
Signature · 4 lines
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/
#50 lore raw
In reply to [email protected]
Quoted 69 lines

-----Original Message-----
From: [email protected] <[email protected]>
Sent: 06 June 2021 14:34
To: [email protected]; Anup Patel <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; [email protected]; Guo Ren <[email protected]>
Subject: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1

From: Guo Ren <[email protected]>

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.

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

Quoted 11 lines

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

Quoted 53 lines

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

author
#51 lore raw

Hi Anup,

Quoted 82 lines · Anup Patel
Quoted 70 lines

-----Original Message-----
From: [email protected] <[email protected]>
Sent: 06 June 2021 14:34
To: [email protected]; Anup Patel <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; [email protected]; Guo Ren <[email protected]>
Subject: [RFC PATCH v2 00/11] riscv: Add DMA_COHERENT support for Allwinner D1

From: Guo Ren <[email protected]>

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.

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

Quoted 11 lines

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

Quoted 5 lines

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/[email protected]/T/#t

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

Quoted 4 lines

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.

Quoted 58 lines

Regards,
Anup

Quoted 53 lines

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

Signature · 4 lines
Best Regards
 Guo Ren

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