Re: PCI master abort with USB3.0 card

Pratyush Anand <[email protected]>
Newsgroups gmane.linux.ports.arm.general
Message-ID <[email protected]>
Hello Amit,

Card is from NEC having controller NEC D720200.

CPU is cortexa9.


Please also find code for PCIE host support in my platform.

Regards
Pratyush

---------------------------------------------------------------------------------------------

/*
 * arch/arm/mach-spear13xx/pcie.c
 *
 * PCIe functions for SPEAr 13xx SoCs
 *
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/mbus.h>
#include <linux/sched.h>
#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <asm/mach/irq.h>
#include <mach/pcie.h>
#include <mach/irqs.h>
#include <mach/misc_regs.h>
#define NUM_PCIE_PORTS 3
/* Sum of all these space can maximum be 256MB*/
#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
/* In current implementation address translation is done using IN0 only.
 * So IN1 start address and IN0 end address has been kept same
*/
#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
#define IN_CFG0_SIZE (1 * 1024 * 1024 - 1)
#define IN_CFG1_SIZE (1 * 1024 * 1024 - 1)
#define IN_MSG_SIZE (1 * 1024 * 1024 - 1)
#define MAX_LINK_UP_WAIT_JIFFIES 10
static struct pcie_port pcie_port[NUM_PCIE_PORTS];
static u32 spr_pcie_base[NUM_PCIE_PORTS] = {
 SPEAR13XX_PCIE0_BASE,
 SPEAR13XX_PCIE1_BASE,
 SPEAR13XX_PCIE2_BASE,
};
static u32 spr_pcie_app_base[NUM_PCIE_PORTS] = {
 SPEAR13XX_PCIE0_APP_BASE,
 SPEAR13XX_PCIE1_APP_BASE,
 SPEAR13XX_PCIE2_APP_BASE,
};
/* Keeping all DDR area of 256MB accesible for inbound transaction */
#define INBOUND_ADDR_MASK 0xFFFFFFF
#ifdef CONFIG_PCI_MSI
static DECLARE_BITMAP(msi_irq_in_use[NUM_PCIE_PORTS], SPEAR_NUM_MSI_IRQS);
static unsigned int spear_msi_data[NUM_PCIE_PORTS];
static void spear13xx_msi_init(struct pcie_port *pp);
#endif
static void spear_pcie_int_handler(unsigned int irq, struct irq_desc *desc);
static void enable_dbi_access(struct pcie_app_reg *app_reg)
{
 /* Enable DBI access */
 writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
   &app_reg->slv_armisc);
 writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
   &app_reg->slv_awmisc);
}
static void disable_dbi_access(struct pcie_app_reg *app_reg)
{
 /* disable DBI access */
 writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
   &app_reg->slv_armisc);
 writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
   &app_reg->slv_awmisc);
}
static void spear_dbi_read_reg(struct pcie_port *pp, int where, int size,
  u32 *val)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *) pp->va_app_base;
 u32 va_address;
 /* Enable DBI access */
 enable_dbi_access(app_reg);
 va_address = (u32)pp->va_dbi_base + (where & ~0x3);
 *val = readl(va_address);
 if (size == 1)
  *val = (*val >> (8 * (where & 3))) & 0xff;
 else if (size == 2)
  *val = (*val >> (8 * (where & 3))) & 0xffff;
 /* Disable DBI access */
 disable_dbi_access(app_reg);
}
static void spear_dbi_write_reg(struct pcie_port *pp, int where, int size,
  u32 val)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *) pp->va_app_base;
 u32 va_address;
 /* Enable DBI access */
 enable_dbi_access(app_reg);
 va_address = (u32)pp->va_dbi_base + (where & ~0x3);
 if (size == 4)
  writel(val, va_address);
 else if (size == 2)
  writew(val, va_address + (where & 2));
 else if (size == 1)
  writeb(val, va_address + (where & 3));
 /* Disable DBI access */
 disable_dbi_access(app_reg);
}
static int spear13xx_pcie_link_up(void __iomem *va_app_base)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *) va_app_base;
 unsigned long deadline = jiffies + MAX_LINK_UP_WAIT_JIFFIES;
 do {
  if (readl(&app_reg->app_status_1) &
    ((u32)1 << XMLH_LINK_UP_ID))
   return 1;
  cond_resched();
 } while (!time_after_eq(jiffies, deadline));
 return 0;
}
static void spear13xx_pcie_host_init(struct pcie_port *pp)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
 /*setup registers for outbound translation */
 writel(pp->base, &app_reg->in0_mem_addr_start);
 writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
   &app_reg->in0_mem_addr_limit);
 writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
 writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
   &app_reg->in1_mem_addr_limit);
 writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
 writel(app_reg->in_io_addr_start + IN_IO_SIZE,
   &app_reg->in_io_addr_limit);
 writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
 writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
   &app_reg->in_cfg0_addr_limit);
 writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
 writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
   &app_reg->in_cfg1_addr_limit);
 writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
 writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
   &app_reg->in_msg_addr_limit);
 writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
 writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
 writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
 /*setup registers for inbound translation */
 writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
 writel(0, &app_reg->pim0_mem_addr_start);
 writel(0, &app_reg->pim1_mem_addr_start);
 spear_dbi_write_reg(pp, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
 spear_dbi_write_reg(pp, PCI_BASE_ADDRESS_0, 4, 0);
 writel(0x0, &app_reg->pim_io_addr_start);
 writel(0x0, &app_reg->pim_io_addr_start);
 writel(0x0, &app_reg->pim_rom_addr_start);
 writel(DEVICE_TYPE_RC | (1 << MISCTRL_EN_ID)
   | (1 << APP_LTSSM_ENABLE_ID)
   | ((u32)1 << REG_TRANSLATION_ENABLE),
   &app_reg->app_ctrl_0);
}
static void __init spear13xx_pcie_preinit(void)
{
 int i;
 struct pcie_port *pp;
 struct pcie_app_reg *app_reg;
 for (i = 0; i < NUM_PCIE_PORTS; i++) {
  pp = pcie_port + i;
  app_reg = (struct pcie_app_reg *) (pp->va_app_base);
  if (!spear13xx_pcie_port_is_host(i))
   continue;
  snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
   "PCIe %d MEM", pp->port);
  pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
  pp->res[0].name = pp->mem_space_name;
  pp->res[0].start = app_reg->in0_mem_addr_start;
  pp->res[0].end = app_reg->in0_mem_addr_limit;
  pp->res[0].flags = IORESOURCE_MEM;
  snprintf(pp->io_space_name, sizeof(pp->io_space_name),
   "PCIe %d I/O", pp->port);
  pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
  pp->res[1].name = pp->io_space_name;
  pp->res[1].start = app_reg->in_io_addr_start;
  pp->res[1].end = app_reg->in_io_addr_limit;
  pp->res[1].flags = IORESOURCE_IO;
  if (request_resource(&iomem_resource, &pp->res[0]))
   panic("can't allocate PCIe I/O space");
  if (request_resource(&iomem_resource, &pp->res[1]))
   panic("can't allocate PCIe MEM space");
 }
}
static int __init spear13xx_pcie_setup(int nr, struct pci_sys_data *sys)
{
 struct pcie_port *pp;
 u32 val = 0;
 if (nr >= NUM_PCIE_PORTS)
  return 0;
 if (!spear13xx_pcie_port_is_host(nr))
  return 0;
 pp = &pcie_port[nr];
 if (!spear13xx_pcie_link_up((void __iomem *)pp->va_app_base))
  return 0;
 pp->root_bus_nr = sys->busnr;
 /* Generic PCIe unit setup.*/
 /* Enable own BME. It is necessary to enable own BME to do a
  * memory transaction on a downstream device
  */
 spear_dbi_read_reg(pp, PCI_COMMAND, 2, &val);
 val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
   | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
 spear_dbi_write_reg(pp, PCI_COMMAND, 2, val);
 /* Need to come back here*/
 sys->resource[0] = &pp->res[0];
 sys->resource[1] = &pp->res[1];
 sys->resource[2] = NULL;
 return 1;
}
static struct pcie_port *bus_to_port(int bus)
{
 int i;
 for (i = NUM_PCIE_PORTS - 1; i >= 0; i--) {
  int rbus = pcie_port[i].root_bus_nr;
  if (!spear13xx_pcie_port_is_host(i))
   continue;
  if (rbus != -1 && rbus <= bus)
   break;
 }
 return i >= 0 ? pcie_port + i : NULL;
}
static int pcie_valid_config(struct pcie_port *pp, int bus, int dev)
{
 /*If there is no link, then there is no device*/
 if (!spear13xx_pcie_link_up((void __iomem *)pp->va_app_base))
  return 0;
 /*
  * Don't go out when trying to access nonexisting devices
  * on the local bus.
  * we have only one slot on each root port.
  */
 if (bus == pp->root_bus_nr && dev > 0)
  return 0;
 return 1;
}
static int spear13xx_pcie_rd_conf(struct pcie_port *pp, struct pci_bus *bus,
  u32 devfn, int where, int size, u32 *val)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *) pp->va_app_base;
 u32 address = (u32)pp->va_cfg0_base | (PCI_FUNC(devfn) << 16)
  | (where & 0xFFFC);
 writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
   &app_reg->pom_cfg0_addr_start);
 writel(readl(&app_reg->slv_armisc) & ~(AXI_OP_TYPE_MASK),
   &app_reg->slv_armisc);
 writel(readl(&app_reg->slv_armisc) | AXI_OP_TYPE_CONFIG_RDRW_TYPE0,
   &app_reg->slv_armisc);
 *val = readl(address);
 if (size == 1)
  *val = (*val >> (8 * (where & 3))) & 0xff;
 else if (size == 2)
  *val = (*val >> (8 * (where & 3))) & 0xffff;
 writel(readl(&app_reg->slv_armisc) & ~(AXI_OP_TYPE_MASK),
   &app_reg->slv_armisc);
 return PCIBIOS_SUCCESSFUL;
}
static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
   int size, u32 *val)
{
 struct pcie_port *pp = bus_to_port(bus->number);
 unsigned long flags;
 int ret;
 if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) {
  *val = 0xffffffff;
  return PCIBIOS_DEVICE_NOT_FOUND;
 }
 spin_lock_irqsave(&pp->conf_lock, flags);
 ret = spear13xx_pcie_rd_conf(pp, bus, devfn, where, size, val);
 spin_unlock_irqrestore(&pp->conf_lock, flags);
 return ret;
}
static int spear13xx_pcie_wr_conf(struct pcie_port *pp, struct pci_bus *bus,
  u32 devfn, int where, int size, u32 val)
{
 int ret = PCIBIOS_SUCCESSFUL;
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *) pp->va_app_base;
 u32 address = (u32)pp->va_cfg0_base | (PCI_FUNC(devfn) << 16)
   | (where & 0xFFFC);
 writel((bus->number << 24) | (PCI_SLOT(devfn) << 19),
   &app_reg->pom_cfg0_addr_start);
 writel(readl(&app_reg->slv_awmisc) & ~(AXI_OP_TYPE_MASK),
   &app_reg->slv_awmisc);
 writel(readl(&app_reg->slv_awmisc) | AXI_OP_TYPE_CONFIG_RDRW_TYPE0,
   &app_reg->slv_awmisc);
 if (size == 4)
  writel(val, address);
 else if (size == 2)
  writew(val, address + (where & 2));
 else if (size == 1)
  writeb(val, address + (where & 3));
 else
  ret = PCIBIOS_BAD_REGISTER_NUMBER;
 writel(readl(&app_reg->slv_awmisc) & ~(AXI_OP_TYPE_MASK),
   &app_reg->slv_awmisc);
 return ret;
}
static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
   int where, int size, u32 val)
{
 struct pcie_port *pp = bus_to_port(bus->number);
 unsigned long flags;
 int ret;
 if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0)
  return PCIBIOS_DEVICE_NOT_FOUND;
 spin_lock_irqsave(&pp->conf_lock, flags);
 ret = spear13xx_pcie_wr_conf(pp, bus, devfn, where, size, val);
 spin_unlock_irqrestore(&pp->conf_lock, flags);
 return ret;
}
static struct pci_ops pcie_ops = {
 .read = pcie_rd_conf,
 .write = pcie_wr_conf,
};
static struct pci_bus __init *
spear13xx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
{
 struct pci_bus *bus;
 if ((nr < NUM_PCIE_PORTS) && spear13xx_pcie_port_is_host(nr)) {
  bus = pci_scan_bus(sys->busnr, &pcie_ops, sys);
 } else {
  bus = NULL;
  BUG();
 }
 return bus;
}
static int __init spear13xx_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
{
 struct pcie_port *pp = bus_to_port(dev->bus->number);
 int irq = (SPEAR_INTX0_BASE + pp->port * SPEAR_NUM_INTX_IRQS + pin - 1);
 return irq;
}
static struct hw_pci spear13xx_pci __initdata = {
 .nr_controllers = NUM_PCIE_PORTS,
 .preinit = spear13xx_pcie_preinit,
 .swizzle = pci_std_swizzle,
 .setup  = spear13xx_pcie_setup,
 .scan  = spear13xx_pcie_scan_bus,
 .map_irq = spear13xx_pcie_map_irq,
};
void mask_intx_irq(unsigned int irq)
{
 int irq_offset = (irq - SPEAR_INTX0_BASE) % SPEAR_NUM_INTX_IRQS;
 int port = (irq - SPEAR_INTX0_BASE) / SPEAR_NUM_INTX_IRQS;
 struct pcie_port *pp = &pcie_port[port];
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
 switch (irq_offset) {
 case 0:
  writel(readl(&app_reg->int_mask) & ~INTA_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 1:
  writel(readl(&app_reg->int_mask) & ~INTB_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 2:
  writel(readl(&app_reg->int_mask) & ~INTC_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 3:
  writel(readl(&app_reg->int_mask) & ~INTD_CTRL_INT,
    &app_reg->int_mask);
  break;
 }
}
void unmask_intx_irq(unsigned int irq)
{
 int irq_offset = (irq - SPEAR_INTX0_BASE) % SPEAR_NUM_INTX_IRQS;
 int port = (irq - SPEAR_INTX0_BASE) / SPEAR_NUM_INTX_IRQS;
 struct pcie_port *pp = &pcie_port[port];
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
 switch (irq_offset) {
 case 0:
  writel(readl(&app_reg->int_mask) | INTA_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 1:
  writel(readl(&app_reg->int_mask) | INTB_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 2:
  writel(readl(&app_reg->int_mask) | INTC_CTRL_INT,
    &app_reg->int_mask);
  break;
 case 3:
  writel(readl(&app_reg->int_mask) | INTD_CTRL_INT,
    &app_reg->int_mask);
  break;
 }
}
static struct irq_chip spear13xx_intx_chip = {
 .name = "PCI-INTX",
 .mask = mask_intx_irq,
 .unmask = unmask_intx_irq,
};
static void spear13xx_int_init(struct pcie_port *pp)
{
 int i, irq;
 struct pcie_app_reg *app_reg;
 set_irq_chained_handler(IRQ_PCIE0 + pp->port, spear_pcie_int_handler);
#ifdef CONFIG_PCI_MSI
 spear13xx_msi_init(pp);
#endif
 /* Enbale INTX interrupt*/
 app_reg = (struct pcie_app_reg *)pp->va_app_base;
 writel(readl(&app_reg->int_mask) | INTA_CTRL_INT
   | INTB_CTRL_INT | INTC_CTRL_INT
   | INTD_CTRL_INT, &app_reg->int_mask);
 /* initilize INTX chip here only. MSI chip will be
  * initilized dynamically.*/
 irq = (SPEAR_INTX0_BASE + pp->port * SPEAR_NUM_INTX_IRQS);
 for (i = 0; i < SPEAR_NUM_INTX_IRQS; i++) {
  set_irq_chip_and_handler(irq + i, &spear13xx_intx_chip,
    handle_simple_irq);
  set_irq_flags(irq + i, IRQF_VALID);
 }
}
static void __init add_pcie_port(int port, u32 base, u32 app_base)
{
 struct pcie_port *pp = &pcie_port[port];
 struct pcie_app_reg *app_reg;
 pp->port = port;
 pp->root_bus_nr = -1;
 pp->base = (void __iomem *)base;
 pp->app_base = (void __iomem *)app_base;
 pp->va_app_base = (void __iomem *) ioremap(app_base, 0x200);
 if (!pp->va_app_base) {
  pr_err("error with ioremap in function %s\n", __func__);
  return;
 }
 pp->va_dbi_base = (void __iomem *) ioremap(base, 0x2000);
 if (!pp->va_dbi_base) {
  pr_err("error with ioremap in function %s\n", __func__);
  return;
 }
 spin_lock_init(&pp->conf_lock);
 memset(pp->res, 0, sizeof(pp->res));
 pr_info("spear13xx PCIe port %d\n", port);
 if (spear13xx_pcie_link_up((void __iomem *)pp->va_app_base)) {
  pr_info("link up in bios\n");
 } else {
  pr_info("link down in bios\n");
  spear13xx_pcie_host_init(pp);
  spear13xx_int_init(pp);
  app_reg = (struct pcie_app_reg *)pp->va_app_base;
  pp->va_cfg0_base = (void __iomem *)
   ioremap(app_reg->in_cfg0_addr_start, IN_CFG0_SIZE);
  if (!pp->va_cfg0_base) {
   pr_err("error with ioremap in function %s\n", __func__);
   return;
  }
 }
}
static int __init spear13xx_pcie_init(void)
{
 int port;
 struct clk *clk;
 for (port = 0; port < NUM_PCIE_PORTS; port++) {
  /* do not enable clock if it is PCIE0. Ideally , all controller
   * should have been independent from others with respect to
   * clock. But PCIE1 and 2 depends on PCIE0.So PCIE0 clk
   * is provided during board init.*/
  if (port == 1) {
   /* Ideally CFG Clock should have been also enabled
    * here. But it is done currently during board
    * init routne*/
   clk = clk_get_sys("pcie1", NULL);
   if (!clk) {
    pr_err("%s:couldn't get clk for pcie1\n",
      __func__);
    continue;
   }
   if (clk_enable(clk)) {
    pr_err("%s:couldn't enable clk for pcie1\n",
      __func__);
    continue;
   }
  } else if (port == 2) {
   /* Ideally CFG Clock should have been also enabled
    * here. But it is done currently during board
    * init routne*/
   clk = clk_get_sys("pcie2", NULL);
   if (!clk) {
    pr_err("%s:couldn't get clk for pcie2\n",
      __func__);
    continue;
   }
   if (clk_enable(clk)) {
    pr_err("%s:couldn't enable clk for pcie2\n",
      __func__);
    continue;
   }
  }
  if (spear13xx_pcie_port_is_host(port))
   add_pcie_port(port, spr_pcie_base[port],
     spr_pcie_app_base[port]);
 }
 pci_common_init(&spear13xx_pci);
 return 0;
}
subsys_initcall(spear13xx_pcie_init);
#ifdef CONFIG_PCI_MSI
/* MSI int handler
 */
static void handle_msi(struct pcie_port *pp)
{
 unsigned long val;
 int i, pos;
 for (i = 0; i < 8; i++) {
  spear_dbi_read_reg(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
    (u32 *)&val);
  if (val) {
   pos = 0;
   while ((pos = find_next_bit(&val, 32, pos)) != 32) {
    generic_handle_irq(SPEAR_MSI0_INT_BASE
     + pp->port * SPEAR_NUM_MSI_IRQS
     + (i * 32) + pos);
    pos++;
   }
  }
  spear_dbi_write_reg(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val);
 }
}
#else
static void handle_msi(struct pcie_port *pp)
{
}
#endif
static void spear_pcie_int_handler(unsigned int irq, struct irq_desc *desc)
{
 struct pcie_port *pp = &pcie_port[irq - IRQ_PCIE0];
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
 unsigned int status;
 status = readl(&app_reg->int_sts);
 desc->chip->ack(irq);
 if (status & MSI_CTRL_INT) {
  handle_msi(pp);
  writel(MSI_CTRL_INT, &app_reg->int_clr);
 }
 else if (status & INTA_CTRL_INT)
  generic_handle_irq(SPEAR_INTX0_BASE
    + pp->port * SPEAR_NUM_INTX_IRQS);
 else if (status & INTB_CTRL_INT)
  generic_handle_irq(SPEAR_INTX0_BASE
    + pp->port * SPEAR_NUM_INTX_IRQS + 1);
 else if (status & INTC_CTRL_INT)
  generic_handle_irq(SPEAR_INTX0_BASE
    + pp->port * SPEAR_NUM_INTX_IRQS + 2);
 else if (status & INTD_CTRL_INT)
  generic_handle_irq(SPEAR_INTX0_BASE
    + pp->port * SPEAR_NUM_INTX_IRQS + 3);
 else
  writel(status, &app_reg->int_clr);
 desc->chip->unmask(irq);
}
#ifdef CONFIG_PCI_MSI
static int find_valid_pos0(int port, int nvec, int pos, int *pos0)
{
 int flag = 1;
 do {
  pos = find_next_zero_bit(msi_irq_in_use[port],
    SPEAR_NUM_MSI_IRQS, pos);
  /*if you have reached to the end then get out from here.*/
  if (pos == SPEAR_NUM_MSI_IRQS)
   return -ENOSPC;
  /* Check if this position is at correct offset.nvec is always a
   * power of two. pos0 must be nvec bit alligned.
   */
  if (pos % nvec)
   pos += nvec - (pos % nvec);
  else
   flag = 0;
 } while (flag);
 *pos0 = pos;
 return 0;
}
static void spear13xx_msi_nop(unsigned int irq)
{
 return;
}
static struct irq_chip spear13xx_msi_chip = {
 .name = "PCI-MSI",
 .ack = spear13xx_msi_nop,
 .enable = unmask_msi_irq,
 .disable = mask_msi_irq,
 .mask = mask_msi_irq,
 .unmask = unmask_msi_irq,
};
/*
 * Dynamic irq allocate and deallocation
 */
static int get_irq(int nvec, struct msi_desc *desc, int *pos)
{
 int res, bit, irq, pos0, pos1, i;
 u32 val;
 struct pcie_port *pp = bus_to_port(desc->dev->bus->number);
 pos0 = find_first_zero_bit(msi_irq_in_use[pp->port],
   SPEAR_NUM_MSI_IRQS);
 if (pos0 % nvec) {
  if (find_valid_pos0(pp->port, nvec, pos0, &pos0))
   goto no_valid_irq;
 }
 if (nvec > 1) {
  pos1 = find_next_bit(msi_irq_in_use[pp->port],
    SPEAR_NUM_MSI_IRQS, pos0);
  /* there must be nvec number of consecutive free bits */
  while ((pos1 - pos0) < nvec) {
   if (find_valid_pos0(pp->port, nvec, pos1, &pos0))
    goto no_valid_irq;
   pos1 = find_next_bit(msi_irq_in_use[pp->port],
     SPEAR_NUM_MSI_IRQS, pos0);
  }
 }
 irq = (SPEAR_MSI0_INT_BASE + (pp->port * SPEAR_NUM_MSI_IRQS)) + pos0;;
 if ((irq + nvec) > (SPEAR_MSI0_INT_END
    + (pp->port * SPEAR_NUM_MSI_IRQS)))
  goto no_valid_irq;
 i = 0;
 while (i < nvec) {
  set_bit(pos0 + i, msi_irq_in_use[pp->port]);
  dynamic_irq_init(irq + i);
  set_irq_msi(irq + i, desc);
  set_irq_chip_and_handler(irq + i, &spear13xx_msi_chip,
    handle_simple_irq);
  /* Enable corresponding interrupt on MSI interrupt
   * controller.
   */
  res = ((pos0 + i) / 32) * 12;
  bit = (pos0 + i) % 32;
  spear_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
  val |= 1 << bit;
  spear_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
  i++;
 }
 *pos = pos0;
 return irq;
no_valid_irq:
 *pos = pos0;
 return -ENOSPC;
}
static void clean_irq(unsigned int irq)
{
 int res, bit, val, pos;
 struct irq_desc *desc = irq_to_desc(irq);
 struct pcie_port *pp = bus_to_port(desc->msi_desc->dev->bus->number);
 pos = irq - (SPEAR_MSI0_INT_BASE + (pp->port * SPEAR_NUM_MSI_IRQS));
 dynamic_irq_cleanup(irq);
 clear_bit(pos, msi_irq_in_use[pp->port]);
 /* Disable corresponding interrupt on MSI interrupt
  * controller.
  */
 res = (pos / 32) * 12;
 bit = pos % 32;
 spear_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
 val &= ~(1 << bit);
 spear_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
}
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
 int cvec, rvec, irq, pos;
 struct msi_msg msg;
 uint16_t control;
 struct pcie_port *pp = bus_to_port(pdev->bus->number);
 /*
  * Read the MSI config to figure out how many IRQs this device
  * wants.Most devices only want 1, which will give
  * configured_private_bits and request_private_bits equal 0.
  */
 pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
   &control);
 /*
  * If the number of private bits has been configured then use
  * that value instead of the requested number. This gives the
  * driver the chance to override the number of interrupts
  * before calling pci_enable_msi().
  */
 cvec = (control & PCI_MSI_FLAGS_QSIZE) >> 4;
 if (cvec == 0) {
  /* Nothing is configured, so use the hardware requested size */
  rvec = (control & PCI_MSI_FLAGS_QMASK) >> 1;
 } else {
  /*
   * Use the number of configured bits, assuming the
   * driver wanted to override the hardware request
   * value.
   */
  rvec = cvec;
 }
 /*
  * The PCI 2.3 spec mandates that there are at most 32
  * interrupts. If this device asks for more, only give it one.
  */
 if (rvec > 5)
  rvec = 0;
 irq = get_irq((1 << rvec), desc, &pos);
  if (irq < 0)
  return irq;
  /* Update the number of IRQs the device has available to it */
  control &= ~PCI_MSI_FLAGS_QSIZE;
  control |= rvec << 4;
  pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
    control);
  desc->msi_attrib.multiple = rvec;
 /* An EP will modify lower 8 bits(max) of msi data while
  * sending any msi interrupt
  */
 msg.address_hi = 0x0;
 msg.address_lo = __virt_to_phys((u32)(&spear_msi_data[pp->port]));
 msg.data = pos;
 write_msi_msg(irq, &msg);
 return 0;
}
void arch_teardown_msi_irq(unsigned int irq)
{
 clean_irq(irq);
}
static void spear13xx_msi_init(struct pcie_port *pp)
{
 struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->va_app_base;
 spear_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4,
   __virt_to_phys((u32)(&spear_msi_data[pp->port])));
 spear_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
 /* Enbale MSI interrupt*/
 writel(readl(&app_reg->int_mask) | MSI_CTRL_INT,
   &app_reg->int_mask);
}
#endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.