forked from Minki/linux
[POWERPC] celleb: Add support for PCI Express
This adds support for PCI Express port on Celleb. I/O space of this PCI Express port is not mapped in memory space. So we use the io-workaround mechanism to make accesses indirect. Signed-off-by: Kou Ishizaki <kou.ishizaki@toshiba.co.jp> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
parent
ad2c698797
commit
884d04cd8d
@ -33,6 +33,7 @@ obj-$(CONFIG_PCI_MSI) += axon_msi.o
|
||||
ifeq ($(CONFIG_PPC_CELLEB),y)
|
||||
obj-y += celleb_setup.o \
|
||||
celleb_pci.o celleb_scc_epci.o \
|
||||
celleb_scc_pciex.o \
|
||||
celleb_scc_uhc.o \
|
||||
io-workarounds.o spider-pci.o \
|
||||
beat.o beat_htab.o beat_hvCall.o \
|
||||
|
@ -467,6 +467,9 @@ static struct of_device_id celleb_phb_match[] __initdata = {
|
||||
}, {
|
||||
.name = "epci",
|
||||
.data = &celleb_epci_spec,
|
||||
}, {
|
||||
.name = "pcie",
|
||||
.data = &celleb_pciex_spec,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
@ -40,5 +40,6 @@ extern int celleb_setup_phb(struct pci_controller *);
|
||||
extern int celleb_pci_probe_mode(struct pci_bus *);
|
||||
|
||||
extern struct celleb_phb_spec celleb_epci_spec;
|
||||
extern struct celleb_phb_spec celleb_pciex_spec;
|
||||
|
||||
#endif /* _CELLEB_PCI_H */
|
||||
|
@ -125,6 +125,93 @@
|
||||
/* bits for SCC_EPCI_CNTOPT */
|
||||
#define SCC_EPCI_CNTOPT_O2PMB 0x00000002
|
||||
|
||||
/* SCC PCIEXC SMMIO registers */
|
||||
#define PEXCADRS 0x000
|
||||
#define PEXCWDATA 0x004
|
||||
#define PEXCRDATA 0x008
|
||||
#define PEXDADRS 0x010
|
||||
#define PEXDCMND 0x014
|
||||
#define PEXDWDATA 0x018
|
||||
#define PEXDRDATA 0x01c
|
||||
#define PEXREQID 0x020
|
||||
#define PEXTIDMAP 0x024
|
||||
#define PEXINTMASK 0x028
|
||||
#define PEXINTSTS 0x02c
|
||||
#define PEXAERRMASK 0x030
|
||||
#define PEXAERRSTS 0x034
|
||||
#define PEXPRERRMASK 0x040
|
||||
#define PEXPRERRSTS 0x044
|
||||
#define PEXPRERRID01 0x048
|
||||
#define PEXPRERRID23 0x04c
|
||||
#define PEXVDMASK 0x050
|
||||
#define PEXVDSTS 0x054
|
||||
#define PEXRCVCPLIDA 0x060
|
||||
#define PEXLENERRIDA 0x068
|
||||
#define PEXPHYPLLST 0x070
|
||||
#define PEXDMRDEN0 0x100
|
||||
#define PEXDMRDADR0 0x104
|
||||
#define PEXDMRDENX 0x110
|
||||
#define PEXDMRDADRX 0x114
|
||||
#define PEXECMODE 0xf00
|
||||
#define PEXMAEA(n) (0xf50 + (8 * n))
|
||||
#define PEXMAEC(n) (0xf54 + (8 * n))
|
||||
#define PEXCCRCTRL 0xff0
|
||||
|
||||
/* SCC PCIEXC bits and shifts for PEXCADRS */
|
||||
#define PEXCADRS_BYTE_EN_SHIFT 20
|
||||
#define PEXCADRS_CMD_SHIFT 16
|
||||
#define PEXCADRS_CMD_READ (0xa << PEXCADRS_CMD_SHIFT)
|
||||
#define PEXCADRS_CMD_WRITE (0xb << PEXCADRS_CMD_SHIFT)
|
||||
|
||||
/* SCC PCIEXC shifts for PEXDADRS */
|
||||
#define PEXDADRS_BUSNO_SHIFT 20
|
||||
#define PEXDADRS_DEVNO_SHIFT 15
|
||||
#define PEXDADRS_FUNCNO_SHIFT 12
|
||||
|
||||
/* SCC PCIEXC bits and shifts for PEXDCMND */
|
||||
#define PEXDCMND_BYTE_EN_SHIFT 4
|
||||
#define PEXDCMND_IO_READ 0x2
|
||||
#define PEXDCMND_IO_WRITE 0x3
|
||||
#define PEXDCMND_CONFIG_READ 0xa
|
||||
#define PEXDCMND_CONFIG_WRITE 0xb
|
||||
|
||||
/* SCC PCIEXC bits for PEXPHYPLLST */
|
||||
#define PEXPHYPLLST_PEXPHYAPLLST 0x00000001
|
||||
|
||||
/* SCC PCIEXC bits for PEXECMODE */
|
||||
#define PEXECMODE_ALL_THROUGH 0x00000000
|
||||
#define PEXECMODE_ALL_8BIT 0x00550155
|
||||
#define PEXECMODE_ALL_16BIT 0x00aa02aa
|
||||
|
||||
/* SCC PCIEXC bits for PEXCCRCTRL */
|
||||
#define PEXCCRCTRL_PEXIPCOREEN 0x00040000
|
||||
#define PEXCCRCTRL_PEXIPCONTEN 0x00020000
|
||||
#define PEXCCRCTRL_PEXPHYPLLEN 0x00010000
|
||||
#define PEXCCRCTRL_PCIEXCAOCKEN 0x00000100
|
||||
|
||||
/* SCC PCIEXC port configuration registers */
|
||||
#define PEXTCERRCHK 0x21c
|
||||
#define PEXTAMAPB0 0x220
|
||||
#define PEXTAMAPL0 0x224
|
||||
#define PEXTAMAPB(n) (PEXTAMAPB0 + 8 * (n))
|
||||
#define PEXTAMAPL(n) (PEXTAMAPL0 + 8 * (n))
|
||||
#define PEXCHVC0P 0x500
|
||||
#define PEXCHVC0NP 0x504
|
||||
#define PEXCHVC0C 0x508
|
||||
#define PEXCDVC0P 0x50c
|
||||
#define PEXCDVC0NP 0x510
|
||||
#define PEXCDVC0C 0x514
|
||||
#define PEXCHVCXP 0x518
|
||||
#define PEXCHVCXNP 0x51c
|
||||
#define PEXCHVCXC 0x520
|
||||
#define PEXCDVCXP 0x524
|
||||
#define PEXCDVCXNP 0x528
|
||||
#define PEXCDVCXC 0x52c
|
||||
#define PEXCTTRG 0x530
|
||||
#define PEXTSCTRL 0x700
|
||||
#define PEXTSSTS 0x704
|
||||
#define PEXSKPCTRL 0x708
|
||||
|
||||
/* UHC registers */
|
||||
#define SCC_UHC_CKRCTRL 0xff0
|
||||
#define SCC_UHC_ECMODE 0xf00
|
||||
|
547
arch/powerpc/platforms/cell/celleb_scc_pciex.c
Normal file
547
arch/powerpc/platforms/cell/celleb_scc_pciex.c
Normal file
@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Support for Celleb PCI-Express.
|
||||
*
|
||||
* (C) Copyright 2007-2008 TOSHIBA CORPORATION
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "celleb_scc.h"
|
||||
#include "celleb_pci.h"
|
||||
|
||||
#define PEX_IN(base, off) in_be32((void *)(base) + (off))
|
||||
#define PEX_OUT(base, off, data) out_be32((void *)(base) + (off), (data))
|
||||
|
||||
static void scc_pciex_io_flush(struct iowa_bus *bus)
|
||||
{
|
||||
(void)PEX_IN(bus->phb->cfg_addr, PEXDMRDEN0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory space access to device on PCIEX
|
||||
*/
|
||||
#define PCIEX_MMIO_READ(name, ret) \
|
||||
static ret scc_pciex_##name(const PCI_IO_ADDR addr) \
|
||||
{ \
|
||||
ret val = __do_##name(addr); \
|
||||
scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
#define PCIEX_MMIO_READ_STR(name) \
|
||||
static void scc_pciex_##name(const PCI_IO_ADDR addr, void *buf, \
|
||||
unsigned long count) \
|
||||
{ \
|
||||
__do_##name(addr, buf, count); \
|
||||
scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
|
||||
}
|
||||
|
||||
PCIEX_MMIO_READ(readb, u8)
|
||||
PCIEX_MMIO_READ(readw, u16)
|
||||
PCIEX_MMIO_READ(readl, u32)
|
||||
PCIEX_MMIO_READ(readq, u64)
|
||||
PCIEX_MMIO_READ(readw_be, u16)
|
||||
PCIEX_MMIO_READ(readl_be, u32)
|
||||
PCIEX_MMIO_READ(readq_be, u64)
|
||||
PCIEX_MMIO_READ_STR(readsb)
|
||||
PCIEX_MMIO_READ_STR(readsw)
|
||||
PCIEX_MMIO_READ_STR(readsl)
|
||||
|
||||
static void scc_pciex_memcpy_fromio(void *dest, const PCI_IO_ADDR src,
|
||||
unsigned long n)
|
||||
{
|
||||
__do_memcpy_fromio(dest, src, n);
|
||||
scc_pciex_io_flush(iowa_mem_find_bus(src));
|
||||
}
|
||||
|
||||
/*
|
||||
* I/O port access to devices on PCIEX.
|
||||
*/
|
||||
|
||||
static inline unsigned long get_bus_address(struct pci_controller *phb,
|
||||
unsigned long port)
|
||||
{
|
||||
return port - ((unsigned long)(phb->io_base_virt) - _IO_BASE);
|
||||
}
|
||||
|
||||
static u32 scc_pciex_read_port(struct pci_controller *phb,
|
||||
unsigned long port, int size)
|
||||
{
|
||||
unsigned int byte_enable;
|
||||
unsigned int cmd, shift;
|
||||
unsigned long addr;
|
||||
u32 data, ret;
|
||||
|
||||
BUG_ON(((port & 0x3ul) + size) > 4);
|
||||
|
||||
addr = get_bus_address(phb, port);
|
||||
shift = addr & 0x3ul;
|
||||
byte_enable = ((1 << size) - 1) << shift;
|
||||
cmd = PEXDCMND_IO_READ | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
|
||||
PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
|
||||
PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
|
||||
data = PEX_IN(phb->cfg_addr, PEXDRDATA);
|
||||
ret = (data >> (shift * 8)) & (0xFFFFFFFF >> ((4 - size) * 8));
|
||||
|
||||
pr_debug("PCIEX:PIO READ:port=0x%lx, addr=0x%lx, size=%d, be=%x,"
|
||||
" cmd=%x, data=%x, ret=%x\n", port, addr, size, byte_enable,
|
||||
cmd, data, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void scc_pciex_write_port(struct pci_controller *phb,
|
||||
unsigned long port, int size, u32 val)
|
||||
{
|
||||
unsigned int byte_enable;
|
||||
unsigned int cmd, shift;
|
||||
unsigned long addr;
|
||||
u32 data;
|
||||
|
||||
BUG_ON(((port & 0x3ul) + size) > 4);
|
||||
|
||||
addr = get_bus_address(phb, port);
|
||||
shift = addr & 0x3ul;
|
||||
byte_enable = ((1 << size) - 1) << shift;
|
||||
cmd = PEXDCMND_IO_WRITE | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
|
||||
data = (val & (0xFFFFFFFF >> (4 - size) * 8)) << (shift * 8);
|
||||
PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
|
||||
PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
|
||||
PEX_OUT(phb->cfg_addr, PEXDWDATA, data);
|
||||
|
||||
pr_debug("PCIEX:PIO WRITE:port=0x%lx, addr=%lx, size=%d, val=%x,"
|
||||
" be=%x, cmd=%x, data=%x\n", port, addr, size, val,
|
||||
byte_enable, cmd, data);
|
||||
}
|
||||
|
||||
static u8 __scc_pciex_inb(struct pci_controller *phb, unsigned long port)
|
||||
{
|
||||
return (u8)scc_pciex_read_port(phb, port, 1);
|
||||
}
|
||||
|
||||
static u16 __scc_pciex_inw(struct pci_controller *phb, unsigned long port)
|
||||
{
|
||||
u32 data;
|
||||
if ((port & 0x3ul) < 3)
|
||||
data = scc_pciex_read_port(phb, port, 2);
|
||||
else {
|
||||
u32 d1 = scc_pciex_read_port(phb, port, 1);
|
||||
u32 d2 = scc_pciex_read_port(phb, port + 1, 1);
|
||||
data = d1 | (d2 << 8);
|
||||
}
|
||||
return (u16)data;
|
||||
}
|
||||
|
||||
static u32 __scc_pciex_inl(struct pci_controller *phb, unsigned long port)
|
||||
{
|
||||
unsigned int mod = port & 0x3ul;
|
||||
u32 data;
|
||||
if (mod == 0)
|
||||
data = scc_pciex_read_port(phb, port, 4);
|
||||
else {
|
||||
u32 d1 = scc_pciex_read_port(phb, port, 4 - mod);
|
||||
u32 d2 = scc_pciex_read_port(phb, port + 1, mod);
|
||||
data = d1 | (d2 << (mod * 8));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void __scc_pciex_outb(struct pci_controller *phb,
|
||||
u8 val, unsigned long port)
|
||||
{
|
||||
scc_pciex_write_port(phb, port, 1, (u32)val);
|
||||
}
|
||||
|
||||
static void __scc_pciex_outw(struct pci_controller *phb,
|
||||
u16 val, unsigned long port)
|
||||
{
|
||||
if ((port & 0x3ul) < 3)
|
||||
scc_pciex_write_port(phb, port, 2, (u32)val);
|
||||
else {
|
||||
u32 d1 = val & 0x000000FF;
|
||||
u32 d2 = (val & 0x0000FF00) >> 8;
|
||||
scc_pciex_write_port(phb, port, 1, d1);
|
||||
scc_pciex_write_port(phb, port + 1, 1, d2);
|
||||
}
|
||||
}
|
||||
|
||||
static void __scc_pciex_outl(struct pci_controller *phb,
|
||||
u32 val, unsigned long port)
|
||||
{
|
||||
unsigned int mod = port & 0x3ul;
|
||||
if (mod == 0)
|
||||
scc_pciex_write_port(phb, port, 4, val);
|
||||
else {
|
||||
u32 d1 = val & (0xFFFFFFFFul >> (mod * 8));
|
||||
u32 d2 = val >> ((4 - mod) * 8);
|
||||
scc_pciex_write_port(phb, port, 4 - mod, d1);
|
||||
scc_pciex_write_port(phb, port + 1, mod, d2);
|
||||
}
|
||||
}
|
||||
|
||||
#define PCIEX_PIO_FUNC(size, name) \
|
||||
static u##size scc_pciex_in##name(unsigned long port) \
|
||||
{ \
|
||||
struct iowa_bus *bus = iowa_pio_find_bus(port); \
|
||||
u##size data = __scc_pciex_in##name(bus->phb, port); \
|
||||
scc_pciex_io_flush(bus); \
|
||||
return data; \
|
||||
} \
|
||||
static void scc_pciex_ins##name(unsigned long p, void *b, unsigned long c) \
|
||||
{ \
|
||||
struct iowa_bus *bus = iowa_pio_find_bus(p); \
|
||||
u##size *dst = b; \
|
||||
for (; c != 0; c--, dst++) \
|
||||
*dst = cpu_to_le##size(__scc_pciex_in##name(bus->phb, p)); \
|
||||
scc_pciex_io_flush(bus); \
|
||||
} \
|
||||
static void scc_pciex_out##name(u##size val, unsigned long port) \
|
||||
{ \
|
||||
struct iowa_bus *bus = iowa_pio_find_bus(port); \
|
||||
__scc_pciex_out##name(bus->phb, val, port); \
|
||||
} \
|
||||
static void scc_pciex_outs##name(unsigned long p, const void *b, \
|
||||
unsigned long c) \
|
||||
{ \
|
||||
struct iowa_bus *bus = iowa_pio_find_bus(p); \
|
||||
const u##size *src = b; \
|
||||
for (; c != 0; c--, src++) \
|
||||
__scc_pciex_out##name(bus->phb, le##size##_to_cpu(*src), p); \
|
||||
}
|
||||
#define cpu_to_le8(x) (x)
|
||||
#define le8_to_cpu(x) (x)
|
||||
PCIEX_PIO_FUNC(8, b)
|
||||
PCIEX_PIO_FUNC(16, w)
|
||||
PCIEX_PIO_FUNC(32, l)
|
||||
|
||||
static struct ppc_pci_io scc_pciex_ops = {
|
||||
.readb = scc_pciex_readb,
|
||||
.readw = scc_pciex_readw,
|
||||
.readl = scc_pciex_readl,
|
||||
.readq = scc_pciex_readq,
|
||||
.readw_be = scc_pciex_readw_be,
|
||||
.readl_be = scc_pciex_readl_be,
|
||||
.readq_be = scc_pciex_readq_be,
|
||||
.readsb = scc_pciex_readsb,
|
||||
.readsw = scc_pciex_readsw,
|
||||
.readsl = scc_pciex_readsl,
|
||||
.memcpy_fromio = scc_pciex_memcpy_fromio,
|
||||
.inb = scc_pciex_inb,
|
||||
.inw = scc_pciex_inw,
|
||||
.inl = scc_pciex_inl,
|
||||
.outb = scc_pciex_outb,
|
||||
.outw = scc_pciex_outw,
|
||||
.outl = scc_pciex_outl,
|
||||
.insb = scc_pciex_insb,
|
||||
.insw = scc_pciex_insw,
|
||||
.insl = scc_pciex_insl,
|
||||
.outsb = scc_pciex_outsb,
|
||||
.outsw = scc_pciex_outsw,
|
||||
.outsl = scc_pciex_outsl,
|
||||
};
|
||||
|
||||
static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data)
|
||||
{
|
||||
dma_addr_t dummy_page_da;
|
||||
void *dummy_page_va;
|
||||
|
||||
dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!dummy_page_va) {
|
||||
pr_err("PCIEX:Alloc dummy_page_va failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va,
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dummy_page_da)) {
|
||||
pr_err("PCIEX:Map dummy page failed.\n");
|
||||
kfree(dummy_page_va);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PEX_OUT(bus->phb->cfg_addr, PEXDMRDADR0, dummy_page_da);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* config space access
|
||||
*/
|
||||
#define MK_PEXDADRS(bus_no, dev_no, func_no, addr) \
|
||||
((uint32_t)(((addr) & ~0x3UL) | \
|
||||
((bus_no) << PEXDADRS_BUSNO_SHIFT) | \
|
||||
((dev_no) << PEXDADRS_DEVNO_SHIFT) | \
|
||||
((func_no) << PEXDADRS_FUNCNO_SHIFT)))
|
||||
|
||||
#define MK_PEXDCMND_BYTE_EN(addr, size) \
|
||||
((((0x1 << (size))-1) << ((addr) & 0x3)) << PEXDCMND_BYTE_EN_SHIFT)
|
||||
#define MK_PEXDCMND(cmd, addr, size) ((cmd) | MK_PEXDCMND_BYTE_EN(addr, size))
|
||||
|
||||
static uint32_t config_read_pciex_dev(unsigned int *base,
|
||||
uint64_t bus_no, uint64_t dev_no, uint64_t func_no,
|
||||
uint64_t off, uint64_t size)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint32_t addr, cmd;
|
||||
|
||||
addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
|
||||
cmd = MK_PEXDCMND(PEXDCMND_CONFIG_READ, off, size);
|
||||
PEX_OUT(base, PEXDADRS, addr);
|
||||
PEX_OUT(base, PEXDCMND, cmd);
|
||||
ret = (PEX_IN(base, PEXDRDATA)
|
||||
>> ((off & (4-size)) * 8)) & ((0x1 << (size * 8)) - 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void config_write_pciex_dev(unsigned int *base, uint64_t bus_no,
|
||||
uint64_t dev_no, uint64_t func_no, uint64_t off, uint64_t size,
|
||||
uint32_t data)
|
||||
{
|
||||
uint32_t addr, cmd;
|
||||
|
||||
addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
|
||||
cmd = MK_PEXDCMND(PEXDCMND_CONFIG_WRITE, off, size);
|
||||
PEX_OUT(base, PEXDADRS, addr);
|
||||
PEX_OUT(base, PEXDCMND, cmd);
|
||||
PEX_OUT(base, PEXDWDATA,
|
||||
(data & ((0x1 << (size * 8)) - 1)) << ((off & (4-size)) * 8));
|
||||
}
|
||||
|
||||
#define MK_PEXCADRS_BYTE_EN(off, len) \
|
||||
((((0x1 << (len)) - 1) << ((off) & 0x3)) << PEXCADRS_BYTE_EN_SHIFT)
|
||||
#define MK_PEXCADRS(cmd, addr, size) \
|
||||
((cmd) | MK_PEXCADRS_BYTE_EN(addr, size) | ((addr) & ~0x3))
|
||||
static uint32_t config_read_pciex_rc(unsigned int *base,
|
||||
uint32_t where, uint32_t size)
|
||||
{
|
||||
PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_READ, where, size));
|
||||
return (PEX_IN(base, PEXCRDATA)
|
||||
>> ((where & (4 - size)) * 8)) & ((0x1 << (size * 8)) - 1);
|
||||
}
|
||||
|
||||
static void config_write_pciex_rc(unsigned int *base, uint32_t where,
|
||||
uint32_t size, uint32_t val)
|
||||
{
|
||||
uint32_t data;
|
||||
|
||||
data = (val & ((0x1 << (size * 8)) - 1)) << ((where & (4 - size)) * 8);
|
||||
PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_WRITE, where, size));
|
||||
PEX_OUT(base, PEXCWDATA, data);
|
||||
}
|
||||
|
||||
/* Interfaces */
|
||||
/* Note: Work-around
|
||||
* On SCC PCIEXC, one device is seen on all 32 dev_no.
|
||||
* As SCC PCIEXC can have only one device on the bus, we look only one dev_no.
|
||||
* (dev_no = 1)
|
||||
*/
|
||||
static int scc_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, unsigned int *val)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct pci_controller *phb;
|
||||
|
||||
dn = bus->sysdata;
|
||||
phb = pci_find_hose_for_OF_device(dn);
|
||||
|
||||
if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) {
|
||||
*val = ~0;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (bus->number == 0 && PCI_SLOT(devfn) == 0)
|
||||
*val = config_read_pciex_rc(phb->cfg_addr, where, size);
|
||||
else
|
||||
*val = config_read_pciex_dev(phb->cfg_addr, bus->number,
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int scc_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, unsigned int val)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct pci_controller *phb;
|
||||
|
||||
dn = bus->sysdata;
|
||||
phb = pci_find_hose_for_OF_device(dn);
|
||||
|
||||
if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (bus->number == 0 && PCI_SLOT(devfn) == 0)
|
||||
config_write_pciex_rc(phb->cfg_addr, where, size, val);
|
||||
else
|
||||
config_write_pciex_dev(phb->cfg_addr, bus->number,
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static struct pci_ops scc_pciex_pci_ops = {
|
||||
scc_pciex_read_config,
|
||||
scc_pciex_write_config,
|
||||
};
|
||||
|
||||
static void pciex_clear_intr_all(unsigned int *base)
|
||||
{
|
||||
PEX_OUT(base, PEXAERRSTS, 0xffffffff);
|
||||
PEX_OUT(base, PEXPRERRSTS, 0xffffffff);
|
||||
PEX_OUT(base, PEXINTSTS, 0xffffffff);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void pciex_disable_intr_all(unsigned int *base)
|
||||
{
|
||||
PEX_OUT(base, PEXINTMASK, 0x0);
|
||||
PEX_OUT(base, PEXAERRMASK, 0x0);
|
||||
PEX_OUT(base, PEXPRERRMASK, 0x0);
|
||||
PEX_OUT(base, PEXVDMASK, 0x0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pciex_enable_intr_all(unsigned int *base)
|
||||
{
|
||||
PEX_OUT(base, PEXINTMASK, 0x0000e7f1);
|
||||
PEX_OUT(base, PEXAERRMASK, 0x03ff01ff);
|
||||
PEX_OUT(base, PEXPRERRMASK, 0x0001010f);
|
||||
PEX_OUT(base, PEXVDMASK, 0x00000001);
|
||||
}
|
||||
|
||||
static void pciex_check_status(unsigned int *base)
|
||||
{
|
||||
uint32_t err = 0;
|
||||
uint32_t intsts, aerr, prerr, rcvcp, lenerr;
|
||||
uint32_t maea, maec;
|
||||
|
||||
intsts = PEX_IN(base, PEXINTSTS);
|
||||
aerr = PEX_IN(base, PEXAERRSTS);
|
||||
prerr = PEX_IN(base, PEXPRERRSTS);
|
||||
rcvcp = PEX_IN(base, PEXRCVCPLIDA);
|
||||
lenerr = PEX_IN(base, PEXLENERRIDA);
|
||||
|
||||
if (intsts || aerr || prerr || rcvcp || lenerr)
|
||||
err = 1;
|
||||
|
||||
pr_info("PCEXC interrupt!!\n");
|
||||
pr_info("PEXINTSTS :0x%08x\n", intsts);
|
||||
pr_info("PEXAERRSTS :0x%08x\n", aerr);
|
||||
pr_info("PEXPRERRSTS :0x%08x\n", prerr);
|
||||
pr_info("PEXRCVCPLIDA :0x%08x\n", rcvcp);
|
||||
pr_info("PEXLENERRIDA :0x%08x\n", lenerr);
|
||||
|
||||
/* print detail of Protection Error */
|
||||
if (intsts & 0x00004000) {
|
||||
uint32_t i, n;
|
||||
for (i = 0; i < 4; i++) {
|
||||
n = 1 << i;
|
||||
if (prerr & n) {
|
||||
maea = PEX_IN(base, PEXMAEA(i));
|
||||
maec = PEX_IN(base, PEXMAEC(i));
|
||||
pr_info("PEXMAEC%d :0x%08x\n", i, maec);
|
||||
pr_info("PEXMAEA%d :0x%08x\n", i, maea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
pciex_clear_intr_all(base);
|
||||
}
|
||||
|
||||
static irqreturn_t pciex_handle_internal_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct pci_controller *phb = dev_id;
|
||||
|
||||
pr_debug("PCIEX:pciex_handle_internal_irq(irq=%d)\n", irq);
|
||||
|
||||
BUG_ON(phb->cfg_addr == NULL);
|
||||
|
||||
pciex_check_status(phb->cfg_addr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static __init int celleb_setup_pciex(struct device_node *node,
|
||||
struct pci_controller *phb)
|
||||
{
|
||||
struct resource r;
|
||||
struct of_irq oirq;
|
||||
int virq;
|
||||
|
||||
/* SMMIO registers; used inside this file */
|
||||
if (of_address_to_resource(node, 0, &r)) {
|
||||
pr_err("PCIEXC:Failed to get config resource.\n");
|
||||
return 1;
|
||||
}
|
||||
phb->cfg_addr = ioremap(r.start, r.end - r.start + 1);
|
||||
if (!phb->cfg_addr) {
|
||||
pr_err("PCIEXC:Failed to remap SMMIO region.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Not use cfg_data, cmd and data regs are near address reg */
|
||||
phb->cfg_data = NULL;
|
||||
|
||||
/* set pci_ops */
|
||||
phb->ops = &scc_pciex_pci_ops;
|
||||
|
||||
/* internal interrupt handler */
|
||||
if (of_irq_map_one(node, 1, &oirq)) {
|
||||
pr_err("PCIEXC:Failed to map irq\n");
|
||||
goto error;
|
||||
}
|
||||
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
|
||||
oirq.size);
|
||||
if (request_irq(virq, pciex_handle_internal_irq,
|
||||
IRQF_DISABLED, "pciex", (void *)phb)) {
|
||||
pr_err("PCIEXC:Failed to request irq\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* enable all interrupts */
|
||||
pciex_clear_intr_all(phb->cfg_addr);
|
||||
pciex_enable_intr_all(phb->cfg_addr);
|
||||
/* MSI: TBD */
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
phb->cfg_data = NULL;
|
||||
if (phb->cfg_addr)
|
||||
iounmap(phb->cfg_addr);
|
||||
phb->cfg_addr = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct celleb_phb_spec celleb_pciex_spec __initdata = {
|
||||
.setup = celleb_setup_pciex,
|
||||
.ops = &scc_pciex_ops,
|
||||
.iowa_init = &scc_pciex_iowa_init,
|
||||
};
|
Loading…
Reference in New Issue
Block a user