6c0a11c118
On the 85xx/86xx PCIe controllers if there is no device connected to the PHB we will still allocate a pci_bus for downstream bus of the virtual P2P bridge. However the resources allocated to the downstream bus are not correct and so we just mimic the resources from the upstream pci_bus. Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
447 lines
10 KiB
C
447 lines
10 KiB
C
/*
|
|
* MPC86xx HPCN board specific routines
|
|
*
|
|
* Recode: ZHANG WEI <wei.zhang@freescale.com>
|
|
* Initial author: Xianghua Xiao <x.xiao@freescale.com>
|
|
*
|
|
* Copyright 2006 Freescale Semiconductor Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/stddef.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/time.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/pci-bridge.h>
|
|
#include <asm/mpc86xx.h>
|
|
#include <asm/prom.h>
|
|
#include <mm/mmu_decl.h>
|
|
#include <asm/udbg.h>
|
|
#include <asm/i8259.h>
|
|
|
|
#include <asm/mpic.h>
|
|
|
|
#include <sysdev/fsl_pci.h>
|
|
#include <sysdev/fsl_soc.h>
|
|
|
|
#include "mpc86xx.h"
|
|
#include "mpc8641_hpcn.h"
|
|
|
|
#undef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(fmt...) do { printk(KERN_ERR fmt); } while(0)
|
|
#else
|
|
#define DBG(fmt...) do { } while(0)
|
|
#endif
|
|
|
|
#ifdef CONFIG_PCI
|
|
static void mpc86xx_8259_cascade(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
unsigned int cascade_irq = i8259_irq();
|
|
if (cascade_irq != NO_IRQ)
|
|
generic_handle_irq(cascade_irq);
|
|
desc->chip->eoi(irq);
|
|
}
|
|
#endif /* CONFIG_PCI */
|
|
|
|
void __init
|
|
mpc86xx_hpcn_init_irq(void)
|
|
{
|
|
struct mpic *mpic1;
|
|
struct device_node *np;
|
|
struct resource res;
|
|
#ifdef CONFIG_PCI
|
|
struct device_node *cascade_node = NULL;
|
|
int cascade_irq;
|
|
#endif
|
|
|
|
/* Determine PIC address. */
|
|
np = of_find_node_by_type(NULL, "open-pic");
|
|
if (np == NULL)
|
|
return;
|
|
of_address_to_resource(np, 0, &res);
|
|
|
|
/* Alloc mpic structure and per isu has 16 INT entries. */
|
|
mpic1 = mpic_alloc(np, res.start,
|
|
MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN,
|
|
0, 256, " MPIC ");
|
|
BUG_ON(mpic1 == NULL);
|
|
|
|
mpic_init(mpic1);
|
|
|
|
#ifdef CONFIG_PCI
|
|
/* Initialize i8259 controller */
|
|
for_each_node_by_type(np, "interrupt-controller")
|
|
if (of_device_is_compatible(np, "chrp,iic")) {
|
|
cascade_node = np;
|
|
break;
|
|
}
|
|
if (cascade_node == NULL) {
|
|
printk(KERN_DEBUG "mpc86xxhpcn: no ISA interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
cascade_irq = irq_of_parse_and_map(cascade_node, 0);
|
|
if (cascade_irq == NO_IRQ) {
|
|
printk(KERN_ERR "mpc86xxhpcn: failed to map cascade interrupt");
|
|
return;
|
|
}
|
|
DBG("mpc86xxhpcn: cascade mapped to irq %d\n", cascade_irq);
|
|
|
|
i8259_init(cascade_node, 0);
|
|
of_node_put(cascade_node);
|
|
|
|
set_irq_chained_handler(cascade_irq, mpc86xx_8259_cascade);
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_PCI
|
|
|
|
enum pirq{PIRQA = 8, PIRQB, PIRQC, PIRQD, PIRQE, PIRQF, PIRQG, PIRQH};
|
|
const unsigned char uli1575_irq_route_table[16] = {
|
|
0, /* 0: Reserved */
|
|
0x8, /* 1: 0b1000 */
|
|
0, /* 2: Reserved */
|
|
0x2, /* 3: 0b0010 */
|
|
0x4, /* 4: 0b0100 */
|
|
0x5, /* 5: 0b0101 */
|
|
0x7, /* 6: 0b0111 */
|
|
0x6, /* 7: 0b0110 */
|
|
0, /* 8: Reserved */
|
|
0x1, /* 9: 0b0001 */
|
|
0x3, /* 10: 0b0011 */
|
|
0x9, /* 11: 0b1001 */
|
|
0xb, /* 12: 0b1011 */
|
|
0, /* 13: Reserved */
|
|
0xd, /* 14, 0b1101 */
|
|
0xf, /* 15, 0b1111 */
|
|
};
|
|
|
|
static int __devinit
|
|
get_pci_irq_from_of(struct pci_controller *hose, int slot, int pin)
|
|
{
|
|
struct of_irq oirq;
|
|
u32 laddr[3];
|
|
struct device_node *hosenode = hose ? hose->arch_data : NULL;
|
|
|
|
if (!hosenode) return -EINVAL;
|
|
|
|
laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(slot, 0) << 8);
|
|
laddr[1] = laddr[2] = 0;
|
|
of_irq_map_raw(hosenode, &pin, 1, laddr, &oirq);
|
|
DBG("mpc86xx_hpcn: pci irq addr %x, slot %d, pin %d, irq %d\n",
|
|
laddr[0], slot, pin, oirq.specifier[0]);
|
|
return oirq.specifier[0];
|
|
}
|
|
|
|
static void __devinit quirk_uli1575(struct pci_dev *dev)
|
|
{
|
|
unsigned short temp;
|
|
struct pci_controller *hose = pci_bus_to_host(dev->bus);
|
|
unsigned char irq2pin[16], c;
|
|
unsigned long pirq_map_word = 0;
|
|
u32 irq;
|
|
int i;
|
|
|
|
/*
|
|
* ULI1575 interrupts route setup
|
|
*/
|
|
memset(irq2pin, 0, 16); /* Initialize default value 0 */
|
|
|
|
/*
|
|
* PIRQA -> PIRQD mapping read from OF-tree
|
|
*
|
|
* interrupts for PCI slot0 -- PIRQA / PIRQB / PIRQC / PIRQD
|
|
* PCI slot1 -- PIRQB / PIRQC / PIRQD / PIRQA
|
|
*/
|
|
for (i = 0; i < 4; i++){
|
|
irq = get_pci_irq_from_of(hose, 17, i + 1);
|
|
if (irq > 0 && irq < 16)
|
|
irq2pin[irq] = PIRQA + i;
|
|
else
|
|
printk(KERN_WARNING "ULI1575 device"
|
|
"(slot %d, pin %d) irq %d is invalid.\n",
|
|
17, i, irq);
|
|
}
|
|
|
|
/*
|
|
* PIRQE -> PIRQF mapping set manually
|
|
*
|
|
* IRQ pin IRQ#
|
|
* PIRQE ---- 9
|
|
* PIRQF ---- 10
|
|
* PIRQG ---- 11
|
|
* PIRQH ---- 12
|
|
*/
|
|
for (i = 0; i < 4; i++) irq2pin[i + 9] = PIRQE + i;
|
|
|
|
/* Set IRQ-PIRQ Mapping to ULI1575 */
|
|
for (i = 0; i < 16; i++)
|
|
if (irq2pin[i])
|
|
pirq_map_word |= (uli1575_irq_route_table[i] & 0xf)
|
|
<< ((irq2pin[i] - PIRQA) * 4);
|
|
|
|
/* ULI1575 IRQ mapping conf register default value is 0xb9317542 */
|
|
DBG("Setup ULI1575 IRQ mapping configuration register value = 0x%x\n",
|
|
pirq_map_word);
|
|
pci_write_config_dword(dev, 0x48, pirq_map_word);
|
|
|
|
#define ULI1575_SET_DEV_IRQ(slot, pin, reg) \
|
|
do { \
|
|
int irq; \
|
|
irq = get_pci_irq_from_of(hose, slot, pin); \
|
|
if (irq > 0 && irq < 16) \
|
|
pci_write_config_byte(dev, reg, irq2pin[irq]); \
|
|
else \
|
|
printk(KERN_WARNING "ULI1575 device" \
|
|
"(slot %d, pin %d) irq %d is invalid.\n", \
|
|
slot, pin, irq); \
|
|
} while(0)
|
|
|
|
/* USB 1.1 OHCI controller 1, slot 28, pin 1 */
|
|
ULI1575_SET_DEV_IRQ(28, 1, 0x86);
|
|
|
|
/* USB 1.1 OHCI controller 2, slot 28, pin 2 */
|
|
ULI1575_SET_DEV_IRQ(28, 2, 0x87);
|
|
|
|
/* USB 1.1 OHCI controller 3, slot 28, pin 3 */
|
|
ULI1575_SET_DEV_IRQ(28, 3, 0x88);
|
|
|
|
/* USB 2.0 controller, slot 28, pin 4 */
|
|
irq = get_pci_irq_from_of(hose, 28, 4);
|
|
if (irq >= 0 && irq <=15)
|
|
pci_write_config_dword(dev, 0x74, uli1575_irq_route_table[irq]);
|
|
|
|
/* Audio controller, slot 29, pin 1 */
|
|
ULI1575_SET_DEV_IRQ(29, 1, 0x8a);
|
|
|
|
/* Modem controller, slot 29, pin 2 */
|
|
ULI1575_SET_DEV_IRQ(29, 2, 0x8b);
|
|
|
|
/* HD audio controller, slot 29, pin 3 */
|
|
ULI1575_SET_DEV_IRQ(29, 3, 0x8c);
|
|
|
|
/* SMB interrupt: slot 30, pin 1 */
|
|
ULI1575_SET_DEV_IRQ(30, 1, 0x8e);
|
|
|
|
/* PMU ACPI SCI interrupt: slot 30, pin 2 */
|
|
ULI1575_SET_DEV_IRQ(30, 2, 0x8f);
|
|
|
|
/* Serial ATA interrupt: slot 31, pin 1 */
|
|
ULI1575_SET_DEV_IRQ(31, 1, 0x8d);
|
|
|
|
/* Primary PATA IDE IRQ: 14
|
|
* Secondary PATA IDE IRQ: 15
|
|
*/
|
|
pci_write_config_byte(dev, 0x44, 0x30 | uli1575_irq_route_table[14]);
|
|
pci_write_config_byte(dev, 0x75, uli1575_irq_route_table[15]);
|
|
|
|
/* Set IRQ14 and IRQ15 to legacy IRQs */
|
|
pci_read_config_word(dev, 0x46, &temp);
|
|
temp |= 0xc000;
|
|
pci_write_config_word(dev, 0x46, temp);
|
|
|
|
/* Set i8259 interrupt trigger
|
|
* IRQ 3: Level
|
|
* IRQ 4: Level
|
|
* IRQ 5: Level
|
|
* IRQ 6: Level
|
|
* IRQ 7: Level
|
|
* IRQ 9: Level
|
|
* IRQ 10: Level
|
|
* IRQ 11: Level
|
|
* IRQ 12: Level
|
|
* IRQ 14: Edge
|
|
* IRQ 15: Edge
|
|
*/
|
|
outb(0xfa, 0x4d0);
|
|
outb(0x1e, 0x4d1);
|
|
|
|
#undef ULI1575_SET_DEV_IRQ
|
|
|
|
/* Disable the HD interface and enable the AC97 interface. */
|
|
pci_read_config_byte(dev, 0xb8, &c);
|
|
c &= 0x7f;
|
|
pci_write_config_byte(dev, 0xb8, c);
|
|
}
|
|
|
|
static void __devinit quirk_uli5288(struct pci_dev *dev)
|
|
{
|
|
unsigned char c;
|
|
|
|
pci_read_config_byte(dev,0x83,&c);
|
|
c |= 0x80;
|
|
pci_write_config_byte(dev, 0x83, c);
|
|
|
|
pci_write_config_byte(dev, 0x09, 0x01);
|
|
pci_write_config_byte(dev, 0x0a, 0x06);
|
|
|
|
pci_read_config_byte(dev,0x83,&c);
|
|
c &= 0x7f;
|
|
pci_write_config_byte(dev, 0x83, c);
|
|
|
|
pci_read_config_byte(dev,0x84,&c);
|
|
c |= 0x01;
|
|
pci_write_config_byte(dev, 0x84, c);
|
|
}
|
|
|
|
static void __devinit quirk_uli5229(struct pci_dev *dev)
|
|
{
|
|
unsigned short temp;
|
|
pci_write_config_word(dev, 0x04, 0x0405);
|
|
dev->class &= ~0x5;
|
|
pci_read_config_word(dev, 0x4a, &temp);
|
|
temp |= 0x1000;
|
|
pci_write_config_word(dev, 0x4a, temp);
|
|
}
|
|
|
|
static void __devinit early_uli5249(struct pci_dev *dev)
|
|
{
|
|
unsigned char temp;
|
|
pci_write_config_word(dev, 0x04, 0x0007);
|
|
pci_read_config_byte(dev, 0x7c, &temp);
|
|
pci_write_config_byte(dev, 0x7c, 0x80);
|
|
pci_write_config_byte(dev, 0x09, 0x01);
|
|
pci_write_config_byte(dev, 0x7c, temp);
|
|
dev->class |= 0x1;
|
|
}
|
|
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1575, quirk_uli1575);
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5288, quirk_uli5288);
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5229, quirk_uli5229);
|
|
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AL, 0x5249, early_uli5249);
|
|
#endif /* CONFIG_PCI */
|
|
|
|
|
|
static void __init
|
|
mpc86xx_hpcn_setup_arch(void)
|
|
{
|
|
struct device_node *np;
|
|
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("mpc86xx_hpcn_setup_arch()", 0);
|
|
|
|
np = of_find_node_by_type(NULL, "cpu");
|
|
if (np != 0) {
|
|
const unsigned int *fp;
|
|
|
|
fp = of_get_property(np, "clock-frequency", NULL);
|
|
if (fp != 0)
|
|
loops_per_jiffy = *fp / HZ;
|
|
else
|
|
loops_per_jiffy = 50000000 / HZ;
|
|
of_node_put(np);
|
|
}
|
|
|
|
#ifdef CONFIG_PCI
|
|
for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) {
|
|
struct resource rsrc;
|
|
of_address_to_resource(np, 0, &rsrc);
|
|
if ((rsrc.start & 0xfffff) == 0x8000)
|
|
fsl_add_bridge(np, 1);
|
|
else
|
|
fsl_add_bridge(np, 0);
|
|
}
|
|
#endif
|
|
|
|
printk("MPC86xx HPCN board from Freescale Semiconductor\n");
|
|
|
|
#ifdef CONFIG_SMP
|
|
mpc86xx_smp_init();
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
mpc86xx_hpcn_show_cpuinfo(struct seq_file *m)
|
|
{
|
|
struct device_node *root;
|
|
uint memsize = total_memory;
|
|
const char *model = "";
|
|
uint svid = mfspr(SPRN_SVR);
|
|
|
|
seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n");
|
|
|
|
root = of_find_node_by_path("/");
|
|
if (root)
|
|
model = of_get_property(root, "model", NULL);
|
|
seq_printf(m, "Machine\t\t: %s\n", model);
|
|
of_node_put(root);
|
|
|
|
seq_printf(m, "SVR\t\t: 0x%x\n", svid);
|
|
seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
|
|
}
|
|
|
|
|
|
/*
|
|
* Called very early, device-tree isn't unflattened
|
|
*/
|
|
static int __init mpc86xx_hpcn_probe(void)
|
|
{
|
|
unsigned long root = of_get_flat_dt_root();
|
|
|
|
if (of_flat_dt_is_compatible(root, "mpc86xx"))
|
|
return 1; /* Looks good */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
mpc86xx_restart(char *cmd)
|
|
{
|
|
void __iomem *rstcr;
|
|
|
|
rstcr = ioremap(get_immrbase() + MPC86XX_RSTCR_OFFSET, 0x100);
|
|
|
|
local_irq_disable();
|
|
|
|
/* Assert reset request to Reset Control Register */
|
|
out_be32(rstcr, 0x2);
|
|
|
|
/* not reached */
|
|
}
|
|
|
|
|
|
long __init
|
|
mpc86xx_time_init(void)
|
|
{
|
|
unsigned int temp;
|
|
|
|
/* Set the time base to zero */
|
|
mtspr(SPRN_TBWL, 0);
|
|
mtspr(SPRN_TBWU, 0);
|
|
|
|
temp = mfspr(SPRN_HID0);
|
|
temp |= HID0_TBEN;
|
|
mtspr(SPRN_HID0, temp);
|
|
asm volatile("isync");
|
|
|
|
return 0;
|
|
}
|
|
|
|
define_machine(mpc86xx_hpcn) {
|
|
.name = "MPC86xx HPCN",
|
|
.probe = mpc86xx_hpcn_probe,
|
|
.setup_arch = mpc86xx_hpcn_setup_arch,
|
|
.init_IRQ = mpc86xx_hpcn_init_irq,
|
|
.show_cpuinfo = mpc86xx_hpcn_show_cpuinfo,
|
|
.get_irq = mpic_get_irq,
|
|
.restart = mpc86xx_restart,
|
|
.time_init = mpc86xx_time_init,
|
|
.calibrate_decr = generic_calibrate_decr,
|
|
.progress = udbg_progress,
|
|
.pcibios_fixup_bus = fsl_pcibios_fixup_bus,
|
|
};
|