summaryrefslogtreecommitdiff
path: root/bsp/ls2kdev/drivers
diff options
context:
space:
mode:
authorbigmagic <[email protected]>2020-12-29 17:56:12 +0800
committerbigmagic <[email protected]>2020-12-29 18:54:12 +0800
commit68689c7c4307cdcb4b97a77cd0b59ed6379528ce (patch)
tree110d6bb0ff12f0a83d75f752fd009ff677789817 /bsp/ls2kdev/drivers
parenteaf3e4795d5fc3b54b95b1d9ccca259983ae5909 (diff)
add ls2k sata ssd driver
Diffstat (limited to 'bsp/ls2kdev/drivers')
-rw-r--r--bsp/ls2kdev/drivers/ata/SConscript14
-rw-r--r--bsp/ls2kdev/drivers/ata/ahci.h281
-rw-r--r--bsp/ls2kdev/drivers/ata/ata_debug.h20
-rw-r--r--bsp/ls2kdev/drivers/ata/ata_interface.h101
-rw-r--r--bsp/ls2kdev/drivers/ata/blk_device.c127
-rw-r--r--bsp/ls2kdev/drivers/ata/blk_device.h48
-rw-r--r--bsp/ls2kdev/drivers/ata/dwc_ahsata.c1234
-rw-r--r--bsp/ls2kdev/drivers/ata/dwc_ahsata.h25
-rw-r--r--bsp/ls2kdev/drivers/ata/dwc_ahsata_priv.h323
-rw-r--r--bsp/ls2kdev/drivers/ata/fis.h152
-rw-r--r--bsp/ls2kdev/drivers/ata/libata.c150
-rw-r--r--bsp/ls2kdev/drivers/ata/libata.h653
-rw-r--r--bsp/ls2kdev/drivers/pci/SConscript10
-rw-r--r--bsp/ls2kdev/drivers/pci/pci.c20
-rw-r--r--bsp/ls2kdev/drivers/pci/pci.h42
15 files changed, 3200 insertions, 0 deletions
diff --git a/bsp/ls2kdev/drivers/ata/SConscript b/bsp/ls2kdev/drivers/ata/SConscript
new file mode 100644
index 0000000000..74db1f1012
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/SConscript
@@ -0,0 +1,14 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+if GetDepend('RT_USING_DFS_ELMFAT') == False:
+ SrcRemove(src, 'dwc_ahsata.c')
+ SrcRemove(src, 'libata.c')
+
+group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/bsp/ls2kdev/drivers/ata/ahci.h b/bsp/ls2kdev/drivers/ata/ahci.h
new file mode 100644
index 0000000000..bccec2c379
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/ahci.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#ifndef _AHCI_H_
+#define _AHCI_H_
+
+#define AHCI_PCI_BAR 0x24
+#define AHCI_MAX_SG 56 /* hardware max is 64K */
+#define AHCI_CMD_SLOT_SZ 32
+#define AHCI_MAX_CMD_SLOT 32
+#define AHCI_RX_FIS_SZ 256
+#define AHCI_CMD_TBL_HDR 0x80
+#define AHCI_CMD_TBL_CDB 0x40
+#define AHCI_CMD_TBL_SZ AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16)
+#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ * AHCI_MAX_CMD_SLOT + \
+ AHCI_CMD_TBL_SZ + AHCI_RX_FIS_SZ)
+#define AHCI_CMD_ATAPI (1 << 5)
+#define AHCI_CMD_WRITE (1 << 6)
+#define AHCI_CMD_PREFETCH (1 << 7)
+#define AHCI_CMD_RESET (1 << 8)
+#define AHCI_CMD_CLR_BUSY (1 << 10)
+
+#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */
+
+/* Global controller registers */
+#define HOST_CAP 0x00 /* host capabilities */
+#define HOST_CTL 0x04 /* global host control */
+#define HOST_IRQ_STAT 0x08 /* interrupt status */
+#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
+#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
+#define HOST_CAP2 0x24 /* host capabilities, extended */
+
+/* HOST_CTL bits */
+#define HOST_RESET (1 << 0) /* reset controller; self-clear */
+#define HOST_IRQ_EN (1 << 1) /* global IRQ enable */
+#define HOST_AHCI_EN (1 << 31) /* AHCI enabled */
+
+/* Registers for each SATA port */
+#define PORT_LST_ADDR 0x00 /* command list DMA addr */
+#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
+#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
+#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
+#define PORT_IRQ_STAT 0x10 /* interrupt status */
+#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
+#define PORT_CMD 0x18 /* port command */
+#define PORT_TFDATA 0x20 /* taskfile data */
+#define PORT_SIG 0x24 /* device TF signature */
+#define PORT_CMD_ISSUE 0x38 /* command issue */
+#define PORT_SCR 0x28 /* SATA phy register block */
+#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
+#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
+#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
+#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
+
+#ifdef CONFIG_SUNXI_AHCI
+#define PORT_P0DMACR 0x70 /* SUNXI specific "DMA register" */
+#endif
+
+/* PORT_IRQ_{STAT,MASK} bits */
+#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */
+#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */
+#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */
+#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */
+#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */
+#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */
+#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */
+#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */
+
+#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */
+#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */
+#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */
+#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */
+#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */
+#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */
+#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */
+#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */
+#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */
+
+#define PORT_IRQ_FATAL PORT_IRQ_TF_ERR | PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR | PORT_IRQ_IF_ERR
+
+#define DEF_PORT_IRQ PORT_IRQ_FATAL | PORT_IRQ_PHYRDY | PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE | PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS
+
+/* PORT_SCR_STAT bits */
+#define PORT_SCR_STAT_DET_MASK 0x3
+#define PORT_SCR_STAT_DET_COMINIT 0x1
+#define PORT_SCR_STAT_DET_PHYRDY 0x3
+
+/* PORT_CMD bits */
+#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */
+#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */
+#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */
+#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */
+#define PORT_CMD_CLO (1 << 3) /* Command list override */
+#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */
+#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
+#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
+
+#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
+#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
+#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
+
+#define AHCI_MAX_PORTS 32
+
+#define ATA_FLAG_SATA (1 << 3)
+#define ATA_FLAG_NO_LEGACY (1 << 4) /* no legacy mode check */
+#define ATA_FLAG_MMIO (1 << 6) /* use MMIO, not PIO */
+#define ATA_FLAG_SATA_RESET (1 << 7) /* (obsolete) use COMRESET */
+#define ATA_FLAG_PIO_DMA (1 << 8) /* PIO cmds via DMA */
+#define ATA_FLAG_NO_ATAPI (1 << 11) /* No ATAPI support */
+
+struct ahci_cmd_hdr
+{
+ u32 opts;
+ u32 status;
+ u64 tbl_addr;
+ //u32 tbl_addr_hi;
+ u32 reserved[4];
+};
+
+struct ahci_sg
+{
+ u64 addr;
+ //u32 addr_hi;
+ u32 reserved;
+ u32 flags_size;
+};
+
+struct ahci_ioports
+{
+ void __iomem *port_mmio;
+ struct ahci_cmd_hdr *cmd_slot;
+ struct ahci_sg *cmd_tbl_sg;
+ ulong cmd_tbl;
+ u32 rx_fis;
+};
+
+/**
+ * struct ahci_uc_priv - information about an AHCI controller
+ *
+ * When driver model is used, this is accessible using dev_get_uclass_priv(dev)
+ * where dev is the controller (although at present it sometimes stands alone).
+ */
+struct ahci_uc_priv
+{
+ struct rt_device parent;
+ struct ahci_ioports port[AHCI_MAX_PORTS];
+ u16 *ataid[AHCI_MAX_PORTS];
+ u32 n_ports;
+ u32 hard_port_no;
+ u32 host_flags;
+ u32 host_set_flags;
+ void *mmio_base;
+ u32 pio_mask;
+ u32 udma_mask;
+ u32 flags;
+ u32 cap; /* cache of HOST_CAP register */
+ u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+ u32 link_port_map; /*linkup port map*/
+};
+
+struct ahci_ops
+{
+ /**
+ * reset() - reset the controller
+ *
+ * @dev: Controller to reset
+ * @return 0 if OK, -ve on error
+ */
+ int (*reset)(struct rt_device *dev);
+
+ /**
+ * port_status() - get the status of a SATA port
+ *
+ * @dev: Controller to reset
+ * @port: Port number to check (0 for first)
+ * @return 0 if detected, -ENXIO if nothing on port, other -ve on error
+ */
+ int (*port_status)(struct rt_device *dev, int port);
+
+ /**
+ * scan() - scan SATA ports
+ *
+ * @dev: Controller to scan
+ * @return 0 if OK, -ve on error
+ */
+ int (*scan)(struct rt_device *dev);
+};
+
+#define ahci_get_ops(dev) ((struct ahci_ops *)(dev)->driver->ops)
+
+/**
+ * sata_reset() - reset the controller
+ *
+ * @dev: Controller to reset
+ * @return 0 if OK, -ve on error
+ */
+int sata_reset(struct rt_device *dev);
+
+/**
+ * sata_port_status() - get the status of a SATA port
+ *
+ * @dev: Controller to reset
+ * @port: Port number to check (0 for first)
+ * @return 0 if detected, -ENXIO if nothin on port, other -ve on error
+ */
+int sata_dm_port_status(struct rt_device *dev, int port);
+
+/**
+ * sata_scan() - scan SATA ports
+ *
+ * @dev: Controller to scan
+ * @return 0 if OK, -ve on error
+ */
+int sata_scan(struct rt_device *dev);
+
+int ahci_init(void __iomem *base);
+int ahci_reset(void __iomem *base);
+
+/**
+ * ahci_init_one_dm() - set up a single AHCI port
+ *
+ * @dev: Controller to init
+ */
+int ahci_init_one_dm(struct rt_device *dev);
+
+/**
+ * ahci_start_ports_dm() - start all AHCI ports for a controller
+ *
+ * @dev: Controller containing ports to start
+ */
+int ahci_start_ports_dm(struct rt_device *dev);
+
+/**
+ * ahci_init_dm() - init AHCI for a controller, finding all ports
+ *
+ * @dev: Device to init
+ */
+int ahci_init_dm(struct rt_device *dev, void __iomem *base);
+
+/**
+ * ahci_bind_scsi() - bind a new SCSI bus as a child
+ *
+ * Note that the SCSI bus device will itself bind block devices
+ *
+ * @ahci_dev: AHCI parent device
+ * @devp: Returns new SCSI bus device
+ * @return 0 if OK, -ve on error
+ */
+int ahci_bind_scsi(struct rt_device *ahci_dev, struct rt_device **devp);
+
+/**
+ * ahci_probe_scsi() - probe and scan the attached SCSI bus
+ *
+ * Note that the SCSI device will itself bind block devices for any storage
+ * devices it finds.
+ *
+ * @ahci_dev: AHCI parent device
+ * @base: Base address of AHCI port
+ * @return 0 if OK, -ve on error
+ */
+int ahci_probe_scsi(struct rt_device *ahci_dev, ulong base);
+
+/**
+ * ahci_probe_scsi_pci() - probe and scan the attached SCSI bus on PCI
+ *
+ * Note that the SCSI device will itself bind block devices for any storage
+ * devices it finds.
+ *
+ * @ahci_dev: AHCI parent device
+ * @return 0 if OK, -ve on error
+ */
+int ahci_probe_scsi_pci(struct rt_device *ahci_dev);
+
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/ata_debug.h b/bsp/ls2kdev/drivers/ata/ata_debug.h
new file mode 100644
index 0000000000..cf628d73ea
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/ata_debug.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui first version
+ */
+
+#ifndef __ATA_DEBUG_H__
+#define __ATA_DEBUG_H__
+//#define ATA_DEBUG
+#include <rtthread.h>
+#ifdef ATA_DEBUG
+#define debug rt_kprintf
+#else
+#define debug(...)
+#endif
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/ata_interface.h b/bsp/ls2kdev/drivers/ata/ata_interface.h
new file mode 100644
index 0000000000..fc7fb5cbe6
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/ata_interface.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui first version
+ */
+
+#ifndef __ATA_INTERFACE_H__
+#define __ATA_INTERFACE_H__
+
+typedef rt_uint8_t u8;
+typedef rt_uint16_t u16;
+typedef rt_uint32_t u32;
+typedef rt_uint64_t u64;
+typedef rt_uint64_t ulong;
+
+typedef rt_int8_t s8;
+typedef rt_int16_t s16;
+typedef rt_int32_t s32;
+typedef rt_int64_t s64;
+
+typedef rt_size_t lbaint_t;
+
+#define __iomem
+#define mdelay rt_thread_mdelay
+#define udelay(...) rt_thread_mdelay(1)
+
+#define cpu_to_le32
+#define cpu_to_le16
+#define le32_to_cpu
+#define le16_to_cpu
+
+#define flush_cache(...)
+#define invalidate_dcache_range(...)
+
+#define ARCH_DMA_MINALIGN 1024
+
+#define CONFIG_IS_ENABLED
+#define AHCI 1
+
+#define VADDR_TO_PHY(vaddr) (((u64)vaddr) - KSEG0BASE)
+#define LOW_PHY(vaddr) ((u32)VADDR_TO_PHY(vaddr))
+#define HIGH_PHY(vaddr) ((u32)((VADDR_TO_PHY(vaddr)) >> 32))
+
+#define ALIGN_1(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1)
+#define ALIGN_DOWN(x, a) ALIGN_1((x) - ((a)-1), (a))
+#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
+#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
+#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a)-1)) == 0)
+
+#define ROUND(a, b) (((a) + (b)-1) & ~((b)-1))
+
+#define PAD_COUNT(s, pad) (((s)-1) / (pad) + 1)
+#define PAD_SIZE(s, pad) (PAD_COUNT(s, pad) * pad)
+#define ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad) \
+ char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align) + (align - 1)]; \
+ \
+ type *name = (type *)ALIGN_1((rt_ubase_t)__##name, align)
+#define ALLOC_ALIGN_BUFFER(type, name, size, align) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
+#define ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, ARCH_DMA_MINALIGN, pad)
+#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \
+ ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
+
+static inline u32 readl(void *addr)
+{
+ return *((u32 *)addr);
+}
+
+static inline void writel(u32 data, void *addr)
+{
+ *((u32 *)addr) = data;
+}
+
+static inline int ffs(int word)
+{
+ int r;
+
+ if (word == 0)
+ {
+ return 0;
+ }
+
+ word &= (-word);
+
+ __asm__("clz %0, %1"
+ : "=r"(r)
+ : "r"(word));
+ return 32 - r;
+}
+
+static inline void setbits_le32(u32 *addr, u32 value)
+{
+ *addr = value;
+}
+
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/blk_device.c b/bsp/ls2kdev/drivers/ata/blk_device.c
new file mode 100644
index 0000000000..c6ed880988
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/blk_device.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui first version
+ */
+
+#include <rtthread.h>
+#include <rtdef.h>
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <dfs_file.h>
+#include <ext4.h>
+#include <ext4_debug.h>
+#include <blk_device.h>
+#include <stdint.h>
+
+static struct blk_device *blkdev;
+static rt_uint32_t disk_sector_size;
+
+static int blockdev_open(struct ext4_blockdev *bdev)
+{
+ int r;
+ uint32_t size;
+ rt_device_t device = (rt_device_t)blkdev;
+ struct rt_device_blk_geometry geometry;
+
+ RT_ASSERT(device);
+
+ r = rt_device_open((rt_device_t)blkdev, RT_DEVICE_OFLAG_RDWR);
+
+ if (r != RT_EOK)
+ {
+ return r;
+ }
+
+ r = rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
+
+ if (RT_EOK == r)
+ {
+ bdev->part_offset = 0;
+ bdev->part_size = geometry.sector_count * geometry.bytes_per_sector;
+ disk_sector_size = geometry.bytes_per_sector;
+ bdev->bdif->ph_bcnt = bdev->part_size / bdev->bdif->ph_bsize;
+ }
+
+ return r;
+}
+
+static int blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
+ uint32_t blk_cnt)
+{
+ int result;
+ rt_device_t device = (rt_device_t)blkdev;
+ struct blk_device *blk = (struct blk_device *)device;
+ RT_ASSERT(device);
+
+ result = rt_device_read(device, blk_id * (bdev->bdif->ph_bsize / disk_sector_size),
+ buf, blk_cnt * (bdev->bdif->ph_bsize / disk_sector_size));
+
+ if ((blk_cnt * (bdev->bdif->ph_bsize / disk_sector_size)) == result)
+ {
+ result = 0;
+ }
+ else
+ {
+ result = -EIO;
+ }
+
+ return result;
+}
+
+static int blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf,
+ uint64_t blk_id, uint32_t blk_cnt)
+{
+ int result;
+ rt_device_t device = (rt_device_t)blkdev;
+
+ RT_ASSERT(device);
+
+ result = rt_device_write(device, blk_id * (bdev->bdif->ph_bsize / disk_sector_size),
+ buf, blk_cnt * (bdev->bdif->ph_bsize / disk_sector_size));
+
+ if ((blk_cnt * (bdev->bdif->ph_bsize / disk_sector_size)) == result)
+ {
+ result = 0;
+ }
+ else
+ {
+ result = -EIO;
+ }
+
+ return result;
+}
+
+static int blockdev_close(struct ext4_blockdev *bdev)
+{
+ return rt_device_close((rt_device_t)blkdev);
+}
+
+static int blockdev_lock(struct ext4_blockdev *bdev)
+{
+ return 0;
+}
+
+static int blockdev_unlock(struct ext4_blockdev *bdev)
+{
+ return 0;
+}
+
+EXT4_BLOCKDEV_STATIC_INSTANCE(bdev, 4096, 0, blockdev_open,
+ blockdev_bread, blockdev_bwrite, blockdev_close,
+ blockdev_lock, blockdev_unlock);
+
+void blk_device_init(struct blk_device *blk_devices)
+{
+ blkdev = blk_devices;
+
+ if (ext4_mbr_scan(&bdev, &(blkdev->ext4_partition)) != EOK)
+ {
+ rt_kprintf("MBR scan failed!\n");
+ return;
+ }
+}
diff --git a/bsp/ls2kdev/drivers/ata/blk_device.h b/bsp/ls2kdev/drivers/ata/blk_device.h
new file mode 100644
index 0000000000..8401e8aac6
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/blk_device.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui first version
+ */
+
+#ifndef __BLK_DEVICE_H__
+#define __BLK_DEVICE_H__
+
+#include <rtconfig.h>
+#include <ext4_mbr.h>
+
+#define DEV_TYPE_UNKNOWN 0xff /* not connected */
+#define DEV_TYPE_HARDDISK 0x00 /* harddisk */
+#define DEV_TYPE_TAPE 0x01 /* Tape */
+#define DEV_TYPE_CDROM 0x05 /* CD-ROM */
+#define DEV_TYPE_OPDISK 0x07 /* optical disk */
+
+struct blk_device
+{
+ struct rt_device parent;
+ struct ahci_uc_priv *ahci_device;
+
+ rt_uint8_t target;
+ rt_uint8_t lun;
+ rt_uint8_t type;
+
+#ifdef RT_USING_DFS_LWEXT4
+ struct ext4_mbr_bdevs ext4_partition;
+#endif
+
+ rt_bool_t lba48;
+ rt_uint64_t lba;
+ rt_uint64_t blksz;
+ rt_int32_t log2blksz;
+
+ char product[21];
+ char revision[9];
+ char vendor[41];
+};
+
+void blk_device_init(struct blk_device *blk_devices);
+
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/dwc_ahsata.c b/bsp/ls2kdev/drivers/ata/dwc_ahsata.c
new file mode 100644
index 0000000000..bf76636e06
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/dwc_ahsata.c
@@ -0,0 +1,1234 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#include <rtthread.h>
+#include <rtdef.h>
+#include <mips_addrspace.h>
+#include <ata_interface.h>
+#include <ahci.h>
+#include <dwc_ahsata.h>
+#include <fis.h>
+#include <libata.h>
+#include <ata_debug.h>
+#include <blk_device.h>
+#include "dwc_ahsata_priv.h"
+
+struct sata_port_regs
+{
+ u32 clb;
+ u32 clbu;
+ u32 fb;
+ u32 fbu;
+ u32 is;
+ u32 ie;
+ u32 cmd;
+ u32 res1[1];
+ u32 tfd;
+ u32 sig;
+ u32 ssts;
+ u32 sctl;
+ u32 serr;
+ u32 sact;
+ u32 ci;
+ u32 sntf;
+ u32 res2[1];
+ u32 dmacr;
+ u32 res3[1];
+ u32 phycr;
+ u32 physr;
+};
+
+struct sata_host_regs
+{
+ u32 cap;
+ u32 ghc;
+ u32 is;
+ u32 pi;
+ u32 vs;
+ u32 ccc_ctl;
+ u32 ccc_ports;
+ u32 res1[2];
+ u32 cap2;
+ u32 res2[30];
+ u32 bistafr;
+ u32 bistcr;
+ u32 bistfctr;
+ u32 bistsr;
+ u32 bistdecr;
+ u32 res3[2];
+ u32 oobr;
+ u32 res4[8];
+ u32 timer1ms;
+ u32 res5[1];
+ u32 gparam1r;
+ u32 gparam2r;
+ u32 pparamr;
+ u32 testr;
+ u32 versionr;
+ u32 idr;
+};
+
+#define MAX_DATA_BYTES_PER_SG (4 * 1024 * 1024)
+#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
+
+#define writel_with_flush(a, b) \
+ do \
+ { \
+ writel(a, b); \
+ readl(b); \
+ } while (0)
+
+static inline void __iomem *ahci_port_base(void __iomem *base, u32 port)
+{
+ return base + 0x100 + (port * 0x80);
+}
+
+static int waiting_for_cmd_completed(u8 *offset, int timeout_msec, u32 sign)
+{
+ int i;
+ u32 status;
+
+ for (i = 0; ((status = readl(offset)) & sign) && i < timeout_msec; i++)
+ {
+ mdelay(1);
+ }
+
+ return (i < timeout_msec) ? 0 : -1;
+}
+
+static int ahci_setup_oobr(struct ahci_uc_priv *uc_priv, int clk)
+{
+ struct sata_host_regs *host_mmio = uc_priv->mmio_base;
+
+ writel(SATA_HOST_OOBR_WE, &host_mmio->oobr);
+ writel(0x02060b14, &host_mmio->oobr);
+
+ return 0;
+}
+
+int ahci_host_init(struct ahci_uc_priv *uc_priv)
+{
+ u32 tmp, cap_save, num_ports;
+ int i, j, timeout = 1000;
+ struct sata_port_regs *port_mmio = NULL;
+ struct sata_host_regs *host_mmio = uc_priv->mmio_base;
+
+ //prepare to enable staggered spin-up
+ cap_save = readl(&host_mmio->cap);
+ cap_save |= SATA_HOST_CAP_SSS;
+
+ /* global controller reset */
+ tmp = readl(&host_mmio->ghc);
+
+ //ahsata controller reset
+ if ((tmp & SATA_HOST_GHC_HR) == 0)
+ {
+ writel_with_flush(tmp | SATA_HOST_GHC_HR, &host_mmio->ghc);
+ }
+
+ //wait for reset finishing
+ while ((readl(&host_mmio->ghc) & SATA_HOST_GHC_HR) && --timeout)
+ ;
+
+ //reset timeout
+ if (timeout <= 0)
+ {
+ debug("controller reset failed (0x%x)\n", tmp);
+ return -1;
+ }
+
+ /* Set timer 1ms @ 100MHz*/
+ writel(100000000 / 1000, &host_mmio->timer1ms);
+
+ ahci_setup_oobr(uc_priv, 0);
+
+ //enable ahci
+ writel_with_flush(SATA_HOST_GHC_AE, &host_mmio->ghc);
+
+ //enable staggered spin-up
+ writel(cap_save, &host_mmio->cap);
+
+ //get sata port number
+ num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1;
+
+ //initialize pi register to set correct port number
+ writel_with_flush((1 << num_ports) - 1, &host_mmio->pi);
+
+ /*
+ * Determine which Ports are implemented by the DWC_ahsata,
+ * by reading the PI register. This bit map value aids the
+ * software to determine how many Ports are available and
+ * which Port registers need to be initialized.
+ */
+ uc_priv->cap = readl(&host_mmio->cap);
+ uc_priv->port_map = readl(&host_mmio->pi);
+
+ /* Determine how many command slots the HBA supports */
+ uc_priv->n_ports = (uc_priv->cap & SATA_HOST_CAP_NP_MASK) + 1;
+
+ debug("cap 0x%x port_map 0x%x n_ports %d\n",
+ uc_priv->cap, uc_priv->port_map, uc_priv->n_ports);
+
+ for (i = 0; i < uc_priv->n_ports; i++)
+ {
+ uc_priv->port[i].port_mmio = ahci_port_base(host_mmio, i);
+ port_mmio = uc_priv->port[i].port_mmio;
+
+ /* Ensure that the DWC_ahsata is in idle state */
+ tmp = readl(&port_mmio->cmd);
+
+ /*
+ * When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR
+ * are all cleared, the Port is in an idle state.
+ */
+ if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR |
+ SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST))
+ {
+
+ /*
+ * System software places a Port into the idle state by
+ * clearing P#CMD.ST and waiting for P#CMD.CR to return
+ * 0 when read.
+ */
+ tmp &= ~SATA_PORT_CMD_ST;
+ writel_with_flush(tmp, &port_mmio->cmd);
+
+ /*
+ * spec says 500 msecs for each bit, so
+ * this is slightly incorrect.
+ */
+ mdelay(500);
+
+ timeout = 1000;
+ while ((readl(&port_mmio->cmd) & SATA_PORT_CMD_CR) && --timeout)
+ ;
+
+ if (timeout <= 0)
+ {
+ debug("port reset failed (0x%x)\n", tmp);
+ return -1;
+ }
+ }
+
+ /* Spin-up device */
+ tmp = readl(&port_mmio->cmd);
+ writel((tmp | SATA_PORT_CMD_SUD), &port_mmio->cmd);
+
+ /* Wait for spin-up to finish */
+ timeout = 1000;
+ while (!(readl(&port_mmio->cmd) | SATA_PORT_CMD_SUD) && --timeout)
+ ;
+
+ if (timeout <= 0)
+ {
+ debug("Spin-Up can't finish!\n");
+ return -1;
+ }
+
+ for (j = 0; j < 100; ++j)
+ {
+ mdelay(10);
+ tmp = readl(&port_mmio->ssts);
+ if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) ||
+ ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1))
+ {
+ break;
+ }
+ }
+
+ /* Wait for COMINIT bit 26 (DIAG_X) in SERR */
+ timeout = 1000;
+ while (!(readl(&port_mmio->serr) & SATA_PORT_SERR_DIAG_X) && --timeout)
+ ;
+
+ if (timeout <= 0)
+ {
+ debug("Can't find DIAG_X set!\n");
+ return -1;
+ }
+
+ /*
+ * For each implemented Port, clear the P#SERR
+ * register, by writing ones to each implemented\
+ * bit location.
+ */
+ tmp = readl(&port_mmio->serr);
+ debug("P#SERR 0x%x\n",
+ tmp);
+ writel(tmp, &port_mmio->serr);
+
+ /* Ack any pending irq events for this port */
+ tmp = readl(&host_mmio->is);
+ debug("IS 0x%x\n", tmp);
+ if (tmp)
+ {
+ writel(tmp, &host_mmio->is);
+ }
+
+ writel(1 << i, &host_mmio->is);
+
+ /* set irq mask (enables interrupts) */
+ writel(DEF_PORT_IRQ, &port_mmio->ie);
+
+ /* register linkup ports */
+ tmp = readl(&port_mmio->ssts);
+ debug("Port %d status: 0x%x\n", i, tmp);
+ if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03)
+ {
+ uc_priv->link_port_map |= (0x01 << i);
+ }
+ }
+
+ tmp = readl(&host_mmio->ghc);
+ debug("GHC 0x%x\n", tmp);
+ //Interrupt Enable
+ writel(tmp | SATA_HOST_GHC_IE, &host_mmio->ghc);
+ tmp = readl(&host_mmio->ghc);
+ debug("GHC 0x%x\n", tmp);
+
+ return 0;
+}
+
+int rt_hw_ahci_host_init()
+{
+ struct ahci_uc_priv *ahci_device;
+ ahci_device = (struct ahci_uc_priv *)rt_device_create(RT_Device_Class_Miscellaneous, sizeof(struct ahci_uc_priv) - sizeof(struct rt_device));
+
+ ahci_device->mmio_base = (void *)DWCAHSATA_BASE;
+ ahci_device->parent.init = NULL;
+ ahci_device->parent.open = NULL;
+ ahci_device->parent.close = NULL;
+ ahci_device->parent.read = NULL;
+ ahci_device->parent.write = NULL;
+ ahci_device->parent.control = NULL;
+
+ if (rt_device_register((rt_device_t)ahci_device, "dwc_ahsata_ahci", 0) != RT_EOK)
+ {
+ rt_kprintf("dwc_ahsata_ahci device register failed!\n");
+ return -RT_ERROR;
+ }
+
+ if (dwc_ahsata_probe((rt_device_t)ahci_device) != 0)
+ {
+ rt_kprintf("ahci probe failed!\n");
+ return -RT_ERROR;
+ }
+
+ if (dwc_ahsata_scan((rt_device_t)ahci_device) != 0)
+ {
+ rt_kprintf("ahci host sata device scan failed!\n");
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+INIT_COMPONENT_EXPORT(rt_hw_ahci_host_init);
+
+static void ahci_print_info(struct ahci_uc_priv *uc_priv)
+{
+ struct sata_host_regs *host_mmio = uc_priv->mmio_base;
+ u32 vers, cap, impl, speed;
+ const char *speed_s;
+ const char *scc_s;
+
+ vers = readl(&host_mmio->vs);
+ cap = uc_priv->cap;
+ impl = uc_priv->port_map;
+
+ speed = (cap & SATA_HOST_CAP_ISS_MASK) >> SATA_HOST_CAP_ISS_OFFSET;
+
+ if (speed == 1)
+ {
+ speed_s = "1.5";
+ }
+ else if (speed == 2)
+ {
+ speed_s = "3";
+ }
+ else
+ {
+ speed_s = "?";
+ }
+
+ scc_s = "SATA";
+
+ rt_kprintf("AHCI %02x%02x.%02x%02x "
+ "%u slots %u ports %s Gbps 0x%x impl %s mode\n",
+ (vers >> 24) & 0xff,
+ (vers >> 16) & 0xff,
+ (vers >> 8) & 0xff,
+ vers & 0xff,
+ ((cap >> 8) & 0x1f) + 1,
+ (cap & 0x1f) + 1,
+ speed_s,
+ impl,
+ scc_s);
+
+ rt_kprintf("flags: "
+ "%s%s%s%s%s%s"
+ "%s%s%s%s%s%s%s\n",
+ cap & (1 << 31) ? "64bit " : "",
+ cap & (1 << 30) ? "ncq " : "",
+ cap & (1 << 28) ? "ilck " : "",
+ cap & (1 << 27) ? "stag " : "",
+ cap & (1 << 26) ? "pm " : "",
+ cap & (1 << 25) ? "led " : "",
+ cap & (1 << 24) ? "clo " : "",
+ cap & (1 << 19) ? "nz " : "",
+ cap & (1 << 18) ? "only " : "",
+ cap & (1 << 17) ? "pmp " : "",
+ cap & (1 << 15) ? "pio " : "",
+ cap & (1 << 14) ? "slum " : "",
+ cap & (1 << 13) ? "part " : "");
+
+ rt_kprintf("version = %08x\n", ((struct sata_host_regs *)(uc_priv->mmio_base))->versionr);
+}
+
+static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port,
+ unsigned char *buf, int buf_len)
+{
+ struct ahci_ioports *pp = &uc_priv->port[port];
+ struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
+ u32 sg_count, max_bytes;
+ int i;
+
+ max_bytes = MAX_DATA_BYTES_PER_SG;
+ sg_count = ((buf_len - 1) / max_bytes) + 1;
+
+ if (sg_count > AHCI_MAX_SG)
+ {
+ rt_kprintf("Error:Too much sg!\n");
+ return -1;
+ }
+
+ for (i = 0; i < sg_count; i++)
+ {
+ ahci_sg->addr = VADDR_TO_PHY(buf + i * max_bytes);
+ //ahci_sg->addr_hi = 0;
+ ahci_sg->flags_size = cpu_to_le32(0x3fffff &
+ (buf_len < max_bytes
+ ? (buf_len - 1)
+ : (max_bytes - 1)));
+ ahci_sg++;
+ buf_len -= max_bytes;
+ }
+
+ return sg_count;
+}
+
+static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts)
+{
+ struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot +
+ AHCI_CMD_SLOT_SZ * cmd_slot);
+
+ memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ);
+ cmd_hdr->opts = cpu_to_le32(opts);
+ cmd_hdr->status = 0;
+ pp->cmd_slot->tbl_addr = VADDR_TO_PHY(pp->cmd_tbl);
+ /*#ifdef CONFIG_PHYS_64BIT
+ pp->cmd_slot->tbl_addr_hi =
+ cpu_to_le32((u32)(((pp->cmd_tbl) >> 16) >> 16));
+#endif*/
+}
+
+#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0)
+
+static int ahci_exec_ata_cmd(struct ahci_uc_priv *uc_priv, u8 port,
+ struct sata_fis_h2d *cfis, u8 *buf, u32 buf_len,
+ s32 is_write)
+{
+ struct ahci_ioports *pp = &uc_priv->port[port];
+ struct sata_port_regs *port_mmio = pp->port_mmio;
+ u32 opts;
+ int sg_count = 0, cmd_slot = 0;
+
+ cmd_slot = AHCI_GET_CMD_SLOT(readl(&port_mmio->ci));
+
+ if (32 == cmd_slot)
+ {
+ rt_kprintf("Can't find empty command slot!\n");
+ return 0;
+ }
+
+ /* Check xfer length */
+ if (buf_len > MAX_BYTES_PER_TRANS)
+ {
+ rt_kprintf("Max transfer length is %dB\n\r",
+ MAX_BYTES_PER_TRANS);
+ return 0;
+ }
+
+ memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d));
+
+ if (buf && buf_len)
+ {
+ sg_count = ahci_fill_sg(uc_priv, port, buf, buf_len);
+ }
+
+ opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16);
+
+ if (is_write)
+ {
+ opts |= 0x40;
+ flush_cache((ulong)buf, buf_len);
+ }
+
+ ahci_fill_cmd_slot(pp, cmd_slot, opts);
+
+ flush_cache((int)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ);
+ writel_with_flush(1 << cmd_slot, &port_mmio->ci);
+
+ if (waiting_for_cmd_completed((u8 *)&port_mmio->ci, 10000,
+ 0x1 << cmd_slot))
+ {
+ rt_kprintf("timeout exit!\n");
+ return -1;
+ }
+
+ invalidate_dcache_range((int)(pp->cmd_slot),
+ (int)(pp->cmd_slot) + AHCI_PORT_PRIV_DMA_SZ);
+
+ debug("ahci_exec_ata_cmd: %d byte transferred.\n",
+ pp->cmd_slot->status);
+
+ if (!is_write)
+ {
+ invalidate_dcache_range((ulong)buf, (ulong)buf + buf_len);
+ }
+
+ return buf_len;
+}
+
+static void ahci_set_feature(struct ahci_uc_priv *uc_priv, u8 port)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 1 << 7;
+ cfis->command = ATA_CMD_SET_FEATURES;
+ cfis->features = SETFEATURES_XFER;
+ cfis->sector_count = ffs(uc_priv->udma_mask + 1) + 0x3e;
+
+ ahci_exec_ata_cmd(uc_priv, port, cfis, NULL, 0, READ_CMD);
+}
+
+static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
+{
+ struct ahci_ioports *pp = &uc_priv->port[port];
+ struct sata_port_regs *port_mmio = pp->port_mmio;
+ u32 port_status;
+ u64 mem;
+ int timeout = 10000000;
+
+ debug("Enter start port: %d\n", port);
+ port_status = readl(&port_mmio->ssts);
+ debug("Port %d status: %x\n", port, port_status);
+
+ if ((port_status & 0xf) != 0x03)
+ {
+ rt_kprintf("No Link on this port!\n");
+ return -1;
+ }
+
+ mem = (u64)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024);
+
+ if (!mem)
+ {
+ rt_kprintf("No mem for table!\n");
+ return -ENOMEM;
+ }
+
+ mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */
+ memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+ /*
+ * First item in chunk of DMA memory: 32-slot command table,
+ * 32 bytes each in size
+ */
+ pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
+ debug("cmd_slot = 0x%p\n", pp->cmd_slot);
+ mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS);
+
+ /*
+ * Second item: Received-FIS area, 256-Byte aligned
+ */
+ pp->rx_fis = mem;
+ mem += AHCI_RX_FIS_SZ;
+
+ /*
+ * Third item: data area for storing a single command
+ * and its scatter-gather table
+ */
+ pp->cmd_tbl = mem;
+ debug("cmd_tbl_dma = 0x%lx\n", pp->cmd_tbl);
+
+ mem += AHCI_CMD_TBL_HDR;
+
+ writel_with_flush(0x00004444, &port_mmio->dmacr);
+ pp->cmd_tbl_sg = (struct ahci_sg *)mem;
+ writel_with_flush(LOW_PHY(pp->cmd_slot), &port_mmio->clb);
+ writel_with_flush(HIGH_PHY(pp->cmd_slot), &port_mmio->clbu);
+ writel_with_flush(LOW_PHY(pp->rx_fis), &port_mmio->fb);
+ writel_with_flush(HIGH_PHY(pp->rx_fis), &port_mmio->fbu);
+
+ /* Enable FRE */
+ writel_with_flush((SATA_PORT_CMD_FRE | readl(&port_mmio->cmd)),
+ &port_mmio->cmd);
+
+ /* Wait device ready */
+ while ((readl(&port_mmio->tfd) & (SATA_PORT_TFD_STS_ERR |
+ SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY)) &&
+ --timeout)
+ ;
+ if (timeout <= 0)
+ {
+ debug("Device not ready for BSY, DRQ and"
+ "ERR in TFD!\n");
+ return -1;
+ }
+
+ writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+ PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+ PORT_CMD_START,
+ &port_mmio->cmd);
+
+ debug("Exit start port %d\n", port);
+
+ return 0;
+}
+
+static void dwc_ahsata_print_info(struct blk_device *pdev)
+{
+ rt_kprintf("SATA Device Info:\n\r");
+ rt_kprintf("S/N: %s\n\rProduct model number: %s\n\r"
+ "Firmware version: %s\n\rCapacity: %lu sectors\n\r",
+ pdev->product, pdev->vendor, pdev->revision, pdev->lba);
+}
+
+static void dwc_ahsata_identify(struct ahci_uc_priv *uc_priv, u16 *id)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+ u8 port = uc_priv->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_ID_ATA;
+
+ ahci_exec_ata_cmd(uc_priv, port, cfis, (u8 *)id, ATA_ID_WORDS * 2,
+ READ_CMD);
+ ata_swap_buf_le16(id, ATA_ID_WORDS);
+}
+
+static void dwc_ahsata_xfer_mode(struct ahci_uc_priv *uc_priv, u16 *id)
+{
+ uc_priv->pio_mask = id[ATA_ID_PIO_MODES];
+ uc_priv->udma_mask = id[ATA_ID_UDMA_MODES];
+ debug("pio %04x, udma %04x\n\r", uc_priv->pio_mask, uc_priv->udma_mask);
+}
+
+static u32 dwc_ahsata_rw_cmd(struct ahci_uc_priv *uc_priv, u32 start,
+ u32 blkcnt, u8 *buffer, int is_write)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+ u8 port = uc_priv->hard_port_no;
+ u32 block;
+
+ block = start;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
+ cfis->device = ATA_LBA;
+
+ cfis->device |= (block >> 24) & 0xf;
+ cfis->lba_high = (block >> 16) & 0xff;
+ cfis->lba_mid = (block >> 8) & 0xff;
+ cfis->lba_low = block & 0xff;
+ cfis->sector_count = (u8)(blkcnt & 0xff);
+
+ if (ahci_exec_ata_cmd(uc_priv, port, cfis, buffer,
+ ATA_SECT_SIZE * blkcnt, is_write) > 0)
+ return blkcnt;
+ else
+ return 0;
+}
+
+static void dwc_ahsata_flush_cache(struct ahci_uc_priv *uc_priv)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+ u8 port = uc_priv->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_FLUSH;
+
+ ahci_exec_ata_cmd(uc_priv, port, cfis, NULL, 0, 0);
+}
+
+static u32 dwc_ahsata_rw_cmd_ext(struct ahci_uc_priv *uc_priv, u32 start,
+ lbaint_t blkcnt, u8 *buffer, int is_write)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+ u8 port = uc_priv->hard_port_no;
+ u64 block;
+
+ block = (u64)start;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+
+ cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
+ : ATA_CMD_READ_EXT;
+
+ cfis->lba_high_exp = (block >> 40) & 0xff;
+ cfis->lba_mid_exp = (block >> 32) & 0xff;
+ cfis->lba_low_exp = (block >> 24) & 0xff;
+ cfis->lba_high = (block >> 16) & 0xff;
+ cfis->lba_mid = (block >> 8) & 0xff;
+ cfis->lba_low = block & 0xff;
+ cfis->device = ATA_LBA;
+ cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
+ cfis->sector_count = blkcnt & 0xff;
+
+ if (ahci_exec_ata_cmd(uc_priv, port, cfis, buffer,
+ ATA_SECT_SIZE * blkcnt, is_write) > 0)
+ return blkcnt;
+ else
+ return 0;
+}
+
+static void dwc_ahsata_flush_cache_ext(struct ahci_uc_priv *uc_priv)
+{
+ struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN);
+ struct sata_fis_h2d *cfis = &h2d;
+ u8 port = uc_priv->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_FLUSH_EXT;
+
+ ahci_exec_ata_cmd(uc_priv, port, cfis, NULL, 0, 0);
+}
+
+static void dwc_ahsata_init_wcache(struct ahci_uc_priv *uc_priv, u16 *id)
+{
+ if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
+ uc_priv->flags |= SATA_FLAG_WCACHE;
+ if (ata_id_has_flush(id))
+ uc_priv->flags |= SATA_FLAG_FLUSH;
+ if (ata_id_has_flush_ext(id))
+ uc_priv->flags |= SATA_FLAG_FLUSH_EXT;
+}
+
+static u32 ata_low_level_rw_lba48(struct ahci_uc_priv *uc_priv, u32 blknr,
+ lbaint_t blkcnt, const void *buffer,
+ int is_write)
+{
+ u32 start, blks;
+ u8 *addr;
+ int max_blks;
+
+ start = blknr;
+ blks = blkcnt;
+ addr = (u8 *)buffer;
+
+ max_blks = ATA_MAX_SECTORS_LBA48;
+
+ do
+ {
+ if (blks > max_blks)
+ {
+ if (max_blks != dwc_ahsata_rw_cmd_ext(uc_priv, start,
+ max_blks, addr,
+ is_write))
+ return 0;
+ start += max_blks;
+ blks -= max_blks;
+ addr += ATA_SECT_SIZE * max_blks;
+ }
+ else
+ {
+ if (blks != dwc_ahsata_rw_cmd_ext(uc_priv, start, blks,
+ addr, is_write))
+ return 0;
+ start += blks;
+ blks = 0;
+ addr += ATA_SECT_SIZE * blks;
+ }
+ } while (blks != 0);
+
+ return blkcnt;
+}
+
+static u32 ata_low_level_rw_lba28(struct ahci_uc_priv *uc_priv, u32 blknr,
+ lbaint_t blkcnt, const void *buffer,
+ int is_write)
+{
+ u32 start, blks;
+ u8 *addr;
+ int max_blks;
+
+ start = blknr;
+ blks = blkcnt;
+ addr = (u8 *)buffer;
+
+ max_blks = ATA_MAX_SECTORS;
+ do
+ {
+ if (blks > max_blks)
+ {
+ if (max_blks != dwc_ahsata_rw_cmd(uc_priv, start,
+ max_blks, addr,
+ is_write))
+ return 0;
+ start += max_blks;
+ blks -= max_blks;
+ addr += ATA_SECT_SIZE * max_blks;
+ }
+ else
+ {
+ if (blks != dwc_ahsata_rw_cmd(uc_priv, start, blks,
+ addr, is_write))
+ return 0;
+ start += blks;
+ blks = 0;
+ addr += ATA_SECT_SIZE * blks;
+ }
+ } while (blks != 0);
+
+ return blkcnt;
+}
+
+static int dwc_ahci_start_ports(struct ahci_uc_priv *uc_priv)
+{
+ u32 linkmap;
+ int i;
+
+ linkmap = uc_priv->link_port_map;
+
+ if (0 == linkmap)
+ {
+ rt_kprintf("No port device detected!\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < uc_priv->n_ports; i++)
+ {
+ if ((linkmap >> i) && ((linkmap >> i) & 0x01))
+ {
+ if (ahci_port_start(uc_priv, (u8)i))
+ {
+ rt_kprintf("Can not start port %d\n", i);
+ return 1;
+ }
+
+ uc_priv->hard_port_no = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+unsigned char sector_data[512];
+
+void dump_pbuf(void *p, int len)
+{
+ rt_kprintf("----dump_pbuf----\n");
+ rt_kprintf("pbuf = 0x%p,len = %d\n", p, len);
+ u32 i;
+ u8 *q = p;
+ rt_kprintf("%p", q);
+
+ for (i = 0; i < 16; i++)
+ {
+ rt_kprintf(" %02x", i);
+ }
+
+ rt_kprintf("\n");
+
+ for (i = 0; i < len; i++)
+ {
+ if (!(i & 0xF))
+ {
+ rt_kprintf("%p", &q[i]);
+ }
+
+ rt_kprintf(" %02x", q[i]);
+
+ if ((i & 0xF) == 0xF)
+ {
+ rt_kprintf("\n");
+ }
+ }
+
+ rt_kprintf("\n-----------------\n");
+}
+
+static int dwc_ahsata_scan_common(struct ahci_uc_priv *uc_priv,
+ struct blk_device *pdev)
+{
+ u8 serial[ATA_ID_SERNO_LEN + 1] = {0};
+ u8 firmware[ATA_ID_FW_REV_LEN + 1] = {0};
+ u8 product[ATA_ID_PROD_LEN + 1] = {0};
+ u8 port = uc_priv->hard_port_no;
+ ALLOC_CACHE_ALIGN_BUFFER(u16, id, ATA_ID_WORDS);
+
+ /* Identify device to get information */
+ dwc_ahsata_identify(uc_priv, id);
+
+ /* Serial number */
+ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ memcpy(pdev->product, serial, sizeof(serial));
+
+ /* Firmware version */
+ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
+ memcpy(pdev->revision, firmware, sizeof(firmware));
+
+ /* Product model */
+ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
+ memcpy(pdev->vendor, product, sizeof(product));
+
+ /* Total sectors */
+ pdev->lba = ata_id_n_sectors(id);
+
+ pdev->type = DEV_TYPE_HARDDISK;
+ pdev->blksz = ATA_SECT_SIZE;
+ pdev->lun = 0;
+
+ /* Check if support LBA48 */
+ if (ata_id_has_lba48(id))
+ {
+ pdev->lba48 = 1;
+ debug("Device support LBA48\n\r");
+ }
+
+ /* Get the NCQ queue depth from device */
+ uc_priv->flags &= (~SATA_FLAG_Q_DEP_MASK);
+ uc_priv->flags |= ata_id_queue_depth(id);
+
+ /* Get the xfer mode from device */
+ dwc_ahsata_xfer_mode(uc_priv, id);
+
+ /* Get the write cache status from device */
+ dwc_ahsata_init_wcache(uc_priv, id);
+
+ /* Set the xfer mode to highest speed */
+ ahci_set_feature(uc_priv, port);
+ dwc_ahsata_read((rt_device_t)pdev, 0, sector_data, 1);
+ //dump_pbuf(sector_data, 512);
+ dwc_ahsata_print_info(pdev);
+
+ return 0;
+}
+
+/*
+ * SATA interface between low level driver and command layer
+ */
+static ulong sata_read_common(struct ahci_uc_priv *uc_priv,
+ struct blk_device *desc, ulong blknr,
+ lbaint_t blkcnt, void *buffer)
+{
+ u32 rc;
+
+ if (desc->lba48)
+ rc = ata_low_level_rw_lba48(uc_priv, blknr, blkcnt, buffer,
+ READ_CMD);
+ else
+ rc = ata_low_level_rw_lba28(uc_priv, blknr, blkcnt, buffer,
+ READ_CMD);
+
+ return rc;
+}
+
+static ulong sata_write_common(struct ahci_uc_priv *uc_priv,
+ struct blk_device *desc, ulong blknr,
+ lbaint_t blkcnt, const void *buffer)
+{
+ u32 rc;
+ u32 flags = uc_priv->flags;
+
+ if (desc->lba48)
+ {
+ rc = ata_low_level_rw_lba48(uc_priv, blknr, blkcnt, buffer,
+ WRITE_CMD);
+ if ((flags & SATA_FLAG_WCACHE) && (flags & SATA_FLAG_FLUSH_EXT))
+ dwc_ahsata_flush_cache_ext(uc_priv);
+ }
+ else
+ {
+ rc = ata_low_level_rw_lba28(uc_priv, blknr, blkcnt, buffer,
+ WRITE_CMD);
+ if ((flags & SATA_FLAG_WCACHE) && (flags & SATA_FLAG_FLUSH))
+ dwc_ahsata_flush_cache(uc_priv);
+ }
+
+ return rc;
+}
+
+#if !CONFIG_IS_ENABLED(AHCI)
+static int ahci_init_one(int pdev)
+{
+ int rc;
+ struct ahci_uc_priv *uc_priv = NULL;
+
+ uc_priv = malloc(sizeof(struct ahci_uc_priv));
+ if (!uc_priv)
+ return -ENOMEM;
+
+ memset(uc_priv, 0, sizeof(struct ahci_uc_priv));
+ uc_priv->dev = pdev;
+
+ uc_priv->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NO_ATAPI;
+
+ uc_priv->mmio_base = (void __iomem *)CONFIG_DWC_AHSATA_BASE_ADDR;
+
+ /* initialize adapter */
+ rc = ahci_host_init(uc_priv);
+ if (rc)
+ goto err_out;
+
+ ahci_print_info(uc_priv);
+
+ /* Save the uc_private struct to block device struct */
+ sata_dev_desc[pdev].priv = uc_priv;
+
+ return 0;
+
+err_out:
+ if (uc_priv)
+ free(uc_priv);
+ return rc;
+}
+
+int init_sata(int dev)
+{
+ struct ahci_uc_priv *uc_priv = NULL;
+
+#if defined(CONFIG_MX6)
+ if (!is_mx6dq() && !is_mx6dqp())
+ return 1;
+#endif
+ if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1))
+ {
+ rt_kprintf("The sata index %d is out of ranges\n\r", dev);
+ return -1;
+ }
+
+ ahci_init_one(dev);
+
+ uc_priv = sata_dev_desc[dev].priv;
+
+ return dwc_ahci_start_ports(uc_priv) ? 1 : 0;
+}
+
+int reset_sata(int dev)
+{
+ struct ahci_uc_priv *uc_priv;
+ struct sata_host_regs *host_mmio;
+
+ if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1))
+ {
+ rt_kprintf("The sata index %d is out of ranges\n\r", dev);
+ return -1;
+ }
+
+ uc_priv = sata_dev_desc[dev].priv;
+ if (NULL == uc_priv)
+ /* not initialized, so nothing to reset */
+ return 0;
+
+ host_mmio = uc_priv->mmio_base;
+ setbits_le32(&host_mmio->ghc, SATA_HOST_GHC_HR);
+ while (readl(&host_mmio->ghc) & SATA_HOST_GHC_HR)
+ udelay(100);
+
+ free(uc_priv);
+ memset(&sata_dev_desc[dev], 0, sizeof(struct blk_desc));
+
+ return 0;
+}
+
+int sata_port_status(int dev, int port)
+{
+ struct sata_port_regs *port_mmio;
+ struct ahci_uc_priv *uc_priv = NULL;
+
+ if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1))
+ return -EINVAL;
+
+ if (sata_dev_desc[dev].priv == NULL)
+ return -ENODEV;
+
+ uc_priv = sata_dev_desc[dev].priv;
+ port_mmio = uc_priv->port[port].port_mmio;
+
+ return readl(&port_mmio->ssts) & SATA_PORT_SSTS_DET_MASK;
+}
+
+/*
+ * SATA interface between low level driver and command layer
+ */
+ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
+{
+ struct ahci_uc_priv *uc_priv = sata_dev_desc[dev].priv;
+
+ return sata_read_common(uc_priv, &sata_dev_desc[dev], blknr, blkcnt,
+ buffer);
+}
+
+ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
+{
+ struct ahci_uc_priv *uc_priv = sata_dev_desc[dev].priv;
+
+ return sata_write_common(uc_priv, &sata_dev_desc[dev], blknr, blkcnt,
+ buffer);
+}
+
+int scan_sata(int dev)
+{
+ struct ahci_uc_priv *uc_priv = sata_dev_desc[dev].priv;
+ struct blk_desc *pdev = &sata_dev_desc[dev];
+
+ return dwc_ahsata_scan_common(uc_priv, pdev);
+}
+#endif /* CONFIG_IS_ENABLED(AHCI) */
+
+#if CONFIG_IS_ENABLED(AHCI)
+
+int dwc_ahsata_port_status(struct rt_device *dev, int port)
+{
+ struct ahci_uc_priv *uc_priv = (struct ahci_uc_priv *)dev;
+ struct sata_port_regs *port_mmio;
+
+ port_mmio = uc_priv->port[port].port_mmio;
+ return readl(&port_mmio->ssts) & SATA_PORT_SSTS_DET_MASK ? 0 : -ENXIO;
+}
+
+int dwc_ahsata_bus_reset(struct rt_device *dev)
+{
+ struct ahci_uc_priv *uc_priv = (struct ahci_uc_priv *)dev;
+ struct sata_host_regs *host_mmio = uc_priv->mmio_base;
+
+ setbits_le32(&host_mmio->ghc, SATA_HOST_GHC_HR);
+
+ while (readl(&host_mmio->ghc) & SATA_HOST_GHC_HR)
+ {
+ udelay(100);
+ }
+
+ return 0;
+}
+
+int dwc_ahsata_scan(struct rt_device *dev)
+{
+ struct ahci_uc_priv *uc_priv = (struct ahci_uc_priv *)dev;
+ struct blk_device *blk;
+ rt_err_t ret;
+
+ blk = (struct blk_device *)rt_device_create(RT_Device_Class_Block, sizeof(struct blk_device) - sizeof(struct rt_device));
+ blk->parent.init = NULL;
+ blk->parent.open = NULL;
+ blk->parent.close = NULL;
+ blk->parent.control = dwc_ahsata_control;
+ blk->parent.read = dwc_ahsata_read;
+ blk->parent.write = dwc_ahsata_write;
+ blk->ahci_device = uc_priv;
+ blk->blksz = 512;
+ blk->log2blksz = 9;
+ blk->lba = 0;
+ ret = rt_device_register((rt_device_t)blk, "dwc_ahsata_blk", RT_DEVICE_FLAG_RDWR);
+
+ if (ret != RT_EOK)
+ {
+ debug("Can't create device\n");
+ return ret;
+ }
+
+ ret = dwc_ahsata_scan_common(uc_priv, blk);
+
+ if (ret)
+ {
+ debug("%s: Failed to scan bus\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int dwc_ahsata_probe(struct rt_device *dev)
+{
+ struct ahci_uc_priv *uc_priv = (struct ahci_uc_priv *)dev;
+ int ret;
+
+ uc_priv->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NO_ATAPI;
+
+ /* initialize adapter */
+ ret = ahci_host_init(uc_priv);
+ if (ret)
+ return ret;
+
+ ahci_print_info(uc_priv);
+
+ return dwc_ahci_start_ports(uc_priv);
+}
+
+rt_size_t dwc_ahsata_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ struct blk_device *blk = (struct blk_device *)dev;
+ return sata_read_common(blk->ahci_device, blk, pos, size, buffer);
+}
+
+rt_size_t dwc_ahsata_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ struct blk_device *blk = (struct blk_device *)dev;
+ return sata_write_common(blk->ahci_device, blk, pos, size, buffer);
+}
+
+rt_err_t dwc_ahsata_control(rt_device_t dev, int cmd, void *args)
+{
+ struct blk_device *blk = (struct blk_device *)dev;
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_BLK_GETGEOME:
+
+ if (args != NULL)
+ {
+ struct rt_device_blk_geometry *info = (struct rt_device_blk_geometry *)args;
+ info->sector_count = blk->lba;
+ info->bytes_per_sector = blk->blksz;
+ info->block_size = 0;
+ }
+
+ break;
+ }
+
+ return RT_EOK;
+}
+
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/dwc_ahsata.h b/bsp/ls2kdev/drivers/ata/dwc_ahsata.h
new file mode 100644
index 0000000000..44e993d03e
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/dwc_ahsata.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+
+#ifndef __DWC_AHSATA_H__
+#define __DWC_AHSATA_H__
+
+#define DWCAHSATA_BASE (0x9000000000000000 | 0x400e0000)
+
+int dwc_ahsata_bus_reset(struct rt_device *dev);
+int dwc_ahsata_probe(struct rt_device *dev);
+int dwc_ahsata_scan(struct rt_device *dev);
+int dwc_ahsata_port_status(struct rt_device *dev, int port);
+rt_size_t dwc_ahsata_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
+rt_size_t dwc_ahsata_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
+rt_err_t dwc_ahsata_control(rt_device_t dev, int cmd, void *args);
+
+#endif
diff --git a/bsp/ls2kdev/drivers/ata/dwc_ahsata_priv.h b/bsp/ls2kdev/drivers/ata/dwc_ahsata_priv.h
new file mode 100644
index 0000000000..03e64808b9
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/dwc_ahsata_priv.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#ifndef __DWC_AHSATA_PRIV_H__
+#define __DWC_AHSATA_PRIV_H__
+
+#define DWC_AHSATA_MAX_CMD_SLOTS 32
+
+/* Max host controller numbers */
+#define SATA_HC_MAX_NUM 4
+/* Max command queue depth per host controller */
+#define DWC_AHSATA_HC_MAX_CMD 32
+/* Max port number per host controller */
+#define SATA_HC_MAX_PORT 16
+
+/* Generic Host Register */
+
+/* HBA Capabilities Register */
+#define SATA_HOST_CAP_S64A 0x80000000
+#define SATA_HOST_CAP_SNCQ 0x40000000
+#define SATA_HOST_CAP_SSNTF 0x20000000
+#define SATA_HOST_CAP_SMPS 0x10000000
+#define SATA_HOST_CAP_SSS 0x08000000
+#define SATA_HOST_CAP_SALP 0x04000000
+#define SATA_HOST_CAP_SAL 0x02000000
+#define SATA_HOST_CAP_SCLO 0x01000000
+#define SATA_HOST_CAP_ISS_MASK 0x00f00000
+#define SATA_HOST_CAP_ISS_OFFSET 20
+#define SATA_HOST_CAP_SNZO 0x00080000
+#define SATA_HOST_CAP_SAM 0x00040000
+#define SATA_HOST_CAP_SPM 0x00020000
+#define SATA_HOST_CAP_PMD 0x00008000
+#define SATA_HOST_CAP_SSC 0x00004000
+#define SATA_HOST_CAP_PSC 0x00002000
+#define SATA_HOST_CAP_NCS 0x00001f00
+#define SATA_HOST_CAP_CCCS 0x00000080
+#define SATA_HOST_CAP_EMS 0x00000040
+#define SATA_HOST_CAP_SXS 0x00000020
+#define SATA_HOST_CAP_NP_MASK 0x0000001f
+
+/* Global HBA Control Register */
+#define SATA_HOST_GHC_AE 0x80000000
+#define SATA_HOST_GHC_IE 0x00000002
+#define SATA_HOST_GHC_HR 0x00000001
+
+/* Interrupt Status Register */
+
+/* Ports Implemented Register */
+
+/* AHCI Version Register */
+#define SATA_HOST_VS_MJR_MASK 0xffff0000
+#define SATA_HOST_VS_MJR_OFFSET 16
+#define SATA_HOST_VS_MJR_MNR 0x0000ffff
+
+/* Command Completion Coalescing Control */
+#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000
+#define SATA_HOST_CCC_CTL_TV_OFFSET 16
+#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00
+#define SATA_HOST_CCC_CTL_CC_OFFSET 8
+#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8
+#define SATA_HOST_CCC_CTL_INT_OFFSET 3
+#define SATA_HOST_CCC_CTL_EN 0x00000001
+
+/* Command Completion Coalescing Ports */
+
+/* HBA Capabilities Extended Register */
+#define SATA_HOST_CAP2_APST 0x00000004
+
+/* BIST Activate FIS Register */
+#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00
+#define SATA_HOST_BISTAFR_NCP_OFFSET 8
+#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff
+#define SATA_HOST_BISTAFR_PD_OFFSET 0
+
+/* BIST Control Register */
+#define SATA_HOST_BISTCR_FERLB 0x00100000
+#define SATA_HOST_BISTCR_TXO 0x00040000
+#define SATA_HOST_BISTCR_CNTCLR 0x00020000
+#define SATA_HOST_BISTCR_NEALB 0x00010000
+#define SATA_HOST_BISTCR_LLC_MASK 0x00000700
+#define SATA_HOST_BISTCR_LLC_OFFSET 8
+#define SATA_HOST_BISTCR_ERREN 0x00000040
+#define SATA_HOST_BISTCR_FLIP 0x00000020
+#define SATA_HOST_BISTCR_PV 0x00000010
+#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f
+#define SATA_HOST_BISTCR_PATTERN_OFFSET 0
+
+/* BIST FIS Count Register */
+
+/* BIST Status Register */
+#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff
+#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0
+#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000
+#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16
+
+/* BIST DWORD Error Count Register */
+
+/* OOB Register*/
+#define SATA_HOST_OOBR_WE 0x80000000
+#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000
+#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000
+#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00
+#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff
+
+/* Timer 1-ms Register */
+
+/* Global Parameter 1 Register */
+#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000
+#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000
+#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000
+#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000
+#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000
+#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000
+#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000
+#define SATA_HOST_GPARAM1R_BIST_M 0x00002000
+#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000
+#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400
+#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300
+#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080
+#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040
+
+/* Global Parameter 2 Register */
+#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000
+#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000
+#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000
+#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800
+#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400
+#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200
+#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff
+
+/* Port Parameter Register */
+#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200
+#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100
+#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080
+#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040
+#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038
+#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007
+
+/* Test Register */
+#define SATA_HOST_TESTR_PSEL_MASK 0x00070000
+#define SATA_HOST_TESTR_TEST_IF 0x00000001
+
+/* Port Register Descriptions */
+/* Port# Command List Base Address Register */
+#define SATA_PORT_CLB_CLB_MASK 0xfffffc00
+
+/* Port# Command List Base Address Upper 32-Bits Register */
+
+/* Port# FIS Base Address Register */
+#define SATA_PORT_FB_FB_MASK 0xfffffff0
+
+/* Port# FIS Base Address Upper 32-Bits Register */
+
+/* Port# Interrupt Status Register */
+#define SATA_PORT_IS_CPDS 0x80000000
+#define SATA_PORT_IS_TFES 0x40000000
+#define SATA_PORT_IS_HBFS 0x20000000
+#define SATA_PORT_IS_HBDS 0x10000000
+#define SATA_PORT_IS_IFS 0x08000000
+#define SATA_PORT_IS_INFS 0x04000000
+#define SATA_PORT_IS_OFS 0x01000000
+#define SATA_PORT_IS_IPMS 0x00800000
+#define SATA_PORT_IS_PRCS 0x00400000
+#define SATA_PORT_IS_DMPS 0x00000080
+#define SATA_PORT_IS_PCS 0x00000040
+#define SATA_PORT_IS_DPS 0x00000020
+#define SATA_PORT_IS_UFS 0x00000010
+#define SATA_PORT_IS_SDBS 0x00000008
+#define SATA_PORT_IS_DSS 0x00000004
+#define SATA_PORT_IS_PSS 0x00000002
+#define SATA_PORT_IS_DHRS 0x00000001
+
+/* Port# Interrupt Enable Register */
+#define SATA_PORT_IE_CPDE 0x80000000
+#define SATA_PORT_IE_TFEE 0x40000000
+#define SATA_PORT_IE_HBFE 0x20000000
+#define SATA_PORT_IE_HBDE 0x10000000
+#define SATA_PORT_IE_IFE 0x08000000
+#define SATA_PORT_IE_INFE 0x04000000
+#define SATA_PORT_IE_OFE 0x01000000
+#define SATA_PORT_IE_IPME 0x00800000
+#define SATA_PORT_IE_PRCE 0x00400000
+#define SATA_PORT_IE_DMPE 0x00000080
+#define SATA_PORT_IE_PCE 0x00000040
+#define SATA_PORT_IE_DPE 0x00000020
+#define SATA_PORT_IE_UFE 0x00000010
+#define SATA_PORT_IE_SDBE 0x00000008
+#define SATA_PORT_IE_DSE 0x00000004
+#define SATA_PORT_IE_PSE 0x00000002
+#define SATA_PORT_IE_DHRE 0x00000001
+
+/* Port# Command Register */
+#define SATA_PORT_CMD_ICC_MASK 0xf0000000
+#define SATA_PORT_CMD_ASP 0x08000000
+#define SATA_PORT_CMD_ALPE 0x04000000
+#define SATA_PORT_CMD_DLAE 0x02000000
+#define SATA_PORT_CMD_ATAPI 0x01000000
+#define SATA_PORT_CMD_APSTE 0x00800000
+#define SATA_PORT_CMD_ESP 0x00200000
+#define SATA_PORT_CMD_CPD 0x00100000
+#define SATA_PORT_CMD_MPSP 0x00080000
+#define SATA_PORT_CMD_HPCP 0x00040000
+#define SATA_PORT_CMD_PMA 0x00020000
+#define SATA_PORT_CMD_CPS 0x00010000
+#define SATA_PORT_CMD_CR 0x00008000
+#define SATA_PORT_CMD_FR 0x00004000
+#define SATA_PORT_CMD_MPSS 0x00002000
+#define SATA_PORT_CMD_CCS_MASK 0x00001f00
+#define SATA_PORT_CMD_FRE 0x00000010
+#define SATA_PORT_CMD_CLO 0x00000008
+#define SATA_PORT_CMD_POD 0x00000004
+#define SATA_PORT_CMD_SUD 0x00000002
+#define SATA_PORT_CMD_ST 0x00000001
+
+/* Port# Task File Data Register */
+#define SATA_PORT_TFD_ERR_MASK 0x0000ff00
+#define SATA_PORT_TFD_STS_MASK 0x000000ff
+#define SATA_PORT_TFD_STS_ERR 0x00000001
+#define SATA_PORT_TFD_STS_DRQ 0x00000008
+#define SATA_PORT_TFD_STS_BSY 0x00000080
+
+/* Port# Signature Register */
+
+/* Port# Serial ATA Status {SStatus} Register */
+#define SATA_PORT_SSTS_IPM_MASK 0x00000f00
+#define SATA_PORT_SSTS_SPD_MASK 0x000000f0
+#define SATA_PORT_SSTS_DET_MASK 0x0000000f
+
+/* Port# Serial ATA Control {SControl} Register */
+#define SATA_PORT_SCTL_IPM_MASK 0x00000f00
+#define SATA_PORT_SCTL_SPD_MASK 0x000000f0
+#define SATA_PORT_SCTL_DET_MASK 0x0000000f
+
+/* Port# Serial ATA Error {SError} Register */
+#define SATA_PORT_SERR_DIAG_X 0x04000000
+#define SATA_PORT_SERR_DIAG_F 0x02000000
+#define SATA_PORT_SERR_DIAG_T 0x01000000
+#define SATA_PORT_SERR_DIAG_S 0x00800000
+#define SATA_PORT_SERR_DIAG_H 0x00400000
+#define SATA_PORT_SERR_DIAG_C 0x00200000
+#define SATA_PORT_SERR_DIAG_D 0x00100000
+#define SATA_PORT_SERR_DIAG_B 0x00080000
+#define SATA_PORT_SERR_DIAG_W 0x00040000
+#define SATA_PORT_SERR_DIAG_I 0x00020000
+#define SATA_PORT_SERR_DIAG_N 0x00010000
+#define SATA_PORT_SERR_ERR_E 0x00000800
+#define SATA_PORT_SERR_ERR_P 0x00000400
+#define SATA_PORT_SERR_ERR_C 0x00000200
+#define SATA_PORT_SERR_ERR_T 0x00000100
+#define SATA_PORT_SERR_ERR_M 0x00000002
+#define SATA_PORT_SERR_ERR_I 0x00000001
+
+/* Port# Serial ATA Active {SActive} Register */
+
+/* Port# Command Issue Register */
+
+/* Port# Serial ATA Notification Register */
+
+/* Port# DMA Control Register */
+#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000
+#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00
+#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0
+#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f
+
+/* Port# PHY Control Register */
+
+/* Port# PHY Status Register */
+
+#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
+
+/* DW0
+*/
+#define CMD_HDR_DI_CFL_MASK 0x0000001f
+#define CMD_HDR_DI_CFL_OFFSET 0
+#define CMD_HDR_DI_A 0x00000020
+#define CMD_HDR_DI_W 0x00000040
+#define CMD_HDR_DI_P 0x00000080
+#define CMD_HDR_DI_R 0x00000100
+#define CMD_HDR_DI_B 0x00000200
+#define CMD_HDR_DI_C 0x00000400
+#define CMD_HDR_DI_PMP_MASK 0x0000f000
+#define CMD_HDR_DI_PMP_OFFSET 12
+#define CMD_HDR_DI_PRDTL 0xffff0000
+#define CMD_HDR_DI_PRDTL_OFFSET 16
+
+/* prde_fis_len
+*/
+#define CMD_HDR_PRD_ENTRY_SHIFT 16
+#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
+#define CMD_HDR_FIS_LEN_SHIFT 2
+
+/* attribute
+*/
+#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
+#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
+/* Snoop enable for all descriptor */
+#define CMD_HDR_ATTR_SNOOP 0x00000200
+#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
+#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
+/* BIST - require the host to enter BIST mode */
+#define CMD_HDR_ATTR_BIST 0x00000040
+#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
+#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
+
+#define FLAGS_DMA 0x00000000
+#define FLAGS_FPDMA 0x00000001
+
+#define SATA_FLAG_Q_DEP_MASK 0x0000000f
+#define SATA_FLAG_WCACHE 0x00000100
+#define SATA_FLAG_FLUSH 0x00000200
+#define SATA_FLAG_FLUSH_EXT 0x00000400
+
+#define READ_CMD 0
+#define WRITE_CMD 1
+
+#endif /* __DWC_AHSATA_H__ */
diff --git a/bsp/ls2kdev/drivers/ata/fis.h b/bsp/ls2kdev/drivers/ata/fis.h
new file mode 100644
index 0000000000..c4c6557b8f
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/fis.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#ifndef __FIS_H__
+#define __FIS_H__
+/*
+* Register - Host to Device FIS
+*/
+typedef struct sata_fis_h2d
+{
+ u8 fis_type;
+ u8 pm_port_c;
+ u8 command;
+ u8 features;
+ u8 lba_low;
+ u8 lba_mid;
+ u8 lba_high;
+ u8 device;
+ u8 lba_low_exp;
+ u8 lba_mid_exp;
+ u8 lba_high_exp;
+ u8 features_exp;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 res1;
+ u8 control;
+ u8 res2[4];
+} __attribute__((packed)) sata_fis_h2d_t;
+
+/*
+* Register - Host to Device FIS for read/write FPDMA queued
+*/
+typedef struct sata_fis_h2d_ncq
+{
+ u8 fis_type;
+ u8 pm_port_c;
+ u8 command;
+ u8 sector_count_low;
+ u8 lba_low;
+ u8 lba_mid;
+ u8 lba_high;
+ u8 device;
+ u8 lba_low_exp;
+ u8 lba_mid_exp;
+ u8 lba_high_exp;
+ u8 sector_count_high;
+ u8 tag;
+ u8 res1;
+ u8 res2;
+ u8 control;
+ u8 res3[4];
+} __attribute__((packed)) sata_fis_h2d_ncq_t;
+
+/*
+* Register - Device to Host FIS
+*/
+typedef struct sata_fis_d2h
+{
+ u8 fis_type;
+ u8 pm_port_i;
+ u8 status;
+ u8 error;
+ u8 lba_low;
+ u8 lba_mid;
+ u8 lba_high;
+ u8 device;
+ u8 lba_low_exp;
+ u8 lba_mid_exp;
+ u8 lba_high_exp;
+ u8 res1;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 res2[2];
+ u8 res3[4];
+} __attribute__((packed)) sata_fis_d2h_t;
+
+/*
+* DMA Setup - Device to Host or Host to Device FIS
+*/
+typedef struct sata_fis_dma_setup
+{
+ u8 fis_type;
+ u8 pm_port_dir_int_act;
+ u8 res1;
+ u8 res2;
+ u32 dma_buffer_id_low;
+ u32 dma_buffer_id_high;
+ u32 res3;
+ u32 dma_buffer_offset;
+ u32 dma_transfer_count;
+ u32 res4;
+} __attribute__((packed)) sata_fis_dma_setup_t;
+
+/*
+* PIO Setup - Device to Host FIS
+*/
+typedef struct sata_fis_pio_setup
+{
+ u8 fis_type;
+ u8 pm_port_dir_int;
+ u8 status;
+ u8 error;
+ u8 lba_low;
+ u8 lba_mid;
+ u8 lba_high;
+ u8 res1;
+ u8 lba_low_exp;
+ u8 lba_mid_exp;
+ u8 lba_high_exp;
+ u8 res2;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 res3;
+ u8 e_status;
+ u16 transfer_count;
+ u16 res4;
+} __attribute__((packed)) sata_fis_pio_setup_t;
+
+/*
+* Data - Host to Device or Device to Host FIS
+*/
+typedef struct sata_fis_data
+{
+ u8 fis_type;
+ u8 pm_port;
+ u8 res1;
+ u8 res2;
+ u32 data[2048];
+} __attribute__((packed)) sata_fis_data_t;
+
+/* fis_type - SATA FIS type
+ */
+enum sata_fis_type
+{
+ SATA_FIS_TYPE_REGISTER_H2D = 0x27,
+ SATA_FIS_TYPE_REGISTER_D2H = 0x34,
+ SATA_FIS_TYPE_DMA_ACT_D2H = 0x39,
+ SATA_FIS_TYPE_DMA_SETUP_BI = 0x41,
+ SATA_FIS_TYPE_DATA_BI = 0x46,
+ SATA_FIS_TYPE_BIST_ACT_BI = 0x58,
+ SATA_FIS_TYPE_PIO_SETUP_D2H = 0x5F,
+ SATA_FIS_TYPE_SET_DEVICE_BITS_D2H = 0xA1,
+};
+
+#endif /* __FIS_H__ */
diff --git a/bsp/ls2kdev/drivers/ata/libata.c b/bsp/ls2kdev/drivers/ata/libata.c
new file mode 100644
index 0000000000..980fc071fc
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/libata.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#include <rtthread.h>
+#include <ata_interface.h>
+#include <libata.h>
+
+u64 ata_id_n_sectors(u16 *id)
+{
+ if (ata_id_has_lba(id)) {
+ if (ata_id_has_lba48(id))
+ return ata_id_u64(id, ATA_ID_LBA48_SECTORS);
+ else
+ return ata_id_u32(id, ATA_ID_LBA_SECTORS);
+ } else {
+ return 0;
+ }
+}
+
+u32 ata_dev_classify(u32 sig)
+{
+ u8 lbam, lbah;
+
+ lbam = (sig >> 16) & 0xff;
+ lbah = (sig >> 24) & 0xff;
+
+ if (((lbam == 0) && (lbah == 0)) ||
+ ((lbam == 0x3c) && (lbah == 0xc3)))
+ return ATA_DEV_ATA;
+
+ if ((lbam == 0x14) && (lbah == 0xeb))
+ return ATA_DEV_ATAPI;
+
+ if ((lbam == 0x69) && (lbah == 0x96))
+ return ATA_DEV_PMP;
+
+ return ATA_DEV_UNKNOWN;
+}
+
+static void ata_id_string(const u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned int c;
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+void ata_id_c_string(const u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned char *p;
+
+ ata_id_string(id, s, ofs, len - 1);
+
+ p = s + strnlen((char *)s, len - 1);
+ while (p > s && p[-1] == ' ')
+ p--;
+ *p = '\0';
+}
+
+void ata_dump_id(u16 *id)
+{
+ unsigned char serial[ATA_ID_SERNO_LEN + 1];
+ unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
+ unsigned char product[ATA_ID_PROD_LEN + 1];
+ u64 n_sectors;
+
+ /* Serial number */
+ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ printf("S/N: %s\n\r", serial);
+
+ /* Firmware version */
+ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
+ printf("Firmware version: %s\n\r", firmware);
+
+ /* Product model */
+ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
+ printf("Product model number: %s\n\r", product);
+
+ /* Total sectors of device */
+ n_sectors = ata_id_n_sectors(id);
+ printf("Capablity: %lld sectors\n\r", n_sectors);
+
+ printf ("id[49]: capabilities = 0x%04x\n"
+ "id[53]: field valid = 0x%04x\n"
+ "id[63]: mwdma = 0x%04x\n"
+ "id[64]: pio = 0x%04x\n"
+ "id[75]: queue depth = 0x%04x\n",
+ id[49],
+ id[53],
+ id[63],
+ id[64],
+ id[75]);
+
+ printf ("id[76]: sata capablity = 0x%04x\n"
+ "id[78]: sata features supported = 0x%04x\n"
+ "id[79]: sata features enable = 0x%04x\n",
+ id[76],
+ id[78],
+ id[79]);
+
+ printf ("id[80]: major version = 0x%04x\n"
+ "id[81]: minor version = 0x%04x\n"
+ "id[82]: command set supported 1 = 0x%04x\n"
+ "id[83]: command set supported 2 = 0x%04x\n"
+ "id[84]: command set extension = 0x%04x\n",
+ id[80],
+ id[81],
+ id[82],
+ id[83],
+ id[84]);
+ printf ("id[85]: command set enable 1 = 0x%04x\n"
+ "id[86]: command set enable 2 = 0x%04x\n"
+ "id[87]: command set default = 0x%04x\n"
+ "id[88]: udma = 0x%04x\n"
+ "id[93]: hardware reset result = 0x%04x\n",
+ id[85],
+ id[86],
+ id[87],
+ id[88],
+ id[93]);
+}
+
+void ata_swap_buf_le16(u16 *buf, unsigned int buf_words)
+{
+ unsigned int i;
+
+ for (i = 0; i < buf_words; i++)
+ {
+ buf[i] = le16_to_cpu(buf[i]);
+ }
+}
diff --git a/bsp/ls2kdev/drivers/ata/libata.h b/bsp/ls2kdev/drivers/ata/libata.h
new file mode 100644
index 0000000000..ae00c68446
--- /dev/null
+++ b/bsp/ls2kdev/drivers/ata/libata.h
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-08-19 lizhirui porting to ls2k
+ */
+
+#ifndef __LIBATA_H__
+#define __LIBATA_H__
+
+enum {
+ /* various global constants */
+ ATA_MAX_DEVICES = 2, /* per bus/port */
+ ATA_MAX_PRD = 256, /* we could make these 256/256 */
+ ATA_SECT_SIZE = 512,
+ ATA_MAX_SECTORS_128 = 128,
+ ATA_MAX_SECTORS = 256,
+ ATA_MAX_SECTORS_LBA48 = 65535,
+ ATA_MAX_SECTORS_TAPE = 65535,
+
+ ATA_ID_WORDS = 256,
+ ATA_ID_SERNO = 10,
+ ATA_ID_FW_REV = 23,
+ ATA_ID_PROD = 27,
+ ATA_ID_OLD_PIO_MODES = 51,
+ ATA_ID_FIELD_VALID = 53,
+ ATA_ID_LBA_SECTORS = 60,
+ ATA_ID_MWDMA_MODES = 63,
+ ATA_ID_PIO_MODES = 64,
+ ATA_ID_EIDE_DMA_MIN = 65,
+ ATA_ID_EIDE_PIO = 67,
+ ATA_ID_EIDE_PIO_IORDY = 68,
+ ATA_ID_PIO4 = (1 << 1),
+ ATA_ID_QUEUE_DEPTH = 75,
+ ATA_ID_SATA_CAP = 76,
+ ATA_ID_SATA_FEATURES = 78,
+ ATA_ID_SATA_FEATURES_EN = 79,
+ ATA_ID_MAJOR_VER = 80,
+ ATA_ID_MINOR_VER = 81,
+ ATA_ID_UDMA_MODES = 88,
+ ATA_ID_LBA48_SECTORS = 100,
+
+ ATA_ID_SERNO_LEN = 20,
+ ATA_ID_FW_REV_LEN = 8,
+ ATA_ID_PROD_LEN = 40,
+
+ ATA_PCI_CTL_OFS = 2,
+
+ ATA_PIO0 = (1 << 0),
+ ATA_PIO1 = ATA_PIO0 | (1 << 1),
+ ATA_PIO2 = ATA_PIO1 | (1 << 2),
+ ATA_PIO3 = ATA_PIO2 | (1 << 3),
+ ATA_PIO4 = ATA_PIO3 | (1 << 4),
+ ATA_PIO5 = ATA_PIO4 | (1 << 5),
+ ATA_PIO6 = ATA_PIO5 | (1 << 6),
+
+ ATA_SWDMA0 = (1 << 0),
+ ATA_SWDMA1 = ATA_SWDMA0 | (1 << 1),
+ ATA_SWDMA2 = ATA_SWDMA1 | (1 << 2),
+
+ ATA_SWDMA2_ONLY = (1 << 2),
+
+ ATA_MWDMA0 = (1 << 0),
+ ATA_MWDMA1 = ATA_MWDMA0 | (1 << 1),
+ ATA_MWDMA2 = ATA_MWDMA1 | (1 << 2),
+
+ ATA_MWDMA12_ONLY = (1 << 1) | (1 << 2),
+ ATA_MWDMA2_ONLY = (1 << 2),
+
+ ATA_UDMA0 = (1 << 0),
+ ATA_UDMA1 = ATA_UDMA0 | (1 << 1),
+ ATA_UDMA2 = ATA_UDMA1 | (1 << 2),
+ ATA_UDMA3 = ATA_UDMA2 | (1 << 3),
+ ATA_UDMA4 = ATA_UDMA3 | (1 << 4),
+ ATA_UDMA5 = ATA_UDMA4 | (1 << 5),
+ ATA_UDMA6 = ATA_UDMA5 | (1 << 6),
+ ATA_UDMA7 = ATA_UDMA6 | (1 << 7),
+ /* ATA_UDMA7 is just for completeness... doesn't exist (yet?). */
+
+ ATA_UDMA_MASK_40C = ATA_UDMA2, /* udma0-2 */
+
+ /* DMA-related */
+ ATA_PRD_SZ = 8,
+ ATA_PRD_TBL_SZ = (ATA_MAX_PRD * ATA_PRD_SZ),
+ ATA_PRD_EOT = (1 << 31), /* end-of-table flag */
+
+ ATA_DMA_TABLE_OFS = 4,
+ ATA_DMA_STATUS = 2,
+ ATA_DMA_CMD = 0,
+ ATA_DMA_WR = (1 << 3),
+ ATA_DMA_START = (1 << 0),
+ ATA_DMA_INTR = (1 << 2),
+ ATA_DMA_ERR = (1 << 1),
+ ATA_DMA_ACTIVE = (1 << 0),
+
+ /* bits in ATA command block registers */
+ ATA_HOB = (1 << 7), /* LBA48 selector */
+ ATA_NIEN = (1 << 1), /* disable-irq flag */
+ ATA_LBA = (1 << 6), /* LBA28 selector */
+ ATA_DEV1 = (1 << 4), /* Select Device 1 (slave) */
+ ATA_DEVICE_OBS = (1 << 7) | (1 << 5), /* obs bits in dev reg */
+ ATA_DEVCTL_OBS = (1 << 3), /* obsolete bit in devctl reg */
+ ATA_BUSY = (1 << 7), /* BSY status bit */
+ ATA_DRDY = (1 << 6), /* device ready */
+ ATA_DF = (1 << 5), /* device fault */
+ ATA_DRQ = (1 << 3), /* data request i/o */
+ ATA_ERR = (1 << 0), /* have an error */
+ ATA_SRST = (1 << 2), /* software reset */
+ ATA_ICRC = (1 << 7), /* interface CRC error */
+ ATA_UNC = (1 << 6), /* uncorrectable media error */
+ ATA_IDNF = (1 << 4), /* ID not found */
+ ATA_ABORTED = (1 << 2), /* command aborted */
+
+ /* ATA command block registers */
+ ATA_REG_DATA = 0x00,
+ ATA_REG_ERR = 0x01,
+ ATA_REG_NSECT = 0x02,
+ ATA_REG_LBAL = 0x03,
+ ATA_REG_LBAM = 0x04,
+ ATA_REG_LBAH = 0x05,
+ ATA_REG_DEVICE = 0x06,
+ ATA_REG_STATUS = 0x07,
+
+ ATA_REG_FEATURE = ATA_REG_ERR, /* and their aliases */
+ ATA_REG_CMD = ATA_REG_STATUS,
+ ATA_REG_BYTEL = ATA_REG_LBAM,
+ ATA_REG_BYTEH = ATA_REG_LBAH,
+ ATA_REG_DEVSEL = ATA_REG_DEVICE,
+ ATA_REG_IRQ = ATA_REG_NSECT,
+
+ /* ATA device commands */
+ ATA_CMD_DEV_RESET = 0x08, /* ATAPI device reset */
+ ATA_CMD_PIO_READ = 0x20, /* Read sectors with retry */
+ ATA_CMD_PIO_READ_EXT = 0x24,
+ ATA_CMD_READ_EXT = 0x25,
+ ATA_CMD_READ_NATIVE_MAX_EXT = 0x27,
+ ATA_CMD_READ_MULTI_EXT = 0x29,
+ ATA_CMD_READ_LOG_EXT = 0x2f,
+ ATA_CMD_PIO_WRITE = 0x30, /* write sectors with retry */
+ ATA_CMD_PIO_WRITE_EXT = 0x34,
+ ATA_CMD_WRITE_EXT = 0x35,
+ ATA_CMD_SET_MAX_EXT = 0x37,
+ ATA_CMD_WRITE_MULTI_EXT = 0x39,
+ ATA_CMD_WRITE_FUA_EXT = 0x3D,
+ ATA_CMD_VERIFY = 0x40, /* read verify sectors with retry */
+ ATA_CMD_VERIFY_EXT = 0x42,
+ ATA_CMD_FPDMA_READ = 0x60,
+ ATA_CMD_FPDMA_WRITE = 0x61,
+ ATA_CMD_EDD = 0x90, /* execute device diagnostic */
+ ATA_CMD_INIT_DEV_PARAMS = 0x91, /* initialize device parameters */
+ ATA_CMD_PACKET = 0xA0, /* ATAPI packet */
+ ATA_CMD_ID_ATAPI = 0xA1, /* ATAPI identify device */
+ ATA_CMD_CONF_OVERLAY = 0xB1,
+ ATA_CMD_READ_MULTI = 0xC4, /* read multiple */
+ ATA_CMD_WRITE_MULTI = 0xC5, /* write multiple */
+ ATA_CMD_SET_MULTI = 0xC6, /* set multiple mode */
+ ATA_CMD_READ = 0xC8, /* read DMA with retry */
+ ATA_CMD_WRITE = 0xCA, /* write DMA with retry */
+ ATA_CMD_WRITE_MULTI_FUA_EXT = 0xCE,
+ ATA_CMD_STANDBYNOW1 = 0xE0, /* standby immediate */
+ ATA_CMD_IDLEIMMEDIATE = 0xE1, /* idle immediate */
+ ATA_CMD_STANDBY = 0xE2, /* place in standby power mode */
+ ATA_CMD_IDLE = 0xE3, /* place in idle power mode */
+ ATA_CMD_PMP_READ = 0xE4, /* read buffer */
+ ATA_CMD_CHK_POWER = 0xE5, /* check power mode */
+ ATA_CMD_SLEEP = 0xE6, /* sleep */
+ ATA_CMD_FLUSH = 0xE7,
+ ATA_CMD_PMP_WRITE = 0xE8, /* write buffer */
+ ATA_CMD_FLUSH_EXT = 0xEA,
+ ATA_CMD_ID_ATA = 0xEC, /* identify device */
+ ATA_CMD_SET_FEATURES = 0xEF, /* set features */
+ ATA_CMD_SEC_FREEZE_LOCK = 0xF5, /* security freeze */
+ ATA_CMD_READ_NATIVE_MAX = 0xF8,
+ ATA_CMD_SET_MAX = 0xF9,
+
+ /* READ_LOG_EXT pages */
+ ATA_LOG_SATA_NCQ = 0x10,
+
+ /* READ/WRITE LONG (obsolete) */
+ ATA_CMD_READ_LONG = 0x22,
+ ATA_CMD_READ_LONG_ONCE = 0x23,
+ ATA_CMD_WRITE_LONG = 0x32,
+ ATA_CMD_WRITE_LONG_ONCE = 0x33,
+
+ /* SETFEATURES stuff */
+ SETFEATURES_XFER = 0x03,
+ XFER_UDMA_7 = 0x47,
+ XFER_UDMA_6 = 0x46,
+ XFER_UDMA_5 = 0x45,
+ XFER_UDMA_4 = 0x44,
+ XFER_UDMA_3 = 0x43,
+ XFER_UDMA_2 = 0x42,
+ XFER_UDMA_1 = 0x41,
+ XFER_UDMA_0 = 0x40,
+ XFER_MW_DMA_4 = 0x24, /* CFA only */
+ XFER_MW_DMA_3 = 0x23, /* CFA only */
+ XFER_MW_DMA_2 = 0x22,
+ XFER_MW_DMA_1 = 0x21,
+ XFER_MW_DMA_0 = 0x20,
+ XFER_SW_DMA_2 = 0x12,
+ XFER_SW_DMA_1 = 0x11,
+ XFER_SW_DMA_0 = 0x10,
+ XFER_PIO_6 = 0x0E, /* CFA only */
+ XFER_PIO_5 = 0x0D, /* CFA only */
+ XFER_PIO_4 = 0x0C,
+ XFER_PIO_3 = 0x0B,
+ XFER_PIO_2 = 0x0A,
+ XFER_PIO_1 = 0x09,
+ XFER_PIO_0 = 0x08,
+ XFER_PIO_SLOW = 0x00,
+
+ SETFEATURES_WC_ON = 0x02, /* Enable write cache */
+ SETFEATURES_WC_OFF = 0x82, /* Disable write cache */
+
+ SETFEATURES_SPINUP = 0x07, /* Spin-up drive */
+
+ SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */
+ SETFEATURES_SATA_DISABLE = 0x90, /* Disable use of SATA feature */
+
+ /* SETFEATURE Sector counts for SATA features */
+ SATA_AN = 0x05, /* Asynchronous Notification */
+ SATA_DIPM = 0x03, /* Device Initiated Power Management */
+
+ /* feature values for SET_MAX */
+ ATA_SET_MAX_ADDR = 0x00,
+ ATA_SET_MAX_PASSWD = 0x01,
+ ATA_SET_MAX_LOCK = 0x02,
+ ATA_SET_MAX_UNLOCK = 0x03,
+ ATA_SET_MAX_FREEZE_LOCK = 0x04,
+
+ /* feature values for DEVICE CONFIGURATION OVERLAY */
+ ATA_DCO_RESTORE = 0xC0,
+ ATA_DCO_FREEZE_LOCK = 0xC1,
+ ATA_DCO_IDENTIFY = 0xC2,
+ ATA_DCO_SET = 0xC3,
+
+ /* ATAPI stuff */
+ ATAPI_PKT_DMA = (1 << 0),
+ ATAPI_DMADIR = (1 << 2), /* ATAPI data dir:
+ 0=to device, 1=to host */
+ ATAPI_CDB_LEN = 16,
+
+ /* PMP stuff */
+ SATA_PMP_MAX_PORTS = 15,
+ SATA_PMP_CTRL_PORT = 15,
+
+ SATA_PMP_GSCR_DWORDS = 128,
+ SATA_PMP_GSCR_PROD_ID = 0,
+ SATA_PMP_GSCR_REV = 1,
+ SATA_PMP_GSCR_PORT_INFO = 2,
+ SATA_PMP_GSCR_ERROR = 32,
+ SATA_PMP_GSCR_ERROR_EN = 33,
+ SATA_PMP_GSCR_FEAT = 64,
+ SATA_PMP_GSCR_FEAT_EN = 96,
+
+ SATA_PMP_PSCR_STATUS = 0,
+ SATA_PMP_PSCR_ERROR = 1,
+ SATA_PMP_PSCR_CONTROL = 2,
+
+ SATA_PMP_FEAT_BIST = (1 << 0),
+ SATA_PMP_FEAT_PMREQ = (1 << 1),
+ SATA_PMP_FEAT_DYNSSC = (1 << 2),
+ SATA_PMP_FEAT_NOTIFY = (1 << 3),
+
+ /* cable types */
+ ATA_CBL_NONE = 0,
+ ATA_CBL_PATA40 = 1,
+ ATA_CBL_PATA80 = 2,
+ ATA_CBL_PATA40_SHORT = 3, /* 40 wire cable to high UDMA spec */
+ ATA_CBL_PATA_UNK = 4, /* don't know, maybe 80c? */
+ ATA_CBL_PATA_IGN = 5, /* don't know, ignore cable handling */
+ ATA_CBL_SATA = 6,
+
+ /* SATA Status and Control Registers */
+ SCR_STATUS = 0,
+ SCR_ERROR = 1,
+ SCR_CONTROL = 2,
+ SCR_ACTIVE = 3,
+ SCR_NOTIFICATION = 4,
+
+ /* SError bits */
+ SERR_DATA_RECOVERED = (1 << 0), /* recovered data error */
+ SERR_COMM_RECOVERED = (1 << 1), /* recovered comm failure */
+ SERR_DATA = (1 << 8), /* unrecovered data error */
+ SERR_PERSISTENT = (1 << 9), /* persistent data/comm error */
+ SERR_PROTOCOL = (1 << 10), /* protocol violation */
+ SERR_INTERNAL = (1 << 11), /* host internal error */
+ SERR_PHYRDY_CHG = (1 << 16), /* PHY RDY changed */
+ SERR_PHY_INT_ERR = (1 << 17), /* PHY internal error */
+ SERR_COMM_WAKE = (1 << 18), /* Comm wake */
+ SERR_10B_8B_ERR = (1 << 19), /* 10b to 8b decode error */
+ SERR_DISPARITY = (1 << 20), /* Disparity */
+ SERR_CRC = (1 << 21), /* CRC error */
+ SERR_HANDSHAKE = (1 << 22), /* Handshake error */
+ SERR_LINK_SEQ_ERR = (1 << 23), /* Link sequence error */
+ SERR_TRANS_ST_ERROR = (1 << 24), /* Transport state trans. error */
+ SERR_UNRECOG_FIS = (1 << 25), /* Unrecognized FIS */
+ SERR_DEV_XCHG = (1 << 26), /* device exchanged */
+
+ /* struct ata_taskfile flags */
+ ATA_TFLAG_LBA48 = (1 << 0), /* enable 48-bit LBA and "HOB" */
+ ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */
+ ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */
+ ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */
+ ATA_TFLAG_LBA = (1 << 4), /* enable LBA */
+ ATA_TFLAG_FUA = (1 << 5), /* enable FUA */
+ ATA_TFLAG_POLLING = (1 << 6), /* set nIEN to 1 and use polling */
+
+ /* protocol flags */
+ ATA_PROT_FLAG_PIO = (1 << 0), /* is PIO */
+ ATA_PROT_FLAG_DMA = (1 << 1), /* is DMA */
+ ATA_PROT_FLAG_DATA = ATA_PROT_FLAG_PIO | ATA_PROT_FLAG_DMA,
+ ATA_PROT_FLAG_NCQ = (1 << 2), /* is NCQ */
+ ATA_PROT_FLAG_ATAPI = (1 << 3), /* is ATAPI */
+};
+
+enum ata_tf_protocols {
+ /* ATA taskfile protocols */
+ ATA_PROT_UNKNOWN, /* unknown/invalid */
+ ATA_PROT_NODATA, /* no data */
+ ATA_PROT_PIO, /* PIO data xfer */
+ ATA_PROT_DMA, /* DMA */
+ ATA_PROT_NCQ, /* NCQ */
+ ATAPI_PROT_NODATA, /* packet command, no data */
+ ATAPI_PROT_PIO, /* packet command, PIO data xfer*/
+ ATAPI_PROT_DMA, /* packet command with special DMA sauce */
+};
+
+enum ata_ioctls {
+ ATA_IOC_GET_IO32 = 0x309,
+ ATA_IOC_SET_IO32 = 0x324,
+};
+
+enum ata_dev_typed {
+ ATA_DEV_ATA, /* ATA device */
+ ATA_DEV_ATAPI, /* ATAPI device */
+ ATA_DEV_PMP, /* Port Multiplier Port */
+ ATA_DEV_UNKNOWN, /* unknown */
+};
+
+struct ata_taskfile {
+ unsigned long flags; /* ATA_TFLAG_xxx */
+ u8 protocol; /* ATA_PROT_xxx */
+
+ u8 ctl; /* control reg */
+
+ u8 hob_feature; /* additional data */
+ u8 hob_nsect; /* to support LBA48 */
+ u8 hob_lbal;
+ u8 hob_lbam;
+ u8 hob_lbah;
+
+ u8 feature;
+ u8 nsect;
+ u8 lbal;
+ u8 lbam;
+ u8 lbah;
+
+ u8 device;
+
+ u8 command; /* IO operation */
+};
+
+/*
+ * protocol tests
+ */
+static inline unsigned int ata_prot_flags(u8 prot)
+{
+ switch (prot) {
+ case ATA_PROT_NODATA:
+ return 0;
+ case ATA_PROT_PIO:
+ return ATA_PROT_FLAG_PIO;
+ case ATA_PROT_DMA:
+ return ATA_PROT_FLAG_DMA;
+ case ATA_PROT_NCQ:
+ return ATA_PROT_FLAG_DMA | ATA_PROT_FLAG_NCQ;
+ case ATAPI_PROT_NODATA:
+ return ATA_PROT_FLAG_ATAPI;
+ case ATAPI_PROT_PIO:
+ return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_PIO;
+ case ATAPI_PROT_DMA:
+ return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_DMA;
+ }
+ return 0;
+}
+
+static inline int ata_is_atapi(u8 prot)
+{
+ return ata_prot_flags(prot) & ATA_PROT_FLAG_ATAPI;
+}
+
+static inline int ata_is_nodata(u8 prot)
+{
+ return !(ata_prot_flags(prot) & ATA_PROT_FLAG_DATA);
+}
+
+static inline int ata_is_pio(u8 prot)
+{
+ return ata_prot_flags(prot) & ATA_PROT_FLAG_PIO;
+}
+
+static inline int ata_is_dma(u8 prot)
+{
+ return ata_prot_flags(prot) & ATA_PROT_FLAG_DMA;
+}
+
+static inline int ata_is_ncq(u8 prot)
+{
+ return ata_prot_flags(prot) & ATA_PROT_FLAG_NCQ;
+}
+
+static inline int ata_is_data(u8 prot)
+{
+ return ata_prot_flags(prot) & ATA_PROT_FLAG_DATA;
+}
+
+/*
+ * id tests
+ */
+#define ata_id_is_ata(id) (((id)[0] & (1 << 15)) == 0)
+#define ata_id_has_lba(id) ((id)[49] & (1 << 9))
+#define ata_id_has_dma(id) ((id)[49] & (1 << 8))
+#define ata_id_has_ncq(id) ((id)[76] & (1 << 8))
+#define ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1)
+#define ata_id_removeable(id) ((id)[0] & (1 << 7))
+#define ata_id_iordy_disable(id) ((id)[49] & (1 << 10))
+#define ata_id_has_iordy(id) ((id)[49] & (1 << 11))
+
+#define ata_id_u32(id,n) \
+ (((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)]))
+#define ata_id_u64(id,n) \
+ ( ((u64) (id)[(n) + 3] << 48) | \
+ ((u64) (id)[(n) + 2] << 32) | \
+ ((u64) (id)[(n) + 1] << 16) | \
+ ((u64) (id)[(n) + 0]) )
+
+#define ata_id_cdb_intr(id) (((id)[0] & 0x60) == 0x20)
+
+static inline int ata_id_has_fua(const u16 *id)
+{
+ if ((id[84] & 0xC000) != 0x4000)
+ return 0;
+ return id[84] & (1 << 6);
+}
+
+static inline int ata_id_has_flush(const u16 *id)
+{
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ return id[83] & (1 << 12);
+}
+
+static inline int ata_id_has_flush_ext(const u16 *id)
+{
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ return id[83] & (1 << 13);
+}
+
+static inline int ata_id_has_lba48(const u16 *id)
+{
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ if (!ata_id_u64(id, 100))
+ return 0;
+ return id[83] & (1 << 10);
+}
+
+static inline int ata_id_hpa_enabled(const u16 *id)
+{
+ /* Yes children, word 83 valid bits cover word 82 data */
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ /* And 87 covers 85-87 */
+ if ((id[87] & 0xC000) != 0x4000)
+ return 0;
+ /* Check command sets enabled as well as supported */
+ if ((id[85] & ( 1 << 10)) == 0)
+ return 0;
+ return id[82] & (1 << 10);
+}
+
+static inline int ata_id_has_wcache(const u16 *id)
+{
+ /* Yes children, word 83 valid bits cover word 82 data */
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ return id[82] & (1 << 5);
+}
+
+static inline int ata_id_has_pm(const u16 *id)
+{
+ if ((id[83] & 0xC000) != 0x4000)
+ return 0;
+ return id[82] & (1 << 3);
+}
+
+static inline int ata_id_rahead_enabled(const u16 *id)
+{
+ if ((id[87] & 0xC000) != 0x4000)
+ return 0;
+ return id[85] & (1 << 6);
+}
+
+static inline int ata_id_wcache_enabled(const u16 *id)
+{
+ if ((id[87] & 0xC000) != 0x4000)
+ return 0;
+ return id[85] & (1 << 5);
+}
+
+static inline unsigned int ata_id_major_version(const u16 *id)
+{
+ unsigned int mver;
+
+ if (id[ATA_ID_MAJOR_VER] == 0xFFFF)
+ return 0;
+
+ for (mver = 14; mver >= 1; mver--)
+ if (id[ATA_ID_MAJOR_VER] & (1 << mver))
+ break;
+ return mver;
+}
+
+static inline int ata_id_is_sata(const u16 *id)
+{
+ return ata_id_major_version(id) >= 5 && id[93] == 0;
+}
+
+static inline int ata_id_has_tpm(const u16 *id)
+{
+ /* The TPM bits are only valid on ATA8 */
+ if (ata_id_major_version(id) < 8)
+ return 0;
+ if ((id[48] & 0xC000) != 0x4000)
+ return 0;
+ return id[48] & (1 << 0);
+}
+
+static inline int ata_id_has_dword_io(const u16 *id)
+{
+ /* ATA 8 reuses this flag for "trusted" computing */
+ if (ata_id_major_version(id) > 7)
+ return 0;
+ if (id[48] & (1 << 0))
+ return 1;
+ return 0;
+}
+
+static inline int ata_id_current_chs_valid(const u16 *id)
+{
+ /* For ATA-1 devices, if the INITIALIZE DEVICE PARAMETERS command
+ has not been issued to the device then the values of
+ id[54] to id[56] are vendor specific. */
+ return (id[53] & 0x01) && /* Current translation valid */
+ id[54] && /* cylinders in current translation */
+ id[55] && /* heads in current translation */
+ id[55] <= 16 &&
+ id[56]; /* sectors in current translation */
+}
+
+static inline int ata_id_is_cfa(const u16 *id)
+{
+ u16 v = id[0];
+ if (v == 0x848A) /* Standard CF */
+ return 1;
+ /* Could be CF hiding as standard ATA */
+ if (ata_id_major_version(id) >= 3 && id[82] != 0xFFFF &&
+ (id[82] & ( 1 << 2)))
+ return 1;
+ return 0;
+}
+
+static inline int ata_drive_40wire(const u16 *dev_id)
+{
+ if (ata_id_is_sata(dev_id))
+ return 0; /* SATA */
+ if ((dev_id[93] & 0xE000) == 0x6000)
+ return 0; /* 80 wire */
+ return 1;
+}
+
+static inline int ata_drive_40wire_relaxed(const u16 *dev_id)
+{
+ if ((dev_id[93] & 0x2000) == 0x2000)
+ return 0; /* 80 wire */
+ return 1;
+}
+
+static inline int atapi_cdb_len(const u16 *dev_id)
+{
+ u16 tmp = dev_id[0] & 0x3;
+ switch (tmp) {
+ case 0: return 12;
+ case 1: return 16;
+ default: return -1;
+ }
+}
+
+static inline int atapi_command_packet_set(const u16 *dev_id)
+{
+ return (dev_id[0] >> 8) & 0x1f;
+}
+
+static inline int atapi_id_dmadir(const u16 *dev_id)
+{
+ return ata_id_major_version(dev_id) >= 7 && (dev_id[62] & 0x8000);
+}
+
+static inline int is_multi_taskfile(struct ata_taskfile *tf)
+{
+ return (tf->command == ATA_CMD_READ_MULTI) ||
+ (tf->command == ATA_CMD_WRITE_MULTI) ||
+ (tf->command == ATA_CMD_READ_MULTI_EXT) ||
+ (tf->command == ATA_CMD_WRITE_MULTI_EXT) ||
+ (tf->command == ATA_CMD_WRITE_MULTI_FUA_EXT);
+}
+
+static inline int ata_ok(u8 status)
+{
+ return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR))
+ == ATA_DRDY);
+}
+
+static inline int lba_28_ok(u64 block, u32 n_block)
+{
+ /* check the ending block number */
+ return ((block + n_block - 1) < ((u64)1 << 28)) && (n_block <= 256);
+}
+
+static inline int lba_48_ok(u64 block, u32 n_block)
+{
+ /* check the ending block number */
+ return ((block + n_block - 1) < ((u64)1 << 48)) && (n_block <= 65536);
+}
+
+#define sata_pmp_gscr_vendor(gscr) ((gscr)[SATA_PMP_GSCR_PROD_ID] & 0xffff)
+#define sata_pmp_gscr_devid(gscr) ((gscr)[SATA_PMP_GSCR_PROD_ID] >> 16)
+#define sata_pmp_gscr_rev(gscr) (((gscr)[SATA_PMP_GSCR_REV] >> 8) & 0xff)
+#define sata_pmp_gscr_ports(gscr) ((gscr)[SATA_PMP_GSCR_PORT_INFO] & 0xf)
+
+u64 ata_id_n_sectors(u16 *id);
+u32 ata_dev_classify(u32 sig);
+void ata_id_c_string(const u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len);
+void ata_dump_id(u16 *id);
+void ata_swap_buf_le16(u16 *buf, unsigned int buf_words);
+
+#endif /* __LIBATA_H__ */
diff --git a/bsp/ls2kdev/drivers/pci/SConscript b/bsp/ls2kdev/drivers/pci/SConscript
new file mode 100644
index 0000000000..6c6d2c7adb
--- /dev/null
+++ b/bsp/ls2kdev/drivers/pci/SConscript
@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/bsp/ls2kdev/drivers/pci/pci.c b/bsp/ls2kdev/drivers/pci/pci.c
new file mode 100644
index 0000000000..7f62e88dc7
--- /dev/null
+++ b/bsp/ls2kdev/drivers/pci/pci.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-12-28 lizhirui first version
+ */
+
+#include <rtthread.h>
+#include <pci.h>
+
+rt_uint64_t pci_get_device_map_addr(rt_uint64_t bus,rt_uint64_t device,rt_uint64_t function,rt_uint32_t index)
+{
+
+ rt_uint64_t device_addr = 0xFE00000000 | (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x07) << 8);
+ struct pci_header *p = (struct pci_header *)(0x9000000000000000UL | device_addr);
+ return 0x9000000000000000UL | ((rt_uint64_t)(p -> BaseAddressRegister[index] & 0xFFFFFFF0));
+}
diff --git a/bsp/ls2kdev/drivers/pci/pci.h b/bsp/ls2kdev/drivers/pci/pci.h
new file mode 100644
index 0000000000..db4b3a0185
--- /dev/null
+++ b/bsp/ls2kdev/drivers/pci/pci.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-12-28 lizhirui first version
+ */
+
+#ifndef __PCI_H__
+#define __PCI_H__
+
+ struct pci_header
+ {
+ rt_uint16_t VendorID;
+ rt_uint16_t DeviceID;
+ rt_uint16_t Command;
+ rt_uint16_t Status;
+ rt_uint32_t RevisionID : 8;
+ rt_uint32_t ClassCode : 24;
+ rt_uint8_t CachelineSize;
+ rt_uint8_t LatencyTimer;
+ rt_uint8_t HeaderType;
+ rt_uint8_t BIST;
+ rt_uint32_t BaseAddressRegister[6];
+ rt_uint32_t CardbusCISPointer;
+ rt_uint16_t SubsystemVendorID;
+ rt_uint16_t SubsystemID;
+ rt_uint32_t ExpansionROMBaseAddress;
+ rt_uint32_t CapabilitiesPointer : 8;
+ rt_uint32_t resv1 : 24;
+ rt_uint32_t resv2;
+ rt_uint8_t InterruptLine;
+ rt_uint8_t InterruptPin;
+ rt_uint8_t Min_Gnt;
+ rt_uint8_t Max_Lat;
+ };
+
+ rt_uint64_t pci_get_device_map_addr(rt_uint64_t bus,rt_uint64_t device,rt_uint64_t function,rt_uint32_t index);
+
+#endif