⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sata_qstor.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
static inline void qs_packet_start(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	u8 __iomem *chan = qs_mmio_base(ap->host) + (ap->port_no * 0x4000);	VPRINTK("ENTER, ap %p\n", ap);	writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0);	wmb();                             /* flush PRDs and pkt to memory */	writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF);	readl(chan + QS_CCT_CFF);          /* flush */}static unsigned int qs_qc_issue(struct ata_queued_cmd *qc){	struct qs_port_priv *pp = qc->ap->private_data;	switch (qc->tf.protocol) {	case ATA_PROT_DMA:		pp->state = qs_state_pkt;		qs_packet_start(qc);		return 0;	case ATA_PROT_ATAPI_DMA:		BUG();		break;	default:		break;	}	pp->state = qs_state_mmio;	return ata_qc_issue_prot(qc);}static void qs_do_or_die(struct ata_queued_cmd *qc, u8 status){	qc->err_mask |= ac_err_mask(status);	if (!qc->err_mask) {		ata_qc_complete(qc);	} else {		struct ata_port    *ap  = qc->ap;		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);	}}static inline unsigned int qs_intr_pkt(struct ata_host *host){	unsigned int handled = 0;	u8 sFFE;	u8 __iomem *mmio_base = qs_mmio_base(host);	do {		u32 sff0 = readl(mmio_base + QS_HST_SFF);		u32 sff1 = readl(mmio_base + QS_HST_SFF + 4);		u8 sEVLD = (sff1 >> 30) & 0x01;	/* valid flag */		sFFE  = sff1 >> 31;		/* empty flag */		if (sEVLD) {			u8 sDST = sff0 >> 16;	/* dev status */			u8 sHST = sff1 & 0x3f;	/* host status */			unsigned int port_no = (sff1 >> 8) & 0x03;			struct ata_port *ap = host->ports[port_no];			DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n",					sff1, sff0, port_no, sHST, sDST);			handled = 1;			if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {				struct ata_queued_cmd *qc;				struct qs_port_priv *pp = ap->private_data;				if (!pp || pp->state != qs_state_pkt)					continue;				qc = ata_qc_from_tag(ap, ap->link.active_tag);				if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {					switch (sHST) {					case 0: /* successful CPB */					case 3: /* device error */						qs_enter_reg_mode(qc->ap);						qs_do_or_die(qc, sDST);						break;					default:						break;					}				}			}		}	} while (!sFFE);	return handled;}static inline unsigned int qs_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 qs_port_priv *pp;			qc = ata_qc_from_tag(ap, ap->link.active_tag);			if (!qc || !(qc->flags & ATA_QCFLAG_ACTIVE)) {				/*				 * The qstor hardware generates spurious				 * interrupts from time to time when switching				 * in and out of packet mode.				 * There's no obvious way to know if we're				 * here now due to that, so just ack the irq				 * and pretend we knew it was ours.. (ugh).				 * This does not affect packet mode.				 */				ata_check_status(ap);				handled = 1;				continue;			}			pp = ap->private_data;			if (!pp || pp->state != qs_state_mmio)				continue;			if (!(qc->tf.flags & ATA_TFLAG_POLLING))				handled |= ata_host_intr(ap, qc);		}	}	return handled;}static irqreturn_t qs_intr(int irq, void *dev_instance){	struct ata_host *host = dev_instance;	unsigned int handled = 0;	unsigned long flags;	VPRINTK("ENTER\n");	spin_lock_irqsave(&host->lock, flags);	handled  = qs_intr_pkt(host) | qs_intr_mmio(host);	spin_unlock_irqrestore(&host->lock, flags);	VPRINTK("EXIT\n");	return IRQ_RETVAL(handled);}static void qs_ata_setup_port(struct ata_ioports *port, void __iomem *base){	port->cmd_addr		=	port->data_addr		= base + 0x400;	port->error_addr	=	port->feature_addr	= base + 0x408; /* hob_feature = 0x409 */	port->nsect_addr	= base + 0x410; /* hob_nsect   = 0x411 */	port->lbal_addr		= base + 0x418; /* hob_lbal    = 0x419 */	port->lbam_addr		= base + 0x420; /* hob_lbam    = 0x421 */	port->lbah_addr		= base + 0x428; /* hob_lbah    = 0x429 */	port->device_addr	= base + 0x430;	port->status_addr	=	port->command_addr	= base + 0x438;	port->altstatus_addr	=	port->ctl_addr		= base + 0x440;	port->scr_addr		= base + 0xc00;}static int qs_port_start(struct ata_port *ap){	struct device *dev = ap->host->dev;	struct qs_port_priv *pp;	void __iomem *mmio_base = qs_mmio_base(ap->host);	void __iomem *chan = mmio_base + (ap->port_no * 0x4000);	u64 addr;	int rc;	rc = ata_port_start(ap);	if (rc)		return rc;	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);	if (!pp)		return -ENOMEM;	pp->pkt = dmam_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma,				      GFP_KERNEL);	if (!pp->pkt)		return -ENOMEM;	memset(pp->pkt, 0, QS_PKT_BYTES);	ap->private_data = pp;	qs_enter_reg_mode(ap);	addr = (u64)pp->pkt_dma;	writel((u32) addr,        chan + QS_CCF_CPBA);	writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4);	return 0;}static void qs_host_stop(struct ata_host *host){	void __iomem *mmio_base = qs_mmio_base(host);	writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */}static void qs_host_init(struct ata_host *host, unsigned int chip_id){	void __iomem *mmio_base = host->iomap[QS_MMIO_BAR];	unsigned int port_no;	writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */	/* reset each channel in turn */	for (port_no = 0; port_no < host->n_ports; ++port_no) {		u8 __iomem *chan = mmio_base + (port_no * 0x4000);		writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);		writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);		readb(chan + QS_CCT_CTR0);        /* flush */	}	writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */	for (port_no = 0; port_no < host->n_ports; ++port_no) {		u8 __iomem *chan = mmio_base + (port_no * 0x4000);		/* set FIFO depths to same settings as Windows driver */		writew(32, chan + QS_CFC_HUFT);		writew(32, chan + QS_CFC_HDFT);		writew(10, chan + QS_CFC_DUFT);		writew( 8, chan + QS_CFC_DDFT);		/* set CPB size in bytes, as a power of two */		writeb(QS_CPB_ORDER,    chan + QS_CCF_CSEP);	}	writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */}/* * The QStor understands 64-bit buses, and uses 64-bit fields * for DMA pointers regardless of bus width.  We just have to * make sure our DMA masks are set appropriately for whatever * bridge lies between us and the QStor, and then the DMA mapping * code will ensure we only ever "see" appropriate buffer addresses. * If we're 32-bit limited somewhere, then our 64-bit fields will * just end up with zeros in the upper 32-bits, without any special * logic required outside of this routine (below). */static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base){	u32 bus_info = readl(mmio_base + QS_HID_HPHY);	int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT);	if (have_64bit_bus &&	    !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {		rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);		if (rc) {			rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);			if (rc) {				dev_printk(KERN_ERR, &pdev->dev,					   "64-bit DMA enable failed\n");				return rc;			}		}	} else {		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 qs_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[] = { &qs_port_info[board_idx], NULL };	struct ata_host *host;	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, QS_PORTS);	if (!host)		return -ENOMEM;	/* acquire resources and fill host */	rc = pcim_enable_device(pdev);	if (rc)		return rc;	if ((pci_resource_flags(pdev, QS_MMIO_BAR) & IORESOURCE_MEM) == 0)		return -ENODEV;	rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME);	if (rc)		return rc;	host->iomap = pcim_iomap_table(pdev);	rc = qs_set_dma_masks(pdev, host->iomap[QS_MMIO_BAR]);	if (rc)		return rc;	for (port_no = 0; port_no < host->n_ports; ++port_no) {		struct ata_port *ap = host->ports[port_no];		unsigned int offset = port_no * 0x4000;		void __iomem *chan = host->iomap[QS_MMIO_BAR] + offset;		qs_ata_setup_port(&ap->ioaddr, chan);		ata_port_pbar_desc(ap, QS_MMIO_BAR, -1, "mmio");		ata_port_pbar_desc(ap, QS_MMIO_BAR, offset, "port");	}	/* initialize adapter */	qs_host_init(host, board_idx);	pci_set_master(pdev);	return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED,				 &qs_ata_sht);}static int __init qs_ata_init(void){	return pci_register_driver(&qs_ata_pci_driver);}static void __exit qs_ata_exit(void){	pci_unregister_driver(&qs_ata_pci_driver);}MODULE_AUTHOR("Mark Lord");MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl);MODULE_VERSION(DRV_VERSION);module_init(qs_ata_init);module_exit(qs_ata_exit);

⌨️ 快捷键说明

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