blob: 83a2a97c7abbc514d0daf5b4f2bc2206f06e8f6d [file] [log] [blame]
/*
* Copyright (C) 2009 - 2010 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <asm/debug.h>
#include <asm/brcmstb/brcmstb.h>
#include <spaces.h>
/* NOTE: all PHYSICAL addresses */
#if defined(CONFIG_BRCM_HAS_SATA2)
#define SATA_MEM_START (BCHP_PCIX_BRIDGE_GRB_REG_START + 0x10010000)
#define SATA_MEM_SIZE 0x00010000
#endif
/* internal controller registers for configuration reads/writes */
#define PCI_CFG_INDEX 0x04
#define PCI_CFG_DATA 0x08
#define PCIE_CFG_INDEX 0x00
#define PCIE_CFG_DATA 0x04
/* Use assigned bus numbers so "ops" can tell the controllers apart */
#define BRCM_BUSNO_PCI23 0x00
#define BRCM_BUSNO_SATA 0x01
#define BRCM_BUSNO_PCIE 0x02
#if defined(CONFIG_BCM7420)
/* 7420 uses the legacy register names */
#define BCHP_HIF_TOP_CTRL_PCI_MWIN_CTRL BCHP_HIF_TOP_CTRL_MWIN_CTRL
#define SET_BRIDGE_RESET(x) \
BDEV_WR_F_RB(HIF_RGR1_SW_RESET_1, PCIE_BRIDGE_SW_RESET, (x))
#define SET_PERST(x) \
BDEV_WR_F_RB(HIF_RGR1_SW_RESET_1, PCIE_SW_PERST, (x))
#define PCIE_IO_REG_START _AC(0xf1000000, UL)
#define PCIE_IO_REG_SIZE _AC(0x00000020, UL)
#else /* defined(CONFIG_BCM7420) */
#define SET_BRIDGE_RESET(x) \
BDEV_WR_F_RB(HIF_RGR1_SW_INIT_1, PCIE_BRIDGE_SW_INIT, (x))
#define SET_PERST(x) \
BDEV_WR_F_RB(HIF_RGR1_SW_INIT_1, PCIE_SW_PERST, (x))
#define PCIE_IO_REG_START BPHYSADDR(BCHP_PCIE_EXT_CFG_REG_START)
#define PCIE_IO_REG_SIZE 0x00000008
#endif /* defined(CONFIG_BCM7420) */
#if defined(CONFIG_BCM7420)
/*
* legacy interface - this can be combined with the 7420 code above,
* once 7425a0/a1 (both non-production revs) reach EOL
*/
#define PCIE_OUTBOUND_WIN(win, start, len) do { \
BDEV_WR(BCHP_PCIE_MISC_CPU_2_PCIE_MEM_WIN##win##_LO, \
(start) + MMIO_ENDIAN); \
BDEV_WR(BCHP_PCIE_MISC_CPU_2_PCIE_MEM_WIN##win##_HI, 0); \
} while (0)
#else /* defined(CONFIG_BCM7420) */
#define PCIE_OUTBOUND_WIN(win, start, len) do { \
BDEV_WR(BCHP_PCIE_MISC_CPU_2_PCIE_MEM_WIN##win##_LO, \
(start) + MMIO_ENDIAN); \
BDEV_WR(BCHP_PCIE_MISC_CPU_2_PCIE_MEM_WIN##win##_HI, 0); \
BDEV_WR(BCHP_PCIE_MISC_CPU_2_PCIE_MEM_WIN##win##_BASE_LIMIT, \
(((start) >> 20) << 4) | \
((((start) + (len) - 1) >> 20) << 20)); \
} while (0)
#endif
static int brcm_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *data);
static int brcm_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 data);
#if defined(CONFIG_BRCM_HAS_SATA2)
static int brcm_sata_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *data);
static int brcm_sata_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 data);
#endif
static inline int get_busno_pci23(void) { return BRCM_BUSNO_PCI23; }
#if defined(CONFIG_BRCM_HAS_SATA2)
static inline int get_busno_sata(void) { return BRCM_BUSNO_SATA; }
#endif
static inline int get_busno_pcie(void) { return BRCM_BUSNO_PCIE; }
static struct pci_ops brcmstb_pci_ops = {
.read = brcm_pci_read_config,
.write = brcm_pci_write_config,
};
#if defined(CONFIG_BRCM_HAS_SATA2)
static struct pci_ops brcmstb_sata_ops = {
.read = brcm_sata_read_config,
.write = brcm_sata_write_config,
};
static u32 sata_pci_reg[] = { [PCI_INTERRUPT_PIN] = 0x01 };
#endif
/*
* Notes on PCI IO space:
*
* This is only really supported by PCI2.3.
* The SATA PCI-X controller does support IO space, but libata never uses it.
* Our PCIe core does not support IO BARs at all. MEM only.
* The Linux PCI code insists on having IO resources and io_map_base for
* all PCI controllers, so the values for PCI-X and PCIe are totally bogus.
*/
#define IO_ADDR_PCI23 0x400
#define IO_ADDR_SATA (IO_ADDR_PCI23 + PCI_IO_SIZE)
#define IO_ADDR_PCIE (IO_ADDR_SATA + SATA_IO_SIZE)
#define SATA_IO_SIZE 0x400
#define PCIE_IO_SIZE 0x400
#define BOGUS_IO_MAP_BASE 1
/* pci_controller resources */
static struct resource pci23_mem_resource = {
.name = "External PCI2.3 MEM",
.start = PCI_MEM_START,
.end = PCI_MEM_START + PCI_MEM_SIZE - 1,
.flags = IORESOURCE_MEM,
};
#if defined(CONFIG_BRCM_HAS_SATA2)
static struct resource sata_mem_resource = {
.name = "Internal SATA PCI-X MEM",
.start = SATA_MEM_START,
.end = SATA_MEM_START + SATA_MEM_SIZE - 1,
.flags = IORESOURCE_MEM,
};
#endif
static struct resource pcie_mem_resource = {
.name = "External PCIe MEM",
.start = PCIE_MEM_START,
.end = PCIE_MEM_START + PCIE_MEM_SIZE - 1,
.flags = IORESOURCE_MEM,
};
static struct resource pci23_io_resource = {
.name = "External PCI2.3 IO",
.start = IO_ADDR_PCI23,
.end = IO_ADDR_PCI23 + PCI_IO_SIZE - 1,
.flags = IORESOURCE_IO,
};
#if defined(CONFIG_BRCM_HAS_SATA2)
static struct resource sata_io_resource = {
.name = "Internal SATA PCI-X IO (unavailable)",
.start = IO_ADDR_SATA,
.end = IO_ADDR_SATA + SATA_IO_SIZE - 1,
.flags = IORESOURCE_IO,
};
#endif
static struct resource pcie_io_resource = {
.name = "External PCIe IO (unavailable)",
.start = IO_ADDR_PCIE,
.end = IO_ADDR_PCIE + PCIE_IO_SIZE - 1,
.flags = IORESOURCE_MEM,
};
/* Definitions for each controller */
static struct pci_controller brcmstb_pci_controller = {
.pci_ops = &brcmstb_pci_ops,
.io_resource = &pci23_io_resource,
.mem_resource = &pci23_mem_resource,
.get_busno = &get_busno_pci23,
};
#if defined(CONFIG_BRCM_HAS_SATA2)
static struct pci_controller brcmstb_sata_controller = {
.pci_ops = &brcmstb_sata_ops,
.io_resource = &sata_io_resource,
.mem_resource = &sata_mem_resource,
.get_busno = &get_busno_sata,
.io_map_base = BOGUS_IO_MAP_BASE,
};
#endif
static struct pci_controller brcmstb_pcie_controller = {
.pci_ops = &brcmstb_pci_ops,
.io_resource = &pcie_io_resource,
.mem_resource = &pcie_mem_resource,
.get_busno = &get_busno_pcie,
.io_map_base = BOGUS_IO_MAP_BASE,
};
struct brcm_pci_bus {
struct pci_controller *controller;
char *name;
unsigned int hw_busnum;
unsigned int busnum_shift;
unsigned int slot_shift;
unsigned int func_shift;
int memory_hole;
unsigned long idx_reg;
unsigned long data_reg;
};
static struct brcm_pci_bus brcm_buses[] = {
[BRCM_BUSNO_PCI23] = {
&brcmstb_pci_controller, "PCI2.3", 0, 0, 11, 8, 0 },
#if defined(CONFIG_BRCM_HAS_SATA2)
[BRCM_BUSNO_SATA] = {
&brcmstb_sata_controller, "SATA", 0, 0, 15, 12, 1 },
#endif
[BRCM_BUSNO_PCIE] = {
&brcmstb_pcie_controller, "PCIe", 1, 20, 15, 12, 0 },
};
/***********************************************************************
* PCI/PCIX/PCIe Bridge setup
***********************************************************************/
#define SATA_MEM_ENABLE 1
#define SATA_BUS_MASTER_ENABLE 2
#define SATA_PERR_ENABLE 0x10
#define SATA_SERR_ENABLE 0x20
#define PCI_BUS_MASTER BCHP_PCI_CFG_STATUS_COMMAND_BUS_MASTER_MASK
#define PCI_IO_ENABLE BCHP_PCI_CFG_STATUS_COMMAND_MEMORY_SPACE_MASK
#define PCI_MEM_ENABLE BCHP_PCI_CFG_STATUS_COMMAND_IO_SPACE_MASK
#if defined(CONFIG_CPU_BIG_ENDIAN)
#define DATA_ENDIAN 2 /* PCI->DDR inbound accesses */
#define MMIO_ENDIAN 2 /* MIPS->PCI outbound accesses */
#else
#define DATA_ENDIAN 0
#define MMIO_ENDIAN 0
#endif
static inline void brcm_setup_sata_bridge(void)
{
#if defined(CONFIG_BRCM_HAS_SATA2)
/* Internal PCI-X SATA bridge setup for 7400, 7405, 7335 */
BDEV_WR(BCHP_PCIX_BRIDGE_PCIX_CTRL,
(SATA_MEM_ENABLE|SATA_BUS_MASTER_ENABLE|
SATA_PERR_ENABLE|SATA_SERR_ENABLE));
/* PCI slave window (SATA access to MIPS memory) */
BDEV_WR(BCHP_PCIX_BRIDGE_PCIX_SLV_MEM_WIN_BASE, 1);
BDEV_WR(BCHP_PCIX_BRIDGE_PCIX_SLV_MEM_WIN_MODE, DATA_ENDIAN);
/* PCI master window (MIPS access to SATA BARs) */
BDEV_WR(BCHP_PCIX_BRIDGE_CPU_TO_SATA_MEM_WIN_BASE,
SATA_MEM_START | MMIO_ENDIAN);
BDEV_WR(BCHP_PCIX_BRIDGE_CPU_TO_SATA_IO_WIN_BASE, MMIO_ENDIAN);
/* Set up MMIO BAR in PCI configuration space */
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_INDEX, PCI_BASE_ADDRESS_5);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_DATA, SATA_MEM_START);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_INDEX, PCI_COMMAND);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_DATA,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* PCI latency settings */
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_INDEX, PCI_INTERRUPT_LINE);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_DATA, 0x000f0100);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_INDEX, PCI_CACHE_LINE_SIZE);
BDEV_WR_RB(BCHP_PCIX_BRIDGE_SATA_CFG_DATA, 0x0080ff00);
/* Device ID in the emulated PCI configuration registers */
sata_pci_reg[PCI_CLASS_REVISION] = 0x01010f00;
sata_pci_reg[PCI_VENDOR_ID] = 0x860214e4;
#endif
}
static inline void brcm_setup_pci_bridge(void)
{
#if defined(CONFIG_BRCM_HAS_PCI23)
unsigned long win_size_mb;
/* External PCI bridge setup (most chips) */
BDEV_SET(BCHP_PCI_CFG_STATUS_COMMAND,
PCI_BUS_MASTER|PCI_IO_ENABLE|PCI_MEM_ENABLE);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_MEM_WIN0, PCI_MEM_START + 0x00000000 +
MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_MEM_WIN1, PCI_MEM_START + 0x08000000 +
MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_MEM_WIN2, PCI_MEM_START + 0x10000000 +
MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_MEM_WIN3, PCI_MEM_START + 0x18000000 +
MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_IO_WIN0, 0x00000000 | MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_IO_WIN1, 0x00200000 | MMIO_ENDIAN);
BDEV_WR(BCHP_PCI_CFG_CPU_2_PCI_IO_WIN2, 0x00400000 | MMIO_ENDIAN);
#ifdef BCHP_HIF_TOP_CTRL_PCI_MWIN_CTRL
/* force PCI->SDRAM window to 1GB on reduced-strap chips */
BDEV_WR_RB(BCHP_HIF_TOP_CTRL_PCI_MWIN_CTRL, 0x1);
#endif
BDEV_WR(BCHP_PCI_CFG_MEMORY_BASE_W0, 0xfffffff0);
win_size_mb = (~(BDEV_RD(BCHP_PCI_CFG_MEMORY_BASE_W0) & ~0xfUL) +
1UL) >> 20;
printk(KERN_INFO "PCI2.3->SDRAM window: %lu MB\n", win_size_mb);
if (win_size_mb < brcm_dram0_size_mb)
printk(KERN_WARNING
"WARNING: PCI2.3 window size is smaller than "
"system memory\n");
BDEV_WR(BCHP_PCI_CFG_MEMORY_BASE_W0, 0x00000000);
/* not used - move them out of the way */
BDEV_WR(BCHP_PCI_CFG_MEMORY_BASE_W1, 0x80000000);
BDEV_WR(BCHP_PCI_CFG_MEMORY_BASE_W2, 0x80000000);
BDEV_WR(BCHP_PCI_CFG_GISB_BASE_W, 0x80000000);
/* set endianness for W0 */
BDEV_UNSET(BCHP_PCI_CFG_PCI_SDRAM_ENDIAN_CTRL,
BCHP_PCI_CFG_PCI_SDRAM_ENDIAN_CTRL_ENDIAN_MODE_MWIN0_MASK);
BDEV_SET(BCHP_PCI_CFG_PCI_SDRAM_ENDIAN_CTRL, DATA_ENDIAN);
#endif
}
#if defined(CONFIG_BRCM_HAS_PCIE)
static struct wktmr_time pcie_reset_started;
#define PCIE_LINK_UP() \
(((BDEV_RD(BCHP_PCIE_MISC_PCIE_STATUS) & 0x30) == 0x30) ? 1 : 0)
#else
#define PCIE_LINK_UP() 0
#endif
void brcm_early_pcie_setup(void)
{
#if defined(CONFIG_BRCM_HAS_PCIE)
/*
* Called from bchip_early_setup() in order to start PCIe link
* negotiation immediately at kernel boot time. The RC is supposed
* to give the endpoint device 100ms to settle down before
* attempting configuration accesses. So we let the link negotiation
* happen in the background instead of busy-waiting.
*/
struct wktmr_time tmp;
/* reset the bridge and the endpoint device */
SET_BRIDGE_RESET(1);
SET_PERST(1);
/* delay 100us */
wktmr_read(&tmp);
while (wktmr_elapsed(&tmp) < (100 * WKTMR_1US))
;
/* take the bridge out of reset */
SET_BRIDGE_RESET(0);
/* enable SCB_MAX_BURST_SIZE | CSR_READ_UR_MODE | SCB_ACCESS_EN */
BDEV_WR(BCHP_PCIE_MISC_MISC_CTRL, 0x00103000);
/* set up MIPS->PCIE memory windows (4x 128MB) */
PCIE_OUTBOUND_WIN(0, PCIE_MEM_START + 0x00000000, 0x08000000);
PCIE_OUTBOUND_WIN(1, PCIE_MEM_START + 0x08000000, 0x08000000);
PCIE_OUTBOUND_WIN(2, PCIE_MEM_START + 0x10000000, 0x08000000);
PCIE_OUTBOUND_WIN(3, PCIE_MEM_START + 0x18000000, 0x08000000);
#if defined(CONFIG_BRCM_HAS_2GB_MEMC0)
/* set up 4GB PCIE->SCB memory window on BAR2 */
BDEV_WR(BCHP_PCIE_MISC_RC_BAR2_CONFIG_LO, 0x00000011);
BDEV_WR(BCHP_PCIE_MISC_RC_BAR2_CONFIG_HI, 0x00000000);
BDEV_WR_F(PCIE_MISC_MISC_CTRL, SCB0_SIZE, 0x10);
BDEV_WR_F(PCIE_MISC_MISC_CTRL, SCB1_SIZE, 0x0f);
#else
/* set up 1GB PCIE->SCB memory window on BAR2 */
BDEV_WR(BCHP_PCIE_MISC_RC_BAR2_CONFIG_LO, 0x0000000f);
BDEV_WR(BCHP_PCIE_MISC_RC_BAR2_CONFIG_HI, 0x00000000);
#endif
/* disable PCIE->GISB window */
BDEV_WR(BCHP_PCIE_MISC_RC_BAR1_CONFIG_LO, 0x00000000);
/* disable the other PCIE->SCB memory window */
BDEV_WR(BCHP_PCIE_MISC_RC_BAR3_CONFIG_LO, 0x00000000);
/* disable MSI (for now...) */
BDEV_WR(BCHP_PCIE_MISC_MSI_BAR_CONFIG_LO, 0);
/* set up L2 interrupt masks */
BDEV_WR_RB(BCHP_PCIE_INTR2_CPU_CLEAR, 0);
BDEV_WR_RB(BCHP_PCIE_INTR2_CPU_MASK_CLEAR, 0);
BDEV_WR_RB(BCHP_PCIE_INTR2_CPU_MASK_SET, 0xffffffff);
/* take the EP device out of reset */
SET_PERST(0);
/* record the current time */
wktmr_read(&pcie_reset_started);
#endif
}
void brcm_setup_pcie_bridge(void)
{
#if defined(CONFIG_BRCM_HAS_PCIE)
/* give the RC/EP time to wake up, before trying to configure RC */
while (wktmr_elapsed(&pcie_reset_started) < (100 * WKTMR_1MS))
;
if (!PCIE_LINK_UP()) {
struct clk *clk = clk_get(NULL, "pcie");
brcm_pcie_enabled = 0;
if (clk)
clk_disable(clk);
printk(KERN_INFO "PCI: PCIe link down\n");
return;
}
printk(KERN_INFO "PCI: PCIe link up\n");
/* enable MEM_SPACE and BUS_MASTER for RC */
BDEV_WR(BCHP_PCIE_RC_CFG_TYPE1_STATUS_COMMAND, 0x6);
/* set base/limit for outbound transactions */
#if defined(CONFIG_BRCM_HAS_2GB_MEMC0)
BDEV_WR(BCHP_PCIE_RC_CFG_TYPE1_RC_MEM_BASE_LIMIT, 0xeff0d000);
#else
BDEV_WR(BCHP_PCIE_RC_CFG_TYPE1_RC_MEM_BASE_LIMIT, 0xbff0a000);
#endif
/* disable the prefetch range */
BDEV_WR(BCHP_PCIE_RC_CFG_TYPE1_RC_PREF_BASE_LIMIT, 0x0000fff0);
/* set pri/sec bus numbers */
BDEV_WR(BCHP_PCIE_RC_CFG_TYPE1_PRI_SEC_BUS_NO, 0x00010100);
/* enable configuration request retry (see pci_scan_device()) */
BDEV_WR_F(PCIE_RC_CFG_PCIE_ROOT_CAP_CONTROL, RC_CRS_EN, 1);
/* PCIE->SCB endian mode for BAR2 */
BDEV_WR_F_RB(PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1, ENDIAN_MODE_BAR2,
DATA_ENDIAN);
#endif
}
/***********************************************************************
* PCI controller registration
***********************************************************************/
static int __init brcmstb_pci_init(void)
{
unsigned long __maybe_unused reg_base;
#if defined(CONFIG_BRCM_HAS_PCI23)
if (brcm_pci_enabled) {
request_mem_region(PCI_IO_START, PCI_IO_ACTUAL_SIZE,
"External PCI2.3 IO");
request_mem_region(PCI_IO_REG_START, PCI_IO_REG_SIZE,
"External PCI2.3 registers");
reg_base = (unsigned long)
ioremap(PCI_IO_REG_START, PCI_IO_REG_SIZE);
brcm_buses[BRCM_BUSNO_PCI23].idx_reg = reg_base + PCI_CFG_INDEX;
brcm_buses[BRCM_BUSNO_PCI23].data_reg = reg_base + PCI_CFG_DATA;
brcm_setup_pci_bridge();
brcmstb_pci_controller.io_map_base = (unsigned long)
ioremap(PCI_IO_START, PCI_IO_SIZE);
/*
* INVALID port ranges include:
* 0x0000 .. (IO_ADDR_PCI23 - 1)
* SATA/PCIe IO regions
* These are (intentionally) located in the ioremap() guard
* pages so that they cause an exception on access.
*/
set_io_port_base(brcmstb_pci_controller.io_map_base -
IO_ADDR_PCI23);
register_pci_controller(&brcmstb_pci_controller);
}
#endif
#if defined(CONFIG_BRCM_HAS_SATA2)
if (brcm_sata_enabled) {
brcm_setup_sata_bridge();
register_pci_controller(&brcmstb_sata_controller);
}
#endif
#ifdef CONFIG_BRCM_HAS_PCIE
if (brcm_pcie_enabled) {
brcm_setup_pcie_bridge();
request_mem_region(PCIE_IO_REG_START, PCIE_IO_REG_SIZE,
"External PCIe registers");
reg_base = (unsigned long)ioremap(PCIE_IO_REG_START,
PCIE_IO_REG_SIZE);
brcm_buses[BRCM_BUSNO_PCIE].idx_reg = reg_base + PCIE_CFG_INDEX;
brcm_buses[BRCM_BUSNO_PCIE].data_reg = reg_base + PCIE_CFG_DATA;
register_pci_controller(&brcmstb_pcie_controller);
}
#endif
return 0;
}
arch_initcall(brcmstb_pci_init);
/***********************************************************************
* Read/write PCI configuration registers
***********************************************************************/
#define CFG_INDEX(bus, devfn, reg) \
(((PCI_SLOT(devfn) & 0x1f) << (brcm_buses[bus->number].slot_shift)) | \
((PCI_FUNC(devfn) & 0x07) << (brcm_buses[bus->number].func_shift)) | \
(brcm_buses[bus->number].hw_busnum << \
brcm_buses[bus->number].busnum_shift) | \
(reg))
static int devfn_ok(struct pci_bus *bus, unsigned int devfn)
{
/* SATA is the only device on the bus, with devfn == 0 */
if (bus->number == BRCM_BUSNO_SATA && devfn != 0)
return 0;
/* PCIe: check for link down or invalid slot number */
if (bus->number == BRCM_BUSNO_PCIE &&
(!PCIE_LINK_UP() || PCI_SLOT(devfn) != 0))
return 0;
return 1; /* OK */
}
static int brcm_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 data)
{
u32 val = 0, mask, shift;
if (!devfn_ok(bus, devfn))
return PCIBIOS_FUNC_NOT_SUPPORTED;
BUG_ON(((where & 3) + size) > 4);
if (size < 4) {
/* partial word - read, modify, write */
DEV_WR_RB(brcm_buses[bus->number].idx_reg,
CFG_INDEX(bus, devfn, where & ~3));
val = DEV_RD(brcm_buses[bus->number].data_reg);
}
shift = (where & 3) << 3;
mask = (0xffffffff >> ((4 - size) << 3)) << shift;
DEV_WR_RB(brcm_buses[bus->number].idx_reg,
CFG_INDEX(bus, devfn, where & ~3));
val = (val & ~mask) | ((data << shift) & mask);
DEV_WR_RB(brcm_buses[bus->number].data_reg, val);
return PCIBIOS_SUCCESSFUL;
}
static int brcm_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *data)
{
u32 val, mask, shift;
if (!devfn_ok(bus, devfn))
return PCIBIOS_FUNC_NOT_SUPPORTED;
BUG_ON(((where & 3) + size) > 4);
DEV_WR_RB(brcm_buses[bus->number].idx_reg,
CFG_INDEX(bus, devfn, where & ~3));
val = DEV_RD(brcm_buses[bus->number].data_reg);
shift = (where & 3) << 3;
mask = (0xffffffff >> ((4 - size) << 3)) << shift;
*data = (val & mask) >> shift;
return PCIBIOS_SUCCESSFUL;
}
/*
* SATA2 has real PCI configuration registers, but they are set up
* once at boot time then subsequently emulated because they interfere with
* power management. i.e. SATA PCI registers are inaccessible when the
* SATA core is clock gated.
*
* BAR5 (MMIO registers) will be 4KB @ SATA_MEM_START. This is the
* only active BAR, on both AHCI and Serverworks "K2" cores.
*/
#if defined(CONFIG_BRCM_HAS_SATA2)
static int brcm_sata_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 data)
{
if (!devfn_ok(bus, devfn))
return PCIBIOS_FUNC_NOT_SUPPORTED;
if (where == PCI_BASE_ADDRESS_5)
sata_pci_reg[where] = data & ~0x0fff;
else if (where <= PCI_INTERRUPT_PIN)
sata_pci_reg[where] = data;
return PCIBIOS_SUCCESSFUL;
}
static int brcm_sata_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *data)
{
if (!devfn_ok(bus, devfn))
return PCIBIOS_FUNC_NOT_SUPPORTED;
if (where <= PCI_INTERRUPT_PIN)
*data = sata_pci_reg[where];
else
*data = 0;
return PCIBIOS_SUCCESSFUL;
}
#endif
/***********************************************************************
* PCI slot to IRQ mappings (aka "fixup")
***********************************************************************/
int __devinit pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
#if defined(CONFIG_BRCM_HAS_SATA2)
if (dev->bus->number == BRCM_BUSNO_SATA)
return BRCM_IRQ_SATA;
#endif
#if defined(CONFIG_BRCM_HAS_PCIE)
if (dev->bus->number == BRCM_BUSNO_PCIE) {
const int pcie_irq[] = {
BRCM_IRQ_PCIE_INTA, BRCM_IRQ_PCIE_INTB,
BRCM_IRQ_PCIE_INTC, BRCM_IRQ_PCIE_INTD,
};
if ((pin - 1) > 3)
return 0;
return pcie_irq[pin - 1];
}
#endif
#if defined(CONFIG_BRCM_HAS_PCI23)
if ((slot >= BRCM_PCI_SLOTS) || ((pin - 1) > 3))
return 0;
return brcm_docsis_platform ?
irq_tab_brcmstb_docsis[slot][pin - 1] :
irq_tab_brcmstb[slot][pin - 1];
#else
return 0;
#endif
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
/***********************************************************************
* Per-device initialization
***********************************************************************/
static void __devinit brcm_pcibios_fixup(struct pci_dev *dev)
{
int slot = PCI_SLOT(dev->devfn);
printk(KERN_INFO
"PCI: found device %04x:%04x on %s bus, slot %d (irq %d)\n",
dev->vendor, dev->device, brcm_buses[dev->bus->number].name,
slot, pcibios_map_irq(dev, slot, 1));
/* zero out the BARs and let Linux assign an address */
pci_write_config_dword(dev, PCI_COMMAND, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_4, 0);
pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, 0);
pci_write_config_dword(dev, PCI_INTERRUPT_LINE, 0);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, brcm_pcibios_fixup);
/***********************************************************************
* DMA address remapping
***********************************************************************/
/*
* PCI2.3 and PCIe inbound BARs collapse the "holes" in the chip's SCB
* address space. SATA PCI/PCI-X does not. Therefore the PCI addresses
* need to be adjusted as they will not match the SCB addresses (MIPS
* physical addresses).
*
* The address maps can be found in <asm/mach-brcmstb/spaces.h> .
*/
static int dev_collapses_memory_hole(struct device *dev)
{
#if defined(CONFIG_BRCM_UPPER_MEMORY) || defined(CONFIG_HIGHMEM)
struct pci_dev *pdev;
if (unlikely(dev == NULL) ||
likely(dev->bus != &pci_bus_type))
return 0;
pdev = to_pci_dev(dev);
if (unlikely(pdev == NULL) ||
brcm_buses[pdev->bus->number].memory_hole)
return 0;
return 1;
#else
return 0;
#endif
}
static dma_addr_t brcm_phys_to_pci(struct device *dev, unsigned long phys)
{
if (!dev_collapses_memory_hole(dev))
return phys;
if (phys >= MEMC1_START)
return phys - MEMC1_PCI_OFFSET;
if (phys >= (BRCM_PCI_HOLE_START + BRCM_PCI_HOLE_SIZE))
return phys - BRCM_PCI_HOLE_SIZE;
return phys;
}
dma_addr_t plat_map_dma_mem(struct device *dev, void *addr, size_t size)
{
return brcm_phys_to_pci(dev, virt_to_phys(addr));
}
dma_addr_t plat_map_dma_mem_page(struct device *dev, struct page *page)
{
return brcm_phys_to_pci(dev, page_to_phys(page));
}
unsigned long plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr)
{
if (!dev_collapses_memory_hole(dev))
return dma_addr;
if (dma_addr >= (MEMC1_START - MEMC1_PCI_OFFSET))
return dma_addr + MEMC1_PCI_OFFSET;
if (dma_addr >= BRCM_PCI_HOLE_START)
return dma_addr + BRCM_PCI_HOLE_SIZE;
return dma_addr;
}