msi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,148 行 · 第 1/3 页

C
1,148
字号
		return;	spin_lock_irqsave(&msi_lock, flags);	entry = msi_desc[dev->irq];	if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {		spin_unlock_irqrestore(&msi_lock, flags);		return;	}	if (entry->msi_attrib.state) {		spin_unlock_irqrestore(&msi_lock, flags);		printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on vector->%d\n",		dev->bus->number, PCI_SLOT(dev->devfn),	PCI_FUNC(dev->devfn),		dev->irq);		BUG_ON(entry->msi_attrib.state > 0);	} else {		vector_irq[dev->irq] = 0; /* free it */		nr_released_vectors++;		default_vector = entry->msi_attrib.default_vector;		spin_unlock_irqrestore(&msi_lock, flags);		/* Restore dev->irq to its default pin-assertion vector */		dev->irq = default_vector;		disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),					PCI_CAP_ID_MSI);	}}static void release_msi(unsigned int vector){	struct msi_desc *entry;	unsigned long flags;	spin_lock_irqsave(&msi_lock, flags);	entry = msi_desc[vector];	if (entry && entry->dev)		entry->msi_attrib.state = 0;	/* Mark it not active */	spin_unlock_irqrestore(&msi_lock, flags);}static int msi_free_vector(struct pci_dev* dev, int vector, int reassign){	struct msi_desc *entry;	int head, entry_nr, type;	unsigned long base = 0L;	unsigned long flags;	spin_lock_irqsave(&msi_lock, flags);	entry = msi_desc[vector];	if (!entry || entry->dev != dev) {		spin_unlock_irqrestore(&msi_lock, flags);		return -EINVAL;	}	type = entry->msi_attrib.type;	entry_nr = entry->msi_attrib.entry_nr;	head = entry->link.head;	base = entry->mask_base;	msi_desc[entry->link.head]->link.tail = entry->link.tail;	msi_desc[entry->link.tail]->link.head = entry->link.head;	entry->dev = NULL;	if (!reassign) {		vector_irq[vector] = 0;		nr_released_vectors++;	}	msi_desc[vector] = NULL;	spin_unlock_irqrestore(&msi_lock, flags);	kmem_cache_free(msi_cachep, entry);	if (type == PCI_CAP_ID_MSIX) {		if (!reassign)			writel(1, base +				entry_nr * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);		if (head == vector) {			/*			 * Detect last MSI-X vector to be released.			 * Release the MSI-X memory-mapped table.			 */			int pos, nr_entries;			u32 phys_addr, table_offset;			u16 control;			u8 bir;   			pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);			pci_read_config_word(dev, msi_control_reg(pos),				&control);			nr_entries = multi_msix_capable(control);			pci_read_config_dword(dev, msix_table_offset_reg(pos),				&table_offset);			bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);			phys_addr = pci_resource_start (dev, bir);			phys_addr += (u32)(table_offset &				~PCI_MSIX_FLAGS_BIRMASK);			iounmap((void*)base);			release_mem_region(phys_addr,				nr_entries * PCI_MSIX_ENTRY_SIZE);		}	}	return 0;}static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec){	int vector = head, tail = 0;	int i = 0, j = 0, nr_entries = 0;	unsigned long base = 0L;	unsigned long flags;	spin_lock_irqsave(&msi_lock, flags);	while (head != tail) {		nr_entries++;		tail = msi_desc[vector]->link.tail;		if (entries[0].entry == msi_desc[vector]->msi_attrib.entry_nr)			j = vector;		vector = tail;	}	if (*nvec > nr_entries) {		spin_unlock_irqrestore(&msi_lock, flags);		*nvec = nr_entries;		return -EINVAL;	}	vector = ((j > 0) ? j : head);	for (i = 0; i < *nvec; i++) {		j = msi_desc[vector]->msi_attrib.entry_nr;		msi_desc[vector]->msi_attrib.state = 0;	/* Mark it not active */		vector_irq[vector] = -1;		/* Mark it busy */		nr_released_vectors--;		entries[i].vector = vector;		if (j != (entries + i)->entry) {			base = msi_desc[vector]->mask_base;			msi_desc[vector]->msi_attrib.entry_nr =				(entries + i)->entry;			writel( readl(base + j * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET), base +				(entries + i)->entry * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);			writel(	readl(base + j * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET), base +				(entries + i)->entry * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);			writel( (readl(base + j * PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_DATA_OFFSET) & 0xff00) | vector,				base + (entries+i)->entry*PCI_MSIX_ENTRY_SIZE +				PCI_MSIX_ENTRY_DATA_OFFSET);		}		vector = msi_desc[vector]->link.tail;	}	spin_unlock_irqrestore(&msi_lock, flags);	return 0;}/** * pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function * @data: pointer to an array of MSI-X entries * @nvec: number of MSI-X vectors requested for allocation by device driver * * Setup the MSI-X capability structure of device function with the number * of requested vectors upon its software driver call to request for * MSI-X mode enabled on its hardware device function. A return of zero * indicates the successful configuration of MSI-X capability structure * with new allocated MSI-X vectors. A return of < 0 indicates a failure. * Or a return of > 0 indicates that driver request is exceeding the number * of vectors available. Driver should use the returned value to re-send * its request. **/int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec){	int status, pos, nr_entries, free_vectors;	int i, j, temp;	u16 control;	unsigned long flags;	if (!pci_msi_enable || !dev || !entries) 		return -EINVAL;	if ((status = msi_init()) < 0)		return status;   	if (!(pos = pci_find_capability(dev, PCI_CAP_ID_MSIX))) 		return -EINVAL;	pci_read_config_word(dev, msi_control_reg(pos), &control);	if (control & PCI_MSIX_FLAGS_ENABLE)		return -EINVAL;			/* Already in MSI-X mode */	nr_entries = multi_msix_capable(control);	if (nvec > nr_entries)		return -EINVAL;	/* Check for any invalid entries */	for (i = 0; i < nvec; i++) {		if (entries[i].entry >= nr_entries)			return -EINVAL;		/* invalid entry */		for (j = i + 1; j < nvec; j++) {			if (entries[i].entry == entries[j].entry)				return -EINVAL;	/* duplicate entry */		}	}	temp = dev->irq;	if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {		/* Lookup Sucess */		nr_entries = nvec;		/* Reroute MSI-X table */		if (reroute_msix_table(dev->irq, entries, &nr_entries)) {			/* #requested > #previous-assigned */			dev->irq = temp;			return nr_entries;		}		dev->irq = temp;		enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);		return 0;	}	/* Check whether driver already requested for MSI vector */   	if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&		!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {		printk(KERN_INFO "Can't enable MSI-X. Device already had MSI vector assigned\n");		dev->irq = temp;		return -EINVAL;	}	spin_lock_irqsave(&msi_lock, flags);	/*	 * msi_lock is provided to ensure that enough vectors resources are	 * available before granting.	 */	free_vectors = pci_vector_resources(last_alloc_vector,				nr_released_vectors);	/* Ensure that each MSI/MSI-X device has one vector reserved by	   default to avoid any MSI-X driver to take all available 	   resources */	free_vectors -= nr_reserved_vectors;	/* Find the average of free vectors among MSI-X devices */	if (nr_msix_devices > 0)		free_vectors /= nr_msix_devices;	spin_unlock_irqrestore(&msi_lock, flags);	if (nvec > free_vectors) {		if (free_vectors > 0)			return free_vectors;		else			return -EBUSY;	}	status = msix_capability_init(dev, entries, nvec);	if (!status && nr_msix_devices > 0)		nr_msix_devices--;	return status;}void pci_disable_msix(struct pci_dev* dev){	int pos, temp;	u16 control;   	if (!dev || !(pos = pci_find_capability(dev, PCI_CAP_ID_MSIX)))		return;	pci_read_config_word(dev, msi_control_reg(pos), &control);	if (!(control & PCI_MSIX_FLAGS_ENABLE))		return;	temp = dev->irq;	if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {		int state, vector, head, tail = 0, warning = 0;		unsigned long flags;		vector = head = dev->irq;		spin_lock_irqsave(&msi_lock, flags);		while (head != tail) {			state = msi_desc[vector]->msi_attrib.state;			if (state)				warning = 1;			else {				vector_irq[vector] = 0; /* free it */				nr_released_vectors++;			}			tail = msi_desc[vector]->link.tail;			vector = tail;		}		spin_unlock_irqrestore(&msi_lock, flags);		if (warning) {			dev->irq = temp;			printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on all vectors\n",			dev->bus->number, PCI_SLOT(dev->devfn),			PCI_FUNC(dev->devfn));			BUG_ON(warning > 0);		} else {			dev->irq = temp;			disable_msi_mode(dev,				pci_find_capability(dev, PCI_CAP_ID_MSIX),				PCI_CAP_ID_MSIX);		}	}}/** * msi_remove_pci_irq_vectors - reclaim MSI(X) vectors to unused state * @dev: pointer to the pci_dev data structure of MSI(X) device function * * Being called during hotplug remove, from which the device funciton * is hot-removed. All previous assigned MSI/MSI-X vectors, if * allocated for this device function, are reclaimed to unused state, * which may be used later on. **/void msi_remove_pci_irq_vectors(struct pci_dev* dev){	int state, pos, temp;	unsigned long flags;	if (!pci_msi_enable || !dev) 		return;	temp = dev->irq;		/* Save IOAPIC IRQ */   	if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSI)) > 0 &&		!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {		spin_lock_irqsave(&msi_lock, flags);		state = msi_desc[dev->irq]->msi_attrib.state;		spin_unlock_irqrestore(&msi_lock, flags);		if (state) {			printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on vector->%d\n",			dev->bus->number, PCI_SLOT(dev->devfn),			PCI_FUNC(dev->devfn), dev->irq);			BUG_ON(state > 0);		} else /* Release MSI vector assigned to this device */			msi_free_vector(dev, dev->irq, 0);		dev->irq = temp;		/* Restore IOAPIC IRQ */	}   	if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSIX)) > 0 &&		!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {		int vector, head, tail = 0, warning = 0;		unsigned long base = 0L;		vector = head = dev->irq;		while (head != tail) {			spin_lock_irqsave(&msi_lock, flags);			state = msi_desc[vector]->msi_attrib.state;			tail = msi_desc[vector]->link.tail;			base = msi_desc[vector]->mask_base;			spin_unlock_irqrestore(&msi_lock, flags);			if (state)				warning = 1;			else if (vector != head) /* Release MSI-X vector */				msi_free_vector(dev, vector, 0);			vector = tail;		}		msi_free_vector(dev, vector, 0);		if (warning) {			/* Force to release the MSI-X memory-mapped table */			u32 phys_addr, table_offset;			u16 control;			u8 bir;			pci_read_config_word(dev, msi_control_reg(pos),				&control);			pci_read_config_dword(dev, msix_table_offset_reg(pos),				&table_offset);			bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);			phys_addr = pci_resource_start (dev, bir);			phys_addr += (u32)(table_offset &				~PCI_MSIX_FLAGS_BIRMASK);			iounmap((void*)base);			release_mem_region(phys_addr, PCI_MSIX_ENTRY_SIZE *				multi_msix_capable(control));			printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on all vectors\n",				dev->bus->number, PCI_SLOT(dev->devfn),				PCI_FUNC(dev->devfn));			BUG_ON(warning > 0);		}		dev->irq = temp;		/* Restore IOAPIC IRQ */	}}EXPORT_SYMBOL(pci_enable_msi);EXPORT_SYMBOL(pci_disable_msi);EXPORT_SYMBOL(pci_enable_msix);EXPORT_SYMBOL(pci_disable_msix);

⌨️ 快捷键说明

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