| /* |
| * Copyright IBM Corp. 2012 |
| * |
| * Author(s): |
| * Jan Glauber <jang@linux.vnet.ibm.com> |
| */ |
| |
| #define COMPONENT "zPCI" |
| #define pr_fmt(fmt) COMPONENT ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/err.h> |
| #include <linux/rculist.h> |
| #include <linux/hash.h> |
| #include <linux/pci.h> |
| #include <linux/msi.h> |
| #include <asm/hw_irq.h> |
| |
| /* mapping of irq numbers to msi_desc */ |
| static struct hlist_head *msi_hash; |
| static const unsigned int msi_hash_bits = 8; |
| #define MSI_HASH_BUCKETS (1U << msi_hash_bits) |
| #define msi_hashfn(nr) hash_long(nr, msi_hash_bits) |
| |
| static DEFINE_SPINLOCK(msi_map_lock); |
| |
| struct msi_desc *__irq_get_msi_desc(unsigned int irq) |
| { |
| struct msi_map *map; |
| |
| hlist_for_each_entry_rcu(map, |
| &msi_hash[msi_hashfn(irq)], msi_chain) |
| if (map->irq == irq) |
| return map->msi; |
| return NULL; |
| } |
| |
| int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag) |
| { |
| if (msi->msi_attrib.is_msix) { |
| int offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
| PCI_MSIX_ENTRY_VECTOR_CTRL; |
| msi->masked = readl(msi->mask_base + offset); |
| writel(flag, msi->mask_base + offset); |
| } else { |
| if (msi->msi_attrib.maskbit) { |
| int pos; |
| u32 mask_bits; |
| |
| pos = (long) msi->mask_base; |
| pci_read_config_dword(msi->dev, pos, &mask_bits); |
| mask_bits &= ~(mask); |
| mask_bits |= flag & mask; |
| pci_write_config_dword(msi->dev, pos, mask_bits); |
| } else { |
| return 0; |
| } |
| } |
| |
| msi->msi_attrib.maskbit = !!flag; |
| return 1; |
| } |
| |
| int zpci_setup_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi, |
| unsigned int nr, int offset) |
| { |
| struct msi_map *map; |
| struct msi_msg msg; |
| int rc; |
| |
| map = kmalloc(sizeof(*map), GFP_KERNEL); |
| if (map == NULL) |
| return -ENOMEM; |
| |
| map->irq = nr; |
| map->msi = msi; |
| zdev->msi_map[nr & ZPCI_MSI_MASK] = map; |
| INIT_HLIST_NODE(&map->msi_chain); |
| |
| pr_debug("%s hashing irq: %u to bucket nr: %llu\n", |
| __func__, nr, msi_hashfn(nr)); |
| hlist_add_head_rcu(&map->msi_chain, &msi_hash[msi_hashfn(nr)]); |
| |
| spin_lock(&msi_map_lock); |
| rc = irq_set_msi_desc(nr, msi); |
| if (rc) { |
| spin_unlock(&msi_map_lock); |
| hlist_del_rcu(&map->msi_chain); |
| kfree(map); |
| zdev->msi_map[nr & ZPCI_MSI_MASK] = NULL; |
| return rc; |
| } |
| spin_unlock(&msi_map_lock); |
| |
| msg.data = nr - offset; |
| msg.address_lo = zdev->msi_addr & 0xffffffff; |
| msg.address_hi = zdev->msi_addr >> 32; |
| write_msi_msg(nr, &msg); |
| return 0; |
| } |
| |
| void zpci_teardown_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi) |
| { |
| int irq = msi->irq & ZPCI_MSI_MASK; |
| struct msi_map *map; |
| |
| msi->msg.address_lo = 0; |
| msi->msg.address_hi = 0; |
| msi->msg.data = 0; |
| msi->irq = 0; |
| zpci_msi_set_mask_bits(msi, 1, 1); |
| |
| spin_lock(&msi_map_lock); |
| map = zdev->msi_map[irq]; |
| hlist_del_rcu(&map->msi_chain); |
| kfree(map); |
| zdev->msi_map[irq] = NULL; |
| spin_unlock(&msi_map_lock); |
| } |
| |
| /* |
| * The msi hash table has 256 entries which is good for 4..20 |
| * devices (a typical device allocates 10 + CPUs MSI's). Maybe make |
| * the hash table size adjustable later. |
| */ |
| int __init zpci_msihash_init(void) |
| { |
| unsigned int i; |
| |
| msi_hash = kmalloc(MSI_HASH_BUCKETS * sizeof(*msi_hash), GFP_KERNEL); |
| if (!msi_hash) |
| return -ENOMEM; |
| |
| for (i = 0; i < MSI_HASH_BUCKETS; i++) |
| INIT_HLIST_HEAD(&msi_hash[i]); |
| return 0; |
| } |
| |
| void __init zpci_msihash_exit(void) |
| { |
| kfree(msi_hash); |
| } |