msi.c

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

C
1,148
字号
/* * 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/mm.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/config.h>#include <linux/ioport.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <asm/errno.h>#include <asm/io.h>#include <asm/smp.h>#include "msi.h"static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED;static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };static kmem_cache_t* msi_cachep;static int pci_msi_enable = 1;static int last_alloc_vector = 0;static int nr_released_vectors = 0;static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS;static int nr_msix_devices = 0;#ifndef CONFIG_X86_IO_APICint vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };#endifstatic void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags){	memset(p, 0, NR_IRQS * sizeof(struct msi_desc));}static int msi_cache_init(void){	msi_cachep = kmem_cache_create("msi_cache",			NR_IRQS * sizeof(struct msi_desc),		       	0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL);	if (!msi_cachep)		return -ENOMEM;	return 0;}static void msi_set_mask_bit(unsigned int vector, int flag){	struct msi_desc *entry;	entry = (struct msi_desc *)msi_desc[vector];	if (!entry || !entry->dev || !entry->mask_base)		return;	switch (entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:	{		int		pos;		u32		mask_bits;		pos = 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);		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);		break;	}	default:		break;	}}#ifdef CONFIG_SMPstatic void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask){	struct msi_desc *entry;	struct msg_address address;	entry = (struct msi_desc *)msi_desc[vector];	if (!entry || !entry->dev)		return;	switch (entry->msi_attrib.type) {	case PCI_CAP_ID_MSI:	{		int pos;   		if (!(pos = pci_find_capability(entry->dev, PCI_CAP_ID_MSI)))			return;		pci_read_config_dword(entry->dev, msi_lower_address_reg(pos),			&address.lo_address.value);		address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK;		address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) <<			MSI_TARGET_CPU_SHIFT);		entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask);		pci_write_config_dword(entry->dev, msi_lower_address_reg(pos),			address.lo_address.value);		break;	}	case PCI_CAP_ID_MSIX:	{		int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +			PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET;		address.lo_address.value = readl(entry->mask_base + offset);		address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK;		address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) <<			MSI_TARGET_CPU_SHIFT);		entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask);		writel(address.lo_address.value, entry->mask_base + offset);		break;	}	default:		break;	}}#ifdef CONFIG_IRQBALANCEstatic inline void move_msi(int vector){	if (!cpus_empty(pending_irq_balance_cpumask[vector])) {		set_msi_affinity(vector, pending_irq_balance_cpumask[vector]);		cpus_clear(pending_irq_balance_cpumask[vector]);	}}#endif /* CONFIG_IRQBALANCE */#endif /* CONFIG_SMP */static void mask_MSI_irq(unsigned int vector){	msi_set_mask_bit(vector, 1);}static void unmask_MSI_irq(unsigned int vector){	msi_set_mask_bit(vector, 0);}static unsigned int startup_msi_irq_wo_maskbit(unsigned int vector){	struct msi_desc *entry;	unsigned long flags;	spin_lock_irqsave(&msi_lock, flags);	entry = msi_desc[vector];	if (!entry || !entry->dev) {		spin_unlock_irqrestore(&msi_lock, flags);		return 0;	}	entry->msi_attrib.state = 1;	/* Mark it active */	spin_unlock_irqrestore(&msi_lock, flags);	return 0;	/* never anything pending */}static void release_msi(unsigned int vector);static void shutdown_msi_irq(unsigned int vector){	release_msi(vector);}#define shutdown_msi_irq_wo_maskbit	shutdown_msi_irqstatic void enable_msi_irq_wo_maskbit(unsigned int vector) {}static void disable_msi_irq_wo_maskbit(unsigned int vector) {}static void ack_msi_irq_wo_maskbit(unsigned int vector) {}static void end_msi_irq_wo_maskbit(unsigned int vector){	move_msi(vector);	ack_APIC_irq();}static unsigned int startup_msi_irq_w_maskbit(unsigned int vector){	struct msi_desc *entry;	unsigned long flags;	spin_lock_irqsave(&msi_lock, flags);	entry = msi_desc[vector];	if (!entry || !entry->dev) {		spin_unlock_irqrestore(&msi_lock, flags);		return 0;	}	entry->msi_attrib.state = 1;	/* Mark it active */	spin_unlock_irqrestore(&msi_lock, flags);	unmask_MSI_irq(vector);	return 0;	/* never anything pending */}#define shutdown_msi_irq_w_maskbit	shutdown_msi_irq#define enable_msi_irq_w_maskbit	unmask_MSI_irq#define disable_msi_irq_w_maskbit	mask_MSI_irq#define ack_msi_irq_w_maskbit		mask_MSI_irqstatic void end_msi_irq_w_maskbit(unsigned int vector){	move_msi(vector);	unmask_MSI_irq(vector);	ack_APIC_irq();}/* * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices, * which implement the MSI-X Capability Structure. */static struct hw_interrupt_type msix_irq_type = {	.typename	= "PCI-MSI-X",	.startup	= startup_msi_irq_w_maskbit,	.shutdown	= shutdown_msi_irq_w_maskbit,	.enable		= enable_msi_irq_w_maskbit,	.disable	= disable_msi_irq_w_maskbit,	.ack		= ack_msi_irq_w_maskbit,	.end		= end_msi_irq_w_maskbit,	.set_affinity	= set_msi_irq_affinity};/* * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, * which implement the MSI Capability Structure with * Mask-and-Pending Bits. */static struct hw_interrupt_type msi_irq_w_maskbit_type = {	.typename	= "PCI-MSI",	.startup	= startup_msi_irq_w_maskbit,	.shutdown	= shutdown_msi_irq_w_maskbit,	.enable		= enable_msi_irq_w_maskbit,	.disable	= disable_msi_irq_w_maskbit,	.ack		= ack_msi_irq_w_maskbit,	.end		= end_msi_irq_w_maskbit,	.set_affinity	= set_msi_irq_affinity};/* * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, * which implement the MSI Capability Structure without * Mask-and-Pending Bits. */static struct hw_interrupt_type msi_irq_wo_maskbit_type = {	.typename	= "PCI-MSI",	.startup	= startup_msi_irq_wo_maskbit,	.shutdown	= shutdown_msi_irq_wo_maskbit,	.enable		= enable_msi_irq_wo_maskbit,	.disable	= disable_msi_irq_wo_maskbit,	.ack		= ack_msi_irq_wo_maskbit,	.end		= end_msi_irq_wo_maskbit,	.set_affinity	= set_msi_irq_affinity};static void msi_data_init(struct msg_data *msi_data,			  unsigned int vector){	memset(msi_data, 0, sizeof(struct msg_data));	msi_data->vector = (u8)vector;	msi_data->delivery_mode = MSI_DELIVERY_MODE;	msi_data->level = MSI_LEVEL_MODE;	msi_data->trigger = MSI_TRIGGER_MODE;}static void msi_address_init(struct msg_address *msi_address){	unsigned int	dest_id;	memset(msi_address, 0, sizeof(struct msg_address));	msi_address->hi_address = (u32)0;	dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT);	msi_address->lo_address.u.dest_mode = MSI_DEST_MODE;	msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE;	msi_address->lo_address.u.dest_id = dest_id;	msi_address->lo_address.value |= (MSI_TARGET_CPU << MSI_TARGET_CPU_SHIFT);}static int msi_free_vector(struct pci_dev* dev, int vector, int reassign);static int assign_msi_vector(void){	static int new_vector_avail = 1;	int vector;	unsigned long flags;	/*	 * msi_lock is provided to ensure that successful allocation of MSI	 * vector is assigned unique among drivers.	 */	spin_lock_irqsave(&msi_lock, flags);	if (!new_vector_avail) {		int free_vector = 0;		/*	 	 * vector_irq[] = -1 indicates that this specific vector is:	 	 * - assigned for MSI (since MSI have no associated IRQ) or	 	 * - assigned for legacy if less than 16, or	 	 * - having no corresponding 1:1 vector-to-IOxAPIC IRQ mapping	 	 * vector_irq[] = 0 indicates that this vector, previously		 * assigned for MSI, is freed by hotplug removed operations.		 * This vector will be reused for any subsequent hotplug added		 * operations.	 	 * vector_irq[] > 0 indicates that this vector is assigned for		 * IOxAPIC IRQs. This vector and its value provides a 1-to-1		 * vector-to-IOxAPIC IRQ mapping.	 	 */		for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) {			if (vector_irq[vector] != 0)				continue;			free_vector = vector;			if (!msi_desc[vector])			      	break;			else				continue;		}		if (!free_vector) {			spin_unlock_irqrestore(&msi_lock, flags);			return -EBUSY;		}		vector_irq[free_vector] = -1;		nr_released_vectors--;		spin_unlock_irqrestore(&msi_lock, flags);		if (msi_desc[free_vector] != NULL) {			struct pci_dev *dev;			int tail;			/* free all linked vectors before re-assign */			do {				spin_lock_irqsave(&msi_lock, flags);				dev = msi_desc[free_vector]->dev;				tail = msi_desc[free_vector]->link.tail;				spin_unlock_irqrestore(&msi_lock, flags);				msi_free_vector(dev, tail, 1);			} while (free_vector != tail);		}		return free_vector;	}	vector = assign_irq_vector(AUTO_ASSIGN);	last_alloc_vector = vector;	if (vector  == LAST_DEVICE_VECTOR)		new_vector_avail = 0;	spin_unlock_irqrestore(&msi_lock, flags);	return vector;}static int get_new_vector(void){	int vector;	if ((vector = assign_msi_vector()) > 0)		set_intr_gate(vector, interrupt[vector]);	return vector;}static int msi_init(void){	static int status = -ENOMEM;	if (!status)		return status;	if ((status = msi_cache_init()) < 0) {		pci_msi_enable = 0;		printk(KERN_INFO "WARNING: MSI INIT FAILURE\n");		return status;	}	last_alloc_vector = assign_irq_vector(AUTO_ASSIGN);	if (last_alloc_vector < 0) {		pci_msi_enable = 0;		printk(KERN_INFO "WARNING: ALL VECTORS ARE BUSY\n");

⌨️ 快捷键说明

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