u-boot scsi sata源码解析

作者

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 DEBUG
	else {
		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);
	}
#endif
	bootstage_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_SATAPWR
	gpio_request(CONFIG_SATAPWR, "satapwr");
	gpio_direction_output(CONFIG_SATAPWR, 1);
	/* Give attached sata device time to power-up to avoid link timeouts */
	mdelay(500);
#endif

	if (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_XFER
			ahci_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
#endif

int __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: OK
  design 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.0
            Type: Hard Disk
            Capacity: 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> 

你可能感兴趣的:(linux内核与驱动开发,u-boot,sata)