作者
QQ群:852283276
微信:arm80x86
微信公众号:青儿创客基地
B站:主页 https://space.bilibili.com/208826118
方法
u-boot下访问sata或者sata ahci有两种方式,一种是pcie上的pcie转sata芯片,比如marvell的88se9230,需要先开启对pcie的支持,一种是cpu内部总线上的,linux上称为platform设备,以xilinx zynqmp为例,u-boot下开启的宏为:
/*u-boot-xlnx-v2018.2\include\configs\xilinx_zynqmp.h*/
#define CONFIG_SYS_SCSI_MAX_SCSI_ID 2
#define CONFIG_SYS_SCSI_MAX_LUN 1
#define CONFIG_SYS_SCSI_MAX_DEVICE (CONFIG_SYS_SCSI_MAX_SCSI_ID * \CONFIG_SYS_SCSI_MAX_LUN)
/*menuconfig*/
#define CONFIG_SCSI 1
#define CONFIG_AHCI 1/*scsi 对应scsi系列命令 2015.2.1*/
#ifdef CONFIG_AHCI
#define CONFIG_LIBATA
#define CONFIG_SCSI_AHCI
#define CONFIG_SCSI_AHCI_PLAT
#define CONFIG_SUNXI_AHCI
#define CONFIG_SYS_SCSI_MAX_SCSI_ID 1
#define CONFIG_SYS_SCSI_MAX_LUN 1
#define CONFIG_SYS_SCSI_MAX_DEVICE (CONFIG_SYS_SCSI_MAX_SCSI_ID * \CONFIG_SYS_SCSI_MAX_LUN)
#define CONFIG_CMD_SCSI
#endif/*sata 对应sata系列命令 2015.2.1*/
#define CONFIG_CMD_SATA
#ifdef CONFIG_CMD_SATA
#define CONFIG_DWC_AHSATA
#define CONFIG_SYS_SATA_MAX_DEVICE 1
#define CONFIG_DWC_AHSATA_PORT_ID 0
#define CONFIG_DWC_AHSATA_BASE_ADDR SATA_ARB_BASE_ADDR
#define CONFIG_LBA48
#define CONFIG_LIBATA
#endif
如果我们的sata是通过pcie转sata实现,首先我们需要打开pcie相关的宏,u-boot-xlnx-v2015.2.1\common\board_r.c
中会先初始化pcie,initr_pci
,
/*pcie*/
#define CONFIG_PCI
#define CONFIG_DM_PCI
#define CONFIG_PCI_XILINX
#define CONFIG_CMD_PCI#ifdef CONFIG_PCI
static int initr_pci(void)
{pci_init();return 0;
}
#endif
然后调用后调用scsi_init
/*cmd_scsi.c*/
#ifdef CONFIG_PCI
void scsi_init(void)
{int busdevfunc;int i;/** Find a device from the list, this driver will support a single* controller.*/for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) {/* get PCI Device ID */busdevfunc = pci_find_device(scsi_device_list[i].vendor,scsi_device_list[i].device,0);if (busdevfunc != -1)break;}if (busdevfunc == -1) {printf("Error: SCSI Controller(s) ");for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) {printf("%04X:%04X ",scsi_device_list[i].vendor,scsi_device_list[i].device);}printf("not found\n");return;}
#ifdef DEBUGelse {printf("SCSI Controller (%04X,%04X) found (%d:%d:%d)\n",scsi_device_list[i].vendor,scsi_device_list[i].device,(busdevfunc >> 16) & 0xFF,(busdevfunc >> 11) & 0x1F,(busdevfunc >> 8) & 0x7);}
#endifbootstage_start(BOOTSTAGE_ID_ACCUM_SCSI, "ahci");scsi_low_level_init(busdevfunc);scsi_scan(1);bootstage_accum(BOOTSTAGE_ID_ACCUM_SCSI);
}
#endif
这里需要定义你的scsi_device_list
,对应宏CONFIG_SCSI_DEV_LIST
/******************************* console command******************************/
#define CONFIG_CMD_EXT4
#define CONFIG_CMD_EXT4_WRITE
#define CONFIG_CMD_SCSI
#define CONFIG_CMD_SATA
#define CONFIG_CMD_USB/* ****************************** SATA AHCI driver configure******************************/
#if defined(CONFIG_CMD_SCSI)
#define CONFIG_SCSI_AHCI
#define CONFIG_AHCI_SETFEATURES_XFER
#define CONFIG_LIBATA
#ifdef CONFIG_SCSI_AHCI
#define CONFIG_SATA_MAX_DEVICE_AHCI 32
#define CONFIG_SYS_SCSI_MAX_SCSI_ID 4
#define CONFIG_SYS_SCSI_MAX_LUN 1
#define CONFIG_SYS_SCSI_MAX_DEVICE (CONFIG_SYS_SCSI_MAX_SCSI_ID * \CONFIG_SYS_SCSI_MAX_LUN * CONFIG_SATA_MAX_DEVICE_AHCI)
#endif
#define CONFIG_ATAPI
#endif
#define CONFIG_SCSI_DEV_LIST {0x1b21, 0x0612}, \{0x1cc4, 0x1401}, \{0x1b4b, 0x9215}, \{0x1b4b, 0x9235}, \{0x1b4b, 0x9125}, \{0x1b4b, 0x9230}, \{0x21b4, 0x0835}
scsi的底层支持函数在u-boot-xlnx-v2015.2.1\drivers\block\ahci.c
中实现,
void scsi_low_level_init(int busdevfunc)
-->static int ahci_init_one(int pdev)-->static int ahci_host_init(struct ahci_probe_ent *probe_ent)-->static void ahci_print_info(struct ahci_probe_ent *probe_ent)
void scsi_scan(int mode)/*cmd_scsi.c*/
-->int scsi_exec(ccb *pccb)int scsi_exec(ccb *pccb)
{int ret;switch (pccb->cmd[0]) {case SCSI_READ10:ret = ata_scsiop_read_write(pccb, 0);break;case SCSI_WRITE10:ret = ata_scsiop_read_write(pccb, 1);break;case SCSI_RD_CAPAC10:ret = ata_scsiop_read_capacity10(pccb);break;case SCSI_RD_CAPAC16:ret = ata_scsiop_read_capacity16(pccb);break;case SCSI_TST_U_RDY:ret = ata_scsiop_test_unit_ready(pccb);break;case SCSI_INQUIRY:ret = ata_scsiop_inquiry(pccb);break;default:printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);return false;}if (ret) {debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);return false;}return true;}
scsi系列命令,如果定义了CONFIG_SCSI_AHCI_PLAT
则需要开发者自己实现scsi_init
,或者你就是想用自己的sata ip,那你就实现自己的scsi_init函数
,参考sunxi_ahci_phy_init
,如果你需要初始化Xilinx FPGA的GTX,那就在这个地方,
/*board\xilinx\zynqmp\zynqmp.c*/
#ifdef CONFIG_SCSI_AHCI_PLAT
void scsi_init(void)
{ahci_init(ZYNQMP_SATA_BASEADDR);scsi_scan(1);
}
#endif
/*u-boot-xlnx-v2015.2.1\board\sunxi\ahci.c*/
void scsi_init(void)
{printf("SUNXI SCSI INIT\n");
#ifdef CONFIG_SATAPWRgpio_request(CONFIG_SATAPWR, "satapwr");gpio_direction_output(CONFIG_SATAPWR, 1);/* Give attached sata device time to power-up to avoid link timeouts */mdelay(500);
#endifif (sunxi_ahci_phy_init(SUNXI_SATA_BASE) < 0)return;ahci_init(SUNXI_SATA_BASE);
}
函数ahci_init
位于u-boot-xlnx-v2015.2.1\drivers\block\ahci.c
,注意其中的CONFIG_SYS_SCSI_MAX_SCSI_ID
,表示支持多少个port,每个port对应一个盘,CONFIG_SYS_SCSI_MAX_LUN
表示每个盘有多少分区,
#ifdef CONFIG_SCSI_AHCI_PLAT
int ahci_init(u32 base)
{int i, rc = 0;u32 linkmap;probe_ent = malloc(sizeof(struct ahci_probe_ent));if (!probe_ent) {printf("%s: No memory for probe_ent\n", __func__);return -ENOMEM;}memset(probe_ent, 0, sizeof(struct ahci_probe_ent));probe_ent->host_flags = ATA_FLAG_SATA| ATA_FLAG_NO_LEGACY| ATA_FLAG_MMIO| ATA_FLAG_PIO_DMA| ATA_FLAG_NO_ATAPI;probe_ent->pio_mask = 0x1f;probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */probe_ent->mmio_base = base;/* initialize adapter */rc = ahci_host_init(probe_ent);if (rc)goto err_out;ahci_print_info(probe_ent);linkmap = probe_ent->link_port_map;for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {if (((linkmap >> i) & 0x01)) {if (ahci_port_start((u8) i)) {printf("Can not start port %d\n", i);continue;}
#ifdef CONFIG_AHCI_SETFEATURES_XFERahci_set_feature((u8) i);
#endif}}
err_out:return rc;
}
如果走sata系列命令,比如i.mx上使用的dwc的sata ahci ip,驱动实现在了u-boot-xlnx-v2015.2.1\drivers\block\dwc_ahsata.c
驱动初始化入口init_sata
在sata命令中调用,
#define CONFIG_CMD_SATA
#ifdef CONFIG_CMD_SATA#define CONFIG_DWC_AHSATA#define CONFIG_SYS_SATA_MAX_DEVICE 1#define CONFIG_DWC_AHSATA_PORT_ID 0#define CONFIG_DWC_AHSATA_BASE_ADDR SATA_BASE_ADDR#define CONFIG_LBA48#define CONFIG_LIBATA
#endifint __sata_initialize(void)
{int rc;int i;for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++) {memset(&sata_dev_desc[i], 0, sizeof(struct block_dev_desc));sata_dev_desc[i].if_type = IF_TYPE_SATA;sata_dev_desc[i].dev = i;sata_dev_desc[i].part_type = PART_TYPE_UNKNOWN;sata_dev_desc[i].type = DEV_TYPE_HARDDISK;sata_dev_desc[i].lba = 0;sata_dev_desc[i].blksz = 512;sata_dev_desc[i].log2blksz = LOG2(sata_dev_desc[i].blksz);sata_dev_desc[i].block_read = sata_read;sata_dev_desc[i].block_write = sata_write;rc = init_sata(i);if (!rc) {rc = scan_sata(i);if (!rc && (sata_dev_desc[i].lba > 0) &&(sata_dev_desc[i].blksz > 0))init_part(&sata_dev_desc[i]);}}sata_curr_device = 0;return rc;
}
int sata_initialize(void) __attribute__((weak,alias("__sata_initialize")));
验证
这里没有实现scsi_init
函数,因为FPGA配置文件没有加载,初始化就死机了,所以做了一个命令,
zynq-uboot> run fpga_boot && fdk_ahci && scsi scan
SF: Detected N25Q128 with page size 512 Bytes, erase size 128 KiB, total 32 MiB
SF: 15728640 bytes @ 0x100000 Read: OKdesign filename = "MWM195_V2_U40_V1;UserID=0XFFFFFFFF;Version=2017.4"part number = "7z030ffg676"date = "2019/04/08"time = "18:52:22"bytes in bitstream = 5979916
zynq_align_dma_buffer: Align buffer at 10000070 to 10000000(swap 1)
FDK SCSI INIT...after fpga load
Target spinup took 0 ms.
AHCI 0001.0300 32 slots 1 ports 6 Gbps 0x1 impl SATA mode
flags: ncq stag pm led clo only pmp pio slum part apst
scanning bus for devices...Device 0: (0:0) Vendor: ATA Prod.: MWM198-1TB Rev: V1.0Type: Hard DiskCapacity: 915715.3 MB = 894.2 GB (1875385008 x 512)
Found 1 device(s).
zynq-uboot> ext4ls scsi 0 /
<DIR> 4096 .
<DIR> 4096 ..
<DIR> 4096 NET
<DIR> 4096 CAN
<DIR> 4096 RS422
<DIR> 4096 RS485
zynq-uboot> ext4ls scsi 0 /NET
<DIR> 4096 .
<DIR> 4096 ..
<DIR> 4096 20191015
<DIR> 4096 20191016
<DIR> 4096 20191017
zynq-uboot>