/*
- * Copyright 2004 Freescale Semiconductor.
- * Copyright (C) 2003 Motorola Inc.
- * Xianghua Xiao (x.xiao@motorola.com)
+ * Copyright (C) Freescale Semiconductor, Inc. 2007
+ *
+ * Author: Scott Wood <scottwood@freescale.com>,
+ * with some bits from older board-specific PCI initialization.
*
* See file CREDITS for list of people who contributed to this
* project.
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
- *
- * Change log:
- *
- * 20050101: Eran Liberty (liberty@freescale.com)
- * Initial file creating (porting from 85XX & 8260)
*/
-/*
- * PCI Configuration space access support for MPC85xx PCI Bridge
- */
-#include <asm/mmu.h>
-#include <asm/io.h>
#include <common.h>
#include <pci.h>
-#ifdef CONFIG_MPC8349ADS
-#include <asm/i2c.h>
+#if defined(CONFIG_OF_LIBFDT)
+#include <libfdt.h>
+#include <fdt_support.h>
#endif
-#if defined(CONFIG_PCI)
+#include <asm/mpc8349_pci.h>
+
+#define MAX_BUSES 2
+
+DECLARE_GLOBAL_DATA_PTR;
-void
-pci_mpc83xx_init(volatile struct pci_controller *hose)
+static struct pci_controller pci_hose[MAX_BUSES];
+static int pci_num_buses;
+
+static void pci_init_bus(int bus, struct pci_region *reg)
{
- volatile immap_t * immr;
- volatile clk8349_t * clk;
- volatile law8349_t * pci_law;
- volatile pot8349_t * pci_pot;
- volatile pcictrl8349_t * pci_ctrl;
- volatile pciconf8349_t * pci_conf;
-
- u8 val8,tmp8,ret;
- u16 reg16,tmp16;
- u32 val32,tmp32;
-
- immr = (immap_t *)CFG_IMMRBAR;
- clk = (clk8349_t *)&immr->clk;
- pci_law = immr->sysconf.pcilaw;
- pci_pot = immr->ios.pot;
- pci_ctrl = immr->pci_ctrl;
- pci_conf = immr->pci_conf;
+ volatile immap_t *immr = (volatile immap_t *)CFG_IMMR;
+ volatile pot83xx_t *pot = immr->ios.pot;
+ volatile pcictrl83xx_t *pci_ctrl = &immr->pci_ctrl[bus];
+ struct pci_controller *hose = &pci_hose[bus];
+ u32 dev;
+ u16 reg16;
+ int i;
+
+ if (bus == 1)
+ pot += 3;
+
+ /* Setup outbound translation windows */
+ for (i = 0; i < 3; i++, reg++, pot++) {
+ if (reg->size == 0)
+ break;
+
+ hose->regions[i] = *reg;
+ hose->region_count++;
+
+ pot->potar = reg->bus_start >> 12;
+ pot->pobar = reg->phys_start >> 12;
+ pot->pocmr = ~(reg->size - 1) >> 12;
+
+ if (reg->flags & PCI_REGION_IO)
+ pot->pocmr |= POCMR_IO;
+#ifdef CONFIG_83XX_PCI_STREAMING
+ else if (reg->flags & PCI_REGION_PREFETCH)
+ pot->pocmr |= POCMR_SE;
+#endif
- /*
- * Configure PCI controller and PCI_CLK_OUTPUT both in 66M mode
- */
- val32 = clk->occr;
- udelay(2000);
- clk->occr = 0xff000000;
- udelay(2000);
+ if (bus == 1)
+ pot->pocmr |= POCMR_DST;
- /*
- * Configure PCI Local Access Windows
- */
- pci_law[0].bar = CFG_PCI1_MEM_PHYS & LAWBAR_BAR;
- pci_law[0].ar = LAWAR_EN | LAWAR_SIZE_1G;
- pci_law[1].bar = CFG_PCI1_IO_PHYS & LAWBAR_BAR;
- pci_law[1].ar = LAWAR_EN | LAWAR_SIZE_32M;
+ pot->pocmr |= POCMR_EN;
+ }
- /*
- * Configure PCI Outbound Translation Windows
- */
- pci_pot[0].potar = (CFG_PCI1_MEM_BASE >> 12) & POTAR_TA_MASK;
- pci_pot[0].pobar = (CFG_PCI1_MEM_PHYS >> 12) & POBAR_BA_MASK;
- pci_pot[0].pocmr = POCMR_EN | (POCMR_CM_512M & POCMR_CM_MASK);
+ /* Point inbound translation at RAM */
+ pci_ctrl->pitar1 = 0;
+ pci_ctrl->pibar1 = 0;
+ pci_ctrl->piebar1 = 0;
+ pci_ctrl->piwar1 = PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP |
+ PIWAR_WTT_SNOOP | (__ilog2(gd->ram_size) - 1);
- /* mapped to PCI1 IO space 0x0 to local 0xe2000000 */
- pci_pot[1].potar = (CFG_PCI1_IO_BASE >> 12) & POTAR_TA_MASK;
- pci_pot[1].pobar = (CFG_PCI1_IO_PHYS >> 12) & POBAR_BA_MASK;
- pci_pot[1].pocmr = POCMR_EN | POCMR_IO | (POCMR_CM_16M & POCMR_CM_MASK);
+ i = hose->region_count++;
+ hose->regions[i].bus_start = 0;
+ hose->regions[i].phys_start = 0;
+ hose->regions[i].size = gd->ram_size;
+ hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_MEMORY;
- pci_pot[3].potar = (CFG_PCI2_MEM_BASE >> 12) & POTAR_TA_MASK;
- pci_pot[3].pobar = (CFG_PCI2_MEM_PHYS >> 12) & POBAR_BA_MASK;
- pci_pot[3].pocmr = POCMR_EN | POCMR_DST | (POCMR_CM_512M & POCMR_CM_MASK);
+ hose->first_busno = 0;
+ hose->last_busno = 0xff;
- /* mapped to PCI2 IO space 0x0 to local 0xe3000000 */
- pci_pot[4].potar = (CFG_PCI2_IO_BASE >> 12) & POTAR_TA_MASK;
- pci_pot[4].pobar = (CFG_PCI2_IO_PHYS >> 12) & POBAR_BA_MASK;
- pci_pot[4].pocmr = POCMR_EN | POCMR_DST | POCMR_IO | (POCMR_CM_16M & POCMR_CM_MASK);
+ pci_setup_indirect(hose, CFG_IMMR + 0x8300 + bus * 0x80,
+ CFG_IMMR + 0x8304 + bus * 0x80);
+
+ pci_register_hose(hose);
/*
- * Configure PCI Inbound Translation Windows
+ * Write to Command register
*/
- pci_ctrl[0].pitar1 = 0x0;
- pci_ctrl[0].pibar1 = 0x0;
- pci_ctrl[0].piebar1 = 0x0;
- pci_ctrl[0].piwar1 = PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP | PIWAR_WTT_SNOOP | PIWAR_IWS_2G;
-
- pci_ctrl[1].pitar1 = 0x0;
- pci_ctrl[1].pibar1 = 0x0;
- pci_ctrl[1].piebar1 = 0x0;
- pci_ctrl[1].piwar1 = PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP | PIWAR_WTT_SNOOP | PIWAR_IWS_2G;
+ reg16 = 0xff;
+ dev = PCI_BDF(hose->first_busno, 0, 0);
+ pci_hose_read_config_word(hose, dev, PCI_COMMAND, ®16);
+ reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16);
+
/*
- * Assign PIB PMC slot to desired PCI bus
+ * Clear non-reserved bits in status register.
*/
-#ifdef CONFIG_MPC8349ADS
- mpc8349_i2c = (i2c_t*)(CFG_IMMRBAR + CFG_I2C2_OFFSET);
- i2c_init(CFG_I2C_SPEED,CFG_I2C_SLAVE);
-#endif
- val8 = 0;
- ret = i2c_write(0x23,0x6,1,&val8,1);
- ret = i2c_write(0x23,0x7,1,&val8,1);
- val8 = 0xff;
- ret = i2c_write(0x23,0x2,1,&val8,1);
- ret = i2c_write(0x23,0x3,1,&val8,1);
-
- val8 = 0;
- ret = i2c_write(0x26,0x6,1,&val8,1);
- val8 = 0x34;
- ret = i2c_write(0x26,0x7,1,&val8,1);
-#if defined(PCI_64BIT)
- val8 = 0xf4; /* PMC2<->PCI1 64bit */
-#elif defined(PCI_ALL_PCI1)
- val8 = 0xf3; /* PMC1<->PCI1,PMC2<->PCI1,PMC3<->PCI1 32bit */
-#elif defined(PCI_ONE_PCI1)
- val8 = 0xf9; /* PMC1<->PCI1,PMC2<->PCI2,PMC3<->PCI2 32bit */
-#elif defined(PCI_TWO_PCI1)
- val8 = 0xf5; /* PMC1<->PCI1,PMC2<->PCI1,PMC3<->PCI2 32bit */
-#else
- val8 = 0xf5;
-#endif
- ret = i2c_write(0x26,0x2,1,&val8,1);
- val8 = 0xff;
- ret = i2c_write(0x26,0x3,1,&val8,1);
- val8 = 0;
- ret = i2c_write(0x27,0x6,1,&val8,1);
- ret = i2c_write(0x27,0x7,1,&val8,1);
- val8 = 0xff;
- ret = i2c_write(0x27,0x2,1,&val8,1);
- val8 = 0xef;
- ret = i2c_write(0x27,0x3,1,&val8,1);
- asm("eieio");
+ pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff);
+ pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
+ pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
+#ifdef CONFIG_PCI_SCAN_SHOW
+ printf("PCI: Bus Dev VenId DevId Class Int\n");
+#endif
/*
- * Release PCI RST Output signal
+ * Hose scan.
*/
- udelay(2000);
- pci_ctrl[0].gcr = 1;
-#ifndef PCI_64BIT
- pci_ctrl[1].gcr = 1;
-#endif
- udelay(2000);
-
- hose[0].first_busno = 0;
- hose[0].last_busno = 0xff;
-
- pci_set_region(hose[0].regions + 0,
- CFG_PCI1_MEM_BASE,
- CFG_PCI1_MEM_PHYS,
- CFG_PCI1_MEM_SIZE,
- PCI_REGION_MEM);
+ hose->last_busno = pci_hose_scan(hose);
+}
- pci_set_region(hose[0].regions + 1,
- CFG_PCI1_IO_BASE,
- CFG_PCI1_IO_PHYS,
- CFG_PCI1_IO_SIZE,
- PCI_REGION_IO);
+/*
+ * The caller must have already set OCCR, and the PCI_LAW BARs
+ * must have been set to cover all of the requested regions.
+ *
+ * If fewer than three regions are requested, then the region
+ * list is terminated with a region of size 0.
+ */
+void mpc83xx_pci_init(int num_buses, struct pci_region **reg, int warmboot)
+{
+ volatile immap_t *immr = (volatile immap_t *)CFG_IMMR;
+ int i;
- hose[0].region_count = 2;
+ if (num_buses > MAX_BUSES) {
+ printf("%d PCI buses requsted, %d supported\n",
+ num_buses, MAX_BUSES);
- pci_setup_indirect(&hose[0],
- (CFG_IMMRBAR+0x8300),
- (CFG_IMMRBAR+0x8304));
-#define PCI_CLASS_BRIDGE 0x06
- reg16 = 0xff;
- tmp32 = 0xffff;
- pci_hose_write_config_byte(&hose[0],PCI_BDF(0,0,0),PCI_CLASS_CODE,PCI_CLASS_BRIDGE);
+ num_buses = MAX_BUSES;
+ }
- pci_hose_read_config_word (&hose[0],PCI_BDF(0,0,0),PCI_COMMAND, ®16);
- reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
- pci_hose_write_config_word(&hose[0],PCI_BDF(0,0,0), PCI_COMMAND, reg16);
+ pci_num_buses = num_buses;
/*
- * Clear non-reserved bits in status register.
+ * Release PCI RST Output signal.
+ * Power on to RST high must be at least 100 ms as per PCI spec.
+ * On warm boots only 1 ms is required.
*/
- pci_hose_write_config_word(&hose[0],PCI_BDF(0,0,0), PCI_STATUS, 0xffff);
- pci_hose_write_config_byte(&hose[0],PCI_BDF(0,0,0), PCI_LATENCY_TIMER,0x80);
-#ifndef PCI_64BIT
- hose[1].first_busno = 0;
- hose[1].last_busno = 0xff;
-
- pci_set_region(hose[1].regions + 0,
- CFG_PCI2_MEM_BASE,
- CFG_PCI2_MEM_PHYS,
- CFG_PCI2_MEM_SIZE,
- PCI_REGION_MEM);
-
- pci_set_region(hose[1].regions + 1,
- CFG_PCI2_IO_BASE,
- CFG_PCI2_IO_PHYS,
- CFG_PCI2_IO_SIZE,
- PCI_REGION_IO);
-
- hose[1].region_count = 2;
-
- pci_setup_indirect(&hose[1],
- (CFG_IMMRBAR+0x8380),
- (CFG_IMMRBAR+0x8384));
-
- pci_hose_write_config_byte(&hose[1],PCI_BDF(0,0,0),PCI_CLASS_CODE,PCI_CLASS_BRIDGE);
- pci_hose_read_config_word (&hose[1],PCI_BDF(0,0,0), PCI_COMMAND, ®16);
- reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
- pci_hose_write_config_word(&hose[1],PCI_BDF(0,0,0), PCI_COMMAND, reg16);
+ udelay(warmboot ? 1000 : 100000);
- /*
- * Clear non-reserved bits in status register.
- */
- pci_hose_write_config_word(&hose[1],PCI_BDF(0,0,0), PCI_STATUS, 0xffff);
- pci_hose_write_config_byte(&hose[1],PCI_BDF(0,0,0), PCI_LATENCY_TIMER,0x80);
-#endif
+ for (i = 0; i < num_buses; i++)
+ immr->pci_ctrl[i].gcr = 1;
-#if defined(PCI_64BIT)
- printf("PCI1 64bit on PMC2\n");
-#elif defined(PCI_ALL_PCI1)
- printf("PCI1 32bit on PMC1 & PMC2 & PMC3\n");
-#elif defined(PCI_ONE_PCI1)
- printf("PCI1 32bit on PMC1,PCI2 32bit on PMC2 & PMC3\n");
-#else
- printf("PCI1 32bit on PMC1 & PMC2 & PMC3 in default\n");
-#endif
-
-#if 1
/*
- * Hose scan.
+ * RST high to first config access must be at least 2^25 cycles
+ * as per PCI spec. This could be cut in half if we know we're
+ * running at 66MHz. This could be insufficiently long if we're
+ * running the PCI bus at significantly less than 33MHz.
*/
- pci_register_hose(hose);
- hose->last_busno = pci_hose_scan(hose);
-#endif
+ udelay(1020000);
+
+ for (i = 0; i < num_buses; i++)
+ pci_init_bus(i, reg[i]);
}
-#endif /* CONFIG_PCI */
+#if defined(CONFIG_OF_LIBFDT)
+void ft_pci_setup(void *blob, bd_t *bd)
+{
+ int nodeoffset;
+ int tmp[2];
+ const char *path;
+
+ if (pci_num_buses < 1)
+ return;
+
+ nodeoffset = fdt_path_offset(blob, "/aliases");
+ if (nodeoffset >= 0) {
+ path = fdt_getprop(blob, nodeoffset, "pci0", NULL);
+ if (path) {
+ tmp[0] = cpu_to_be32(pci_hose[0].first_busno);
+ tmp[1] = cpu_to_be32(pci_hose[0].last_busno);
+ do_fixup_by_path(blob, path, "bus-range",
+ &tmp, sizeof(tmp), 1);
+
+ tmp[0] = cpu_to_be32(gd->pci_clk);
+ do_fixup_by_path(blob, path, "clock-frequency",
+ &tmp, sizeof(tmp[0]), 1);
+ }
+
+ if (pci_num_buses < 2)
+ return;
+
+ path = fdt_getprop(blob, nodeoffset, "pci1", NULL);
+ if (path) {
+ tmp[0] = cpu_to_be32(pci_hose[0].first_busno);
+ tmp[1] = cpu_to_be32(pci_hose[0].last_busno);
+ do_fixup_by_path(blob, path, "bus-range",
+ &tmp, sizeof(tmp), 1);
+
+ tmp[0] = cpu_to_be32(gd->pci_clk);
+ do_fixup_by_path(blob, path, "clock-frequency",
+ &tmp, sizeof(tmp[0]), 1);
+ }
+ }
+}
+#endif /* CONFIG_OF_LIBFDT */