SCSI子系统基础学习笔记 (之UFS子系统) - 2.2 UFS子系统初始化之ufshcd_async_scan

1. 前言

本专题我们开始学习SCSI子系统的相关内容。本专题主要参考了《存储技术原理分析》、ULA、ULK的相关内容。本专题主要以硬件UFS为例,记录SCSI子系统的框架流程。
在ufs_qcom_probe->ufshcd_pltfrm_init->ufshcd_init的最后执行了async_schedule(ufshcd_async_scan, hba),此处的async_schedule之所以称为异步调度,是因为通过创建了一个worker,并将worker加入到了system_unbound_wq,由系统在某个合适时机调度执行,执行函数为ufshcd_async_scan,本文是UFS子系统初始化第二部分,主要介绍ufshcd_pltfrm_probe的ufshcd_async_scan执行过程。

kernel版本:5.10
平台:arm64

注:
为方便阅读,正文标题采用分级结构标识,每一级用一个"-“表示,如:两级为”|- -", 三级为”|- - -“

2. ufshcd_async_scan

static void ufshcd_async_scan(void *data, async_cookie_t cookie)
	|--struct ufs_hba *hba = (struct ufs_hba *)data;
	|--ufshcd_probe_hba(hba, true);
	|--ufshcd_add_lus(hba);

ufshcd_probe_hba(hba):主要通过探测ufs device是否链接,如果探测到ufs device则检测是否ufs device已经ready,获取host和device都能支持的传输速率,最后扫描well known lun和normal lun.

|- -ufshcd_probe_hba

static int ufshcd_probe_hba(struct ufs_hba *hba, bool async)
	|--ufshcd_link_startup(hba)
	|  //Debug counters initialization
	|--ufshcd_clear_dbg_ufs_stats(hba)
	|  //UniPro link is active now
	|--ufshcd_set_link_active(hba)
	|  //Verify device initialization by sending NOP OUT UPIU
	|--ufshcd_verify_dev_init(hba)
	|  //Initiate UFS initialization, and waiting until completion
	|--ufshcd_complete_dev_init(hba);
	|--ufshcd_tune_unipro_params(hba);
	|--ufshcd_set_ufs_dev_active(hba);
	|--ufshcd_force_reset_auto_bkops(hba)
	|  //Gear up to HS gear if supported
	|--if (hba->max_pwr_info.is_valid)
	|		if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL)
	|			ufshcd_set_dev_ref_clk(hba);
	|		ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
	|--ufshcd_set_active_icc_lvl(hba);
	|--ufshcd_wb_config(hba);
	|  //Enable Auto-Hibernate if configured
	|--ufshcd_auto_hibern8_enable(hba);	

主要通过探测ufs device是否链接,如果探测到ufs device则检测是否ufs device已经ready,获取host和device都能支持的传输速率,最后扫描well known lun和normal lun.

  1. ufshcd_link_startup(hba)
    Initialize unipro link startup
    (1)ufshcd_vops_link_startup_notify(hba, PRE_CHANGE)
    主要调用hba->var->vops->link_startup_notify回调,对于高通即为ufs_qcom_link_startup_pre_change,主要是在ufs device的unipro layer执行startup之前,执行host controller端的unipro layer初始化,即在ufs device的unipro layer执行startup操作之前,qcom需要执行一些预备操作,如确认qcom unipro layer RX已经使能等
    (2)ufshcd_dme_link_startup(hba)
    Notify Unipro to perform link startup,通过向Unipro layer发送UIC_CMD_DME_LINK_STARTUP命令来初始化Unipro layer启动流程,这样host controller才能检测到。
    (3)ufshcd_init_pwr_info(hba);
    Mark that link is up in PWM-G1, 1-lane, SLOW-AUTO mode
    (4)ufshcd_make_hba_operational(hba)

To bring UFS host controller to operational state,

  1. Enable required interrupts
  2. Configure interrupt aggregation
  3. Program UTRL and UTMRL base address
  4. Configure run-stop-registers
  1. ufshcd_clear_dbg_ufs_stats(hba)
    Debug counters initialization

  2. ufshcd_set_link_active(hba)
    设置link为激活状态UIC_LINK_ACTIVE_STATE,UniPro link is active now

  3. ufshcd_verify_dev_init(hba)

Send NOP OUT UPIU and wait for NOP IN response to check whether the
device Transport Protocol (UTP) layer is ready after a reset. If the
UTP layer at the device side is not initialized, it may not respond
with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT and we retry
sending NOP OUT for %NOP_OUT_RETRIES iterations.

通过发送NOP OUT UPIU,并等待NOT IN UPIU回应信息来判断UTP是否已经ready,如果ufs设备没有ready则需要重新发送NOP OUT UPIU,直到超出指定次数

  1. ufshcd_complete_dev_init(hba)
    检查ufs设备是否ready,主要通过发送UPIU_QUERY_OPCODE_SET_FLAG(UPIU:QUERY REQUEST UPIU.Set Flag), ufs device收到此upiu会将FLAG IDN清零,通过发送UPIU_QUERY_OPCODE_READ_FLAG(UPIU:QUERY REQUEST UPIU.Read Flag),来读取FLAG IDN,如果返回的UPIU RESPONSE值显示FLAG IDN 被清零,则说明ufs device已经ready

  2. ufs_read_device_desc_data(hba)
    主要通过调用ufshcd_query_descriptor_retry通过发送QUERY REQUEST UPIU.Read Descriptor Opcode来获取UFS设备描述信息,相关信息将被保存到hba->dev_info中

  3. ufshcd_init_desc_sizes(hba)
    Init check for device descriptor sizes(TODO)

  4. ufs_get_device_desc(hba, &card)
    通过QUERY REQUEST command code,UPIU_QUERY_OPCODE_READ_DESC,QUERY_DESC_IDN_STRING,读取UFS的设备细节信息,并保存在card中,此处的card为struct ufs_dev_desc类型

  5. ufshcd_needs_reinit(hba)
    判断ufs是否需要重新初始化,如果需要重新初始化则执行如下操作:
    ufshcd_vops_full_reset(hba)
    ufshcd_reset_device(hba)//reset ufs device
    ufshcd_hba_stop(hba, false);//reset controller
    ufshcd_hba_enable(hba)

  6. ufs_fixup_device_setup(hba, &card)
    此处的card为struct ufs_dev_desc类型,它是上一步通过QUERY REQUEST 操作从UFS读取出的描述符信息,因为有一些UFS有一些怪癖,这些怪癖的UFS主要保存在ufs_fixups里,通过检查链接的UFS是否在这个列表,如果在这个列表,则需要标识出此UFS具有这些怪癖,这样在使用时就可以做特殊处理

  7. ufshcd_tune_unipro_params(hba)
    调整unipro的参数

  8. ufshcd_set_ufs_dev_active(hba);
    hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
    UFS device is also active now

  9. ufshcd_force_reset_auto_bkops(hba);
    TODO

  10. ufshcd_get_max_pwr_mode(hba)
    通过mipi命令获取UFS支持的最大的lane以及速率,通过与ufs host controller比较采用两者支持的最大值,

  11. ufshcd_set_active_icc_lvl(hba)
    TODO

  12. hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
    ufshcd_wb_config(hba)
    set the state as operational after switching to desired gear

  13. ufshcd_set_auto_hibern8_timer(hba)
    设置进入hibern的定时器

|- -ufshcd_add_lus

static int ufshcd_add_lus(struct ufs_hba *hba)
	|--ufshcd_scsi_add_wlus(hba);
	|--if (ufshcd_is_clkscaling_supported(hba))
	|		if (!hba->devfreq)
	|			ufshcd_devfreq_init(hba);
	|--ufs_bsg_probe(hba);
	|--scsi_scan_host(hba->host);
	|--pm_runtime_put_sync(hba->dev);
  1. ufshcd_scsi_add_wlus(hba)
    This function adds scsi device instances for each of all well known LUs (except “REPORT LUNS” LU).

  2. ufshcd_set_low_vcc_level(hba, &card)
    lower VCC voltage level

  3. ufshcd_devfreq_init(hba)
    通过调用devfreq_add_device将ufs加入到调频子系统

  4. scsi_scan_host(hba->host)
    扫描常规的LUN,scsi_scan_host对指定的scsi_host进行扫描,通过异步扫描host, channel, target, device, 最终将扫描到的lun加入到系统中
    参考:SCSI子系统基础学习笔记 - 3. SCSI设备探测

3. 总结

现在,我们以UFS为例,来总结一下作为一个SCSI块设备,所必须经历哪些标准化的流程:

  1. scsi_host_alloc:为Scsi host控制器Scsi_Host及私有属性分配空间;

  2. scsi_add_host:将代表scsi host的devivce添加进设备模型,同时加入到sysfs,并创建相关属性文件。对于多队列,期间会执行blk_mq_alloc_tag_set,为每个硬队列分配blk_mq_tags指针数组,blk_mq_tag_set 一般内嵌在块设备相关结构体中,此处为hba中;分配数组mq_map用于维护软硬队列的映射,并建立软硬队列的映射关系,同时对每个硬件队列,根据队列深度为每个硬件队列分配bitmap,并根据队列深度创建相应数目的request。

  3. blk_mq_init_queue:分配request_queue,并对其进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系,如果支持调度器,则对调度器初始化

  4. scsi_scan_host:扫描常规的LUN,scsi_scan_host对指定的scsi_host进行扫描,通过异步扫描host, channel, target, device, 最终将扫描到的lun加入到系统中

  5. sd_probe:通过alloc_disk分配gendisk;device_add_disk将通用磁盘添加到系统

参考文档

存储技术原理分析

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值