162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Board setup routines for the IBM 750GX/CL platform w/ TSI10x bridge 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007 IBM Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Stephen Winiecki <stevewin@us.ibm.com> 862306a36Sopenharmony_ci * Josh Boyer <jwboyer@linux.vnet.ibm.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Based on code from mpc7448_hpc2.c 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/stddef.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci#include <linux/kdev_t.h> 1762306a36Sopenharmony_ci#include <linux/console.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/irq.h> 2062306a36Sopenharmony_ci#include <linux/seq_file.h> 2162306a36Sopenharmony_ci#include <linux/root_dev.h> 2262306a36Sopenharmony_ci#include <linux/serial.h> 2362306a36Sopenharmony_ci#include <linux/tty.h> 2462306a36Sopenharmony_ci#include <linux/serial_core.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/of_address.h> 2762306a36Sopenharmony_ci#include <linux/of_irq.h> 2862306a36Sopenharmony_ci#include <linux/extable.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/time.h> 3162306a36Sopenharmony_ci#include <asm/machdep.h> 3262306a36Sopenharmony_ci#include <asm/udbg.h> 3362306a36Sopenharmony_ci#include <asm/tsi108.h> 3462306a36Sopenharmony_ci#include <asm/pci-bridge.h> 3562306a36Sopenharmony_ci#include <asm/reg.h> 3662306a36Sopenharmony_ci#include <mm/mmu_decl.h> 3762306a36Sopenharmony_ci#include <asm/tsi108_irq.h> 3862306a36Sopenharmony_ci#include <asm/tsi108_pci.h> 3962306a36Sopenharmony_ci#include <asm/mpic.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#undef DEBUG 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define HOLLY_PCI_CFG_PHYS 0x7c000000 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int holly_exclude_device(struct pci_controller *hose, u_char bus, 4662306a36Sopenharmony_ci u_char devfn) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci if (bus == 0 && PCI_SLOT(devfn) == 0) 4962306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 5062306a36Sopenharmony_ci else 5162306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void __init holly_remap_bridge(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci u32 lut_val, lut_addr; 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci printk(KERN_INFO "Remapping PCI bridge\n"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Re-init the PCI bridge and LUT registers to have mappings that don't 6262306a36Sopenharmony_ci * rely on PIBS 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci lut_addr = 0x900; 6562306a36Sopenharmony_ci for (i = 0; i < 31; i++) { 6662306a36Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000201); 6762306a36Sopenharmony_ci lut_addr += 4; 6862306a36Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); 6962306a36Sopenharmony_ci lut_addr += 4; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Reserve the last LUT entry for PCI I/O space */ 7362306a36Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000241); 7462306a36Sopenharmony_ci lut_addr += 4; 7562306a36Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Map PCI I/O space */ 7862306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_IO_UPPER, 0x0); 7962306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_IO, 0x1); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Map PCI CFG space */ 8262306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_BAR0_UPPER, 0x0); 8362306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_BAR0, 0x7c000000 | 0x01); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* We don't need MEM32 and PRM remapping so disable them */ 8662306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_MEM32, 0x0); 8762306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_PFM3, 0x0); 8862306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_PFAB_PFM4, 0x0); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Set P2O_BAR0 */ 9162306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_P2O_BAR0_UPPER, 0x0); 9262306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_P2O_BAR0, 0xc0000000); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Init the PCI LUTs to do no remapping */ 9562306a36Sopenharmony_ci lut_addr = 0x500; 9662306a36Sopenharmony_ci lut_val = 0x00000002; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 9962306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, lut_val); 10062306a36Sopenharmony_ci lut_addr += 4; 10162306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, 0x40000000); 10262306a36Sopenharmony_ci lut_addr += 4; 10362306a36Sopenharmony_ci lut_val += 0x02000000; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_P2O_PAGE_SIZES, 0x00007900); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Set 64-bit PCI bus address for system memory */ 10862306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_P2O_BAR2_UPPER, 0x0); 10962306a36Sopenharmony_ci tsi108_write_reg(TSI108_PCI_P2O_BAR2, 0x0); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void __init holly_init_pci(void) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct device_node *np; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (ppc_md.progress) 11762306a36Sopenharmony_ci ppc_md.progress("holly_setup_arch():set_bridge", 0); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* setup PCI host bridge */ 12062306a36Sopenharmony_ci holly_remap_bridge(); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci np = of_find_node_by_type(NULL, "pci"); 12362306a36Sopenharmony_ci if (np) 12462306a36Sopenharmony_ci tsi108_setup_pci(np, HOLLY_PCI_CFG_PHYS, 1); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci of_node_put(np); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ppc_md.pci_exclude_device = holly_exclude_device; 12962306a36Sopenharmony_ci if (ppc_md.progress) 13062306a36Sopenharmony_ci ppc_md.progress("tsi108: resources set", 0x100); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void __init holly_setup_arch(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci tsi108_csr_vir_base = get_vir_csrbase(); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci printk(KERN_INFO "PPC750GX/CL Platform\n"); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * Interrupt setup and service. Interrupts on the holly come 14262306a36Sopenharmony_ci * from the four external INT pins, PCI interrupts are routed via 14362306a36Sopenharmony_ci * PCI interrupt control registers, it generates internal IRQ23 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Interrupt routing on the Holly Board: 14662306a36Sopenharmony_ci * TSI108:PB_INT[0] -> CPU0:INT# 14762306a36Sopenharmony_ci * TSI108:PB_INT[1] -> CPU0:MCP# 14862306a36Sopenharmony_ci * TSI108:PB_INT[2] -> N/C 14962306a36Sopenharmony_ci * TSI108:PB_INT[3] -> N/C 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic void __init holly_init_IRQ(void) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct mpic *mpic; 15462306a36Sopenharmony_ci#ifdef CONFIG_PCI 15562306a36Sopenharmony_ci unsigned int cascade_pci_irq; 15662306a36Sopenharmony_ci struct device_node *tsi_pci; 15762306a36Sopenharmony_ci struct device_node *cascade_node = NULL; 15862306a36Sopenharmony_ci#endif 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | 16162306a36Sopenharmony_ci MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, 16262306a36Sopenharmony_ci 24, 0, 16362306a36Sopenharmony_ci "Tsi108_PIC"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci BUG_ON(mpic == NULL); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci mpic_assign_isu(mpic, 0, mpic->paddr + 0x100); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci mpic_init(mpic); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#ifdef CONFIG_PCI 17262306a36Sopenharmony_ci tsi_pci = of_find_node_by_type(NULL, "pci"); 17362306a36Sopenharmony_ci if (tsi_pci == NULL) { 17462306a36Sopenharmony_ci printk(KERN_ERR "%s: No tsi108 pci node found !\n", __func__); 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci cascade_node = of_find_node_by_type(NULL, "pic-router"); 17962306a36Sopenharmony_ci if (cascade_node == NULL) { 18062306a36Sopenharmony_ci printk(KERN_ERR "%s: No tsi108 pci cascade node found !\n", __func__); 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci cascade_pci_irq = irq_of_parse_and_map(tsi_pci, 0); 18562306a36Sopenharmony_ci pr_debug("%s: tsi108 cascade_pci_irq = 0x%x\n", __func__, (u32) cascade_pci_irq); 18662306a36Sopenharmony_ci tsi108_pci_int_init(cascade_node); 18762306a36Sopenharmony_ci irq_set_handler_data(cascade_pci_irq, mpic); 18862306a36Sopenharmony_ci irq_set_chained_handler(cascade_pci_irq, tsi108_irq_cascade); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci of_node_put(tsi_pci); 19162306a36Sopenharmony_ci of_node_put(cascade_node); 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci /* Configure MPIC outputs to CPU0 */ 19462306a36Sopenharmony_ci tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void holly_show_cpuinfo(struct seq_file *m) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci seq_printf(m, "vendor\t\t: IBM\n"); 20062306a36Sopenharmony_ci seq_printf(m, "machine\t\t: PPC750 GX/CL\n"); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void __noreturn holly_restart(char *cmd) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci __be32 __iomem *ocn_bar1 = NULL; 20662306a36Sopenharmony_ci unsigned long bar; 20762306a36Sopenharmony_ci struct device_node *bridge = NULL; 20862306a36Sopenharmony_ci struct resource res; 20962306a36Sopenharmony_ci phys_addr_t addr = 0xc0000000; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci local_irq_disable(); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci bridge = of_find_node_by_type(NULL, "tsi-bridge"); 21462306a36Sopenharmony_ci if (bridge) { 21562306a36Sopenharmony_ci of_address_to_resource(bridge, 0, &res); 21662306a36Sopenharmony_ci addr = res.start; 21762306a36Sopenharmony_ci of_node_put(bridge); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci addr += (TSI108_PB_OFFSET + 0x414); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ocn_bar1 = ioremap(addr, 0x4); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Turn on the BOOT bit so the addresses are correctly 22462306a36Sopenharmony_ci * routed to the HLP interface */ 22562306a36Sopenharmony_ci bar = ioread32be(ocn_bar1); 22662306a36Sopenharmony_ci bar |= 2; 22762306a36Sopenharmony_ci iowrite32be(bar, ocn_bar1); 22862306a36Sopenharmony_ci iosync(); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Set SRR0 to the reset vector and turn on MSR_IP */ 23162306a36Sopenharmony_ci mtspr(SPRN_SRR0, 0xfff00100); 23262306a36Sopenharmony_ci mtspr(SPRN_SRR1, MSR_IP); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Do an rfi to jump back to firmware. Somewhat evil, 23562306a36Sopenharmony_ci * but it works 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci __asm__ __volatile__("rfi" : : : "memory"); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Spin until reset happens. Shouldn't really get here */ 24062306a36Sopenharmony_ci for (;;) ; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int ppc750_machine_check_exception(struct pt_regs *regs) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci const struct exception_table_entry *entry; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Are we prepared to handle this fault */ 24862306a36Sopenharmony_ci if ((entry = search_exception_tables(regs->nip)) != NULL) { 24962306a36Sopenharmony_ci tsi108_clear_pci_cfg_error(); 25062306a36Sopenharmony_ci regs_set_recoverable(regs); 25162306a36Sopenharmony_ci regs_set_return_ip(regs, extable_fixup(entry)); 25262306a36Sopenharmony_ci return 1; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cidefine_machine(holly){ 25862306a36Sopenharmony_ci .name = "PPC750 GX/CL TSI", 25962306a36Sopenharmony_ci .compatible = "ibm,holly", 26062306a36Sopenharmony_ci .setup_arch = holly_setup_arch, 26162306a36Sopenharmony_ci .discover_phbs = holly_init_pci, 26262306a36Sopenharmony_ci .init_IRQ = holly_init_IRQ, 26362306a36Sopenharmony_ci .show_cpuinfo = holly_show_cpuinfo, 26462306a36Sopenharmony_ci .get_irq = mpic_get_irq, 26562306a36Sopenharmony_ci .restart = holly_restart, 26662306a36Sopenharmony_ci .machine_check_exception = ppc750_machine_check_exception, 26762306a36Sopenharmony_ci .progress = udbg_progress, 26862306a36Sopenharmony_ci}; 269