📄 sata_fsl.c
字号:
ata_std_postreset);}static void sata_fsl_post_internal_cmd(struct ata_queued_cmd *qc){ if (qc->flags & ATA_QCFLAG_FAILED) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { /* make DMA engine forget about the failed command */ }}static void sata_fsl_irq_clear(struct ata_port *ap){ /* unused */}static void sata_fsl_error_intr(struct ata_port *ap){ struct ata_link *link = &ap->link; struct ata_eh_info *ehi = &link->eh_info; struct sata_fsl_host_priv *host_priv = ap->host->private_data; void __iomem *hcr_base = host_priv->hcr_base; u32 hstatus, dereg, cereg = 0, SError = 0; unsigned int err_mask = 0, action = 0; struct ata_queued_cmd *qc; int freeze = 0; hstatus = ioread32(hcr_base + HSTATUS); cereg = ioread32(hcr_base + CE); ata_ehi_clear_desc(ehi); /* * Handle & Clear SError */ sata_fsl_scr_read(ap, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { sata_fsl_scr_write(ap, SCR_ERROR, SError); err_mask |= AC_ERR_ATA_BUS; } DPRINTK("error_intr,hStat=0x%x,CE=0x%x,DE =0x%x,SErr=0x%x\n", hstatus, cereg, ioread32(hcr_base + DE), SError); /* handle single device errors */ if (cereg) { /* * clear the command error, also clears queue to the device * in error, and we can (re)issue commands to this device. * When a device is in error all commands queued into the * host controller and at the device are considered aborted * and the queue for that device is stopped. Now, after * clearing the device error, we can issue commands to the * device to interrogate it to find the source of the error. */ dereg = ioread32(hcr_base + DE); iowrite32(dereg, hcr_base + DE); iowrite32(cereg, hcr_base + CE); DPRINTK("single device error, CE=0x%x, DE=0x%x\n", ioread32(hcr_base + CE), ioread32(hcr_base + DE)); /* * We should consider this as non fatal error, and TF must * be updated as done below. */ err_mask |= AC_ERR_DEV; } /* handle fatal errors */ if (hstatus & FATAL_ERROR_DECODE) { err_mask |= AC_ERR_ATA_BUS; action |= ATA_EH_SOFTRESET; /* how will fatal error interrupts be completed ?? */ freeze = 1; } /* Handle PHYRDY change notification */ if (hstatus & INT_ON_PHYRDY_CHG) { DPRINTK("SATA FSL: PHYRDY change indication\n"); /* Setup a soft-reset EH action */ ata_ehi_hotplugged(ehi); freeze = 1; } /* record error info */ qc = ata_qc_from_tag(ap, link->active_tag); if (qc) { sata_fsl_cache_taskfile_from_d2h_fis(qc, qc->ap); qc->err_mask |= err_mask; } else ehi->err_mask |= err_mask; ehi->action |= action; ehi->serror |= SError; /* freeze or abort */ if (freeze) ata_port_freeze(ap); else ata_port_abort(ap);}static void sata_fsl_qc_complete(struct ata_queued_cmd *qc){ if (qc->flags & ATA_QCFLAG_RESULT_TF) { DPRINTK("xx_qc_complete called\n"); sata_fsl_cache_taskfile_from_d2h_fis(qc, qc->ap); }}static void sata_fsl_host_intr(struct ata_port *ap){ struct ata_link *link = &ap->link; struct sata_fsl_host_priv *host_priv = ap->host->private_data; void __iomem *hcr_base = host_priv->hcr_base; u32 hstatus, qc_active = 0; struct ata_queued_cmd *qc; u32 SError; hstatus = ioread32(hcr_base + HSTATUS); sata_fsl_scr_read(ap, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { DPRINTK("serror @host_intr : 0x%x\n", SError); sata_fsl_error_intr(ap); } if (unlikely(hstatus & INT_ON_ERROR)) { DPRINTK("error interrupt!!\n"); sata_fsl_error_intr(ap); return; } if (link->sactive) { /* only true for NCQ commands */ int i; /* Read command completed register */ qc_active = ioread32(hcr_base + CC); /* clear CC bit, this will also complete the interrupt */ iowrite32(qc_active, hcr_base + CC); DPRINTK("Status of all queues :\n"); DPRINTK("qc_active/CC = 0x%x, CA = 0x%x, CE=0x%x\n", qc_active, ioread32(hcr_base + CA), ioread32(hcr_base + CE)); for (i = 0; i < SATA_FSL_QUEUE_DEPTH; i++) { if (qc_active & (1 << i)) { qc = ata_qc_from_tag(ap, i); if (qc) { sata_fsl_qc_complete(qc); ata_qc_complete(qc); } DPRINTK ("completing ncq cmd,tag=%d,CC=0x%x,CA=0x%x\n", i, ioread32(hcr_base + CC), ioread32(hcr_base + CA)); } } return; } else if (ap->qc_active) { iowrite32(1, hcr_base + CC); qc = ata_qc_from_tag(ap, link->active_tag); DPRINTK("completing non-ncq cmd, tag=%d,CC=0x%x\n", link->active_tag, ioread32(hcr_base + CC)); if (qc) { sata_fsl_qc_complete(qc); ata_qc_complete(qc); } } else { /* Spurious Interrupt!! */ DPRINTK("spurious interrupt!!, CC = 0x%x\n", ioread32(hcr_base + CC)); return; }}static irqreturn_t sata_fsl_interrupt(int irq, void *dev_instance){ struct ata_host *host = dev_instance; struct sata_fsl_host_priv *host_priv = host->private_data; void __iomem *hcr_base = host_priv->hcr_base; u32 interrupt_enables; unsigned handled = 0; struct ata_port *ap; /* ack. any pending IRQs for this controller/port */ interrupt_enables = ioread32(hcr_base + HSTATUS); interrupt_enables &= 0x3F; DPRINTK("interrupt status 0x%x\n", interrupt_enables); if (!interrupt_enables) return IRQ_NONE; spin_lock(&host->lock); /* Assuming one port per host controller */ ap = host->ports[0]; if (ap) { sata_fsl_host_intr(ap); } else { dev_printk(KERN_WARNING, host->dev, "interrupt on disabled port 0\n"); } iowrite32(interrupt_enables, hcr_base + HSTATUS); handled = 1; spin_unlock(&host->lock); return IRQ_RETVAL(handled);}/* * Multiple ports are represented by multiple SATA controllers with * one port per controller */static int sata_fsl_init_controller(struct ata_host *host){ struct sata_fsl_host_priv *host_priv = host->private_data; void __iomem *hcr_base = host_priv->hcr_base; u32 temp; /* * NOTE : We cannot bring the controller online before setting * the CHBA, hence main controller initialization is done as * part of the port_start() callback */ /* ack. any pending IRQs for this controller/port */ temp = ioread32(hcr_base + HSTATUS); if (temp & 0x3F) iowrite32((temp & 0x3F), hcr_base + HSTATUS); /* Keep interrupts disabled on the controller */ temp = ioread32(hcr_base + HCONTROL); iowrite32((temp & ~0x3F), hcr_base + HCONTROL); /* Disable interrupt coalescing control(icc), for the moment */ DPRINTK("icc = 0x%x\n", ioread32(hcr_base + ICC)); iowrite32(0x01000000, hcr_base + ICC); /* clear error registers, SError is cleared by libATA */ iowrite32(0x00000FFFF, hcr_base + CE); iowrite32(0x00000FFFF, hcr_base + DE); /* initially assuming no Port multiplier, set CQPMP to 0 */ iowrite32(0x0, hcr_base + CQPMP); /* * host controller will be brought on-line, during xx_port_start() * callback, that should also initiate the OOB, COMINIT sequence */ DPRINTK("HStatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); DPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); return 0;}/* * scsi mid-layer and libata interface structures */static struct scsi_host_template sata_fsl_sht = { .module = THIS_MODULE, .name = "sata_fsl", .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .change_queue_depth = ata_scsi_change_queue_depth, .can_queue = SATA_FSL_QUEUE_DEPTH, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = SATA_FSL_MAX_PRD_USABLE, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, .use_clustering = ATA_SHT_USE_CLUSTERING, .proc_name = "sata_fsl", .dma_boundary = ATA_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param,};static const struct ata_port_operations sata_fsl_ops = { .check_status = sata_fsl_check_status, .check_altstatus = sata_fsl_check_status, .dev_select = ata_noop_dev_select, .tf_read = sata_fsl_tf_read, .qc_prep = sata_fsl_qc_prep, .qc_issue = sata_fsl_qc_issue, .irq_clear = sata_fsl_irq_clear, .scr_read = sata_fsl_scr_read, .scr_write = sata_fsl_scr_write, .freeze = sata_fsl_freeze, .thaw = sata_fsl_thaw, .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,};static const struct ata_port_info sata_fsl_port_info[] = { { .flags = SATA_FSL_HOST_FLAGS, .link_flags = SATA_FSL_HOST_LFLAGS, .pio_mask = 0x1f, /* pio 0-4 */ .udma_mask = 0x7f, /* udma 0-6 */ .port_ops = &sata_fsl_ops, },};static int sata_fsl_probe(struct of_device *ofdev, const struct of_device_id *match){ int retval = 0; void __iomem *hcr_base = NULL; void __iomem *ssr_base = NULL; void __iomem *csr_base = NULL; struct sata_fsl_host_priv *host_priv = NULL; struct resource *r; int irq; struct ata_host *host; struct ata_port_info pi = sata_fsl_port_info[0]; const struct ata_port_info *ppi[] = { &pi, NULL }; dev_printk(KERN_INFO, &ofdev->dev, "Sata FSL Platform/CSB Driver init\n"); r = kmalloc(sizeof(struct resource), GFP_KERNEL); hcr_base = of_iomap(ofdev->node, 0); if (!hcr_base) goto error_exit_with_cleanup; ssr_base = hcr_base + 0x100; csr_base = hcr_base + 0x140; DPRINTK("@reset i/o = 0x%x\n", ioread32(csr_base + TRANSCFG)); DPRINTK("sizeof(cmd_desc) = %d\n", sizeof(struct command_desc)); DPRINTK("sizeof(#define cmd_desc) = %d\n", SATA_FSL_CMD_DESC_SIZE); host_priv = kzalloc(sizeof(struct sata_fsl_host_priv), GFP_KERNEL); if (!host_priv) goto error_exit_with_cleanup; host_priv->hcr_base = hcr_base; host_priv->ssr_base = ssr_base; host_priv->csr_base = csr_base; irq = irq_of_parse_and_map(ofdev->node, 0); if (irq < 0) { dev_printk(KERN_ERR, &ofdev->dev, "invalid irq from platform\n"); goto error_exit_with_cleanup; } host_priv->irq = irq; /* allocate host structure */ host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS); /* host->iomap is not used currently */ host->private_data = host_priv; /* setup port(s) */ host->ports[0]->ioaddr.cmd_addr = host_priv->hcr_base; host->ports[0]->ioaddr.scr_addr = host_priv->ssr_base; /* initialize host controller */ sata_fsl_init_controller(host); /* * Now, register with libATA core, this will also initiate the * device discovery process, invoking our port_start() handler & * error_handler() to execute a dummy Softreset EH session */ ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG, &sata_fsl_sht); dev_set_drvdata(&ofdev->dev, host); return 0;error_exit_with_cleanup: if (hcr_base) iounmap(hcr_base); if (host_priv) kfree(host_priv); return retval;}static int sata_fsl_remove(struct of_device *ofdev){ struct ata_host *host = dev_get_drvdata(&ofdev->dev); struct sata_fsl_host_priv *host_priv = host->private_data; ata_host_detach(host); dev_set_drvdata(&ofdev->dev, NULL); irq_dispose_mapping(host_priv->irq); iounmap(host_priv->hcr_base); kfree(host_priv); return 0;}static struct of_device_id fsl_sata_match[] = { { .compatible = "fsl,mpc8315-sata", }, { .compatible = "fsl,mpc8379-sata", }, {},};MODULE_DEVICE_TABLE(of, fsl_sata_match);static struct of_platform_driver fsl_sata_driver = { .name = "fsl-sata", .match_table = fsl_sata_match, .probe = sata_fsl_probe, .remove = sata_fsl_remove,};static int __init sata_fsl_init(void){ of_register_platform_driver(&fsl_sata_driver); return 0;}static void __exit sata_fsl_exit(void){ of_unregister_platform_driver(&fsl_sata_driver);}MODULE_LICENSE("GPL");MODULE_AUTHOR("Ashish Kalra, Freescale Semiconductor");MODULE_DESCRIPTION("Freescale 3.0Gbps SATA controller low level driver");MODULE_VERSION("1.10");module_init(sata_fsl_init);module_exit(sata_fsl_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -