pdc_adma.c

来自「linux 内核源代码」· C语言 代码 · 共 747 行 · 第 1/2 页

C
747
字号
	i++;		/* cLEN, gets filled in below */	*(__le32 *)(buf+i) = cpu_to_le32(pkt_dma);	/* cNCPB */	i += 4;		/* cNCPB */	i += 4;		/* cPRD, gets filled in below */	buf[i++] = 0;	/* reserved */	buf[i++] = 0;	/* reserved */	buf[i++] = 0;	/* reserved */	buf[i++] = 0;	/* reserved */	/* ATA registers; must be a multiple of 4 */	buf[i++] = qc->tf.device;	buf[i++] = ADMA_REGS_DEVICE;	if ((qc->tf.flags & ATA_TFLAG_LBA48)) {		buf[i++] = qc->tf.hob_nsect;		buf[i++] = ADMA_REGS_SECTOR_COUNT;		buf[i++] = qc->tf.hob_lbal;		buf[i++] = ADMA_REGS_LBA_LOW;		buf[i++] = qc->tf.hob_lbam;		buf[i++] = ADMA_REGS_LBA_MID;		buf[i++] = qc->tf.hob_lbah;		buf[i++] = ADMA_REGS_LBA_HIGH;	}	buf[i++] = qc->tf.nsect;	buf[i++] = ADMA_REGS_SECTOR_COUNT;	buf[i++] = qc->tf.lbal;	buf[i++] = ADMA_REGS_LBA_LOW;	buf[i++] = qc->tf.lbam;	buf[i++] = ADMA_REGS_LBA_MID;	buf[i++] = qc->tf.lbah;	buf[i++] = ADMA_REGS_LBA_HIGH;	buf[i++] = 0;	buf[i++] = ADMA_REGS_CONTROL;	buf[i++] = rIGN;	buf[i++] = 0;	buf[i++] = qc->tf.command;	buf[i++] = ADMA_REGS_COMMAND | rEND;	buf[3] = (i >> 3) - 2;				/* cLEN */	*(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i);	/* cPRD */	i = adma_fill_sg(qc);	wmb();	/* flush PRDs and pkt to memory */#if 0	/* dump out CPB + PRDs for debug */	{		int j, len = 0;		static char obuf[2048];		for (j = 0; j < i; ++j) {			len += sprintf(obuf+len, "%02x ", buf[j]);			if ((j & 7) == 7) {				printk("%s\n", obuf);				len = 0;			}		}		if (len)			printk("%s\n", obuf);	}#endif}static inline void adma_packet_start(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	void __iomem *chan = ADMA_PORT_REGS(ap);	VPRINTK("ENTER, ap %p\n", ap);	/* fire up the ADMA engine */	writew(aPIOMD4 | aGO, chan + ADMA_CONTROL);}static unsigned int adma_qc_issue(struct ata_queued_cmd *qc){	struct adma_port_priv *pp = qc->ap->private_data;	switch (qc->tf.protocol) {	case ATA_PROT_DMA:		pp->state = adma_state_pkt;		adma_packet_start(qc);		return 0;	case ATA_PROT_ATAPI_DMA:		BUG();		break;	default:		break;	}	pp->state = adma_state_mmio;	return ata_qc_issue_prot(qc);}static inline unsigned int adma_intr_pkt(struct ata_host *host){	unsigned int handled = 0, port_no;	for (port_no = 0; port_no < host->n_ports; ++port_no) {		struct ata_port *ap = host->ports[port_no];		struct adma_port_priv *pp;		struct ata_queued_cmd *qc;		void __iomem *chan = ADMA_PORT_REGS(ap);		u8 status = readb(chan + ADMA_STATUS);		if (status == 0)			continue;		handled = 1;		adma_enter_reg_mode(ap);		if (ap->flags & ATA_FLAG_DISABLED)			continue;		pp = ap->private_data;		if (!pp || pp->state != adma_state_pkt)			continue;		qc = ata_qc_from_tag(ap, ap->link.active_tag);		if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {			if (status & aPERR)				qc->err_mask |= AC_ERR_HOST_BUS;			else if ((status & (aPSD | aUIRQ)))				qc->err_mask |= AC_ERR_OTHER;			if (pp->pkt[0] & cATERR)				qc->err_mask |= AC_ERR_DEV;			else if (pp->pkt[0] != cDONE)				qc->err_mask |= AC_ERR_OTHER;			if (!qc->err_mask)				ata_qc_complete(qc);			else {				struct ata_eh_info *ehi = &ap->link.eh_info;				ata_ehi_clear_desc(ehi);				ata_ehi_push_desc(ehi,					"ADMA-status 0x%02X", status);				ata_ehi_push_desc(ehi,					"pkt[0] 0x%02X", pp->pkt[0]);				if (qc->err_mask == AC_ERR_DEV)					ata_port_abort(ap);				else					ata_port_freeze(ap);			}		}	}	return handled;}static inline unsigned int adma_intr_mmio(struct ata_host *host){	unsigned int handled = 0, port_no;	for (port_no = 0; port_no < host->n_ports; ++port_no) {		struct ata_port *ap;		ap = host->ports[port_no];		if (ap && (!(ap->flags & ATA_FLAG_DISABLED))) {			struct ata_queued_cmd *qc;			struct adma_port_priv *pp = ap->private_data;			if (!pp || pp->state != adma_state_mmio)				continue;			qc = ata_qc_from_tag(ap, ap->link.active_tag);			if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {				/* check main status, clearing INTRQ */				u8 status = ata_check_status(ap);				if ((status & ATA_BUSY))					continue;				DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",					ap->print_id, qc->tf.protocol, status);				/* complete taskfile transaction */				pp->state = adma_state_idle;				qc->err_mask |= ac_err_mask(status);				if (!qc->err_mask)					ata_qc_complete(qc);				else {					struct ata_eh_info *ehi =						&ap->link.eh_info;					ata_ehi_clear_desc(ehi);					ata_ehi_push_desc(ehi,						"status 0x%02X", status);					if (qc->err_mask == AC_ERR_DEV)						ata_port_abort(ap);					else						ata_port_freeze(ap);				}				handled = 1;			}		}	}	return handled;}static irqreturn_t adma_intr(int irq, void *dev_instance){	struct ata_host *host = dev_instance;	unsigned int handled = 0;	VPRINTK("ENTER\n");	spin_lock(&host->lock);	handled  = adma_intr_pkt(host) | adma_intr_mmio(host);	spin_unlock(&host->lock);	VPRINTK("EXIT\n");	return IRQ_RETVAL(handled);}static void adma_ata_setup_port(struct ata_ioports *port, void __iomem *base){	port->cmd_addr		=	port->data_addr		= base + 0x000;	port->error_addr	=	port->feature_addr	= base + 0x004;	port->nsect_addr	= base + 0x008;	port->lbal_addr		= base + 0x00c;	port->lbam_addr		= base + 0x010;	port->lbah_addr		= base + 0x014;	port->device_addr	= base + 0x018;	port->status_addr	=	port->command_addr	= base + 0x01c;	port->altstatus_addr	=	port->ctl_addr		= base + 0x038;}static int adma_port_start(struct ata_port *ap){	struct device *dev = ap->host->dev;	struct adma_port_priv *pp;	int rc;	rc = ata_port_start(ap);	if (rc)		return rc;	adma_enter_reg_mode(ap);	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);	if (!pp)		return -ENOMEM;	pp->pkt = dmam_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma,				      GFP_KERNEL);	if (!pp->pkt)		return -ENOMEM;	/* paranoia? */	if ((pp->pkt_dma & 7) != 0) {		printk(KERN_ERR "bad alignment for pp->pkt_dma: %08x\n",						(u32)pp->pkt_dma);		return -ENOMEM;	}	memset(pp->pkt, 0, ADMA_PKT_BYTES);	ap->private_data = pp;	adma_reinit_engine(ap);	return 0;}static void adma_port_stop(struct ata_port *ap){	adma_reset_engine(ap);}static void adma_host_stop(struct ata_host *host){	unsigned int port_no;	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)		adma_reset_engine(host->ports[port_no]);}static void adma_host_init(struct ata_host *host, unsigned int chip_id){	unsigned int port_no;	/* enable/lock aGO operation */	writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK);	/* reset the ADMA logic */	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)		adma_reset_engine(host->ports[port_no]);}static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base){	int rc;	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);	if (rc) {		dev_printk(KERN_ERR, &pdev->dev,			"32-bit DMA enable failed\n");		return rc;	}	rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);	if (rc) {		dev_printk(KERN_ERR, &pdev->dev,			"32-bit consistent DMA enable failed\n");		return rc;	}	return 0;}static int adma_ata_init_one(struct pci_dev *pdev,			     const struct pci_device_id *ent){	static int printed_version;	unsigned int board_idx = (unsigned int) ent->driver_data;	const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL };	struct ata_host *host;	void __iomem *mmio_base;	int rc, port_no;	if (!printed_version++)		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");	/* alloc host */	host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS);	if (!host)		return -ENOMEM;	/* acquire resources and fill host */	rc = pcim_enable_device(pdev);	if (rc)		return rc;	if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0)		return -ENODEV;	rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME);	if (rc)		return rc;	host->iomap = pcim_iomap_table(pdev);	mmio_base = host->iomap[ADMA_MMIO_BAR];	rc = adma_set_dma_masks(pdev, mmio_base);	if (rc)		return rc;	for (port_no = 0; port_no < ADMA_PORTS; ++port_no) {		struct ata_port *ap = host->ports[port_no];		void __iomem *port_base = ADMA_ATA_REGS(mmio_base, port_no);		unsigned int offset = port_base - mmio_base;		adma_ata_setup_port(&ap->ioaddr, port_base);		ata_port_pbar_desc(ap, ADMA_MMIO_BAR, -1, "mmio");		ata_port_pbar_desc(ap, ADMA_MMIO_BAR, offset, "port");	}	/* initialize adapter */	adma_host_init(host, board_idx);	pci_set_master(pdev);	return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED,				 &adma_ata_sht);}static int __init adma_ata_init(void){	return pci_register_driver(&adma_ata_pci_driver);}static void __exit adma_ata_exit(void){	pci_unregister_driver(&adma_ata_pci_driver);}MODULE_AUTHOR("Mark Lord");MODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl);MODULE_VERSION(DRV_VERSION);module_init(adma_ata_init);module_exit(adma_ata_exit);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?