Exemple #1
0
int pci_save_msix_state(struct pci_dev *dev)
{
	int pos;
	int temp;
	int vector, head, tail = 0;
	u16 control;
	struct pci_cap_saved_state *save_state;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos <= 0 || dev->no_msi)
		return 0;

	/* save the capability */
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSIX_FLAGS_ENABLE))
		return 0;
	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16),
		GFP_KERNEL);
	if (!save_state) {
		printk(KERN_ERR "Out of memory in pci_save_msix_state\n");
		return -ENOMEM;
	}
	*((u16 *)&save_state->data[0]) = control;

	/* save the table */
	temp = dev->irq;
	if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
		kfree(save_state);
		return -EINVAL;
	}

	vector = head = dev->irq;
	while (head != tail) {
		int j;
		void __iomem *base;
		struct msi_desc *entry;

		entry = msi_desc[vector];
		base = entry->mask_base;
		j = entry->msi_attrib.entry_nr;

		entry->address_lo_save =
			readl(base + j * PCI_MSIX_ENTRY_SIZE +
			      PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
		entry->address_hi_save =
			readl(base + j * PCI_MSIX_ENTRY_SIZE +
			      PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
		entry->data_save =
			readl(base + j * PCI_MSIX_ENTRY_SIZE +
			      PCI_MSIX_ENTRY_DATA_OFFSET);

		tail = msi_desc[vector]->link.tail;
		vector = tail;
	}
	dev->irq = temp;

	save_state->cap_nr = PCI_CAP_ID_MSIX;
	pci_add_saved_cap(dev, save_state);
	return 0;
}
Exemple #2
0
/**
 * pci_enable_msi - 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 vector upon its software driver call to request for
 * MSI mode enabled on its hardware device function. A return of zero
 * indicates the successful setup of an entry zero with the new MSI
 * vector or non-zero for otherwise.
 **/
int pci_enable_msi(struct pci_dev* dev)
{
    int pos, temp, status = -EINVAL;
    u16 control;

    if (!pci_msi_enable || !dev)
        return status;

    if (dev->no_msi)
        return status;

    temp = dev->irq;

    if ((status = msi_init()) < 0)
        return status;

    if (!(pos = pci_find_capability(dev, PCI_CAP_ID_MSI)))
        return -EINVAL;

    pci_read_config_word(dev, msi_control_reg(pos), &control);
    if (control & PCI_MSI_FLAGS_ENABLE)
        return 0;			/* Already in MSI mode */

    if (!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
        /* Lookup Sucess */
        unsigned long flags;

        spin_lock_irqsave(&msi_lock, flags);
        if (!vector_irq[dev->irq]) {
            msi_desc[dev->irq]->msi_attrib.state = 0;
            vector_irq[dev->irq] = -1;
            nr_released_vectors--;
            spin_unlock_irqrestore(&msi_lock, flags);
            enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
            return 0;
        }
        spin_unlock_irqrestore(&msi_lock, flags);
        dev->irq = temp;
    }
    /* Check whether driver already requested for MSI-X vectors */
    if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSIX)) > 0 &&
            !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
        printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
               "Device already has MSI-X vectors assigned\n",
               pci_name(dev));
        dev->irq = temp;
        return -EINVAL;
    }
    status = msi_capability_init(dev);
    if (!status) {
        if (!pos)
            nr_reserved_vectors--;	/* Only MSI capable */
        else if (nr_msix_devices > 0)
            nr_msix_devices--;	/* Both MSI and MSI-X capable,
						   but choose enabling MSI */
    }

    return status;
}
Exemple #3
0
void disable_msi_mode(struct pci_dev *dev, int pos, int type)
{
    u16 control;

    pci_read_config_word(dev, msi_control_reg(pos), &control);
    if (type == PCI_CAP_ID_MSI) {
        /* Set enabled bits to single MSI & enable MSI_enable bit */
        msi_disable(control);
        pci_write_config_word(dev, msi_control_reg(pos), control);
    } else {
        msix_disable(control);
        pci_write_config_word(dev, msi_control_reg(pos), control);
    }
    if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
        /* PCI Express Endpoint device detected */
        pci_intx(dev, 1);  /* enable intx */
    }
}
Exemple #4
0
static void disable_msi_mode(struct pci_dev *dev, int pos, int type)
{
	u16 control;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (type == PCI_CAP_ID_MSI) {
		/* Set enabled bits to single MSI & enable MSI_enable bit */
		msi_disable(control);
		pci_write_config_word(dev, msi_control_reg(pos), control);
	} else {
		msix_disable(control);
		pci_write_config_word(dev, msi_control_reg(pos), control);
	}
    	if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
		/* PCI Express Endpoint device detected */
		u16 cmd;
		pci_read_config_word(dev, PCI_COMMAND, &cmd);
		cmd &= ~PCI_COMMAND_INTX_DISABLE;
		pci_write_config_word(dev, PCI_COMMAND, cmd);
	}
}
Exemple #5
0
void pci_restore_msix_state(struct pci_dev *dev)
{
	u16 save;
	int pos;
	int vector, head, tail = 0;
	void __iomem *base;
	int j;
	struct msi_desc *entry;
	int temp;
	struct pci_cap_saved_state *save_state;

	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX);
	if (!save_state)
		return;
	save = *((u16 *)&save_state->data[0]);
	pci_remove_saved_cap(save_state);
	kfree(save_state);

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos <= 0)
		return;

	/* route the table */
	temp = dev->irq;
	if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX))
		return;
	vector = head = dev->irq;
	while (head != tail) {
		entry = msi_desc[vector];
		base = entry->mask_base;
		j = entry->msi_attrib.entry_nr;

		writel(entry->address_lo_save,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
		writel(entry->address_hi_save,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
		writel(entry->data_save,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_DATA_OFFSET);

		tail = msi_desc[vector]->link.tail;
		vector = tail;
	}
	dev->irq = temp;

	pci_write_config_word(dev, msi_control_reg(pos), save);
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
}
Exemple #6
0
/**
 * 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 vector, 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 vector or non-zero for otherwise.
 **/
static int msi_capability_init(struct pci_dev *dev)
{
	int status;
	struct msi_desc *entry;
	int pos, vector;
	u16 control;

   	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;

	vector = get_msi_vector(dev);
	if (vector < 0) {
		kmem_cache_free(msi_cachep, entry);
		return -EBUSY;
	}
	entry->link.head = vector;
	entry->link.tail = vector;
	entry->msi_attrib.type = PCI_CAP_ID_MSI;
	entry->msi_attrib.state = 0;			/* Mark it not active */
	entry->msi_attrib.entry_nr = 0;
	entry->msi_attrib.maskbit = is_mask_bit_support(control);
	entry->msi_attrib.default_vector = dev->irq;	/* Save IOAPIC IRQ */
	dev->irq = vector;
	entry->dev = dev;
	if (is_mask_bit_support(control)) {
		entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
				is_64bit_address(control));
	}
	/* Replace with MSI handler */
	irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit);
	/* Configure MSI capability structure */
	status = msi_register_init(dev, entry);
	if (status != 0) {
		dev->irq = entry->msi_attrib.default_vector;
		kmem_cache_free(msi_cachep, entry);
		return status;
	}

	attach_msi_entry(entry, vector);
	/* Set MSI enabled bits	 */
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);

	return 0;
}
Exemple #7
0
void pci_disable_msix(struct pci_dev* dev)
{
	int pos, temp;
	u16 control;

	if (!pci_msi_enable)
		return;
	if (!dev)
		return;
	if (!dev->msix_enabled)
		return;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos) {
		pci_read_config_word(dev, msi_control_reg(pos), &control);
		if (control & PCI_MSIX_FLAGS_ENABLE)
			disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
	}

	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;
		dev->irq = temp;			/* Restore pin IRQ */
		while (head != tail) {
			spin_lock_irqsave(&msi_lock, flags);
			state = msi_desc[vector]->msi_attrib.state;
			tail = msi_desc[vector]->link.tail;
			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) {
			printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without "
			       "free_irq() on all MSI-X vectors\n",
			       pci_name(dev));
			BUG_ON(warning > 0);
		}
	}
}
Exemple #8
0
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_WARNING "PCI: %s: pci_disable_msix() called without "
                   "free_irq() on all MSI-X vectors\n",
                   pci_name(dev));
            BUG_ON(warning > 0);
        } else {
            dev->irq = temp;
            disable_msi_mode(dev,
                             pci_find_capability(dev, PCI_CAP_ID_MSIX),
                             PCI_CAP_ID_MSIX);

        }
    }
}
Exemple #9
0
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);

		}
	}
}
Exemple #10
0
void pci_disable_msi(struct pci_dev* dev)
{
	struct msi_desc *entry;
	int pos, default_vector;
	u16 control;
	unsigned long flags;

	if (!pci_msi_enable)
		return;
	if (!dev)
		return;
	if (!dev->msi_enabled)
		return;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (pos) {
		pci_read_config_word(dev, msi_control_reg(pos), &control);
		if (control & PCI_MSI_FLAGS_ENABLE)
			disable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
	}

	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_WARNING "PCI: %s: pci_disable_msi() called without "
		       "free_irq() on MSI vector %d\n",
		       pci_name(dev), dev->irq);
		BUG_ON(entry->msi_attrib.state > 0);
	} else {
		default_vector = entry->msi_attrib.default_vector;
		spin_unlock_irqrestore(&msi_lock, flags);
		msi_free_vector(dev, dev->irq, 0);

		/* Restore dev->irq to its default pin-assertion vector */
		dev->irq = default_vector;
	}
}
Exemple #11
0
static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry)
{
	int status;
	u32 address_hi;
	u32 address_lo;
	u32 data;
	int pos, vector = dev->irq;
	u16 control;

   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	pci_read_config_word(dev, msi_control_reg(pos), &control);

	/* Configure MSI capability structure */
	status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data);
	if (status < 0)
		return status;

	pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo);
	if (is_64bit_address(control)) {
		pci_write_config_dword(dev,
			msi_upper_address_reg(pos), address_hi);
		pci_write_config_word(dev,
			msi_data_reg(pos, 1), data);
	} else
		pci_write_config_word(dev,
			msi_data_reg(pos, 0), data);
	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);
	}

	return 0;
}
Exemple #12
0
void pci_disable_msi(struct pci_dev* dev)
{
	struct msi_desc *entry;
	int pos, default_vector;
	u16 control;
	unsigned long flags;

   	if (!dev || !(pos = pci_find_capability(dev, PCI_CAP_ID_MSI)))
		return;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSI_FLAGS_ENABLE))
		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);
	}
}
Exemple #13
0
int pci_save_msi_state(struct pci_dev *dev)
{
	int pos, i = 0;
	u16 control;
	struct pci_cap_saved_state *save_state;
	u32 *cap;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (pos <= 0 || dev->no_msi)
		return 0;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSI_FLAGS_ENABLE))
		return 0;

	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5,
		GFP_KERNEL);
	if (!save_state) {
		printk(KERN_ERR "Out of memory in pci_save_msi_state\n");
		return -ENOMEM;
	}
	cap = &save_state->data[0];

	pci_read_config_dword(dev, pos, &cap[i++]);
	control = cap[0] >> 16;
	pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]);
	if (control & PCI_MSI_FLAGS_64BIT) {
		pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]);
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]);
	} else
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]);
	if (control & PCI_MSI_FLAGS_MASKBIT)
		pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]);
	save_state->cap_nr = PCI_CAP_ID_MSI;
	pci_add_saved_cap(dev, save_state);
	return 0;
}
Exemple #14
0
static int msi_free_vector(struct pci_dev* dev, int vector, int reassign)
{
    struct msi_desc *entry;
    int head, entry_nr, type;
    void __iomem *base;
    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(base);
        }
    }

    return 0;
}
Exemple #15
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
 * single MSI-X vector. A return of zero indicates the successful setup of
 * requested MSI-X entries with allocated vectors or non-zero for otherwise.
 **/
static int msix_capability_init(struct pci_dev *dev,
				struct msix_entry *entries, int nvec)
{
	struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
	u32 address_hi;
	u32 address_lo;
	u32 data;
	int status;
	int vector, pos, i, j, nr_entries, temp = 0;
	unsigned long phys_addr;
	u32 table_offset;
 	u16 control;
	u8 bir;
	void __iomem *base;

   	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	/* Request & Map MSI-X table region */
 	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);
	table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
	phys_addr = pci_resource_start (dev, bir) + table_offset;
	base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
	if (base == NULL)
		return -ENOMEM;

	/* MSI-X Table Initialization */
	for (i = 0; i < nvec; i++) {
		entry = alloc_msi_entry();
		if (!entry)
			break;
		vector = get_msi_vector(dev);
		if (vector < 0) {
			kmem_cache_free(msi_cachep, entry);
			break;
		}

 		j = entries[i].entry;
 		entries[i].vector = vector;
		entry->msi_attrib.type = PCI_CAP_ID_MSIX;
 		entry->msi_attrib.state = 0;		/* Mark it not active */
		entry->msi_attrib.entry_nr = j;
		entry->msi_attrib.maskbit = 1;
		entry->msi_attrib.default_vector = dev->irq;
		entry->dev = dev;
		entry->mask_base = base;
		if (!head) {
			entry->link.head = vector;
			entry->link.tail = vector;
			head = entry;
		} else {
			entry->link.head = temp;
			entry->link.tail = tail->link.tail;
			tail->link.tail = vector;
			head->link.head = vector;
		}
		temp = vector;
		tail = entry;
		/* Replace with MSI-X handler */
		irq_handler_init(PCI_CAP_ID_MSIX, vector, 1);
		/* Configure MSI-X capability structure */
		status = msi_ops->setup(dev, vector,
					&address_hi,
					&address_lo,
					&data);
		if (status < 0)
			break;

		writel(address_lo,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
		writel(address_hi,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
		writel(data,
			base + j * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_DATA_OFFSET);
		attach_msi_entry(entry, vector);
	}
	if (i != nvec) {
		i--;
		for (; i >= 0; i--) {
			vector = (entries + i)->vector;
			msi_free_vector(dev, vector, 0);
			(entries + i)->vector = 0;
		}
		return -EBUSY;
	}
	/* Set MSI-X enabled bits */
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);

	return 0;
}
Exemple #16
0
/**
 * 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 function
 * 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_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
                   "called without free_irq() on MSI vector %d\n",
                   pci_name(dev), 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;
        void __iomem *base = NULL;

        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(base);
            printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
                   "called without free_irq() on all MSI-X vectors\n",
                   pci_name(dev));
            BUG_ON(warning > 0);
        }
        dev->irq = temp;		/* Restore IOAPIC IRQ */
    }
}
Exemple #17
0
/**
 * 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 vector, 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 vector or non-zero for otherwise.
 **/
static int msi_capability_init(struct pci_dev *dev)
{
    struct msi_desc *entry;
    struct msg_address address;
    struct msg_data data;
    int pos, vector;
    u16 control;

    pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
    pci_read_config_word(dev, msi_control_reg(pos), &control);
    /* MSI Entry Initialization */
    if (!(entry = alloc_msi_entry()))
        return -ENOMEM;

    if ((vector = get_msi_vector(dev)) < 0) {
        kmem_cache_free(msi_cachep, entry);
        return -EBUSY;
    }
    entry->link.head = vector;
    entry->link.tail = vector;
    entry->msi_attrib.type = PCI_CAP_ID_MSI;
    entry->msi_attrib.state = 0;			/* Mark it not active */
    entry->msi_attrib.entry_nr = 0;
    entry->msi_attrib.maskbit = is_mask_bit_support(control);
    entry->msi_attrib.default_vector = dev->irq;	/* Save IOAPIC IRQ */
    dev->irq = vector;
    entry->dev = dev;
    if (is_mask_bit_support(control)) {
        entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
                           is_64bit_address(control));
    }
    /* Replace with MSI handler */
    irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit);
    /* Configure MSI capability structure */
    msi_address_init(&address);
    msi_data_init(&data, vector);
    entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >>
                                      MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK);
    pci_write_config_dword(dev, msi_lower_address_reg(pos),
                           address.lo_address.value);
    if (is_64bit_address(control)) {
        pci_write_config_dword(dev,
                               msi_upper_address_reg(pos), address.hi_address);
        pci_write_config_word(dev,
                              msi_data_reg(pos, 1), *((u32*)&data));
    } else
        pci_write_config_word(dev,
                              msi_data_reg(pos, 0), *((u32*)&data));
    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);
    }
    attach_msi_entry(entry, vector);
    /* Set MSI enabled bits	 */
    enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);

    return 0;
}
Exemple #18
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
 * single MSI-X vector. A return of zero indicates the successful setup of
 * requested MSI-X entries with allocated vectors or non-zero for otherwise.
 **/
static int msix_capability_init(struct pci_dev *dev,
                                struct msix_entry *entries, int nvec)
{
    struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
    struct msg_address address;
    struct msg_data data;
    int vector, pos, i, j, nr_entries, temp = 0;
    u32 phys_addr, table_offset;
    u16 control;
    u8 bir;
    void __iomem *base;

    pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
    /* Request & Map MSI-X table region */
    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);
    base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
    if (base == NULL)
        return -ENOMEM;

    /* MSI-X Table Initialization */
    for (i = 0; i < nvec; i++) {
        entry = alloc_msi_entry();
        if (!entry)
            break;
        if ((vector = get_msi_vector(dev)) < 0)
            break;

        j = entries[i].entry;
        entries[i].vector = vector;
        entry->msi_attrib.type = PCI_CAP_ID_MSIX;
        entry->msi_attrib.state = 0;		/* Mark it not active */
        entry->msi_attrib.entry_nr = j;
        entry->msi_attrib.maskbit = 1;
        entry->msi_attrib.default_vector = dev->irq;
        entry->dev = dev;
        entry->mask_base = base;
        if (!head) {
            entry->link.head = vector;
            entry->link.tail = vector;
            head = entry;
        } else {
            entry->link.head = temp;
            entry->link.tail = tail->link.tail;
            tail->link.tail = vector;
            head->link.head = vector;
        }
        temp = vector;
        tail = entry;
        /* Replace with MSI-X handler */
        irq_handler_init(PCI_CAP_ID_MSIX, vector, 1);
        /* Configure MSI-X capability structure */
        msi_address_init(&address);
        msi_data_init(&data, vector);
        entry->msi_attrib.current_cpu =
            ((address.lo_address.u.dest_id >>
              MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK);
        writel(address.lo_address.value,
               base + j * PCI_MSIX_ENTRY_SIZE +
               PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
        writel(address.hi_address,
               base + j * PCI_MSIX_ENTRY_SIZE +
               PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
        writel(*(u32*)&data,
               base + j * PCI_MSIX_ENTRY_SIZE +
               PCI_MSIX_ENTRY_DATA_OFFSET);
        attach_msi_entry(entry, vector);
    }
    if (i != nvec) {
        i--;
        for (; i >= 0; i--) {
            vector = (entries + i)->vector;
            msi_free_vector(dev, vector, 0);
            (entries + i)->vector = 0;
        }
        return -EBUSY;
    }
    /* Set MSI-X enabled bits */
    enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);

    return 0;
}
Exemple #19
0
/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
 * @entries: 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 (!entries || pci_msi_supported(dev) < 0)
 		return -EINVAL;

	status = msi_init();
	if (status < 0)
		return status;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (!pos)
 		return -EINVAL;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	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;
	WARN_ON(!msi_lookup_vector(dev, PCI_CAP_ID_MSIX));

	/* 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 "PCI: %s: Can't enable MSI-X.  "
		       "Device already has an MSI vector assigned\n",
		       pci_name(dev));
		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;
	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);

	return status;
}
Exemple #20
0
/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
 * @entries: 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 "PCI: %s: Can't enable MSI-X.  "
               "Device already has an MSI vector assigned\n",
               pci_name(dev));
        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;
}
Exemple #21
0
/**
 * 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 */
	}
}