blob: d63a3d293a1370a38b1e73189e944f90fb754ba2 [file] [log] [blame]
/*
* 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/autoconf.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/mach/pci.h>
#include <mach/irqs.h>
#include "mvCommon.h"
#include "ctrlEnv/mvCtrlEnvSpec.h"
#include "pex/mvPex.h"
#include "pex/mvPexRegs.h"
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "mvSysPexApi.h"
#include "ctrlEnv/sys/mvCpuIf.h"
#undef DEBUG
#ifdef DEBUG
# define DB(x) x
#else
# define DB(x)
#endif
#define PCI_ERR_NAME_LEN 12
#define MV_MAX_PEX_IF_NUMBER 2
static int __init mv_map_irq_0(struct pci_dev *dev, u8 slot, u8 pin);
static int __init mv_map_irq_1(struct pci_dev *dev, u8 slot, u8 pin);
void mv_pci_error_init(u32 pciIf);
static irqreturn_t pex_error_interrupt(int irq, void *dev_id);
static struct pex_if_error {
MV_8 irq_name[PCI_ERR_NAME_LEN];
MV_U32 ifNumber;
} pex_err[MV_MAX_PEX_IF_NUMBER];
void __init mv_pci_preinit(void)
{
MV_ADDR_WIN win;
MV_STATUS retval;
u32 pciIf;
u32 maxif = mvCtrlPexMaxIfGet();
/* Check Power State */
if (MV_FALSE == mvCtrlPwrClckGet(PEX_UNIT_ID, 0))
return;
for (pciIf = 0; pciIf < maxif; pciIf++)
{
retval = mvSysPexInit(pciIf, MV_PEX_ROOT_COMPLEX);
if(retval == MV_NO_SUCH){
//printk("pci_init:no such calling mvPexInit for PEX-%x\n",pciIf);
continue;
}
if (retval != MV_OK)
{
printk("pci_init:Error calling mvPexInit for PEX-%x\n",pciIf);
continue;
}
/* unmask inter A/B/C/D */
//printk("writing %x tp %x \n",MV_PCI_MASK_ABCD, MV_PCI_MASK_REG(pciIf) );
MV_REG_WRITE(MV_PCI_MASK_REG(pciIf), MV_PCI_MASK_ABCD );
/* init PCI express error handling */
mv_pci_error_init(pciIf);
/* remmap IO !! */
win.baseLow = (pciIf? PEX1_IO_BASE : PEX0_IO_BASE) - IO_SPACE_REMAP;
win.baseHigh = 0x0;
win.size = pciIf? PEX1_IO_SIZE : PEX0_IO_SIZE;
mvCpuIfPciIfRemap((pciIf? PCI_IF1_IO : PCI_IF0_IO), &win);
}
}
/**
* mv_pci_error_init
* DESCRIPTION: init PCI express error handling
* INPUTS: pciIf - number of pex device
* OUTPUTS: N/A
* RETURNS: N/A
**/
void mv_pci_error_init(u32 pciIf)
{
MV_U32 reg_val;
/* enable PCI express error handling */
MV_REG_BIT_SET(MV_IRQ_MASK_ERROR_REG, (1 << (PEX_ERR_IRQ_NUM(pciIf) - IRQ_ERROR(0))));
/* init pex_err structure per each pex */
pex_err[pciIf].ifNumber = pciIf;
snprintf(pex_err[pciIf].irq_name, PCI_ERR_NAME_LEN, "error_pex%d", pciIf);
/* register interrupt for PCI express error */
if (request_irq(PEX_ERR_IRQ_NUM(pciIf), pex_error_interrupt, IRQF_DISABLED,
(const char*)pex_err[pciIf].irq_name, &pex_err[pciIf].ifNumber) < 0) {
panic("Could not allocate IRQ for PCI express error!");
}
/* init PCI Express Interrupt Mask Register */
/* get current value of Interrupt Mask Register */
reg_val = MV_REG_READ(MV_PCI_MASK_REG(pciIf));
/* set relevant mask to Interrupt Mask Register */
MV_REG_WRITE(MV_PCI_MASK_REG(pciIf), (reg_val | MV_PCI_MASK_ERR));
}
/* Currentlly the PCI config read/write are implemented as read modify write
to 32 bit.
TBD: adjust it to realy use 1/2/4 byte(partial) read/write, after the pex
read config WA will be removed.
*/
static int mv_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 *val)
{
MV_U32 bus_num,func,regOff,dev_no,temp;
MV_U32 localBus;
struct pci_sys_data *sysdata = (struct pci_sys_data *)bus->sysdata;
u32 pciIf = sysdata->mv_controller_num;
*val = 0xffffffff;
/* Check Power State */
if (MV_FALSE == mvCtrlPwrClckGet(PEX_UNIT_ID, 0))
return 0;
bus_num = bus->number;
dev_no = PCI_SLOT(devfn);
/* don't return for our device */
localBus = mvPexLocalBusNumGet(pciIf);
if((dev_no == 0) && ( bus_num == localBus))
{
DB(printk("PCI %x read from our own dev return 0xffffffff \n", pciIf));
return 0xffffffff;
}
func = PCI_FUNC(devfn);
regOff = (MV_U32)where & PXCAR_REG_NUM_MASK;
#if PCI0_IF_PTP
/* WA: use only the first function of the bridge and te first bus*/
if( (bus_num == mvPexLocalBusNumGet(pciIf)) && (dev_no == 1) && (func != 0) )
{
DB(printk("PCI %x read from bridge func != 0 return 0xffffffff \n",pciIf));
return 0xffffffff;
}
#endif
if ((func == 0)&&(dev_no < 2))
{
DB(printk("PCI %x read: bus = %x dev = %x func = %x regOff = %x ",pciIf, bus_num,dev_no,func,regOff));
}
temp = (u32) mvPexConfigRead(pciIf, bus_num, dev_no, func, regOff);
switch (size) {
case 1:
temp = (temp >> (8*(where & 0x3))) & 0xff;
break;
case 2:
temp = (temp >> (8*(where & 0x2))) & 0xffff;
break;
default:
break;
}
*val = temp;
if ((func == 0)&&(dev_no < 2))
{
DB(printk(" got %x \n",temp));
}
return 0;
}
static int mv_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 val)
{
MV_U32 bus_num,func,regOff,dev_no,temp, mask , shift;
struct pci_sys_data *sysdata = (struct pci_sys_data *)bus->sysdata;
u32 pciIf = sysdata->mv_controller_num;
bus_num = bus->number;
dev_no = PCI_SLOT(devfn);
func = PCI_FUNC(devfn);
regOff = (MV_U32)where & PXCAR_REG_NUM_MASK;
DB(printk("PCI %x: writing data %x size %x to bus %x dev %x func %x offs %x \n",pciIf, val,size,bus_num,dev_no,func,regOff));
if( size != 4)
{
temp = (u32) mvPexConfigRead(pciIf, bus_num, dev_no, func, regOff);
}
else
{
temp = val;
}
switch (size) {
case 1:
shift = (8*(where & 0x3));
mask = 0xff;
break;
case 2:
shift = (8*(where & 0x2));
mask = 0xffff;
break;
default:
shift = 0;
mask = 0xffffffff;
break;
}
temp = (temp & (~(mask<<shift))) | ((val & mask) << shift);
mvPexConfigWrite(pciIf,bus_num,dev_no,func,regOff,temp);
return 0;
}
static struct pci_ops mv_pci_ops = {
.read = mv_pci_read_config,
.write = mv_pci_write_config,
};
int __init mv_pci_setup(int nr, struct pci_sys_data *sys)
{
struct resource *res;
switch (nr) {
case 0:
sys->map_irq = mv_map_irq_0;
break;
case 1:
sys->map_irq = mv_map_irq_1;
break;
default:
printk("mv_pci_setup: nr(%d) out of scope\n",nr);
return 0;
}
res = kmalloc(sizeof(struct resource) * 2, GFP_KERNEL);
if (!res)
panic("PCI: unable to alloc resources");
memset(res, 0, sizeof(struct resource) * 2);
if(!nr) {
res[0].start = PEX0_IO_BASE - IO_SPACE_REMAP;
res[0].end = PEX0_IO_BASE - IO_SPACE_REMAP + PEX0_IO_SIZE - 1;
res[0].name = "PEX0 IO";
res[0].flags = IORESOURCE_IO;
res[1].start = PEX0_MEM_BASE;
res[1].end = PEX0_MEM_BASE + PEX0_MEM_SIZE - 1;
res[1].name = "PEX0 Memory";
res[1].flags = IORESOURCE_MEM;
} else {
res[0].start = PEX1_IO_BASE - IO_SPACE_REMAP;
res[0].end = PEX1_IO_BASE - IO_SPACE_REMAP + PEX1_IO_SIZE - 1;
res[0].name = "PEX1 IO";
res[0].flags = IORESOURCE_IO;
res[1].start = PEX1_MEM_BASE;
res[1].end = PEX1_MEM_BASE + PEX1_MEM_SIZE - 1;
res[1].name = "PEX1 Memory";
res[1].flags = IORESOURCE_MEM;
}
if (request_resource(&ioport_resource, &res[0]))
{
printk ("IO Request resource failed - Pci If %x\n",nr);
}
if (request_resource(&iomem_resource, &res[1]))
{
printk ("Memory Request resource failed - Pci If %x\n",nr);
}
sys->resource[0] = &res[0];
sys->resource[1] = &res[1];
sys->resource[2] = NULL;
sys->io_offset = 0x0;
sys->mv_controller_num = nr;
return 1;
}
struct pci_bus *mv_pci_scan_bus(int nr, struct pci_sys_data *sys)
{
struct pci_ops *ops = &mv_pci_ops;
struct pci_bus *bus;
bus = pci_scan_bus(sys->busnr, ops, sys);
if(sys->mv_controller_num == 0)
mvPexLocalBusNumSet(1,
bus->number + bus->subordinate - bus->secondary + 1);
return bus;
}
/**
* pex_error_interrupt
* DESCRIPTION: PCI express error interrupt routine
* INPUTS: @irq: irq number
@dev_id: device id - ignored
* OUTPUTS: kernel error message
* RETURNS: IRQ_HANDLED
**/
static irqreturn_t pex_error_interrupt(int irq, void *dev_id)
{
MV_U32 reg_val;
MV_U32 ifPexNumber=*(MV_U32 *)dev_id;
/* get current value of Interrupt Cause Register */
reg_val = MV_REG_READ(MV_PCI_IRQ_CAUSE_REG(ifPexNumber));
printk(KERN_ERR "PCI express error: irq - %d, Pex number: %d, "
"Interrupt Cause Register value: %x \n", irq, ifPexNumber, reg_val);
MV_REG_WRITE(MV_PCI_IRQ_CAUSE_REG(ifPexNumber), ~reg_val);
return IRQ_HANDLED;
}
static int __init mv_map_irq_0(struct pci_dev *dev, u8 slot, u8 pin)
{
return PEX0_IRQ_NUM;
}
static int __init mv_map_irq_1(struct pci_dev *dev, u8 slot, u8 pin)
{
return PEX1_IRQ_NUM;
}
static struct hw_pci mv_pci __initdata = {
.swizzle = pci_std_swizzle,
.map_irq = mv_map_irq_0,
.setup = mv_pci_setup,
.scan = mv_pci_scan_bus,
.preinit = mv_pci_preinit,
};
static int __init mv_pci_init(void)
{
#if defined(MV_INCLUDE_CLK_PWR_CNTRL)
/* Check pex power state */
MV_U32 pexPower;
pexPower = mvCtrlPwrClckGet(PEX_UNIT_ID,0);
if (pexPower == MV_FALSE)
{
printk("\nWarning PCI-E is Powered Off\n");
return 0;
}
#endif
mv_pci.nr_controllers = mvCtrlPexMaxIfGet();
pci_common_init(&mv_pci);
return 0;
}
subsys_initcall(mv_pci_init);