blob: 60ebba937b0623b6e8c1be33b15014a60285d2b8 [file] [log] [blame]
/*
* linux/arch/arm/mach-comcerto/pcie-c2000.c
*
* Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
#if defined(CONFIG_PCI_MSI)
#include <linux/msi.h>
#endif
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/sizes.h>
#include <asm/mach/pci.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/mach/irq.h>
#include <mach/pcie-c2000.h>
#include <mach/serdes-c2000.h>
#include <mach/reset.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
//#define COMCERTO_PCIE_DEBUG
#ifdef CONFIG_PCI_MSI
static DECLARE_BITMAP(msi_irq_in_use[NUM_PCIE_PORTS], PCIE_NUM_MSI_IRQS);
static int comcerto_msi_init(struct pcie_port *pp);
#endif
int pcie_external_clk = 0;
static int __init get_pcie_clk_mode(char *str)
{
if (!strcmp(str, "yes"))
pcie_external_clk = 1;
return 1;
}
__setup("pcie_external_clk=", get_pcie_clk_mode);
int pcie_gen1_only = 0;
static int __init get_pcie_gen_mode(char *str)
{
if (!strcmp(str, "yes"))
pcie_gen1_only = 1;
return 1;
}
__setup("pcie_gen1_only=", get_pcie_gen_mode);
/* DWC PCIEe configuration register offsets on APB */
struct pcie_app_reg app_regs[MAX_PCIE_PORTS] = {
/* PCIe0 */
{
0x00000000,
0x00000004,
0x00000008,
0x0000000C,
0x00000010,
0x00000014,
0x00000018,
0x00000040,
0x00000044,
0x00000048,
0x00000058,
0x00000080,
0x00000084,
0x000000C0,
0x000000C4,
0x00000100,
0x00000104,
0x00000108,
0x0000010C
},
/* PCIe1 */
{
0x00000020,
0x00000024,
0x00000028,
0x0000002C,
0x00000030,
0x00000034,
0x00000038,
0x0000004C,
0x00000050,
0x00000054,
0x0000005C,
0x00000088,
0x0000008C,
0x000000C8,
0x000000CC,
0x00000110,
0x00000114,
0x00000118,
0x0000011C
}
};
/* Keeping all DDR area of 512MB accesible for inbound transaction */
#define INBOUND_ADDR_MASK 0x1FFFFFFF
#define PCIE_SETUP_iATU_IB_ENTRY( _pp, _view_port, _base, _limit, _ctl1, _ctl2, _target ) \
{\
comcerto_dbi_write_reg(_pp, PCIE_iATU_VIEW_PORT, 4, (u32)(_view_port|iATU_VIEW_PORT_IN_BOUND)); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, 0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_LOW, 4, (u32)_base); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_HIGH, 4, 0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_LIMIT, 4, (u32)((_base)+(_limit))); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_LOW, 4, (u32)_target); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_HIGH, 4, (u32)0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL1, 4, (u32)_ctl1); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, (u32)(_ctl2 |iATU_CTRL2_ID_EN) ); \
}
#define PCIE_SETUP_iATU_OB_ENTRY( _pp, _view_port, _base, _limit, _ctl1, _ctl2, _target ) \
{\
comcerto_dbi_write_reg(_pp, PCIE_iATU_VIEW_PORT, 4, (u32)_view_port); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, 0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_LOW, 4, (u32)_base); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_HIGH, 4, (u32)0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_LIMIT, 4, ((u32)((_base)+(_limit)))); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_LOW, 4, (u32)_target); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_HIGH, 4, (u32)0); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL1, 4, (u32)_ctl1); \
comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, (u32)(_ctl2 |iATU_CTRL2_ID_EN) ); \
}
#define MAX_LINK_UP_WAIT_JIFFIES HZ /* 1 Second */
static unsigned long pcie_cnf_base_addr[MAX_PCIE_PORTS] =
{ COMCERTO_AXI_PCIe0_BASE, COMCERTO_AXI_PCIe1_BASE };
static unsigned long pcie_remote_base_addr[MAX_PCIE_PORTS] =
{ COMCERTO_AXI_PCIe0_SLAVE_BASE, COMCERTO_AXI_PCIe1_SLAVE_BASE };
static int pcie_msi_base[MAX_PCIE_PORTS] =
{ PCIE0_MSI_INT_BASE, PCIE1_MSI_INT_BASE };
static int pcie_intx_base[MAX_PCIE_PORTS] =
{ PCIE0_INTX_BASE, PCIE1_INTX_BASE };
static int pcie_irqs[MAX_PCIE_PORTS] =
{ IRQ_PCIe0, IRQ_PCIe1 };
static struct pcie_port pcie_port[MAX_PCIE_PORTS];
static int pcie_port_is_host( int nr )
{
struct pcie_port *pp= &pcie_port[nr];
return ( pp->port_mode == PCIE_PORT_MODE_RC ) ? 1 : 0;
}
/**
* This function sets PCIe port mode.
*/
static void pcie_port_set_mode( int nr, int mode )
{
struct pcie_port *pp= &pcie_port[nr];
writel( (readl(pp->va_app_base + pp->app_regs->cfg0) & ~0xf) | mode, pp->va_app_base + pp->app_regs->cfg0);
}
/**
* This function returns the given port mode.
* @param nr PCIe Port number.
*/
static int pcie_port_get_mode( int nr )
{
struct pcie_port *pp= &pcie_port[nr];
return ( readl(pp->va_app_base + pp->app_regs->cfg0) &
DWC_CFG0_DEV_TYPE_MASK );
}
/**
* This function checks whether link is up or not.
* Returns true if link is up otherwise returns false.
* @param pp Pointer to PCIe Port control block.
*/
static int comcerto_pcie_link_up( struct pcie_port *pp )
{
unsigned long deadline = jiffies + MAX_LINK_UP_WAIT_JIFFIES;
do {
if (readl( pp->va_app_base + pp->app_regs->sts0 ) & STS0_RDLH_LINK_UP) {
return 1;
}
cond_resched();
} while (!time_after_eq(jiffies, deadline));
return 0;
}
/**
* bus_to_port().
*
*/
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 ( !pcie_port_is_host(i) )
continue;
if (rbus != -1 && rbus <= bus)
break;
}
return i >= 0 ? pcie_port + i : NULL;
}
/**
* This function is used to read DBI registers.
*/
static void comcerto_dbi_read_reg(struct pcie_port *pp, int where, int size,
u32 *val)
{
u32 va_address;
va_address = (u32)pp->va_dbi_base + (where & ~0x3);
*val = readl_relaxed(va_address);
if (size == 1)
*val = (*val >> (8 * (where & 3))) & 0xff;
else if (size == 2)
*val = (*val >> (8 * (where & 3))) & 0xffff;
}
/**
* This function is used to write into DBI registers.
*/
static void comcerto_dbi_write_reg(struct pcie_port *pp, int where, int size,
u32 val)
{
u32 va_address;
int pos, val1, mask = 0;
va_address = (u32)pp->va_dbi_base + (where & ~0x3);
pos = (where & 0x3) << 3;
if (size == 4)
val1 = val;
else
{
if (size == 2)
mask = 0xffff;
else if (size == 1)
mask = 0xff;
val1 = readl_relaxed(va_address);
val1 = ( val1 & ~( mask << pos ) ) | ( (val & mask) << pos );
}
writel_relaxed(val1, va_address);
}
static int comcerto_pcie_rd_conf(struct pcie_port *pp, int bus_nr,
u32 devfn, int where, int size, u32 *val)
{
u32 address;
u32 target_address = (u32)(bus_nr << 24) | (PCI_SLOT(devfn) << 19) | (PCI_FUNC(devfn) << 16);
/* Initialize iATU */
if (bus_nr != pp->root_bus_nr) {
if (pp->cfg1_prev_taddr != target_address) {
/* Type1 configuration request */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF1, (u32)iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr),
iATU_CFG1_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE1 & iATU_CTRL1_TYPE_MASK),
0, target_address );
pp->cfg1_prev_taddr = target_address;
}
address = (u32)pp->va_cfg1_base |(where & 0xFFFC);
} else {
if (pp->cfg0_prev_taddr != target_address) {
/* Type0 configuration request */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF0, (u32)iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr),
iATU_CFG0_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE0 & iATU_CTRL1_TYPE_MASK),
0, target_address );
pp->cfg0_prev_taddr = target_address;
}
address = (u32)pp->va_cfg0_base |(where & 0xFFFC);
}
*val = readl_relaxed(address);
if (size == 1)
*val = (*val >> (8 * (where & 3))) & 0xff;
else if (size == 2)
*val = (*val >> (8 * (where & 3))) & 0xffff;
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d addr : %x value:%x\n", __func__, bus_nr, devfn, where, size, address, *val);
#endif
return PCIBIOS_SUCCESSFUL;
}
static int pcie_read_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;
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d\n", __func__, bus->number, devfn, where, size);
#endif
/* Make sure that link is up.
* Filter device numbers, unless it's a type1 access
*/
if ( (!pp->link_state)||
((bus->number == pp->root_bus_nr) && (PCI_SLOT(devfn) > 0)) ) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
BUG_ON (((where & 0x3) + size) > 4);
/* Enter critical section. */
spin_lock_irqsave(&pp->conf_lock, flags);
ret = comcerto_pcie_rd_conf(pp, bus->number, devfn, where, size, val);
/* Exit critical section. */
spin_unlock_irqrestore(&pp->conf_lock, flags);
return ret;
}
static int comcerto_pcie_wr_conf(struct pcie_port *pp, int bus_nr,
u32 devfn, int where, int size, u32 val)
{
int ret = PCIBIOS_SUCCESSFUL;
u32 address;
u32 target_address = (u32)(bus_nr << 24) | (PCI_SLOT(devfn) << 19) | (PCI_FUNC(devfn) << 16);
/* Initialize iATU */
if (bus_nr != pp->root_bus_nr) {
if (pp->cfg1_prev_taddr != target_address) {
/* Type1 configuration request */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF1, (u32)iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr),
iATU_CFG1_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE1 & iATU_CTRL1_TYPE_MASK),
0, target_address );
pp->cfg1_prev_taddr = target_address;
}
address = (u32)pp->va_cfg1_base |(where & 0xFFFC);
} else {
if (pp->cfg0_prev_taddr != target_address) {
/* Type0 configuration request */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF0, (u32)iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr),
iATU_CFG0_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE0 & iATU_CTRL1_TYPE_MASK),
0, target_address );
pp->cfg0_prev_taddr = target_address;
}
address = (u32)pp->va_cfg0_base |(where & 0xFFFC);
}
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d addr : %x value:%x\n", __func__, bus_nr, devfn, where, size, address, val);
#endif
if (size == 4)
writel_relaxed(val, address);
else if (size == 2)
writew_relaxed(val, address + (where & 2));
else if (size == 1)
writeb_relaxed(val, address + (where & 3));
else
ret = PCIBIOS_BAD_REGISTER_NUMBER;
return ret;
}
static int pcie_write_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;
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d val:%d\n", __func__, bus->number, devfn, where, size, val);
#endif
/* Make sure that link is up.
* Filter device numbers, unless it's a type1 access
*/
if ( (!pp->link_state)||
((bus->number == pp->root_bus_nr) && (PCI_SLOT(devfn) > 0)) ) {
return PCIBIOS_DEVICE_NOT_FOUND;
}
BUG_ON (((where & 0x3) + size) > 4);
/* Enter critical section. */
spin_lock_irqsave(&pp->conf_lock, flags);
ret = comcerto_pcie_wr_conf(pp, bus->number, devfn, where, size, val);
if (where == PCI_COMMAND)
pp->cmd_reg_val = val & 0xffff;
/* Exit critical section. */
spin_unlock_irqrestore(&pp->conf_lock, flags);
return ret;
}
static u8 __init comcerto_pcie_swizzle(struct pci_dev *dev, u8 *pin)
{
return PCI_SLOT(dev->devfn);
}
static int __init comcerto_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
struct pcie_port *pp = bus_to_port(dev->bus->number);
int irq = (PCIE0_INTX_BASE + pp->port * PCIE_NUM_INTX_IRQS + pin - 1);
return irq;
}
static int __init comcerto_pcie_setup(int nr, struct pci_sys_data *sys)
{
struct pcie_port *pp;
struct pcie_app_reg *app_reg;
u32 val;
if ((nr >= NUM_PCIE_PORTS) || !pcie_port_is_host(nr))
return 0;
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s:%d nr:%d\n", __func__, __LINE__, nr);
#endif
pp = &pcie_port[nr];
if (!pp->link_state)
return 0;
pp->root_bus_nr = sys->busnr;
app_reg = pp->app_regs;
/* Allocate device memory mapped and IO mapped regions */
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 = iATU_GET_MEM_BASE(pp->remote_mem_baseaddr);
pp->res[0].end = pp->res[0].start + iATU_MEM_SIZE - 1;
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 = iATU_GET_IO_BASE(pp->remote_mem_baseaddr);
pp->res[1].end = pp->res[1].start + iATU_IO_SIZE - 1;
pp->res[1].flags = IORESOURCE_IO;
if (request_resource(&iomem_resource, &pp->res[0]))
{
printk(KERN_ERR "%s:can't allocate PCIe Memory space", __func__);
return 0;
}
if (request_resource(&iomem_resource, &pp->res[1]))
{
printk(KERN_ERR "%s:can't allocate PCIe IO space", __func__);
return 0;
}
/* Generic PCIe unit setup.*/
/* Enable own BME. It is necessary to enable own BME to do a
* memory transaction on a downstream device
*/
comcerto_dbi_read_reg(pp, PCI_COMMAND, 2, &val);
val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
comcerto_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;
pp->cfg0_prev_taddr = 0xffffffff;
pp->cfg1_prev_taddr = 0xffffffff;
return 1;
}
static struct pci_ops pcie_ops = {
.read = pcie_read_conf,
.write = pcie_write_conf,
};
static struct pci_bus *__init comcerto_pcie_scan_bus(int nr, struct pci_sys_data *sys)
{
struct pci_bus *bus;
if ((nr < NUM_PCIE_PORTS) && (pcie_port_is_host(nr))) {
bus = pci_scan_bus(sys->busnr, &pcie_ops, sys);
} else {
bus = NULL;
BUG();
}
return bus;
}
static struct hw_pci comcerto_pcie __initdata = {
.nr_controllers = NUM_PCIE_PORTS,
.swizzle = comcerto_pcie_swizzle,
.map_irq = comcerto_pcie_map_irq,
.setup = comcerto_pcie_setup,
.scan = comcerto_pcie_scan_bus,
};
#ifdef CONFIG_PCI_MSI
/* MSI int handler
*/
static void handle_msi(struct pcie_port *pp)
{
unsigned long val, mask;
unsigned int pos = 0, mask0;
val = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_STATUS);
while (val) {
mask0 = 1 << pos;
if (val & mask0) {
/* FIXME : WA for bz69520
* To avoid race condition during avk the interrupt disabling interrupt before
* Ack and enabling after Ack.
*/
spin_lock(&pp->intr_lock);
mask = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
writel_relaxed(mask & ~mask0, pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
writel_relaxed(mask0, pp->va_dbi_base + PCIE_MSI_INTR0_STATUS);
writel_relaxed(mask & mask0, pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
spin_unlock(&pp->intr_lock);
generic_handle_irq(pp->msi_base + pos);
val = val & ~mask0;
}
pos++;
}
#if 0
for (i = 0; i < (PCIE_NUM_MSI_IRQS >> 5); i++) {
val = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_STATUS + (i * 12));
if (val) {
pos = 0;
while ((pos = find_next_bit(&val, 32, pos)) != 32) {
/* FIXME : WA for bz69520
* To avoid race condition during avk the interrupt disabling interrupt before
* Ack and enabling after Ack.
*/
spin_lock(&pp->intr_lock);
mask = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
writel_relaxed(mask & ~(1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
writel_relaxed((1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_STATUS + (i * 12));
writel_relaxed(mask & (1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
spin_unlock(&pp->intr_lock);
generic_handle_irq(pp->msi_base + (i * 32) + pos);
pos++;
}
}
}
#endif
}
#else
static void handle_msi(struct pcie_port *pp)
{
}
#endif
static void comcerto_pcie_int_handler(unsigned int irq, struct irq_desc *desc)
{
struct pcie_port *pp = irq_get_handler_data(irq);
struct pcie_app_reg *app_reg = pp->app_regs;
struct irq_chip *chip;
unsigned int status;
int pos, ii;
chip = irq_desc_get_chip(desc);
chained_irq_enter(chip, desc);
status = readl_relaxed(pp->va_app_base + app_reg->intr_sts);
if (status & INTR_CTRL_MSI) {
writel_relaxed(INTR_CTRL_MSI, (pp->va_app_base + app_reg->intr_sts));
status &= ~(INTR_CTRL_MSI);
handle_msi(pp);
}
for (ii = 0; (ii < PCIE_NUM_INTX_IRQS) && status; ii++) {
pos = ii << 1;
/* Handle INTx Assert */
if (status & (INTR_CTRL_INTA_ASSERT << pos)) {
status &= ~(INTR_CTRL_INTA_ASSERT << pos);
writel_relaxed((INTR_CTRL_INTA_ASSERT << pos), (pp->va_app_base + app_reg->intr_sts));
generic_handle_irq(pp->intx_base + ii);
}
/*FIXME : No need to handle DEASSERT, simply clear the interrupt */
status &= ~(INTR_CTRL_INTA_DEASSERT << pos);
#if 0
/* Handle INTx Deasert */
if (status & (INTR_CTRL_INTA_DEASSERT << pos)) {
status &= ~(INTR_CTRL_INTA_DEASSERT << pos);
writel_relaxed((INTR_CTRL_INTA_DEASSERT << pos), (pp->va_app_base + app_reg->intr_sts));
}
#endif
}
if (status) {
printk(KERN_INFO "%s:Unhandled interrupt %x\n", __func__, status);
/* FIXME: HP, AER, PME interrupts need to be handled */
writel_relaxed(status, (pp->va_app_base + app_reg->intr_sts));
}
chained_irq_exit(chip, desc);
}
static void pcie_nop_intx_irq(struct irq_data *data )
{
return;
}
static void pcie_unmask_intx_irq( struct irq_data *data )
{
struct pcie_port *pp = data->chip_data;
spin_lock(&pp->conf_lock);
comcerto_pcie_wr_conf(pp, pp->root_bus_nr, 0, PCI_COMMAND, 2, (pp->cmd_reg_val & ~PCI_COMMAND_INTX_DISABLE));
spin_unlock(&pp->conf_lock);
}
static void pcie_enable_intx_irq( struct irq_data *data )
{
int irq = data->irq;
struct pcie_port *pp = data->chip_data;
int irq_offset = irq - pp->intx_base;
struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
unsigned long flags;
if( irq_offset >= PCIE_NUM_INTX_IRQS )
return;
/*
* In interrupt sts/mask register each interrupt has two
* consecutive bits, one for assert and another for dessert.
*/
irq_offset = irq_offset << 1;
spin_lock_irqsave(&pp->intr_lock, flags);
writel_relaxed(readl_relaxed(pp->va_app_base + app_reg->intr_en) | ( INTR_CTRL_INTA_ASSERT << irq_offset),
(pp->va_app_base + app_reg->intr_en) );
spin_unlock_irqrestore(&pp->intr_lock, flags);
}
static void pcie_mask_intx_irq( struct irq_data *data )
{
struct pcie_port *pp = data->chip_data;
spin_lock(&pp->conf_lock);
comcerto_pcie_wr_conf(pp, pp->root_bus_nr, 0, PCI_COMMAND, 2, (pp->cmd_reg_val | PCI_COMMAND_INTX_DISABLE));
spin_unlock(&pp->conf_lock);
}
static void pcie_disable_intx_irq( struct irq_data *data )
{
int irq = data->irq;
struct pcie_port *pp = data->chip_data;
int irq_offset = irq - pp->intx_base;
struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
unsigned long flags;
if( irq_offset >= PCIE_NUM_INTX_IRQS )
return;
/*
* In interrupt sts/mask register each interrupt has two
* consecutive bits, one for assert and another for dessert.
*/
irq_offset = irq_offset << 1;
spin_lock_irqsave(&pp->intr_lock, flags);
writel_relaxed( readl_relaxed(pp->va_app_base + app_reg->intr_en) & ~( INTR_CTRL_INTA_ASSERT << irq_offset),
(pp->va_app_base + app_reg->intr_en) );
spin_unlock_irqrestore(&pp->intr_lock, flags);
}
static struct irq_chip pcie_intx_chip = {
.name = "PCIe INTx",
.irq_ack = pcie_nop_intx_irq,
.irq_enable = pcie_enable_intx_irq,
.irq_disable = pcie_disable_intx_irq,
.irq_mask = pcie_mask_intx_irq,
.irq_unmask = pcie_unmask_intx_irq,
};
static int comcerto_pcie_intx_init(struct pcie_port *pp)
{
int i, irq;
struct pcie_app_reg *app_reg;
/* Disable INTX interrupt*/
app_reg = pp->app_regs;
writel(readl(pp->va_app_base + app_reg->intr_en) &
~(INTR_CTRL_INTA_ASSERT |
INTR_CTRL_INTB_ASSERT |
INTR_CTRL_INTC_ASSERT |
INTR_CTRL_INTD_ASSERT),
pp->va_app_base + app_reg->intr_en );
/* initilize INTX chip here only. MSI chip will be
* initilized dynamically.*/
irq = pp->intx_base;
for (i = 0; i < PCIE_NUM_INTX_IRQS; i++) {
irq_set_chip_data(irq + i, pp);
irq_set_chip_and_handler(irq + i, &pcie_intx_chip,
handle_level_irq);
set_irq_flags(irq + i, IRQF_VALID);
}
irq_set_handler_data(pp->irq, pp);
irq_set_chained_handler(pp->irq, comcerto_pcie_int_handler);
/* FIXME Added for debuging */
writel(readl(pp->va_app_base + app_reg->intr_en) &
~(INTR_CTRL_AER | INTR_CTRL_PME |
INTR_CTRL_HP | INTR_CTRL_LINK_AUTO_BW ),
pp->va_app_base + app_reg->intr_en );
return 0;
}
static int comcerto_pcie_rc_int_init( struct pcie_port *pp )
{
printk(KERN_INFO "%s\n",__func__);
comcerto_pcie_intx_init(pp);
#ifdef CONFIG_PCI_MSI
comcerto_msi_init(pp);
#endif
return 0;
}
#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],
PCIE_NUM_MSI_IRQS, pos);
/*if you have reached to the end then get out from here.*/
if (pos == PCIE_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;
}
#define GET_MSI_INT_REG_POS(_irq) ((_irq >> 5) * 12)
#define GET_MSI_INT_OFST(_irq) (_irq & 0x1F)
static void comcerto_msi_nop(struct irq_data *data)
{
return;
}
static void comcerto_msi_unmask(struct irq_data *data)
{
struct pcie_port *pp = data->chip_data;
int irq = data->irq - pp->msi_base;
int ofst, pos;
unsigned int val;
unsigned long flags;
if( irq >= PCIE_NUM_MSI_IRQS )
return;
pos = GET_MSI_INT_REG_POS(irq);
ofst = GET_MSI_INT_OFST(irq);
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
#endif
spin_lock_irqsave(&pp->intr_lock, flags);
comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, &val);
val &= ~(1 << ofst);
comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, val);
spin_unlock_irqrestore(&pp->intr_lock, flags);
return;
}
static void comcerto_msi_mask(struct irq_data *data)
{
struct pcie_port *pp = data->chip_data;
int irq = data->irq - pp->msi_base;
int ofst, pos;
unsigned int val;
unsigned long flags;
if( irq >= PCIE_NUM_MSI_IRQS )
return;
pos = GET_MSI_INT_REG_POS(irq);
ofst = GET_MSI_INT_OFST(irq);
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
#endif
spin_lock_irqsave(&pp->intr_lock, flags);
comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, &val);
val |= (1 << ofst);
comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, val);
spin_unlock_irqrestore(&pp->intr_lock, flags);
return;
}
static void comcerto_msi_enable(struct irq_data *data)
{
struct pcie_port *pp = data->chip_data;
int irq = data->irq - pp->msi_base;
int ofst, pos;
unsigned int val;
unsigned long flags;
if( irq >= PCIE_NUM_MSI_IRQS )
return;
pos = GET_MSI_INT_REG_POS(irq);
ofst = GET_MSI_INT_OFST(irq);
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
#endif
spin_lock_irqsave(&pp->intr_lock, flags);
comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, &val);
val |= (1 << ofst);
comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, val);
spin_unlock_irqrestore(&pp->intr_lock, flags);
return;
}
static void comcerto_msi_disable(struct irq_data *data)
{
struct pcie_port *pp = data->chip_data;
int irq = data->irq - pp->msi_base;
int ofst, pos;
unsigned int val;
unsigned long flags;
if( irq >= PCIE_NUM_MSI_IRQS )
return;
pos = GET_MSI_INT_REG_POS(irq);
ofst = GET_MSI_INT_OFST(irq);
#ifdef COMCERTO_PCIE_DEBUG
printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
#endif
spin_lock_irqsave(&pp->intr_lock, flags);
comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, &val);
val &= ~(1 << ofst);
comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, val);
spin_unlock_irqrestore(&pp->intr_lock, flags);
return;
}
static struct irq_chip comcerto_msi_chip = {
.name = "PCI-MSI",
.irq_ack = comcerto_msi_nop,
.irq_enable = comcerto_msi_enable,
.irq_disable = comcerto_msi_disable,
.irq_mask = comcerto_msi_mask,
.irq_unmask = comcerto_msi_unmask,
};
/*
* Dynamic irq allocate and deallocation
*/
static int get_irq(int nvec, struct msi_desc *desc, int *pos)
{
int irq, pos0, pos1, i;
struct pcie_port *pp = bus_to_port(desc->dev->bus->number);
pos0 = find_first_zero_bit(msi_irq_in_use[pp->port],
PCIE_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],
PCIE_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],
PCIE_NUM_MSI_IRQS, pos0);
}
}
irq = pp->msi_base + pos0;
if ((irq + nvec) > (pp->msi_base + PCIE_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);
irq_set_chip_data(irq + i, pp);
irq_set_chip_and_handler(irq + i, &comcerto_msi_chip,
handle_simple_irq);
set_irq_flags(irq + i, IRQF_VALID);
i++;
}
irq_set_msi_desc(irq, desc);
*pos = pos0;
return irq;
no_valid_irq:
printk(KERN_ERR "%s : MSI interrupt allocate failed\n", __func__);
*pos = pos0;
return -ENOSPC;
}
static void clean_irq(unsigned int irq)
{
int res, bit, val, pos;
struct irq_data *data = irq_get_irq_data(irq);
struct pcie_port *pp = data->chip_data;
unsigned long flags;
if( !pp )
return;
pos = irq - pp->msi_base;
dynamic_irq_cleanup(irq);
spin_lock_irqsave(&pp->msi_map_lock, flags);
clear_bit(pos, msi_irq_in_use[pp->port]);
/* Disable corresponding interrupt on MSI interrupt
* controller.
*/
res = (pos / 32) * 12;
bit = pos % 32;
comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
val &= ~(1 << bit);
comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
spin_unlock_irqrestore(&pp->msi_map_lock, flags);
}
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
int irq, pos;
struct msi_msg msg;
struct pcie_port *pp = bus_to_port(pdev->bus->number);
unsigned long flags;
spin_lock_irqsave(&pp->msi_map_lock, flags);
irq = get_irq(1, desc, &pos);
spin_unlock_irqrestore(&pp->msi_map_lock, flags);
if (irq < 0)
return irq;
desc->msi_attrib.multiple = 0;
/* An EP will modify lower 8 bits(max) of msi data while
* sending any msi interrupt
*/
msg.address_hi = 0x0;
msg.address_lo = pp->msi_mbox_handle;
msg.data = pos;
write_msi_msg(irq, &msg);
return 0;
}
void arch_teardown_msi_irq(unsigned int irq)
{
clean_irq(irq);
}
static int comcerto_msi_init(struct pcie_port *pp)
{
struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
pp->msi_mbox_baseaddr = dma_alloc_coherent(NULL, sizeof(u32), &pp->msi_mbox_handle, GFP_KERNEL);
if (!pp->msi_mbox_baseaddr) {
printk(KERN_ERR "PCIe(%d): failed to allocate msi mailbox coherent memory\n", pp->port);
goto err;
}
comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4, pp->msi_mbox_handle);
comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
/* Enbale MSI interrupt*/
writel(readl(pp->va_app_base + app_reg->intr_en) | INTR_CTRL_MSI,
pp->va_app_base + app_reg->intr_en);
return 0;
err:
return -1;
}
#endif
static void comcerto_pcie_rc_init(struct pcie_port *pp)
{
struct pcie_app_reg *app_reg = pp->app_regs;
unsigned int val;
//FIXME : Bit:27 definition is not clear from document
// This register setting is copied from simulation code.
writel(readl(pp->va_app_base + app_reg->cfg0) | 0x08007FF0,
pp->va_app_base + app_reg->cfg0);
/**
*FIXME : Bit:15 definition is not clear from document
* This register setting is copied from simulation code.
*/
writel(readl(pp->va_app_base + app_reg->cfg4) | 0x00008000,
pp->va_app_base + app_reg->cfg4);
comcerto_dbi_read_reg(pp, PCIE_AFL0L1_REG, 4, &val);
val &= ~(0x00FFFF00);
val |= 0x00F1F100;
comcerto_dbi_write_reg(pp, PCIE_AFL0L1_REG, 4, val);
if(pcie_gen1_only)
{
comcerto_dbi_write_reg(pp, PCIE_LCNT2_REG, 4, 0x1);
comcerto_dbi_write_reg(pp, PCIE_LCAP_REG, 4, 0x1);
}
else
{
comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
val &= ~(0xFF);
val |= 0xF1;
comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
}
// instruct pcie to switch to gen2 after init
comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
val |= (1 << 17);
comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
/*setup iATU for outbound translation */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MEM, iATU_GET_MEM_BASE(pp->remote_mem_baseaddr),
iATU_MEM_SIZE - 1, 0, 0, pp->remote_mem_baseaddr );
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_IO, iATU_GET_IO_BASE(pp->remote_mem_baseaddr),
iATU_IO_SIZE - 1, (AXI_OP_TYPE_IO_RDRW & iATU_CTRL1_TYPE_MASK),
0, iATU_GET_IO_BASE(pp->remote_mem_baseaddr) );
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MSG, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr),
iATU_MSG_SIZE - 1, (AXI_OP_TYPE_MSG_REQ & iATU_CTRL1_TYPE_MASK),
0, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr) );
PCIE_SETUP_iATU_IB_ENTRY( pp, 0, 0,
INBOUND_ADDR_MASK, 0, 0, COMCERTO_AXI_DDR_BASE);
}
static int pcie_app_init(struct pcie_port *pp, int nr, int mode)
{
if (nr > MAX_PCIE_PORTS) {
printk(KERN_ERR "%s: Invalid port\n", __func__);
goto err0;
}
if (mode != CFG0_DEV_TYPE_RC) {
printk(KERN_ERR "%s: Unsupported mode selected mode: %d. \n", __func__, mode);
goto err0;
}
memset(pp, 0, sizeof(struct pcie_port));
pp->port = nr;
pp->app_regs = &app_regs[nr];
pp->root_bus_nr = nr;
pp->base = pcie_cnf_base_addr[nr];
pp->app_base = (unsigned long)COMCERTO_APB_PCI_SATA_USB_CTRL_BASE;
pp->va_app_base = (void __iomem *)APB_VADDR(COMCERTO_APB_PCI_SATA_USB_CTRL_BASE);
pp->remote_mem_baseaddr = pcie_remote_base_addr[nr];
if (!nr)
pp->va_dbi_base = (void __iomem *) COMCERTO_AXI_PCIe0_VADDR_BASE;
else
pp->va_dbi_base = (void __iomem *) COMCERTO_AXI_PCIe1_VADDR_BASE;
pp->va_cfg0_base = (void __iomem *)
ioremap( iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr), iATU_CFG0_SIZE);
if (!pp->va_cfg0_base) {
pr_err("error with ioremap in function %s\n", __func__);
goto err0;
}
pp->va_cfg1_base = (void __iomem *)
ioremap( iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr) , iATU_CFG1_SIZE);
if (!pp->va_cfg1_base) {
pr_err("error with ioremap in function %s\n", __func__);
goto err1;
}
pp->intx_base = pcie_intx_base[nr];
pp->msi_base = pcie_msi_base[nr];
pp->irq = pcie_irqs[nr];
spin_lock_init(&pp->conf_lock);
spin_lock_init(&pp->intr_lock);
spin_lock_init(&pp->msi_map_lock);
memset(pp->res, 0, sizeof(pp->res));
/* Get the reference clock to PCIe port*/
switch (nr) {
case 0:
pp->ref_clock = clk_get(NULL, "pcie0");
break;
case 1:
pp->ref_clock = clk_get(NULL, "pcie1");
break;
default:
printk(KERN_ERR "%s: Invalid port\n", __func__);
goto err2;
}
if (IS_ERR(pp->ref_clock)) {
pr_err("%s: Unable to obtain pcie%d clock: %ld\n", __func__, nr, PTR_ERR(pcie_port[nr].ref_clock));
goto err2;
}
/* Enable the PCIE_OCC clock */
#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
pp->occ_clock = clk_get(NULL,"pcie_occ");
if (IS_ERR(pp->occ_clock)) {
pr_err("%s: Unable to obtain pcie_occ clock: %ld\n", __func__, PTR_ERR(pp->occ_clock));
goto err_occ_clock;
}
#endif
pcie_port_set_mode(nr, mode);
return 0;
#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
err_occ_clock:
clk_put(pp->occ_clock);
#endif
err2:
iounmap(pp->va_cfg1_base);
err1:
iounmap(pp->va_cfg0_base);
err0:
return -1;
}
#if defined(CONFIG_C2K_EVM) || defined(CONFIG_C2K_ASIC)
#define PCIE_DEV_EXT_RESET_DEASSERT(_id) \
writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~(GPIO_PIN_27), COMCERTO_GPIO_OUTPUT_REG);
#define PCIE_DEV_EXT_RESET_ASSERT(_id) \
writel(readl(COMCERTO_GPIO_OUTPUT_REG) | (GPIO_PIN_27), COMCERTO_GPIO_OUTPUT_REG);
#else
/* Board specific */
#define PCIE_DEV_EXT_RESET_DEASSERT(_id)
#define PCIE_DEV_EXT_RESET_ASSERT(_id)
#endif
static int comcerto_pcie_device_reset(struct pcie_port *pp)
{
int ii;
struct pcie_port *l_pp = pp;
struct pci_dev *pci_dev = NULL;
printk(KERN_INFO "ENTER: Bringing PCIe%d device reset\n", pp->port);
if (!pp->link_state)
return -1;
/* On C2KEVM and ASIC same reset is connected to PCIe0/1.
* So, device might have kept in reset, using other PCIe host.
*/
if (!comcerto_pcie_link_up(pp)) {
printk(KERN_INFO "%s : Device is already link down state\n", __func__);
return 0;
}
/*FIXME : Below code might be required if we want to reset pcie device, without
* invoking pcie device supend. If we invoke pcie device suspend it will
* take care of saving pcie config space.
*/
#if 0
/* Now save the PCIe device configuration space.*/
while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
if (pp->root_bus_nr == pci_dev->bus->number) {
pci_save_state(pci_dev);
}
}
/* On C2KEVM/ASIC, since same GPIO27 is used to reset PCIe0/PCIe1 devices,
* save configuration of devices on other PCIe also.
* This may not be applicable for other cutomer boards.
*/
#if defined(CONFIG_C2K_EVM) || defined(CONFIG_C2K_ASIC)
l_pp = &pcie_port[!pp->port];
pci_dev = NULL;
if (l_pp->link_state) {
while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
if (l_pp->root_bus_nr == pci_dev->bus->number) {
pci_save_state(pci_dev);
}
}
}
#endif
#endif
/************** Ready to issue rest *****************/
/* De-assert external reset (GPIO-27) */
PCIE_DEV_EXT_RESET_DEASSERT(pp->port);
printk(KERN_INFO "EXIT: Bringing PCIe%d device reset\n", pp->port);
return 0;
}
static int comcerto_pcie_device_reset_exit(struct pcie_port *pp)
{
unsigned int val;
struct pci_dev *pci_dev;
printk(KERN_INFO "ENTER: Bringing PCIe%d device out-of-reset\n", pp->port);
if (!pp->link_state && pp->reset)
return -1;
/* Pull up external reset */
/* assert external reset (GPIO-27) */
PCIE_DEV_EXT_RESET_ASSERT(pp->port);
udelay(1000);
udelay(1000);
udelay(1000);
udelay(1000);
udelay(1000);
udelay(1000);
udelay(1000);
/* Restore the RC configuration */
comcerto_dbi_read_reg(pp, PCIE_AFL0L1_REG, 4, &val);
val &= ~(0x00FFFF00);
val |= 0x00F1F100;
comcerto_dbi_write_reg(pp, PCIE_AFL0L1_REG, 4, val);
if(pcie_gen1_only)
{
comcerto_dbi_write_reg(pp, PCIE_LCNT2_REG, 4, 0x1);
comcerto_dbi_write_reg(pp, PCIE_LCAP_REG, 4, 0x1);
}
else
{
comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
val &= ~(0xFF);
val |= 0xF1;
comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
}
// instruct pcie to switch to gen2 after init
comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
val |= (1 << 17);
comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
/*setup iATU for outbound translation */
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MEM, iATU_GET_MEM_BASE(pp->remote_mem_baseaddr),
iATU_MEM_SIZE - 1, 0, 0, pp->remote_mem_baseaddr );
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_IO, iATU_GET_IO_BASE(pp->remote_mem_baseaddr),
iATU_IO_SIZE - 1, (AXI_OP_TYPE_IO_RDRW & iATU_CTRL1_TYPE_MASK),
0, iATU_GET_IO_BASE(pp->remote_mem_baseaddr) );
PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MSG, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr),
iATU_MSG_SIZE - 1, (AXI_OP_TYPE_MSG_REQ & iATU_CTRL1_TYPE_MASK),
0, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr) );
PCIE_SETUP_iATU_IB_ENTRY( pp, 0, 0,
INBOUND_ADDR_MASK, 0, 0, COMCERTO_AXI_DDR_BASE);
comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4, pp->msi_mbox_handle);
comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
writel_relaxed(0x7, pp->va_app_base + pp->app_regs->cfg5);
/* Generic PCIe unit setup.*/
/* Enable own BME. It is necessary to enable own BME to do a
* memory transaction on a downstream device
*/
comcerto_dbi_read_reg(pp, PCI_COMMAND, 2, &val);
val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
comcerto_dbi_write_reg(pp, PCI_COMMAND, 2, val);
pp->cfg0_prev_taddr = 0xffffffff;
pp->cfg1_prev_taddr = 0xffffffff;
//udelay(1000);
if(comcerto_pcie_link_up(pp)) {
printk(KERN_INFO " Bringing PCIe%d device out-of-reset : Link Up\n", pp->port);
/*FIXME : Below code might be required if we want to bring pcie device out-of-reset,
* without invoking pcie device supend. If we invoke pcie device resume it will
* take care of restoring, saved pcie config space.
*/
#if 0
pci_dev = NULL;
while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
if (pp->root_bus_nr == pci_dev->bus->number) {
pci_restore_state(pci_dev);
}
}
#endif
}
printk(KERN_INFO "EXIT: Bringing PCIe%d device out-of-reset\n", pp->port);
return 0;
}
static ssize_t comcerto_pcie_show_reset(struct device *dev, struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct pcie_port *pp = &pcie_port[pdev->id];
return sprintf(buf, "%d\n", pp->reset);
}
static ssize_t comcerto_pcie_set_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct pcie_port *pp = &pcie_port[pdev->id];
int reset = 0;
if (!pcie_port_is_host(pdev->id) || !(pp->link_state))
return count;
sscanf(buf, "%d", &reset);
reset = reset ? 1:0;
if (pp->reset == reset) {
printk(KERN_INFO "%s: Already in same state\n", __func__);
return count;
}
if (reset) {
printk(KERN_INFO "ENTER : Putting PCIe%d device into reset\n", pdev->id);
if (!comcerto_pcie_device_reset(pp)) {
int ii = 10;
/* Wait for link_req_rst_not, to be de-asseted */
while (ii--) {
if (!(readl( pp->va_app_base + pp->app_regs->sts0 ) & STS0_LINK_REQ_RST_NOT)) {
printk(KERN_INFO "%s : (PCIe%d) link_req_rst_not is de-asseted\n", __func__, pp->port);
break;
}
udelay(1000);
}
if (ii == 10)
printk(KERN_WARNING "%s : (PCIe%d) link_req_rst_not is not de-asseted \n", __func__, pp->port);
pp->reset = 1;
/* Disable LTSSM and initiate linkdown reset */
writel((readl(pp->va_app_base + pp->app_regs->cfg5) &
~(CFG5_LTSSM_ENABLE)) | CFG5_LINK_DOWN_RST,
pp->va_app_base + pp->app_regs->cfg5);
udelay(1000);
}
printk(KERN_INFO "EXIT : Putting PCIe%d device into reset\n", pdev->id);
}
else {
printk(KERN_INFO "ENTER: Bringing PCIe%d device outof reset\n", pdev->id);
if (!comcerto_pcie_device_reset_exit(pp)) {
pp->reset = 0;
}
}
return count;
}
static DEVICE_ATTR(device_reset, 0644, comcerto_pcie_show_reset, comcerto_pcie_set_reset);
#ifdef CONFIG_PM
static int comcerto_pcie_suspend(struct platform_device *pdev, pm_message_t state)
{
unsigned int val, i;
printk(KERN_INFO "%s: pcie device %p (id = %d): state %d\n",
__func__, pdev, pdev->id, state.event);
if (pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
if (comcerto_pcie_link_up(&pcie_port[pdev->id])){
/* Enable PME to root Port */
comcerto_dbi_read_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, &val);
comcerto_dbi_write_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, val | PCI_PM_CTRL_STATE_MASK);
/* Required PM Delay */
for (i = 0 ; i < 40 ; i++)
udelay(500);
}
}
return 0;
}
static int comcerto_pcie_resume(struct platform_device *pdev)
{
unsigned int val, i;
printk(KERN_INFO "%s: pcie device %p (id = %d)\n",
__func__, pdev, pdev->id);
if(pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
if (comcerto_pcie_link_up(&pcie_port[pdev->id])){
/* Put In D0 State */
comcerto_dbi_read_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, &val);
comcerto_dbi_write_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, val & (~PCI_PM_CTRL_STATE_MASK));
/* Required PM Delay */
for (i = 0 ; i < 40 ; i++)
udelay(500);
}
}
return 0;
}
static struct platform_driver comcerto_pcie_driver = {
.suspend = comcerto_pcie_suspend,
.resume = comcerto_pcie_resume,
.driver = {
.name = "pcie",
.owner = THIS_MODULE,
},
};
static struct platform_device pcie_pwr0 = {
.name = "pcie",
.id = 0,
};
static struct platform_device pcie_pwr1 = {
.name = "pcie",
.id = 1,
};
#endif
static void comcerto_serdes_set_polarity(struct serdes_regs_s *p_pcie_phy_reg_file, int current_polarity)
{
switch(current_polarity)
{
case 0:
p_pcie_phy_reg_file[0x73].val = 0x0;
p_pcie_phy_reg_file[0x75].val = 0x0;
break;
case 1:
p_pcie_phy_reg_file[0x73].val = 0x8;
p_pcie_phy_reg_file[0x75].val = 0x2;
break;
case 2:
p_pcie_phy_reg_file[0x73].val = 0x0;
p_pcie_phy_reg_file[0x75].val = 0x2;
break;
case 3:
p_pcie_phy_reg_file[0x73].val = 0x8;
p_pcie_phy_reg_file[0x75].val = 0x0;
break;
}
}
static int comcerto_pcie_bsp_link_init(struct pcie_port *pp, int nr, struct serdes_regs_s *p_pcie_phy_reg_file, int serdes_reg_size, int current_polarity)
{
int axi_pcie_component;
int pcie_component;
int serdes_component;
int rc;
int if_err = 1;
comcerto_serdes_set_polarity(p_pcie_phy_reg_file, current_polarity);
if(nr == 0)
{
axi_pcie_component = COMPONENT_AXI_PCIE0;
pcie_component = COMPONENT_SERDES_PCIE0;
serdes_component = COMPONENT_SERDES0;
}
else
{
axi_pcie_component = COMPONENT_AXI_PCIE1;
pcie_component = COMPONENT_SERDES_PCIE1;
serdes_component = COMPONENT_SERDES1;
}
//Bring serdes out of reset
c2000_block_reset(axi_pcie_component,0);
c2000_block_reset(serdes_component,0);
/* SW select for ck_soc_div_i SOC clock */
writel(0xFF3C, COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL3_REG_OFST ));
writel(readl(COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL2_REG_OFST )) & ~0x3,
COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL2_REG_OFST ));
rc = clk_enable(pp->ref_clock);
if (rc){
pr_err("%s: PCIe%d clock enable failed\n", __func__, nr);
goto err1;
}
/* Enable the PCIE_OCC clock */
#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
rc = clk_enable(pp->occ_clock);
if (rc){
pr_err("%s: PCIe_occ clock enable failed\n", __func__);
goto err_occ_clock;
}
#endif
/* Serdes Initialization. */
if( serdes_phy_init(nr, p_pcie_phy_reg_file,
serdes_reg_size/ sizeof(serdes_regs_t),
SD_DEV_TYPE_PCIE) )
{
pp->port_mode = PCIE_PORT_MODE_NONE;
pr_err("%s: Failed to initialize serdes (%d)\n", __func__, nr );
goto err_phy_link;
}
mdelay(1); //After CMU locks wait for sometime
//Bring PCIe out of reset
c2000_block_reset(pcie_component,0);
//Hold the LTSSM in detect state
writel(readl(pp->va_app_base + pp->app_regs->cfg5) & ~CFG5_LTSSM_ENABLE,
pp->va_app_base + pp->app_regs->cfg5);
comcerto_pcie_rc_init(pp);
//Enable LTSSM to start link initialization
writel(readl(pp->va_app_base + pp->app_regs->cfg5) | (CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE),
pp->va_app_base + pp->app_regs->cfg5);
pp->link_state = comcerto_pcie_link_up( &pcie_port[nr] );
if(!pp->link_state)
{
if_err = 0;
goto err_phy_link;
}
return 0;
err_phy_link:
clk_disable(pp->ref_clock);
clk_put(pp->ref_clock);
#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
err_occ_clock:
clk_disable(pp->occ_clock);
clk_put(pp->occ_clock);
#endif
err1:
//Put all to reset
c2000_block_reset(axi_pcie_component,1);
c2000_block_reset(serdes_component,1);
c2000_block_reset(pcie_component,1);
if(if_err)
return -1;
else
return 0;
}
static int comcerto_pcie_bsp_init(struct pcie_port *pp, int nr)
{
struct serdes_regs_s *p_pcie_phy_reg_file;
int serdes_regs_size;
int polarity_max = 4;
int polarity;
int ret;
if (nr >= NUM_PCIE_PORTS) {
printk("%s : Invalid PCIe port number\n", __func__);
goto err0;
}
ret = pcie_app_init(pp, nr, CFG0_DEV_TYPE_RC);
if(ret == -1)
goto err0;;
pp->port_mode = pcie_port_get_mode(nr);
if(pcie_external_clk)
{
p_pcie_phy_reg_file = &pcie_phy_reg_file_100[0];
serdes_regs_size = sizeof(pcie_phy_reg_file_100);
}
else
{
if(HAL_get_ref_clk() == REF_CLK_24MHZ)
{
p_pcie_phy_reg_file = &pcie_phy_reg_file_24[0];
serdes_regs_size = sizeof(pcie_phy_reg_file_24);
printk(KERN_INFO "PCIe: Ref clk 24Mhz\n");
}
else
{
p_pcie_phy_reg_file = &pcie_phy_reg_file_48[0];
serdes_regs_size = sizeof(pcie_phy_reg_file_48);
printk(KERN_INFO "PCIe: Ref clk 48Mhz\n");
}
}
if (system_rev == 1) {
// C2K RevA1 devices use a different serdes clk divider
p_pcie_phy_reg_file[0x61].val = 0x6;
}
for(polarity = 0 ; polarity < polarity_max; polarity++)
{
ret = comcerto_pcie_bsp_link_init(pp, nr, p_pcie_phy_reg_file, serdes_regs_size, polarity);
if(ret == -1)
goto err1;;
if (pp->link_state)
goto linkup;
if(!pcie_port_is_host(nr))
return 0; //Endpoint, so no need to change polarity
}
printk(KERN_INFO "PCIe%d: Link Up Failed \n",nr);
err1:
iounmap(pp->va_cfg0_base);
iounmap(pp->va_cfg1_base);
pp->port_mode = PCIE_PORT_MODE_NONE;
err0:
return -1;
linkup:
printk(KERN_INFO "PCIe%d: Link Up Success \n",nr);
printk(KERN_INFO "PCIe%d: Polarity: %d Gen1 mode: %d External Clk: %d\n",nr, polarity, pcie_gen1_only,pcie_external_clk);
if (pcie_port_is_host(nr))
comcerto_pcie_rc_int_init(pp);
return 0;
}
static int __init comcerto_pcie_init(void)
{
struct pcie_port *pp;
int i, rc;
int num_pcie_port = 1;
struct pci_dev *pdev = NULL;
pp = &pcie_port[0];
comcerto_pcie_bsp_init(pp, 0);
if ( (NUM_PCIE_PORTS == 2) &&
!(readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0) )
{
num_pcie_port = 2;
pp = &pcie_port[1];
comcerto_pcie_bsp_init(pp, 1);
}
comcerto_pcie.nr_controllers = num_pcie_port;
pcibios_min_io = iATU_GET_IO_BASE(COMCERTO_AXI_PCIe0_SLAVE_BASE);
pcibios_min_mem = COMCERTO_AXI_PCIe0_SLAVE_BASE;
pci_add_flags(PCI_REASSIGN_ALL_RSRC);
pci_common_init(&comcerto_pcie);
for ( i = 0; i < num_pcie_port; i++ )
{
if (!pcie_port_is_host(i) || !(pcie_port[i].link_state))
continue;
while((pdev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pdev))) {
if (pcie_port[i].root_bus_nr == pdev->bus->number) {
if( (rc = pcie_get_readrq(pdev)) > 512 ) {
printk(KERN_WARNING "PCIe%d Device rdreq size (%d) is more than supported\n", i, rc);
pcie_set_readrq(pdev, 512);
}
}
}
}
#ifdef CONFIG_PM
platform_device_register(&pcie_pwr0);
if (device_create_file(&pcie_pwr0.dev, &dev_attr_device_reset))
printk(KERN_ERR "%s: Unable to create pcie0 reset sysfs entry\n", __func__);
if(num_pcie_port > 1) {
platform_device_register(&pcie_pwr1);
if (device_create_file(&pcie_pwr1.dev, &dev_attr_device_reset))
printk(KERN_ERR "%s: Unable to create pcie1 reset sysfs entry\n", __func__);
}
platform_driver_register(&comcerto_pcie_driver);
#endif
return 0;
}
subsys_initcall(comcerto_pcie_init);