linux/arch/powerpc/platforms/wsp/msi.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Michael Ellerman, IBM Corp.
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License
   6 * as published by the Free Software Foundation; either version
   7 * 2 of the License, or (at your option) any later version.
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/pci.h>
  12#include <linux/msi.h>
  13#include <linux/irq.h>
  14#include <linux/interrupt.h>
  15
  16#include "msi.h"
  17#include "ics.h"
  18#include "wsp_pci.h"
  19
  20/* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */
  21#define MSI_ADDR_32             0xFFFF0000ul
  22#define MSI_ADDR_64             0x1000000000000000ul
  23
  24int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
  25{
  26        struct pci_controller *phb;
  27        struct msi_desc *entry;
  28        struct msi_msg msg;
  29        unsigned int virq;
  30        int hwirq;
  31
  32        phb = pci_bus_to_host(dev->bus);
  33        if (!phb)
  34                return -ENOENT;
  35
  36        entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
  37        if (entry->msi_attrib.is_64) {
  38                msg.address_lo = 0;
  39                msg.address_hi = MSI_ADDR_64 >> 32;
  40        } else {
  41                msg.address_lo = MSI_ADDR_32;
  42                msg.address_hi = 0;
  43        }
  44
  45        list_for_each_entry(entry, &dev->msi_list, list) {
  46                hwirq = wsp_ics_alloc_irq(phb->dn, 1);
  47                if (hwirq < 0) {
  48                        dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n");
  49                        return hwirq;
  50                }
  51
  52                virq = irq_create_mapping(NULL, hwirq);
  53                if (virq == NO_IRQ) {
  54                        dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n");
  55                        return -1;
  56                }
  57
  58                dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n",
  59                        hwirq, virq);
  60
  61                wsp_ics_set_msi_chip(virq);
  62                irq_set_msi_desc(virq, entry);
  63                msg.data = hwirq & XIVE_ADDR_MASK;
  64                write_msi_msg(virq, &msg);
  65        }
  66
  67        return 0;
  68}
  69
  70void wsp_teardown_msi_irqs(struct pci_dev *dev)
  71{
  72        struct pci_controller *phb;
  73        struct msi_desc *entry;
  74        int hwirq;
  75
  76        phb = pci_bus_to_host(dev->bus);
  77
  78        dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n");
  79
  80        list_for_each_entry(entry, &dev->msi_list, list) {
  81                if (entry->irq == NO_IRQ)
  82                        continue;
  83
  84                irq_set_msi_desc(entry->irq, NULL);
  85                wsp_ics_set_std_chip(entry->irq);
  86
  87                hwirq = virq_to_hw(entry->irq);
  88                /* In this order to avoid racing with irq_create_mapping() */
  89                irq_dispose_mapping(entry->irq);
  90                wsp_ics_free_irq(phb->dn, hwirq);
  91        }
  92}
  93
  94void wsp_setup_phb_msi(struct pci_controller *phb)
  95{
  96        /* Create a single MVE at offset 0 that matches everything */
  97        out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT);
  98        out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
  99
 100        ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
 101        ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
 102}
 103