blob: cee9b79d334d07846bcfa9c046954bde00dcd91c [file] [log] [blame]
/*
* (C) Copyright 2011 Quantenna Communications Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <asm/board/platform.h>
#include <linux/version.h>
/**
* get_irq_msi- return the msi_desc of irq
*/
struct
msi_desc * get_irq_msi(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc) {
printk(KERN_ERR
"Trying to install msi data for IRQ%d\n", irq);
return NULL;
}
return desc->msi_desc;
}
/**
* set_irq_msi - Associated the irq with a MSI descriptor
*/
int
set_irq_msi(unsigned int irq, struct msi_desc *entry)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc) {
printk(KERN_ERR
"Trying to install msi data for IRQ%d\n", irq);
return -EINVAL;
}
if (irq != RUBY_IRQ_MSI) {
printk(KERN_ERR "set up msi irq(%u) out of expected\n", irq);
return -EINVAL;
}
if (desc->msi_desc != NULL) {
printk(KERN_WARNING "%s: overwriting previous msi entry of irq %u\n", __FUNCTION__, irq);
}
desc->msi_desc = entry;
if (entry)
entry->irq = irq;
return 0;
}
static void
handle_bad_irq(unsigned int irq, struct irq_desc *desc)
{
printk(KERN_ERR "unexpected IRQ %u\n", irq);
}
/**
* arch_teardown_msi_irq - cleanup a dynamically allocated msi irq
* @irq: irq number to teardown
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
#define PCIMSI_SPIN_LOCK_IRQSAVE(lock, flags) raw_spin_lock_irqsave(lock, flags)
#define PCIMSI_SPIN_UNLOCK_IRQSAVE(lock, flags) raw_spin_unlock_irqrestore(lock, flags)
#else
#define PCIMSI_SPIN_LOCK_IRQSAVE(lock, flags) spin_lock_irqsave(lock, flags)
#define PCIMSI_SPIN_UNLOCK_IRQSAVE(lock, flags) spin_unlock_irqrestore(lock, flags)
#endif
void
arch_teardown_msi_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc) {
WARN(1, KERN_ERR "Trying to cleanup invalid IRQ%d\n", irq);
return;
}
PCIMSI_SPIN_LOCK_IRQSAVE(&desc->lock, flags);
if (desc->action) {
PCIMSI_SPIN_UNLOCK_IRQSAVE(&desc->lock, flags);
WARN(1, KERN_ERR "Destroying IRQ%d without calling free_irq\n", irq);
return;
}
desc->msi_desc = NULL;
desc->handler_data = NULL;
desc->chip_data = NULL;
desc->handle_irq = handle_bad_irq;
desc->chip = NULL;
desc->name = NULL;
PCIMSI_SPIN_UNLOCK_IRQSAVE(&desc->lock, flags);
}
/**
* arch_setup_msi_irq - setup irq associated with a msi_desc
* and write the MSI message info to dev config space
* @desc:
*/
int
arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
printk("%s-%s called\n", __FILE__, __FUNCTION__);
struct msi_msg msg;
unsigned int irq;
irq = RUBY_IRQ_MSI;
set_irq_msi(irq, desc);
msg.address_hi = 0;
msg.address_lo = RUBY_PCIE_MSI_REGION;
msg.data = RUBY_MSI_DATA;
write_msi_msg(irq, &msg);
return 0;
}