/* * Marvell hostbridge routines * * Author: Mark A. Greer * * 2004, 2005, 2007 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */ #include #include #include "types.h" #include "elf.h" #include "page.h" #include "string.h" #include "stdio.h" #include "io.h" #include "ops.h" #include "mv64x60.h" #define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define MV64x60_CPU2MEM_WINDOWS 4 #define MV64x60_CPU2MEM_0_BASE 0x0008 #define MV64x60_CPU2MEM_0_SIZE 0x0010 #define MV64x60_CPU2MEM_1_BASE 0x0208 #define MV64x60_CPU2MEM_1_SIZE 0x0210 #define MV64x60_CPU2MEM_2_BASE 0x0018 #define MV64x60_CPU2MEM_2_SIZE 0x0020 #define MV64x60_CPU2MEM_3_BASE 0x0218 #define MV64x60_CPU2MEM_3_SIZE 0x0220 #define MV64x60_ENET2MEM_BAR_ENABLE 0x2290 #define MV64x60_ENET2MEM_0_BASE 0x2200 #define MV64x60_ENET2MEM_0_SIZE 0x2204 #define MV64x60_ENET2MEM_1_BASE 0x2208 #define MV64x60_ENET2MEM_1_SIZE 0x220c #define MV64x60_ENET2MEM_2_BASE 0x2210 #define MV64x60_ENET2MEM_2_SIZE 0x2214 #define MV64x60_ENET2MEM_3_BASE 0x2218 #define MV64x60_ENET2MEM_3_SIZE 0x221c #define MV64x60_ENET2MEM_4_BASE 0x2220 #define MV64x60_ENET2MEM_4_SIZE 0x2224 #define MV64x60_ENET2MEM_5_BASE 0x2228 #define MV64x60_ENET2MEM_5_SIZE 0x222c #define MV64x60_ENET2MEM_ACC_PROT_0 0x2294 #define MV64x60_ENET2MEM_ACC_PROT_1 0x2298 #define MV64x60_ENET2MEM_ACC_PROT_2 0x229c #define MV64x60_MPSC2MEM_BAR_ENABLE 0xf250 #define MV64x60_MPSC2MEM_0_BASE 0xf200 #define MV64x60_MPSC2MEM_0_SIZE 0xf204 #define MV64x60_MPSC2MEM_1_BASE 0xf208 #define MV64x60_MPSC2MEM_1_SIZE 0xf20c #define MV64x60_MPSC2MEM_2_BASE 0xf210 #define MV64x60_MPSC2MEM_2_SIZE 0xf214 #define MV64x60_MPSC2MEM_3_BASE 0xf218 #define MV64x60_MPSC2MEM_3_SIZE 0xf21c #define MV64x60_MPSC_0_REMAP 0xf240 #define MV64x60_MPSC_1_REMAP 0xf244 #define MV64x60_MPSC2MEM_ACC_PROT_0 0xf254 #define MV64x60_MPSC2MEM_ACC_PROT_1 0xf258 #define MV64x60_MPSC2REGS_BASE 0xf25c #define MV64x60_IDMA2MEM_BAR_ENABLE 0x0a80 #define MV64x60_IDMA2MEM_0_BASE 0x0a00 #define MV64x60_IDMA2MEM_0_SIZE 0x0a04 #define MV64x60_IDMA2MEM_1_BASE 0x0a08 #define MV64x60_IDMA2MEM_1_SIZE 0x0a0c #define MV64x60_IDMA2MEM_2_BASE 0x0a10 #define MV64x60_IDMA2MEM_2_SIZE 0x0a14 #define MV64x60_IDMA2MEM_3_BASE 0x0a18 #define MV64x60_IDMA2MEM_3_SIZE 0x0a1c #define MV64x60_IDMA2MEM_4_BASE 0x0a20 #define MV64x60_IDMA2MEM_4_SIZE 0x0a24 #define MV64x60_IDMA2MEM_5_BASE 0x0a28 #define MV64x60_IDMA2MEM_5_SIZE 0x0a2c #define MV64x60_IDMA2MEM_6_BASE 0x0a30 #define MV64x60_IDMA2MEM_6_SIZE 0x0a34 #define MV64x60_IDMA2MEM_7_BASE 0x0a38 #define MV64x60_IDMA2MEM_7_SIZE 0x0a3c #define MV64x60_IDMA2MEM_ACC_PROT_0 0x0a70 #define MV64x60_IDMA2MEM_ACC_PROT_1 0x0a74 #define MV64x60_IDMA2MEM_ACC_PROT_2 0x0a78 #define MV64x60_IDMA2MEM_ACC_PROT_3 0x0a7c #define MV64x60_PCI_ACC_CNTL_WINDOWS 6 #define MV64x60_PCI0_PCI_DECODE_CNTL 0x0d3c #define MV64x60_PCI1_PCI_DECODE_CNTL 0x0dbc #define MV64x60_PCI0_BAR_ENABLE 0x0c3c #define MV64x60_PCI02MEM_0_SIZE 0x0c08 #define MV64x60_PCI0_ACC_CNTL_0_BASE_LO 0x1e00 #define MV64x60_PCI0_ACC_CNTL_0_BASE_HI 0x1e04 #define MV64x60_PCI0_ACC_CNTL_0_SIZE 0x1e08 #define MV64x60_PCI0_ACC_CNTL_1_BASE_LO 0x1e10 #define MV64x60_PCI0_ACC_CNTL_1_BASE_HI 0x1e14 #define MV64x60_PCI0_ACC_CNTL_1_SIZE 0x1e18 #define MV64x60_PCI0_ACC_CNTL_2_BASE_LO 0x1e20 #define MV64x60_PCI0_ACC_CNTL_2_BASE_HI 0x1e24 #define MV64x60_PCI0_ACC_CNTL_2_SIZE 0x1e28 #define MV64x60_PCI0_ACC_CNTL_3_BASE_LO 0x1e30 #define MV64x60_PCI0_ACC_CNTL_3_BASE_HI 0x1e34 #define MV64x60_PCI0_ACC_CNTL_3_SIZE 0x1e38 #define MV64x60_PCI0_ACC_CNTL_4_BASE_LO 0x1e40 #define MV64x60_PCI0_ACC_CNTL_4_BASE_HI 0x1e44 #define MV64x60_PCI0_ACC_CNTL_4_SIZE 0x1e48 #define MV64x60_PCI0_ACC_CNTL_5_BASE_LO 0x1e50 #define MV64x60_PCI0_ACC_CNTL_5_BASE_HI 0x1e54 #define MV64x60_PCI0_ACC_CNTL_5_SIZE 0x1e58 #define MV64x60_PCI1_BAR_ENABLE 0x0cbc #define MV64x60_PCI12MEM_0_SIZE 0x0c88 #define MV64x60_PCI1_ACC_CNTL_0_BASE_LO 0x1e80 #define MV64x60_PCI1_ACC_CNTL_0_BASE_HI 0x1e84 #define MV64x60_PCI1_ACC_CNTL_0_SIZE 0x1e88 #define MV64x60_PCI1_ACC_CNTL_1_BASE_LO 0x1e90 #define MV64x60_PCI1_ACC_CNTL_1_BASE_HI 0x1e94 #define MV64x60_PCI1_ACC_CNTL_1_SIZE 0x1e98 #define MV64x60_PCI1_ACC_CNTL_2_BASE_LO 0x1ea0 #define MV64x60_PCI1_ACC_CNTL_2_BASE_HI 0x1ea4 #define MV64x60_PCI1_ACC_CNTL_2_SIZE 0x1ea8 #define MV64x60_PCI1_ACC_CNTL_3_BASE_LO 0x1eb0 #define MV64x60_PCI1_ACC_CNTL_3_BASE_HI 0x1eb4 #define MV64x60_PCI1_ACC_CNTL_3_SIZE 0x1eb8 #define MV64x60_PCI1_ACC_CNTL_4_BASE_LO 0x1ec0 #define MV64x60_PCI1_ACC_CNTL_4_BASE_HI 0x1ec4 #define MV64x60_PCI1_ACC_CNTL_4_SIZE 0x1ec8 #define MV64x60_PCI1_ACC_CNTL_5_BASE_LO 0x1ed0 #define MV64x60_PCI1_ACC_CNTL_5_BASE_HI 0x1ed4 #define MV64x60_PCI1_ACC_CNTL_5_SIZE 0x1ed8 #define MV64x60_CPU2PCI_SWAP_NONE 0x01000000 #define MV64x60_CPU2PCI0_IO_BASE 0x0048 #define MV64x60_CPU2PCI0_IO_SIZE 0x0050 #define MV64x60_CPU2PCI0_IO_REMAP 0x00f0 #define MV64x60_CPU2PCI0_MEM_0_BASE 0x0058 #define MV64x60_CPU2PCI0_MEM_0_SIZE 0x0060 #define MV64x60_CPU2PCI0_MEM_0_REMAP_LO 0x00f8 #define MV64x60_CPU2PCI0_MEM_0_REMAP_HI 0x0320 #define MV64x60_CPU2PCI1_IO_BASE 0x0090 #define MV64x60_CPU2PCI1_IO_SIZE 0x0098 #define MV64x60_CPU2PCI1_IO_REMAP 0x0108 #define MV64x60_CPU2PCI1_MEM_0_BASE 0x00a0 #define MV64x60_CPU2PCI1_MEM_0_SIZE 0x00a8 #define MV64x60_CPU2PCI1_MEM_0_REMAP_LO 0x0110 #define MV64x60_CPU2PCI1_MEM_0_REMAP_HI 0x0340 struct mv64x60_mem_win { u32 hi; u32 lo; u32 size; }; struct mv64x60_pci_win { u32 fcn; u32 hi; u32 lo; u32 size; }; /* PCI config access routines */ struct { u32 addr; u32 data; } static mv64x60_pci_cfgio[2] = { { /* hose 0 */ .addr = 0xcf8, .data = 0xcfc, }, { /* hose 1 */ .addr = 0xc78, .data = 0xc7c, } }; u32 mv64x60_cfg_read(u8 *bridge_base, u8 hose, u8 bus, u8 devfn, u8 offset) { out_le32((u32 *)(bridge_base + mv64x60_pci_cfgio[hose].addr), (1 << 31) | (bus << 16) | (devfn << 8) | offset); return in_le32((u32 *)(bridge_base + mv64x60_pci_cfgio[hose].data)); } void mv64x60_cfg_write(u8 *bridge_base, u8 hose, u8 bus, u8 devfn, u8 offset, u32 val) { out_le32((u32 *)(bridge_base + mv64x60_pci_cfgio[hose].addr), (1 << 31) | (bus << 16) | (devfn << 8) | offset); out_le32((u32 *)(bridge_base + mv64x60_pci_cfgio[hose].data), val); } /* I/O ctlr -> system memory setup */ static struct mv64x60_mem_win mv64x60_cpu2mem[MV64x60_CPU2MEM_WINDOWS] = { { .lo = MV64x60_CPU2MEM_0_BASE, .size = MV64x60_CPU2MEM_0_SIZE, }, { .lo = MV64x60_CPU2MEM_1_BASE, .size = MV64x60_CPU2MEM_1_SIZE, }, { .lo = MV64x60_CPU2MEM_2_BASE, .size = MV64x60_CPU2MEM_2_SIZE, }, { .lo = MV64x60_CPU2MEM_3_BASE, .size = MV64x60_CPU2MEM_3_SIZE, }, }; static struct mv64x60_mem_win mv64x60_enet2mem[MV64x60_CPU2MEM_WINDOWS] = { { .lo = MV64x60_ENET2MEM_0_BASE, .size = MV64x60_ENET2MEM_0_SIZE, }, { .lo = MV64x60_ENET2MEM_1_BASE, .size = MV64x60_ENET2MEM_1_SIZE, }, { .lo = MV64x60_ENET2MEM_2_BASE, .size = MV64x60_ENET2MEM_2_SIZE, }, { .lo = MV64x60_ENET2MEM_3_BASE, .size = MV64x60_ENET2MEM_3_SIZE, }, }; static struct mv64x60_mem_win mv64x60_mpsc2mem[MV64x60_CPU2MEM_WINDOWS] = { { .lo = MV64x60_MPSC2MEM_0_BASE, .size = MV64x60_MPSC2MEM_0_SIZE, }, { .lo = MV64x60_MPSC2MEM_1_BASE, .size = MV64x60_MPSC2MEM_1_SIZE, }, { .lo = MV64x60_MPSC2MEM_2_BASE, .size = MV64x60_MPSC2MEM_2_SIZE, }, { .lo = MV64x60_MPSC2MEM_3_BASE, .size = MV64x60_MPSC2MEM_3_SIZE, }, }; static struct mv64x60_mem_win mv64x60_idma2mem[MV64x60_CPU2MEM_WINDOWS] = { { .lo = MV64x60_IDMA2MEM_0_BASE, .size = MV64x60_IDMA2MEM_0_SIZE, }, { .lo = MV64x60_IDMA2MEM_1_BASE, .size = MV64x60_IDMA2MEM_1_SIZE, }, { .lo = MV64x60_IDMA2MEM_2_BASE, .size = MV64x60_IDMA2MEM_2_SIZE, }, { .lo = MV64x60_IDMA2MEM_3_BASE, .size = MV64x60_IDMA2MEM_3_SIZE, }, }; static u32 mv64x60_dram_selects[MV64x60_CPU2MEM_WINDOWS] = {0xe,0xd,0xb,0x7}; /* * ENET, MPSC, and IDMA ctlrs on the MV64x60 have separate windows that * must be set up so that the respective ctlr can access system memory. * Configure them to be same as cpu->memory windows. */ void mv64x60_config_ctlr_windows(u8 *bridge_base, u8 *bridge_pbase, u8 is_coherent) { u32 i, base, size, enables, prot = 0, snoop_bits = 0; /* Disable ctlr->mem windows */ out_le32((u32 *)(bridge_base + MV64x60_ENET2MEM_BAR_ENABLE), 0x3f); out_le32((u32 *)(bridge_base + MV64x60_MPSC2MEM_BAR_ENABLE), 0xf); out_le32((u32 *)(bridge_base + MV64x60_ENET2MEM_BAR_ENABLE), 0xff); if (is_coherent) snoop_bits = 0x2 << 12; /* Writeback */ enables = in_le32((u32 *)(bridge_base + MV64x60_CPU_BAR_ENABLE)) & 0xf; for (i=0; ibridge's reg window to the bridge's internal registers. */ out_le32((u32 *)(bridge_base + MV64x60_MPSC2REGS_BASE), (u32)bridge_pbase); out_le32((u32 *)(bridge_base + MV64x60_ENET2MEM_BAR_ENABLE), enables); out_le32((u32 *)(bridge_base + MV64x60_MPSC2MEM_BAR_ENABLE), enables); out_le32((u32 *)(bridge_base + MV64x60_IDMA2MEM_BAR_ENABLE), enables); } /* PCI MEM -> system memory, et. al. setup */ static struct mv64x60_pci_win mv64x60_pci2mem[2] = { { /* hose 0 */ .fcn = 0, .hi = 0x14, .lo = 0x10, .size = MV64x60_PCI02MEM_0_SIZE, }, { /* hose 1 */ .fcn = 0, .hi = 0x94, .lo = 0x90, .size = MV64x60_PCI12MEM_0_SIZE, }, }; static struct mv64x60_mem_win mv64x60_pci_acc[2][MV64x60_PCI_ACC_CNTL_WINDOWS] = { { /* hose 0 */ { .hi = MV64x60_PCI0_ACC_CNTL_0_BASE_HI, .lo = MV64x60_PCI0_ACC_CNTL_0_BASE_LO, .size = MV64x60_PCI0_ACC_CNTL_0_SIZE, }, { .hi = MV64x60_PCI0_ACC_CNTL_1_BASE_HI, .lo = MV64x60_PCI0_ACC_CNTL_1_BASE_LO, .size = MV64x60_PCI0_ACC_CNTL_1_SIZE, }, { .hi = MV64x60_PCI0_ACC_CNTL_2_BASE_HI, .lo = MV64x60_PCI0_ACC_CNTL_2_BASE_LO, .size = MV64x60_PCI0_ACC_CNTL_2_SIZE, }, { .hi = MV64x60_PCI0_ACC_CNTL_3_BASE_HI, .lo = MV64x60_PCI0_ACC_CNTL_3_BASE_LO, .size = MV64x60_PCI0_ACC_CNTL_3_SIZE, }, }, { /* hose 1 */ { .hi = MV64x60_PCI1_ACC_CNTL_0_BASE_HI, .lo = MV64x60_PCI1_ACC_CNTL_0_BASE_LO, .size = MV64x60_PCI1_ACC_CNTL_0_SIZE, }, { .hi = MV64x60_PCI1_ACC_CNTL_1_BASE_HI, .lo = MV64x60_PCI1_ACC_CNTL_1_BASE_LO, .size = MV64x60_PCI1_ACC_CNTL_1_SIZE, }, { .hi = MV64x60_PCI1_ACC_CNTL_2_BASE_HI, .lo = MV64x60_PCI1_ACC_CNTL_2_BASE_LO, .size = MV64x60_PCI1_ACC_CNTL_2_SIZE, }, { .hi = MV64x60_PCI1_ACC_CNTL_3_BASE_HI, .lo = MV64x60_PCI1_ACC_CNTL_3_BASE_LO, .size = MV64x60_PCI1_ACC_CNTL_3_SIZE, }, }, }; static struct mv64x60_mem_win mv64x60_pci2reg[2] = { { .hi = 0x24, .lo = 0x20, .size = 0, }, { .hi = 0xa4, .lo = 0xa0, .size = 0, }, }; /* Only need to use 1 window (per hose) to get access to all of system memory */ void mv64x60_config_pci_windows(u8 *bridge_base, u8 *bridge_pbase, u8 hose, u8 bus, u32 mem_size, u32 acc_bits) { u32 i, offset, bar_enable, enables; /* Disable all windows but PCI MEM -> Bridge's regs window */ enables = ~(1 << 9); bar_enable = hose ? MV64x60_PCI1_BAR_ENABLE : MV64x60_PCI0_BAR_ENABLE; out_le32((u32 *)(bridge_base + bar_enable), enables); for (i=0; i System Mem addr 0 */ mv64x60_cfg_write(bridge_base, hose, bus, PCI_DEVFN(0, mv64x60_pci2mem[hose].fcn), mv64x60_pci2mem[hose].hi, 0); mv64x60_cfg_write(bridge_base, hose, bus, PCI_DEVFN(0, mv64x60_pci2mem[hose].fcn), mv64x60_pci2mem[hose].lo, 0); out_le32((u32 *)(bridge_base + mv64x60_pci2mem[hose].size),mem_size); acc_bits |= MV64x60_PCI_ACC_CNTL_ENABLE; out_le32((u32 *)(bridge_base + mv64x60_pci_acc[hose][0].hi), 0); out_le32((u32 *)(bridge_base + mv64x60_pci_acc[hose][0].lo), acc_bits); out_le32((u32 *)(bridge_base + mv64x60_pci_acc[hose][0].size),mem_size); /* Set PCI MEM->bridge's reg window to where they are in CPU mem map */ i = (u32)bridge_base; i &= 0xffff0000; i |= (0x2 << 1); mv64x60_cfg_write(bridge_base, hose, bus, PCI_DEVFN(0,0), mv64x60_pci2reg[hose].hi, 0); mv64x60_cfg_write(bridge_base, hose, bus, PCI_DEVFN(0,0), mv64x60_pci2reg[hose].lo, i); enables &= ~0x1; /* Enable PCI MEM -> System Mem window 0 */ out_le32((u32 *)(bridge_base + bar_enable), enables); } /* CPU -> PCI I/O & MEM setup */ struct mv64x60_cpu2pci_win mv64x60_cpu2pci_io[2] = { { /* hose 0 */ .lo = MV64x60_CPU2PCI0_IO_BASE, .size = MV64x60_CPU2PCI0_IO_SIZE, .remap_hi = 0, .remap_lo = MV64x60_CPU2PCI0_IO_REMAP, }, { /* hose 1 */ .lo = MV64x60_CPU2PCI1_IO_BASE, .size = MV64x60_CPU2PCI1_IO_SIZE, .remap_hi = 0, .remap_lo = MV64x60_CPU2PCI1_IO_REMAP, }, }; struct mv64x60_cpu2pci_win mv64x60_cpu2pci_mem[2] = { { /* hose 0 */ .lo = MV64x60_CPU2PCI0_MEM_0_BASE, .size = MV64x60_CPU2PCI0_MEM_0_SIZE, .remap_hi = MV64x60_CPU2PCI0_MEM_0_REMAP_HI, .remap_lo = MV64x60_CPU2PCI0_MEM_0_REMAP_LO, }, { /* hose 1 */ .lo = MV64x60_CPU2PCI1_MEM_0_BASE, .size = MV64x60_CPU2PCI1_MEM_0_SIZE, .remap_hi = MV64x60_CPU2PCI1_MEM_0_REMAP_HI, .remap_lo = MV64x60_CPU2PCI1_MEM_0_REMAP_LO, }, }; /* Only need to set up 1 window to pci mem space */ void mv64x60_config_cpu2pci_window(u8 *bridge_base, u8 hose, u32 pci_base_hi, u32 pci_base_lo, u32 cpu_base, u32 size, struct mv64x60_cpu2pci_win *offset_tbl) { cpu_base >>= 16; cpu_base |= MV64x60_CPU2PCI_SWAP_NONE; out_le32((u32 *)(bridge_base + offset_tbl[hose].lo), cpu_base); if (offset_tbl[hose].remap_hi != 0) out_le32((u32 *)(bridge_base + offset_tbl[hose].remap_hi), pci_base_hi); out_le32((u32 *)(bridge_base + offset_tbl[hose].remap_lo), pci_base_lo >> 16); size = (size - 1) >> 16; out_le32((u32 *)(bridge_base + offset_tbl[hose].size), size); } /* Read mem ctlr to get the amount of mem in system */ u32 mv64x60_get_mem_size(u8 *bridge_base) { u32 enables, i, v; u32 mem = 0; enables = in_le32((u32 *)(bridge_base + MV64x60_CPU_BAR_ENABLE)) & 0xf; for (i=0; i