/*
* Copyright 2012 Tilera Corporation. All Rights Reserved.
*
* 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, version 2.
*
* 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, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*/
#include <linux/kernel.h>
#include <linux/mmzone.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <asm/processor.h>
#include <asm/sections.h>
#include <asm/byteorder.h>
#include <gxio/iorpc_globals.h>
#include <gxio/kiorpc.h>
#include <gxio/trio.h>
#include <gxio/iorpc_trio.h>
#include <hv/drv_trio_intf.h>
#include <arch/sim.h>
/*
* This file contains the routines to search for PCI buses,
* enumerate the buses, and configure any attached devices.
*/
#define DEBUG_PCI_CFG 0
#if DEBUG_PCI_CFG
#define TRACE_CFG_WR(size, val, bus, dev, func, offset) \
pr_info("CFG WR %d-byte VAL %#x to bus %d dev %d func %d addr %u\n", \
size, val, bus, dev, func, offset & 0xFFF);
#define TRACE_CFG_RD(size, val, bus, dev, func, offset) \
pr_info("CFG RD %d-byte VAL %#x from bus %d dev %d func %d addr %u\n", \
size, val, bus, dev, func, offset & 0xFFF);
#else
#define TRACE_CFG_WR(...)
#define TRACE_CFG_RD(...)
#endif
static int pci_probe = 1;
/* Information on the PCIe RC ports configuration. */
static int pcie_rc[TILEGX_NUM_TRIO][TILEGX_TRIO_PCIES];
/*
* On some platforms with one or more Gx endpoint ports, we need to
* delay the PCIe RC port probe for a few seconds to work around
* a HW PCIe link-training bug. The exact delay is specified with
* a kernel boot argument in the form of "pcie_rc_delay=T,P,S",
* where T is the TRIO instance number, P is the port number and S is
* the delay in seconds. If the argument is specified, but the delay is
* not provided, the value will be DEFAULT_RC_DELAY.
*/
static int rc_delay[TILEGX_NUM_TRIO][TILEGX_TRIO_PCIES];
/* Default number of seconds that the PCIe RC port probe can be delayed. */
#define DEFAULT_RC_DELAY 10
/* The PCI I/O space size in each PCI domain. */
#define IO_SPACE_SIZE 0x10000
/* Provide shorter versions of some very long constant names. */
#define AUTO_CONFIG_RC \
TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC
#define AUTO_CONFIG_RC_G1 \
TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC_G1
#define AUTO_CONFIG_EP \
TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT
#define AUTO_CONFIG_EP_G1 \
TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT_G1
/* Array of the PCIe ports configuration info obtained from the BIB. */
struct pcie_trio_ports_property pcie_ports[TILEGX_NUM_TRIO];
/* Number of configured TRIO instances. */
int num_trio_shims;
/* All drivers share the TRIO contexts defined here. */
gxio_trio_context_t trio_contexts[TILEGX_NUM_TRIO];
/* Pointer to an array of PCIe RC controllers. */
struct pci_controller pci_controllers[TILEGX_NUM_TRIO * TILEGX_TRIO_PCIES];
int num_rc_controllers;
static struct pci_ops tile_cfg_ops;
/* Mask of CPUs that should receive PCIe interrupts. */
static struct cpumask intr_cpus_map;
/*
* Pick a CPU to receive and handle the PCIe interrupts, based on the IRQ #.
* For now, we simply send interrupts to non-dataplane CPUs.
* We may implement methods to allow user to specify the target CPUs,
* e.g. via boot arguments.
*/
static int tile_irq_cpu(int irq)
{
unsigned int count;
int i = 0;
int cpu;
count = cpumask_weight(&intr_cpus_map);
if (unlikely(count == 0)) {
pr_warn("intr_cpus_map empty, interrupts will be delivered to dataplane tiles\n");
return irq % (smp_height * smp_width);
}
count = irq % count;
for_each_cpu(cpu, &intr_cpus_map) {
if (i++ == count)
break;
}
return cpu;
}
/* Open a file descriptor to the TRIO shim. */
static int tile_pcie_open(int trio_index)
{
gxio_trio_context_t *context = &trio_contexts[trio_index];
int ret;
int mac;
/* This opens a file descriptor to the TRIO shim. */
ret = gxio_trio_init(context, trio_index);
if (ret < 0)
goto gxio_trio_init_failure;
/* Allocate an ASID for the kernel. */
ret = gxio_trio_alloc_asids(context, 1, 0, 0);
if (ret < 0) {
pr_err("PCI: ASID alloc failure on TRIO %d, give up\n",
trio_index);
goto asid_alloc_failure;
}
context->asid = ret;
#ifdef USE_SHARED_PCIE_CONFIG_REGION
/*
* Alloc a PIO region for config access, shared by all MACs per TRIO.
* This shouldn't fail since the kernel is supposed to the first
* client of the TRIO's PIO regions.
*/
ret = gxio_trio_alloc_pio_regions(context,
|