android_kernel_samsung_msm8976/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
Benjamin Herrenschmidt f90bb153b1 [POWERPC] Make pci_read_irq_line the default
This patch reworks the way IRQs are fixed up on PCI for arch powerpc.

It makes pci_read_irq_line() called by default in the PCI code for
devices that are probed, and add an optional per-device fixup in
ppc_md for platforms that really need to correct what they obtain
from pci_read_irq_line().

It also removes ppc_md.irq_bus_setup which was only used by pSeries
and should not be needed anymore.

I've also removed the pSeries s7a workaround as it can't work with
the current interrupt code anyway. I'm trying to get one of these
machines working so I can test a proper fix for that problem.

I also haven't updated the old-style fixup code from 85xx_cds.c
because it's actually buggy :) It assigns pci_dev->irq hard coded
numbers which is no good with the new IRQ mapping code. It should
at least use irq_create_mapping(NULL, hard_coded_number); and possibly
also set_irq_type() to set them as level low.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2006-12-04 16:00:04 +11:00

460 lines
11 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 <linux/root_dev.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_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
#ifndef CONFIG_PCI
unsigned long isa_io_base = 0;
unsigned long isa_mem_base = 0;
unsigned long pci_dram_offset = 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,
16, NR_IRQS - 4,
" MPIC ");
BUG_ON(mpic1 == NULL);
mpic_assign_isu(mpic1, 0, res.start + 0x10000);
/* 48 Internal Interrupts */
mpic_assign_isu(mpic1, 1, res.start + 0x10200);
mpic_assign_isu(mpic1, 2, res.start + 0x10400);
mpic_assign_isu(mpic1, 3, res.start + 0x10600);
/* 16 External interrupts
* Moving them from [0 - 15] to [64 - 79]
*/
mpic_assign_isu(mpic1, 4, res.start + 0x10000);
mpic_init(mpic1);
#ifdef CONFIG_PCI
/* Initialize i8259 controller */
for_each_node_by_type(np, "interrupt-controller")
if (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);
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];
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
}
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);
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 = 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;)
add_bridge(np);
ppc_md.pci_exclude_device = mpc86xx_exclude_device;
#endif
printk("MPC86xx HPCN board from Freescale Semiconductor\n");
#ifdef CONFIG_ROOT_NFS
ROOT_DEV = Root_NFS;
#else
ROOT_DEV = Root_HDA1;
#endif
#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 = 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,
};