msi.c

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

C
732
字号
/* * File:	msi.c * Purpose:	PCI Message Signaled Interrupt (MSI) * * Copyright (C) 2003-2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */#include <linux/err.h>#include <linux/mm.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/msi.h>#include <linux/smp.h>#include <asm/errno.h>#include <asm/io.h>#include "pci.h"#include "msi.h"static int pci_msi_enable = 1;static void msi_set_enable(struct pci_dev *dev, int enable){	int pos;	u16 control;	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);	if (pos) {		pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);		control &= ~PCI_MSI_FLAGS_ENABLE;		if (enable)			control |= PCI_MSI_FLAGS_ENABLE;		pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);	}}static void msix_set_enable(struct pci_dev *dev, int enable){	int pos;	u16 control;	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);	if (pos) {		pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);		control &= ~PCI_MSIX_FLAGS_ENABLE;		if (enable)			control |= PCI_MSIX_FLAGS_ENABLE;		pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);	}}static void msix_flush_writes(unsigned int irq){	struct msi_desc *entry;	entry = get_irq_msi(irq);	BUG_ON(!entry || !entry->dev);	switch (entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:		/* nothing to do */		break;	case PCI_CAP_ID_MSIX:	{		int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;		readl(entry->mask_base + offset);		break;	}	default:		BUG();		break;	}}static void msi_set_mask_bit(unsigned int irq, int flag){	struct msi_desc *entry;	entry = get_irq_msi(irq);	BUG_ON(!entry || !entry->dev);	switch (entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:		if (entry->msi_attrib.maskbit) {			int pos;			u32 mask_bits;			pos = (long)entry->mask_base;			pci_read_config_dword(entry->dev, pos, &mask_bits);			mask_bits &= ~(1);			mask_bits |= flag;			pci_write_config_dword(entry->dev, pos, mask_bits);		} else {			msi_set_enable(entry->dev, !flag);		}		break;	case PCI_CAP_ID_MSIX:	{		int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;		writel(flag, entry->mask_base + offset);		readl(entry->mask_base + offset);		break;	}	default:		BUG();		break;	}	entry->msi_attrib.masked = !!flag;}void read_msi_msg(unsigned int irq, struct msi_msg *msg){	struct msi_desc *entry = get_irq_msi(irq);	switch(entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:	{		struct pci_dev *dev = entry->dev;		int pos = entry->msi_attrib.pos;		u16 data;		pci_read_config_dword(dev, msi_lower_address_reg(pos),					&msg->address_lo);		if (entry->msi_attrib.is_64) {			pci_read_config_dword(dev, msi_upper_address_reg(pos),						&msg->address_hi);			pci_read_config_word(dev, msi_data_reg(pos, 1), &data);		} else {			msg->address_hi = 0;			pci_read_config_word(dev, msi_data_reg(pos, 0), &data);		}		msg->data = data;		break;	}	case PCI_CAP_ID_MSIX:	{		void __iomem *base;		base = entry->mask_base +			entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;		msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);		msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);		msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); 		break; 	} 	default:		BUG();	}}void write_msi_msg(unsigned int irq, struct msi_msg *msg){	struct msi_desc *entry = get_irq_msi(irq);	switch (entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:	{		struct pci_dev *dev = entry->dev;		int pos = entry->msi_attrib.pos;		pci_write_config_dword(dev, msi_lower_address_reg(pos),					msg->address_lo);		if (entry->msi_attrib.is_64) {			pci_write_config_dword(dev, msi_upper_address_reg(pos),						msg->address_hi);			pci_write_config_word(dev, msi_data_reg(pos, 1),						msg->data);		} else {			pci_write_config_word(dev, msi_data_reg(pos, 0),						msg->data);		}		break;	}	case PCI_CAP_ID_MSIX:	{		void __iomem *base;		base = entry->mask_base +			entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;		writel(msg->address_lo,			base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);		writel(msg->address_hi,			base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);		writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);		break;	}	default:		BUG();	}	entry->msg = *msg;}void mask_msi_irq(unsigned int irq){	msi_set_mask_bit(irq, 1);	msix_flush_writes(irq);}void unmask_msi_irq(unsigned int irq){	msi_set_mask_bit(irq, 0);	msix_flush_writes(irq);}static int msi_free_irqs(struct pci_dev* dev);static struct msi_desc* alloc_msi_entry(void){	struct msi_desc *entry;	entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL);	if (!entry)		return NULL;	INIT_LIST_HEAD(&entry->list);	entry->irq = 0;	entry->dev = NULL;	return entry;}static void pci_intx_for_msi(struct pci_dev *dev, int enable){	if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG))		pci_intx(dev, enable);}#ifdef CONFIG_PMstatic void __pci_restore_msi_state(struct pci_dev *dev){	int pos;	u16 control;	struct msi_desc *entry;	if (!dev->msi_enabled)		return;	entry = get_irq_msi(dev->irq);	pos = entry->msi_attrib.pos;	pci_intx_for_msi(dev, 0);	msi_set_enable(dev, 0);	write_msi_msg(dev->irq, &entry->msg);	if (entry->msi_attrib.maskbit)		msi_set_mask_bit(dev->irq, entry->msi_attrib.masked);	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);	control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);	if (entry->msi_attrib.maskbit || !entry->msi_attrib.masked)		control |= PCI_MSI_FLAGS_ENABLE;	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);}static void __pci_restore_msix_state(struct pci_dev *dev){	int pos;	struct msi_desc *entry;	u16 control;	if (!dev->msix_enabled)		return;	/* route the table */	pci_intx_for_msi(dev, 0);	msix_set_enable(dev, 0);	list_for_each_entry(entry, &dev->msi_list, list) {		write_msi_msg(entry->irq, &entry->msg);		msi_set_mask_bit(entry->irq, entry->msi_attrib.masked);	}	BUG_ON(list_empty(&dev->msi_list));	entry = list_entry(dev->msi_list.next, struct msi_desc, list);	pos = entry->msi_attrib.pos;	pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);	control &= ~PCI_MSIX_FLAGS_MASKALL;	control |= PCI_MSIX_FLAGS_ENABLE;	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);}void pci_restore_msi_state(struct pci_dev *dev){	__pci_restore_msi_state(dev);	__pci_restore_msix_state(dev);}#endif	/* CONFIG_PM *//** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with a single * MSI irq, regardless of device function is capable of handling * multiple messages. A return of zero indicates the successful setup * of an entry zero with the new MSI irq or non-zero for otherwise. **/static int msi_capability_init(struct pci_dev *dev){	struct msi_desc *entry;	int pos, ret;	u16 control;	msi_set_enable(dev, 0);	/* Ensure msi is disabled as I set it up */   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);	pci_read_config_word(dev, msi_control_reg(pos), &control);	/* MSI Entry Initialization */	entry = alloc_msi_entry();	if (!entry)		return -ENOMEM;	entry->msi_attrib.type = PCI_CAP_ID_MSI;	entry->msi_attrib.is_64 = is_64bit_address(control);	entry->msi_attrib.entry_nr = 0;	entry->msi_attrib.maskbit = is_mask_bit_support(control);	entry->msi_attrib.masked = 1;	entry->msi_attrib.default_irq = dev->irq;	/* Save IOAPIC IRQ */	entry->msi_attrib.pos = pos;	if (is_mask_bit_support(control)) {		entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,				is_64bit_address(control));	}	entry->dev = dev;	if (entry->msi_attrib.maskbit) {		unsigned int maskbits, temp;		/* All MSIs are unmasked by default, Mask them all */		pci_read_config_dword(dev,			msi_mask_bits_reg(pos, is_64bit_address(control)),			&maskbits);		temp = (1 << multi_msi_capable(control));		temp = ((temp - 1) & ~temp);		maskbits |= temp;		pci_write_config_dword(dev,			msi_mask_bits_reg(pos, is_64bit_address(control)),			maskbits);	}	list_add_tail(&entry->list, &dev->msi_list);	/* Configure MSI capability structure */	ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);	if (ret) {		msi_free_irqs(dev);		return ret;	}	/* Set MSI enabled bits	 */	pci_intx_for_msi(dev, 0);	msi_set_enable(dev, 1);	dev->msi_enabled = 1;	dev->irq = entry->irq;	return 0;}/** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of struct msix_entry entries * @nvec: number of @entries * * Setup the MSI-X capability structure of device function with a

⌨️ 快捷键说明

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