/*
* Copyright IBM Corp. 2012
*
* Author(s):
* Jan Glauber <jang@linux.vnet.ibm.com>
*
* The System z PCI code is a rewrite from a prototype by
* the following people (Kudoz!):
* Alexander Schmidt
* Christoph Raisch
* Hannes Hering
* Hoang-Nam Nguyen
* Jan-Bernd Themann
* Stefan Roscher
* Thomas Klein
*/
#define COMPONENT "zPCI"
#define pr_fmt(fmt) COMPONENT ": " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <asm/isc.h>
#include <asm/airq.h>
#include <asm/facility.h>
#include <asm/pci_insn.h>
#include <asm/pci_clp.h>
#include <asm/pci_dma.h>
#define DEBUG /* enable pr_debug */
#define SIC_IRQ_MODE_ALL 0
#define SIC_IRQ_MODE_SINGLE 1
#define ZPCI_NR_DMA_SPACES 1
#define ZPCI_MSI_VEC_BITS 6
#define ZPCI_NR_DEVICES CONFIG_PCI_NR_FUNCTIONS
/* list of all detected zpci devices */
LIST_HEAD(zpci_list);
EXPORT_SYMBOL_GPL(zpci_list);
DEFINE_MUTEX(zpci_list_lock);
EXPORT_SYMBOL_GPL(zpci_list_lock);
static struct pci_hp_callback_ops *hotplug_ops;
static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
static DEFINE_SPINLOCK(zpci_domain_lock);
struct callback {
irq_handler_t handler;
void *data;
};
struct zdev_irq_map {
unsigned long aibv; /* AI bit vector */
int msi_vecs; /* consecutive MSI-vectors used */
int __unused;
struct callback cb[ZPCI_NR_MSI_VECS]; /* callback handler array */
spinlock_t lock; /* protect callbacks against de-reg */
};
struct intr_bucket {
/* amap of adapters, one bit per dev, corresponds to one irq nr */
unsigned long *alloc;
/* AI summary bit, global page for all devices */
unsigned long *aisb;
/* pointer to aibv and callback data in zdev */
struct zdev_irq_map *imap[ZPCI_NR_DEVICES];
/* protects the whole bucket struct */
spinlock_t lock;
};
static struct intr_bucket *bucket;
/* Adapter local summary indicator */
static u8 *zpci_irq_si;
static atomic_t irq_retries = ATOMIC_INIT(0);
/* I/O Map */
static DEFINE_SPINLOCK(zpci_iomap_lock);
static DECLARE_BITMAP(zpci_iomap, ZPCI_IOMAP_MAX_ENTRIES);
struct zpci_iomap_entry *zpci_iomap_start;
EXPORT_SYMBOL_GPL(zpci_iomap_start);
/* highest irq summary bit */
static int __read_mostly aisb_max;
static struct kmem_cache *zdev_irq_cache;
static struct kmem_cache *zdev_fmb_cache;
static inline int irq_to_msi_nr(unsigned int irq)
{
return irq & ZPCI_MSI_MASK;
}
static inline int irq_to_dev_nr(unsigned int irq)
{
return irq >> ZPCI_MSI_VEC_BITS;
}
static inline struct zdev_irq_map *get_imap(unsigned int irq)
{
return bucket->imap[irq_to_dev_nr(irq)];
}
struct zpci_dev *get_zdev(struct pci_dev *pdev)
{
return (struct zpci_dev *) pdev->sysdata;
}
struct zpci_dev *get_zdev_by_fid(u32 fid)
{
struct zpci_dev *tmp, *zdev = NULL;
mutex_lock(&zpci_list_lock);
list_for_each_entry(tmp, &zpci_list, entry) {
if (tmp->fid == fid) {
zdev = tmp;
break;
}
}
mutex_unlock(&zpci_list_lock);
return zdev;
}
bool zpci_fid_present(u32 fid)
{
return (get_zdev_by_fid(fid) != NULL) ? true : false;
}
static struct zpci_dev *get_zdev_by_bus(struct pci_bus *bus)
{
return (bus && bus->sysdata) ? (struct zpci_dev *) bus->sysdata : NULL;
}
int pci_domain_nr(struct pci_bus *bus)
{
return ((struct zpci_dev *) bus->sysdata)->domain;
}
EXPORT_SYMBOL_GPL(pci_domain_nr);
int pci_proc_domain(struct pci_bus *bus)
{
return pci_domain_nr(bus);
}
EXPORT_SYMBOL_GPL(pci_proc_domain);
/* Modify PCI: Register adapter interruptions */
static int zpci_register_airq(struct zpci_dev *zdev, unsigned int aisb,
u64 aibv)
{
u64 req = ZPCI_CREATE_REQ(zdev->