Buildroot Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Yu Chien Peter Lin <peterlin@andestech.com>
To: <buildroot@buildroot.org>
Cc: Yu Chien Peter Lin <peterlin@andestech.com>,
	Alan Kao <alankao@andestech.com>
Subject: [Buildroot] [PATCH 2/2] board/andes/ae350: add support for Andes AE350
Date: Tue, 11 Jan 2022 11:58:59 +0800	[thread overview]
Message-ID: <20220111035859.12895-2-peterlin@andestech.com> (raw)
In-Reply-To: <20220111035859.12895-1-peterlin@andestech.com>

This patch provides defconfig and basic support for the Andes
45 series RISC-V architecture.

Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
Signed-off-by: Alan Kao <alankao@andestech.com>
---
 DEVELOPERS                                    |    3 +-
 board/andes/ae350/ae350.dts                   |  274 ++
 board/andes/ae350/boot.cmd                    |    3 +
 board/andes/ae350/genimage_sdcard.cfg         |   29 +
 board/andes/ae350/linux.config.fragment       |    2 +
 .../0001-Add-AE350-platform-defconfig.patch   |  158 +
 ...002-Andes-support-for-Faraday-ATCMAC.patch |  510 +++
 .../0003-Andes-support-for-ATCDMAC.patch      | 3301 +++++++++++++++++
 .../linux/0004-Andes-support-for-FTSDC.patch  | 1884 ++++++++++
 ...5-Non-cacheability-and-Cache-support.patch | 1132 ++++++
 ...-Add-andes-sbi-call-vendor-extension.patch |  231 ++
 ...e-update-function-local_flush_tlb_al.patch |  101 +
 ...rt-time32-stat64-sys_clone3-syscalls.patch |   47 +
 .../0009-dma-Support-smp-up-with-dma.patch    |  120 +
 ...ix-atcdmac300-chained-irq-mapping-is.patch |  300 ++
 .../linux/0011-DMA-Add-msb-bit-patch.patch    |  387 ++
 .../0012-Remove-unused-Andes-SBI-call.patch   |  147 +
 ...isable-PIC-explicitly-for-assembling.patch |   29 +
 ...2-Enable-cache-for-opensbi-jump-mode.patch |   25 +
 ...001-Fix-mmc-no-partition-table-error.patch |   27 +
 ...2-Prevent-fw_dynamic-from-relocation.patch |   27 +
 ...0003-Fix-u-boot-proper-booting-issue.patch |   26 +
 ...04-Enable-printing-OpenSBI-boot-logo.patch |   25 +
 board/andes/ae350/readme.txt                  |   66 +
 board/andes/ae350/uboot.config.fragment       |    5 +
 configs/ae350_andestar45_defconfig            |   46 +
 26 files changed, 8904 insertions(+), 1 deletion(-)
 create mode 100755 board/andes/ae350/ae350.dts
 create mode 100644 board/andes/ae350/boot.cmd
 create mode 100644 board/andes/ae350/genimage_sdcard.cfg
 create mode 100644 board/andes/ae350/linux.config.fragment
 create mode 100644 board/andes/ae350/patches/linux/0001-Add-AE350-platform-defconfig.patch
 create mode 100644 board/andes/ae350/patches/linux/0002-Andes-support-for-Faraday-ATCMAC.patch
 create mode 100644 board/andes/ae350/patches/linux/0003-Andes-support-for-ATCDMAC.patch
 create mode 100644 board/andes/ae350/patches/linux/0004-Andes-support-for-FTSDC.patch
 create mode 100644 board/andes/ae350/patches/linux/0005-Non-cacheability-and-Cache-support.patch
 create mode 100644 board/andes/ae350/patches/linux/0006-Add-andes-sbi-call-vendor-extension.patch
 create mode 100644 board/andes/ae350/patches/linux/0007-riscv-Porting-pte-update-function-local_flush_tlb_al.patch
 create mode 100644 board/andes/ae350/patches/linux/0008-Support-time32-stat64-sys_clone3-syscalls.patch
 create mode 100644 board/andes/ae350/patches/linux/0009-dma-Support-smp-up-with-dma.patch
 create mode 100644 board/andes/ae350/patches/linux/0010-riscv-platform-Fix-atcdmac300-chained-irq-mapping-is.patch
 create mode 100644 board/andes/ae350/patches/linux/0011-DMA-Add-msb-bit-patch.patch
 create mode 100644 board/andes/ae350/patches/linux/0012-Remove-unused-Andes-SBI-call.patch
 create mode 100644 board/andes/ae350/patches/opensbi/0001-Disable-PIC-explicitly-for-assembling.patch
 create mode 100644 board/andes/ae350/patches/opensbi/0002-Enable-cache-for-opensbi-jump-mode.patch
 create mode 100644 board/andes/ae350/patches/uboot/0001-Fix-mmc-no-partition-table-error.patch
 create mode 100644 board/andes/ae350/patches/uboot/0002-Prevent-fw_dynamic-from-relocation.patch
 create mode 100644 board/andes/ae350/patches/uboot/0003-Fix-u-boot-proper-booting-issue.patch
 create mode 100644 board/andes/ae350/patches/uboot/0004-Enable-printing-OpenSBI-boot-logo.patch
 create mode 100644 board/andes/ae350/readme.txt
 create mode 100644 board/andes/ae350/uboot.config.fragment
 create mode 100644 configs/ae350_andestar45_defconfig

diff --git a/DEVELOPERS b/DEVELOPERS
index 12777e8d61..18b0444c72 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -2122,10 +2122,11 @@ N:	Norbert Lange <nolange79@gmail.com>
 F:	package/systemd/
 F:	package/tcf-agent/
 
-N:	Nylon Chen <nylon7@andestech.com>
+N:	Yu Chien Peter Lin <peterlin@andestech.com>
 F:	arch/Config.in.nds32
 F:	board/andes
 F:	configs/andes_ae3xx_defconfig
+F:	configs/ae350_andestar45_defconfig
 F:	toolchain/toolchain-external/toolchain-external-andes-nds32/
 
 N:	Olaf Rempel <razzor@kopf-tisch.de>
diff --git a/board/andes/ae350/ae350.dts b/board/andes/ae350/ae350.dts
new file mode 100755
index 0000000000..fe64234eef
--- /dev/null
+++ b/board/andes/ae350/ae350.dts
@@ -0,0 +1,274 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	compatible = "andestech,ae350";
+	model = "andestech,ax45";
+	aliases {
+		uart0 = &serial0;
+		spi0 = &spi;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7";
+		stdout-path = "uart0:38400n8";
+	};
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		timebase-frequency = <60000000>;
+		CPU0: cpu@0 {
+			device_type = "cpu";
+			reg = <0>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0";
+			riscv,priv-major = <1>;
+			riscv,priv-minor = <10>;
+			mmu-type = "riscv,sv48";
+			clock-frequency = <60000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <256>;
+			i-cache-block-size = <64>;
+			i-cache-line-size = <64>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <128>;
+			d-cache-block-size = <64>;
+			d-cache-line-size = <64>;
+			next-level-cache = <&L2>;
+			CPU0_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+		CPU1: cpu@1 {
+			device_type = "cpu";
+			reg = <1>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0";
+			riscv,priv-major = <1>;
+			riscv,priv-minor = <10>;
+			mmu-type = "riscv,sv48";
+			clock-frequency = <60000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <256>;
+			i-cache-block-size = <64>;
+			i-cache-line-size = <64>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <128>;
+			d-cache-block-size = <64>;
+			d-cache-line-size = <64>;
+			next-level-cache = <&L2>;
+			CPU1_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+		CPU2: cpu@2 {
+			device_type = "cpu";
+			reg = <2>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0";
+			riscv,priv-major = <1>;
+			riscv,priv-minor = <10>;
+			mmu-type = "riscv,sv48";
+			clock-frequency = <60000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <256>;
+			i-cache-block-size = <64>;
+			i-cache-line-size = <64>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <128>;
+			d-cache-block-size = <64>;
+			d-cache-line-size = <64>;
+			next-level-cache = <&L2>;
+			CPU2_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+		CPU3: cpu@3 {
+			device_type = "cpu";
+			reg = <3>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0";
+			riscv,priv-major = <1>;
+			riscv,priv-minor = <10>;
+			mmu-type = "riscv,sv48";
+			clock-frequency = <60000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <256>;
+			i-cache-block-size = <64>;
+			i-cache-line-size = <64>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <128>;
+			d-cache-block-size = <64>;
+			d-cache-line-size = <64>;
+			next-level-cache = <&L2>;
+			CPU3_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+	};
+	L2: l2-cache@e0500000 {
+		compatible = "cache";
+		cache-level = <2>;
+		cache-size = <0x80000>;
+		reg = <0x00000000 0xe0500000 0x00000000 0x00001000>;
+		andes,inst-prefetch = <3>;
+		andes,data-prefetch = <3>;
+		// The value format is <XRAMOCTL XRAMICTL>
+		andes,tag-ram-ctl = <0 0>;
+		andes,data-ram-ctl = <0 0>;
+	};
+	memory@0 {
+		reg = <0x00000000 0x00000000 0x00000000 0x80000000>;
+		device_type = "memory";
+	};
+	soc {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		compatible = "andestech,riscv-ae350-soc", "simple-bus";
+		ranges;
+		plic0: interrupt-controller@e4000000 {
+			compatible = "riscv,plic0";
+			reg = <0x00000000 0xe4000000 0x00000000 0x02000000>;
+			interrupts-extended = < &CPU0_intc 11 &CPU0_intc 9 &CPU1_intc 11 &CPU1_intc 9 &CPU2_intc 11 &CPU2_intc 9 &CPU3_intc 11 &CPU3_intc 9>;
+			interrupt-controller;
+			#address-cells = <2>;
+			#interrupt-cells = <2>;
+			riscv,ndev = <71>;
+		};
+		plic1: interrupt-controller@e6400000 {
+			compatible = "riscv,plic1";
+			reg = <0x00000000 0xe6400000 0x00000000 0x00400000>;
+			interrupts-extended = < &CPU0_intc 3 &CPU1_intc 3 &CPU2_intc 3 &CPU3_intc 3>;
+			interrupt-controller;
+			#address-cells = <2>;
+			#interrupt-cells = <2>;
+			riscv,ndev = <4>;
+		};
+		plmt0: plmt0@e6000000 {
+			compatible = "riscv,plmt0";
+			reg = <0x00000000 0xe6000000 0x00000000 0x00100000>;
+			interrupts-extended = < &CPU0_intc 7 &CPU1_intc 7 &CPU2_intc 7 &CPU3_intc 7>;
+		};
+		spiclk: virt_100mhz {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <100000000>;
+		};
+		timer0: timer@f0400000 {
+			compatible = "andestech,atcpit100";
+			reg = <0x00000000 0xf0400000 0x00000000 0x00001000>;
+			interrupts = <3 4>;
+			interrupt-parent = <&plic0>;
+			clock-frequency = <60000000>;
+		};
+		pwm: pwm@f0400000 {
+			compatible = "andestech,atcpit100-pwm";
+			reg = <0x00000000 0xf0400000 0x00000000 0x00001000>;
+			interrupts = <3 4>;
+			interrupt-parent = <&plic0>;
+			clock-frequency = <60000000>;
+			pwm-cells = <2>;
+		};
+		wdt: wdt@f0500000 {
+			compatible = "andestech,atcwdt200";
+			reg = <0x00000000 0xf0500000 0x00000000 0x00001000>;
+			interrupts = <3 4>;
+			interrupt-parent = <&plic0>;
+			clock-frequency = <15000000>;
+		};
+		serial0: serial@f0300000 {
+			compatible = "andestech,uart16550", "ns16550a";
+			reg = <0x00000000 0xf0300000 0x00000000 0x00001000>;
+			interrupts = <9 4>;
+			interrupt-parent = <&plic0>;
+			clock-frequency = <19660800>;
+			reg-shift = <2>;
+			reg-offset = <32>;
+			no-loopback-test = <1>;
+		};
+		rtc0: rtc@f0600000 {
+			compatible = "andestech,atcrtc100";
+			reg = <0x00000000 0xf0600000 0x00000000 0x00001000>;
+			interrupts = <1 4 2 4>;
+			interrupt-parent = <&plic0>;
+			wakeup-source;
+		};
+		gpio: gpio@f0700000 {
+			compatible = "andestech,atcgpio100";
+			reg = <0x00000000 0xf0700000 0x00000000 0x00001000>;
+			interrupts = <7 4>;
+			interrupt-parent = <&plic0>;
+			wakeup-source;
+		};
+		mac0: mac@e0100000 {
+			compatible = "andestech,atmac100";
+			reg = <0x00000000 0xe0100000 0x00000000 0x00001000>;
+			interrupts = <19 4>;
+			interrupt-parent = <&plic0>;
+		dma-coherent;
+		};
+		smu: smu@f0100000 {
+			compatible = "andestech,atcsmu";
+			reg = <0x00000000 0xf0100000 0x00000000 0x00001000>;
+		};
+		mmc0: mmc@f0e00000 {
+			compatible = "andestech,atfsdc010";
+			reg = <0x00000000 0xf0e00000 0x00000000 0x00001000>;
+			interrupts = <18 4>;
+			interrupt-parent = <&plic0>;
+			clock-freq-min-max = <400000 100000000>;
+			max-frequency = <100000000>;
+			fifo-depth = <16>;
+			cap-sd-highspeed;
+		dma-coherent;
+		};
+		dma0: dma@f0c00000 {
+			compatible = "andestech,atcdmac300";
+			reg = <0x00000000 0xf0c00000 0x00000000 0x00001000>;
+			interrupts = <10 4 64 4 65 4 66 4 67 4 68 4 69 4 70 4 71 4>;
+			interrupt-parent = <&plic0>;
+			dma-channels = <8>;
+		};
+		lcd0: lcd@e0200000 {
+			compatible = "andestech,atflcdc100";
+			reg = <0x00000000 0xe0200000 0x00000000 0x00001000>;
+			interrupts = <20 4>;
+			interrupt-parent = <&plic0>;
+		dma-coherent;
+		};
+		pmu: pmu {
+			compatible = "riscv,andes-pmu";
+			device_type = "pmu";
+		};
+		spi: spi@f0b00000 {
+			compatible = "andestech,atcspi200";
+			reg = <0x00000000 0xf0b00000 0x00000000 0x00001000>;
+			interrupts = <4 4>;
+			interrupt-parent = <&plic0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			num-cs = <1>;
+			clocks = <&spiclk>;
+			flash@0 {
+				compatible = "jedec,spi-nor";
+				reg = <0x00000000>;
+				spi-max-frequency = <50000000>;
+				spi-cpol;
+				spi-cpha;
+			};
+		};
+	};
+};
diff --git a/board/andes/ae350/boot.cmd b/board/andes/ae350/boot.cmd
new file mode 100644
index 0000000000..2a046c6c7a
--- /dev/null
+++ b/board/andes/ae350/boot.cmd
@@ -0,0 +1,3 @@
+setenv bootargs earlycon=sbi root=/dev/mmcblk0p2 rootwait
+load mmc 0:1 0x600000 Image
+booti 0x600000 - $fdtcontroladdr
diff --git a/board/andes/ae350/genimage_sdcard.cfg b/board/andes/ae350/genimage_sdcard.cfg
new file mode 100644
index 0000000000..e8bb3d4903
--- /dev/null
+++ b/board/andes/ae350/genimage_sdcard.cfg
@@ -0,0 +1,29 @@
+image boot.vfat {
+  vfat {
+    files = {
+      "Image",
+      "boot.scr",
+      "u-boot-spl.bin",
+      "u-boot.itb",
+      "ae350.dtb",
+    }
+  }
+  size = 128M
+}
+
+image sdcard.img {
+  hdimage {
+    gpt = true
+  }
+
+  partition u-boot {
+    partition-type-uuid = ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+    bootable = "true"
+    image = "boot.vfat"
+  }
+
+  partition rootfs {
+    partition-type-uuid = 0fc63daf-8483-4772-8e79-3d69d8477de4
+    image = "rootfs.ext4"
+  }
+}
diff --git a/board/andes/ae350/linux.config.fragment b/board/andes/ae350/linux.config.fragment
new file mode 100644
index 0000000000..299b75d2f4
--- /dev/null
+++ b/board/andes/ae350/linux.config.fragment
@@ -0,0 +1,2 @@
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_EFI_PARTITION=y
diff --git a/board/andes/ae350/patches/linux/0001-Add-AE350-platform-defconfig.patch b/board/andes/ae350/patches/linux/0001-Add-AE350-platform-defconfig.patch
new file mode 100644
index 0000000000..1384369972
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0001-Add-AE350-platform-defconfig.patch
@@ -0,0 +1,158 @@
+From 8a9097c1be79fdab3d907a8bbc66a222807cb81a Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 09:05:34 +0800
+Subject: [PATCH 01/12] Add AE350 platform defconfig
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/configs/ae350_rv64_smp_defconfig | 138 ++++++++++++++++++++
+ 1 file changed, 138 insertions(+)
+ create mode 100755 arch/riscv/configs/ae350_rv64_smp_defconfig
+
+diff --git a/arch/riscv/configs/ae350_rv64_smp_defconfig b/arch/riscv/configs/ae350_rv64_smp_defconfig
+new file mode 100755
+index 000000000000..8c6b84b2b9fe
+--- /dev/null
++++ b/arch/riscv/configs/ae350_rv64_smp_defconfig
+@@ -0,0 +1,138 @@
++CONFIG_SMP=y
++CONFIG_NR_CPUS=4
++CONFIG_ANDES_PMU=y
++CONFIG_PREEMPT=y
++CONFIG_HZ_100=y
++CONFIG_CROSS_COMPILE="riscv64-linux-"
++CONFIG_DEFAULT_HOSTNAME="andes-test"
++# CONFIG_SWAP is not set
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=14
++CONFIG_CGROUPS=y
++CONFIG_CGROUP_SCHED=y
++CONFIG_CFS_BANDWIDTH=y
++CONFIG_CGROUP_CPUACCT=y
++CONFIG_NAMESPACES=y
++CONFIG_USER_NS=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_INITRAMFS_SOURCE="rootfs-lite initramfs.txt.lite"
++# CONFIG_RD_BZIP2 is not set
++# CONFIG_RD_LZMA is not set
++# CONFIG_RD_XZ is not set
++# CONFIG_RD_LZO is not set
++# CONFIG_RD_LZ4 is not set
++CONFIG_INITRAMFS_COMPRESSION_GZIP=y
++CONFIG_SYSCTL_SYSCALL=y
++CONFIG_CHECKPOINT_RESTORE=y
++CONFIG_KALLSYMS_ALL=y
++CONFIG_EMBEDDED=y
++CONFIG_PROFILING=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++# CONFIG_BLK_DEV_BSG is not set
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_EFI_PARTITION is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_NET_KEY=y
++CONFIG_INET=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_DIAG is not set
++# CONFIG_WIRELESS is not set
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_NETDEVICES=y
++CONFIG_TUN=y
++CONFIG_FTMAC100=y
++# CONFIG_WLAN is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_KEYBOARD is not set
++# CONFIG_INPUT_MOUSE is not set
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_DEVKMEM=y
++CONFIG_SERIAL_8250=y
++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_OF_PLATFORM=y
++# CONFIG_HW_RANDOM is not set
++CONFIG_GPIOLIB=y
++CONFIG_DEBUG_GPIO=y
++CONFIG_GPIO_SYSFS=y
++CONFIG_GPIO_ATCGPIO100=y
++# CONFIG_HWMON is not set
++CONFIG_I2C=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_ATCIIC100=y
++CONFIG_FB=y
++CONFIG_FB_FTLCDC100=y
++CONFIG_DUMMY_CONSOLE_COLUMNS=40
++CONFIG_DUMMY_CONSOLE_ROWS=30
++CONFIG_FRAMEBUFFER_CONSOLE=y
++CONFIG_LOGO=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_OSSEMUL=y
++CONFIG_SND_PCM_OSS=y
++CONFIG_SND_FTSSP010=y
++# CONFIG_USB_SUPPORT is not set
++CONFIG_MMC=y
++CONFIG_MMC_FTSDC=y
++# CONFIG_IOMMU_SUPPORT is not set
++CONFIG_GENERIC_PHY=y
++CONFIG_EXT2_FS=y
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_POSIX_ACL=y
++CONFIG_EXT4_FS_SECURITY=y
++CONFIG_EXT4_ENCRYPTION=y
++CONFIG_FANOTIFY=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_POSIX_ACL=y
++CONFIG_CONFIGFS_FS=y
++# CONFIG_MISC_FILESYSTEMS is not set
++CONFIG_NFS_FS=y
++CONFIG_NFS_V3_ACL=y
++CONFIG_NFS_V4=y
++CONFIG_NFS_V4_1=y
++CONFIG_NFS_V4_2=y
++CONFIG_NFS_USE_LEGACY_DNS=y
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ISO8859_1=y
++CONFIG_DEBUG_INFO=y
++CONFIG_DEBUG_INFO_DWARF4=y
++CONFIG_GDB_SCRIPTS=y
++CONFIG_READABLE_ASM=y
++CONFIG_DEBUG_FS=y
++CONFIG_HEADERS_CHECK=y
++CONFIG_DEBUG_SECTION_MISMATCH=y
++CONFIG_PANIC_ON_OOPS=y
++# CONFIG_DEBUG_PREEMPT is not set
++CONFIG_STACKTRACE=y
++CONFIG_RCU_CPU_STALL_TIMEOUT=300
++# CONFIG_FTRACE is not set
++CONFIG_CRYPTO_ECHAINIV=y
++# CONFIG_CRYPTO_HW is not set
++CONFIG_SOC_SIFIVE=y
++CONFIG_SERIAL_SIFIVE=y
++CONFIG_SERIAL_SIFIVE_CONSOLE=y
++CONFIG_CLK_SIFIVE=y
++CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC=y
++CONFIG_CLK_SIFIVE_FU540_PRCI=y
++CONFIG_SIFIVE_PLIC=y
++CONFIG_PRINTK_TIME=y
++CONFIG_RISCV_BASE_PMU=n
++CONFIG_PERF_EVENTS=n
++CONFIG_MODULES_TREE_LOOKUP=n
++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
+\ No newline at end of file
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0002-Andes-support-for-Faraday-ATCMAC.patch b/board/andes/ae350/patches/linux/0002-Andes-support-for-Faraday-ATCMAC.patch
new file mode 100644
index 0000000000..772700ba55
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0002-Andes-support-for-Faraday-ATCMAC.patch
@@ -0,0 +1,510 @@
+From 1966040a640c5629cd8a437111072e092caad205 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 09:06:19 +0800
+Subject: [PATCH 02/12] Andes support for Faraday ATCMAC
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ drivers/net/ethernet/faraday/Kconfig    |  13 +-
+ drivers/net/ethernet/faraday/ftmac100.c | 154 ++++++++++++++----------
+ drivers/net/ethernet/faraday/ftmac100.h |  28 ++++-
+ 3 files changed, 121 insertions(+), 74 deletions(-)
+
+diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig
+index 3d1e9a302148..d3ed03cc984d 100644
+--- a/drivers/net/ethernet/faraday/Kconfig
++++ b/drivers/net/ethernet/faraday/Kconfig
+@@ -6,7 +6,7 @@
+ config NET_VENDOR_FARADAY
+	bool "Faraday devices"
+	default y
+-	depends on ARM || NDS32 || COMPILE_TEST
++	depends on ARM || RISCV || COMPILE_TEST
+	help
+	  If you have a network (Ethernet) card belonging to this class, say Y.
+
+@@ -19,24 +19,21 @@ if NET_VENDOR_FARADAY
+
+ config FTMAC100
+	tristate "Faraday FTMAC100 10/100 Ethernet support"
+-	depends on ARM || NDS32 || COMPILE_TEST
+-	depends on !64BIT || BROKEN
++	depends on ARM || RISCV || COMPILE_TEST
+	select MII
+	help
+	  This driver supports the FTMAC100 10/100 Ethernet controller
+	  from Faraday. It is used on Faraday A320, Andes AG101 and some
+-	  other ARM/NDS32 SoC's.
++	  other ARM/RISCV SoC's.
+
+ config FTGMAC100
+	tristate "Faraday FTGMAC100 Gigabit Ethernet support"
+-	depends on ARM || NDS32 || COMPILE_TEST
+-	depends on !64BIT || BROKEN
++	depends on ARM || RISCV || COMPILE_TEST
+	select PHYLIB
+	select MDIO_ASPEED if MACH_ASPEED_G6
+-	select CRC32
+	help
+	  This driver supports the FTGMAC100 Gigabit Ethernet controller
+	  from Faraday. It is used on Faraday A369, Andes AG102 and some
+-	  other ARM/NDS32 SoC's.
++	  other ARM/RISCV SoC's.
+
+ endif # NET_VENDOR_FARADAY
+diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c
+index 473b337b2e3b..49a9d4ea5826 100644
+--- a/drivers/net/ethernet/faraday/ftmac100.c
++++ b/drivers/net/ethernet/faraday/ftmac100.c
+@@ -4,6 +4,20 @@
+  *
+  * (C) Copyright 2009-2011 Faraday Technology
+  * Po-Yu Chuang <ratbert@faraday-tech.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+
+ #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+@@ -38,12 +52,25 @@
+ #error invalid RX_BUF_SIZE
+ #endif
+
++#define xprintk(...)
++
++#define FTMAC100_RX_DESC(priv, index)     (&priv->descs->rxdes[index])
++#define FTMAC100_RX_DESC_EXT(priv, index) (&priv->descs->rxdes_ext[index])
++#define FTMAC100_TX_DESC(priv, index)     (&priv->descs->txdes[index])
++#define FTMAC100_TX_DESC_EXT(priv, index) (&priv->descs->txdes_ext[index])
++
++#define FTMAC100_CURRENT_RX_DESC_INDEX(priv) (priv->rx_pointer)
++#define FTMAC100_CURRENT_TX_DESC_INDEX(priv) (priv->tx_pointer);
++#define FTMAC100_CURRENT_CLEAN_TX_DESC_INDEX(priv) (priv->tx_clean_pointer);
++
+ /******************************************************************************
+  * private data
+  *****************************************************************************/
+ struct ftmac100_descs {
+	struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+	struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES];
++	struct ftmac100_rxdes_ext rxdes_ext[RX_QUEUE_ENTRIES];
++	struct ftmac100_txdes_ext txdes_ext[TX_QUEUE_ENTRIES];
+ };
+
+ struct ftmac100 {
+@@ -69,7 +96,7 @@ struct ftmac100 {
+ };
+
+ static int ftmac100_alloc_rx_page(struct ftmac100 *priv,
+-				  struct ftmac100_rxdes *rxdes, gfp_t gfp);
++				  int index, gfp_t gfp);
+
+ /******************************************************************************
+  * internal functions (hardware register access)
+@@ -96,11 +123,13 @@ static void ftmac100_disable_all_int(struct ftmac100 *priv)
+
+ static void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
+ {
++	xprintk("%s: addr %p\n", __func__, (void*)addr);
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
+ }
+
+ static void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
+ {
++	xprintk("%s: addr %p\n", __func__, (void*)addr);
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
+ }
+
+@@ -259,25 +288,22 @@ static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
+					dma_addr_t addr)
+ {
+	rxdes->rxdes2 = cpu_to_le32(addr);
++	rxdes->rxdes3 = cpu_to_le32(addr >> 32);
+ }
+
+ static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes)
+ {
+-	return le32_to_cpu(rxdes->rxdes2);
++	return le32_to_cpu(rxdes->rxdes2) | (dma_addr_t)le32_to_cpu(rxdes->rxdes3) << 32;
+ }
+
+-/*
+- * rxdes3 is not used by hardware. We use it to keep track of page.
+- * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+- */
+-static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page)
++static void ftmac100_rxdes_set_page(struct ftmac100 *priv, int index, struct page *page)
+ {
+-	rxdes->rxdes3 = (unsigned int)page;
++	FTMAC100_RX_DESC_EXT(priv, index)->page = page;
+ }
+
+-static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes)
++static struct page *ftmac100_rxdes_get_page(struct ftmac100 *priv, int index)
+ {
+-	return (struct page *)rxdes->rxdes3;
++	return (struct page *)FTMAC100_RX_DESC_EXT(priv, index)->page;
+ }
+
+ /******************************************************************************
+@@ -293,26 +319,23 @@ static void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
+	priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
+ }
+
+-static struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv)
+-{
+-	return &priv->descs->rxdes[priv->rx_pointer];
+-}
+-
+-static struct ftmac100_rxdes *
++static int
+ ftmac100_rx_locate_first_segment(struct ftmac100 *priv)
+ {
+-	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
++	int index = FTMAC100_CURRENT_RX_DESC_INDEX(priv);
++	struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, index);
+
+	while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
+		if (ftmac100_rxdes_first_segment(rxdes))
+-			return rxdes;
++			return index;
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+-		rxdes = ftmac100_current_rxdes(priv);
++		index = FTMAC100_CURRENT_RX_DESC_INDEX(priv);
++		rxdes = FTMAC100_RX_DESC(priv, index);
+	}
+
+-	return NULL;
++	return -1;
+ }
+
+ static bool ftmac100_rx_packet_error(struct ftmac100 *priv,
+@@ -363,9 +386,13 @@ static bool ftmac100_rx_packet_error(struct ftmac100 *priv,
+ static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+ {
+	struct net_device *netdev = priv->netdev;
+-	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
++	struct ftmac100_rxdes *rxdes;
++	int index;
+	bool done = false;
+
++	index = FTMAC100_CURRENT_RX_DESC_INDEX(priv);
++	rxdes = FTMAC100_RX_DESC(priv, index);
++
+	if (net_ratelimit())
+		netdev_dbg(netdev, "drop packet %p\n", rxdes);
+
+@@ -375,7 +402,8 @@ static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+-		rxdes = ftmac100_current_rxdes(priv);
++		index = FTMAC100_CURRENT_RX_DESC_INDEX(priv);
++		rxdes = FTMAC100_RX_DESC(priv, index);
+	} while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
+
+	netdev->stats.rx_dropped++;
+@@ -389,11 +417,12 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+	struct page *page;
+	dma_addr_t map;
+	int length;
+-	bool ret;
++	int index;
+
+-	rxdes = ftmac100_rx_locate_first_segment(priv);
+-	if (!rxdes)
++	index = ftmac100_rx_locate_first_segment(priv);
++	if (index < 0)
+		return false;
++	rxdes = FTMAC100_RX_DESC(priv, index);
+
+	if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
+		ftmac100_rx_drop_packet(priv);
+@@ -404,8 +433,8 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+	 * It is impossible to get multi-segment packets
+	 * because we always provide big enough receive buffers.
+	 */
+-	ret = ftmac100_rxdes_last_segment(rxdes);
+-	BUG_ON(!ret);
++	if (unlikely(!ftmac100_rxdes_last_segment(rxdes)))
++		BUG();
+
+	/* start processing */
+	skb = netdev_alloc_skb_ip_align(netdev, 128);
+@@ -424,7 +453,7 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+	dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+	length = ftmac100_rxdes_frame_length(rxdes);
+-	page = ftmac100_rxdes_get_page(rxdes);
++	page = ftmac100_rxdes_get_page(priv, index);
+	skb_fill_page_desc(skb, 0, page, 0, length);
+	skb->len += length;
+	skb->data_len += length;
+@@ -437,7 +466,7 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+		/* Small frames are copied into linear part to free one page */
+		__pskb_pull_tail(skb, length);
+	}
+-	ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
++	ftmac100_alloc_rx_page(priv, index, GFP_ATOMIC);
+
+	ftmac100_rx_pointer_advance(priv);
+
+@@ -520,25 +549,27 @@ static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
+					dma_addr_t addr)
+ {
+	txdes->txdes2 = cpu_to_le32(addr);
++	txdes->txdes3 = cpu_to_le32(addr >> 32);
+ }
+
+ static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes)
+ {
+-	return le32_to_cpu(txdes->txdes2);
++	return le32_to_cpu(txdes->txdes2) | (dma_addr_t)le32_to_cpu(txdes->txdes3) << 32;
+ }
+
+-/*
+- * txdes3 is not used by hardware. We use it to keep track of socket buffer.
+- * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+- */
+-static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb)
++static void ftmac100_txdes_skb_reset(struct ftmac100_txdes *txdes)
+ {
+-	txdes->txdes3 = (unsigned int)skb;
++	txdes->txdes3 = 0;
+ }
+
+-static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes)
++static void ftmac100_txdes_set_skb(struct ftmac100 *priv, int index, struct sk_buff *skb)
+ {
+-	return (struct sk_buff *)txdes->txdes3;
++	FTMAC100_TX_DESC_EXT(priv, index)->skb = skb;
++}
++
++static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100 *priv, int index)
++{
++	return (struct sk_buff *)FTMAC100_TX_DESC_EXT(priv, index)->skb;
+ }
+
+ /******************************************************************************
+@@ -559,32 +590,24 @@ static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
+	priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer);
+ }
+
+-static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv)
+-{
+-	return &priv->descs->txdes[priv->tx_pointer];
+-}
+-
+-static struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv)
+-{
+-	return &priv->descs->txdes[priv->tx_clean_pointer];
+-}
+-
+ static bool ftmac100_tx_complete_packet(struct ftmac100 *priv)
+ {
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_txdes *txdes;
+	struct sk_buff *skb;
+	dma_addr_t map;
++	int index;
+
+	if (priv->tx_pending == 0)
+		return false;
+
+-	txdes = ftmac100_current_clean_txdes(priv);
++	index = FTMAC100_CURRENT_CLEAN_TX_DESC_INDEX(priv);
++	txdes = FTMAC100_TX_DESC(priv, index);
+
+	if (ftmac100_txdes_owned_by_dma(txdes))
+		return false;
+
+-	skb = ftmac100_txdes_get_skb(txdes);
++	skb = ftmac100_txdes_get_skb(priv, index);
+	map = ftmac100_txdes_get_dma_addr(txdes);
+
+	if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
+@@ -603,6 +626,7 @@ static bool ftmac100_tx_complete_packet(struct ftmac100 *priv)
+	dev_kfree_skb(skb);
+
+	ftmac100_txdes_reset(txdes);
++	ftmac100_txdes_skb_reset(txdes);
+
+	ftmac100_tx_clean_pointer_advance(priv);
+
+@@ -620,18 +644,20 @@ static void ftmac100_tx_complete(struct ftmac100 *priv)
+		;
+ }
+
+-static netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
++static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+				 dma_addr_t map)
+ {
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_txdes *txdes;
+	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
++	int index;
+
+-	txdes = ftmac100_current_txdes(priv);
++	index = FTMAC100_CURRENT_TX_DESC_INDEX(priv);
++	txdes = FTMAC100_TX_DESC(priv, index);
+	ftmac100_tx_pointer_advance(priv);
+
+	/* setup TX descriptor */
+-	ftmac100_txdes_set_skb(txdes, skb);
++	ftmac100_txdes_set_skb(priv, index, skb);
+	ftmac100_txdes_set_dma_addr(txdes, map);
+
+	ftmac100_txdes_set_first_segment(txdes);
+@@ -656,9 +682,10 @@ static netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+  * internal functions (buffer)
+  *****************************************************************************/
+ static int ftmac100_alloc_rx_page(struct ftmac100 *priv,
+-				  struct ftmac100_rxdes *rxdes, gfp_t gfp)
++				  int index, gfp_t gfp)
+ {
+	struct net_device *netdev = priv->netdev;
++	struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, index);
+	struct page *page;
+	dma_addr_t map;
+
+@@ -677,7 +704,7 @@ static int ftmac100_alloc_rx_page(struct ftmac100 *priv,
+		return -ENOMEM;
+	}
+
+-	ftmac100_rxdes_set_page(rxdes, page);
++	ftmac100_rxdes_set_page(priv, index, page);
+	ftmac100_rxdes_set_dma_addr(rxdes, map);
+	ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
+	ftmac100_rxdes_set_dma_own(rxdes);
+@@ -689,8 +716,8 @@ static void ftmac100_free_buffers(struct ftmac100 *priv)
+	int i;
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+-		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+-		struct page *page = ftmac100_rxdes_get_page(rxdes);
++		struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, i);
++		struct page *page = ftmac100_rxdes_get_page(priv, i);
+		dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes);
+
+		if (!page)
+@@ -701,8 +728,8 @@ static void ftmac100_free_buffers(struct ftmac100 *priv)
+	}
+
+	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+-		struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
+-		struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
++		struct ftmac100_txdes *txdes = FTMAC100_TX_DESC(priv, i);
++		struct sk_buff *skb = ftmac100_txdes_get_skb(priv, i);
+		dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes);
+
+		if (!skb)
+@@ -722,7 +749,8 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+
+	priv->descs = dma_alloc_coherent(priv->dev,
+					 sizeof(struct ftmac100_descs),
+-					 &priv->descs_dma_addr, GFP_KERNEL);
++					 &priv->descs_dma_addr,
++					 GFP_KERNEL);
+	if (!priv->descs)
+		return -ENOMEM;
+
+@@ -730,9 +758,7 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+	ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+-		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+-
+-		if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
++		if (ftmac100_alloc_rx_page(priv, i, GFP_KERNEL))
+			goto err;
+	}
+
+@@ -999,7 +1025,7 @@ static int ftmac100_stop(struct net_device *netdev)
+	return 0;
+ }
+
+-static netdev_tx_t
++static int
+ ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+ {
+	struct ftmac100 *priv = netdev_priv(netdev);
+diff --git a/drivers/net/ethernet/faraday/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h
+index fe986f1673fc..1e65a7ef27ba 100644
+--- a/drivers/net/ethernet/faraday/ftmac100.h
++++ b/drivers/net/ethernet/faraday/ftmac100.h
+@@ -4,6 +4,20 @@
+  *
+  * (C) Copyright 2009-2011 Faraday Technology
+  * Po-Yu Chuang <ratbert@faraday-tech.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+
+ #ifndef __FTMAC100_H
+@@ -22,6 +36,8 @@
+ #define	FTMAC100_OFFSET_ITC		0x28
+ #define	FTMAC100_OFFSET_APTC		0x2c
+ #define	FTMAC100_OFFSET_DBLAC		0x30
++#define	FTMAC100_OFFSET_TXR_BADR_H	0x40
++#define	FTMAC100_OFFSET_RXR_BADR_H	0x44
+ #define	FTMAC100_OFFSET_MACCR		0x88
+ #define	FTMAC100_OFFSET_MACSR		0x8c
+ #define	FTMAC100_OFFSET_PHYCR		0x90
+@@ -125,7 +141,7 @@ struct ftmac100_txdes {
+	unsigned int	txdes0;
+	unsigned int	txdes1;
+	unsigned int	txdes2;	/* TXBUF_BADR */
+-	unsigned int	txdes3;	/* not used by HW */
++	unsigned int	txdes3;	/* TXBUF_BADR_H */
+ } __attribute__ ((aligned(16)));
+
+ #define	FTMAC100_TXDES0_TXPKT_LATECOL	(1 << 0)
+@@ -146,7 +162,7 @@ struct ftmac100_rxdes {
+	unsigned int	rxdes0;
+	unsigned int	rxdes1;
+	unsigned int	rxdes2;	/* RXBUF_BADR */
+-	unsigned int	rxdes3;	/* not used by HW */
++	unsigned int	rxdes3;	/* RXBUF_BADR_H */
+ } __attribute__ ((aligned(16)));
+
+ #define	FTMAC100_RXDES0_RFL		0x7ff
+@@ -164,4 +180,12 @@ struct ftmac100_rxdes {
+ #define	FTMAC100_RXDES1_RXBUF_SIZE(x)	((x) & 0x7ff)
+ #define	FTMAC100_RXDES1_EDORR		(1 << 31)
+
++struct ftmac100_txdes_ext {
++	void *skb;
++};
++
++struct ftmac100_rxdes_ext {
++	void *page;
++};
++
+ #endif /* __FTMAC100_H */
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0003-Andes-support-for-ATCDMAC.patch b/board/andes/ae350/patches/linux/0003-Andes-support-for-ATCDMAC.patch
new file mode 100644
index 0000000000..eb50c1f02d
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0003-Andes-support-for-ATCDMAC.patch
@@ -0,0 +1,3301 @@
+From ddc5b4035397fcdc91afc3a008b6632fa82e3715 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 10:55:06 +0800
+Subject: [PATCH 03/12] Andes support for ATCDMAC
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/include/asm/atcdmac300.h     |  543 +++++
+ arch/riscv/include/asm/dmad.h           |   71 +
+ arch/riscv/platforms/Kconfig            |   21 +
+ arch/riscv/platforms/Makefile           |    2 +
+ arch/riscv/platforms/ae350/Kconfig      |    7 +
+ arch/riscv/platforms/ae350/Makefile     |    1 +
+ arch/riscv/platforms/ae350/atcdmac300.c | 2531 +++++++++++++++++++++++
+ arch/riscv/platforms/dmad_intc.c        |   49 +
+ 8 files changed, 3225 insertions(+)
+ create mode 100644 arch/riscv/include/asm/atcdmac300.h
+ create mode 100644 arch/riscv/include/asm/dmad.h
+ create mode 100644 arch/riscv/platforms/Kconfig
+ create mode 100644 arch/riscv/platforms/Makefile
+ create mode 100644 arch/riscv/platforms/ae350/Kconfig
+ create mode 100644 arch/riscv/platforms/ae350/Makefile
+ create mode 100644 arch/riscv/platforms/ae350/atcdmac300.c
+ create mode 100644 arch/riscv/platforms/dmad_intc.c
+
+diff --git a/arch/riscv/include/asm/atcdmac300.h b/arch/riscv/include/asm/atcdmac300.h
+new file mode 100644
+index 000000000000..20fe88212dfc
+--- /dev/null
++++ b/arch/riscv/include/asm/atcdmac300.h
+@@ -0,0 +1,543 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2018 Andes Technology Corporation
++ *
++ */
++
++#ifndef __NDS_DMAD_ATF_INC__
++#define __NDS_DMAD_ATF_INC__
++
++/*****************************************************************************
++ * Configuration section
++*****************************************************************************/
++/* Debug trace enable switch */
++#define DMAD_ERROR_TRACE            1       /* message for fatal errors */
++#define DMAD_DEBUG_TRACE            0       /* message for debug trace */
++typedef u32 addr_t;
++
++/* Device base address */
++
++extern resource_size_t	dmac_base;
++#define DMAC_BASE			(dmac_base)
++
++/* ID and Revision Register */
++#define ID_REV				(DMAC_BASE + 0x00)
++/* DMAC Configuration Register*/
++#define CFG				(DMAC_BASE + 0x10)
++#define REQSYNC				30
++#define CTL				(DMAC_BASE + 0x20)
++#define CH_ABT				(DMAC_BASE + 0x24)
++/* Interrupt Status Register */
++#define INT_STA				(DMAC_BASE + 0x30)
++#define TC_OFFSET			16
++#define ABT_OFFSET			8
++#define ERR_OFFSET			0
++
++
++#define CH_EN				(DMAC_BASE + 0x34)
++
++
++
++#define DMAC_CH_OFFSET			0x40
++#define CH_CTL_OFF			0x0
++#define CH_SIZE_OFF			0x4
++#define CH_SRC_LOW_OFF			0x8
++#define CH_SRC_HIGH_OFF			0xc
++#define CH_DST_LOW_OFF			0x10
++#define CH_DST_HIGH_OFF			0x14
++#define CH_LLP_LOW_OFF			0x18
++#define CH_LLP_HIGH_OFF			0x1c
++
++
++#define DMAC_C0_BASE			(DMAC_BASE + DMAC_CH_OFFSET)
++#define DMAC_MAX_CHANNELS		8
++#define DMAC_BASE_CH(n)			(DMAC_C0_BASE + n*0x20)
++
++/***** Channel n Control Register ******/
++#define CH_CTL(n)			DMAC_BASE_CH(n)+CH_CTL_OFF
++#define PRIORITY_SHIFT			29
++#define PRIORITY_LOW			0
++#define PRIORITY_HIGH			1
++#define DMAC_CSR_CHPRI_0		PRIORITY_LOW
++#define DMAC_CSR_CHPRI_1		PRIORITY_LOW
++#define DMAC_CSR_CHPRI_2		PRIORITY_HIGH
++#define DMAC_CSR_CHPRI_3		PRIORITY_HIGH
++
++
++#define SBURST_SIZE_SHIFT		24
++#define SBURST_SIZE_MASK		(0xf<<24)
++#define DMAC_CSR_SIZE_1			0x0
++#define DMAC_CSR_SIZE_2			0x1
++#define DMAC_CSR_SIZE_4			0x2
++#define DMAC_CSR_SIZE_8			0x3
++#define DMAC_CSR_SIZE_16		0x4
++#define DMAC_CSR_SIZE_32		0x5
++#define DMAC_CSR_SIZE_64		0x6
++#define DMAC_CSR_SIZE_128		0x7
++#define DMAC_CSR_SIZE_256		0x8
++#define DMAC_CSR_SIZE_512		0x9
++#define DMAC_CSR_SIZE_1024		0xa
++/* Source transfer width */
++#define SRCWIDTH			21
++#define SRCWIDTH_MASK			(0x7<<SRCWIDTH)
++#define WIDTH_1				0x0
++#define WIDTH_2				0x1
++#define WIDTH_4				0x2
++#define WIDTH_8				0x3
++#define WIDTH_16			0x4
++#define WIDTH_32			0x5
++#define DMAC_CSR_WIDTH_8	      WIDTH_1
++#define DMAC_CSR_WIDTH_16	      WIDTH_2
++#define DMAC_CSR_WIDTH_32	      WIDTH_4
++
++
++/* Destination transfer width */
++#define DSTWIDTH			18
++#define DSTWIDTH_MASK			(0x7<<DSTWIDTH)
++/* Source DMA handshake mode */
++#define SRCMODE				17
++#define HANDSHAKE			1
++#define SRC_HS				(HANDSHAKE<<SRCMODE)
++/* Destination DMA handshake mode */
++#define DSTMODE				16
++#define DST_HS				(HANDSHAKE<<DSTMODE)
++
++/* Source address control */
++#define SRCADDRCTRL			14
++#define SRCADDRCTRL_MASK		(0x3<<SRCADDRCTRL)
++#define ADDR_INC			0x0
++#define ADDR_DEC			0x1
++#define ADDR_FIX			0x2
++#define DMAC_CSR_AD_INC			ADDR_INC
++#define DMAC_CSR_AD_DEC			ADDR_DEC
++#define DMAC_CSR_AD_FIX			ADDR_FIX
++
++/* Destination address control */
++#define DSTADDRCTRL			12
++#define DSTADDRCTRL_MASK		(0x3<<DSTADDRCTRL)
++/* Source DMA request select */
++#define SRCREQSEL			8
++#define SRCREQSEL_MASK			(0xf<<SRCREQSEL)
++/* Destination DMA request select */
++#define DSTREQSEL			4
++#define DSTREQSEL_MASK			(0xf<<DSTREQSEL)
++
++
++/* Channel abort interrupt mask */
++#define INTABTMASK			(3)
++/* Channel error interrupt mask */
++#define INTERRMASK			(2)
++/* Channel terminal count interrupt mask */
++#define INTTCMASK			(1)
++/* Channel Enable */
++#define CHEN				(0)
++
++/***** Channel n Transfer Size Register ******/
++#define CH_SIZE(n)			DMAC_BASE_CH(n)+CH_SIZE_OFF
++/* total transfer size from source */
++#define DMAC_TOT_SIZE_MASK          0xffffffff
++
++
++
++
++#define CH_SRC_L(n)			DMAC_BASE_CH(n)+CH_SRC_LOW_OFF
++#define CH_SRC_H(n)			DMAC_BASE_CH(n)+CH_SRC_HIGH_OFF
++#define CH_DST_L(n)			DMAC_BASE_CH(n)+CH_DST_LOW_OFF
++#define CH_DST_H(n)			DMAC_BASE_CH(n)+CH_DST_HIGH_OFF
++#define CH_LLP_L(n)			DMAC_BASE_CH(n)+CH_LLP_LOW_OFF
++#define CH_LLP_H(n)			DMAC_BASE_CH(n)+CH_LLP_HIGH_OFF
++
++
++typedef struct channel_control
++{
++	u32	sWidth;
++	u32	sCtrl;
++	u32	sReqn;
++	u32	dWidth;
++	u32	dCtrl;
++	u32	dReqn;
++}channel_control;
++
++
++
++
++
++
++/* DMA channel 0 registers (32-bit width) */
++#define DMAC_C0_CSR                 (DMAC_C0_BASE + DMAC_CSR_OFFSET)
++#define DMAC_C0_CFG                 (DMAC_C0_BASE + DMAC_CFG_OFFSET)
++#define DMAC_C0_SRC_ADDR            (DMAC_C0_BASE + DMAC_SRC_ADDR_OFFSET)
++#define DMAC_C0_DST_ADDR            (DMAC_C0_BASE + DMAC_DST_ADDR_OFFSET)
++#define DMAC_C0_LLP                 (DMAC_C0_BASE + DMAC_LLP_OFFSET)
++#define DMAC_C0_SIZE                (DMAC_C0_BASE + DMAC_SIZE_OFFSET)
++
++
++#ifdef CONFIG_PLATFORM_AHBDMA
++#define DMAC_CYCLE_TO_BYTES(cycle, width) ((cycle) << (width))
++#define DMAC_BYTES_TO_CYCLE(bytes, width) ((bytes) >> (width))
++#else
++#define DMAC_CYCLE_TO_BYTES(cycle, width) 0
++#define DMAC_BYTES_TO_CYCLE(bytes, width) 0
++#endif  /* CONFIG_PLATFORM_AHBDMA */
++
++
++/* Assignment of DMA hardware handshake ID */
++#define DMAC_REQN_SPITX			0
++#define DMAC_REQN_SPIRX			1
++#ifdef CONFIG_PLAT_AE350
++#define DMAC_REQN_I2SAC97TX		14
++#define DMAC_REQN_I2SAC97RX		15
++#else
++
++#define DMAC_REQN_I2SAC97TX		2
++#define DMAC_REQN_I2SAC97RX		3
++#endif
++#define DMAC_REQN_UART1TX		4
++#define DMAC_REQN_UART1RX		5
++#define DMAC_REQN_UART2TX		6
++#define DMAC_REQN_UART2RX		7
++#define DMAC_REQN_I2C			8
++#define DMAC_REQN_SDC			9
++#define DMAC_REQN_NONE			15
++
++
++enum DMAD_DMAC_CORE {
++	DMAD_DMAC_AHB_CORE,
++	DMAD_DMAC_APB_CORE
++};
++
++enum DMAD_CHREG_FLAGS {
++	DMAD_FLAGS_NON_BLOCK    = 0x00000000,
++	DMAD_FLAGS_SLEEP_BLOCK  = 0x00000001,
++	DMAD_FLAGS_SPIN_BLOCK   = 0x00000002,
++	DMAD_FLAGS_RING_MODE    = 0x00000008,   /* ring submission mode */
++	DMAD_FLAGS_BIDIRECTION  = 0x00000010,   /* indicates both tx and rx */
++};
++
++enum DMAD_CHDIR
++{
++	DMAD_DIR_A0_TO_A1       = 0,
++	DMAD_DIR_A1_TO_A0       = 1,
++};
++
++/* AHB Channel Request
++ *
++ * Notes for developers:
++ *   These should be channel-only properties. Controller-specific properties
++ *   should be separated as other driver structure or driver buildin-hardcode.
++ *   If controller properties are embeded in this union, request for a channel
++ *   may unexpectedly override the controller setting of the request of other
++ *   channels.
++ */
++typedef struct dmad_ahb_chreq
++{
++	/* channel property */
++	u32 sync;                       /* (in)  different clock domain */
++	u32 priority;                   /* (in)  DMAC_CSR_CHPRI_xxx */
++	u32 hw_handshake;               /* (in)  hardware handshaking on/off */
++	u32 burst_size;                 /* (in)  DMAC_CSR_SIZE_xxx */
++
++	/* source property */
++	union {
++		u32 src_width;          /* (in)  DMAC_CSR_WIDTH_xxx */
++		u32 addr0_width;        /* (in)  bi-direction mode alias */
++		u32 ring_width;         /* (in)  ring-mode alias */
++	};
++	union {
++		u32 src_ctrl;           /* (in)  DMAC_CSR_AD_xxx */
++		u32 addr0_ctrl;         /* (in)  bi-direction mode alias */
++		u32 ring_ctrl;          /* (in)  ring-mode alias */
++	};
++	union {
++		u32 src_reqn;           /* (in)  DMAC_REQN_xxx */
++		u32 addr0_reqn;         /* (in)  bi-direction mode alias */
++		u32 ring_reqn;          /* (in)  ring-mode alias */
++	};
++
++	/* destination property */
++	union {
++		u32 dst_width;          /* (in)  DMAC_CSR_WIDTH_xxx */
++		u32 addr1_width;        /* (in)  bi-direction mode alias */
++		u32 dev_width;          /* (in)  ring-mode alias */
++	};
++	union {
++		u32 dst_ctrl;           /* (in)  DMAC_CSR_AD_xxx */
++		u32 addr1_ctrl;         /* (in)  bi-direction mode alias */
++		u32 dev_ctrl;           /* (in)  ring-mode alias */
++	};
++	union {
++		u32 dst_reqn;           /* (in)  DMAC_REQN_xxx */
++		u32 addr1_reqn;         /* (in)  bi-direction mode alias */
++		u32 dev_reqn;           /* (in)  ring-mode alias */
++	};
++
++	/* (in)  transfer direction, valid only if following flags were set ...
++	 *         DMAD_FLAGS_BIDIRECTION or
++	 *         DMAD_FLAGS_RING_MODE
++	 *       value:
++	 *         0 (addr0 -> addr1, or ring-buff to device)
++	 *         1 (addr0 <- addr1, or device to ring-buff)
++	 */
++	u32 tx_dir;
++
++} dmad_ahb_chreq;
++
++/* APB Channel Request
++ *
++ * Notes for developers:
++ *   These should be channel-only properties. Controller-specific properties
++ *   should be separated as other driver structure or driver buildin-hardcode.
++ *   If controller properties are embeded in this union, request for a channel
++ *   may unexpectedly override the controller setting of the request of other
++ *   channels.
++ */
++typedef struct dmad_apb_chreq
++{
++	/* controller property (removed! should not exist in this struct) */
++
++	/* channel property */
++	u32 burst_mode;                 /* (in)  Burst mode (0/1) */
++	u32 data_width;                 /* (in)  APBBR_DATAWIDTH_xxx */
++
++	/* source property */
++	union {
++		u32 src_ctrl;           /* (in)  APBBR_ADDRINC_xxx */
++		u32 addr0_ctrl;         /* (in)  bi-direction mode alias */
++		u32 ring_ctrl;          /* (in)  ring-mode alias */
++	};
++	union {
++		u32 src_reqn;           /* (in)  APBBR_REQN_xxx */
++		u32 addr0_reqn;         /* (in)  bi-direction mode alias */
++		u32 ring_reqn;          /* (in)  ring-mode alias */
++	};
++
++	/* destination property */
++	union {
++		u32 dst_ctrl;           /* (in)  APBBR_ADDRINC_xxx */
++		u32 addr1_ctrl;         /* (in)  bi-direction mode alias */
++		u32 dev_ctrl;           /* (in)  ring-mode alias */
++	};
++	union {
++		u32 dst_reqn;           /* (in)  APBBR_REQN_xxx */
++		u32 addr1_reqn;         /* (in)  bi-direction mode alias */
++		u32 dev_reqn;           /* (in)  ring-mode alias */
++	};
++
++	/* (in)  transfer direction, valid only if following flags were set ...
++	 *         DMAD_FLAGS_BIDIRECTION or
++	 *         DMAD_FLAGS_RING_MODE
++	 *       value:
++	 *         0 (addr0 -> addr1, or ring-buff to device)
++	 *         1 (addr0 <- addr1, or device to ring-buff)
++	 */
++	u32 tx_dir;
++
++} dmad_apb_chreq;
++
++/* Channel Request Descriptor */
++typedef struct dmad_chreq
++{
++	/* common fields */
++	u32     controller;                 /* (in)  enum DMAD_DMAC_CORE */
++	u32	flags;                      /* (in)  enum DMAD_CHREQ_FLAGS */
++
++	/**********************************************************************
++	 * ring mode specific fields (valid only for DMAD_FLAGS_RING_MODE)
++	 * note:
++	 *  - size fields are in unit of data width
++	 *    * for AHB, ring size is limited to 4K * data_width of data if
++	 *      hw-LLP is not used
++	 *    * for AHB, ring size is limited to 4K * data_width * LLP-count
++	 *      hw-if LLP is used
++	 *    * for APB, ring size is limited to 16M * data_width of data
++	 *  - currently sw ring mode dma supports only fixed or incremental
++	 *    src/dst addressing
++	 *  - ring_size shoule >= periods * period_size
++	 */
++	dma_addr_t ring_base;               /* (in)  ring buffer base (pa) */
++	dma_addr_t ring_size;               /* (in)  unit of data width */
++	dma_addr_t     dev_addr;                /* (in)  device data port address */
++	dma_addr_t periods;                 /* (in)  number of ints per ring */
++	dma_addr_t period_size;             /* (in)  size per int, data-width */
++
++
++	/* channel-wise completion callback - called when hw-ptr catches sw-ptr
++	 * (i.e., channel stops)
++	 *
++	 * completion_cb:   (in) client supplied callback function, executed in
++	 *                       interrupt context.
++	 * completion_data: (in) client private data to be passed to data
++	 *                       argument of completion_cb().
++	 */
++	void (*completion_cb)(int channel, u16 status, void *data);
++	void *completion_data;
++	/*********************************************************************/
++
++	/* channel allocation output */
++	u32     channel;                    /* (out) allocated channel */
++	void    *drq;                       /* (out) internal use (DMAD_DRQ *)*/
++
++	/* channel-alloc parameters (channel-wise properties) */
++	union {
++#ifdef CONFIG_PLATFORM_AHBDMA
++		dmad_ahb_chreq ahb_req;     /* (in)  for AHB DMA parameters */
++#endif
++#ifdef CONFIG_PLATFORM_APBDMA
++		dmad_apb_chreq apb_req;     /* (in)  APB Bridge DMA params */
++#endif
++	};
++
++} dmad_chreq;
++
++/* drb states are mutual exclusive */
++enum DMAD_DRB_STATE
++{
++	DMAD_DRB_STATE_FREE             = 0,
++	DMAD_DRB_STATE_READY            = 0x00000001,
++	DMAD_DRB_STATE_SUBMITTED        = 0x00000002,
++	DMAD_DRB_STATE_EXECUTED         = 0x00000004,
++	DMAD_DRB_STATE_COMPLETED        = 0x00000008,
++	//DMAD_DRB_STATE_ERROR          = 0x00000010,
++	DMAD_DRB_STATE_ABORT            = 0x00000020,
++};
++
++/* DMA request block
++ * todo: replaced link with kernel struct list_head ??
++ */
++typedef struct dmad_drb
++{
++	u32  prev;                       /* (internal) previous node */
++	u32  next;                       /* (internal) next node */
++	u32  node;                       /* (internal) this node */
++
++	u32  state;                      /* (out) DRB's current state */
++
++	union {
++		dma_addr_t src_addr;     /* (in)  source pa */
++		dma_addr_t addr0;        /* (in)  bi-direction mode alias */
++	};
++
++	union {
++		dma_addr_t dst_addr;     /* (in)  destination pa */
++		dma_addr_t addr1;        /* (in)  bi-direction mode alias */
++	};
++
++	/* (in) AHB DMA (22 bits): 0 ~ 4M-1, unit is "data width"
++	 *      APB DMA (24 bits): 0 ~ 16M-1, unit is "data width * burst size"
++	 *      => for safe without mistakes, use dmad_make_req_cycles() to
++	 *         compose this value if the addressing mode is incremental
++	 *         mode (not working yet for decremental mode).
++	 */
++	dma_addr_t req_cycle;
++
++	/* (in)  if non-null, this sync object will be signaled upon dma
++	 * completion (for blocked-waiting dma completion)
++	 */
++	struct completion *sync;
++
++} dmad_drb;
++
++
++/******************************************************************************
++ * Debug Trace Mechanism
++ */
++#if (DMAD_ERROR_TRACE)
++#define dmad_err(format, arg...)  printk(KERN_ERR format , ## arg)
++#else
++#define dmad_err(format, arg...)  (void)(0)
++#endif
++
++#if (DMAD_DEBUG_TRACE)
++#define dmad_dbg(format, arg...)  printk(KERN_INFO format , ## arg)
++#else
++#define dmad_dbg(format, arg...)  (void)(0)
++#endif
++
++#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA))
++
++/******************************************************************************
++ * DMAD Driver Interface
++******************************************************************************/
++
++extern int dmad_channel_alloc(dmad_chreq *ch_req);
++extern int dmad_channel_free(dmad_chreq *ch_req);
++extern int dmad_channel_enable(const dmad_chreq *ch_req, u8 enable);
++extern u32 dmad_max_size_per_drb(dmad_chreq *ch_req);
++extern u32 dmad_bytes_to_cycles(dmad_chreq *ch_req, u32 byte_size);
++
++extern int dmad_kickoff_requests(dmad_chreq *ch_req);
++extern int dmad_drain_requests(dmad_chreq *ch_req, u8 shutdown);
++
++/* for performance reason, these two functions are platform-specific */
++#ifdef CONFIG_PLATFORM_AHBDMA
++extern int dmad_probe_irq_source_ahb(void);
++#endif
++#ifdef CONFIG_PLATFORM_APBDMA
++extern int dmad_probe_irq_source_apb(void);
++#endif
++
++/* note: hw_ptr here is phyical address of dma source or destination */
++extern dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req);
++extern dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req);
++
++/*****************************************************************************
++ * routines only valid in discrete (non-ring) mode
++ */
++extern int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir);
++extern int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb);
++extern int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb);
++extern int dmad_submit_request(dmad_chreq *ch_req,
++			       dmad_drb *drb, u8 keep_fired);
++extern int dmad_withdraw_request(dmad_chreq *ch_req, dmad_drb *drb);
++/****************************************************************************/
++
++/*****************************************************************************
++ * routines only valid in ring mode
++ * note: sw_ptr and hw_ptr are values offset from the ring buffer base
++ *       unit of sw_ptr is data-width
++ *       unit of hw_ptr returned is byte
++ */
++extern int dmad_update_ring(dmad_chreq *ch_req);
++extern int dmad_update_ring_sw_ptr(dmad_chreq *ch_req,
++				   dma_addr_t sw_ptr, u8 keep_fired);
++extern dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq *ch_req);
++/****************************************************************************/
++
++#else  /* CONFIG_PLATFORM_AHBDMA || CONFIG_PLATFORM_APBDMA */
++
++static inline int dmad_channel_alloc(dmad_chreq *ch_req) { return -EFAULT; }
++static inline int dmad_channel_free(dmad_chreq *ch_req) { return -EFAULT; }
++static inline int dmad_channel_enable(const dmad_chreq *ch_req, u8 enable)
++	{ return -EFAULT; }
++static inline u32 dmad_max_size_per_drb(dmad_chreq *ch_req) { return 0; }
++static inline u32 dmad_bytes_to_cycles(dmad_chreq *ch_req, u32 byte_size)
++	{ return 0; }
++static inline int dmad_kickoff_requests(dmad_chreq *ch_req) { return -EFAULT; }
++static inline int dmad_drain_requests(dmad_chreq *ch_req, u8 shutdown)
++	{ return -EFAULT; }
++static inline int dmad_probe_irq_source_ahb(void) { return -EFAULT; }
++static inline int dmad_probe_irq_source_apb(void) { return -EFAULT; }
++static inline dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req)
++	{ return (dma_addr_t)NULL; }
++static inline dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req)
++	{ return (dma_addr_t)NULL; }
++static inline int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir)
++	{ return -EFAULT; }
++static inline int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb)
++	{ return -EFAULT; }
++static inline int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb)
++	{ return -EFAULT; }
++static inline int dmad_submit_request(dmad_chreq *ch_req,
++       dmad_drb *drb, u8 keep_fired) { return -EFAULT; }
++static inline int dmad_withdraw_request(dmad_chreq *ch_req, dmad_drb *drb)
++	{ return -EFAULT; }
++static inline int dmad_update_ring(dmad_chreq *ch_req)
++	{ return -EFAULT; }
++static inline int dmad_update_ring_sw_ptr(dmad_chreq *ch_req,
++	dma_addr_t sw_ptr, u8 keep_fired) { return -EFAULT; }
++static inline dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq *ch_req)
++	{ return (dma_addr_t)NULL; }
++
++#endif  /* CONFIG_PLATFORM_AHBDMA || CONFIG_PLATFORM_APBDMA */
++
++#endif  /* __NDS_DMAD_ATF_INC__ */
+diff --git a/arch/riscv/include/asm/dmad.h b/arch/riscv/include/asm/dmad.h
+new file mode 100644
+index 000000000000..44c87b49e606
+--- /dev/null
++++ b/arch/riscv/include/asm/dmad.h
+@@ -0,0 +1,71 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2018 Andes Technology Corporation
++ *
++ */
++
++#ifndef __NDS_DMAD_INC__
++#define __NDS_DMAD_INC__
++
++#include <asm/atcdmac300.h>
++
++#ifdef CONFIG_PLATFORM_AHBDMA
++int intc_ftdmac020_init_irq(int irq);
++#endif
++
++extern resource_size_t		ahb_base;
++extern resource_size_t		pmu_base;
++
++#define AMERALD_PRODUCT_ID	0x41471000
++#define AMERALD_MASK		0xFFFFF000
++
++/* DMAC */
++#define DMAC_FTDMAC020_IRQ_COUNT	8
++#define DMAC_FTDMAC020_IRQ0		64
++#define DMAC_FTDMAC020_IRQ1		65
++#define DMAC_FTDMAC020_IRQ2		66
++#define DMAC_FTDMAC020_IRQ3		67
++#define DMAC_FTDMAC020_IRQ4		68
++#define DMAC_FTDMAC020_IRQ5		69
++#define DMAC_FTDMAC020_IRQ6		70
++#define DMAC_FTDMAC020_IRQ7		71
++
++/* APBBRG */
++#define APBBRG_FTAPBBRG020S_IRQ_COUNT	4
++#define APBBRG_FTAPBBRG020S_IRQ0	72
++#define APBBRG_FTAPBBRG020S_IRQ1	73
++#define APBBRG_FTAPBBRG020S_IRQ2	74
++#define APBBRG_FTAPBBRG020S_IRQ3	75
++
++
++/* Dma irq */
++#define DMA_IRQ_COUNT	DMAC_FTDMAC020_IRQ_COUNT
++#define DMA_IRQ0	DMAC_FTDMAC020_IRQ0
++#define DMA_IRQ1	DMAC_FTDMAC020_IRQ1
++#define DMA_IRQ2	DMAC_FTDMAC020_IRQ2
++#define DMA_IRQ3	DMAC_FTDMAC020_IRQ3
++#define DMA_IRQ4	DMAC_FTDMAC020_IRQ4
++#define DMA_IRQ5	DMAC_FTDMAC020_IRQ5
++#define DMA_IRQ6	DMAC_FTDMAC020_IRQ6
++#define DMA_IRQ7	DMAC_FTDMAC020_IRQ7
++
++
++struct at_dma_platform_data {
++	unsigned int	nr_channels;
++	bool		is_private;
++#define CHAN_ALLOCATION_ASCENDING	0	/* zero to seven */
++#define CHAN_ALLOCATION_DESCENDING	1	/* seven to zero */
++	unsigned char	chan_allocation_order;
++#define CHAN_PRIORITY_ASCENDING		0	/* chan0 highest */
++#define CHAN_PRIORITY_DESCENDING	1	/* chan7 highest */
++	unsigned char	chan_priority;
++	unsigned short	block_size;
++	unsigned char	nr_masters;
++	unsigned char	data_width[4];
++	struct resource	*io;
++	void __iomem	*dmac_regs;
++	void __iomem	*pmu_regs;
++	void __iomem	*apb_regs;
++};
++
++#endif  /* __NDS_DMAD_INC__ */
+diff --git a/arch/riscv/platforms/Kconfig b/arch/riscv/platforms/Kconfig
+new file mode 100644
+index 000000000000..96462808c5ef
+--- /dev/null
++++ b/arch/riscv/platforms/Kconfig
+@@ -0,0 +1,21 @@
++choice
++	prompt "platform type"
++	default PLAT_AE350
++
++config PLAT_AE350
++	bool "ae350 platform"
++
++endchoice
++
++if PLAT_AE350
++source "arch/riscv/platforms/ae350/Kconfig"
++endif
++
++menu "Common Platform Options"
++
++config PLATFORM_AHBDMA
++	prompt "platform AHB DMA support"
++	bool
++	default y
++
++endmenu
+diff --git a/arch/riscv/platforms/Makefile b/arch/riscv/platforms/Makefile
+new file mode 100644
+index 000000000000..a95c2e44a903
+--- /dev/null
++++ b/arch/riscv/platforms/Makefile
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_PLATFORM_AHBDMA) += dmad_intc.o
++obj-$(CONFIG_PLAT_AE350)	+= ae350/
+\ No newline at end of file
+diff --git a/arch/riscv/platforms/ae350/Kconfig b/arch/riscv/platforms/ae350/Kconfig
+new file mode 100644
+index 000000000000..57d3a9aa5508
+--- /dev/null
++++ b/arch/riscv/platforms/ae350/Kconfig
+@@ -0,0 +1,7 @@
++menu "AE3XX Platform Options"
++
++config ATCDMAC300
++	def_bool y
++	depends on PLATFORM_AHBDMA
++
++endmenu
+diff --git a/arch/riscv/platforms/ae350/Makefile b/arch/riscv/platforms/ae350/Makefile
+new file mode 100644
+index 000000000000..36c86ca38e3b
+--- /dev/null
++++ b/arch/riscv/platforms/ae350/Makefile
+@@ -0,0 +1 @@
++obj-y	= atcdmac300.o
+diff --git a/arch/riscv/platforms/ae350/atcdmac300.c b/arch/riscv/platforms/ae350/atcdmac300.c
+new file mode 100644
+index 000000000000..e635328f9362
+--- /dev/null
++++ b/arch/riscv/platforms/ae350/atcdmac300.c
+@@ -0,0 +1,2531 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2018 Andes Technology Corporation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/sizes.h>
++#include <asm/types.h>
++#include <asm/io.h>
++#include <asm/dmad.h>
++#include <linux/irq.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/of_dma.h>
++#include <linux/math64.h>
++#include <asm/div64.h>
++
++resource_size_t	dmac_base;
++
++static inline addr_t REG_READ(unsigned long r)
++{
++	return readl((volatile void __iomem *) (unsigned long) r);
++}
++
++static inline void REG_WRITE(addr_t d, unsigned long r)
++{
++	writel(d, (volatile void __iomem *) (unsigned long) r);
++}
++
++#if (defined(CONFIG_PLATFORM_AHBDMA))
++#define DMAD_AHB_MAX_CHANNELS           DMAC_MAX_CHANNELS
++
++#define DMAD_DRB_POOL_SIZE              32	/* 128 */
++
++
++static inline addr_t din(unsigned long r)
++{
++	return REG_READ(r);
++}
++
++static inline void dout(addr_t d, unsigned long r)
++{
++	REG_WRITE(d,r);
++}
++
++/* reg/io supplementals */
++
++static void setbl(addr_t bit, unsigned long reg)
++{
++	REG_WRITE(REG_READ(reg) | (addr_t) ((addr_t) 1 << bit), reg);
++}
++
++static inline void clrbl(addr_t bit, unsigned long reg)
++{
++	REG_WRITE(REG_READ(reg) & (~((addr_t) ((addr_t) 1 << bit))), reg);
++}
++
++static inline addr_t getbl(addr_t bit, unsigned long reg)
++{
++ return REG_READ(reg) & (addr_t) ((addr_t) 1 << bit);
++}
++/******************************************************************************/
++
++enum DMAD_DRQ_FLAGS {
++	DMAD_DRQ_STATE_READY = 0x00000001,	/* channel allocation status */
++	DMAD_DRQ_STATE_ABORT = 0x00000002,	/* abort drb alloc block-wait */
++	DMAD_DRQ_DIR_A1_TO_A0 = 0x00000004,	/* Transfer direction */
++};
++
++#define DMAD_DRQ_DIR_MASK  DMAD_DRQ_DIR_A1_TO_A0
++
++/* DMA request queue, one instance per channel */
++typedef struct dmad_drq {
++	u32 state;		/* enum DMAD_DRQ_STATE */
++
++	unsigned long channel_base;	/* register base address */
++	unsigned long enable_port;	/* enable register */
++	unsigned long src_port;	/* source address register */
++	unsigned long dst_port;	/* dest address register */
++	unsigned long cyc_port;	/* size(cycle) register */
++
++	u32 flags;		/* enum DMAD_CHREQ_FLAGS */
++
++	spinlock_t drb_pool_lock;
++	dmad_drb *drb_pool;	/* drb pool */
++
++	unsigned long fre_head;		/* free list head */
++	unsigned long fre_tail;		/* free list tail */
++
++	unsigned long rdy_head;		/* ready list head */
++	unsigned long rdy_tail;		/* ready list tail */
++
++	unsigned long sbt_head;		/* submitted list head */
++	unsigned long sbt_tail;		/* submitted list tail */
++
++	u32 data_width;		/* dma transfer data width */
++
++	struct completion drb_alloc_sync;
++
++	/* client supplied callback function, executed in interrupt context
++	 * client private data to be passed to data argument of completion_cb().
++	 */
++	void (*completion_cb) (int channel, u16 status, void *data);
++	void *completion_data;
++
++	/* ring-mode fields are valid for DMAD_FLAGS_RING_MODE */
++	dma_addr_t ring_base;	/* ring buffer base address */
++	int ring_size;	/* size (of data width) */
++	unsigned long ring_port;	/* for setup/fetch hw_ptr */
++	dmad_drb *ring_drb;
++
++	addr_t dev_addr;	/* device data port */
++
++	int periods;		/* interrupts periods */
++	int period_size;	/* of dma data with */
++	dma_addr_t period_bytes;	/* Period size, in bytes */
++
++	/* ring_size - period_size * periods */
++	dma_addr_t remnant_size;
++
++	dma_addr_t sw_ptr;	/* sw pointer */
++	int sw_p_idx;		/* current ring_ptr */
++	dma_addr_t sw_p_off;	/* offset to period base */
++
++} dmad_drq;
++
++static inline void dmad_enable_channel(dmad_drq * drq)
++{
++	setbl(CHEN, drq->enable_port);
++}
++
++static inline void dmad_disable_channel(dmad_drq * drq)
++{
++	clrbl(CHEN, drq->enable_port);
++}
++
++static inline addr_t dmad_is_channel_enabled(dmad_drq * drq)
++{
++	return (addr_t) getbl(CHEN, drq->enable_port);
++}
++
++/* system irq number (per channel, ahb) */
++static const unsigned int ahb_irqs[DMAD_AHB_MAX_CHANNELS] = {
++	DMA_IRQ0,
++	DMA_IRQ1,
++	DMA_IRQ2,
++	DMA_IRQ3,
++	DMA_IRQ4,
++	DMA_IRQ5,
++	DMA_IRQ6,
++	DMA_IRQ7,
++};
++
++/* Driver data structure, one instance per system */
++typedef struct DMAD_DATA_STRUCT {
++	/* Driver data initialization flag */
++
++	/* DMA queue pool access control object */
++	spinlock_t drq_pool_lock;
++
++	/* DMA queue base address, to ease alloc/free flow */
++	dmad_drq *drq_pool;
++	/* DMA queue for AHB DMA channels */
++	dmad_drq *ahb_drq_pool;
++	void *plat;
++} DMAD_DATA;
++
++/* Driver data structure instance, one instance per system */
++
++static DMAD_DATA dmad __attribute__ ((aligned(8))) = {
++
++	.drq_pool_lock = __SPIN_LOCK_UNLOCKED(dmad.drq_pool_lock),
++	.drq_pool = 0,
++	.ahb_drq_pool = 0,
++	.plat = 0,
++};
++
++/**
++ * dmad_next_drb - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @node     : [in] The node number to lookup its next node
++ * @drb      : [out] The drb next to the "node" node number
++ *
++ * Lookup next DRB of the specified node number. "drb" is null if reaches end
++ * of the list.
++ */
++static inline void dmad_next_drb(dmad_drb * drb_pool, u32 node, dmad_drb ** drb)
++{
++	if (likely(drb_pool[node].next != 0))
++		*drb = &drb_pool[drb_pool[node].next];
++	else
++		*drb = 0;
++}
++
++/**
++ * dmad_prev_drb - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @node     : [in] The node number to lookup its previous node
++ * @drb      : [out] The drb previous to the "node" node number
++ *
++ * Lookup previous DRB of the specified node number. "drb" is null if reaches
++ * head-end of the list.
++ */
++static inline void dmad_prev_drb(dmad_drb * drb_pool, u32 node, dmad_drb ** drb)
++{
++	if (unlikely(drb_pool[node].prev != 0))
++		*drb = &drb_pool[drb_pool[node].prev];
++	else
++		*drb = 0;
++}
++
++/**
++ * dmad_detach_node - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @node     : [in] The node to be dettached from the queue
++ *
++ * Detached a DRB specified by the node number from the queue.  The head and
++ * tail records will be updated accordingly.
++ */
++static inline void dmad_detach_node(dmad_drb * drb_pool,
++				    unsigned long * head, unsigned long * tail, u32 node)
++{
++	if (likely(drb_pool[node].prev != 0)) {
++		/* prev->next = this->next (= 0, if this is a tail) */
++		drb_pool[drb_pool[node].prev].next = drb_pool[node].next;
++	} else {
++		/* this node is head, move head to next node
++		 * (= 0, if this is the only one node) */
++		*head = drb_pool[node].next;
++	}
++
++	if (unlikely(drb_pool[node].next != 0)) {
++		/* next->prev = this->prev (= 0, if this is a head) */
++		drb_pool[drb_pool[node].next].prev = drb_pool[node].prev;
++	} else {
++		/* this node is tail, move tail to previous node
++		 * (= 0, if this is the only one node) */
++		*tail = drb_pool[node].prev;
++	}
++
++	drb_pool[node].prev = drb_pool[node].next = 0;
++}
++
++/**
++ * dmad_detach_head - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @drb      : [out] The detached head node; null if the queue is empty
++ *
++ * Detached a DRB from the head of the queue.  The head and tail records will
++ * be updated accordingly.
++ */
++static inline void dmad_detach_head(dmad_drb * drb_pool,
++				    unsigned long * head, unsigned long * tail, dmad_drb ** drb)
++{
++	if (unlikely(*head == 0)) {
++		*drb = NULL;
++		return;
++	}
++
++	*drb = &drb_pool[*head];
++
++	if (likely((*drb)->next != 0)) {
++		/* next->prev = this->prev (= 0, if this is a head) */
++		drb_pool[(*drb)->next].prev = 0;
++
++		/* prev->next = this->next (do nothing, if this is a head) */
++
++		/* head = this->next */
++		*head = (*drb)->next;
++	} else {
++		/* head = tail = 0 */
++		*head = 0;
++		*tail = 0;
++	}
++
++	/* this->prev = this->next = 0 (do nothing, if save code size) */
++	(*drb)->prev = (*drb)->next = 0;
++}
++
++/**
++ * dmad_get_head - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @drb      : [out] The head node; null if the queue is empty
++ *
++ * Get a DRB from the head of the queue.  The head and tail records remain
++ * unchanged.
++ */
++static inline void dmad_get_head(dmad_drb * drb_pool, const unsigned long * head,
++				 const unsigned long * tail, dmad_drb ** drb)
++{
++	if (unlikely(*head == 0)) {
++		*drb = NULL;
++		return;
++	}
++
++	*drb = &drb_pool[*head];
++}
++
++/**
++ * dmad_detach_tail - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @drb      : [out] The tail node; null if the queue is empty
++ *
++ * Detached a DRB from the head of the queue.  The head and tail records will
++ * be updated accordingly.
++ */
++static inline void dmad_detach_tail(dmad_drb * drb_pool,
++				    unsigned long * head, unsigned long * tail, dmad_drb ** drb)
++{
++	if (unlikely(*tail == 0)) {
++		*drb = NULL;
++		return;
++	}
++
++	*drb = &drb_pool[*tail];
++
++	if (likely((*drb)->prev != 0)) {
++		/* prev->next = this->next (= 0, if this is a tail) */
++		drb_pool[(*drb)->prev].next = 0;
++
++		/* next->prev = this->prev (do nothing, if this is a tail) */
++
++		/* tail = this->prev */
++		*tail = (*drb)->prev;
++	} else {
++		/* head = tail = 0 */
++		*head = 0;
++		*tail = 0;
++	}
++
++	/* this->next = this->prev = 0 (do nothing, if save code size) */
++	(*drb)->prev = (*drb)->next = 0;
++}
++
++/**
++ * dmad_get_tail - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @drb      : [out] The tail node; null if the queue is empty
++ *
++ * Get a DRB from the tail of the queue.  The head and tail records remain
++ * unchanged.
++ */
++static inline void dmad_get_tail(dmad_drb * drb_pool,
++				 unsigned long * head, unsigned long * tail, dmad_drb ** drb)
++{
++	if (unlikely(*tail == 0)) {
++		*drb = NULL;
++		return;
++	}
++
++	*drb = &drb_pool[*tail];
++}
++
++/**
++ * dmad_attach_head - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @node     : [in] The node to be attached
++ *
++ * Attach a DRB node to the head of the queue.  The head and tail records will
++ * be updated accordingly.
++ */
++static inline void dmad_attach_head(dmad_drb * drb_pool,
++				    unsigned long * head, unsigned long * tail, u32 node)
++{
++	if (likely(*head != 0)) {
++		/* head->prev = this */
++		drb_pool[*head].prev = node;
++
++		/* this->next = head */
++		drb_pool[node].next = *head;
++		/* this->prev = 0 */
++		drb_pool[node].prev = 0;
++
++		/* head = node */
++		*head = node;
++	} else {
++		/* head = tail = node */
++		*head = *tail = node;
++		drb_pool[node].prev = drb_pool[node].next = 0;
++	}
++}
++
++/**
++ * dmad_attach_head - static function
++ * @drb_pool : [in] The raw DRB pool of a DMA channel
++ * @head     : [in/out] Reference to the head node number
++ * @tail     : [in/out] Reference to the tail node number
++ * @node     : [in] The node to be attached
++ *
++ * Attach a DRB node to the tail of the queue.  The head and tail records will
++ * be updated accordingly.
++ */
++static inline void dmad_attach_tail(dmad_drb * drb_pool,
++				    unsigned long * head, unsigned long * tail, u32 node)
++{
++	if (likely(*tail != 0)) {
++		/* tail->next = this */
++		drb_pool[*tail].next = node;
++
++		/* this->prev = tail */
++		drb_pool[node].prev = *tail;
++		/* this->next = 0 */
++		drb_pool[node].next = 0;
++
++		/* tail = node */
++		*tail = node;
++	} else {
++		/* head = tail = node */
++		*head = *tail = node;
++		drb_pool[node].prev = drb_pool[node].next = 0;
++	}
++}
++
++#ifdef CONFIG_PLATFORM_AHBDMA
++
++/**
++ * dmad_ahb_isr - AHB DMA interrupt service routine
++ *
++ * @irq    : [in] The irq number
++ * @dev_id : [in] The identifier to identify the asserted channel
++ *
++ * This is the ISR that services all AHB DMA channels.
++ */
++static irqreturn_t dmad_ahb_isr(int irq, void *dev_id)
++{
++	dmad_drq *drq;
++	dmad_drb *drb, *drb_iter;
++	u32 channel = ((unsigned long) dev_id) - 1;
++	u8 tc_int = 0;
++	u8 err_int = 0;
++	u8 abt_int = 0;
++	u8 cpl_events = 1;
++
++	dmad_dbg("%s() >> channel(%d)\n", __func__, channel);
++
++	if (channel >= DMAD_AHB_MAX_CHANNELS) {
++		dmad_err("%s() invlaid channel number: %d!\n",
++			 __func__, channel);
++		return IRQ_HANDLED;
++	}
++
++	/* Fetch channel's DRQ struct (DMA Request Queue) */
++	drq = (dmad_drq *) & dmad.ahb_drq_pool[channel];
++
++	/* Check DMA status register to get channel number */
++	if (likely(getbl(channel+TC_OFFSET, (unsigned long)INT_STA))) {
++
++		/* Mark as TC int */
++		tc_int = 1;
++
++		/* DMAC INT TC status clear */
++		setbl(channel+TC_OFFSET, (unsigned long)INT_STA);
++
++	} else if (getbl(channel+ERR_OFFSET, (unsigned long)INT_STA)) {
++
++		/* Mark as ERR int */
++		err_int = 1;
++
++		/* DMAC INT ERR status clear */
++		setbl(channel+ERR_OFFSET, (unsigned long)INT_STA);
++
++	} else if (getbl(channel+ABT_OFFSET, (unsigned long)INT_STA)) {
++
++		/* Mark as ABT int */
++		abt_int = 1;
++
++		/* DMAC INT ABT status clear */
++		setbl(channel+INT_STA, (unsigned long)INT_STA);
++
++	} else {
++
++		dmad_err("%s() possible false-fired ahb dma int,"
++			 "channel %d status-reg: status(0x%08x)\n",
++			 __func__, channel,din((unsigned long)INT_STA));
++
++		/* Stop DMA channel (make sure the channel will be stopped) */
++		clrbl(CH_EN, drq->enable_port);
++		return IRQ_HANDLED;
++	}
++
++	/* DMAC
++	 * Stop DMA channel temporarily */
++	dmad_disable_channel(drq);
++
++	spin_lock(&drq->drb_pool_lock);
++
++	/* Lookup/detach latest submitted DRB (DMA Request Block) from
++	 * the DRQ (DMA Request Queue), so ISR could kick off next DRB */
++	dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
++	if (drb == NULL) {
++		spin_unlock(&drq->drb_pool_lock);
++		/* submitted list could be empty if client cancel all requests
++		 * of the channel. */
++		return IRQ_HANDLED;
++	}
++
++	/* release blocking of drb-allocation, if any ... */
++	if (unlikely((drq->fre_head == 0) &&
++		     (drq->flags & DMAD_FLAGS_SLEEP_BLOCK))) {
++		complete_all(&drq->drb_alloc_sync);
++	}
++
++	/* Process DRBs according to interrupt reason */
++	if (tc_int) {
++
++		dmad_dbg("dma finish\n");
++
++		dmad_dbg("finish drb(%d 0x%08x) addr0(0x%08x) "
++			 "addr1(0x%08x) size(0x%08x)\n",
++			 drb->node, (u32) drb, drb->src_addr,
++			 drb->dst_addr, drb->req_cycle);
++
++		if (drb->req_cycle == 0)
++			cpl_events = 0;
++
++		// Mark DRB state as completed
++		drb->state = DMAD_DRB_STATE_COMPLETED;
++		if (cpl_events && drb->sync)
++			complete_all(drb->sync);
++
++		dmad_attach_tail(drq->drb_pool, &drq->fre_head,
++				 &drq->fre_tail, drb->node);
++
++		// Check whether there are pending requests in the DRQ
++		if (drq->sbt_head != 0) {
++
++			// Lookup next DRB (DMA Request Block)
++			drb_iter = &drq->drb_pool[drq->sbt_head];
++
++			dmad_dbg("exec drb(%d 0x%08x) addr0(0x%08x) "
++				 "addr1(0x%08x) size(0x%08x)\n",
++				 drb_iter->node, (u32) drb_iter,
++				 drb_iter->src_addr, drb_iter->dst_addr,
++				 drb_iter->req_cycle);
++
++			// Kick-off DMA for next DRB
++			// - Source and destination address
++			if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) {
++				dout(drb_iter->addr1, (unsigned long)drq->src_port);
++				dout(drb_iter->addr0, (unsigned long)drq->dst_port);
++			} else {
++				dout(drb_iter->addr0, (unsigned long)drq->src_port);
++				dout(drb_iter->addr1, (unsigned long)drq->dst_port);
++			}
++
++			/* - Transfer size (in units of source width) */
++			dout(drb_iter->req_cycle, (unsigned long)drq->cyc_port);
++
++			/* Kick off next request */
++			dmad_enable_channel(drq);
++
++			drb_iter->state = DMAD_DRB_STATE_EXECUTED;
++
++		} else {
++			/* No pending requests, keep the DMA channel stopped */
++		}
++
++	} else {
++
++		dmad_err("%s() ahb dma channel %d error!\n", __func__, channel);
++
++		/* Zero out src, dst, and size */
++		dout(0, (unsigned long)drq->src_port);
++		dout(0, (unsigned long)drq->dst_port);
++		dout(0, (unsigned long)drq->cyc_port);
++
++		/* Remove all pending requests in the queue */
++		drb_iter = drb;
++		while (drb_iter) {
++
++			dmad_err("abort drb ");
++
++			if (drb_iter->req_cycle == 0)
++				cpl_events = 0;
++
++			/* Mark DRB state as abort */
++			drb_iter->state = DMAD_DRB_STATE_ABORT;
++
++			if (cpl_events && drb_iter->sync)
++				complete_all(drb_iter->sync);
++
++			dmad_attach_tail(drq->drb_pool, &drq->fre_head,
++					 &drq->fre_tail, drb_iter->node);
++
++			/* Detach next submitted DRB (DMA Request Block)
++			 * from the DRQ (DMA Request Queue) */
++			dmad_detach_head(drq->drb_pool, &drq->sbt_head,
++					 &drq->sbt_tail, &drb_iter);
++		}
++	}
++
++	spin_unlock(&drq->drb_pool_lock);
++
++	/* dispatch interrupt-context level callbacks */
++	if (cpl_events && drq->completion_cb) {
++		/* signal DMA driver that new node is available */
++		drq->completion_cb(channel, tc_int, drq->completion_data);
++	}
++
++	dmad_dbg("%s() <<\n", __func__);
++
++	return IRQ_HANDLED;
++}
++
++/**
++ * dmad_ahb_config_dir - prepare command reg according to tx direction
++ * @ch_req       : [in] Reference to the DMA request descriptor structure
++ * @channel_cmds : [out] Reference to array of command words to be prepared with
++ * @return       : none
++ *
++ * Prepare command registers according to transfer direction ...
++ *   channel_cmd[0]  DMAC_CSR
++ *   channel_cmd[1]  DMAC_CFG
++ *
++ * This function only serves as local helper.  No protection wrappers.
++ */
++static void dmad_ahb_config_dir(dmad_chreq * ch_req, unsigned long * channel_cmds)
++{
++	dmad_drq *drq = (dmad_drq *) ch_req->drq;
++	dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req);
++	channel_control ch_ctl;
++	dmad_dbg("%s() channel_cmds(0x%08x)\n",__func__, channel_cmds[0]);
++	channel_cmds[0] &= ~(u32)(SRCWIDTH_MASK|SRCADDRCTRL_MASK|
++		DSTWIDTH_MASK|DSTADDRCTRL_MASK|
++		SRC_HS|DST_HS|SRCREQSEL_MASK|DSTREQSEL_MASK);
++
++	if (ahb_req->tx_dir == 0) {
++		dmad_dbg("%s() addr0 --> addr1\n", __func__);
++		memcpy((u8 *)&ch_ctl.sWidth,(u8 *)&ahb_req->addr0_width,12);
++		memcpy((u8 *)&ch_ctl.dWidth,(u8 *)&ahb_req->addr1_width,12);
++		drq->flags &= ~(addr_t) DMAD_DRQ_DIR_A1_TO_A0;
++	}else{
++		dmad_dbg("%s() addr0 <-- addr1\n", __func__);
++		memcpy((u8 *)&ch_ctl.sWidth,(u8 *)&ahb_req->addr1_width,12);
++		memcpy((u8 *)&ch_ctl.dWidth,(u8 *)&ahb_req->addr0_width,12);
++		drq->flags |= (addr_t) DMAD_DRQ_DIR_A1_TO_A0;
++	}
++	channel_cmds[0] |=(((ch_ctl.sWidth << SRCWIDTH) &SRCWIDTH_MASK) |
++		((ch_ctl.sCtrl << SRCADDRCTRL) &SRCADDRCTRL_MASK) |
++		((ch_ctl.dWidth << DSTWIDTH) &DSTWIDTH_MASK) |
++		((ch_ctl.dCtrl << DSTADDRCTRL) &DSTADDRCTRL_MASK));
++	drq->data_width = ch_ctl.sWidth;
++	if (likely(ahb_req->hw_handshake != 0)) {
++		if (ch_ctl.sReqn != DMAC_REQN_NONE)
++		{
++			channel_cmds[0] |= (SRC_HS |
++				((ch_ctl.sReqn <<SRCREQSEL)&SRCREQSEL_MASK));
++		}
++		if (ch_ctl.dReqn != DMAC_REQN_NONE)
++		{
++			channel_cmds[0] |= (DST_HS |
++				((ch_ctl.dReqn <<DSTREQSEL)&DSTREQSEL_MASK));
++		}
++	}
++	dmad_dbg("%s() channel_cmds(0x%08x)\n",
++		 __func__, channel_cmds[0]);
++}
++
++/**
++ * dmad_ahb_init - initialize a ahb dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * Register AHB DMA ISR and performs hw initialization for the given DMA
++ * channel.
++ */
++static int dmad_ahb_init(dmad_chreq * ch_req)
++{
++	int err = 0;
++	dmad_drq *drq = (dmad_drq *) ch_req->drq;
++	dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req);
++	u32 channel = (u32) ch_req->channel;
++
++	unsigned long channel_base = drq->channel_base;
++	addr_t channel_cmds[1];
++	unsigned long lock_flags;
++	dmad_dbg("%s()\n", __func__);
++	/* register interrupt handler */
++	err = request_irq(ahb_irqs[channel], dmad_ahb_isr, 0,
++			  "AHB_DMA", (void *)(unsigned long)(channel + 1));
++	if (unlikely(err != 0)) {
++		dmad_err("unable to request IRQ %d for AHB DMA "
++			 "(error %d)\n", ahb_irqs[channel], err);
++		free_irq(ahb_irqs[channel], (void *)(unsigned long)(channel + 1));
++		return err;
++	}
++	spin_lock_irqsave(&dmad.drq_pool_lock, lock_flags);
++
++	/* - INT TC/ERR/ABT status clear */
++	setbl(channel+TC_OFFSET, (unsigned long)INT_STA);
++	setbl(channel+ABT_OFFSET, (unsigned long)INT_STA);
++	setbl(channel+ERR_OFFSET, (unsigned long)INT_STA);
++
++	/* - SYNC */
++	if (ahb_req->sync != (getbl(REQSYNC, CFG)>>REQSYNC))
++	{
++		printk("sync configuration error !\n");
++		return EINVAL;
++	}
++	if(ahb_req->priority > PRIORITY_HIGH)
++		ahb_req->priority = PRIORITY_HIGH;
++
++	channel_cmds[0] = (ahb_req->priority << PRIORITY_SHIFT);
++	channel_cmds[0] |= (ahb_req->burst_size << SBURST_SIZE_SHIFT) &
++	    SBURST_SIZE_MASK;
++
++	if (0 ==
++	    (ch_req->flags & (DMAD_FLAGS_RING_MODE | DMAD_FLAGS_BIDIRECTION)))
++		ahb_req->tx_dir = 0;
++
++	dmad_ahb_config_dir(ch_req, (unsigned long *)channel_cmds);
++	dout(channel_cmds[0], (unsigned long)drq->enable_port);
++
++	/* SRCADR and DESADR */
++	dout(0, (unsigned long) drq->src_port);
++	dout(0, (unsigned long) drq->dst_port);
++	/* CYC (transfer size) */
++	dout(0, (unsigned long) drq->cyc_port);
++	/* LLP */
++	dout(0, (unsigned long)channel_base + CH_LLP_LOW_OFF);
++
++	/* TOT_SIZE - not now */
++	spin_unlock_irqrestore(&dmad.drq_pool_lock, lock_flags);
++
++	return err;
++}
++
++#endif /* CONFIG_PLATFORM_AHBDMA */
++/**
++ * dmad_channel_init - initialize given dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * This function serves as the abstraction layer of dmad_ahb_init()
++ * and dmad_apb_init() functions.
++ */
++static int dmad_channel_init(dmad_chreq * ch_req)
++{
++	int err = 0;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL))
++		return -EFAULT;
++
++	if (unlikely(ch_req->drq == NULL))
++		return -EBADR;
++
++	/* Initialize DMA controller */
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE)
++		err = dmad_ahb_init(ch_req);
++	return err;
++}
++
++static inline void dmad_reset_channel(dmad_drq * drq)
++{
++	/* disable dma controller */
++	dmad_disable_channel(drq);
++
++	/* Source and destination address */
++	dout(0, (unsigned long)drq->src_port);
++	dout(0, (unsigned long)drq->dst_port);
++
++	/* Transfer size (in units of source width) */
++	dout(0, (unsigned long)drq->cyc_port);
++}
++
++/**
++ * dmad_channel_reset - reset given dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * This function serves as the abstraction layer of dmad_ahb_reset()
++ * and dmad_apb_reset() functions.
++ */
++static int dmad_channel_reset(dmad_chreq * ch_req)
++{
++	u32 channel = (u32) ch_req->channel;
++	unsigned long lock_flags;
++	int err = 0;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL))
++		return -EFAULT;
++
++	if (unlikely(ch_req->drq == NULL))
++		return -EBADR;
++
++	spin_lock_irqsave(&((dmad_drq *) ch_req->drq)->drb_pool_lock,
++			  lock_flags);
++
++	/* stop DMA channel */
++	dmad_reset_channel((dmad_drq *) ch_req->drq);
++
++	spin_unlock_irqrestore(&((dmad_drq *) ch_req->drq)->drb_pool_lock,
++			       lock_flags);
++
++	/* unregister interrupt handler */
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE)
++		free_irq(ahb_irqs[channel], (void *)(unsigned long)(channel + 1));
++
++	return err;
++}
++
++/**
++ * dmad_channel_alloc - allocates and initialize a dma channel
++ * @ch_req : [in/out] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * This function allocates a DMA channel according to client's request
++ * parameters.  ISR and HW state will also be initialized accordingly.
++ */
++int dmad_channel_alloc(dmad_chreq * ch_req)
++{
++	dmad_drq *drq_iter = NULL;
++	dmad_drb *drb_iter;
++	int err = 0;
++	u32 i = 0;
++	dmad_dbg("%s()\n", __func__);
++
++	if (ch_req == NULL) {
++		printk(KERN_ERR "%s() invalid argument!\n", __func__);
++		return -EFAULT;
++	}
++
++	spin_lock(&dmad.drq_pool_lock);
++
++	/* locate an available DMA channel */
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++
++		drq_iter = dmad.ahb_drq_pool;
++
++		if ((ch_req->ahb_req.src_reqn != DMAC_REQN_NONE) ||
++		    (ch_req->ahb_req.dst_reqn != DMAC_REQN_NONE)) {
++			/* [2007-12-03] It looks current board have problem to
++			 * do dma traffic for APB devices on DMAC channel 0/1.
++			 * Redirect all APB devices to start from channel 2.
++			 */
++
++			/* [todo] include USB controller ? */
++			drq_iter = &dmad.ahb_drq_pool[2];
++			for (i = 2; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter) {
++				if (!(drq_iter->state & DMAD_DRQ_STATE_READY))
++					break;
++			}
++		} else {
++			/* channel for other devices is free to allocate */
++			for (i = 0; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter) {
++				if (!(drq_iter->state & DMAD_DRQ_STATE_READY))
++					break;
++			}
++		}
++		if (unlikely(i == DMAD_AHB_MAX_CHANNELS)) {
++			spin_unlock(&dmad.drq_pool_lock);
++			dmad_err("out of available channels (AHB DMAC)!\n");
++			return -ENOSPC;
++		}
++
++		dmad_dbg("allocated channel: %d (AHB DMAC)\n", i);
++	}
++
++	if (drq_iter == NULL) {
++		spin_unlock(&dmad.drq_pool_lock);
++		printk(KERN_ERR "%s() invalid argument!\n", __func__);
++		return -EFAULT;
++	}
++
++	spin_unlock(&dmad.drq_pool_lock);
++	memset(drq_iter, 0, sizeof(dmad_drq));
++
++	/* Initialize DMA channel's DRB pool as list of free DRBs */
++	drq_iter->drb_pool =
++	    kmalloc(DMAD_DRB_POOL_SIZE * sizeof(dmad_drb), GFP_ATOMIC);
++
++	if (drq_iter->drb_pool == NULL) {
++		printk(KERN_ERR "%s() failed to allocate drb pool!\n",
++		       __func__);
++		return -ENOMEM;
++	}
++
++	/* Allocate the DMA channel */
++	drq_iter->state = DMAD_DRQ_STATE_READY;
++	drq_iter->flags = ch_req->flags;
++
++	/* Initialize synchronization object for DMA queue access control */
++	spin_lock_init(&drq_iter->drb_pool_lock);
++
++	/* Initialize synchronization object for free drb notification */
++	init_completion(&drq_iter->drb_alloc_sync);
++
++	/* Record the channel number in client's struct */
++	ch_req->channel = i;
++	/* Record the channel's queue handle in client's struct */
++	ch_req->drq = drq_iter;
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++		drq_iter->channel_base = (unsigned long) DMAC_BASE_CH(i);
++		drq_iter->enable_port = (unsigned long) CH_CTL(i);
++		drq_iter->src_port = (unsigned long) CH_SRC_L(i);
++		drq_iter->dst_port = (unsigned long) CH_DST_L(i);
++		drq_iter->cyc_port = (unsigned long) CH_SIZE(i);
++	}
++	/* drb-0 is an invalid node - for node validation */
++	drb_iter = &drq_iter->drb_pool[0];
++	drb_iter->prev = 0;
++	drb_iter->next = 0;
++	drb_iter->node = 0;
++	++drb_iter;
++
++	/* init other drbs - link in order */
++	for (i = 1; i < DMAD_DRB_POOL_SIZE; ++i, ++drb_iter) {
++		drb_iter->prev = i - 1;
++		drb_iter->next = i + 1;
++		drb_iter->node = i;
++	}
++	drq_iter->drb_pool[DMAD_DRB_POOL_SIZE - 1].next = 0;
++
++	/* Initialize channel's DRB free-list, ready-list, and submitted-list */
++	drq_iter->fre_head = 1;
++	drq_iter->fre_tail = DMAD_DRB_POOL_SIZE - 1;
++	drq_iter->rdy_head = drq_iter->rdy_tail = 0;
++	drq_iter->sbt_head = drq_iter->sbt_tail = 0;
++
++	/* initialize ring buffer mode resources */
++	if (ch_req->flags & DMAD_FLAGS_RING_MODE) {
++
++		int remnant = (int)ch_req->ring_size -
++		    (int)ch_req->periods * (int)ch_req->period_size;
++		if (remnant == 0) {
++			drq_iter->periods = ch_req->periods;
++		} else if (remnant > 0) {
++			drq_iter->periods = ch_req->periods;	// + 1;
++		} else {
++			dmad_err("%s() Error - buffer_size < "
++				 "periods * period_size!\n", __func__);
++			err = -EFAULT;
++			goto _err_exit;
++		}
++
++		drq_iter->ring_size = ch_req->ring_size;
++		drq_iter->period_size = ch_req->period_size;
++		drq_iter->remnant_size = (dma_addr_t) remnant;
++
++		drq_iter->ring_base = (dma_addr_t) ch_req->ring_base;
++		drq_iter->dev_addr = (dma_addr_t) ch_req->dev_addr;
++
++		if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++			if ((ch_req->ahb_req.ring_ctrl == DMAC_CSR_AD_DEC) ||
++			    (ch_req->ahb_req.dev_ctrl == DMAC_CSR_AD_DEC)) {
++				dmad_err("%s() Error - decremental"
++					 " addressing DMA is not supported in"
++					 " ring mode currently!\n", __func__);
++				err = -EFAULT;
++				goto _err_exit;
++			}
++
++			if (ch_req->ahb_req.ring_ctrl == DMAC_CSR_AD_FIX) {
++				dmad_err("%s() Error - ring address control is "
++					 "fixed in ring DMA mode!\n", __func__);
++				err = -EFAULT;
++				goto _err_exit;
++			}
++
++			drq_iter->period_bytes =
++			    DMAC_CYCLE_TO_BYTES(ch_req->period_size,
++						ch_req->ahb_req.ring_width);
++
++			/* 0 - addr0 to addr1; 1 - addr1 to addr0 */
++			if (ch_req->ahb_req.tx_dir == 0)
++				drq_iter->ring_port =(unsigned long) drq_iter->src_port;
++			else
++				drq_iter->ring_port =(unsigned long) drq_iter->dst_port;
++
++		}
++
++		dmad_dbg("%s() ring: base(0x%08x) port(0x%08x) periods(0x%08x)"
++			 " period_size(0x%08x) period_bytes(0x%08x)"
++			 " remnant_size(0x%08x)\n",
++			 __func__, drq_iter->ring_base, drq_iter->ring_port,
++			 drq_iter->periods, drq_iter->period_size,
++			 drq_iter->period_bytes, drq_iter->remnant_size);
++	}
++
++	drq_iter->completion_cb = ch_req->completion_cb;
++	drq_iter->completion_data = ch_req->completion_data;
++
++	/* Initialize the channel && register isr */
++	err = dmad_channel_init(ch_req);
++
++_err_exit:
++
++	if (err != 0) {
++		spin_lock(&dmad.drq_pool_lock);
++
++		kfree(drq_iter->drb_pool);
++		memset(drq_iter, 0, sizeof(dmad_drq));
++
++		ch_req->channel = -1;
++		ch_req->drq = (void *)0;
++
++		spin_unlock(&dmad.drq_pool_lock);
++
++		dmad_err("Failed to initialize APB DMA! "
++			 "Channel allocation aborted!\n");
++	}
++
++	return err;
++}
++
++EXPORT_SYMBOL_GPL(dmad_channel_alloc);
++
++/**
++ * dmad_channel_free - release a dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * This function releases a DMA channel.  The channel is available for future
++ * allocation after the invokation.
++ */
++int dmad_channel_free(dmad_chreq * ch_req)
++{
++	dmad_drq *drq;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++	if (unlikely((ch_req->channel < 0) ||
++		     ((drq->state & DMAD_DRQ_STATE_READY) == 0))) {
++		dmad_err("try to free a free channel!\n");
++		return -EBADR;
++	}
++
++	/* Stop/abort channel I/O
++	 * (forced to shutdown and should be protected against isr)
++	 */
++	dmad_drain_requests(ch_req, 1);
++	dmad_channel_reset(ch_req);
++
++	dmad_dbg("freed channel: %d\n", ch_req->channel);
++
++	spin_lock(&dmad.drq_pool_lock);
++
++	kfree(drq->drb_pool);
++	memset(drq, 0, sizeof(dmad_drq));
++
++	ch_req->drq = 0;
++	ch_req->channel = (u32) - 1;
++
++	spin_unlock(&dmad.drq_pool_lock);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_channel_free);
++
++/**
++ * dmad_channel_enable - enable/disable a dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @enable : [in] 1 to enable the channel, 0 to disable
++ * @return : 0 if success, non-zero if any error
++ *
++ * Enable or disable the given DMA channel.
++ */
++int dmad_channel_enable(const dmad_chreq * ch_req, u8 enable)
++{
++	dmad_drq *drq;
++	unsigned long lock_flags;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL))
++		return -EFAULT;
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL))
++		return -EBADR;
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/* Enable/disable DMA channel */
++	if (enable)
++		dmad_enable_channel(drq);
++	else
++		dmad_disable_channel(drq);
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_channel_enable);
++
++/**
++ * dmad_config_channel_dir - config dma channel transfer direction
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @dir    : [in] DMAD_DRQ_DIR_A0_TO_A1 or DMAD_DRQ_DIR_A1_TO_A0
++ * @return : 0 if success, non-zero if any error
++ *
++ * Reconfigure the channel transfer direction.  This function works only if
++ * the channel was allocated with the DMAD_FLAGS_BIDIRECTION flags.  Note
++ * that bi-direction mode and ring mode are mutual-exclusive from user's
++ * perspective.
++ */
++int dmad_config_channel_dir(dmad_chreq * ch_req, u8 dir)
++{
++	dmad_drq *drq;
++	addr_t channel_cmds[1];
++	unsigned long lock_flags;
++	u8 cur_dir;
++
++	if (unlikely(ch_req == NULL))
++		return -EFAULT;
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL))
++		return -EBADR;
++
++	if (unlikely(!(ch_req->flags & DMAD_FLAGS_BIDIRECTION))) {
++		dmad_err("%s() Channel is not configured as"
++			 " bidirectional!\n", __func__);
++		return -EFAULT;
++	}
++
++	cur_dir = drq->flags & DMAD_DRQ_DIR_MASK;
++	if (dir == cur_dir) {
++		dmad_dbg("%s() cur_dir(%d) == dir(%d) skip reprogramming hw.\n",
++			 __func__, cur_dir, dir);
++		return 0;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	if (unlikely((drq->sbt_head != 0) /*||dmad_is_channel_enabled(drq) */ )) {
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		dmad_err("%s() Cannot change direction while the "
++			 "channel has pending requests!\n", __func__);
++		return -EFAULT;
++	}
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++		channel_cmds[0] = din((unsigned long)drq->enable_port);
++		ch_req->ahb_req.tx_dir = dir;
++		dmad_ahb_config_dir(ch_req, (unsigned long *)channel_cmds);
++		dout(channel_cmds[0], (unsigned long)drq->enable_port);
++	}
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_config_channel_dir);
++
++/**
++ * dmad_max_size_per_drb - return maximum transfer size per drb
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : The maximum transfer size per drb, in bytes.
++ *
++ * Calculate the maximum transfer size per drb according to the setting of
++ * data width during channel initialization.
++ *
++ * Return size is aligned to 4-byte boundary; this ensures the alignment
++ * requirement of dma starting address if the function was used in a loop to
++ * separate a large size dma transfer.
++ */
++u32 dmad_max_size_per_drb(dmad_chreq * ch_req)
++{
++	addr_t size = 0;
++	addr_t data_width = (addr_t) ((dmad_drq *) ch_req->drq)->data_width;
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++		size = DMAC_CYCLE_TO_BYTES(DMAC_TOT_SIZE_MASK & ((addr_t) ~ 3),
++					   data_width);
++	}
++
++	dmad_dbg("%s() - 0x%08x bytes\n", __func__, size);
++
++	return size;
++}
++
++EXPORT_SYMBOL_GPL(dmad_max_size_per_drb);
++
++/**
++ * dmad_bytes_to_cycles - calculate drb transfer size, in cycles
++ * @ch_req    : [in] Reference to the DMA request descriptor structure
++ * @byte_size : [in] The DMA transfer size to be converted, in bytes
++ * @return    : The drb transfer size, in cycles.
++ *
++ * Calculate the drb transfer cycle according to the setting of channel data
++ * width and burst setting.
++ *
++ * AHB DMA : unit is number of "data width".
++ * APB DMA : unit is number of "data width * burst size"
++ *
++ * APB Note: According to specification, decrement addressing seems to regard
++ *           the burst size setting.  For code efficiency,
++ *           dmad_make_req_cycles() does not take care of this case and might
++ *           produce wrong result.
++ */
++u32 dmad_bytes_to_cycles(dmad_chreq * ch_req, u32 byte_size)
++{
++	addr_t cycle = 0;
++	addr_t data_width = (addr_t) ((dmad_drq *) ch_req->drq)->data_width;
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++		cycle = DMAC_BYTES_TO_CYCLE(byte_size, data_width);
++	}
++
++	dmad_dbg("%s() - 0x%08x bytes --> 0x%08x cycles\n",
++		 __func__, byte_size, cycle);
++	return cycle;
++}
++
++EXPORT_SYMBOL_GPL(dmad_bytes_to_cycles);
++
++/**
++ * dmad_alloc_drb_internal - allocate a dma-request-block of a dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @drb    : [out] Reference to a drb pointer to receive the allocated drb
++ * @return : 0 if success, non-zero if any error
++ *
++ * Allocates a DRB (DMA request block) of the given DMA channel.  DRB is a
++ * single dma request which will be pushed into the submission queue of the
++ * given DMA channel.  This is a lightweight internal version of
++ * dmad_alloc_drb() majorly for use in ring mode.  Critical access to the
++ * drb pool should be protected before entering this function.
++ */
++static inline int dmad_alloc_drb_internal(dmad_drq * drq, dmad_drb ** drb)
++{
++	/* Initialize drb ptr in case of fail allocation */
++	*drb = NULL;
++
++	if (unlikely(drq->fre_head == 0)) {
++		return -EAGAIN;
++	}
++
++	dmad_detach_head(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb);
++
++	dmad_attach_tail(drq->drb_pool,
++			 &drq->rdy_head, &drq->rdy_tail, (*drb)->node);
++
++	(*drb)->state = DMAD_DRB_STATE_READY;
++	(*drb)->sync = 0;
++
++	dmad_dbg("%s() drb(%d 0x%08x)\n", __func__, (*drb)->node, (u32) (*drb));
++
++	return 0;
++}
++
++/**
++ * dmad_alloc_drb - allocate a dma-request-block of a dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @drb    : [out] Reference to a drb pointer to receive the allocated drb
++ * @return : 0 if success, non-zero if any error
++ *
++ * Allocates a DRB (DMA request block) of the given DMA channel.  DRB is a
++ * single dma request which will be pushed into the submission queue of the
++ * given DMA channel.
++ */
++int dmad_alloc_drb(dmad_chreq * ch_req, dmad_drb ** drb)
++{
++	dmad_drq *drq;
++	unsigned long lock_flags;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (likely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/* Initialize drb ptr in case of fail allocation */
++	*drb = NULL;
++
++	if (unlikely(drq->fre_head == 0)) {
++
++		drq->state &= (u32) ~ DMAD_DRQ_STATE_ABORT;
++
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++_wait_for_free_drbs:
++
++		/* Wait for free urbs */
++		if (drq->flags & DMAD_FLAGS_SLEEP_BLOCK) {
++
++			int timeout =
++			    wait_for_completion_interruptible_timeout(&drq->
++								      drb_alloc_sync,
++								      msecs_to_jiffies
++								      (6000));
++
++			/* reset sync object */
++			reinit_completion(&drq->drb_alloc_sync);
++
++			if (timeout < 0) {
++				dmad_err("%s() wait for"
++					 " completion error! (%d)\n",
++					 __func__, timeout);
++				return timeout;
++			}
++
++		} else if (drq->flags & DMAD_FLAGS_SPIN_BLOCK) {
++
++			u32 timeout = 0x00ffffff;
++
++			while ((drq->fre_head == 0) && (--timeout != 0)) {
++			}
++			if (timeout == 0) {
++				dmad_err("%s() polling wait for "
++					 "completion timeout!\n", __func__);
++				return -EAGAIN;
++			}
++
++		} else {
++			return -EAGAIN;
++		}
++
++		spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++		/* check whether all the requests of the channel has been
++		 * abandoned or not */
++		if (unlikely(drq->state & DMAD_DRQ_STATE_ABORT)) {
++			dmad_dbg("%s() drb-allocation aborted due"
++				 " to cancel-request ...\n", __func__);
++			drq->state &= (u32) ~ DMAD_DRQ_STATE_ABORT;
++			spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++			return -ECANCELED;
++		}
++
++		/* check again to avoid non-atomic operation between above
++		 * two calls */
++		if (unlikely(drq->fre_head == 0)) {
++			dmad_dbg("%s() lost free drbs ... "
++				 "continue waiting ...\n", __func__);
++			spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++			goto _wait_for_free_drbs;
++		}
++	}
++
++	dmad_detach_head(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb);
++
++	dmad_attach_tail(drq->drb_pool,
++			 &drq->rdy_head, &drq->rdy_tail, (*drb)->node);
++
++	(*drb)->state = DMAD_DRB_STATE_READY;
++	(*drb)->sync = 0;
++
++	dmad_dbg("%s() drb(%d 0x%08x)\n", __func__, (*drb)->node, (u32) (*drb));
++
++	drq->state &= (u32) ~ DMAD_DRQ_STATE_ABORT;
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_alloc_drb);
++
++/**
++ * dmad_free_drb - free a dma-request-block of a dma channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @drb    : [in] Reference to a drb to be freed
++ * @return : 0 if success, non-zero if any error
++ *
++ * Frees a DRB (DMA request block) of the given DMA channel.  DRB is a
++ * single dma request which will be pushed into the submission queue of the
++ * given DMA channel.
++ */
++int dmad_free_drb(dmad_chreq * ch_req, dmad_drb * drb)
++{
++	dmad_drq *drq;
++	unsigned long lock_flags;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/****************************************************
++	 * Following code requires _safe_exit return path
++	 */
++
++	if (unlikely((drq->rdy_head == 0) || (drb->node == 0) ||
++		     (drb->state != DMAD_DRB_STATE_READY) ||
++		     (drb->node >= DMAD_DRB_POOL_SIZE))) {
++		dmad_err("Ready-queue is empty or invalid node!\n");
++
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return -EBADR;
++	}
++
++	dmad_detach_node(drq->drb_pool,
++			 &drq->rdy_head, &drq->rdy_tail, drb->node);
++	dmad_attach_tail(drq->drb_pool,
++			 &drq->fre_head, &drq->fre_tail, drb->node);
++
++	drb->state = DMAD_DRB_STATE_FREE;
++	drb->sync = 0;
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_free_drb);
++
++/**
++ * dmad_submit_request_internal - submit a dma-request-block to the dma channel
++ * @ch_req     : [in] Reference to the DMA request descriptor structure
++ * @drb        : [in] Reference to a drb to be submitted
++ * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due
++ *                    to finishing its previous request
++ * @return     : 0 if success, non-zero if any error
++ *
++ * Submit a DRB (DMA request block) of the given DMA channel to submission
++ * queue.  DRB is a single dma request which will be pushed into the
++ * submission queue of the given DMA channel.  This is a lightweight internal
++ * version of dmad_alloc_drb() majorly for use in ring mode.  Critical access to
++ * the drb pool should be protected before entering this function.
++ */
++static inline int dmad_submit_request_internal(dmad_drq * drq, dmad_drb * drb)
++{
++	if (drb->state == DMAD_DRB_STATE_READY) {
++		/* Detach user node from ready list */
++		dmad_detach_node(drq->drb_pool,
++				 &drq->rdy_head, &drq->rdy_tail, drb->node);
++
++		dmad_attach_tail(drq->drb_pool,
++				 &drq->sbt_head, &drq->sbt_tail, drb->node);
++
++		drb->state = DMAD_DRB_STATE_SUBMITTED;
++
++		dmad_dbg("%s() submit drb(%d 0x%08x) addr0(0x%08x) "
++			 "addr1(0x%08x) size(0x%08x) state(%d)\n", __func__,
++			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++			 drb->req_cycle, drb->state);
++	} else {
++		dmad_dbg("%s() skip drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x)"
++			 " size(0x%08x) state(%d)\n", __func__,
++			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++			 drb->req_cycle, drb->state);
++	}
++
++	return 0;
++}
++
++/**
++ * dmad_submit_request - submit a dma-request-block to the dma channel
++ * @ch_req     : [in] Reference to the DMA request descriptor structure
++ * @drb        : [in] Reference to a drb to be submitted
++ * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due
++ *                    to finishing its previous request
++ * @return     : 0 if success, non-zero if any error
++ *
++ * Submit a DRB (DMA request block) of the given DMA channel to submission
++ * queue.  DRB is a single dma request which will be pushed into the
++ * submission queue of the given DMA channel.
++ */
++int dmad_submit_request(dmad_chreq * ch_req, dmad_drb * drb, u8 keep_fired)
++{
++	dmad_drq *drq;
++	unsigned long lock_flags;
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/******************************************************
++	 * Following code require _safe_exit return path
++	 */
++
++	if (unlikely((drq->rdy_head == 0) || (drb->node == 0) ||
++		     (drb->node >= DMAD_DRB_POOL_SIZE))) {
++					printk("node error\n");
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return -EBADR;
++	}
++
++	/* Detach user node from ready list */
++	dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail,
++			 drb->node);
++
++	/* Queue DRB to the end of the submitted list */
++	dmad_dbg("submit drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++		 "size(0x%08x) sync(0x%08x) fire(%d)\n",
++		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++		 drb->req_cycle, (u32) drb->sync, keep_fired);
++
++	/* Check if submission is performed to an empty queue */
++	if (unlikely(keep_fired && (drq->sbt_head == 0))) {
++		/* DMA is not running, so kick off transmission */
++		dmad_dbg("kickoff dma engine.\n");
++
++		dmad_attach_tail(drq->drb_pool,
++				 &drq->sbt_head, &drq->sbt_tail, drb->node);
++		/* Source and destination address */
++		if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) {
++			dout(drb->addr1, (unsigned long) drq->src_port);
++			dout(drb->addr0, (unsigned long) drq->dst_port);
++		} else {
++			dout(drb->addr0, (unsigned long) drq->src_port);
++			dout(drb->addr1, (unsigned long) drq->dst_port);
++		}
++
++		/* Transfer size (in units of source width) */
++		dout(drb->req_cycle, (unsigned long) drq->cyc_port);
++
++		/* Enable DMA channel (Kick off transmission when client
++		 * enable it's transfer state) */
++		dmad_enable_channel(drq);
++		drb->state = DMAD_DRB_STATE_EXECUTED;
++	} else {
++		/* DMA is already running, so only queue DRB to the end of the
++		 * list */
++		dmad_attach_tail(drq->drb_pool,
++				 &drq->sbt_head, &drq->sbt_tail, drb->node);
++		drb->state = DMAD_DRB_STATE_SUBMITTED;
++	}
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_submit_request);
++
++/**
++ * dmad_withdraw_request - cancel a submitted dma-request-block
++ * @ch_req     : [in] Reference to the DMA request descriptor structure
++ * @drb        : [in] Reference to a drb to be submitted
++ * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due
++ *                    to finishing its previous request
++ * @return     : 0 if success, non-zero if any error
++ *
++ * Cancel a submitted DRB (DMA request block) of the given DMA channel in its
++ * submission queue.  DRB is a single dma request which will be pushed into the
++ * submission queue of the given DMA channel. Cancellation fails if the DRB has
++ * already been kicked off.
++ */
++int dmad_withdraw_request(dmad_chreq * ch_req, dmad_drb * drb)
++{
++	dmad_drq *drq = 0;
++	unsigned long lock_flags;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++
++	if (unlikely(drq->sbt_head == 0))
++		return -EBADR;
++
++	if (unlikely((drb->node == 0) || (drb->node >= DMAD_DRB_POOL_SIZE)))
++		return -EBADR;
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	if (unlikely((drq->sbt_head == 0) || (drb->node == 0) ||
++		     (drb->state != DMAD_DRB_STATE_SUBMITTED) ||
++		     (drb->node >= DMAD_DRB_POOL_SIZE))) {
++		dmad_err("Submitted-queue is empty or invalid node!\n");
++
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return -EBADR;
++	}
++
++	dmad_dbg("cancel drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++		 "size(0x%08x) state(%d)\n",
++		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++		 drb->req_cycle, drb->state);
++
++	if (unlikely(drb->state == DMAD_DRB_STATE_EXECUTED)) {
++		dmad_dbg("Already running drb cannot be stopped currently!\n");
++
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return 0;/*-EBADR; */
++	}
++
++	dmad_detach_node(drq->drb_pool,
++			 &drq->rdy_head, &drq->rdy_tail, drb->node);
++	dmad_attach_tail(drq->drb_pool,
++			 &drq->fre_head, &drq->fre_tail, drb->node);
++
++	drb->state = DMAD_DRB_STATE_FREE;
++
++	if (drb->sync)
++		complete_all(drb->sync);
++	drb->sync = 0;
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_withdraw_request);
++
++/**
++ * dmad_kickoff_requests_internal - kickoff hw DMA transmission
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * Kickoff hw DMA transmission of the given DMA channel.  This function is
++ * valid for both ring & non-ring mode.  This is a lightweight internal version
++ * of dmad_kickoff_requests() majorly for use in ring mode.  Critical access to
++ * the drb pool should be protected before entering this function.
++ */
++static inline int dmad_kickoff_requests_internal(dmad_drq * drq)
++{
++	dmad_drb *drb;
++
++	dmad_get_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
++
++	if (!drb) {
++		dmad_err("%s() null drb!\n", __func__);
++		return -EBADR;
++	}
++
++	dmad_dbg("%s() drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++		 "size(0x%08x) state(%d)\n", __func__,
++		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++		 drb->req_cycle, drb->state);
++
++	if (drb->state == DMAD_DRB_STATE_SUBMITTED) {
++		/* Transfer size (in units of source width) */
++		dout(drb->req_cycle, (unsigned long) drq->cyc_port);
++
++		/* Source and destination address */
++		if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) {
++			dout(drb->addr1, (unsigned long) drq->src_port);
++			dout(drb->addr0, (unsigned long) drq->dst_port);
++		} else {
++			dout(drb->addr0, (unsigned long) drq->src_port);
++			dout(drb->addr1, (unsigned long) drq->dst_port);
++		}
++
++		drb->state = DMAD_DRB_STATE_EXECUTED;
++	}
++
++	/* Enable DMA channel */
++	if (!dmad_is_channel_enabled(drq)) {
++		dmad_enable_channel(drq);
++	}
++
++	return 0;
++}
++
++/**
++ * dmad_kickoff_requests - kickoff hw DMA transmission of the given DMA channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : 0 if success, non-zero if any error
++ *
++ * Kickoff hw DMA transmission of the given DMA channel.  This function is
++ * valid for both ring & non-ring mode.
++ */
++int dmad_kickoff_requests(dmad_chreq * ch_req)
++{
++	dmad_drq *drq = 0;
++	dmad_drb *drb = 0;
++	unsigned long lock_flags;
++	dma_addr_t req_cycle;
++
++	dmad_dbg("%s()\n", __func__);
++
++	if (unlikely(ch_req == NULL)) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	dmad_get_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
++
++	dmad_dbg("drq(0x%08x) channel_base(0x%08x)\n",
++		 (u32) drq, drq->channel_base);
++	dmad_dbg("kick off drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++		 "size(0x%08x) state(%d) a1_to_a0(%d)\n",
++		 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
++		 drb->req_cycle, drb->state,
++		 drq->flags & DMAD_DRQ_DIR_A1_TO_A0);
++
++	/* do nothing if no drbs are in the submission queue */
++	if (unlikely((drb == 0) || (drb->state != DMAD_DRB_STATE_SUBMITTED))) {
++		dmad_dbg("%s() invalid drb(%d 0x%08x) or drb-state(%d)!\n",
++			 __func__,
++			 drb->node, (u32) drb, drb ? drb->state : 0xffffffff);
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return 0;
++	}
++
++	req_cycle = drb->req_cycle;
++
++	if (unlikely(req_cycle == 0)) {
++		dmad_dbg("%s() zero transfer size!\n", __func__);
++		goto _safe_exit;
++	}
++
++	/* Transfer size (in units of source width) */
++	dout(req_cycle, (unsigned long) drq->cyc_port);
++
++	/* Source and destination address */
++	if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) {
++		dout(drb->addr1, (unsigned long) drq->src_port);
++		dout(drb->addr0, (unsigned long) drq->dst_port);
++	} else {
++		dout(drb->addr0, (unsigned long) drq->src_port);
++		dout(drb->addr1, (unsigned long) drq->dst_port);
++	}
++
++	drb->state = DMAD_DRB_STATE_EXECUTED;
++
++	/* Enable DMA channel */
++	dmad_enable_channel(drq);
++
++_safe_exit:
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_kickoff_requests);
++
++/**
++ * dmad_probe_hw_ptr_src - probe DMA source hw-address of the given channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : physical address of current HW source pointer
++ *
++ * Probe DMA source hw-address of the given channel.
++ */
++dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq * ch_req)
++{
++	return (dma_addr_t) din(((dmad_drq *) ch_req->drq)->src_port);
++}
++
++EXPORT_SYMBOL_GPL(dmad_probe_hw_ptr_src);
++
++/**
++ * dmad_probe_hw_ptr_dst - probe DMA destination hw-address of the given channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @return : physical address of current HW destination pointer
++ *
++ * Probe DMA destination hw-address of the given channel.
++ */
++dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq * ch_req)
++{
++	return (dma_addr_t) din(((dmad_drq *) ch_req->drq)->dst_port);
++}
++
++EXPORT_SYMBOL_GPL(dmad_probe_hw_ptr_dst);
++
++/**
++ * dmad_update_ring - update DMA ring buffer base && size of the given channel
++ * @ch_req : [in] Reference to the DMA request descriptor structure
++ * @size   : [in] The new ring buffer size, in unit of data width (cycles)
++ * @return : 0 if success, non-zero if any error
++ *
++ * Update DMA ring buffer size of the given channel.  This function is valid
++ * only if the channel is initialized as ring buffer mode.
++ */
++int dmad_update_ring(dmad_chreq * ch_req)
++{
++	unsigned long lock_flags;
++	dmad_drq *drq = (dmad_drq *) ch_req->drq;
++	int remnant;
++
++	if (unlikely(dmad_is_channel_enabled(drq))) {
++		dmad_err("%s() Error - dma channel should be "
++			 "disabled before updating ring size!\n", __func__);
++		return -EFAULT;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/* todo: range checking */
++
++	remnant = (int)ch_req->ring_size -
++	    (int)ch_req->periods * (int)ch_req->period_size;
++	if (remnant == 0) {
++		drq->periods = ch_req->periods;
++	} else if (remnant > 0) {
++		drq->periods = ch_req->periods;	// + 1;
++	} else {
++		dmad_err("%s() Error - buffer_size < "
++			 "periods * period_size!\n", __func__);
++		spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++		return -EFAULT;
++	}
++
++	drq->ring_base = ch_req->ring_base;
++	drq->ring_size = ch_req->ring_size;
++	drq->period_size = ch_req->period_size;
++	drq->remnant_size = (dma_addr_t) remnant;
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
++		drq->period_bytes =
++		    DMAC_CYCLE_TO_BYTES(drq->period_size, drq->data_width);
++	}
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	dmad_dbg("%s() ring: base(0x%08x) port(0x%08x) periods(0x%08x) "
++		 "period_size(0x%08x) period_bytes(0x%08x) "
++		 "remnant_size(0x%08x)\n",
++		 __func__, drq->ring_base, drq->ring_port,
++		 drq->periods, drq->period_size, drq->period_bytes,
++		 drq->remnant_size);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_update_ring);
++
++/**
++ * dmad_update_ring_sw_ptr - update DMA ring buffer sw-pointer
++ * @ch_req     : [in] Reference to the DMA request descriptor structure
++ * @sw_ptr     : [in] The new sw-pointer for the hw-pointer to chase of
++ * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due
++ *                    to finishing its previous request
++ * @return     : 0 if success, non-zero if any error
++ *
++ * Update DMA ring buffer sw-pointer of the given channel on the fly.  This
++ * function is valid only if the channel is initialized as ring buffer mode.
++ * Uint of sw_ptr is in number of dma data width.
++ */
++int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
++			    dma_addr_t sw_ptr, u8 keep_fired)
++{
++	dmad_drq *drq;
++	unsigned long lock_flags;
++	dma_addr_t hw_off = 0, ring_ptr;
++	dma_addr_t sw_p_off, ring_p_off, period_bytes;
++	dma_addr_t remnant_size;
++	int period_size;
++	int sw_p_idx, ring_p_idx, period, periods;
++	dmad_drb *drb = NULL;
++
++	/*if (ch_req == NULL) { */
++	/*    dmad_dbg("%s() null ch_req!\n", __func__); */
++	/*    return -EFAULT; */
++	/*} */
++
++	drq = (dmad_drq *) ch_req->drq;
++
++	/*if (drq == NULL) { */
++	/*    dmad_dbg("%s() null ch_req->drq!\n", __func__); */
++	/*    return -EBADR; */
++	/*} */
++
++	if (unlikely(sw_ptr > drq->ring_size)) {
++//		dmad_err("%s() Invalid ring buffer sw-pointer ");
++		return -EBADR;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	periods = drq->periods;
++	period_size = drq->period_size;
++	period_bytes = drq->period_bytes;
++	remnant_size = drq->remnant_size;
++
++	ring_ptr = drq->sw_ptr;
++	ring_p_idx = drq->sw_p_idx;
++	ring_p_off = drq->sw_p_off;
++
++	sw_p_idx = div_u64(sw_ptr, period_size);
++	__iter_div_u64_rem(sw_ptr, period_size, &sw_p_off);
++
++	if (remnant_size && (sw_p_idx == periods)) {
++		--sw_p_idx;
++		sw_p_off += period_size;
++	}
++
++	dmad_dbg("%s() ring_ptr(0x%08x) ring_p_idx(0x%08x) "
++		 "ring_p_off(0x%08x)\n",
++		 __func__, ring_ptr, ring_p_idx, ring_p_off);
++	dmad_dbg("%s() sw_ptr(0x%08x) sw_p_idx(0x%08x) sw_p_off(0x%08x)\n",
++		 __func__, sw_ptr, sw_p_idx, sw_p_off);
++
++	if (drq->ring_drb &&
++	    (drq->ring_drb->state & (DMAD_DRB_STATE_READY |
++				     DMAD_DRB_STATE_SUBMITTED |
++				     DMAD_DRB_STATE_EXECUTED))) {
++		drb = drq->ring_drb;
++	} else {
++		/* alloc new drb if there is none yet at ring_ptr */
++		if (0 != dmad_alloc_drb_internal(drq, &drb)) {
++			dmad_err("%s() drb allocation failed!\n", __func__);
++			spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++			return -ENOSPC;
++		}
++		drb->addr0 = ((dma_addr_t) ring_p_idx * period_bytes) +
++		    drq->ring_base;
++		drb->addr1 = drq->dev_addr;
++		drb->req_cycle = 0;	// redundent, though, no harm to performance
++
++		dmad_dbg("init_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++			 "size(0x%08x) state(%d)\n",
++			 (u32) drb->node, (u32) drb, drb->src_addr,
++			 drb->dst_addr, drb->req_cycle, drb->state);
++
++		drq->ring_drb = drb;
++	}
++
++	/* Following code-path has been optimized.  The design flow is expanded
++	 * below for reference.
++	 *
++	 *   if (sw_ptr >= ring_ptr)
++	 *       if (sw_p_idx == ring_p_idx)
++	 *           ring_drb::req_cycle <- sw_p_off
++	 *           if (ring_drb::state == executed)
++	 *               hw_cycle <- sw_p_idx
++	 *           fi
++	 *       else
++	 *           ring_drb::req_cycle <- period_size
++	 *           if (ring_drb::state == executed)
++	 *               hw_cycle <- period_size
++	 *           fi
++	 *           for (i = ring_p_idx+1 ~ sw_p_idx-1)
++	 *               new_drb::ring_addr <- i * period_bytes + ring_base
++	 *               new_drb::req_cycle <- period_size
++	 *           rof
++	 *           sw_drb::ring_addr <- sw_p_idx * period_bytes + ring_base
++	 *           sw_drb::req_cycle <- sw_p_off
++	 *   else
++	 *       // sw_ptr < ring_ptr
++	 *       ring_drb::req_cycle <- period_size
++	 *       if (ring_drb::state == executed)
++	 *           hw_cycle <- period_size
++	 *       fi
++	 *       for (i = ring_p_idx+1 ~ idx_max)
++	 *           new_drb::ring_addr <- i * period_bytes + ring_base
++	 *           new_drb::req_cycle <- period_size
++	 *       rof
++	 *       for (i = 0 ~ sw_p_idx-1)
++	 *           new_drb::ring_addr <- i * period_bytes + ring_base
++	 *           new_drb::req_cycle <- period_size
++	 *       rof
++	 *       sw_drb::ring_addr <- sw_p_idx * period_bytes + ring_base
++	 *       sw_drb::req_cycle <- sw_p_off
++	 *   fi
++	 */
++	if ((sw_ptr >= ring_ptr) && (sw_p_idx == ring_p_idx) && (sw_p_off != 0)) {
++
++		dmad_dbg("update ring drb\n");
++
++		/* update drb size at ring_ptr */
++		drb->req_cycle = sw_p_off;
++
++		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++			 "size(0x%08x) state(%d)\n",
++			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
++			 drb->req_cycle, drb->state);
++
++		/* update hw dma size of this drb if it has been sent to the
++		 * controller */
++		if (drb->state == DMAD_DRB_STATE_EXECUTED) {
++			dmad_disable_channel(drq);
++
++			if (ch_req->controller == DMAD_DMAC_AHB_CORE)
++				hw_off = DMAC_BYTES_TO_CYCLE((addr_t)
++							     din((addr_t) drq->
++								 ring_port) -
++							     (addr_t) drb->
++							     addr0,
++							     drq->data_width);
++
++			dmad_dbg("hw_off(0x%08x) sw_p_off(0x%08x)\n",
++				 (u32) hw_off, (u32) sw_p_off);
++
++			if (sw_p_off < hw_off)
++				dmad_err("%s() underrun! sw_p_off(0x%08x) <"
++					 " hw_off(0x%08x)\n", __func__,
++					 (u32) sw_p_off, (u32) hw_off);
++			else
++				dout(sw_p_off - hw_off, (unsigned long)drq->cyc_port);
++
++			dmad_enable_channel(drq);
++
++		} else {
++			dmad_submit_request_internal(drq, drb);
++		}
++
++	} else {
++
++		dmad_dbg("fulfill ring drb - sw_ptr(0x%08x) ring_ptr(0x%08x)\n",
++			 (u32) sw_ptr, (u32) ring_ptr);
++
++		/* fulfill last drb at ring_ptr */
++		if (ring_p_idx == (periods - 1))
++			drb->req_cycle = period_size + remnant_size;
++		else
++			drb->req_cycle = period_size;
++
++		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++			 "size(0x%08x) state(%d)\n",
++			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
++			 drb->req_cycle, drb->state);
++
++		if (drb->state == DMAD_DRB_STATE_EXECUTED) {
++			dmad_disable_channel(drq);
++
++			if (ch_req->controller == DMAD_DMAC_AHB_CORE)
++				hw_off = DMAC_BYTES_TO_CYCLE((addr_t)
++							     din((addr_t) drq->
++								 ring_port) -
++							     (addr_t) drb->
++							     addr0,
++							     drq->data_width);
++
++			dmad_dbg("hw_off(0x%08x) period_size(0x%08x)\n",
++				 (u32) hw_off, (u32) period_size);
++
++			if (ring_p_idx == (periods - 1)) {
++				if (period_size < hw_off)
++					dmad_err("%s() illegal! "
++						 "period_size(0x%08x) + "
++						 "remnant_size(0x%08x) < "
++						 "hw_off(0x%08x)\n", __func__,
++						 (u32) period_size,
++						 (u32) remnant_size,
++						 (u32) hw_off);
++				else
++					dout(period_size + remnant_size -
++					     hw_off, (unsigned long)drq->cyc_port);
++			} else {
++				if (period_size < hw_off)
++					dmad_err("%s() illegal! "
++						 "period_size(0x%08x) < "
++						 "hw_off(0x%08x)\n", __func__,
++						 (u32) period_size,
++						 (u32) hw_off);
++				else
++					dout(period_size - hw_off,
++					     (unsigned long)drq->cyc_port);
++			}
++
++			dmad_enable_channel(drq);
++
++		} else {
++			dmad_submit_request_internal(drq, drb);
++		}
++
++		++ring_p_idx;
++
++		/* adjust sw_ptr period index ahead by one ring cycle */
++		//if (sw_ptr < ring_ptr) {
++		if (sw_p_idx < ring_p_idx) {
++			sw_p_idx += periods;
++		}
++
++		/* allocate in-between (ring_ptr+1 to sw_ptr-1)
++		 * full-cycle drbs */
++		for (period = ring_p_idx; period < sw_p_idx; ++period) {
++			if (0 != dmad_alloc_drb_internal(drq, &drb)) {
++				dmad_err("%s() drb allocation failed!\n",
++					 __func__);
++				spin_unlock_irqrestore(&drq->drb_pool_lock,
++						       lock_flags);
++				return -ENOSPC;
++			}
++
++			drb->addr0 = (dma_addr_t) (period % periods) *
++			    period_bytes + drq->ring_base;
++			drb->addr1 = drq->dev_addr;
++
++			if (period == (periods - 1)) {
++				drb->req_cycle = period_size + remnant_size;
++			} else {
++				drb->req_cycle = period_size;
++			}
++
++			dmad_dbg("inbtw_drb(%d 0x%08x) addr0(0x%08x) "
++				 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++				 (u32) drb->node, (u32) drb, drb->addr0,
++				 drb->addr1, drb->req_cycle, drb->state);
++
++			dmad_submit_request_internal(drq, drb);
++		}
++
++		/* allocate drb right at sw_ptr */
++		if (0 != dmad_alloc_drb_internal(drq, &drb)) {
++			dmad_err("%s() drb allocation failed!\n", __func__);
++			spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++			return -ENOSPC;
++		}
++		drb->addr0 = (dma_addr_t) (sw_p_idx % periods) *
++		    period_bytes + drq->ring_base;
++		drb->addr1 = drq->dev_addr;
++		drb->req_cycle = sw_p_off;
++
++		dmad_dbg("swptr_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
++			 "size(0x%08x) state(%d)\n",
++			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
++			 drb->req_cycle, drb->state);
++
++		drq->ring_drb = drb;
++
++		if (sw_p_off > 0)
++			dmad_submit_request_internal(drq, drb);
++	}
++
++	__iter_div_u64_rem(sw_ptr, drq->ring_size, &drq->sw_ptr);
++	drq->sw_p_idx = sw_p_idx % periods;
++	drq->sw_p_off = sw_p_off;
++
++	if (likely(keep_fired)) {
++		dmad_kickoff_requests_internal(drq);
++	}
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(dmad_update_ring_sw_ptr);
++
++/**
++ * dmad_probe_ring_hw_ptr - probe DMA ring buffer position of the given channel
++ * @ch_req     : [in] Reference to the DMA request descriptor structure
++ * @return     : Ring buffer position of current HW ring buffer pointer
++ *
++ * Probe DMA ring buffer position of the given channel.  The position is
++ * relative to the ring buffer base.  This function is valid only if the
++ * channel is initialized as ring buffer mode.
++ */
++dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq * ch_req)
++{
++	dmad_drq *drq = (dmad_drq *) ch_req->drq;
++	dma_addr_t cycles =
++	    (dma_addr_t) din(drq->ring_port) - (dma_addr_t) drq->ring_base;
++
++	if (ch_req->controller == DMAD_DMAC_AHB_CORE)
++		cycles = DMAC_BYTES_TO_CYCLE(cycles, drq->data_width);
++
++	return cycles;
++}
++
++EXPORT_SYMBOL_GPL(dmad_probe_ring_hw_ptr);
++
++/**
++ * dmad_channel_drain - cancel DMA transmission of the given DMA channel
++ * @controller : [in] One of the enum value of DMAD_DMAC_CORE
++ * @drq        : [in] Reference to the DMA queue structure (dmad_drq)
++ * @shutdown   : [in] Non-zero to force a immediate channel shutdown
++ * @return     : 0 if success, non-zero if any error
++ *
++ * Stop the DMA transmission and cancel all submitted requests of the given
++ * DMA channel.  This function drains a single channel and is the internal
++ * implementation of the interface routine dmad_drain_requests() and the
++ * module_exit function.
++ */
++static int dmad_channel_drain(u32 controller, dmad_drq * drq, u8 shutdown)
++{
++	dmad_drb *drb = 0;
++	unsigned long lock_flags;
++
++	if (unlikely(drq == NULL)) {
++		dmad_err("null ch_req->drq!\n");
++		return -EBADR;
++	}
++
++	spin_lock_irqsave(&drq->drb_pool_lock, lock_flags);
++
++	/* Stop DMA channel if forced to shutdown immediately */
++	if (shutdown) {
++		/* disable dma controller */
++		dmad_reset_channel(drq);
++
++		/* todo: more settings to stop DMA controller ?? */
++
++		/*if (drb->state == DMAD_DRB_STATE_EXECUTED) { */
++		/*} */
++	}
++
++	/* Detach DRBs in submit queue */
++	dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
++
++	while (drb) {
++		dmad_dbg("cancel sbt drb(%d 0x%08x) addr0(0x%08x) "
++			 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++			 drb->req_cycle, (u32) drb->state);
++
++		/* Mark DRB state as abort */
++		drb->state = DMAD_DRB_STATE_ABORT;
++
++		if (drb->sync)
++			complete_all(drb->sync);
++
++		dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail,
++				 drb->node);
++
++		dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail,
++				 &drb);
++	}
++
++	/* Detach DRBs in ready queue */
++	dmad_detach_head(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, &drb);
++
++	while (drb) {
++		dmad_dbg("cancel rdy drb(%d 0x%08x) addr0(0x%08x) "
++			 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
++			 drb->req_cycle, (u32) drb->state);
++
++		/* Mark DRB state as abort */
++		drb->state = DMAD_DRB_STATE_ABORT;
++
++		dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail,
++				 drb->node);
++
++		/* Detach next submitted DRB (DMA Request Block) from the
++		 * DRQ (DMA Request Queue) */
++		dmad_detach_head(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail,
++				 &drb);
++	}
++
++	drq->state |= DMAD_DRQ_STATE_ABORT;
++
++	drq->ring_drb = NULL;
++	drq->sw_ptr = 0;
++	drq->sw_p_idx = 0;
++	drq->sw_p_off = 0;
++
++	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
++
++	if ( /*(drq->fre_head == 0) && */ (drq->flags & DMAD_FLAGS_SLEEP_BLOCK)) {
++		complete_all(&drq->drb_alloc_sync);
++	}
++
++	return 0;
++}
++
++/**
++ * dmad_cancel_requests - cancel DMA transmission of the given DMA channel
++ * @ch_req   : [in] Reference to the DMA request descriptor structure
++ * @shutdown : [in] Non-zero to force a immediate channel shutdown
++ * @return   : 0 if success, non-zero if any error
++ *
++ * Stop the DMA transmission and cancel all submitted requests of the given
++ * DMA channel.
++ */
++int dmad_drain_requests(dmad_chreq * ch_req, u8 shutdown)
++{
++	dmad_dbg("%s()\n", __func__);
++
++	if (ch_req == NULL) {
++		dmad_err("null ch_req!\n");
++		return -EFAULT;
++	}
++
++	return dmad_channel_drain(ch_req->controller, ch_req->drq, shutdown);
++}
++
++EXPORT_SYMBOL_GPL(dmad_drain_requests);
++
++/**
++ * dmad_probe_irq_source - probe DMA channel who asserts the shared sw-irq line
++ * @return : The channel number which asserts the shared sw-irq line
++ *
++ * Probe DMA channel who asserts the shared sw-irq line.
++ */
++int dmad_probe_irq_source_ahb(void)
++{
++	int channel;		/* interrupt channel number */
++
++	/* todo: spin_lock */
++
++	/* - Check DMA status register to get channel number */
++	for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) {
++		if (getbl(channel+TC_OFFSET, (unsigned long)INT_STA))
++			return channel;
++	}
++
++	/* Perform DMA error checking if no valid channel was found who
++	 * assert the finish signal. */
++	for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) {
++		if (getbl(channel+ERR_OFFSET, (unsigned long)INT_STA))
++			return channel;
++		if (getbl(channel+ABT_OFFSET, (unsigned long)INT_STA))
++			return channel;
++	}
++
++	/* todo: spin_unlock */
++
++	return -EFAULT;
++}
++
++EXPORT_SYMBOL_GPL(dmad_probe_irq_source_ahb);
++
++
++/**
++ * dmad_module_init - dma module-init function
++ * @return : 0 if success, non-zero if any error
++ */
++int dmad_module_init(void)
++{
++	int err = 0;
++	dmad_dbg("%s() >>\n", __func__);
++
++	/* clear device struct since the module may be load/unload many times */
++	memset(&dmad, 0, sizeof(dmad)-4);
++	dmad.drq_pool =
++	    kmalloc(DMAD_AHB_MAX_CHANNELS * sizeof(dmad_drq), GFP_KERNEL);
++	if (dmad.drq_pool == NULL) {
++		dmad_err("%s() failed to allocate drb pool!\n", __func__);
++		return -ENOMEM;
++	}
++	memset(dmad.drq_pool, 0,DMAD_AHB_MAX_CHANNELS*sizeof(dmad_drq));
++	spin_lock_init(&dmad.drq_pool_lock);
++	dmad.ahb_drq_pool = dmad.drq_pool;
++	dmad_dbg("DMA module init result: (%d)\n", err);
++	dmad_dbg("  AHB channels: %d\n;"
++		 "DRBs per channel: %d\n",
++		 DMAC_MAX_CHANNELS, DMAD_DRB_POOL_SIZE);
++
++	dmad_dbg("%s() return code (%d) <<\n", __func__, err);
++	return err;
++}
++
++/**
++ * dmad_module_init - dma module clean up function
++ */
++int __exit dmad_module_exit(struct platform_device *pdev)
++{
++	dmad_drq *drq;
++	u32 channel;
++	struct at_dma_platform_data *pdata;
++	pdata = dev_get_platdata(&pdev->dev);
++
++	dmad_dbg("%s() >>\n", __func__);
++
++	spin_lock(&dmad.drq_pool_lock);
++
++	/* cancel existing requests and unregister interrupt handler */
++	for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) {
++
++		/* shutdown dma requests */
++		drq = (dmad_drq *) & dmad.ahb_drq_pool[channel];
++
++		if ((drq->state & DMAD_DRQ_STATE_READY) != 0)
++			dmad_channel_drain(DMAD_DMAC_AHB_CORE, drq, 1);
++
++		/* free registered irq handlers */
++		free_irq(ahb_irqs[channel], (void *)(unsigned long)(channel + 1));
++	}
++
++	spin_unlock(&dmad.drq_pool_lock);
++
++	if (dmad.drq_pool)
++		kfree(dmad.drq_pool);
++	memset(&dmad, 0, sizeof(dmad));
++
++	/* release I/O space */
++	release_region((uintptr_t)pdata->dmac_regs, resource_size(pdata->io));
++	dmad_dbg("DMA module unloaded!\n");
++
++	return 0;
++}
++
++#ifdef CONFIG_OF
++static const struct of_device_id atcdma100_of_id_table[] = {
++	{ .compatible = "andestech,atcdmac300" },
++	{}
++};
++MODULE_DEVICE_TABLE(of, atcdma100_of_id_table);
++
++static struct at_dma_platform_data *
++at_dma_parse_dt(struct platform_device *pdev)
++{
++	struct device_node *np = pdev->dev.of_node;
++	struct at_dma_platform_data *pdata;
++
++	if (!np) {
++		dev_err(&pdev->dev, "Missing DT data\n");
++		return NULL;
++	}
++
++	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return NULL;
++
++	if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
++		return NULL;
++
++	return pdata;
++}
++#else
++static inline struct at_dma_platform_data *
++at_dma_parse_dt(struct platform_device *pdev)
++{
++	return NULL;
++}
++#endif
++
++static int atcdma_probe(struct platform_device *pdev)
++{
++	struct at_dma_platform_data *pdata;
++	struct resource 	*io=0;
++	struct resource *mem = NULL;
++	int			irq;
++
++	pdata = dev_get_platdata(&pdev->dev);
++	dmad.plat = pdev;
++
++	if (!pdata)
++		pdata = at_dma_parse_dt(pdev);
++	pdev->dev.platform_data = pdata;
++	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	pdata->io = io;
++	mem = request_mem_region(io->start, resource_size(io), pdev->name);
++
++	if (!mem){
++		printk("failed to get io memory region resouce.\n");
++		return -EINVAL;
++	}
++
++	pdata->dmac_regs = (void __iomem *) ioremap(mem->start, resource_size(io));
++	dmac_base = (uintptr_t)pdata->dmac_regs;
++
++	irq = platform_get_irq(pdev, 0);
++
++	if (irq < 0)
++		return irq;
++
++	intc_ftdmac020_init_irq(irq);
++
++	return dmad_module_init();
++
++}
++
++static int __exit atcdma_remove(struct platform_device *pdev)
++{
++	return dmad_module_exit(pdev);
++}
++
++
++static struct platform_driver atcdma100_driver = {
++	.probe		= atcdma_probe,
++	.remove		= __exit_p(atcdma_remove),
++	.driver = {
++		.name	= "atcdmac100",
++		.of_match_table = of_match_ptr(atcdma100_of_id_table),
++	},
++};
++
++static int __init atcdma_init(void)
++{
++	return platform_driver_register(&atcdma100_driver);
++}
++subsys_initcall(atcdma_init);
++
++#endif /* CONFIG_PLATFORM_AHBDMA */
+diff --git a/arch/riscv/platforms/dmad_intc.c b/arch/riscv/platforms/dmad_intc.c
+new file mode 100644
+index 000000000000..5f831add709a
+--- /dev/null
++++ b/arch/riscv/platforms/dmad_intc.c
+@@ -0,0 +1,49 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2018 Andes Technology Corporation
++ *
++ */
++
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <asm/io.h>
++#include <asm/dmad.h>
++
++#ifdef CONFIG_PLATFORM_AHBDMA
++extern int dmad_probe_irq_source_ahb(void);
++void AHBDMA_irq_rounter(struct irq_desc *desc)
++{
++	int ahb_irq;
++	struct irq_desc *ahb_desc;
++
++	raw_spin_lock(&desc->lock);
++	ahb_irq = dmad_probe_irq_source_ahb();
++
++	if (ahb_irq >= 0) {
++		ahb_irq += DMA_IRQ0;
++		ahb_desc = irq_to_desc(ahb_irq);
++		ahb_desc->irq_data.irq = ahb_irq;
++		raw_spin_unlock(&desc->lock);
++		ahb_desc->handle_irq(ahb_desc);
++		raw_spin_lock(&desc->lock);
++	}
++	desc->irq_data.chip->irq_unmask(&desc->irq_data);
++	raw_spin_unlock(&desc->lock);
++}
++
++int intc_ftdmac020_init_irq(int irq)
++{
++	int i;
++	int ret;
++	/* Register all IRQ */
++	for (i = DMA_IRQ0;
++	     i < DMA_IRQ0 + DMA_IRQ_COUNT; i++) {
++		// level trigger
++		ret = irq_set_chip(i, &dummy_irq_chip);
++		irq_set_handler(i, handle_simple_irq);
++	}
++	irq_set_chained_handler(irq, AHBDMA_irq_rounter);
++	return 0;
++}
++#endif /* CONFIG_PLATFORM_AHBDMA */
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0004-Andes-support-for-FTSDC.patch b/board/andes/ae350/patches/linux/0004-Andes-support-for-FTSDC.patch
new file mode 100644
index 0000000000..9e0fd7303b
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0004-Andes-support-for-FTSDC.patch
@@ -0,0 +1,1884 @@
+From 8ffdbcee891ea3b127f34d0ff745d7578b487a71 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:16:45 +0800
+Subject: [PATCH 04/12] Andes support for FTSDC
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ drivers/mmc/host/Kconfig    |   11 +-
+ drivers/mmc/host/Makefile   |    1 +
+ drivers/mmc/host/ftsdc010.c | 1557 +++++++++++++++++++++++++++++++++++
+ drivers/mmc/host/ftsdc010.h |  257 ++++++
+ 4 files changed, 1825 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/mmc/host/ftsdc010.c
+ create mode 100644 drivers/mmc/host/ftsdc010.h
+
+diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
+index 30ff42fd173e..834cdb8b73cc 100644
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -503,7 +503,7 @@ config MMC_OMAP_HS
+
+ config MMC_WBSD
+	tristate "Winbond W83L51xD SD/MMC Card Interface support"
+-	depends on ISA_DMA_API && !M68K
++	depends on ISA_DMA_API
+	help
+	  This selects the Winbond(R) W83L51xD Secure digital and
+	  Multimedia card Interface.
+@@ -608,6 +608,15 @@ config MMC_DAVINCI
+	  If you have an DAVINCI board with a Multimedia Card slot,
+	  say Y or M here.  If unsure, say N.
+
++config MMC_FTSDC
++	tristate "Andestech SDC Multimedia Card Interface support"
++	depends on RISCV
++	help
++	  This selects the TI DAVINCI Multimedia card Interface.
++	  If you have an DAVINCI board with a Multimedia Card slot,
++	  say Y or M here.  If unsure, say N.
++
++
+ config MMC_GOLDFISH
+	tristate "goldfish qemu Multimedia Card Interface support"
+	depends on GOLDFISH || COMPILE_TEST
+diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
+index 451c25fc2c69..fbc58512f23a 100644
+--- a/drivers/mmc/host/Makefile
++++ b/drivers/mmc/host/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
+ obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+ obj-$(CONFIG_MMC_MVSDIO)	+= mvsdio.o
+ obj-$(CONFIG_MMC_DAVINCI)       += davinci_mmc.o
++obj-$(CONFIG_MMC_FTSDC)         += ftsdc010.o
+ obj-$(CONFIG_MMC_GOLDFISH)	+= android-goldfish.o
+ obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
+ ifeq ($(CONFIG_OF),y)
+diff --git a/drivers/mmc/host/ftsdc010.c b/drivers/mmc/host/ftsdc010.c
+new file mode 100644
+index 000000000000..940b4c03787c
+--- /dev/null
++++ b/drivers/mmc/host/ftsdc010.c
+@@ -0,0 +1,1557 @@
++/* drivers/mmc/host/ftsdc010.c
++ *  Copyright (C) 2010 Andestech
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/dma-mapping.h>
++#include <linux/clk.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/card.h>
++#include <linux/platform_device.h>
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <asm/io.h>
++#include <asm/dmad.h>
++#include "ftsdc010.h"
++#include "../core/core.h"
++
++#define DRIVER_NAME "ftsdc010"
++#define REG_READ(addr) readl((host->base + addr))
++#define REG_WRITE(data, addr) writel((data), (host->base + addr))
++
++enum dbg_channels {
++	dbg_err   = (1 << 0),
++	dbg_debug = (1 << 1),
++	dbg_info  = (1 << 2),
++	dbg_irq   = (1 << 3),
++	dbg_sg    = (1 << 4),
++	dbg_dma   = (1 << 5),
++	dbg_pio   = (1 << 6),
++	dbg_fail  = (1 << 7),
++	dbg_conf  = (1 << 8),
++};
++
++static struct workqueue_struct *mywq;
++
++static const int dbgmap_err   = dbg_fail;
++static const int dbgmap_info  = dbg_info | dbg_conf;
++static const int dbgmap_debug = dbg_err | dbg_debug | dbg_info | dbg_conf;
++#define dbg(host, channels, args...)		  \
++	do {					  \
++	if (dbgmap_err & channels) 		  \
++		dev_err(&host->pdev->dev, args);  \
++	else if (dbgmap_info & channels)	  \
++		dev_info(&host->pdev->dev, args); \
++	else if (dbgmap_debug & channels)	  \
++		dev_dbg(&host->pdev->dev, args);  \
++	} while (0)
++static void finalize_request(struct ftsdc_host *host);
++static void ftsdc_send_request(struct mmc_host *mmc);
++#ifdef CONFIG_MMC_DEBUG
++static void dbg_dumpregs(struct ftsdc_host *host, char *prefix)
++{
++	u32 con, cmdarg, r0, r1, r2, r3, rcmd, dcon, dtimer,
++	    dlen, sta, clr, imask, pcon, ccon, bwidth, scon1,
++	    scon2, ssta, fea;
++
++	con	= REG_READ(SDC_CMD_REG);
++	cmdarg	= REG_READ(SDC_ARGU_REG);
++	r0	= REG_READ(SDC_RESPONSE0_REG);
++	r1	= REG_READ(SDC_RESPONSE1_REG);
++	r2	= REG_READ(SDC_RESPONSE2_REG);
++	r3	= REG_READ(SDC_RESPONSE3_REG);
++	rcmd	= REG_READ(SDC_RSP_CMD_REG);
++	dcon	= REG_READ(SDC_DATA_CTRL_REG);
++	dtimer	= REG_READ(SDC_DATA_TIMER_REG);
++	dlen	= REG_READ(SDC_DATA_LEN_REG);
++	sta	= REG_READ(SDC_STATUS_REG);
++	clr	= REG_READ(SDC_CLEAR_REG);
++	imask	= REG_READ(SDC_INT_MASK_REG);
++	pcon	= REG_READ(SDC_POWER_CTRL_REG);
++	ccon	= REG_READ(SDC_CLOCK_CTRL_REG);
++	bwidth	= REG_READ(SDC_BUS_WIDTH_REG);
++	scon1	= REG_READ(SDC_SDIO_CTRL1_REG);
++	scon2	= REG_READ(SDC_SDIO_CTRL2_REG);
++	ssta	= REG_READ(SDC_SDIO_STATUS_REG);
++	fea	= REG_READ(SDC_FEATURE_REG);
++
++	dbg(host, dbg_debug, "%s CON:[%08x]  STA:[%08x]  INT:[%08x], PWR:[%08x], CLK:[%08x]\n",
++				prefix, con, sta, imask, pcon, ccon);
++
++	dbg(host, dbg_debug, "%s DCON:[%08x] DTIME:[%08x]"
++			       " DLEN:[%08x] DWIDTH:[%08x]\n",
++				prefix, dcon, dtimer, dlen, bwidth);
++
++	dbg(host, dbg_debug, "%s R0:[%08x]   R1:[%08x]"
++			       "   R2:[%08x]   R3:[%08x]\n",
++			       prefix, r0, r1, r2, r3);
++
++	dbg(host, dbg_debug, "%s SCON1:[%08x]   SCON2:[%08x]"
++			       "   SSTA:[%08x]   FEA:[%08x]\n",
++				prefix, scon1, scon2, ssta, fea);
++}
++
++static void prepare_dbgmsg(struct ftsdc_host *host, struct mmc_command *cmd,
++			   int stop)
++{
++	snprintf(host->dbgmsg_cmd, 300,
++		 "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u",
++		 host->ccnt, (stop ? " (STOP)" : ""),
++		 cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
++
++	if (cmd->data) {
++		snprintf(host->dbgmsg_dat, 300,
++			 "#%u bsize:%u blocks:%u bytes:%u",
++			 host->dcnt, cmd->data->blksz,
++			 cmd->data->blocks,
++			 cmd->data->blocks * cmd->data->blksz);
++	} else {
++		host->dbgmsg_dat[0] = '\0';
++	}
++}
++
++static void dbg_dumpcmd(struct ftsdc_host *host, struct mmc_command *cmd,
++			int fail)
++{
++	unsigned int dbglvl = fail ? dbg_fail : dbg_debug;
++
++	if (!cmd)
++		return;
++
++	if (cmd->error == 0) {
++		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
++			host->dbgmsg_cmd, cmd->resp[0]);
++	} else {
++		dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
++			cmd->error, host->dbgmsg_cmd, host->status);
++	}
++
++	if (!cmd->data)
++		return;
++
++	if (cmd->data->error == 0) {
++		dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
++	} else {
++		dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
++			cmd->data->error, host->dbgmsg_dat,
++			REG_READ(SDC_DATA_LEN_REG));
++	}
++}
++#else
++static void dbg_dumpcmd(struct ftsdc_host *host,
++			struct mmc_command *cmd, int fail) { }
++
++static void prepare_dbgmsg(struct ftsdc_host *host, struct mmc_command *cmd,
++			   int stop) { }
++
++static void dbg_dumpregs(struct ftsdc_host *host, char *prefix) { }
++
++#endif /* CONFIG_MMC_DEBUG */
++
++static inline bool ftsdc_dmaexist(struct ftsdc_host *host)
++{
++	return (host->dma_req != NULL);
++}
++
++static inline u32 enable_imask(struct ftsdc_host *host, u32 imask)
++{
++	u32 newmask;
++
++#ifdef CONFIG_MMC_DEBUG
++	if (imask & SDC_STATUS_REG_SDIO_INTR) printk("\n*** E ***\n");
++#endif
++	newmask = REG_READ(SDC_INT_MASK_REG);
++	newmask |= imask;
++
++	REG_WRITE(newmask, SDC_INT_MASK_REG);
++
++	return newmask;
++}
++
++static inline u32 disable_imask(struct ftsdc_host *host, u32 imask)
++{
++	u32 newmask;
++
++#ifdef CONFIG_MMC_DEBUG
++	if (imask & SDC_STATUS_REG_SDIO_INTR) printk("\n*** D ***\n");
++#endif
++	newmask = REG_READ(SDC_INT_MASK_REG);
++	newmask &= ~imask;
++
++	REG_WRITE(newmask, SDC_INT_MASK_REG);
++
++	return newmask;
++}
++
++static inline void clear_imask(struct ftsdc_host *host)
++{
++	u32 mask = REG_READ(SDC_INT_MASK_REG);
++
++	/* preserve the SDIO IRQ mask state */
++	mask &= (SDC_INT_MASK_REG_SDIO_INTR | SDC_INT_MASK_REG_CARD_CHANGE);
++	REG_WRITE(mask, SDC_INT_MASK_REG);
++}
++
++static inline void get_data_buffer(struct ftsdc_host *host)
++{
++	struct scatterlist *sg;
++
++	BUG_ON(host->buf_sgptr >= host->mrq->data->sg_len);
++
++	sg = &host->mrq->data->sg[host->buf_sgptr];
++
++	host->buf_bytes = sg->length;
++	host->buf_ptr = host->dodma ? (u32 *)sg->dma_address : sg_virt(sg);
++	host->buf_sgptr++;
++}
++
++static inline u32 cal_blksz(unsigned int blksz)
++{
++	u32 blksztwo = 0;
++
++	while (blksz >>= 1)
++		blksztwo++;
++
++	return blksztwo;
++}
++
++/**
++ * ftsdc_enable_irq - enable IRQ, after having disabled it.
++ * @host: The device state.
++ * @more: True if more IRQs are expected from transfer.
++ *
++ * Enable the main IRQ if needed after it has been disabled.
++ *
++ * The IRQ can be one of the following states:
++ *	- enable after data read/write
++ *	- disable when handle data read/write
++ */
++static void ftsdc_enable_irq(struct ftsdc_host *host, bool enable)
++{
++	unsigned long flags;
++	local_irq_save(flags);
++
++	host->irq_enabled = enable;
++
++	if (enable)
++		enable_irq(host->irq);
++	else
++		disable_irq(host->irq);
++
++	local_irq_restore(flags);
++}
++
++static void do_pio_read(struct ftsdc_host *host)
++{
++	u32 fifo;
++	u32 fifo_words;
++	u32 *ptr;
++	u32 status;
++	u32 retry = 0;
++
++	BUG_ON(host->buf_bytes != 0);
++
++	while (host->buf_sgptr < host->mrq->data->sg_len) {
++		get_data_buffer(host);
++
++		dbg(host, dbg_pio,
++		    "pio_read(): new target: [%i]@[%p]\n",
++		    host->buf_bytes, host->buf_ptr);
++
++		while (host->buf_bytes) {
++			status = REG_READ(SDC_STATUS_REG);
++			if (status & SDC_STATUS_REG_FIFO_OVERRUN) {
++				fifo = host->fifo_len > host->buf_bytes ?
++					host->buf_bytes : host->fifo_len;
++				dbg(host, dbg_pio,
++				    "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
++				    fifo, host->buf_bytes,
++				    REG_READ(SDC_DATA_LEN_REG));
++				host->buf_bytes -= fifo;
++				host->buf_count += fifo;
++				fifo_words = fifo >> 2;
++				ptr = host->buf_ptr;
++				while (fifo_words--)
++					*ptr++ = REG_READ(SDC_DATA_WINDOW_REG);
++				host->buf_ptr = ptr;
++				if (fifo & 3) {
++					u32 n = fifo & 3;
++					u32 data = REG_READ(SDC_DATA_WINDOW_REG);
++					u8 *p = (u8 *)host->buf_ptr;
++
++					while (n--) {
++						*p++ = data;
++						data >>= 8;
++					}
++				}
++			} else {
++				udelay(1);
++				if (++retry >= SDC_PIO_RETRY) {
++					host->mrq->data->error = -EIO;
++					goto err;
++				}
++			}
++		}
++	}
++err:
++
++	host->buf_active = XFER_NONE;
++	host->complete_what = COMPLETION_FINALIZE;
++}
++
++static void do_pio_write(struct ftsdc_host *host)
++{
++	u32 fifo;
++	u32 *ptr;
++	u32 status;
++	u32 retry = 0;
++
++	BUG_ON(host->buf_bytes != 0);
++
++	while (host->buf_sgptr < host->mrq->data->sg_len) {
++		get_data_buffer(host);
++
++		dbg(host, dbg_pio,
++		    "pio_write(): new source: [%i]@[%p]\n",
++		    host->buf_bytes, host->buf_ptr);
++
++		while (host->buf_bytes) {
++			status = REG_READ(SDC_STATUS_REG);
++			if (status & SDC_STATUS_REG_FIFO_UNDERRUN) {
++				fifo = host->fifo_len > host->buf_bytes ?
++					host->buf_bytes : host->fifo_len;
++
++				dbg(host, dbg_pio,
++				    "pio_write(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
++				    fifo, host->buf_bytes,
++				    REG_READ(SDC_DATA_LEN_REG));
++
++				host->buf_bytes -= fifo;
++				host->buf_count += fifo;
++
++				fifo = (fifo + 3) >> 2;
++				ptr = host->buf_ptr;
++				while (fifo--) {
++					REG_WRITE(*ptr, SDC_DATA_WINDOW_REG);
++					ptr++;
++				}
++				host->buf_ptr = ptr;
++			} else {
++				udelay(1);
++				if (++retry >= SDC_PIO_RETRY) {
++					host->mrq->data->error = -EIO;
++					goto err;
++				}
++			}
++		}
++	}
++
++err:
++	host->buf_active = XFER_NONE;
++	host->complete_what = COMPLETION_FINALIZE;
++}
++
++static void do_dma_access(struct ftsdc_host *host)
++{
++	int res;
++	unsigned long timeout;
++	dmad_chreq *req = host->dma_req;
++	dmad_drb *drb = 0;
++
++	while (host->buf_sgptr < host->mrq->data->sg_len) {
++
++		reinit_completion(&host->dma_complete);
++		get_data_buffer(host);
++
++		dbg(host, dbg_dma,
++		    "dma_%s(): new target: [%i]@[%p]\n",
++		    host->buf_active == XFER_READ ? "read" : "write",
++		    host->buf_bytes, host->buf_ptr);
++
++		res = dmad_alloc_drb(req, &drb);
++
++		if (res != 0 || (drb == 0)) {
++			dbg(host, dbg_err, "%s() Failed to allocate dma request block!\n", __func__);
++			host->mrq->data->error = -ENODEV;
++			goto err;
++		}
++		drb->addr0 = host->mem->start + SDC_DATA_WINDOW_REG;
++		drb->addr1 = (dma_addr_t)host->buf_ptr;
++		drb->req_cycle = dmad_bytes_to_cycles(req, host->buf_bytes);
++		drb->sync = &host->dma_complete;
++		timeout = SDC_TIMEOUT_BASE*((host->buf_bytes+511)>>9);
++		res =  dmad_submit_request(req, drb, 1);
++		if (res != 0) {
++			dbg(host, dbg_err, "%s() Failed to submit dma request block!\n", __func__);
++			host->mrq->data->error = -ENODEV;
++			goto err;
++		}
++		dbg(host, dbg_err, "reach here!\n");
++		if (wait_for_completion_timeout(&host->dma_complete, timeout) == 0) {
++			dbg(host, dbg_err, "%s: read timeout\n", __func__);
++			host->mrq->data->error = -ETIMEDOUT;
++			goto err;
++		}
++	}
++
++	host->dma_finish = true;
++err:
++	host->buf_active = XFER_NONE;
++	host->complete_what = COMPLETION_FINALIZE;
++}
++
++static void ftsdc_work(struct work_struct *work)
++{
++	struct ftsdc_host *host =
++		container_of(work, struct ftsdc_host, work);
++
++	ftsdc_enable_irq(host, false);
++	if (host->dodma) {
++		do_dma_access(host);
++	} else {
++		if (host->buf_active == XFER_WRITE)
++			do_pio_write(host);
++
++		if (host->buf_active == XFER_READ)
++			do_pio_read(host);
++	}
++
++	tasklet_schedule(&host->pio_tasklet);
++	ftsdc_enable_irq(host, true);
++}
++
++static void pio_tasklet(unsigned long data)
++{
++	struct ftsdc_host *host = (struct ftsdc_host *) data;
++
++	if (host->complete_what == COMPLETION_XFER_PROGRESS) {
++		queue_work(mywq, (struct work_struct *)&host->work);
++		return;
++	}
++
++	if (host->complete_what == COMPLETION_FINALIZE) {
++		clear_imask(host);
++		if (host->buf_active != XFER_NONE) {
++			dbg(host, dbg_err, "unfinished %s "
++			    "- buf_count:[%u] buf_bytes:[%u]\n",
++			    (host->buf_active == XFER_READ) ? "read" : "write",
++			    host->buf_count, host->buf_bytes);
++
++			if (host->mrq->data)
++				host->mrq->data->error = -EINVAL;
++		}
++
++		finalize_request(host);
++	}
++}
++
++static void finalize_request(struct ftsdc_host *host)
++{
++	struct mmc_request *mrq = host->mrq;
++	struct mmc_command *cmd;
++	u32 con;
++	int debug_as_failure = 0;
++	if (host->complete_what != COMPLETION_FINALIZE)
++		return;
++
++	if (!mrq)
++		return;
++
++	cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
++
++	if (cmd->data && (cmd->error == 0) &&
++	    (cmd->data->error == 0)) {
++		if (host->dodma && (!host->dma_finish)) {
++			dbg(host, dbg_dma, "DMA Missing (%d)!\n",
++			    host->dma_finish);
++			return;
++		}
++		host->dodma = false;
++	}
++
++	/* Read response from controller. */
++	if (cmd->flags & MMC_RSP_136) {
++		cmd->resp[3] = REG_READ(SDC_RESPONSE0_REG);
++		cmd->resp[2] = REG_READ(SDC_RESPONSE1_REG);
++		cmd->resp[1] = REG_READ(SDC_RESPONSE2_REG);
++		cmd->resp[0] = REG_READ(SDC_RESPONSE3_REG);
++	} else if (cmd->flags & MMC_RSP_PRESENT) {
++		cmd->resp[0] = REG_READ(SDC_RESPONSE0_REG);
++	}
++
++	if (cmd->error)
++		debug_as_failure = 1;
++
++	if (cmd->data && cmd->data->error)
++		debug_as_failure = 1;
++
++	dbg_dumpcmd(host, cmd, debug_as_failure);
++
++	clear_imask(host);
++	con = REG_READ(SDC_STATUS_REG);
++	con &= ~SDC_CLEAR_REG_SDIO_INTR;
++	REG_WRITE(con, SDC_CLEAR_REG);
++
++	if (cmd->data && cmd->error)
++		cmd->data->error = cmd->error;
++
++	if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
++		host->cmd_is_stop = 1;
++		ftsdc_send_request(host->mmc);
++		return;
++	}
++
++	/* If we have no data transfer we are finished here */
++	if (!mrq->data)
++		goto request_done;
++
++	/* Calulate the amout of bytes transfer if there was no error */
++	if (mrq->data->error == 0) {
++		mrq->data->bytes_xfered =
++			(mrq->data->blocks * mrq->data->blksz);
++	} else {
++		mrq->data->bytes_xfered = 0;
++	}
++
++request_done:
++	host->complete_what = COMPLETION_NONE;
++	host->mrq = NULL;
++
++	host->last_opcode = mrq->cmd->opcode;
++	mmc_request_done(host->mmc, mrq);
++}
++
++static void ftsdc_send_command(struct ftsdc_host *host,
++					struct mmc_command *cmd)
++{
++	u32 ccon = 0;
++	u32 newmask = 0;
++	u32 scon;
++
++	if (cmd->data) {
++		host->complete_what = COMPLETION_XFER_PROGRESS;
++		newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT;
++	} else if (cmd->flags & MMC_RSP_PRESENT) {
++		host->complete_what = COMPLETION_RSPFIN;
++		newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT;
++	} else {
++		host->complete_what = COMPLETION_CMDSENT;
++		newmask |= SDC_INT_MASK_REG_CMD_SEND;
++	}
++
++	ccon |= cmd->opcode & SDC_CMD_REG_INDEX;
++	ccon |= SDC_CMD_REG_CMD_EN;
++
++	if (cmd->flags & MMC_RSP_PRESENT) {
++		ccon |= SDC_CMD_REG_NEED_RSP;
++		newmask |= SDC_INT_MASK_REG_RSP_CRC_OK |
++			SDC_INT_MASK_REG_RSP_CRC_FAIL;
++	}
++
++	if (cmd->flags & MMC_RSP_136)
++		ccon |= SDC_CMD_REG_LONG_RSP;
++
++	/* applicatiion specific cmd must follow an MMC_APP_CMD. The
++	 * value will be updated in finalize_request function */
++	if (host->last_opcode == MMC_APP_CMD)
++		ccon |= SDC_CMD_REG_APP_CMD;
++
++	enable_imask(host, newmask);
++	REG_WRITE(cmd->arg, SDC_ARGU_REG);
++
++	scon = REG_READ(SDC_SDIO_CTRL1_REG);
++	if (host->mmc->card != NULL && mmc_card_sdio(host->mmc->card))
++		scon |= SDC_SDIO_CTRL1_REG_SDIO_ENABLE;
++	else
++		scon &= ~SDC_SDIO_CTRL1_REG_SDIO_ENABLE;
++	REG_WRITE(scon, SDC_SDIO_CTRL1_REG);
++
++	dbg_dumpregs(host, "");
++	dbg(host, dbg_debug, "CON[%x]\n", ccon);
++
++	REG_WRITE(ccon, SDC_CMD_REG);
++}
++
++static int ftsdc_setup_data(struct ftsdc_host *host, struct mmc_data *data)
++{
++	u32 dcon, newmask = 0;
++
++	/* configure data transfer paramter */
++	if (!data)
++		return 0;
++	if(host->mmc->card && host->mmc->card->type==(unsigned int)MMC_TYPE_SD){
++		if (((data->blksz - 1) & data->blksz) != 0) {
++			pr_warn("%s: can't do non-power-of 2 sized block transfers (blksz %d)\n", __func__, data->blksz);
++			return -EINVAL;
++		}
++	}
++
++	if (data->blksz <= 2) {
++		/* We cannot deal with unaligned blocks with more than
++		 * one block being transfered. */
++
++		if (data->blocks > 1) {
++			pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
++			return -EINVAL;
++		}
++	}
++
++	/* data length */
++	dcon = data->blksz * data->blocks;
++	REG_WRITE(dcon, SDC_DATA_LEN_REG);
++
++	/* write data control */
++	dcon = 0;
++	dcon = cal_blksz(data->blksz);
++
++	/* enable UNDERFUN will trigger interrupt immediatedly
++	 * So setup it when rsp is received successfully
++	 */
++	if (data->flags & MMC_DATA_WRITE) {
++		dcon |= SDC_DATA_CTRL_REG_DATA_WRITE;
++	} else {
++		dcon &= ~SDC_DATA_CTRL_REG_DATA_WRITE;
++		newmask |= SDC_INT_MASK_REG_FIFO_OVERRUN;
++	}
++
++	/* always reset fifo since last transfer may fail */
++	dcon |= SDC_DATA_CTRL_REG_FIFO_RST;
++
++	/* enable data transfer which will be pended until cmd is send */
++	dcon |= SDC_DATA_CTRL_REG_DATA_EN;
++	if (ftsdc_dmaexist(host) &&
++			((data->blksz * data->blocks) & 0xf) == 0) {
++		newmask &= ~SDC_INT_MASK_REG_FIFO_OVERRUN;
++		dcon |= SDC_DATA_CTRL_REG_DMA_EN;
++		dcon |= SDC_DMA_TYPE_4;
++		host->dodma = true;
++
++	}
++	REG_WRITE(dcon, SDC_DATA_CTRL_REG);
++	/* add to IMASK register */
++	newmask |= SDC_INT_MASK_REG_DATA_CRC_FAIL |
++			SDC_INT_MASK_REG_DATA_TIMEOUT;
++
++	enable_imask(host, newmask);
++	/* handle sdio */
++	dcon = SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE & REG_READ(SDC_SDIO_CTRL1_REG);
++	dcon |= data->blksz | data->blocks << 15;
++	if (1 < data->blocks)
++		dcon |= SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE;
++	REG_WRITE(dcon, SDC_SDIO_CTRL1_REG);
++
++	return 0;
++}
++
++#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ)
++
++static int ftsdc_prepare_buffer(struct ftsdc_host *host, struct mmc_data *data)
++{
++	int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
++
++	if ((!host->mrq) || (!host->mrq->data))
++		return -EINVAL;
++
++	BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
++	host->buf_sgptr = 0;
++	host->buf_bytes = 0;
++	host->buf_count = 0;
++	host->buf_active = rw ? XFER_WRITE : XFER_READ;
++	if (host->dodma) {
++		u32 dma_len;
++		u32 drb_size;
++		dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
++				     rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
++		if (dma_len == 0)
++			return -ENOMEM;
++
++		dmad_config_channel_dir(host->dma_req,
++				rw ? DMAD_DIR_A1_TO_A0 : DMAD_DIR_A0_TO_A1);
++		drb_size = dmad_max_size_per_drb(host->dma_req);
++		if (drb_size < (data->blksz & data->blocks))
++			return -ENODEV;
++
++		host->dma_finish = false;
++	}
++	return 0;
++}
++
++static irqreturn_t ftsdc_irq(int irq, void *dev_id)
++{
++	struct ftsdc_host *host = dev_id;
++	struct mmc_command *cmd;
++	u32 mci_status;
++	u32 mci_clear;
++	u32 mci_imsk;
++	unsigned long iflags;
++
++	mci_status = REG_READ(SDC_STATUS_REG);
++	mci_imsk = REG_READ(SDC_INT_MASK_REG);
++
++	dbg(host, dbg_debug, "irq: status:0x%08x, mask : %08x\n", mci_status, mci_imsk);
++
++	if (mci_status & SDC_STATUS_REG_SDIO_INTR) {
++		if (mci_imsk & SDC_INT_MASK_REG_SDIO_INTR) {
++			mci_clear = SDC_CLEAR_REG_SDIO_INTR;
++			REG_WRITE(mci_clear, SDC_CLEAR_REG);
++
++			mmc_signal_sdio_irq(host->mmc);
++			return IRQ_HANDLED;
++		}
++	}
++
++	spin_lock_irqsave(&host->complete_lock, iflags);
++
++	mci_status = REG_READ(SDC_STATUS_REG);
++	mci_clear = 0;
++
++	if (mci_status & SDC_STATUS_REG_CARD_CHANGE) {
++		if ((mci_status & SDC_STATUS_REG_CARD_DETECT)
++			== SDC_CARD_INSERT) {
++			host->status = "card insert";
++			mmc_detect_change(host->mmc, msecs_to_jiffies(500));
++		} else {
++			host->status = "card remove";
++		}
++		mci_clear |= SDC_CLEAR_REG_CARD_CHANGE;
++		dbg(host, dbg_irq, "%s\n", host->status);
++
++		if (host->complete_what != COMPLETION_NONE) {
++			host->mrq->cmd->error = -EIO;
++			goto close_transfer;
++		}
++
++		goto irq_out;
++	}
++
++	if ((host->complete_what == COMPLETION_NONE) ||
++	    (host->complete_what == COMPLETION_FINALIZE)) {
++		host->status = "nothing to complete";
++		mci_clear = -1u;
++		goto irq_out;
++	}
++
++	if (!host->mrq) {
++		host->status = "no active mrq";
++		clear_imask(host);
++		goto irq_out;
++	}
++
++	cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
++
++	if (!cmd) {
++		host->status = "no active cmd";
++		clear_imask(host);
++		goto irq_out;
++	}
++
++	if (mci_status & SDC_STATUS_REG_CMD_SEND) {
++		mci_clear |= SDC_CLEAR_REG_CMD_SEND;
++
++		if (host->complete_what == COMPLETION_CMDSENT) {
++			host->status = "ok: command sent";
++			goto close_transfer;
++		} else {
++			host->status = "error: command sent(status not match)";
++			cmd->error = -EINVAL;
++			goto fail_transfer;
++		}
++	}
++
++	/* handle error status */
++	if (mci_status & SDC_STATUS_REG_RSP_TIMEOUT) {
++		dbg(host, dbg_err, "CMDSTAT: error RSP TIMEOUT\n");
++		mci_clear |= SDC_CLEAR_REG_RSP_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
++		host->status = "error: response timeout";
++		goto fail_transfer;
++	}
++
++	if (mci_status & SDC_STATUS_REG_RSP_CRC_FAIL) {
++		mci_clear |= SDC_CLEAR_REG_RSP_CRC_FAIL;
++		/* This is wierd hack */
++		if (cmd->flags & MMC_RSP_CRC) {
++			dbg(host, dbg_err, "CMDSTAT: error RSP CRC\n");
++			cmd->error = -EILSEQ;
++			host->status = "error: RSP CRC failed";
++			goto fail_transfer;
++		} else {
++			host->status = "R3 or R4 type command";
++			goto close_transfer;
++		}
++	}
++
++	if (mci_status & SDC_STATUS_REG_RSP_CRC_OK) {
++		mci_clear |= SDC_CLEAR_REG_RSP_CRC_OK;
++
++		if (host->complete_what == COMPLETION_XFER_PROGRESS) {
++			REG_WRITE(mci_clear, SDC_CLEAR_REG);
++
++			host->status = "RSP recv OK";
++			if (!cmd->data)
++				goto close_transfer;
++
++			if (host->dodma) {
++				tasklet_schedule(&host->pio_tasklet);
++				host->status = "dma access";
++				goto irq_out;
++			}
++
++			if (host->buf_active == XFER_WRITE)
++				enable_imask(host, SDC_INT_MASK_REG_FIFO_UNDERRUN);
++		} else if (host->complete_what == COMPLETION_RSPFIN) {
++			goto close_transfer;
++		}
++	}
++
++	/* handler data transfer */
++	if (mci_status & SDC_STATUS_REG_DATA_TIMEOUT) {
++		dbg(host, dbg_err, "CMDSTAT: error DATA TIMEOUT\n");
++		mci_clear |= SDC_CLEAR_REG_DATA_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
++		host->status = "error: data timeout";
++		goto fail_transfer;
++	}
++
++	if (mci_status & SDC_STATUS_REG_DATA_CRC_FAIL) {
++		dbg(host, dbg_err, "CMDSTAT: error DATA CRC\n");
++		mci_clear |= SDC_CLEAR_REG_DATA_CRC_FAIL;
++		cmd->error = -EILSEQ;
++		host->status = "error: data CRC fail";
++		goto fail_transfer;
++	}
++
++	if ((mci_status & SDC_STATUS_REG_FIFO_UNDERRUN) ||
++		mci_status & SDC_STATUS_REG_FIFO_OVERRUN) {
++
++		disable_imask(host, SDC_INT_MASK_REG_FIFO_OVERRUN |
++				SDC_INT_MASK_REG_FIFO_UNDERRUN);
++
++		if (!host->dodma) {
++			if (host->buf_active == XFER_WRITE) {
++				tasklet_schedule(&host->pio_tasklet);
++				host->status = "pio tx";
++			} else if (host->buf_active == XFER_READ) {
++
++				tasklet_schedule(&host->pio_tasklet);
++				host->status = "pio rx";
++			}
++		}
++	}
++
++	goto irq_out;
++
++fail_transfer:
++	host->buf_active = XFER_NONE;
++
++close_transfer:
++	host->complete_what = COMPLETION_FINALIZE;
++
++	clear_imask(host);
++	tasklet_schedule(&host->pio_tasklet);
++
++irq_out:
++	REG_WRITE(mci_clear, SDC_CLEAR_REG);
++
++	dbg(host, dbg_debug, "irq: %s\n", host->status);
++	spin_unlock_irqrestore(&host->complete_lock, iflags);
++	return IRQ_HANDLED;
++}
++
++static void ftsdc_send_request(struct mmc_host *mmc)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++	struct mmc_request *mrq = host->mrq;
++	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
++
++	host->ccnt++;
++	prepare_dbgmsg(host, cmd, host->cmd_is_stop);
++	dbg(host, dbg_debug, "%s\n", host->dbgmsg_cmd);
++
++	if (cmd->data) {
++		int res = ftsdc_setup_data(host, cmd->data);
++
++		host->dcnt++;
++
++		if (res) {
++			dbg(host, dbg_err, "setup data error %d\n", res);
++			cmd->error = res;
++			cmd->data->error = res;
++
++			mmc_request_done(mmc, mrq);
++			return;
++		}
++
++		res = ftsdc_prepare_buffer(host, cmd->data);
++
++		if (res) {
++			dbg(host, dbg_err, "data prepare error %d\n", res);
++			cmd->error = res;
++			cmd->data->error = res;
++
++			mmc_request_done(mmc, mrq);
++			return;
++		}
++	}
++
++	/* Send command */
++	ftsdc_send_command(host, cmd);
++}
++
++static int ftsdc_get_cd(struct mmc_host *mmc)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++
++	u32 con = REG_READ(SDC_STATUS_REG);
++		dbg(host, dbg_debug, "get_cd status:%.8x\n\n", con);
++
++	return (con & SDC_STATUS_REG_CARD_DETECT) ? 0 : 1;
++}
++
++static void ftsdc_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++	host->status = "mmc request";
++	host->cmd_is_stop = 0;
++	host->mrq = mrq;
++	if (ftsdc_get_cd(mmc) == 0) {
++		dbg(host, dbg_err, "%s: no medium present\n", __func__);
++		host->mrq->cmd->error = -ENOMEDIUM;
++		mmc_request_done(mmc, mrq);
++	} else {
++		ftsdc_send_request(mmc);
++	}
++	dbg(host, dbg_debug, "send request \n");
++}
++
++static void ftsdc_set_clk(struct ftsdc_host *host, struct mmc_ios *ios)
++{
++	u32 clk_div = 0;
++	u32 con;
++	struct ftsdc_mmc_config *pdata = host->pdev->dev.platform_data;
++	u32 freq = pdata->max_freq;
++
++	dbg(host, dbg_debug, "request clk : %u \n", ios->clock);
++	con = REG_READ(SDC_CLOCK_CTRL_REG);
++	if (ios->clock == 0) {
++		host->real_rate = 0;
++		con |= SDC_CLOCK_CTRL_REG_CLK_DIS;
++	} else {
++		clk_div = (freq / (ios->clock << 1)) - 1;
++		host->real_rate = freq / ((clk_div+1)<<1);
++		if (host->real_rate > ios->clock) {
++			++clk_div;
++			host->real_rate = freq / ((clk_div+1)<<1);
++		}
++		if (clk_div > 127)
++			dbg(host, dbg_err, "%s: no match clock rate, %u\n", __func__, ios->clock);
++
++		con = (con & ~SDC_CLOCK_CTRL_REG_CLK_DIV) | (clk_div & SDC_CLOCK_CTRL_REG_CLK_DIV);
++		con &= ~SDC_CLOCK_CTRL_REG_CLK_DIS;
++	}
++
++	REG_WRITE(con, SDC_CLOCK_CTRL_REG);
++}
++
++static void ftsdc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++	u32 con;
++
++	con = REG_READ(SDC_POWER_CTRL_REG);
++	switch (ios->power_mode) {
++	case MMC_POWER_ON:
++	case MMC_POWER_UP:
++		con |= SDC_POWER_CTRL_REG_POWER_ON;
++		break;
++	case MMC_POWER_OFF:
++	default:
++		con &= ~SDC_POWER_CTRL_REG_POWER_ON;
++		break;
++	}
++
++	REG_WRITE(con, SDC_POWER_CTRL_REG);
++
++	ftsdc_set_clk(host, ios);
++
++	if ((ios->power_mode == MMC_POWER_ON) ||
++	    (ios->power_mode == MMC_POWER_UP)) {
++		dbg(host, dbg_debug, "running at %ukHz (requested: %ukHz).\n",
++			host->real_rate/1000, ios->clock/1000);
++	} else {
++		dbg(host, dbg_debug, "powered down.\n");
++	}
++
++	host->bus_width = ios->bus_width;
++	/* write bus configure */
++	con = REG_READ(SDC_BUS_WIDTH_REG);
++
++	con &= ~(SDC_BUS_WIDTH_REG_SINGLE_BUS |
++			SDC_BUS_WIDTH_REG_WIDE_4_BUS |
++			SDC_BUS_WIDTH_REG_WIDE_8_BUS);
++	if (host->bus_width == MMC_BUS_WIDTH_1)
++		con |= SDC_BUS_WIDTH_REG_SINGLE_BUS;
++	else if (host->bus_width == MMC_BUS_WIDTH_4)
++		con |= SDC_BUS_WIDTH_REG_WIDE_4_BUS;
++	else if (host->bus_width == MMC_BUS_WIDTH_8)
++		con |= SDC_BUS_WIDTH_REG_WIDE_8_BUS;
++	else {
++		dbg(host, dbg_err, "set_ios: can't support bus mode");
++	}
++	REG_WRITE(con, SDC_BUS_WIDTH_REG);
++
++	/*set rsp and data timeout */
++	con = -1;
++	REG_WRITE(con, SDC_DATA_TIMER_REG);
++	if (ios->power_mode == MMC_POWER_UP)
++		mmc_delay(250);
++}
++
++static int ftsdc_get_ro(struct mmc_host *mmc)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++	u32 con = REG_READ(SDC_STATUS_REG);
++	dbg(host, dbg_debug, "get_ro status:%.8x\n", con);
++
++	return (con & SDC_STATUS_REG_CARD_LOCK) ? 1 : 0;
++}
++
++
++static void ftsdc_enable_sdio_irq(struct mmc_host *mmc, int enable)
++{
++	struct ftsdc_host *host = mmc_priv(mmc);
++	unsigned long flags;
++	u32 con;
++#ifdef CONFIG_MMC_DEBUG
++	u32 ena;
++#endif
++
++	local_irq_save(flags);
++
++	con = REG_READ(SDC_INT_MASK_REG);
++#ifdef CONFIG_MMC_DEBUG
++	ena = (con & SDC_STATUS_REG_SDIO_INTR) ? 1:0;
++	if (ena == enable)
++		printk("\n*** XXX ***\n");
++#endif
++
++	con = enable ? (con | SDC_STATUS_REG_SDIO_INTR) : (con & ~SDC_STATUS_REG_SDIO_INTR);
++	REG_WRITE(con, SDC_INT_MASK_REG);
++
++#ifdef CONFIG_MMC_DEBUG
++	//check and ensure data out to SD host controller
++	ena = (REG_READ(SDC_INT_MASK_REG) & SDC_STATUS_REG_SDIO_INTR) ? 1:0;
++	if (ena != enable) {
++		printk("\n*** YYY ***\n");
++	}
++#endif
++
++	local_irq_restore(flags);
++}
++
++static struct mmc_host_ops ftsdc_ops = {
++	.request	= ftsdc_request,
++	.set_ios	= ftsdc_set_ios,
++	.get_ro		= ftsdc_get_ro,
++	.get_cd		= ftsdc_get_cd,
++	.enable_sdio_irq = ftsdc_enable_sdio_irq,
++};
++
++#ifdef CONFIG_DEBUG_FS
++
++static int ftsdc_state_show(struct seq_file *seq, void *v)
++{
++	struct ftsdc_host *host = seq->private;
++
++	seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base);
++	seq_printf(seq, "Clock rate = %u\n", host->real_rate);
++	seq_printf(seq, "host status = %s\n", host->status);
++	seq_printf(seq, "IRQ = %d\n", host->irq);
++	seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled);
++	seq_printf(seq, "complete what = %d\n", host->complete_what);
++	seq_printf(seq, "dma support = %d\n", ftsdc_dmaexist(host));
++	seq_printf(seq, "use dma = %d\n", host->dodma);
++
++	return 0;
++}
++
++static int ftsdc_state_open(struct inode *inode, struct file *file)
++{
++	return single_open(file, ftsdc_state_show, inode->i_private);
++}
++
++static const struct file_operations ftsdc_fops_state = {
++	.owner		= THIS_MODULE,
++	.open		= ftsdc_state_open,
++	.read		= seq_read,
++	.llseek		= seq_lseek,
++	.release	= single_release,
++};
++
++#define DBG_REG(_r) { .addr = SDC_## _r ## _REG, .name = #_r }
++
++struct ftsdc_reg {
++	unsigned short	addr;
++	unsigned char	*name;
++} debug_regs[] = {
++	DBG_REG(CMD),
++	DBG_REG(ARGU),
++	DBG_REG(RESPONSE0),
++	DBG_REG(RESPONSE1),
++	DBG_REG(RESPONSE2),
++	DBG_REG(RESPONSE3),
++	DBG_REG(RSP_CMD),
++	DBG_REG(DATA_CTRL),
++	DBG_REG(DATA_TIMER),
++	DBG_REG(DATA_LEN),
++	DBG_REG(STATUS),
++	DBG_REG(CLEAR),
++	DBG_REG(INT_MASK),
++	DBG_REG(POWER_CTRL),
++	DBG_REG(CLOCK_CTRL),
++	DBG_REG(BUS_WIDTH),
++	DBG_REG(SDIO_CTRL1),
++	DBG_REG(SDIO_CTRL2),
++	DBG_REG(SDIO_STATUS),
++	DBG_REG(FEATURE),
++	DBG_REG(REVISION),
++	{}
++};
++
++static int ftsdc_regs_show(struct seq_file *seq, void *v)
++{
++	struct ftsdc_host *host = seq->private;
++	struct ftsdc_reg *rptr = debug_regs;
++
++	for (; rptr->name; rptr++)
++		seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name,
++			   REG_READ(rptr->addr));
++
++	return 0;
++}
++
++static int ftsdc_regs_open(struct inode *inode, struct file *file)
++{
++	return single_open(file, ftsdc_regs_show, inode->i_private);
++}
++
++static const struct file_operations ftsdc_fops_regs = {
++	.owner		= THIS_MODULE,
++	.open		= ftsdc_regs_open,
++	.read		= seq_read,
++	.llseek		= seq_lseek,
++	.release	= single_release,
++};
++
++static void ftsdc_debugfs_attach(struct ftsdc_host *host)
++{
++	struct device *dev = &host->pdev->dev;
++
++	host->debug_root = debugfs_create_dir(dev_name(dev), NULL);
++	if (IS_ERR(host->debug_root)) {
++		dev_err(dev, "failed to create debugfs root\n");
++		return;
++	}
++
++	host->debug_state = debugfs_create_file("state", 0444,
++						host->debug_root, host,
++						&ftsdc_fops_state);
++
++	if (IS_ERR(host->debug_state))
++		dev_err(dev, "failed to create debug state file\n");
++
++	host->debug_regs = debugfs_create_file("regs", 0444,
++					       host->debug_root, host,
++					       &ftsdc_fops_regs);
++
++	if (IS_ERR(host->debug_regs))
++		dev_err(dev, "failed to create debug regs file\n");
++}
++
++static void ftsdc_debugfs_remove(struct ftsdc_host *host)
++{
++	debugfs_remove(host->debug_regs);
++	debugfs_remove(host->debug_state);
++	debugfs_remove(host->debug_root);
++}
++
++#else
++static inline void ftsdc_debugfs_attach(struct ftsdc_host *host) { }
++static inline void ftsdc_debugfs_remove(struct ftsdc_host *host) { }
++
++#endif /* CONFIG_DEBUG_FS */
++
++#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA))
++static int ftsdc_alloc_dma(struct ftsdc_host *host)
++{
++	dmad_chreq *req = host->dma_req;
++	req = kzalloc(sizeof(dmad_chreq), GFP_KERNEL);
++#ifdef CONFIG_PLATFORM_APBDMA
++	req->apb_req.addr0_ctrl  = APBBR_ADDRINC_FIXED;  /* (in)  APBBR_ADDRINC_xxx */
++/* for amerald */
++#if !defined(CONFIG_PLAT_AE3XX)
++	if((inl(pmu_base) & AMERALD_MASK) == AMERALD_PRODUCT_ID){
++		req->apb_req.addr0_reqn	= APBBR_REQN_SDC_AMERALD;
++	}else
++#endif
++	{
++		req->apb_req.addr0_reqn  = APBBR_REQN_SDC;       /* (in)  APBBR_REQN_xxx (also used to help determine bus selection) */
++	}
++	req->apb_req.addr1_ctrl  = APBBR_ADDRINC_I4X;    /* (in)  APBBR_ADDRINC_xxx */
++	req->apb_req.addr1_reqn  = APBBR_REQN_NONE;      /* (in)  APBBR_REQN_xxx (also used to help determine bus selection) */
++	req->apb_req.burst_mode  = 1;                    /* (in)  Burst mode (0: no burst 1-, 1: burst 4- data cycles per dma cycle) */
++	req->apb_req.data_width  = APBBR_DATAWIDTH_4;    /* (in)  APBBR_DATAWIDTH_4(word), APBBR_DATAWIDTH_2(half-word), APBBR_DATAWIDTH_1(byte) */
++	req->apb_req.tx_dir      = DMAD_DIR_A0_TO_A1;    /* (in)  DMAD_DIR_A0_TO_A1, DMAD_DIR_A1_TO_A0 */
++	req->controller          = DMAD_DMAC_APB_CORE;   /* (in)  DMAD_DMAC_AHB_CORE, DMAD_DMAC_APB_CORE */
++	req->flags               = DMAD_FLAGS_SLEEP_BLOCK | DMAD_FLAGS_BIDIRECTION;
++
++	if (dmad_channel_alloc(req) == 0) {
++		dbg(host, dbg_debug, "%s: APB dma channel allocated (ch: %d)\n", __func__, req->channel);
++		host->dma_req = req;
++		return 0;
++	}
++
++	memset(req, 0, sizeof(dmad_chreq));
++	dbg(host, dbg_info, "%s: APB dma channel allocation failed\n", __func__);
++#endif /* CONFIG_PLATFORM_APBDMA */
++
++#ifdef CONFIG_PLATFORM_AHBDMA
++	req->ahb_req.sync         = 1;                    /* (in)  non-zero if src and dst have different clock domain */
++	req->ahb_req.priority     = DMAC_CSR_CHPRI_1;     /* (in)  DMAC_CSR_CHPRI_0 (lowest) ~ DMAC_CSR_CHPRI_3 (highest) */
++	req->ahb_req.hw_handshake = 1;                    /* (in)  non-zero to enable hardware handshake mode */
++	req->ahb_req.burst_size   = DMAC_CSR_SIZE_4;      /* (in)  DMAC_CSR_SIZE_1 ~ DMAC_CSR_SIZE_256 */
++	req->ahb_req.addr0_width  = DMAC_CSR_WIDTH_32;    /* (in)  DMAC_CSR_WIDTH_8, DMAC_CSR_WIDTH_16, or DMAC_CSR_WIDTH_32 */
++	req->ahb_req.addr0_ctrl   = DMAC_CSR_AD_FIX;      /* (in)  DMAC_CSR_AD_INC, DMAC_CSR_AD_DEC, or DMAC_CSR_AD_FIX */
++	req->ahb_req.addr0_reqn   = DMAC_REQN_SDC;        /* (in)  DMAC_REQN_xxx (also used to help determine channel number) */
++	req->ahb_req.addr1_width  = DMAC_CSR_WIDTH_32;    /* (in)  DMAC_CSR_WIDTH_8, DMAC_CSR_WIDTH_16, or DMAC_CSR_WIDTH_32 */
++	req->ahb_req.addr1_ctrl   = DMAC_CSR_AD_INC;      /* (in)  DMAC_CSR_AD_INC, DMAC_CSR_AD_DEC, or DMAC_CSR_AD_FIX */
++	req->ahb_req.addr1_reqn   = DMAC_REQN_NONE;       /* (in)  DMAC_REQN_xxx (also used to help determine channel number) */
++	req->ahb_req.tx_dir       = DMAD_DIR_A0_TO_A1;    /* (in)  DMAD_DIR_A0_TO_A1, DMAD_DIR_A1_TO_A0 */
++
++	req->controller           = DMAD_DMAC_AHB_CORE;   /* (in)  DMAD_DMAC_AHB_CORE, DMAD_DMAC_APB_CORE */
++	req->flags                = DMAD_FLAGS_SLEEP_BLOCK | DMAD_FLAGS_BIDIRECTION;
++
++	if (dmad_channel_alloc(req) == 0) {
++		dbg(host, dbg_debug, "%s: AHB dma channel allocated (ch: %d)\n", __func__, req->channel);
++		host->dma_req = req;
++		return 0;
++	}
++	dbg(host, dbg_info, "%s: AHB dma channel allocation failed\n", __func__);
++#endif
++
++	kfree(req);
++	return -ENODEV;
++
++}
++#endif
++
++enum {
++	MMC_CTLR_VERSION_1 = 0,
++	MMC_CTLR_VERSION_2,
++};
++
++
++static struct platform_device_id ftsdc_mmc_devtype[] = {
++	{
++		.name	= "ag101p",
++		.driver_data = MMC_CTLR_VERSION_1,
++	}, {
++		.name	= "ae3xx",
++		.driver_data = MMC_CTLR_VERSION_2,
++	},
++	{},
++};
++MODULE_DEVICE_TABLE(platform, ftsdc_mmc_devtype);
++
++static const struct of_device_id ftsdc_mmc_dt_ids[] = {
++	{
++		.compatible = "andestech,atfsdc010",
++		.data = &ftsdc_mmc_devtype[MMC_CTLR_VERSION_1],
++	},
++	{
++		.compatible = "andestech,atfsdc010",
++		.data = &ftsdc_mmc_devtype[MMC_CTLR_VERSION_2],
++	},
++	{},
++};
++MODULE_DEVICE_TABLE(of, ftsdc_mmc_dt_ids);
++
++
++static struct ftsdc_mmc_config
++	*mmc_parse_pdata(struct platform_device *pdev)
++{
++	struct device_node *np;
++	struct ftsdc_mmc_config *pdata = pdev->dev.platform_data;
++	const struct of_device_id *match =
++		of_match_device(of_match_ptr(ftsdc_mmc_dt_ids), &pdev->dev);
++	u32 data;
++
++	np = pdev->dev.of_node;
++	if (!np)
++		return pdata;
++
++	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
++	pdev->dev.platform_data = (void *)pdata;
++
++	if (!pdata) {
++		dev_err(&pdev->dev, "Failed to allocate memory for struct ftsdc_mmc_config\n");
++		goto nodata;
++	}
++
++	if (match)
++		pdev->id_entry = match->data;
++
++	if (of_property_read_u32(np, "max-frequency", &pdata->max_freq))
++		dev_info(&pdev->dev, "'max-frequency' property not specified, defaulting to 25MHz\n");
++
++	of_property_read_u32(np, "bus-width", &data);
++	switch (data) {
++	case 1:
++	case 4:
++	case 8:
++		pdata->wires = data;
++		break;
++	default:
++		pdata->wires = 1;
++/*
++		dev_info(&pdev->dev, "Unsupported buswidth, defaulting to 1 bit\n");
++*/
++	}
++nodata:
++	return pdata;
++}
++
++static int __init ftsdc_probe(struct platform_device *pdev)
++{
++	struct ftsdc_host *host;
++	struct mmc_host	*mmc;
++	struct ftsdc_mmc_config *pdata = NULL;
++	struct resource *r, *mem = NULL;
++	int ret = -ENOMEM;
++	u32 con;
++	int irq = 0;
++	size_t mem_size;
++
++	pdata = mmc_parse_pdata(pdev);
++	if (pdata == NULL) {
++		dev_err(&pdev->dev, "Couldn't get platform data\n");
++		return -ENOENT;
++	}
++	ret = -ENODEV;
++	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	irq = platform_get_irq(pdev, 0);
++
++	if (!r || irq == NO_IRQ)
++		goto probe_out;
++
++	ret = -EBUSY;
++	mem_size = resource_size(r);
++	mem = request_mem_region(r->start, mem_size, pdev->name);
++
++	if (!mem){
++		dev_err(&pdev->dev,
++			"failed to get io memory region resouce.\n");
++		goto probe_out;
++	}
++	ret = -ENOMEM;
++	mmc = mmc_alloc_host(sizeof(struct ftsdc_host), &pdev->dev);
++	if (!mmc) {
++		goto probe_out;
++	}
++
++	host = mmc_priv(mmc);
++	host->mmc 	= mmc;
++	host->pdev	= pdev;
++	mywq = create_workqueue("atcsdc_queue");
++	if (NULL == mywq)
++		goto probe_free_host;
++
++	spin_lock_init(&host->complete_lock);
++	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
++	init_completion(&host->dma_complete);
++	INIT_WORK(&host->work, ftsdc_work);
++
++	host->complete_what 	= COMPLETION_NONE;
++	host->buf_active 	= XFER_NONE;
++
++#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA))
++	ftsdc_alloc_dma(host);
++#endif
++	host->mem = mem;
++	host->base = (void __iomem *) ioremap(mem->start, mem_size);
++	if (IS_ERR(host->base)){
++		ret = PTR_ERR(host->base);
++		goto probe_free_mem_region;
++	}
++	host->irq = irq;
++
++	ret = request_irq(host->irq, ftsdc_irq, 0, DRIVER_NAME, host);
++	if (ret) {
++		dev_err(&pdev->dev, "failed to request mci interrupt.\n");
++		ret = -ENOENT;
++		goto probe_free_mem_region;
++	}
++	host->irq_enabled = true;
++	/* enable card change interruption */
++	con = REG_READ(SDC_INT_MASK_REG);
++	con |= SDC_INT_MASK_REG_CARD_CHANGE;
++	REG_WRITE(con, SDC_INT_MASK_REG);
++
++	con = REG_READ(SDC_BUS_WIDTH_REG);
++	mmc->ops 	= &ftsdc_ops;
++	mmc->ocr_avail	= MMC_VDD_32_33 | MMC_VDD_33_34;
++
++	if (con & SDC_WIDE_4_BUS_SUPPORT)
++		mmc->caps |= MMC_CAP_4_BIT_DATA;
++	else if (con & SDC_WIDE_8_BUS_SUPPORT)
++		mmc->caps |= MMC_CAP_8_BIT_DATA;
++
++#ifndef A320D_BUILDIN_SDC
++	mmc->caps |= MMC_CAP_SDIO_IRQ;
++#endif
++	mmc->f_min 	= pdata->max_freq / (2 * 128);
++	mmc->f_max 	= pdata->max_freq / 2;
++	/* limit SDIO mode max size */
++	mmc->max_req_size	= 128 * 1024 * 1024 - 1;
++	mmc->max_blk_size	= 2047;
++	mmc->max_req_size	= (mmc->max_req_size + 1) / (mmc->max_blk_size + 1);
++	mmc->max_seg_size	= mmc->max_req_size;
++	mmc->max_blk_count = (1<<17)-1;
++
++	/* kernel default value. see Doc/block/biodocs.txt */
++	/*
++	 'struct mmc_host' has no member named 'max_phys_segs'
++	 'struct mmc_host' has no member named 'max_hw_segs'
++	*/
++//	mmc->max_phys_segs	= 128;
++//	mmc->max_hw_segs	= 128;
++
++	/* set fifo lenght and default threshold half */
++	con = REG_READ(SDC_FEATURE_REG);
++	host->fifo_len = (con & SDC_FEATURE_REG_FIFO_DEPTH) * sizeof(u32);
++
++	dbg(host, dbg_debug,
++	    "probe: mapped mci_base:%p irq:%u.\n",
++	    host->base, host->irq);
++
++	dbg_dumpregs(host, "");
++	ret = mmc_add_host(mmc);
++	if (ret) {
++		dev_err(&pdev->dev, "failed to add mmc host.\n");
++		goto probe_free_irq;
++	}
++	ftsdc_debugfs_attach(host);
++	platform_set_drvdata(pdev, mmc);
++	dev_info(&pdev->dev, "%s - using %s SDIO IRQ\n", mmc_hostname(mmc),
++		 mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");
++	return 0;
++
++ probe_free_irq:
++	free_irq(host->irq, host);
++
++ probe_free_mem_region:
++	release_mem_region(host->mem->start, resource_size(host->mem));
++	destroy_workqueue(mywq);
++
++ probe_free_host:
++	mmc_free_host(mmc);
++
++ probe_out:
++	return ret;
++}
++
++static void ftsdc_shutdown(struct platform_device *pdev)
++{
++	struct mmc_host	*mmc = platform_get_drvdata(pdev);
++	struct ftsdc_host *host = mmc_priv(mmc);
++
++	flush_workqueue(mywq);
++	destroy_workqueue(mywq);
++
++	ftsdc_debugfs_remove(host);
++	mmc_remove_host(mmc);
++}
++
++static int __exit ftsdc_remove(struct platform_device *pdev)
++{
++	struct mmc_host		*mmc  = platform_get_drvdata(pdev);
++	struct ftsdc_host	*host = mmc_priv(mmc);
++
++	ftsdc_shutdown(pdev);
++
++	tasklet_disable(&host->pio_tasklet);
++
++	if (ftsdc_dmaexist(host))
++		kfree(host->dma_req);
++
++	free_irq(host->irq, host);
++
++	iounmap(host->base);
++	release_mem_region(host->mem->start, resource_size(host->mem));
++
++	mmc_free_host(mmc);
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int ftsdc_free_dma(struct ftsdc_host *host)
++{
++	dmad_channel_free(host->dma_req);
++	return 0;
++}
++
++static int ftsdc_suspend(struct platform_device *pdev, pm_message_t state)
++{
++	struct mmc_host *mmc = platform_get_drvdata(pdev);
++	struct ftsdc_host *host = mmc_priv(mmc);
++	int ret = 0;
++	if (mmc) {
++		ftsdc_free_dma(host);
++	}
++	return ret;
++
++}
++
++static int ftsdc_resume(struct platform_device *pdev)
++{
++	struct mmc_host *mmc = platform_get_drvdata(pdev);
++	int ret = 0;
++	struct ftsdc_host *host = mmc_priv(mmc);
++	if (mmc) {
++#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA))
++		ftsdc_alloc_dma(host);
++#endif
++	}
++	return ret;
++}
++
++#else
++#define ftsdc_suspend NULL
++#define ftsdc_resume NULL
++#endif
++
++static struct platform_driver ftsdc_driver = {
++	.driver	= {
++		.name	= "ftsdc010",
++		.owner	= THIS_MODULE,
++		.of_match_table = of_match_ptr(ftsdc_mmc_dt_ids),
++	},
++	.remove		= __exit_p(ftsdc_remove),
++	.shutdown	= ftsdc_shutdown,
++	.suspend	= ftsdc_suspend,
++	.resume		= ftsdc_resume,
++};
++
++module_platform_driver_probe(ftsdc_driver, ftsdc_probe);
++MODULE_DESCRIPTION("Andestech Leopard MMC/SD Card Interface driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/mmc/host/ftsdc010.h b/drivers/mmc/host/ftsdc010.h
+new file mode 100644
+index 000000000000..d8cbe57fab1d
+--- /dev/null
++++ b/drivers/mmc/host/ftsdc010.h
+@@ -0,0 +1,257 @@
++/*
++ *  linux/driver/mmc/ftsdc010.h - Andestech MMC/SD driver
++ *  Andestech FTSDC010 Device Driver
++ *
++ *  Andestech (C) 2005 Faraday Corp. (http://www.Andestech.com)
++ *
++ *  All Rights Reserved
++ */
++#ifndef _FTSDC010_H_
++#define _FTSDC010_H_
++
++#define DELAY_FOR_DMA_READ
++
++#ifdef SD_DEBUG
++	#define P_DEBUG(fmt, args...) printk(KERN_ALERT "SD: " fmt, ## args)
++#else
++	#define P_DEBUG(a...)
++#endif
++#define P_DEBUGG(a...)
++
++/* used for dma timeout */
++#define SDC_TIMEOUT_BASE			(HZ/2)	// Unit is 500 ms
++
++/* used for pio retry times */
++#define SDC_PIO_RETRY				0x300000
++
++/* sd controller register */
++#define SDC_CMD_REG				0x00000000
++#define SDC_ARGU_REG				0x00000004
++#define SDC_RESPONSE0_REG			0x00000008
++#define SDC_RESPONSE1_REG			0x0000000C
++#define SDC_RESPONSE2_REG			0x00000010
++#define SDC_RESPONSE3_REG			0x00000014
++#define SDC_RSP_CMD_REG				0x00000018
++#define SDC_DATA_CTRL_REG			0x0000001C
++#define SDC_DATA_TIMER_REG			0x00000020
++#define SDC_DATA_LEN_REG			0x00000024
++#define SDC_STATUS_REG				0x00000028
++#define SDC_CLEAR_REG				0x0000002C
++#define SDC_INT_MASK_REG			0x00000030
++#define SDC_POWER_CTRL_REG			0x00000034
++#define SDC_CLOCK_CTRL_REG			0x00000038
++#define SDC_BUS_WIDTH_REG			0x0000003C
++#define SDC_DATA_WINDOW_REG			0x00000040
++
++#ifdef A320D_BUILDIN_SDC
++#define SDC_FEATURE_REG				0x00000044
++#define SDC_REVISION_REG			0x00000048
++#else
++#define SDC_MMC_INT_RSP_REG			0x00000044
++#define SDC_GP_OUTPUT_REG			0x00000048
++#define SDC_FEATURE_REG				0x0000009C
++#define SDC_REVISION_REG			0x000000A0
++#endif
++
++#define SDC_SDIO_CTRL1_REG			0x0000006C
++#define SDC_SDIO_CTRL2_REG			0x00000070
++#define SDC_SDIO_STATUS_REG			0x00000074
++
++/* bit mapping of command register */
++#define SDC_CMD_REG_INDEX			0x0000003F
++#define SDC_CMD_REG_NEED_RSP			0x00000040
++#define SDC_CMD_REG_LONG_RSP			0x00000080
++#define SDC_CMD_REG_APP_CMD			0x00000100
++#define SDC_CMD_REG_CMD_EN			0x00000200
++#define SDC_CMD_REG_SDC_RST			0x00000400
++#define SDC_CMD_MMC_INT_STOP			0x00000800
++
++/* bit mapping of response command register */
++#define SDC_RSP_CMD_REG_INDEX			0x0000003F
++#define SDC_RSP_CMD_REG_APP			0x00000040
++
++/* bit mapping of data control register */
++#define SDC_DATA_CTRL_REG_BLK_SIZE		0x0000000F
++#define SDC_DATA_CTRL_REG_DATA_WRITE		0x00000010
++#define SDC_DATA_CTRL_REG_DMA_EN		0x00000020
++#define SDC_DATA_CTRL_REG_DATA_EN		0x00000040
++#define SDC_DATA_CTRL_REG_FIFOTH		0x00000080
++#define SDC_DATA_CTRL_REG_DMA_TYPE		0x00000300
++#define SDC_DATA_CTRL_REG_FIFO_RST		0x00000400
++#define SDC_CPRM_DATA_CHANGE_ENDIAN_EN		0x00000800
++#define SDC_CPRM_DATA_SWAP_HL_EN		0x00001000
++
++#define SDC_DMA_TYPE_1				0x00000000
++#define SDC_DMA_TYPE_4				0x00000100
++#define SDC_DMA_TYPE_8				0x00000200
++
++/* bit mapping of status register */
++#define SDC_STATUS_REG_RSP_CRC_FAIL		0x00000001
++#define SDC_STATUS_REG_DATA_CRC_FAIL		0x00000002
++#define SDC_STATUS_REG_RSP_TIMEOUT		0x00000004
++#define SDC_STATUS_REG_DATA_TIMEOUT		0x00000008
++#define SDC_STATUS_REG_RSP_CRC_OK		0x00000010
++#define SDC_STATUS_REG_DATA_CRC_OK		0x00000020
++#define SDC_STATUS_REG_CMD_SEND			0x00000040
++#define SDC_STATUS_REG_DATA_END			0x00000080
++#define SDC_STATUS_REG_FIFO_UNDERRUN		0x00000100
++#define SDC_STATUS_REG_FIFO_OVERRUN		0x00000200
++#define SDC_STATUS_REG_CARD_CHANGE		0x00000400
++#define SDC_STATUS_REG_CARD_DETECT		0x00000800
++#define SDC_STATUS_REG_CARD_LOCK		0x00001000
++#define SDC_STATUS_REG_CP_READY			0x00002000
++#define SDC_STATUS_REG_CP_BUF_READY		0x00004000
++#define SDC_STATUS_REG_PLAIN_TEXT_READY		0x00008000
++#define SDC_STATUS_REG_SDIO_INTR	    	0x00010000
++
++/* bit mapping of clear register */
++#define SDC_CLEAR_REG_RSP_CRC_FAIL		0x00000001
++#define SDC_CLEAR_REG_DATA_CRC_FAIL		0x00000002
++#define SDC_CLEAR_REG_RSP_TIMEOUT		0x00000004
++#define SDC_CLEAR_REG_DATA_TIMEOUT		0x00000008
++#define SDC_CLEAR_REG_RSP_CRC_OK		0x00000010
++#define SDC_CLEAR_REG_DATA_CRC_OK		0x00000020
++#define SDC_CLEAR_REG_CMD_SEND			0x00000040
++#define SDC_CLEAR_REG_DATA_END			0x00000080
++#define SDC_CLEAR_REG_CARD_CHANGE		0x00000400
++#define SDC_CLEAR_REG_SDIO_INTR			0x00010000
++
++/* bit mapping of int_mask register */
++#define SDC_INT_MASK_REG_RSP_CRC_FAIL		0x00000001
++#define SDC_INT_MASK_REG_DATA_CRC_FAIL		0x00000002
++#define SDC_INT_MASK_REG_RSP_TIMEOUT		0x00000004
++#define SDC_INT_MASK_REG_DATA_TIMEOUT		0x00000008
++#define SDC_INT_MASK_REG_RSP_CRC_OK		0x00000010
++#define SDC_INT_MASK_REG_DATA_CRC_OK		0x00000020
++#define SDC_INT_MASK_REG_CMD_SEND		0x00000040
++#define SDC_INT_MASK_REG_DATA_END		0x00000080
++#define SDC_INT_MASK_REG_FIFO_UNDERRUN		0x00000100
++#define SDC_INT_MASK_REG_FIFO_OVERRUN		0x00000200
++#define SDC_INT_MASK_REG_CARD_CHANGE		0x00000400
++#define SDC_INT_MASK_REG_CARD_LOCK		0x00001000
++#define SDC_INT_MASK_REG_CP_READY		0x00002000
++#define SDC_INT_MASK_REG_CP_BUF_READY		0x00004000
++#define SDC_INT_MASK_REG_PLAIN_TEXT_READY	0x00008000
++#define SDC_INT_MASK_REG_SDIO_INTR	    	0x00010000
++
++
++#define SDC_CARD_INSERT				0x0
++#define SDC_CARD_REMOVE				SDC_STATUS_REG_CARD_DETECT
++
++/* bit mapping of power control register */
++#define SDC_POWER_CTRL_REG_POWER_ON		0x00000010
++#define SDC_POWER_CTRL_REG_POWER_BITS		0x0000000F
++
++/* bit mapping of clock control register */
++#define SDC_CLOCK_CTRL_REG_CLK_DIV		0x0000007F
++#define SDC_CLOCK_CTRL_REG_CARD_TYPE		0x00000080
++#define SDC_CLOCK_CTRL_REG_CLK_DIS		0x00000100
++
++/* card type */
++#define SDC_CARD_TYPE_SD			SDC_CLOCK_REG_CARD_TYPE
++#define SDC_CARD_TYPE_MMC			0x0
++
++/* bit mapping of bus width register */
++#define SDC_BUS_WIDTH_REG_SINGLE_BUS		0x00000001
++#define SDC_BUS_WIDTH_REG_WIDE_8_BUS		0x00000002
++#define SDC_BUS_WIDTH_REG_WIDE_4_BUS		0x00000004
++#define SDC_BUS_WIDTH_REG_WIDE_BUS_SUPPORT	0x00000018
++#define SDC_BUS_WIDTH_REG_CARD_DETECT		0x00000020
++
++#define SDC_WIDE_4_BUS_SUPPORT			0x00000008
++#define SDC_WIDE_8_BUS_SUPPORT			0x00000010
++
++/* bit mapping of feature register */
++#define SDC_FEATURE_REG_FIFO_DEPTH		0x000000FF
++#define SDC_FEATURE_REG_CPRM_FUNCTION		0x00000100
++
++/* bit mapping of sdio control register */
++#define SDC_SDIO_CTRL1_REG_SDIO_BLK_NO		0xFFFF8000
++#define SDC_SDIO_CTRL1_REG_SDIO_ENABLE		0x00004000
++#define SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE	0x00002000
++#define SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE	0x00001000
++#define SDC_SDIO_CTRL1_REG_SDIO_BLK_SIZE	0x00000FFF
++
++/* bit mapping of sdio status register */
++#define SDC_SDIO_SDIO_STATUS_REG_FIFO_REMAIN_NO	0x00FE0000
++#define SDC_SDIO_SDIO_STATUS_REG_SDIO_BLK_CNT	0x0001FFFF
++
++enum ftsdc_waitfor {
++	COMPLETION_NONE,
++	COMPLETION_FINALIZE,
++	COMPLETION_CMDSENT,
++	COMPLETION_RSPFIN,
++	COMPLETION_XFER_PROGRESS,
++};
++
++struct ftsdc_host {
++	struct platform_device	*pdev;
++	struct mmc_host		*mmc;
++	struct resource		*mem;
++	struct clk		*clk;
++	void __iomem		*base;
++	int			irq;
++
++	unsigned int		real_rate;
++	bool			irq_enabled;
++	unsigned int		fifo_len;	/* bytes */
++	unsigned int		last_opcode;	/* keep last successful cmd to judge application specific command */
++
++	struct mmc_request	*mrq;
++	int			cmd_is_stop;
++
++	spinlock_t		complete_lock;
++	enum ftsdc_waitfor	complete_what;
++
++	struct completion	dma_complete;
++	dmad_chreq		*dma_req;
++	bool			dodma;
++	bool			dma_finish;
++
++
++	u32			buf_sgptr;	/* keep next scallterlist buffer index */
++	u32			buf_bytes;	/* keep current total scallterlist buffer length */
++	u32			buf_count;	/* keep real data size rw from sd */
++	u32			*buf_ptr;	/* keep current scallterlist buffer address */
++#define XFER_NONE 0
++#define XFER_READ 1
++#define XFER_WRITE 2
++	u32			buf_active;	/* keep current transfer mode */
++
++	int			bus_width;
++
++	char 			dbgmsg_cmd[301];
++	char 			dbgmsg_dat[301];
++	char			*status;
++
++	unsigned int		ccnt, dcnt;
++	struct tasklet_struct	pio_tasklet;
++	struct work_struct work;
++
++#ifdef CONFIG_DEBUG_FS
++	struct dentry		*debug_root;
++	struct dentry		*debug_state;
++	struct dentry		*debug_regs;
++#endif
++};
++
++struct ftsdc_mmc_config {
++	/* get_cd()/get_wp() may sleep */
++	int	(*get_cd)(int module);
++	int	(*get_ro)(int module);
++
++	void	(*set_power)(int module, bool on);
++
++	/* wires == 0 is equivalent to wires == 4 (4-bit parallel) */
++	u8	wires;
++
++	u32     max_freq;
++
++	/* any additional host capabilities: OR'd in to mmc->f_caps */
++	u32     caps;
++
++	/* Number of sg segments */
++	u8	nr_sg;
++};
++
++#endif
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0005-Non-cacheability-and-Cache-support.patch b/board/andes/ae350/patches/linux/0005-Non-cacheability-and-Cache-support.patch
new file mode 100644
index 0000000000..326bbb71c7
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0005-Non-cacheability-and-Cache-support.patch
@@ -0,0 +1,1132 @@
+From e0f510874201d32d720fc94948a266a29d9b4b52 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:20:40 +0800
+Subject: [PATCH 05/12] Non-cacheability and Cache support
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/andesv5/Makefile           |   4 +
+ arch/riscv/andesv5/cache.c            | 414 ++++++++++++++++++++++++++
+ arch/riscv/andesv5/cctl.c             | 260 ++++++++++++++++
+ arch/riscv/andesv5/noncache_dma.c     | 113 +++++++
+ arch/riscv/include/asm/andesv5/csr.h  | 160 ++++++++++
+ arch/riscv/include/asm/andesv5/proc.h |  36 +++
+ arch/riscv/include/asm/andesv5/smu.h  |  78 +++++
+ 7 files changed, 1065 insertions(+)
+ create mode 100644 arch/riscv/andesv5/Makefile
+ create mode 100644 arch/riscv/andesv5/cache.c
+ create mode 100644 arch/riscv/andesv5/cctl.c
+ create mode 100644 arch/riscv/andesv5/noncache_dma.c
+ create mode 100644 arch/riscv/include/asm/andesv5/csr.h
+ create mode 100644 arch/riscv/include/asm/andesv5/proc.h
+ create mode 100644 arch/riscv/include/asm/andesv5/smu.h
+
+diff --git a/arch/riscv/andesv5/Makefile b/arch/riscv/andesv5/Makefile
+new file mode 100644
+index 000000000000..6188956ae944
+--- /dev/null
++++ b/arch/riscv/andesv5/Makefile
+@@ -0,0 +1,4 @@
++obj-y += cctl.o
++obj-y += cache.o
++obj-y += noncache_dma.o
++obj-y += sbi.o
+diff --git a/arch/riscv/andesv5/cache.c b/arch/riscv/andesv5/cache.c
+new file mode 100644
+index 000000000000..3d4e82f3525e
+--- /dev/null
++++ b/arch/riscv/andesv5/cache.c
+@@ -0,0 +1,414 @@
++#include <linux/irqflags.h>
++#include <linux/module.h>
++#include <linux/cpu.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/cacheinfo.h>
++#include <linux/sizes.h>
++#include <linux/smp.h>
++#include <asm/csr.h>
++#include <asm/sbi.h>
++#include <asm/io.h>
++#include <asm/andesv5/proc.h>
++#include <asm/andesv5/csr.h>
++#ifdef CONFIG_PERF_EVENTS
++#include <asm/perf_event.h>
++#endif
++
++#define MAX_CACHE_LINE_SIZE 256
++#define EVSEL_MASK	0xff
++#define SEL_PER_CTL	8
++#define SEL_OFF(id)	(8 * (id % 8))
++
++static void __iomem *l2c_base;
++
++DEFINE_PER_CPU(struct andesv5_cache_info, cpu_cache_info) = {
++	.init_done = 0,
++	.dcache_line_size = SZ_32
++};
++static void fill_cpu_cache_info(struct andesv5_cache_info *cpu_ci)
++{
++	struct cpu_cacheinfo *this_cpu_ci =
++		get_cpu_cacheinfo(smp_processor_id());
++	struct cacheinfo *this_leaf = this_cpu_ci->info_list;
++	unsigned int i = 0;
++
++	for(; i< this_cpu_ci->num_leaves ; i++, this_leaf++)
++		if(this_leaf->type == CACHE_TYPE_DATA) {
++			cpu_ci->dcache_line_size = this_leaf->coherency_line_size;
++		}
++	cpu_ci->init_done = true;
++}
++
++
++inline int get_cache_line_size(void)
++{
++	struct andesv5_cache_info *cpu_ci =
++		&per_cpu(cpu_cache_info, smp_processor_id());
++
++	if(unlikely(cpu_ci->init_done == false))
++		fill_cpu_cache_info(cpu_ci);
++	return cpu_ci->dcache_line_size;
++}
++
++static uint32_t cpu_l2c_get_cctl_status(void)
++{
++	return readl((void*)(l2c_base + L2C_REG_STATUS_OFFSET));
++}
++
++void cpu_dcache_wb_range(unsigned long start, unsigned long end, int line_size)
++{
++	int mhartid = smp_processor_id();
++	unsigned long pa;
++	while (end > start) {
++		custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start);
++		custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_WB);
++
++		if (l2c_base) {
++			pa = virt_to_phys((void*)start);
++			writel(pa, (void*)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid)));
++			writel(CCTL_L2_PA_WB, (void*)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid)));
++			while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++					!= CCTL_L2_STATUS_IDLE);
++		}
++
++		start += line_size;
++	}
++}
++
++void cpu_dcache_inval_range(unsigned long start, unsigned long end, int line_size)
++{
++	int mhartid = smp_processor_id();
++	unsigned long pa;
++	while (end > start) {
++		custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start);
++		custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_INVAL);
++
++		if (l2c_base) {
++			pa = virt_to_phys((void*)start);
++			writel(pa, (void*)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid)));
++			writel(CCTL_L2_PA_INVAL, (void*)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid)));
++			while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++					!= CCTL_L2_STATUS_IDLE);
++		}
++
++		start += line_size;
++	}
++}
++void cpu_dma_inval_range(unsigned long start, unsigned long end)
++{
++	unsigned long flags;
++	unsigned long line_size = get_cache_line_size();
++	unsigned long old_start = start;
++	unsigned long old_end = end;
++	char cache_buf[2][MAX_CACHE_LINE_SIZE]={0};
++
++	if (unlikely(start == end))
++		return;
++
++	start = start & (~(line_size - 1));
++	end = ((end + line_size - 1) & (~(line_size - 1)));
++
++	local_irq_save(flags);
++	if (unlikely(start != old_start)) {
++		memcpy(&cache_buf[0][0], (void *)start, line_size);
++	}
++	if (unlikely(end != old_end)) {
++		memcpy(&cache_buf[1][0], (void *)(old_end & (~(line_size - 1))), line_size);
++	}
++	cpu_dcache_inval_range(start, end, line_size);
++	if (unlikely(start != old_start)) {
++		memcpy((void *)start, &cache_buf[0][0], (old_start & (line_size - 1)));
++	}
++	if (unlikely(end != old_end)) {
++		memcpy((void *)(old_end + 1), &cache_buf[1][(old_end & (line_size - 1)) + 1], end - old_end - 1);
++	}
++	local_irq_restore(flags);
++
++}
++EXPORT_SYMBOL(cpu_dma_inval_range);
++
++void cpu_dma_wb_range(unsigned long start, unsigned long end)
++{
++	unsigned long flags;
++	unsigned long line_size = get_cache_line_size();
++
++	local_irq_save(flags);
++	start = start & (~(line_size - 1));
++	cpu_dcache_wb_range(start, end, line_size);
++	local_irq_restore(flags);
++}
++EXPORT_SYMBOL(cpu_dma_wb_range);
++
++/* L1 Cache */
++int cpu_l1c_status(void)
++{
++	/* TODO */
++	// return SBI_CALL_0(SBI_L1CACHE_STATUS);
++	return 0;
++}
++
++void cpu_icache_enable(void *info)
++{
++	/* TODO */
++	// SBI_CALL_1(SBI_ICACHE_OP, 1);
++}
++
++void cpu_icache_disable(void *info)
++{
++	/* TODO */
++	// unsigned long flags;
++
++	// local_irq_save(flags);
++	// SBI_CALL_1(SBI_ICACHE_OP, 0);
++	// local_irq_restore(flags);
++}
++
++void cpu_dcache_enable(void *info)
++{
++	/* TODO */
++	// SBI_CALL_1(SBI_DCACHE_OP, 1);
++}
++
++void cpu_dcache_disable(void *info)
++{
++	/* TODO */
++	// unsigned long flags;
++
++	// local_irq_save(flags);
++	// SBI_CALL_1(SBI_DCACHE_OP, 0);
++	// local_irq_restore(flags);
++}
++
++/* L2 Cache */
++uint32_t cpu_l2c_ctl_status(void)
++{
++	return readl((void*)(l2c_base + L2C_REG_CTL_OFFSET));
++}
++
++void cpu_l2c_enable(void)
++{
++#ifdef CONFIG_SMP
++	int mhartid = smp_processor_id();
++#else
++	int mhartid = 0;
++#endif
++	unsigned int val;
++
++	/* No l2 cache */
++	if(!l2c_base)
++		return;
++
++	/* l2 cache has enabled */
++	if(cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN)
++		return;
++
++	/* Enable l2 cache*/
++	val = readl((void*)(l2c_base + L2C_REG_CTL_OFFSET));
++	val |= L2_CACHE_CTL_mskCEN;
++
++	writel(val, (void*)(l2c_base + L2C_REG_CTL_OFFSET));
++	while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++			!= CCTL_L2_STATUS_IDLE);
++}
++
++void cpu_l2c_disable(void)
++{
++#ifdef CONFIG_SMP
++	int mhartid = smp_processor_id();
++#else
++	int mhartid = 0;
++#endif
++	unsigned int val;
++
++	/*No l2 cache */
++	if(!l2c_base)
++		return;
++
++	/*l2 cache has disabled*/
++	if(!(cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN))
++		return;
++
++	/*L2 write-back and invalidate all*/
++	writel(CCTL_L2_WBINVAL_ALL, (void*)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid)));
++	while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++			!= CCTL_L2_STATUS_IDLE);
++
++	/*Disable L2 cache*/
++	val = readl((void*)(l2c_base + L2C_REG_CTL_OFFSET));
++	val &= (~L2_CACHE_CTL_mskCEN);
++
++	writel(val, (void*)(l2c_base + L2C_REG_CTL_OFFSET));
++	while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++			!= CCTL_L2_STATUS_IDLE);
++}
++
++#ifndef CONFIG_SMP
++void cpu_l2c_inval_range(unsigned long pa, unsigned long size)
++{
++	unsigned long line_size = get_cache_line_size();
++	unsigned long start = pa, end = pa + size;
++	unsigned long align_start, align_end;
++
++	align_start = start & ~(line_size - 1);
++	align_end  = (end + line_size - 1) & ~(line_size - 1);
++
++	while(align_end > align_start){
++		writel(align_start, (void*)(l2c_base + L2C_REG_C0_ACC_OFFSET));
++		writel(CCTL_L2_PA_INVAL, (void*)(l2c_base + L2C_REG_C0_CMD_OFFSET));
++		while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK)
++				!= CCTL_L2_STATUS_IDLE);
++		align_start += line_size;
++	}
++}
++EXPORT_SYMBOL(cpu_l2c_inval_range);
++
++void cpu_l2c_wb_range(unsigned long pa, unsigned long size)
++{
++	unsigned long line_size = get_cache_line_size();
++	unsigned long start = pa, end = pa + size;
++	unsigned long align_start, align_end;
++
++	align_start = start & ~(line_size - 1);
++	align_end  = (end + line_size - 1) & ~(line_size - 1);
++
++	while(align_end > align_start){
++		writel(align_start, (void*)(l2c_base + L2C_REG_C0_ACC_OFFSET));
++		writel(CCTL_L2_PA_WB, (void*)(l2c_base + L2C_REG_C0_CMD_OFFSET));
++		while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK)
++				!= CCTL_L2_STATUS_IDLE);
++		align_start += line_size;
++	}
++}
++EXPORT_SYMBOL(cpu_l2c_wb_range);
++#else
++void cpu_l2c_inval_range(unsigned long pa, unsigned long size)
++{
++	int mhartid = smp_processor_id();
++	unsigned long line_size = get_cache_line_size();
++	unsigned long start = pa, end = pa + size;
++	unsigned long align_start, align_end;
++
++	align_start = start & ~(line_size - 1);
++	align_end  = (end + line_size - 1) & ~(line_size - 1);
++
++	while(align_end > align_start){
++		writel(align_start, (void*)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid)));
++		writel(CCTL_L2_PA_INVAL, (void*)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid)));
++		while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++				!= CCTL_L2_STATUS_IDLE);
++		align_start += line_size;
++	}
++}
++EXPORT_SYMBOL(cpu_l2c_inval_range);
++
++void cpu_l2c_wb_range(unsigned long pa, unsigned long size)
++{
++	int mhartid = smp_processor_id();
++	unsigned long line_size = get_cache_line_size();
++	unsigned long start = pa, end = pa + size;
++	unsigned long align_start, align_end;
++
++	align_start = start & ~(line_size - 1);
++	align_end  = (end + line_size - 1) & ~(line_size - 1);
++
++	while(align_end > align_start){
++		writel(align_start, (void*)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid)));
++		writel(CCTL_L2_PA_WB, (void*)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid)));
++		while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid))
++				!= CCTL_L2_STATUS_IDLE);
++		align_start += line_size;
++	}
++}
++EXPORT_SYMBOL(cpu_l2c_wb_range);
++#endif
++
++#ifdef CONFIG_PERF_EVENTS
++int cpu_l2c_get_counter_idx(struct l2c_hw_events *l2c)
++{
++	int idx;
++
++	idx = find_next_zero_bit(l2c->used_mask, L2C_MAX_COUNTERS - 1, 0);
++	return idx;
++}
++
++void l2c_write_counter(int idx, u64 value)
++{
++	u32 vall = value;
++	u32 valh = value >> 32;
++
++	writel(vall, (void*)(l2c_base + L2C_REG_CN_HPM_OFFSET(idx)));
++	writel(valh, (void*)(l2c_base + L2C_REG_CN_HPM_OFFSET(idx) + 0x4));
++}
++
++u64 l2c_read_counter(int idx)
++{
++	u32 vall = readl((void*)(l2c_base + L2C_REG_CN_HPM_OFFSET(idx)));
++	u32 valh = readl((void*)(l2c_base + L2C_REG_CN_HPM_OFFSET(idx) + 0x4));
++	u64 val = ((u64)valh << 32) | vall;
++
++	return val;
++}
++
++void l2c_pmu_disable_counter(int idx)
++{
++	int n = idx / SEL_PER_CTL;
++	u32 vall = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	u32 valh = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++	u64 val = ((u64)valh << 32) | vall;
++
++	val |= (EVSEL_MASK << SEL_OFF(idx));
++	vall = val;
++	valh = val >> 32;
++	writel(vall, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	writel(valh, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++}
++
++#ifndef CONFIG_SMP
++void l2c_pmu_event_enable(u64 config, int idx)
++{
++	int n = idx / SEL_PER_CTL;
++	u32 vall = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	u32 valh = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++	u64 val = ((u64)valh << 32) | vall;
++
++	val = val & ~(EVSEL_MASK << SEL_OFF(idx));
++	val = val | (config << SEL_OFF(idx));
++	vall = val;
++	valh = val >> 32;
++	writel(vall, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	writel(valh, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++}
++#else
++void l2c_pmu_event_enable(u64 config, int idx)
++{
++	int n = idx / SEL_PER_CTL;
++	int mhartid = smp_processor_id();
++	u32 vall = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	u32 valh = readl((void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++	u64 val = ((u64)valh << 32) | vall;
++
++	if (config <= (CN_RECV_SNOOP_DATA(NR_CPUS - 1) & EVSEL_MASK))
++		config = config + mhartid * L2C_REG_PER_CORE_OFFSET;
++
++	val = val & ~(EVSEL_MASK << SEL_OFF(idx));
++	val = val | (config << SEL_OFF(idx));
++	vall = val;
++	valh = val >> 32;
++	writel(vall, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n)));
++	writel(valh, (void*)(l2c_base + L2C_HPM_CN_CTL_OFFSET(n) + 0x4));
++}
++#endif
++#endif
++
++int __init l2c_init(void)
++{
++	struct device_node *node ;
++
++	node = of_find_compatible_node(NULL, NULL, "cache");
++	l2c_base = of_iomap(node, 0);
++
++	return 0;
++}
++arch_initcall(l2c_init)
+diff --git a/arch/riscv/andesv5/cctl.c b/arch/riscv/andesv5/cctl.c
+new file mode 100644
+index 000000000000..f3f61db29e0d
+--- /dev/null
++++ b/arch/riscv/andesv5/cctl.c
+@@ -0,0 +1,260 @@
++/*
++ *  Copyright (C) 2009 Andes Technology Corporation
++ *  Copyright (C) 2019 Andes Technology Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/blkdev.h>
++#include <linux/proc_fs.h>
++#include <asm/andesv5/csr.h>
++#include <asm/andesv5/proc.h>
++
++#define INPUTLEN 32
++
++struct entry_struct{
++
++	char *name;
++	int perm;
++	const struct proc_ops *fops;
++};
++
++static struct proc_dir_entry *proc_cctl;
++
++#define DEBUG( enable, tagged, ...)				\
++	do{							\
++		if(enable){					\
++			if(tagged)				\
++			printk( "[ %30s() ] ", __func__);	\
++			printk( __VA_ARGS__);			\
++		}						\
++	} while( 0)
++
++static int debug = 0;
++module_param(debug, int, 0);
++
++void cpu_icache_smp_enable(void)
++{
++    int cpu_num = num_online_cpus();
++    int id = smp_processor_id();
++    int i, ret;
++
++    for(i = 0; i < cpu_num; i++){
++		if(i == id)
++			continue;
++        ret = smp_call_function_single(i, cpu_icache_enable,
++                                        NULL, true);
++        if(ret)
++            pr_err("Core %d enable I-cache Fail\n"
++                    "Error Code:%d \n", i, ret);
++    }
++    cpu_icache_enable(NULL);
++}
++
++void cpu_icache_smp_disable(void)
++{
++    int cpu_num = num_online_cpus();
++    int id = smp_processor_id();
++    int i, ret;
++
++    for(i = 0; i < cpu_num; i++){
++        if(i == id)
++            continue;
++        ret = smp_call_function_single(i, cpu_icache_disable,
++                                        NULL, true);
++        if(ret)
++            pr_err("Core %d disable I-cache Fail \n"
++                    "Error Code:%d \n", i, ret);
++    }
++    cpu_icache_disable(NULL);
++}
++
++void cpu_dcache_smp_enable(void)
++{
++    int cpu_num = num_online_cpus();
++    int id = smp_processor_id();
++    int i, ret;
++
++    for(i = 0; i < cpu_num; i++){
++        if(i == id)
++            continue;
++        ret = smp_call_function_single(i, cpu_dcache_enable,
++                                        NULL, true);
++        if(ret)
++            pr_err("Core %d disable D-cache Fail \n"
++                    "Error Code:%d \n", i, ret);
++    }
++    cpu_dcache_enable(NULL);
++}
++
++void cpu_dcache_smp_disable(void)
++{
++    int cpu_num = num_online_cpus();
++    int id = smp_processor_id();
++    int i, ret;
++
++    for(i = 0; i < cpu_num; i++){
++        if(i == id)
++            continue;
++        ret = smp_call_function_single(i, cpu_dcache_disable,
++                                        NULL, true);
++        if(ret)
++            pr_err("Core %d disable D-cache Fail \n"
++                    "Error Code:%d \n", i, ret);
++    }
++    cpu_dcache_disable(NULL);
++}
++
++static ssize_t proc_read_cache_en(struct file *file, char __user *userbuf,
++						size_t count, loff_t *ppos)
++{
++    int ret;
++    char buf[18];
++    if (!strncmp(file->f_path.dentry->d_name.name, "ic_en", 7))
++        ret = sprintf(buf, "I-cache: %s\n", (cpu_l1c_status() & CACHE_CTL_mskIC_EN) ? "Enabled" : "Disabled");
++    else if(!strncmp(file->f_path.dentry->d_name.name, "dc_en", 7))
++        ret = sprintf(buf, "D-cache: %s\n", (cpu_l1c_status() & CACHE_CTL_mskDC_EN) ? "Enabled" : "Disabled");
++	else if(!strncmp(file->f_path.dentry->d_name.name, "l2c_en", 7))
++        ret = sprintf(buf, "L2-cache: %s\n", (cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN) ? "Enabled" : "Disabled");
++	else
++		return -EFAULT;
++
++    return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
++}
++
++static ssize_t proc_write_cache_en(struct file *file,
++			const char __user *buffer, size_t count, loff_t *ppos)
++{
++
++	unsigned long en;
++	char inbuf[INPUTLEN];
++
++	if (count > INPUTLEN - 1)
++		count = INPUTLEN - 1;
++
++	if (copy_from_user(inbuf, buffer, count))
++		return -EFAULT;
++
++	inbuf[count] = '\0';
++
++	if (!sscanf(inbuf, "%lu", &en) || en > 1)
++		return -EFAULT;
++
++	if (!strncmp(file->f_path.dentry->d_name.name, "ic_en", 7)) {
++		if (en && !(cpu_l1c_status() & CACHE_CTL_mskIC_EN)) {
++#ifdef CONFIG_SMP
++			cpu_icache_smp_enable();
++#else
++			cpu_icache_enable(NULL);
++#endif
++			DEBUG(debug, 1, "I-cache: Enabled\n");
++		} else if (!en && (cpu_l1c_status() & CACHE_CTL_mskIC_EN)) {
++#ifdef CONFIG_SMP
++			cpu_icache_smp_disable();
++#else
++			cpu_icache_disable(NULL);
++#endif
++			DEBUG(debug, 1, "I-cache: Disabled\n");
++		}
++	} else if(!strncmp(file->f_path.dentry->d_name.name, "dc_en", 7)) {
++		if (en && !(cpu_l1c_status() & CACHE_CTL_mskDC_EN)) {
++#ifdef CONFIG_SMP
++			cpu_dcache_smp_enable();
++#else
++			cpu_dcache_enable(NULL);
++#endif
++			DEBUG(debug, 1, "D-cache: Enabled\n");
++		} else if (!en && (cpu_l1c_status() & CACHE_CTL_mskDC_EN)) {
++#ifdef CONFIG_SMP
++			cpu_dcache_smp_disable();
++#else
++			cpu_dcache_disable(NULL);
++#endif
++			DEBUG(debug, 1, "D-cache: Disabled\n");
++		}
++	}else if(!strncmp(file->f_path.dentry->d_name.name, "l2c_en", 7)){
++		if (en && !(cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN)) {
++			cpu_l2c_enable();
++			DEBUG(debug, 1, "L2-cache: Enabled\n");
++		} else if (!en && (cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN)) {
++			cpu_l2c_disable();
++			DEBUG(debug, 1, "L2-cache: Disabled\n");
++		}
++	}else{
++		return -EFAULT;
++	}
++
++	return count;
++}
++
++static const struct proc_ops en_fops = {
++	.proc_open = simple_open,
++	.proc_read = proc_read_cache_en,
++	.proc_write = proc_write_cache_en,
++};
++
++static void create_seq_entry(struct entry_struct *e, mode_t mode,
++			     struct proc_dir_entry *parent)
++{
++
++	struct proc_dir_entry *entry = proc_create(e->name, mode, parent, e->fops);
++
++	if (!entry)
++		printk(KERN_ERR "invalid %s register.\n", e->name);
++}
++
++static void install_proc_table(struct entry_struct *table)
++{
++	while (table->name) {
++
++		create_seq_entry(table, table->perm, proc_cctl);
++		table++;
++	}
++}
++
++static void remove_proc_table(struct entry_struct *table)
++{
++
++	while (table->name) {
++		remove_proc_entry(table->name, proc_cctl);
++		table++;
++	}
++}
++
++struct entry_struct proc_table_cache[] = {
++
++	{"ic_en", 0644, &en_fops},
++	{"dc_en", 0644, &en_fops},
++	{"l2c_en", 0644, &en_fops},
++	{NULL, 0, 0}
++};
++static int __init init_cctl(void)
++{
++
++	DEBUG(debug, 0, "CCTL module registered\n");
++
++	if(!(proc_cctl = proc_mkdir("cctl", NULL)))
++		return -ENOMEM;
++
++	install_proc_table(proc_table_cache);
++
++	return 0;
++}
++
++static void __exit cleanup_cctl(void)
++{
++
++	remove_proc_table(proc_table_cache);
++	remove_proc_entry("cctl", NULL);
++
++	DEBUG(debug, 1, "CCTL module unregistered\n");
++}
++
++module_init(init_cctl);
++module_exit(cleanup_cctl);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Userspace Cache Control Module");
+diff --git a/arch/riscv/andesv5/noncache_dma.c b/arch/riscv/andesv5/noncache_dma.c
+new file mode 100644
+index 000000000000..fa83cebad777
+--- /dev/null
++++ b/arch/riscv/andesv5/noncache_dma.c
+@@ -0,0 +1,113 @@
++/*
++ * Copyright (C) 2017 SiFive
++ *   Wesley Terpstra <wesley@sifive.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see the file COPYING, or write
++ * to the Free Software Foundation, Inc.,
++ */
++
++#include <linux/gfp.h>
++#include <linux/mm.h>
++#include <linux/dma-mapping.h>
++#include <linux/dma-direct.h>
++#include <linux/scatterlist.h>
++#include <asm/andesv5/proc.h>
++
++static void dma_flush_page(struct page *page, size_t size)
++{
++	unsigned long k_d_vaddr;
++	/*
++	 * Invalidate any data that might be lurking in the
++	 * kernel direct-mapped region for device DMA.
++	 */
++	k_d_vaddr = (unsigned long)page_address(page);
++	memset((void *)k_d_vaddr, 0, size);
++	cpu_dma_wb_range(k_d_vaddr, k_d_vaddr + size);
++	cpu_dma_inval_range(k_d_vaddr, k_d_vaddr + size);
++
++}
++
++
++static inline void cache_op(phys_addr_t paddr, size_t size,
++		void (*fn)(unsigned long start, unsigned long end))
++{
++	unsigned long start;
++
++	start = (unsigned long)phys_to_virt(paddr);
++	fn(start, start + size);
++}
++
++void arch_sync_dma_for_device(phys_addr_t paddr,
++		size_t size, enum dma_data_direction dir)
++{
++	switch (dir) {
++	case DMA_FROM_DEVICE:
++		cache_op(paddr, size, cpu_dma_inval_range);
++		break;
++	case DMA_TO_DEVICE:
++	case DMA_BIDIRECTIONAL:
++		cache_op(paddr, size, cpu_dma_wb_range);
++		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:
++		break;
++	case DMA_FROM_DEVICE:
++	case DMA_BIDIRECTIONAL:
++		cache_op(paddr, size, cpu_dma_inval_range);
++		break;
++	default:
++		BUG();
++	}
++}
++
++void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
++               gfp_t gfp, unsigned long attrs)
++{
++	void* kvaddr, *coherent_kvaddr;
++	size = PAGE_ALIGN(size);
++
++	kvaddr = dma_direct_alloc_pages(dev, size, handle, gfp, attrs);
++	if (!kvaddr)
++		goto no_mem;
++	coherent_kvaddr = ioremap_nocache(dma_to_phys(dev, *handle), size);
++	if (!coherent_kvaddr)
++		goto no_map;
++
++	dma_flush_page(virt_to_page(kvaddr),size);
++	return coherent_kvaddr;
++no_map:
++	dma_direct_free_pages(dev, size, kvaddr, *handle, attrs);
++no_mem:
++	return NULL;
++}
++
++void arch_dma_free(struct device *dev, size_t size, void *vaddr,
++			   dma_addr_t handle, unsigned long attrs)
++{
++	void *kvaddr = phys_to_virt(dma_to_phys(dev, handle));
++
++	size = PAGE_ALIGN(size);
++	iounmap(vaddr);
++	dma_direct_free_pages(dev, size, kvaddr, handle, attrs);
++
++	return;
++}
+diff --git a/arch/riscv/include/asm/andesv5/csr.h b/arch/riscv/include/asm/andesv5/csr.h
+new file mode 100644
+index 000000000000..43936e1fb658
+--- /dev/null
++++ b/arch/riscv/include/asm/andesv5/csr.h
+@@ -0,0 +1,160 @@
++/* micm_cfg: Instruction Cache/Memory Configuration Register */
++#define MICM_CFG 0xfc0
++
++#define MICM_CFG_ISET_OFFSET		0
++#define MICM_CFG_IWAY_OFFSET		3
++#define MICM_CFG_ISZ_OFFSET		6
++#define MICM_CFG_ILCK_OFFSET		9
++#define MICM_CFG_IC_ECC_OFFSET		10
++#define MICM_CFG_ILMB_OFFSET		12
++#define MICM_CFG_ILMSZ_OFFSET		15
++#define MICM_CFG_ULM_2BANK_OFFSET	20
++#define MICM_CFG_ILM_ECC_OFFSET		21
++
++
++#define MICM_CFG_ISET_MASK	(0x7  << MICM_CFG_ISET_OFFSET)
++#define MICM_CFG_IWAY_MASK	(0x7  << MICM_CFG_IWAY_OFFSET)
++#define MICM_CFG_ISZ_MASK	(0x7  << MICM_CFG_ISZ_OFFSET)
++#define MICM_CFG_ILCK_MASK	(0x1  << MICM_CFG_ILCK_OFFSET)
++#define MICM_CFG_IC_ECC_MASK	(0x3  << MICM_CFG_IC_ECC_OFFSET)
++#define MICM_CFG_ILMB_MASK	(0x7  << MICM_CFG_ILMB_OFFSET)
++#define MICM_CFG_ILMSZ_MASK	(0x1f << MICM_CFG_ILMSZ_OFFSET)
++#define MICM_CFG_ULM_2BANK_MASK	(0x1  << MICM_CFG_ULM_2BANK_OFFSET)
++#define MICM_CFG_ILM_ECC_MASK	(0x3  << MICM_CFG_ILM_ECC_OFFSET)
++
++/* mdcm_cfg: Data Cache/Memory Configuration Register */
++#define MDCM_CFG 0xfc1
++
++#define MDCM_CFG_DSET_OFFSET		0
++#define MDCM_CFG_DWAY_OFFSET		3
++#define MDCM_CFG_DSZ_OFFSET		6
++#define MDCM_CFG_DLCK_OFFSET		9
++#define MDCM_CFG_DC_ECC_OFFSET		10
++#define MDCM_CFG_DLMB_OFFSET		12
++#define MDCM_CFG_DLMSZ_OFFSET		15
++#define MDCM_CFG_ULM_2BANK_OFFSET	20
++#define MDCM_CFG_DLM_ECC_OFFSET		21
++
++
++#define MDCM_CFG_DSET_MASK	(0x7  << MDCM_CFG_DSET_OFFSET)
++#define MDCM_CFG_DWAY_MASK	(0x7  << MDCM_CFG_DWAY_OFFSET)
++#define MDCM_CFG_DSZ_MASK	(0x7  << MDCM_CFG_DSZ_OFFSET)
++#define MDCM_CFG_DLCK_MASK	(0x1  << MDCM_CFG_DLCK_OFFSET)
++#define MDCM_CFG_DC_ECC_MASK	(0x3  << MDCM_CFG_DC_ECC_OFFSET)
++#define MDCM_CFG_DLMB_MASK	(0x7  << MDCM_CFG_DLMB_OFFSET)
++#define MDCM_CFG_DLMSZ_MASK	(0x1f << MDCM_CFG_DLMSZ_OFFSET)
++#define MDCM_CFG_ULM_2BANK_MASK	(0x1  << MDCM_CFG_ULM_2BANK_OFFSET)
++#define MDCM_CFG_DLM_ECC_MASK	(0x3  << MDCM_CFG_DLM_ECC_OFFSET)
++
++/* User mode control registers */
++#define CSR_UITB					   0x800
++#define CSR_UCODE					   0x801
++#define CSR_UDCAUSE					   0x809
++#define CCTL_REG_UCCTLBEGINADDR_NUM    0x80b
++#define CCTL_REG_UCCTLCOMMAND_NUM      0x80c
++#define CSR_WFE						   0x810
++#define CSR_SLEEPVALUE				   0x811
++#define CSR_TXEVT					   0x812
++
++#define custom_csr_write(csr_num,val) csr_write(csr_num,val)
++/* ucctlcommand */
++/* D-cache operation */
++#define CCTL_L1D_VA_INVAL	0
++#define CCTL_L1D_VA_WB		1
++#define CCTL_L1D_VA_WBINVAL	2
++
++/* non-blocking & write around */
++#define MMISC_CTL_NON_BLOCKING_ENABLE  (0x1  << MMISC_CTL_NON_BLOCKING_OFFSET)
++#define MMISC_CTL_NON_BLOCKING_OFFSET  0x8
++
++#define MCACHE_CTL_L1I_PREFETCH_OFFSET  9
++#define MCACHE_CTL_L1D_PREFETCH_OFFSET  10
++#define MCACHE_CTL_DC_WAROUND_OFFSET_1  13
++#define MCACHE_CTL_DC_WAROUND_OFFSET_2  14
++#define MCACHE_CTL_L1I_PREFETCH_EN  (0x1  << MCACHE_CTL_L1I_PREFETCH_OFFSET)
++#define MCACHE_CTL_L1D_PREFETCH_EN  (0x1  << MCACHE_CTL_L1D_PREFETCH_OFFSET)
++#define MCACHE_CTL_DC_WAROUND_1_EN  (0x1  << MCACHE_CTL_DC_WAROUND_OFFSET_1)
++#define MCACHE_CTL_DC_WAROUND_2_EN  (0x1  << MCACHE_CTL_DC_WAROUND_OFFSET_2)
++#define WRITE_AROUND_ENABLE  (MCACHE_CTL_L1I_PREFETCH_EN | MCACHE_CTL_L1D_PREFETCH_EN | MCACHE_CTL_DC_WAROUND_1_EN)
++
++/* L1 I-cache , D-cache */
++#define CACHE_CTL_offIC_EN  0   /* Enable I-cache */
++#define CACHE_CTL_offDC_EN  1   /* Enable D-cache */
++#define CACHE_CTL_mskIC_EN  ( 0x1  << CACHE_CTL_offIC_EN )
++#define CACHE_CTL_mskDC_EN  ( 0x1  << CACHE_CTL_offDC_EN )
++
++
++/* L2 cache */
++#define L2_CACHE_CTL_mskCEN 1
++/* L2 cache registers */
++#define L2C_REG_CFG_OFFSET	0
++#define L2C_REG_CTL_OFFSET	0x8
++#define L2C_HPM_C0_CTL_OFFSET	0x10
++#define L2C_HPM_C1_CTL_OFFSET	0x18
++#define L2C_HPM_C2_CTL_OFFSET	0x20
++#define L2C_HPM_C3_CTL_OFFSET	0x28
++#define L2C_REG_C0_CMD_OFFSET	0x40
++#define L2C_REG_C0_ACC_OFFSET	0x48
++#define L2C_REG_C1_CMD_OFFSET	0x50
++#define L2C_REG_C1_ACC_OFFSET	0x58
++#define L2C_REG_C2_CMD_OFFSET	0x60
++#define L2C_REG_C2_ACC_OFFSET	0x68
++#define L2C_REG_C3_CMD_OFFSET	0x70
++#define L2C_REG_C3_ACC_OFFSET	0x78
++#define L2C_REG_STATUS_OFFSET	0x80
++#define L2C_REG_C0_HPM_OFFSET	0x200
++
++/* L2 CCTL status */
++#define CCTL_L2_STATUS_IDLE	0
++#define CCTL_L2_STATUS_PROCESS	1
++#define CCTL_L2_STATUS_ILLEGAL	2
++/* L2 CCTL status cores mask */
++#define CCTL_L2_STATUS_C0_MASK	0xF
++#define CCTL_L2_STATUS_C1_MASK	0xF0
++#define CCTL_L2_STATUS_C2_MASK	0xF00
++#define CCTL_L2_STATUS_C3_MASK	0xF000
++
++/* L2 cache operation */
++#define CCTL_L2_PA_INVAL	0x8
++#define CCTL_L2_PA_WB		0x9
++#define CCTL_L2_PA_WBINVAL	0xA
++#define CCTL_L2_WBINVAL_ALL	0x12
++
++#define L2C_HPM_PER_CORE_OFFSET		0x8
++#define L2C_REG_PER_CORE_OFFSET		0x10
++#define CCTL_L2_STATUS_PER_CORE_OFFSET	4
++#define L2C_REG_CN_CMD_OFFSET(n)	\
++	L2C_REG_C0_CMD_OFFSET + (n * L2C_REG_PER_CORE_OFFSET)
++#define L2C_REG_CN_ACC_OFFSET(n)	\
++	L2C_REG_C0_ACC_OFFSET + (n * L2C_REG_PER_CORE_OFFSET)
++#define CCTL_L2_STATUS_CN_MASK(n)	\
++	CCTL_L2_STATUS_C0_MASK << (n * CCTL_L2_STATUS_PER_CORE_OFFSET)
++#define L2C_HPM_CN_CTL_OFFSET(n)	\
++	L2C_HPM_C0_CTL_OFFSET + (n * L2C_HPM_PER_CORE_OFFSET)
++#define L2C_REG_CN_HPM_OFFSET(n)	\
++	L2C_REG_C0_HPM_OFFSET + (n * L2C_HPM_PER_CORE_OFFSET)
++
++
++/* Debug/Trace Registers (shared with Debug Mode) */
++#define CSR_SCONTEXT            0x7aa
++
++/* Supervisor trap registers */
++#define CSR_SLIE				0x9c4
++#define CSR_SLIP				0x9c5
++#define CSR_SDCAUSE				0x9c9
++
++/* Supervisor counter registers */
++#define CSR_SCOUNTERINTEN		0x9cf
++#define CSR_SCOUNTERMASK_M		0x9d1
++#define CSR_SCOUNTERMASK_S		0x9d2
++#define CSR_SCOUNTERMASK_U		0x9d3
++#define CSR_SCOUNTEROVF			0x9d4
++#define CSR_SCOUNTINHIBIT		0x9e0
++#define CSR_SHPMEVENT3			0x9e3
++#define CSR_SHPMEVENT4			0x9e4
++#define CSR_SHPMEVENT5			0x9e5
++#define CSR_SHPMEVENT6			0x9e6
++
++/* Supervisor control registers */
++#define CSR_SCCTLDATA			0x9cd
++#define CSR_SMISC_CTL			0x9d0
+diff --git a/arch/riscv/include/asm/andesv5/proc.h b/arch/riscv/include/asm/andesv5/proc.h
+new file mode 100644
+index 000000000000..d06fbff65ad0
+--- /dev/null
++++ b/arch/riscv/include/asm/andesv5/proc.h
+@@ -0,0 +1,36 @@
++#include <asm/io.h>
++#include <asm/page.h>
++
++int cpu_l1c_status(void);
++void cpu_icache_enable(void *info);
++void cpu_icache_disable(void *info);
++void cpu_dcache_enable(void *info);
++void cpu_dcache_disable(void *info);
++uint32_t cpu_l2c_ctl_status(void);
++void cpu_l2c_enable(void);
++void cpu_l2c_disable(void);
++
++void cpu_dma_inval_range(unsigned long start, unsigned long end);
++void cpu_dma_wb_range(unsigned long start, unsigned long end);
++void cpu_l2c_inval_range(unsigned long pa, unsigned long size);
++void cpu_l2c_wb_range(unsigned long pa, unsigned long size);
++
++extern phys_addr_t pa_msb;;
++
++#define dma_remap(pa, size) ioremap((pa|(pa_msb << PAGE_SHIFT)), size)
++
++#define dma_unmap(vaddr) iounmap((void __force __iomem *)vaddr)
++
++
++/*
++ * struct andesv5_cache_info
++ * The member of this struct is dupilcated to some content of struct cacheinfo
++ * to reduce the latence of searching dcache inforamtion in andesv5/cache.c.
++ * At current only dcache-line-size is needed. when the content of
++ * andesv5_cache_info has been initilized by function fill_cpu_cache_info(),
++ * member init_done is set as true
++ */
++struct andesv5_cache_info {
++	bool init_done;
++	int dcache_line_size;
++};
+diff --git a/arch/riscv/include/asm/andesv5/smu.h b/arch/riscv/include/asm/andesv5/smu.h
+new file mode 100644
+index 000000000000..14813492c159
+--- /dev/null
++++ b/arch/riscv/include/asm/andesv5/smu.h
+@@ -0,0 +1,78 @@
++#ifndef _ASM_RISCV_SMU_H
++#define _ASM_RISCV_SMU_H
++
++#include <asm/sbi.h>
++#define MAX_PCS_SLOT    7
++
++#define PCS0_WE_OFF     0x90
++#define PCS0_CTL_OFF    0x94
++#define PCS0_STATUS_OFF 0x98
++
++/*
++ * PCS0 --> Always on power domain, includes the JTAG tap and DMI_AHB bus in
++ *  ncejdtm200.
++ * PCS1 --> Power domain for debug subsystem
++ * PCS2 --> Main power domain, includes the system bus and AHB, APB peripheral
++ *  IPs.
++ * PCS3 --> Power domain for Core0 and L2C.
++ * PCSN --> Power domain for Core (N-3)
++ */
++
++#define PCSN_WE_OFF(n)          n * 0x20 + PCS0_WE_OFF
++#define CN_PCS_WE_OFF(n)        (n + 3) * 0x20 + PCS0_WE_OFF
++#define CN_PCS_STATUS_OFF(n)    (n + 3) * 0x20 + PCS0_STATUS_OFF
++#define CN_PCS_CTL_OFF(n)       (n + 3) * 0x20 + PCS0_CTL_OFF
++
++
++#define PD_TYPE_MASK    0x7
++#define PD_STATUS_MASK  0xf8
++#define GET_PD_TYPE(val)        val & PD_TYPE_MASK
++#define GET_PD_STATUS(val)      (val & PD_STATUS_MASK) >> 3
++
++// PD_type
++#define ACTIVE  0
++#define RESET   1
++#define SLEEP   2
++#define TIMEOUT 7
++
++// PD_status for sleep type
++#define LightSleep_STATUS       0
++#define DeepSleep_STATUS        16
++
++// param of PCS_CTL for sleep cmd
++#define LightSleep_CTL          0
++#define DeepSleep_CTL           1
++
++// PCS_CTL
++#define PCS_CTL_PARAM_OFF       3
++#define SLEEP_CMD       3
++
++// wakeup events source offset
++#define PCS_WAKE_DBG_OFF	28
++#define PCS_WAKE_MSIP_OFF	29
++
++#define L2_CTL_OFF              0x8
++#define L2_COMMAND_OFF(cpu)     0x40 + 0x10 * cpu
++#define L2_STATUS_REG           0x80
++#define L2_WBINVAL_COMMAND      0x12
++
++extern unsigned int *wake_mask;
++extern void __iomem *l2c_base;
++
++void set_wakeup_enable(int cpu, unsigned int events);
++void set_sleep(int cpu, unsigned char sleep);
++void andes_suspend2standby(void);
++void andes_suspend2ram(void);
++
++static inline void sbi_suspend_prepare(char main_core, char enable)
++{
++	/* TODO */
++	// SBI_CALL_2(SBI_SUSPEND_PREPARE, main_core, enable);
++}
++
++static inline void sbi_suspend_mem(void)
++{
++	/* TODO */
++	// SBI_CALL_0(SBI_SUSPEND_MEM);
++}
++#endif
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0006-Add-andes-sbi-call-vendor-extension.patch b/board/andes/ae350/patches/linux/0006-Add-andes-sbi-call-vendor-extension.patch
new file mode 100644
index 0000000000..51b4277930
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0006-Add-andes-sbi-call-vendor-extension.patch
@@ -0,0 +1,231 @@
+From d4d9304e009a8c754e16e1ebb17c0ec3071eb68e Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:22:56 +0800
+Subject: [PATCH 06/12] Add andes sbi call vendor extension
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/andesv5/sbi.c     | 111 +++++++++++++++++++++++++++++++++++
+ arch/riscv/include/asm/sbi.h |  59 ++++++++++++++++++-
+ 2 files changed, 168 insertions(+), 2 deletions(-)
+ create mode 100755 arch/riscv/andesv5/sbi.c
+
+diff --git a/arch/riscv/andesv5/sbi.c b/arch/riscv/andesv5/sbi.c
+new file mode 100755
+index 000000000000..c5d2afd83ae0
+--- /dev/null
++++ b/arch/riscv/andesv5/sbi.c
+@@ -0,0 +1,111 @@
++/*
++ *  Copyright (C) 2020 Andes Technology Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <asm/andesv5/csr.h>
++#include <asm/andesv5/proc.h>
++#include <asm/sbi.h>
++
++void sbi_suspend_prepare(char main_core, char enable)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SUSPEND_PREPARE, main_core, enable, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_suspend_prepare);
++
++void sbi_suspend_mem(void)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SUSPEND_MEM, 0, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_suspend_mem);
++
++void sbi_restart(int cpu_num)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_RESTART, cpu_num, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_restart);
++
++void sbi_write_powerbrake(int val)
++{
++  sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_POWERBRAKE, val, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_write_powerbrake);
++
++int sbi_read_powerbrake(void)
++{
++  struct sbiret ret;
++  ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_READ_POWERBRAKE, 0, 0, 0, 0, 0, 0);
++  return ret.value;
++}
++EXPORT_SYMBOL(sbi_read_powerbrake);
++
++void sbi_set_suspend_mode(int suspend_mode)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_SUSPEND_MODE, suspend_mode, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_set_suspend_mode);
++
++void sbi_set_reset_vec(int val)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_RESET_VEC, val, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_set_reset_vec);
++
++void sbi_set_pma(void *arg)
++{
++	phys_addr_t offset = ((struct pma_arg_t*)arg)->offset;
++	unsigned long vaddr = ((struct pma_arg_t*)arg)->vaddr;
++	size_t size = ((struct pma_arg_t*)arg)->size;
++	size_t entry_id = ((struct pma_arg_t*)arg)->entry_id;
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_PMA, offset, vaddr, size, entry_id, 0, 0);
++}
++EXPORT_SYMBOL(sbi_set_pma);
++
++void sbi_free_pma(unsigned long entry_id)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_FREE_PMA, entry_id, 0, 0, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_free_pma);
++
++long sbi_probe_pma(void)
++{
++	struct sbiret ret;
++	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_PROBE_PMA, 0, 0, 0, 0, 0, 0);
++	return ret.value;
++}
++EXPORT_SYMBOL(sbi_probe_pma);
++
++void sbi_set_trigger(unsigned int type, uintptr_t data, int enable)
++{
++	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_TRIGGER, type, data, enable, 0, 0, 0);
++}
++EXPORT_SYMBOL(sbi_set_trigger);
++
++long sbi_get_marchid(void)
++{
++	struct sbiret ret;
++	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_MARCHID, 0, 0, 0, 0, 0, 0);
++	return ret.value;
++}
++EXPORT_SYMBOL(sbi_get_marchid);
++
++long sbi_get_micm_cfg(void)
++{
++	struct sbiret ret;
++	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MICM_CFG,
++			0, 0, 0, 0, 0, 0);
++	return ret.value;
++}
++EXPORT_SYMBOL(sbi_get_micm_cfg);
++
++long sbi_get_mdcm_cfg(void)
++{
++	struct sbiret ret;
++	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MDCM_CFG,
++			0, 0, 0, 0, 0, 0);
++	return ret.value;
++}
++EXPORT_SYMBOL(sbi_get_mdcm_cfg);
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index c0fdb05ffa0b..d3b2d34136f0 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -10,6 +10,14 @@
+ #include <linux/types.h>
+
+ #ifdef CONFIG_RISCV_SBI
++
++struct pma_arg_t {
++	phys_addr_t offset;
++	unsigned long vaddr;
++	size_t size;
++	size_t entry_id;
++};
++
+ enum sbi_ext_id {
+ #ifdef CONFIG_RISCV_SBI_V01
+	SBI_EXT_0_1_SET_TIMER = 0x0,
+@@ -27,6 +35,7 @@ enum sbi_ext_id {
+	SBI_EXT_IPI = 0x735049,
+	SBI_EXT_RFENCE = 0x52464E43,
+	SBI_EXT_HSM = 0x48534D,
++	SBI_EXT_ANDES = 0x0900031E,
+ };
+
+ enum sbi_ext_base_fid {
+@@ -51,10 +60,10 @@ enum sbi_ext_rfence_fid {
+	SBI_EXT_RFENCE_REMOTE_FENCE_I = 0,
+	SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
+	SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
+-	SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID,
+	SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA,
+-	SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID,
++	SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID,
+	SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA,
++	SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID,
+ };
+
+ enum sbi_ext_hsm_fid {
+@@ -63,6 +72,35 @@ enum sbi_ext_hsm_fid {
+	SBI_EXT_HSM_HART_STATUS,
+ };
+
++enum sbi_ext_andes_fid {
++	SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS = 0,
++	SBI_EXT_ANDES_GET_MMISC_CTL_STATUS,
++	SBI_EXT_ANDES_SET_MCACHE_CTL,
++	SBI_EXT_ANDES_SET_MMISC_CTL,
++	SBI_EXT_ANDES_ICACHE_OP,
++	SBI_EXT_ANDES_DCACHE_OP,
++	SBI_EXT_ANDES_L1CACHE_I_PREFETCH,
++	SBI_EXT_ANDES_L1CACHE_D_PREFETCH,
++	SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE,
++	SBI_EXT_ANDES_WRITE_AROUND,
++	SBI_EXT_ANDES_TRIGGER,
++	SBI_EXT_ANDES_SET_PFM,
++	SBI_EXT_ANDES_READ_POWERBRAKE,
++	SBI_EXT_ANDES_WRITE_POWERBRAKE,
++	SBI_EXT_ANDES_SUSPEND_PREPARE,
++	SBI_EXT_ANDES_SUSPEND_MEM,
++	SBI_EXT_ANDES_SET_SUSPEND_MODE,
++	SBI_EXT_ANDES_ENTER_SUSPEND_MODE,
++	SBI_EXT_ANDES_RESTART,
++	SBI_EXT_ANDES_SET_RESET_VEC,
++	SBI_EXT_ANDES_SET_PMA,
++	SBI_EXT_ANDES_FREE_PMA,
++	SBI_EXT_ANDES_PROBE_PMA,
++	SBI_EXT_ANDES_DCACHE_WBINVAL_ALL,
++	SBI_EXT_ANDES_GET_MICM_CFG,
++	SBI_EXT_ANDES_GET_MDCM_CFG,
++};
++
+ enum sbi_hsm_hart_status {
+	SBI_HSM_HART_STATUS_STARTED = 0,
+	SBI_HSM_HART_STATUS_STOPPED,
+@@ -146,6 +184,23 @@ static inline unsigned long sbi_minor_version(void)
+ }
+
+ int sbi_err_map_linux_errno(int err);
++
++void sbi_suspend_prepare(char main_core, char enable);
++void sbi_suspend_mem(void);
++void sbi_restart(int cpu_num);
++void sbi_write_powerbrake(int val);
++int sbi_read_powerbrake(void);
++void sbi_set_suspend_mode(int suspend_mode);
++void sbi_set_reset_vec(int val);
++void sbi_set_pma(void *arg);
++void sbi_free_pma(unsigned long entry_id);
++long sbi_probe_pma(void);
++void sbi_set_trigger(unsigned int type, uintptr_t data, int enable);
++long sbi_get_marchid(void);
++int get_custom_csr_cacheinfo(const char *propname, u32 *out_value);
++long sbi_get_micm_cfg(void);
++long sbi_get_mdcm_cfg(void);
++
+ #else /* CONFIG_RISCV_SBI */
+ /* stubs for code that is only reachable under IS_ENABLED(CONFIG_RISCV_SBI): */
+ void sbi_set_timer(uint64_t stime_value);
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0007-riscv-Porting-pte-update-function-local_flush_tlb_al.patch b/board/andes/ae350/patches/linux/0007-riscv-Porting-pte-update-function-local_flush_tlb_al.patch
new file mode 100644
index 0000000000..238a71841d
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0007-riscv-Porting-pte-update-function-local_flush_tlb_al.patch
@@ -0,0 +1,101 @@
+From 7c7587775d5b86d5822777e226a6bf0bb3704bed Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:44:26 +0800
+Subject: [PATCH 07/12] riscv: Porting pte update function
+ "local_flush_tlb_all"
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/include/asm/pgtable-64.h |  1 +
+ arch/riscv/include/asm/pgtable.h    | 20 ++++++++++++++++++--
+ 2 files changed, 19 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h
+index f3b0da64c6c8..69a9a87b3365 100644
+--- a/arch/riscv/include/asm/pgtable-64.h
++++ b/arch/riscv/include/asm/pgtable-64.h
+@@ -53,6 +53,7 @@ static inline int pud_leaf(pud_t pud)
+ static inline void set_pud(pud_t *pudp, pud_t pud)
+ {
+	*pudp = pud;
++	local_flush_tlb_all();
+ }
+
+ static inline void pud_clear(pud_t *pudp)
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index 73e8b5e5bb65..0fa3fc6658ed 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -7,7 +7,6 @@
+ #define _ASM_RISCV_PGTABLE_H
+
+ #include <linux/mmzone.h>
+-#include <linux/sizes.h>
+
+ #include <asm/pgtable-bits.h>
+
+@@ -18,6 +17,7 @@
+ #include <asm/page.h>
+ #include <asm/tlbflush.h>
+ #include <linux/mm_types.h>
++#include <linux/sizes.h>
+
+ #ifdef CONFIG_MMU
+
+@@ -99,6 +99,7 @@
+				| _PAGE_DIRTY)
+
+ #define PAGE_KERNEL		__pgprot(_PAGE_KERNEL)
++#define PAGE_KERNEL_EXEC	__pgprot(_PAGE_KERNEL | _PAGE_EXEC)
+ #define PAGE_KERNEL_READ	__pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
+ #define PAGE_KERNEL_EXEC	__pgprot(_PAGE_KERNEL | _PAGE_EXEC)
+ #define PAGE_KERNEL_READ_EXEC	__pgprot((_PAGE_KERNEL & ~_PAGE_WRITE) \
+@@ -134,6 +135,12 @@ extern pgd_t swapper_pg_dir[];
+ #define __S110	PAGE_SHARED_EXEC
+ #define __S111	PAGE_SHARED_EXEC
+
++#define pgprot_noncached pgprot_noncached
++static inline pgprot_t pgprot_noncached(pgprot_t _prot)
++{
++       return __pgprot(pgprot_val(_prot) | _PAGE_NONCACHEABLE);
++}
++
+ static inline int pmd_present(pmd_t pmd)
+ {
+	return (pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROT_NONE));
+@@ -159,6 +166,7 @@ static inline int pmd_leaf(pmd_t pmd)
+ static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
+ {
+	*pmdp = pmd;
++	local_flush_tlb_all();
+ }
+
+ static inline void pmd_clear(pmd_t *pmdp)
+@@ -195,9 +203,16 @@ static inline unsigned long pte_pfn(pte_t pte)
+ #define pte_page(x)     pfn_to_page(pte_pfn(x))
+
+ /* Constructs a page table entry */
++extern phys_addr_t pa_msb;
+ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
+ {
+-	return __pte((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
++	pte_t ret;
++	if (pgprot_val(prot) & _PAGE_NONCACHEABLE) {
++		ret = __pte(((pfn|pa_msb) << _PAGE_PFN_SHIFT) | (pgprot_val(prot) & ~_PAGE_NONCACHEABLE));
++	} else {
++		ret = __pte((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
++	}
++	return ret;
+ }
+
+ #define mk_pte(page, prot)       pfn_pte(page_to_pfn(page), prot)
+@@ -327,6 +342,7 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b)
+ static inline void set_pte(pte_t *ptep, pte_t pteval)
+ {
+	*ptep = pteval;
++	local_flush_tlb_all();
+ }
+
+ void flush_icache_pte(pte_t pte);
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0008-Support-time32-stat64-sys_clone3-syscalls.patch b/board/andes/ae350/patches/linux/0008-Support-time32-stat64-sys_clone3-syscalls.patch
new file mode 100644
index 0000000000..9d604ae5bc
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0008-Support-time32-stat64-sys_clone3-syscalls.patch
@@ -0,0 +1,47 @@
+From 4d02088e32ecb5abb3c84d9364f15db2044fadf3 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:47:28 +0800
+Subject: [PATCH 08/12] Support time32, stat64, sys_clone3 syscalls
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/include/asm/unistd.h      | 3 +++
+ arch/riscv/include/uapi/asm/unistd.h | 6 +++---
+ 2 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/include/asm/unistd.h b/arch/riscv/include/asm/unistd.h
+index 977ee6181dab..42ebae0693b1 100644
+--- a/arch/riscv/include/asm/unistd.h
++++ b/arch/riscv/include/asm/unistd.h
+@@ -9,6 +9,9 @@
+  */
+
+ #define __ARCH_WANT_SYS_CLONE
++#define __ARCH_WANT_STAT64
++#define __ARCH_WANT_SYS_CLONE3
++#define __ARCH_WANT_TIME32_SYSCALLS
+
+ #include <uapi/asm/unistd.h>
+
+diff --git a/arch/riscv/include/uapi/asm/unistd.h b/arch/riscv/include/uapi/asm/unistd.h
+index 8062996c2dfd..c05ce62b2b33 100644
+--- a/arch/riscv/include/uapi/asm/unistd.h
++++ b/arch/riscv/include/uapi/asm/unistd.h
+@@ -15,12 +15,12 @@
+  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+  */
+
+-#ifdef __LP64__
+ #define __ARCH_WANT_NEW_STAT
+ #define __ARCH_WANT_SET_GET_RLIMIT
+-#endif /* __LP64__ */
+-
++#define __ARCH_WANT_SYS_NEWFSTATAT
+ #define __ARCH_WANT_SYS_CLONE3
++#define __ARCH_WANT_TIME32_SYSCALLS
++#define __ARCH_WANT_STAT64
+
+ #include <asm-generic/unistd.h>
+
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0009-dma-Support-smp-up-with-dma.patch b/board/andes/ae350/patches/linux/0009-dma-Support-smp-up-with-dma.patch
new file mode 100644
index 0000000000..9c731f003b
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0009-dma-Support-smp-up-with-dma.patch
@@ -0,0 +1,120 @@
+From 27a98deea31b3d724fccb2728f43053ee1a814df Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 16:59:12 +0800
+Subject: [PATCH 09/12] dma: Support smp/up with dma
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/mm/dma-mapping.c | 101 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 101 insertions(+)
+ create mode 100755 arch/riscv/mm/dma-mapping.c
+
+diff --git a/arch/riscv/mm/dma-mapping.c b/arch/riscv/mm/dma-mapping.c
+new file mode 100755
+index 000000000000..8072e765f3ae
+--- /dev/null
++++ b/arch/riscv/mm/dma-mapping.c
+@@ -0,0 +1,101 @@
++#include <linux/dma-direct.h>
++#include <linux/swiotlb.h>
++
++
++/********************************************
++ * The following APIs are for dummy DMA ops *
++ ********************************************/
++
++static void *__dummy_alloc(struct device *dev, size_t size,
++			   dma_addr_t *dma_handle, gfp_t flags,
++			   unsigned long attrs)
++{
++	return NULL;
++}
++
++static void __dummy_free(struct device *dev, size_t size,
++			 void *vaddr, dma_addr_t dma_handle,
++			 unsigned long attrs)
++{
++}
++
++static int __dummy_mmap(struct device *dev,
++			struct vm_area_struct *vma,
++			void *cpu_addr, dma_addr_t dma_addr, size_t size,
++			unsigned long attrs)
++{
++	return -ENXIO;
++}
++
++static dma_addr_t __dummy_map_page(struct device *dev, struct page *page,
++				   unsigned long offset, size_t size,
++				   enum dma_data_direction dir,
++				   unsigned long attrs)
++{
++	return 0;
++}
++
++static void __dummy_unmap_page(struct device *dev, dma_addr_t dev_addr,
++			       size_t size, enum dma_data_direction dir,
++			       unsigned long attrs)
++{
++}
++
++static int __dummy_map_sg(struct device *dev, struct scatterlist *sgl,
++			  int nelems, enum dma_data_direction dir,
++			  unsigned long attrs)
++{
++	return 0;
++}
++
++static void __dummy_unmap_sg(struct device *dev,
++			     struct scatterlist *sgl, int nelems,
++			     enum dma_data_direction dir,
++			     unsigned long attrs)
++{
++}
++
++static void __dummy_sync_single(struct device *dev,
++				dma_addr_t dev_addr, size_t size,
++				enum dma_data_direction dir)
++{
++}
++
++static void __dummy_sync_sg(struct device *dev,
++			    struct scatterlist *sgl, int nelems,
++			    enum dma_data_direction dir)
++{
++}
++
++// static int __dummy_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
++// {
++// 	return 1;
++// }
++
++static int __dummy_dma_supported(struct device *hwdev, u64 mask)
++{
++	return 0;
++}
++
++const struct dma_map_ops dummy_dma_ops = {
++	.alloc                  = __dummy_alloc,
++	.free                   = __dummy_free,
++	.mmap                   = __dummy_mmap,
++	.map_page               = __dummy_map_page,
++	.unmap_page             = __dummy_unmap_page,
++	.map_sg                 = __dummy_map_sg,
++	.unmap_sg               = __dummy_unmap_sg,
++	.sync_single_for_cpu    = __dummy_sync_single,
++	.sync_single_for_device = __dummy_sync_single,
++	.sync_sg_for_cpu        = __dummy_sync_sg,
++	.sync_sg_for_device     = __dummy_sync_sg,
++	// .mapping_error          = __dummy_mapping_error,
++	.dma_supported          = __dummy_dma_supported,
++};
++EXPORT_SYMBOL(dummy_dma_ops);
++
++void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
++		const struct iommu_ops *iommu, bool coherent)
++{
++	dev->dma_coherent = coherent;
++}
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0010-riscv-platform-Fix-atcdmac300-chained-irq-mapping-is.patch b/board/andes/ae350/patches/linux/0010-riscv-platform-Fix-atcdmac300-chained-irq-mapping-is.patch
new file mode 100644
index 0000000000..b81c9dce8b
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0010-riscv-platform-Fix-atcdmac300-chained-irq-mapping-is.patch
@@ -0,0 +1,300 @@
+From 356bf37d40fb4b9f9044cb872d3ebd74a3b0c4ff Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 17:02:35 +0800
+Subject: [PATCH 10/12] riscv/platform: Fix atcdmac300 chained irq mapping
+ issue
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/include/asm/atcdmac300.h     |  2 +-
+ arch/riscv/platforms/ae350/atcdmac300.c | 95 +++++++++++++------------
+ 2 files changed, 51 insertions(+), 46 deletions(-)
+
+diff --git a/arch/riscv/include/asm/atcdmac300.h b/arch/riscv/include/asm/atcdmac300.h
+index 20fe88212dfc..0d4dcc08e7f3 100644
+--- a/arch/riscv/include/asm/atcdmac300.h
++++ b/arch/riscv/include/asm/atcdmac300.h
+@@ -198,7 +198,7 @@ typedef struct channel_control
+ #define DMAC_REQN_UART2RX		7
+ #define DMAC_REQN_I2C			8
+ #define DMAC_REQN_SDC			9
+-#define DMAC_REQN_NONE			15
++#define DMAC_REQN_NONE			16
+
+
+ enum DMAD_DMAC_CORE {
+diff --git a/arch/riscv/platforms/ae350/atcdmac300.c b/arch/riscv/platforms/ae350/atcdmac300.c
+index e635328f9362..8f434b1f8845 100644
+--- a/arch/riscv/platforms/ae350/atcdmac300.c
++++ b/arch/riscv/platforms/ae350/atcdmac300.c
+@@ -526,8 +526,8 @@ static irqreturn_t dmad_ahb_isr(int irq, void *dev_id)
+
+		dmad_dbg("dma finish\n");
+
+-		dmad_dbg("finish drb(%d 0x%08x) addr0(0x%08x) "
+-			 "addr1(0x%08x) size(0x%08x)\n",
++		dmad_dbg("finish drb(%d 0x%08x) addr0(0x%08llx) "
++			 "addr1(0x%08llx) size(0x%08llx)\n",
+			 drb->node, (u32) drb, drb->src_addr,
+			 drb->dst_addr, drb->req_cycle);
+
+@@ -548,8 +548,8 @@ static irqreturn_t dmad_ahb_isr(int irq, void *dev_id)
+			// Lookup next DRB (DMA Request Block)
+			drb_iter = &drq->drb_pool[drq->sbt_head];
+
+-			dmad_dbg("exec drb(%d 0x%08x) addr0(0x%08x) "
+-				 "addr1(0x%08x) size(0x%08x)\n",
++			dmad_dbg("exec drb(%d 0x%08x) addr0(0x%08llx) "
++				 "addr1(0x%08llx) size(0x%08llx)\n",
+				 drb_iter->node, (u32) drb_iter,
+				 drb_iter->src_addr, drb_iter->dst_addr,
+				 drb_iter->req_cycle);
+@@ -640,7 +640,7 @@ static void dmad_ahb_config_dir(dmad_chreq * ch_req, unsigned long * channel_cmd
+	dmad_drq *drq = (dmad_drq *) ch_req->drq;
+	dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req);
+	channel_control ch_ctl;
+-	dmad_dbg("%s() channel_cmds(0x%08x)\n",__func__, channel_cmds[0]);
++	dmad_dbg("%s() channel_cmds(0x%08lx)\n",__func__, channel_cmds[0]);
+	channel_cmds[0] &= ~(u32)(SRCWIDTH_MASK|SRCADDRCTRL_MASK|
+		DSTWIDTH_MASK|DSTADDRCTRL_MASK|
+		SRC_HS|DST_HS|SRCREQSEL_MASK|DSTREQSEL_MASK);
+@@ -656,6 +656,7 @@ static void dmad_ahb_config_dir(dmad_chreq * ch_req, unsigned long * channel_cmd
+		memcpy((u8 *)&ch_ctl.dWidth,(u8 *)&ahb_req->addr0_width,12);
+		drq->flags |= (addr_t) DMAD_DRQ_DIR_A1_TO_A0;
+	}
++
+	channel_cmds[0] |=(((ch_ctl.sWidth << SRCWIDTH) &SRCWIDTH_MASK) |
+		((ch_ctl.sCtrl << SRCADDRCTRL) &SRCADDRCTRL_MASK) |
+		((ch_ctl.dWidth << DSTWIDTH) &DSTWIDTH_MASK) |
+@@ -673,7 +674,7 @@ static void dmad_ahb_config_dir(dmad_chreq * ch_req, unsigned long * channel_cmd
+				((ch_ctl.dReqn <<DSTREQSEL)&DSTREQSEL_MASK));
+		}
+	}
+-	dmad_dbg("%s() channel_cmds(0x%08x)\n",
++	dmad_dbg("%s() channel_cmds(0x%08lx)\n",
+		 __func__, channel_cmds[0]);
+ }
+
+@@ -692,16 +693,19 @@ static int dmad_ahb_init(dmad_chreq * ch_req)
+	dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req);
+	u32 channel = (u32) ch_req->channel;
+
++	int virq=0;
++
+	unsigned long channel_base = drq->channel_base;
+	addr_t channel_cmds[1];
+	unsigned long lock_flags;
+	dmad_dbg("%s()\n", __func__);
+	/* register interrupt handler */
+-	err = request_irq(ahb_irqs[channel], dmad_ahb_isr, 0,
++	virq = ftdmac020_find_irq(ahb_irqs[channel]);
++	err = request_irq(virq, dmad_ahb_isr, 0,
+			  "AHB_DMA", (void *)(unsigned long)(channel + 1));
+	if (unlikely(err != 0)) {
+		dmad_err("unable to request IRQ %d for AHB DMA "
+-			 "(error %d)\n", ahb_irqs[channel], err);
++			 "(error %d)\n", virq, err);
+		free_irq(ahb_irqs[channel], (void *)(unsigned long)(channel + 1));
+		return err;
+	}
+@@ -995,9 +999,9 @@ int dmad_channel_alloc(dmad_chreq * ch_req)
+
+		}
+
+-		dmad_dbg("%s() ring: base(0x%08x) port(0x%08x) periods(0x%08x)"
+-			 " period_size(0x%08x) period_bytes(0x%08x)"
+-			 " remnant_size(0x%08x)\n",
++		dmad_dbg("%s() ring: base(0x%08llx) port(0x%08lx) periods(0x%08x)"
++			 " period_size(0x%08x) period_bytes(0x%08llx)"
++			 " remnant_size(0x%08llx)\n",
+			 __func__, drq_iter->ring_base, drq_iter->ring_port,
+			 drq_iter->periods, drq_iter->period_size,
+			 drq_iter->period_bytes, drq_iter->remnant_size);
+@@ -1484,13 +1488,13 @@ static inline int dmad_submit_request_internal(dmad_drq * drq, dmad_drb * drb)
+
+		drb->state = DMAD_DRB_STATE_SUBMITTED;
+
+-		dmad_dbg("%s() submit drb(%d 0x%08x) addr0(0x%08x) "
+-			 "addr1(0x%08x) size(0x%08x) state(%d)\n", __func__,
++		dmad_dbg("%s() submit drb(%d 0x%08x) addr0(0x%08llx) "
++			 "addr1(0x%08llx) size(0x%08llx) state(%d)\n", __func__,
+			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+			 drb->req_cycle, drb->state);
+	} else {
+-		dmad_dbg("%s() skip drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x)"
+-			 " size(0x%08x) state(%d)\n", __func__,
++		dmad_dbg("%s() skip drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx)"
++			 " size(0x%08llx) state(%d)\n", __func__,
+			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+			 drb->req_cycle, drb->state);
+	}
+@@ -1545,8 +1549,8 @@ int dmad_submit_request(dmad_chreq * ch_req, dmad_drb * drb, u8 keep_fired)
+			 drb->node);
+
+	/* Queue DRB to the end of the submitted list */
+-	dmad_dbg("submit drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-		 "size(0x%08x) sync(0x%08x) fire(%d)\n",
++	dmad_dbg("submit drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++		 "size(0x%08llx) sync(0x%08x) fire(%d)\n",
+		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+		 drb->req_cycle, (u32) drb->sync, keep_fired);
+
+@@ -1636,8 +1640,8 @@ int dmad_withdraw_request(dmad_chreq * ch_req, dmad_drb * drb)
+		return -EBADR;
+	}
+
+-	dmad_dbg("cancel drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-		 "size(0x%08x) state(%d)\n",
++	dmad_dbg("cancel drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++		 "size(0x%08llx) state(%d)\n",
+		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+		 drb->req_cycle, drb->state);
+
+@@ -1687,8 +1691,8 @@ static inline int dmad_kickoff_requests_internal(dmad_drq * drq)
+		return -EBADR;
+	}
+
+-	dmad_dbg("%s() drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-		 "size(0x%08x) state(%d)\n", __func__,
++	dmad_dbg("%s() drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++		 "size(0x%08llx) state(%d)\n", __func__,
+		 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+		 drb->req_cycle, drb->state);
+
+@@ -1749,10 +1753,10 @@ int dmad_kickoff_requests(dmad_chreq * ch_req)
+
+	dmad_get_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
+
+-	dmad_dbg("drq(0x%08x) channel_base(0x%08x)\n",
++	dmad_dbg("drq(0x%08x) channel_base(0x%08lx)\n",
+		 (u32) drq, drq->channel_base);
+-	dmad_dbg("kick off drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-		 "size(0x%08x) state(%d) a1_to_a0(%d)\n",
++	dmad_dbg("kick off drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++		 "size(0x%08llx) state(%d) a1_to_a0(%d)\n",
+		 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
+		 drb->req_cycle, drb->state,
+		 drq->flags & DMAD_DRQ_DIR_A1_TO_A0);
+@@ -1876,9 +1880,9 @@ int dmad_update_ring(dmad_chreq * ch_req)
+
+	spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags);
+
+-	dmad_dbg("%s() ring: base(0x%08x) port(0x%08x) periods(0x%08x) "
+-		 "period_size(0x%08x) period_bytes(0x%08x) "
+-		 "remnant_size(0x%08x)\n",
++	dmad_dbg("%s() ring: base(0x%08llx) port(0x%08lx) periods(0x%08x) "
++		 "period_size(0x%08x) period_bytes(0x%08llx) "
++		 "remnant_size(0x%08llx)\n",
+		 __func__, drq->ring_base, drq->ring_port,
+		 drq->periods, drq->period_size, drq->period_bytes,
+		 drq->remnant_size);
+@@ -1948,10 +1952,10 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+		sw_p_off += period_size;
+	}
+
+-	dmad_dbg("%s() ring_ptr(0x%08x) ring_p_idx(0x%08x) "
+-		 "ring_p_off(0x%08x)\n",
++	dmad_dbg("%s() ring_ptr(0x%08llx) ring_p_idx(0x%08x) "
++		 "ring_p_off(0x%08llx)\n",
+		 __func__, ring_ptr, ring_p_idx, ring_p_off);
+-	dmad_dbg("%s() sw_ptr(0x%08x) sw_p_idx(0x%08x) sw_p_off(0x%08x)\n",
++	dmad_dbg("%s() sw_ptr(0x%08llx) sw_p_idx(0x%08x) sw_p_off(0x%08llx)\n",
+		 __func__, sw_ptr, sw_p_idx, sw_p_off);
+
+	if (drq->ring_drb &&
+@@ -1971,8 +1975,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+		drb->addr1 = drq->dev_addr;
+		drb->req_cycle = 0;	// redundent, though, no harm to performance
+
+-		dmad_dbg("init_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-			 "size(0x%08x) state(%d)\n",
++		dmad_dbg("init_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++			 "size(0x%08llx) state(%d)\n",
+			 (u32) drb->node, (u32) drb, drb->src_addr,
+			 drb->dst_addr, drb->req_cycle, drb->state);
+
+@@ -2024,8 +2028,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+		/* update drb size at ring_ptr */
+		drb->req_cycle = sw_p_off;
+
+-		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-			 "size(0x%08x) state(%d)\n",
++		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++			 "size(0x%08llx) state(%d)\n",
+			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
+			 drb->req_cycle, drb->state);
+
+@@ -2069,8 +2073,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+		else
+			drb->req_cycle = period_size;
+
+-		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-			 "size(0x%08x) state(%d)\n",
++		dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++			 "size(0x%08llx) state(%d)\n",
+			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
+			 drb->req_cycle, drb->state);
+
+@@ -2147,8 +2151,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+				drb->req_cycle = period_size;
+			}
+
+-			dmad_dbg("inbtw_drb(%d 0x%08x) addr0(0x%08x) "
+-				 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++			dmad_dbg("inbtw_drb(%d 0x%08x) addr0(0x%08llx) "
++				 "addr1(0x%08llx) size(0x%08llx) state(%d)\n",
+				 (u32) drb->node, (u32) drb, drb->addr0,
+				 drb->addr1, drb->req_cycle, drb->state);
+
+@@ -2166,8 +2170,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq * ch_req,
+		drb->addr1 = drq->dev_addr;
+		drb->req_cycle = sw_p_off;
+
+-		dmad_dbg("swptr_drb(%d 0x%08x) addr0(0x%08x) addr1(0x%08x) "
+-			 "size(0x%08x) state(%d)\n",
++		dmad_dbg("swptr_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) "
++			 "size(0x%08llx) state(%d)\n",
+			 (u32) drb->node, (u32) drb, drb->addr0, drb->addr1,
+			 drb->req_cycle, drb->state);
+
+@@ -2254,8 +2258,8 @@ static int dmad_channel_drain(u32 controller, dmad_drq * drq, u8 shutdown)
+	dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
+
+	while (drb) {
+-		dmad_dbg("cancel sbt drb(%d 0x%08x) addr0(0x%08x) "
+-			 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++		dmad_dbg("cancel sbt drb(%d 0x%08x) addr0(0x%08llx) "
++			 "addr1(0x%08llx) size(0x%08llx) state(%d)\n",
+			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+			 drb->req_cycle, (u32) drb->state);
+
+@@ -2276,8 +2280,8 @@ static int dmad_channel_drain(u32 controller, dmad_drq * drq, u8 shutdown)
+	dmad_detach_head(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, &drb);
+
+	while (drb) {
+-		dmad_dbg("cancel rdy drb(%d 0x%08x) addr0(0x%08x) "
+-			 "addr1(0x%08x) size(0x%08x) state(%d)\n",
++		dmad_dbg("cancel rdy drb(%d 0x%08x) addr0(0x%08llx) "
++			 "addr1(0x%08llx) size(0x%08llx) state(%d)\n",
+			 drb->node, (u32) drb, drb->src_addr, drb->dst_addr,
+			 drb->req_cycle, (u32) drb->state);
+
+@@ -2474,6 +2478,7 @@ at_dma_parse_dt(struct platform_device *pdev)
+ static int atcdma_probe(struct platform_device *pdev)
+ {
+	struct at_dma_platform_data *pdata;
++	struct device_node *np = pdev->dev.of_node;
+	struct resource 	*io=0;
+	struct resource *mem = NULL;
+	int			irq;
+@@ -2501,7 +2506,7 @@ static int atcdma_probe(struct platform_device *pdev)
+	if (irq < 0)
+		return irq;
+
+-	intc_ftdmac020_init_irq(irq);
++	ftdmac020_init(np, irq);
+
+	return dmad_module_init();
+
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0011-DMA-Add-msb-bit-patch.patch b/board/andes/ae350/patches/linux/0011-DMA-Add-msb-bit-patch.patch
new file mode 100644
index 0000000000..3f60ce850e
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0011-DMA-Add-msb-bit-patch.patch
@@ -0,0 +1,387 @@
+From 6a328cfbcec652d8a56eb4103ace8b818b76240d Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 17:04:05 +0800
+Subject: [PATCH 11/12] DMA: Add msb bit patch
+
+Reformed from the commit:
+c32ef675cffe7a609d7afe2eb1ae92981a503144
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/Kconfig                    |  5 ++
+ arch/riscv/Makefile                   |  2 +-
+ arch/riscv/include/asm/device.h       | 11 ++++
+ arch/riscv/include/asm/dmad.h         |  3 +
+ arch/riscv/include/asm/io.h           | 14 ++++
+ arch/riscv/include/asm/irq.h          | 14 ++++
+ arch/riscv/include/asm/perf_event.h   |  7 +-
+ arch/riscv/include/asm/pgtable-bits.h |  6 ++
+ arch/riscv/kernel/head.S              |  1 +
+ arch/riscv/kernel/setup.c             |  7 ++
+ arch/riscv/mm/Makefile                |  5 +-
+ arch/riscv/mm/ioremap_nocache.c       | 16 +++++
+ arch/riscv/platforms/dmad_intc.c      | 93 +++++++++++++++++++++++----
+ 13 files changed, 169 insertions(+), 15 deletions(-)
+ create mode 100755 arch/riscv/include/asm/device.h
+ create mode 100644 arch/riscv/mm/ioremap_nocache.c
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 1b894c327578..84a83f3a1af6 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -14,6 +14,7 @@ config RISCV
+	def_bool y
+	select ARCH_CLOCKSOURCE_INIT
+	select ARCH_SUPPORTS_ATOMIC_RMW
++	select PHYS_ADDR_T_64BIT
+	select ARCH_HAS_BINFMT_FLAT
+	select ARCH_HAS_DEBUG_VM_PGTABLE
+	select ARCH_HAS_DEBUG_VIRTUAL if MMU
+@@ -88,6 +89,9 @@ config RISCV
+	select SYSCTL_EXCEPTION_TRACE
+	select THREAD_INFO_IN_TASK
+	select UACCESS_MEMCPY if !MMU
++	select ARCH_HAS_SETUP_DMA_OPS
++	select ARCH_HAS_SYNC_DMA_FOR_CPU
++	select ARCH_HAS_SYNC_DMA_FOR_DEVICE
+
+ config ARCH_MMAP_RND_BITS_MIN
+	default 18 if 64BIT
+@@ -204,6 +208,7 @@ source "arch/riscv/Kconfig.socs"
+
+ menu "Platform type"
+
++source "arch/riscv/platforms/Kconfig"
+ choice
+	prompt "Base ISA"
+	default ARCH_RV64I
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index 226c366072da..2d539750c20e 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -83,7 +83,7 @@ KBUILD_IMAGE	:= $(boot)/Image.gz
+
+ head-y := arch/riscv/kernel/head.o
+
+-core-y += arch/riscv/
++core-y += arch/riscv/kernel/ arch/riscv/mm/ arch/riscv/net/ arch/riscv/platforms/ arch/riscv/andesv5/
+
+ libs-y += arch/riscv/lib/
+ libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
+diff --git a/arch/riscv/include/asm/device.h b/arch/riscv/include/asm/device.h
+new file mode 100755
+index 000000000000..122a483f7f03
+--- /dev/null
++++ b/arch/riscv/include/asm/device.h
+@@ -0,0 +1,11 @@
++#ifndef __ASM_DEVICE_H
++#define __ASM_DEVICE_H
++
++struct dev_archdata {
++	bool dma_coherent;
++};
++
++struct pdev_archdata {
++};
++
++#endif
+\ No newline at end of file
+diff --git a/arch/riscv/include/asm/dmad.h b/arch/riscv/include/asm/dmad.h
+index 44c87b49e606..54b47c410915 100644
+--- a/arch/riscv/include/asm/dmad.h
++++ b/arch/riscv/include/asm/dmad.h
+@@ -68,4 +68,7 @@ struct at_dma_platform_data {
+	void __iomem	*apb_regs;
+ };
+
++int ftdmac020_find_irq(int hwirq);
++int ftdmac020_init(struct device_node *node, int irq);
++
+ #endif  /* __NDS_DMAD_INC__ */
+diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h
+index c025a746a148..328afc17b1f6 100644
+--- a/arch/riscv/include/asm/io.h
++++ b/arch/riscv/include/asm/io.h
+@@ -16,6 +16,20 @@
+ #include <asm/mmiowb.h>
+ #include <asm/early_ioremap.h>
+
++/*
++ * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
++ * change the properties of memory regions.  This should be fixed by the
++ * upcoming platform spec.
++ */
++/*
++ * That being said, before PMA is ready, Andes augmented PA with an MSB bit
++ * to indicate the non-cacheability.
++ */
++#define ioremap_nocache ioremap_nocache
++extern void __iomem *ioremap_nocache(phys_addr_t offset, size_t size);
++#define ioremap_wc(addr, size) ioremap_nocache((addr), (size))
++#define ioremap_wt(addr, size) ioremap_nocache((addr), (size))
++
+ /*
+  * MMIO access functions are separated out to break dependency cycles
+  * when using {read,write}* fns in low-level headers
+diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h
+index 9807ad164015..65e5d0514bfc 100644
+--- a/arch/riscv/include/asm/irq.h
++++ b/arch/riscv/include/asm/irq.h
+@@ -10,6 +10,20 @@
+ #include <linux/interrupt.h>
+ #include <linux/linkage.h>
+
++#define NR_IRQS         72
++
++/*
++ * Use this value to indicate lack of interrupt
++ * capability
++ */
++#ifndef NO_IRQ
++#define NO_IRQ  ((unsigned int)(-1))
++#endif
++
++#define INTERRUPT_CAUSE_PMU        274
++
++void riscv_software_interrupt(void);
++
+ #include <asm-generic/irq.h>
+
+ #endif /* _ASM_RISCV_IRQ_H */
+diff --git a/arch/riscv/include/asm/perf_event.h b/arch/riscv/include/asm/perf_event.h
+index 062efd3a1d5d..216462b7578a 100644
+--- a/arch/riscv/include/asm/perf_event.h
++++ b/arch/riscv/include/asm/perf_event.h
+@@ -18,8 +18,13 @@
+ /*
+  * The RISCV_MAX_COUNTERS parameter should be specified.
+  */
+-
++#ifdef CONFIG_ANDES_PMU
++#define RISCV_MAX_COUNTERS	7
++#define L2C_MAX_COUNTERS	32
++#define BASE_COUNTERS		3
++#else
+ #define RISCV_MAX_COUNTERS	2
++#endif	/* CONFIG_ANDES_PMU */
+
+ /*
+  * These are the indexes of bits in counteren register *minus* 1,
+diff --git a/arch/riscv/include/asm/pgtable-bits.h b/arch/riscv/include/asm/pgtable-bits.h
+index bbaeb5d35842..5a04317040bf 100644
+--- a/arch/riscv/include/asm/pgtable-bits.h
++++ b/arch/riscv/include/asm/pgtable-bits.h
+@@ -24,6 +24,12 @@
+ #define _PAGE_DIRTY     (1 << 7)    /* Set by hardware on any write */
+ #define _PAGE_SOFT      (1 << 8)    /* Reserved for software */
+
++#ifdef CONFIG_ANDES_QEMU_SUPPORT
++#define _PAGE_NONCACHEABLE      0
++#else
++#define _PAGE_NONCACHEABLE      (1 << 31)
++#endif
++
+ #define _PAGE_SPECIAL   _PAGE_SOFT
+ #define _PAGE_TABLE     _PAGE_PRESENT
+
+diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
+index 1a819c18bede..dd0e3280c62c 100644
+--- a/arch/riscv/kernel/head.S
++++ b/arch/riscv/kernel/head.S
+@@ -263,6 +263,7 @@ clear_bss_done:
+	/* Initialize page tables and relocate to virtual addresses */
+	la sp, init_thread_union + THREAD_SIZE
+	mv a0, s1
++	call setup_maxpa
+	call setup_vm
+ #ifdef CONFIG_MMU
+	la a0, early_pg_dir
+diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
+index 117f3212a8e4..115a5c91bdae 100644
+--- a/arch/riscv/kernel/setup.c
++++ b/arch/riscv/kernel/setup.c
+@@ -48,6 +48,13 @@ struct screen_info screen_info __section(".data") = {
+  * BSS.
+  */
+ atomic_t hart_lottery __section(".sdata");
++phys_addr_t pa_msb;
++asmlinkage void __init setup_maxpa(void)
++{
++    csr_write(satp, SATP_PPN);
++    pa_msb = (csr_read(satp) + 1) >>1;
++}
++
+ unsigned long boot_cpu_hartid;
+ static DEFINE_PER_CPU(struct cpu, cpu_devices);
+
+diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
+index 7ebaef10ea1b..2c154ae85b10 100644
+--- a/arch/riscv/mm/Makefile
++++ b/arch/riscv/mm/Makefile
+@@ -2,8 +2,7 @@
+
+ CFLAGS_init.o := -mcmodel=medany
+ ifdef CONFIG_FTRACE
+-CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
+-CFLAGS_REMOVE_cacheflush.o = $(CC_FLAGS_FTRACE)
++CFLAGS_REMOVE_init.o = -pg
+ endif
+
+ KCOV_INSTRUMENT_init.o := n
+@@ -12,7 +11,9 @@ obj-y += init.o
+ obj-y += extable.o
+ obj-$(CONFIG_MMU) += fault.o pageattr.o
+ obj-y += cacheflush.o
++obj-y += dma-mapping.o
+ obj-y += context.o
++obj-y += ioremap_nocache.o
+
+ ifeq ($(CONFIG_MMU),y)
+ obj-$(CONFIG_SMP) += tlbflush.o
+diff --git a/arch/riscv/mm/ioremap_nocache.c b/arch/riscv/mm/ioremap_nocache.c
+new file mode 100644
+index 000000000000..c7422219d561
+--- /dev/null
++++ b/arch/riscv/mm/ioremap_nocache.c
+@@ -0,0 +1,16 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * (C) Copyright 1995 1996 Linus Torvalds
++ * (C) Copyright 2012 Regents of the University of California
++ */
++#include <linux/io.h>
++
++#include <asm/pgtable.h>
++void __iomem *ioremap_nocache(phys_addr_t offset, size_t size)
++{
++	void __iomem *ret;
++	pgprot_t pgprot = pgprot_noncached(PAGE_KERNEL);
++	ret = ioremap_prot(offset, size, pgprot.pgprot);
++	return ret;
++}
++EXPORT_SYMBOL(ioremap_nocache);
+diff --git a/arch/riscv/platforms/dmad_intc.c b/arch/riscv/platforms/dmad_intc.c
+index 5f831add709a..e270e31e999b 100644
+--- a/arch/riscv/platforms/dmad_intc.c
++++ b/arch/riscv/platforms/dmad_intc.c
+@@ -5,6 +5,9 @@
+  */
+
+ #include <linux/irq.h>
++#include <linux/irqchip.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
+ #include <linux/interrupt.h>
+ #include <linux/ioport.h>
+ #include <asm/io.h>
+@@ -12,7 +15,49 @@
+
+ #ifdef CONFIG_PLATFORM_AHBDMA
+ extern int dmad_probe_irq_source_ahb(void);
+-void AHBDMA_irq_rounter(struct irq_desc *desc)
++
++/*
++ * Generic dummy implementation which can be used for
++ * real dumb interrupt sources
++ */
++struct irq_chip atcdmac_irq_chip = {
++	.name		= "Andes DMAC",
++};
++
++struct ftdmac020_info {
++	int			parent_irq;
++	struct irq_domain	*irq_domain;
++};
++
++struct ftdmac020_info *ftdmac020;
++
++static int ftdmac020_irq_map(struct irq_domain *domain, unsigned int virq,
++			       irq_hw_number_t hwirq)
++{
++	irq_set_chip_and_handler(virq, &atcdmac_irq_chip, handle_simple_irq);
++	irq_set_chip_data(virq, domain->host_data);
++
++	return 0;
++}
++
++static void ftdmac020_irq_unmap(struct irq_domain *d, unsigned int virq)
++{
++	irq_set_chip_and_handler(virq, NULL, NULL);
++	irq_set_chip_data(virq, NULL);
++}
++
++static const struct irq_domain_ops ftdmac020_irq_ops = {
++	.map    = ftdmac020_irq_map,
++	.unmap  = ftdmac020_irq_unmap,
++};
++
++
++/*
++ * The atcdmac300 provides a single hardware interrupt for all of the dmad
++ * channel, so we use a self-defined interrupt chip to translate this single interrupt
++ * into multiple interrupts, each associated with a single dma channel.
++ */
++static void AHBDMA_irq_rounter(struct irq_desc *desc)
+ {
+	int ahb_irq;
+	struct irq_desc *ahb_desc;
+@@ -29,21 +74,47 @@ void AHBDMA_irq_rounter(struct irq_desc *desc)
+		raw_spin_lock(&desc->lock);
+	}
+	desc->irq_data.chip->irq_unmask(&desc->irq_data);
++	desc->irq_data.chip->irq_eoi(&desc->irq_data);
+	raw_spin_unlock(&desc->lock);
+ }
+
+-int intc_ftdmac020_init_irq(int irq)
++int ftdmac020_find_irq(int hwirq){
++	int virq;
++
++	virq = irq_find_mapping(ftdmac020->irq_domain, hwirq);
++	printk("[ftdmac020_irq_mapping]: virq=%d, hwirq=%d,\n",virq,hwirq);
++	if (!virq)
++		return -EINVAL;
++
++	return virq;
++}
++
++int ftdmac020_init(struct device_node *node, int irq)
+ {
+-	int i;
+-	int ret;
+-	/* Register all IRQ */
+-	for (i = DMA_IRQ0;
+-	     i < DMA_IRQ0 + DMA_IRQ_COUNT; i++) {
+-		// level trigger
+-		ret = irq_set_chip(i, &dummy_irq_chip);
+-		irq_set_handler(i, handle_simple_irq);
++	int ret=0;
++
++	ftdmac020 = kzalloc(sizeof(struct ftdmac020_info), GFP_KERNEL);
++
++	ftdmac020->parent_irq=irq;
++
++	ftdmac020->irq_domain = __irq_domain_add(of_node_to_fwnode(node), DMA_IRQ_COUNT, DMA_IRQ0+DMA_IRQ_COUNT,
++					 ~0, &ftdmac020_irq_ops, ftdmac020);
++	if (!ftdmac020->irq_domain) {
++		printk("ftdmac020: Failed to create irqdomain\n");
++		return -EINVAL;
++	}
++
++	ret = irq_create_strict_mappings(ftdmac020->irq_domain, DMA_IRQ0, DMA_IRQ0, DMA_IRQ_COUNT);
++	if(unlikely(ret < 0)){
++		printk("ftdmac020: Failed to create irq_create_strict_mappings()\n");
++		return -EINVAL;
+	}
+-	irq_set_chained_handler(irq, AHBDMA_irq_rounter);
++
++	ftdmac020->irq_domain->name = "ftdmac020-domain";
++	irq_set_chained_handler_and_data(ftdmac020->parent_irq,
++					 AHBDMA_irq_rounter, ftdmac020);
++
+	return 0;
+ }
++
+ #endif /* CONFIG_PLATFORM_AHBDMA */
+--
+2.25.1
diff --git a/board/andes/ae350/patches/linux/0012-Remove-unused-Andes-SBI-call.patch b/board/andes/ae350/patches/linux/0012-Remove-unused-Andes-SBI-call.patch
new file mode 100644
index 0000000000..22a278ddff
--- /dev/null
+++ b/board/andes/ae350/patches/linux/0012-Remove-unused-Andes-SBI-call.patch
@@ -0,0 +1,147 @@
+From f476cc67c2821f931ff6ffd841327417b9967909 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Tue, 28 Dec 2021 17:34:59 +0800
+Subject: [PATCH 12/12] Remove unused Andes SBI call
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/andesv5/sbi.c     | 92 ------------------------------------
+ arch/riscv/include/asm/sbi.h | 16 -------
+ 2 files changed, 108 deletions(-)
+
+diff --git a/arch/riscv/andesv5/sbi.c b/arch/riscv/andesv5/sbi.c
+index c5d2afd83ae0..647587b81988 100755
+--- a/arch/riscv/andesv5/sbi.c
++++ b/arch/riscv/andesv5/sbi.c
+@@ -10,80 +10,6 @@
+ #include <asm/andesv5/proc.h>
+ #include <asm/sbi.h>
+
+-void sbi_suspend_prepare(char main_core, char enable)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SUSPEND_PREPARE, main_core, enable, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_suspend_prepare);
+-
+-void sbi_suspend_mem(void)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SUSPEND_MEM, 0, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_suspend_mem);
+-
+-void sbi_restart(int cpu_num)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_RESTART, cpu_num, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_restart);
+-
+-void sbi_write_powerbrake(int val)
+-{
+-  sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_POWERBRAKE, val, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_write_powerbrake);
+-
+-int sbi_read_powerbrake(void)
+-{
+-  struct sbiret ret;
+-  ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_READ_POWERBRAKE, 0, 0, 0, 0, 0, 0);
+-  return ret.value;
+-}
+-EXPORT_SYMBOL(sbi_read_powerbrake);
+-
+-void sbi_set_suspend_mode(int suspend_mode)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_SUSPEND_MODE, suspend_mode, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_set_suspend_mode);
+-
+-void sbi_set_reset_vec(int val)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_RESET_VEC, val, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_set_reset_vec);
+-
+-void sbi_set_pma(void *arg)
+-{
+-	phys_addr_t offset = ((struct pma_arg_t*)arg)->offset;
+-	unsigned long vaddr = ((struct pma_arg_t*)arg)->vaddr;
+-	size_t size = ((struct pma_arg_t*)arg)->size;
+-	size_t entry_id = ((struct pma_arg_t*)arg)->entry_id;
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_PMA, offset, vaddr, size, entry_id, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_set_pma);
+-
+-void sbi_free_pma(unsigned long entry_id)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_FREE_PMA, entry_id, 0, 0, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_free_pma);
+-
+-long sbi_probe_pma(void)
+-{
+-	struct sbiret ret;
+-	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_PROBE_PMA, 0, 0, 0, 0, 0, 0);
+-	return ret.value;
+-}
+-EXPORT_SYMBOL(sbi_probe_pma);
+-
+-void sbi_set_trigger(unsigned int type, uintptr_t data, int enable)
+-{
+-	sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_TRIGGER, type, data, enable, 0, 0, 0);
+-}
+-EXPORT_SYMBOL(sbi_set_trigger);
+-
+ long sbi_get_marchid(void)
+ {
+	struct sbiret ret;
+@@ -91,21 +17,3 @@ long sbi_get_marchid(void)
+	return ret.value;
+ }
+ EXPORT_SYMBOL(sbi_get_marchid);
+-
+-long sbi_get_micm_cfg(void)
+-{
+-	struct sbiret ret;
+-	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MICM_CFG,
+-			0, 0, 0, 0, 0, 0);
+-	return ret.value;
+-}
+-EXPORT_SYMBOL(sbi_get_micm_cfg);
+-
+-long sbi_get_mdcm_cfg(void)
+-{
+-	struct sbiret ret;
+-	ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MDCM_CFG,
+-			0, 0, 0, 0, 0, 0);
+-	return ret.value;
+-}
+-EXPORT_SYMBOL(sbi_get_mdcm_cfg);
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index d3b2d34136f0..40dc3a54a32c 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -83,22 +83,6 @@ enum sbi_ext_andes_fid {
+	SBI_EXT_ANDES_L1CACHE_D_PREFETCH,
+	SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE,
+	SBI_EXT_ANDES_WRITE_AROUND,
+-	SBI_EXT_ANDES_TRIGGER,
+-	SBI_EXT_ANDES_SET_PFM,
+-	SBI_EXT_ANDES_READ_POWERBRAKE,
+-	SBI_EXT_ANDES_WRITE_POWERBRAKE,
+-	SBI_EXT_ANDES_SUSPEND_PREPARE,
+-	SBI_EXT_ANDES_SUSPEND_MEM,
+-	SBI_EXT_ANDES_SET_SUSPEND_MODE,
+-	SBI_EXT_ANDES_ENTER_SUSPEND_MODE,
+-	SBI_EXT_ANDES_RESTART,
+-	SBI_EXT_ANDES_SET_RESET_VEC,
+-	SBI_EXT_ANDES_SET_PMA,
+-	SBI_EXT_ANDES_FREE_PMA,
+-	SBI_EXT_ANDES_PROBE_PMA,
+-	SBI_EXT_ANDES_DCACHE_WBINVAL_ALL,
+-	SBI_EXT_ANDES_GET_MICM_CFG,
+-	SBI_EXT_ANDES_GET_MDCM_CFG,
+ };
+
+ enum sbi_hsm_hart_status {
+--
+2.25.1
diff --git a/board/andes/ae350/patches/opensbi/0001-Disable-PIC-explicitly-for-assembling.patch b/board/andes/ae350/patches/opensbi/0001-Disable-PIC-explicitly-for-assembling.patch
new file mode 100644
index 0000000000..aeafed4c9f
--- /dev/null
+++ b/board/andes/ae350/patches/opensbi/0001-Disable-PIC-explicitly-for-assembling.patch
@@ -0,0 +1,29 @@
+From 3ccb71eeca42dbcd5e4d00ae1877a489ae82598d Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Wed, 29 Dec 2021 16:04:54 +0800
+Subject: [PATCH] Disable PIC explicitly for assembling
+
+This patch is necessary if the fw_dynamic load address
+is not equal to link address.
+However, they are equal currently, since we include an u-boot
+patch for preventing fw_dynamic relocation.
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ Makefile | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Makefile b/Makefile
+index d6f097d..441518d 100644
+--- a/Makefile
++++ b/Makefile
+@@ -225,6 +225,7 @@ ASFLAGS		+=	-mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
+ ASFLAGS		+=	$(GENFLAGS)
+ ASFLAGS		+=	$(platform-asflags-y)
+ ASFLAGS		+=	$(firmware-asflags-y)
++ASFLAGS		+=	-fno-pic
+
+ ARFLAGS		=	rcs
+
+--
+2.25.1
diff --git a/board/andes/ae350/patches/opensbi/0002-Enable-cache-for-opensbi-jump-mode.patch b/board/andes/ae350/patches/opensbi/0002-Enable-cache-for-opensbi-jump-mode.patch
new file mode 100644
index 0000000000..ae48a760c8
--- /dev/null
+++ b/board/andes/ae350/patches/opensbi/0002-Enable-cache-for-opensbi-jump-mode.patch
@@ -0,0 +1,25 @@
+From 325328f4204b40b1fcc8db3b46c7c8805710d21c Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Thu, 30 Dec 2021 08:47:34 +0800
+Subject: [PATCH] Enable cache for opensbi jump mode
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ firmware/fw_base.S | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/firmware/fw_base.S b/firmware/fw_base.S
+index ab33e11..155d230 100644
+--- a/firmware/fw_base.S
++++ b/firmware/fw_base.S
+@@ -46,6 +46,8 @@
+	.globl _start
+	.globl _start_warm
+ _start:
++	li t0, 0x80003
++	csrw  0x7ca, t0
+	/* Find preferred boot HART id */
+	MOV_3R	s0, a0, s1, a1, s2, a2
+	call	fw_boot_hart
+--
+2.25.1
diff --git a/board/andes/ae350/patches/uboot/0001-Fix-mmc-no-partition-table-error.patch b/board/andes/ae350/patches/uboot/0001-Fix-mmc-no-partition-table-error.patch
new file mode 100644
index 0000000000..2b0bae875e
--- /dev/null
+++ b/board/andes/ae350/patches/uboot/0001-Fix-mmc-no-partition-table-error.patch
@@ -0,0 +1,27 @@
+From ea4675215b53d16a72d29b8a6fc6a86cccf59cf0 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Wed, 5 Jan 2022 11:00:59 +0800
+Subject: [PATCH 1/3] Fix mmc no partition table error
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ drivers/mmc/ftsdc010_mci.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c
+index 570d54cf..3b1e0aa0 100644
+--- a/drivers/mmc/ftsdc010_mci.c
++++ b/drivers/mmc/ftsdc010_mci.c
+@@ -438,10 +438,6 @@ static int ftsdc010_mmc_probe(struct udevice *dev)
+		return ret;
+ #endif
+
+-	if (dev_read_bool(dev, "cap-mmc-highspeed") || \
+-		  dev_read_bool(dev, "cap-sd-highspeed"))
+-		chip->caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
+-
+	ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps,
+			priv->minmax[1] , priv->minmax[0]);
+	chip->mmc = &plat->mmc;
+--
+2.25.1
diff --git a/board/andes/ae350/patches/uboot/0002-Prevent-fw_dynamic-from-relocation.patch b/board/andes/ae350/patches/uboot/0002-Prevent-fw_dynamic-from-relocation.patch
new file mode 100644
index 0000000000..8ee4240619
--- /dev/null
+++ b/board/andes/ae350/patches/uboot/0002-Prevent-fw_dynamic-from-relocation.patch
@@ -0,0 +1,27 @@
+From 4c0c5378d032f2f95577585935624baf7b4decf3 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Wed, 5 Jan 2022 11:02:26 +0800
+Subject: [PATCH 2/3] Prevent fw_dynamic from relocation
+
+This patch prevents OpenSBI relocation, load fw_dynamic to link address
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ board/AndesTech/ax25-ae350/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/board/AndesTech/ax25-ae350/Kconfig b/board/AndesTech/ax25-ae350/Kconfig
+index e50f505a..385c4c11 100644
+--- a/board/AndesTech/ax25-ae350/Kconfig
++++ b/board/AndesTech/ax25-ae350/Kconfig
+@@ -25,7 +25,7 @@ config SPL_TEXT_BASE
+	default 0x800000
+
+ config SPL_OPENSBI_LOAD_ADDR
+-	default 0x01000000
++	default 0x0
+
+ config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+--
+2.25.1
diff --git a/board/andes/ae350/patches/uboot/0003-Fix-u-boot-proper-booting-issue.patch b/board/andes/ae350/patches/uboot/0003-Fix-u-boot-proper-booting-issue.patch
new file mode 100644
index 0000000000..81870647b8
--- /dev/null
+++ b/board/andes/ae350/patches/uboot/0003-Fix-u-boot-proper-booting-issue.patch
@@ -0,0 +1,26 @@
+From 3d09501175ae6f5e3f6520b48b1358226a99ff16 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Wed, 5 Jan 2022 18:17:39 +0800
+Subject: [PATCH 3/3] Fix u-boot proper booting issue
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ arch/riscv/cpu/start.S | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/arch/riscv/cpu/start.S b/arch/riscv/cpu/start.S
+index 76850ec9..2ccda4f5 100644
+--- a/arch/riscv/cpu/start.S
++++ b/arch/riscv/cpu/start.S
+@@ -139,7 +139,9 @@ call_harts_early_init:
+	 * accesses gd).
+	 */
+	mv	gp, s0
++#if !CONFIG_IS_ENABLED(RISCV_SMODE)
+	bnez	tp, secondary_hart_loop
++#endif
+ #endif
+
+	jal	board_init_f_init_reserve
+--
+2.25.1
diff --git a/board/andes/ae350/patches/uboot/0004-Enable-printing-OpenSBI-boot-logo.patch b/board/andes/ae350/patches/uboot/0004-Enable-printing-OpenSBI-boot-logo.patch
new file mode 100644
index 0000000000..efd78ab26d
--- /dev/null
+++ b/board/andes/ae350/patches/uboot/0004-Enable-printing-OpenSBI-boot-logo.patch
@@ -0,0 +1,25 @@
+From 3847a959ac4c07facbd80104ca5fa6a91fad5f35 Mon Sep 17 00:00:00 2001
+From: Yu Chien Peter Lin <peterlin@andestech.com>
+Date: Thu, 6 Jan 2022 13:50:07 +0800
+Subject: [PATCH] Enable printing OpenSBI boot logo
+
+Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
+---
+ include/opensbi.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/opensbi.h b/include/opensbi.h
+index d812cc8c..91fb8fd9 100644
+--- a/include/opensbi.h
++++ b/include/opensbi.h
+@@ -20,7 +20,7 @@
+
+ enum sbi_scratch_options {
+	/** Disable prints during boot */
+-	SBI_SCRATCH_NO_BOOT_PRINTS = (1 << 0),
++	SBI_SCRATCH_NO_BOOT_PRINTS = 0,
+ };
+
+ /** Representation dynamic info passed by previous booting stage */
+--
+2.25.1
diff --git a/board/andes/ae350/readme.txt b/board/andes/ae350/readme.txt
new file mode 100644
index 0000000000..19cfa721a7
--- /dev/null
+++ b/board/andes/ae350/readme.txt
@@ -0,0 +1,66 @@
+Intro
+=====
+
+Andestech AE350 Platform
+
+The AE350 prototype demonstrates the AE350 platform on the FPGA.
+
+How to build it
+===============
+
+Configure Buildroot
+-------------------
+
+  $ make ae350_andestar45_defconfig
+
+If you want to customize your configuration:
+
+  $ make menuconfig
+
+Build everything
+----------------
+Note: you will need to access to the network, since Buildroot will
+download the packages' sources.
+
+  $ make
+
+Result of the build
+-------------------
+
+After building, you should obtain the following files:
+
+  output/images/
+  |-- Image
+  |-- ae350.dtb
+  |-- boot.scr
+  |-- boot.vfat
+  |-- fw_dynamic.bin
+  |-- fw_dynamic.elf
+  |-- fw_jump.bin
+  |-- fw_jump.elf
+  |-- rootfs.cpio
+  |-- rootfs.ext2
+  |-- rootfs.ext4 -> rootfs.ext2
+  |-- rootfs.tar
+  |-- sdcard.img
+  |-- u-boot-spl.bin
+  `-- u-boot.itb
+
+
+Copy the sdcard.img to a SD card with "dd":
+
+  $ sudo dd if=sdcard.img of=/dev/sdX bs=4096
+
+Your SD card partition should be:
+
+  Disk /dev/mmcblk0: 31457280 sectors, 3072M
+  Logical sector size: 512
+  Disk identifier (GUID): 546663ee-d2f1-427f-93a5-5c7b69dd801c
+  Partition table holds up to 128 entries
+  First usable sector is 34, last usable sector is 385062
+
+  Number  Start (sector)    End (sector)  Size Name
+       1              34          262177  128M u-boot
+       2          262178          385057 60.0M rootfs
+
+Insert SD card and reset the board, should boot Linux from mmc.
diff --git a/board/andes/ae350/uboot.config.fragment b/board/andes/ae350/uboot.config.fragment
new file mode 100644
index 0000000000..4992d712a5
--- /dev/null
+++ b/board/andes/ae350/uboot.config.fragment
@@ -0,0 +1,5 @@
+CONFIG_SPL_FS_FAT=y
+CONFIG_SPL_MMC=y
+# CONFIG_SPL_RAM_SUPPORT is not set
+# CONFIG_OF_BOARD is not set
+CONFIG_OF_SEPARATE=y
diff --git a/configs/ae350_andestar45_defconfig b/configs/ae350_andestar45_defconfig
new file mode 100644
index 0000000000..fb4587b1a7
--- /dev/null
+++ b/configs/ae350_andestar45_defconfig
@@ -0,0 +1,46 @@
+BR2_riscv=y
+BR2_riscv_custom=y
+BR2_RISCV_ISA_CUSTOM_RVM=y
+BR2_RISCV_ISA_CUSTOM_RVF=y
+BR2_RISCV_ISA_CUSTOM_RVD=y
+BR2_RISCV_ISA_CUSTOM_RVC=y
+BR2_GLOBAL_PATCH_DIR="board/andes/ae350/patches"
+BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
+BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_10=y
+BR2_BINUTILS_VERSION_2_37_X=y
+BR2_GCC_VERSION_11_X=y
+BR2_GCC_ENABLE_OPENMP=y
+BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"
+BR2_ROOTFS_POST_IMAGE_SCRIPT="support/scripts/genimage.sh"
+BR2_ROOTFS_POST_SCRIPT_ARGS="-c board/andes/ae350/genimage_sdcard.cfg"
+BR2_LINUX_KERNEL=y
+BR2_LINUX_KERNEL_CUSTOM_VERSION=y
+BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.10.84"
+BR2_LINUX_KERNEL_DEFCONFIG="ae350_rv64_smp"
+BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="board/andes/ae350/linux.config.fragment"
+BR2_LINUX_KERNEL_DTS_SUPPORT=y
+BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="board/andes/ae350/ae350.dts"
+BR2_PACKAGE_OPENSSL=y
+BR2_TARGET_ROOTFS_CPIO=y
+BR2_TARGET_ROOTFS_EXT2=y
+BR2_TARGET_ROOTFS_EXT2_4=y
+BR2_TARGET_OPENSBI=y
+BR2_TARGET_OPENSBI_PLAT="andes/ae350"
+BR2_TARGET_UBOOT=y
+BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y
+BR2_TARGET_UBOOT_CUSTOM_VERSION=y
+BR2_TARGET_UBOOT_CUSTOM_VERSION_VALUE="2022.01"
+BR2_TARGET_UBOOT_BOARD_DEFCONFIG="ae350_rv64_spl_xip"
+BR2_TARGET_UBOOT_CONFIG_FRAGMENT_FILES="board/andes/ae350/uboot.config.fragment"
+BR2_TARGET_UBOOT_NEEDS_OPENSBI=y
+# BR2_TARGET_UBOOT_FORMAT_BIN is not set
+BR2_TARGET_UBOOT_FORMAT_CUSTOM=y
+BR2_TARGET_UBOOT_FORMAT_CUSTOM_NAME="u-boot.itb"
+BR2_TARGET_UBOOT_SPL=y
+BR2_TARGET_UBOOT_CUSTOM_MAKEOPTS="ARCH_FLAGS=-march=rv64imafdc"
+BR2_PACKAGE_HOST_DOSFSTOOLS=y
+BR2_PACKAGE_HOST_GENIMAGE=y
+BR2_PACKAGE_HOST_MTOOLS=y
+BR2_PACKAGE_HOST_UBOOT_TOOLS=y
+BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT=y
+BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE="board/andes/ae350/boot.cmd"
-- 
2.17.1


_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot


  reply	other threads:[~2022-01-11  4:18 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-11  3:58 [Buildroot] [PATCH 1/2] board/andes: rearrange nds32 folder structure Yu Chien Peter Lin
2022-01-11  3:58 ` Yu Chien Peter Lin [this message]
2022-01-11 20:21   ` [Buildroot] [PATCH 2/2] board/andes/ae350: add support for Andes AE350 Thomas Petazzoni
2022-01-11 20:13 ` [Buildroot] [PATCH 1/2] board/andes: rearrange nds32 folder structure Thomas Petazzoni

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220111035859.12895-2-peterlin@andestech.com \
    --to=peterlin@andestech.com \
    --cc=alankao@andestech.com \
    --cc=buildroot@buildroot.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox