驱动层次关系
0-> sata_xxx.c //sata的平台驱动1-> drivers/of/xxx.c //设备树相关操作,of_xxx()1-> sata_xxx.c //不同芯片自己的init、irq handler、ata_port ops接口、scsi_host_template sht接口1-> drivers/ata/libata_core.c //ata驱动核心,ata_host_xxx()1-> drivers/ata/libata_scsi.c //scsi适配层1-> drivers/ata/libata_pmp.c //端口扩展1-> drivers/ata/libata_eh.c //异常处理
以Freescale(飞思卡尔) 主控芯片自带的SATA控制器为例,sata_fsl.c
初始化流程
0-> sata_fsl_probe(drivers/ata/sata_fsl.c)1-> of_iomap //从设备树种获取hcr_base、ssr_base、csr_base1-> irq_of_parse_and_map //从设备树获取中断信息1-> ata_host_alloc_pinfo(drivers/ata/libata_core.c)//通过ata_host_alloc分配host//host.ops及host.port.ops = sata_fsl_ops1-> sata_fsl_init_controller //使用fsl自己的寄存器初始化SATA控制器及中断1-> ata_host_activate //irq_handler: sata_fsl_interrupt, sht: sata_fsl_sht
ATA核心接口 ata_host_alloc_pinfo (drivers/ata/libata-core.c)
0-> ata_host_alloc_pinfo //分配host及初始化port1-> ata_host_alloc //分配ATA host及port1-> 初始化每个host.ports
ATA核心接口 ata_host_activate (drivers/ata/libata-core.c)
0-> ata_host_activate //启动ATA host,请求并注册IRQ1-> ata_host_start //启动ATA host2-> ap->ops->port_start //sata_fsl_port_start 启动端口,通过fsl内部HCONTROL寄存器复位在线phy2-> ap->ops->freeze //sata_fsl_freeze 冻结端口,通过fsl内部HCONTROL寄存器关闭中断实现1-> devm_request_irq //请求并注册中断处理函数1-> ata_host_register //注册ATA 已启动的host,开启ATA端口,注册host到ATA即SCSI层,并probe注册的设备2-> ata_tport_add //每个端口 初始化sysfs的ATA端口结构。2-> ata_scsi_add_hosts //注册SCSI主机适配器实例 sht: sata_fsl_sht3-> scsi_host_alloc //分配SCSI主机内存并初始化3-> scsi_add_host_with_dma //添加带有dma设备的SCSI主机2-> sata_link_init_spd //通过SControl(SCR2)寄存器初始化并限制速率2-> 打印ATA端口信息2-> async_port_probe //每个端口 异步probe3-> ata_port_probe4-> __ata_port_probe5-> ata_port_schedule_eh6-> ata_std_sched_eh //ap->ops->sched_eh4-> ata_port_wait_eh //等待当前挂起的eh完成3-> ata_scsi_scan_host
sysfs下的信息
cxh@cxhserver:~$ ls /sys/class/ata_port/ata1 ata2 ata3cxh@cxhserver:~$ ls /sys/class/ata_port/ata1device idle_irq nr_pmp_links port_no power subsystem ueventcxh@cxhserver:~$ ls /sys/class/ata_link/link1 link2 link3cxh@cxhserver:~$ ls /sys/class/ata_link/link1device hw_sata_spd_limit power sata_spd sata_spd_limit subsystem uevent
接口 ops
ata_port_operations
//libata-onst struct ata_port_operations ata_base_port_ops = {.prereset= ata_std_prereset,.postreset= ata_std_postreset,.error_handler= ata_std_error_handler,.sched_eh= ata_std_sched_eh,.end_eh= ata_std_end_eh,};const struct ata_port_operations sata_port_ops = {.inherits= &ata_base_port_ops,.qc_defer= ata_std_qc_defer,.hardreset= sata_std_hardreset,};//libata-onst struct ata_port_operations sata_pmp_port_ops = {.inherits= &sata_port_ops,.pmp_prereset= ata_std_prereset,.pmp_hardreset= sata_std_hardreset,.pmp_postreset= ata_std_postreset,.error_handler= sata_pmp_error_handler,};//sata_fsl.cstatic struct ata_port_operations sata_fsl_ops = {.inherits = &sata_pmp_port_ops,.qc_defer = ata_std_qc_defer,.qc_prep = sata_fsl_qc_prep,.qc_issue = sata_fsl_qc_issue,.qc_fill_rtf = sata_fsl_qc_fill_rtf,.scr_read = sata_fsl_scr_read,.scr_write = sata_fsl_scr_write,.freeze = sata_fsl_freeze,.thaw = sata_fsl_thaw,.softreset = sata_fsl_softreset,.hardreset = sata_fsl_hardreset,.pmp_softreset = sata_fsl_softreset,.error_handler = sata_fsl_error_handler,.post_internal_cmd = sata_fsl_post_internal_cmd,.port_start = sata_fsl_port_start,.port_stop = sata_fsl_port_stop,.pmp_attach = sata_fsl_pmp_attach,.pmp_detach = sata_fsl_pmp_detach,};
scsi_host_temp
//libata.h#define ATA_BASE_SHT(drv_name)\.module= THIS_MODULE,\.name= drv_name,\.ioctl= ata_scsi_ioctl,\.queuecommand= ata_scsi_queuecmd,\.can_queue= ATA_DEF_QUEUE,\.tag_alloc_policy= BLK_TAG_ALLOC_RR,\.this_id= ATA_SHT_THIS_ID,\.emulated= ATA_SHT_EMULATED,\.use_clustering= ATA_SHT_USE_CLUSTERING,\.proc_name= drv_name,\.slave_configure= ata_scsi_slave_config,\.slave_destroy= ata_scsi_slave_destroy,\.bios_param= ata_std_bios_param,\.unlock_native_capacity= ata_scsi_unlock_native_capacity, \.sdev_attrs= ata_common_sdev_attrs#define ATA_NCQ_SHT(drv_name)\ATA_BASE_SHT(drv_name),\.change_queue_depth= ata_scsi_change_queue_depth//sata_fsl.cstatic struct scsi_host_template ahci_sht = {ATA_NCQ_SHT("sata_fsl"),.can_queue = SATA_FSL_QUEUE_DEPTH,.sg_tablesize = SATA_FSL_MAX_PRD_USABLE,.dma_boundary = ATA_DMA_BOUNDARY,};
核心ATA发送命令
libata内部ata命令即scsi的ata命令流程与AHCI驱动相同。
ata_qc_issue
所有发送ata的命令都会调用到此接口。
//libata-core.c0-> ata_qc_issue //分发taskfile到device,taskfile存放于qc中1-> link->sactive |= 1 << qc->hw_tag; //NCQ模式下使用sactive的bit表示待处理的多个命令, 命令完成后清除sactive对应bit。1-> link->active_tag = qc->tag; //非NCQ模式使用link->active_tag表示待处理的单个命令, 命令完成后active_tag=ATA_TAG_POISON表示空闲。1-> ata_sg_setup //如果为DMA模式,则设置sgl2-> dma_map_sg //配置sgl,此函数调用后dma_address才会被赋值,且新的sgl更连续,sg数量更少2-> ap->ops->qc_prep(qc); //ahci_qc_prep 将qc命令拷贝到正确的位置,准备发送2-> ap->ops->qc_issue(qc); //ahci_qc_issue 发送qc命令
sata_fsl_qc_prep
sata_fsl_qc_issue
流程与AHCI类似,具体操作不同cpu可能不同