162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Maple (970 eval board) setup code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org), 662306a36Sopenharmony_ci * IBM Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#undef DEBUG 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/stddef.h> 1862306a36Sopenharmony_ci#include <linux/unistd.h> 1962306a36Sopenharmony_ci#include <linux/ptrace.h> 2062306a36Sopenharmony_ci#include <linux/user.h> 2162306a36Sopenharmony_ci#include <linux/tty.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/ioport.h> 2562306a36Sopenharmony_ci#include <linux/major.h> 2662306a36Sopenharmony_ci#include <linux/initrd.h> 2762306a36Sopenharmony_ci#include <linux/vt_kern.h> 2862306a36Sopenharmony_ci#include <linux/console.h> 2962306a36Sopenharmony_ci#include <linux/pci.h> 3062306a36Sopenharmony_ci#include <linux/adb.h> 3162306a36Sopenharmony_ci#include <linux/cuda.h> 3262306a36Sopenharmony_ci#include <linux/pmu.h> 3362306a36Sopenharmony_ci#include <linux/irq.h> 3462306a36Sopenharmony_ci#include <linux/seq_file.h> 3562306a36Sopenharmony_ci#include <linux/root_dev.h> 3662306a36Sopenharmony_ci#include <linux/serial.h> 3762306a36Sopenharmony_ci#include <linux/smp.h> 3862306a36Sopenharmony_ci#include <linux/bitops.h> 3962306a36Sopenharmony_ci#include <linux/of.h> 4062306a36Sopenharmony_ci#include <linux/of_address.h> 4162306a36Sopenharmony_ci#include <linux/platform_device.h> 4262306a36Sopenharmony_ci#include <linux/memblock.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <asm/processor.h> 4562306a36Sopenharmony_ci#include <asm/sections.h> 4662306a36Sopenharmony_ci#include <asm/io.h> 4762306a36Sopenharmony_ci#include <asm/pci-bridge.h> 4862306a36Sopenharmony_ci#include <asm/iommu.h> 4962306a36Sopenharmony_ci#include <asm/machdep.h> 5062306a36Sopenharmony_ci#include <asm/dma.h> 5162306a36Sopenharmony_ci#include <asm/cputable.h> 5262306a36Sopenharmony_ci#include <asm/time.h> 5362306a36Sopenharmony_ci#include <asm/mpic.h> 5462306a36Sopenharmony_ci#include <asm/rtas.h> 5562306a36Sopenharmony_ci#include <asm/udbg.h> 5662306a36Sopenharmony_ci#include <asm/nvram.h> 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include "maple.h" 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#ifdef DEBUG 6162306a36Sopenharmony_ci#define DBG(fmt...) udbg_printf(fmt) 6262306a36Sopenharmony_ci#else 6362306a36Sopenharmony_ci#define DBG(fmt...) 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic unsigned long maple_find_nvram_base(void) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct device_node *rtcs; 6962306a36Sopenharmony_ci unsigned long result = 0; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* find NVRAM device */ 7262306a36Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "nvram", "AMD8111"); 7362306a36Sopenharmony_ci if (rtcs) { 7462306a36Sopenharmony_ci struct resource r; 7562306a36Sopenharmony_ci if (of_address_to_resource(rtcs, 0, &r)) { 7662306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Unable to translate NVRAM" 7762306a36Sopenharmony_ci " address\n"); 7862306a36Sopenharmony_ci goto bail; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci if (!(r.flags & IORESOURCE_IO)) { 8162306a36Sopenharmony_ci printk(KERN_EMERG "Maple: NVRAM address isn't PIO!\n"); 8262306a36Sopenharmony_ci goto bail; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci result = r.start; 8562306a36Sopenharmony_ci } else 8662306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Unable to find NVRAM\n"); 8762306a36Sopenharmony_ci bail: 8862306a36Sopenharmony_ci of_node_put(rtcs); 8962306a36Sopenharmony_ci return result; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void __noreturn maple_restart(char *cmd) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci unsigned int maple_nvram_base; 9562306a36Sopenharmony_ci const unsigned int *maple_nvram_offset, *maple_nvram_command; 9662306a36Sopenharmony_ci struct device_node *sp; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci maple_nvram_base = maple_find_nvram_base(); 9962306a36Sopenharmony_ci if (maple_nvram_base == 0) 10062306a36Sopenharmony_ci goto fail; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* find service processor device */ 10362306a36Sopenharmony_ci sp = of_find_node_by_name(NULL, "service-processor"); 10462306a36Sopenharmony_ci if (!sp) { 10562306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); 10662306a36Sopenharmony_ci goto fail; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci maple_nvram_offset = of_get_property(sp, "restart-addr", NULL); 10962306a36Sopenharmony_ci maple_nvram_command = of_get_property(sp, "restart-value", NULL); 11062306a36Sopenharmony_ci of_node_put(sp); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* send command */ 11362306a36Sopenharmony_ci outb_p(*maple_nvram_command, maple_nvram_base + *maple_nvram_offset); 11462306a36Sopenharmony_ci for (;;) ; 11562306a36Sopenharmony_ci fail: 11662306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Manual Restart Required\n"); 11762306a36Sopenharmony_ci for (;;) ; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void __noreturn maple_power_off(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci unsigned int maple_nvram_base; 12362306a36Sopenharmony_ci const unsigned int *maple_nvram_offset, *maple_nvram_command; 12462306a36Sopenharmony_ci struct device_node *sp; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci maple_nvram_base = maple_find_nvram_base(); 12762306a36Sopenharmony_ci if (maple_nvram_base == 0) 12862306a36Sopenharmony_ci goto fail; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* find service processor device */ 13162306a36Sopenharmony_ci sp = of_find_node_by_name(NULL, "service-processor"); 13262306a36Sopenharmony_ci if (!sp) { 13362306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); 13462306a36Sopenharmony_ci goto fail; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci maple_nvram_offset = of_get_property(sp, "power-off-addr", NULL); 13762306a36Sopenharmony_ci maple_nvram_command = of_get_property(sp, "power-off-value", NULL); 13862306a36Sopenharmony_ci of_node_put(sp); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* send command */ 14162306a36Sopenharmony_ci outb_p(*maple_nvram_command, maple_nvram_base + *maple_nvram_offset); 14262306a36Sopenharmony_ci for (;;) ; 14362306a36Sopenharmony_ci fail: 14462306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Manual Power-Down Required\n"); 14562306a36Sopenharmony_ci for (;;) ; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void __noreturn maple_halt(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci maple_power_off(); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#ifdef CONFIG_SMP 15462306a36Sopenharmony_cistatic struct smp_ops_t maple_smp_ops = { 15562306a36Sopenharmony_ci .probe = smp_mpic_probe, 15662306a36Sopenharmony_ci .message_pass = smp_mpic_message_pass, 15762306a36Sopenharmony_ci .kick_cpu = smp_generic_kick_cpu, 15862306a36Sopenharmony_ci .setup_cpu = smp_mpic_setup_cpu, 15962306a36Sopenharmony_ci .give_timebase = smp_generic_give_timebase, 16062306a36Sopenharmony_ci .take_timebase = smp_generic_take_timebase, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void __init maple_use_rtas_reboot_and_halt_if_present(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (rtas_function_implemented(RTAS_FN_SYSTEM_REBOOT) && 16762306a36Sopenharmony_ci rtas_function_implemented(RTAS_FN_POWER_OFF)) { 16862306a36Sopenharmony_ci ppc_md.restart = rtas_restart; 16962306a36Sopenharmony_ci pm_power_off = rtas_power_off; 17062306a36Sopenharmony_ci ppc_md.halt = rtas_halt; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void __init maple_setup_arch(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci /* init to some ~sane value until calibrate_delay() runs */ 17762306a36Sopenharmony_ci loops_per_jiffy = 50000000; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Setup SMP callback */ 18062306a36Sopenharmony_ci#ifdef CONFIG_SMP 18162306a36Sopenharmony_ci smp_ops = &maple_smp_ops; 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci maple_use_rtas_reboot_and_halt_if_present(); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci printk(KERN_DEBUG "Using native/NAP idle loop\n"); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci mmio_nvram_init(); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * This is almost identical to pSeries and CHRP. We need to make that 19262306a36Sopenharmony_ci * code generic at one point, with appropriate bits in the device-tree to 19362306a36Sopenharmony_ci * identify the presence of an HT APIC 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic void __init maple_init_IRQ(void) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct device_node *root, *np, *mpic_node = NULL; 19862306a36Sopenharmony_ci const unsigned int *opprop; 19962306a36Sopenharmony_ci unsigned long openpic_addr = 0; 20062306a36Sopenharmony_ci int naddr, n, i, opplen, has_isus = 0; 20162306a36Sopenharmony_ci struct mpic *mpic; 20262306a36Sopenharmony_ci unsigned int flags = 0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Locate MPIC in the device-tree. Note that there is a bug 20562306a36Sopenharmony_ci * in Maple device-tree where the type of the controller is 20662306a36Sopenharmony_ci * open-pic and not interrupt-controller 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for_each_node_by_type(np, "interrupt-controller") 21062306a36Sopenharmony_ci if (of_device_is_compatible(np, "open-pic")) { 21162306a36Sopenharmony_ci mpic_node = np; 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci if (mpic_node == NULL) 21562306a36Sopenharmony_ci for_each_node_by_type(np, "open-pic") { 21662306a36Sopenharmony_ci mpic_node = np; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci if (mpic_node == NULL) { 22062306a36Sopenharmony_ci printk(KERN_ERR 22162306a36Sopenharmony_ci "Failed to locate the MPIC interrupt controller\n"); 22262306a36Sopenharmony_ci return; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Find address list in /platform-open-pic */ 22662306a36Sopenharmony_ci root = of_find_node_by_path("/"); 22762306a36Sopenharmony_ci naddr = of_n_addr_cells(root); 22862306a36Sopenharmony_ci opprop = of_get_property(root, "platform-open-pic", &opplen); 22962306a36Sopenharmony_ci if (opprop) { 23062306a36Sopenharmony_ci openpic_addr = of_read_number(opprop, naddr); 23162306a36Sopenharmony_ci has_isus = (opplen > naddr); 23262306a36Sopenharmony_ci printk(KERN_DEBUG "OpenPIC addr: %lx, has ISUs: %d\n", 23362306a36Sopenharmony_ci openpic_addr, has_isus); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci BUG_ON(openpic_addr == 0); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Check for a big endian MPIC */ 23962306a36Sopenharmony_ci if (of_property_read_bool(np, "big-endian")) 24062306a36Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* XXX Maple specific bits */ 24362306a36Sopenharmony_ci flags |= MPIC_U3_HT_IRQS; 24462306a36Sopenharmony_ci /* All U3/U4 are big-endian, older SLOF firmware doesn't encode this */ 24562306a36Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Setup the openpic driver. More device-tree junks, we hard code no 24862306a36Sopenharmony_ci * ISUs for now. I'll have to revisit some stuffs with the folks doing 24962306a36Sopenharmony_ci * the firmware for those 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci mpic = mpic_alloc(mpic_node, openpic_addr, flags, 25262306a36Sopenharmony_ci /*has_isus ? 16 :*/ 0, 0, " MPIC "); 25362306a36Sopenharmony_ci BUG_ON(mpic == NULL); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Add ISUs */ 25662306a36Sopenharmony_ci opplen /= sizeof(u32); 25762306a36Sopenharmony_ci for (n = 0, i = naddr; i < opplen; i += naddr, n++) { 25862306a36Sopenharmony_ci unsigned long isuaddr = of_read_number(opprop + i, naddr); 25962306a36Sopenharmony_ci mpic_assign_isu(mpic, n, isuaddr); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* All ISUs are setup, complete initialization */ 26362306a36Sopenharmony_ci mpic_init(mpic); 26462306a36Sopenharmony_ci ppc_md.get_irq = mpic_get_irq; 26562306a36Sopenharmony_ci of_node_put(mpic_node); 26662306a36Sopenharmony_ci of_node_put(root); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void __init maple_progress(char *s, unsigned short hex) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci printk("*** %04x : %s\n", hex, s ? s : ""); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* 27662306a36Sopenharmony_ci * Called very early, MMU is off, device-tree isn't unflattened 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_cistatic int __init maple_probe(void) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci if (!of_machine_is_compatible("Momentum,Maple") && 28162306a36Sopenharmony_ci !of_machine_is_compatible("Momentum,Apache")) 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci pm_power_off = maple_power_off; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci iommu_init_early_dart(&maple_pci_controller_ops); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 1; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#ifdef CONFIG_EDAC 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * Register a platform device for CPC925 memory controller on 29462306a36Sopenharmony_ci * all boards with U3H (CPC925) bridge. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistatic int __init maple_cpc925_edac_setup(void) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct platform_device *pdev; 29962306a36Sopenharmony_ci struct device_node *np = NULL; 30062306a36Sopenharmony_ci struct resource r; 30162306a36Sopenharmony_ci int ret; 30262306a36Sopenharmony_ci volatile void __iomem *mem; 30362306a36Sopenharmony_ci u32 rev; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci np = of_find_node_by_type(NULL, "memory-controller"); 30662306a36Sopenharmony_ci if (!np) { 30762306a36Sopenharmony_ci printk(KERN_ERR "%s: Unable to find memory-controller node\n", 30862306a36Sopenharmony_ci __func__); 30962306a36Sopenharmony_ci return -ENODEV; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &r); 31362306a36Sopenharmony_ci of_node_put(np); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ret < 0) { 31662306a36Sopenharmony_ci printk(KERN_ERR "%s: Unable to get memory-controller reg\n", 31762306a36Sopenharmony_ci __func__); 31862306a36Sopenharmony_ci return -ENODEV; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci mem = ioremap(r.start, resource_size(&r)); 32262306a36Sopenharmony_ci if (!mem) { 32362306a36Sopenharmony_ci printk(KERN_ERR "%s: Unable to map memory-controller memory\n", 32462306a36Sopenharmony_ci __func__); 32562306a36Sopenharmony_ci return -ENOMEM; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci rev = __raw_readl(mem); 32962306a36Sopenharmony_ci iounmap(mem); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (rev < 0x34 || rev > 0x3f) { /* U3H */ 33262306a36Sopenharmony_ci printk(KERN_ERR "%s: Non-CPC925(U3H) bridge revision: %02x\n", 33362306a36Sopenharmony_ci __func__, rev); 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci pdev = platform_device_register_simple("cpc925_edac", 0, &r, 1); 33862306a36Sopenharmony_ci if (IS_ERR(pdev)) 33962306a36Sopenharmony_ci return PTR_ERR(pdev); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci printk(KERN_INFO "%s: CPC925 platform device created\n", __func__); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_cimachine_device_initcall(maple, maple_cpc925_edac_setup); 34662306a36Sopenharmony_ci#endif 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cidefine_machine(maple) { 34962306a36Sopenharmony_ci .name = "Maple", 35062306a36Sopenharmony_ci .probe = maple_probe, 35162306a36Sopenharmony_ci .setup_arch = maple_setup_arch, 35262306a36Sopenharmony_ci .discover_phbs = maple_pci_init, 35362306a36Sopenharmony_ci .init_IRQ = maple_init_IRQ, 35462306a36Sopenharmony_ci .pci_irq_fixup = maple_pci_irq_fixup, 35562306a36Sopenharmony_ci .pci_get_legacy_ide_irq = maple_pci_get_legacy_ide_irq, 35662306a36Sopenharmony_ci .restart = maple_restart, 35762306a36Sopenharmony_ci .halt = maple_halt, 35862306a36Sopenharmony_ci .get_boot_time = maple_get_boot_time, 35962306a36Sopenharmony_ci .set_rtc_time = maple_set_rtc_time, 36062306a36Sopenharmony_ci .get_rtc_time = maple_get_rtc_time, 36162306a36Sopenharmony_ci .progress = maple_progress, 36262306a36Sopenharmony_ci .power_save = power4_idle, 36362306a36Sopenharmony_ci}; 364