162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Imagination Technologies 462306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) "sead3: " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/libfdt.h> 1162306a36Sopenharmony_ci#include <linux/printk.h> 1262306a36Sopenharmony_ci#include <linux/sizes.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/fw/fw.h> 1562306a36Sopenharmony_ci#include <asm/io.h> 1662306a36Sopenharmony_ci#include <asm/machine.h> 1762306a36Sopenharmony_ci#include <asm/yamon-dt.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define SEAD_CONFIG CKSEG1ADDR(0x1b100110) 2062306a36Sopenharmony_ci#define SEAD_CONFIG_GIC_PRESENT BIT(1) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MIPS_REVISION CKSEG1ADDR(0x1fc00010) 2362306a36Sopenharmony_ci#define MIPS_REVISION_MACHINE (0xf << 4) 2462306a36Sopenharmony_ci#define MIPS_REVISION_MACHINE_SEAD3 (0x4 << 4) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Maximum 384MB RAM at physical address 0, preceding any I/O. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic struct yamon_mem_region mem_regions[] __initdata = { 3062306a36Sopenharmony_ci /* start size */ 3162306a36Sopenharmony_ci { 0, SZ_256M + SZ_128M }, 3262306a36Sopenharmony_ci {} 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic __init bool sead3_detect(void) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci uint32_t rev; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci rev = __raw_readl((void *)MIPS_REVISION); 4062306a36Sopenharmony_ci return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic __init int append_memory(void *fdt) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return yamon_dt_append_memory(fdt, mem_regions); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic __init int remove_gic(void *fdt) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci const unsigned int cpu_ehci_int = 2; 5162306a36Sopenharmony_ci const unsigned int cpu_uart_int = 4; 5262306a36Sopenharmony_ci const unsigned int cpu_eth_int = 6; 5362306a36Sopenharmony_ci int gic_off, cpu_off, uart_off, eth_off, ehci_off, err; 5462306a36Sopenharmony_ci uint32_t cfg, cpu_phandle; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* leave the GIC node intact if a GIC is present */ 5762306a36Sopenharmony_ci cfg = __raw_readl((uint32_t *)SEAD_CONFIG); 5862306a36Sopenharmony_ci if (cfg & SEAD_CONFIG_GIC_PRESENT) 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic"); 6262306a36Sopenharmony_ci if (gic_off < 0) { 6362306a36Sopenharmony_ci pr_err("unable to find DT GIC node: %d\n", gic_off); 6462306a36Sopenharmony_ci return gic_off; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci err = fdt_nop_node(fdt, gic_off); 6862306a36Sopenharmony_ci if (err) { 6962306a36Sopenharmony_ci pr_err("unable to nop GIC node\n"); 7062306a36Sopenharmony_ci return err; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci cpu_off = fdt_node_offset_by_compatible(fdt, -1, 7462306a36Sopenharmony_ci "mti,cpu-interrupt-controller"); 7562306a36Sopenharmony_ci if (cpu_off < 0) { 7662306a36Sopenharmony_ci pr_err("unable to find CPU intc node: %d\n", cpu_off); 7762306a36Sopenharmony_ci return cpu_off; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci cpu_phandle = fdt_get_phandle(fdt, cpu_off); 8162306a36Sopenharmony_ci if (!cpu_phandle) { 8262306a36Sopenharmony_ci pr_err("unable to get CPU intc phandle\n"); 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a"); 8762306a36Sopenharmony_ci while (uart_off >= 0) { 8862306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, uart_off, "interrupt-parent", 8962306a36Sopenharmony_ci cpu_phandle); 9062306a36Sopenharmony_ci if (err) { 9162306a36Sopenharmony_ci pr_warn("unable to set UART interrupt-parent: %d\n", 9262306a36Sopenharmony_ci err); 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, uart_off, "interrupts", 9762306a36Sopenharmony_ci cpu_uart_int); 9862306a36Sopenharmony_ci if (err) { 9962306a36Sopenharmony_ci pr_err("unable to set UART interrupts property: %d\n", 10062306a36Sopenharmony_ci err); 10162306a36Sopenharmony_ci return err; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci uart_off = fdt_node_offset_by_compatible(fdt, uart_off, 10562306a36Sopenharmony_ci "ns16550a"); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (uart_off != -FDT_ERR_NOTFOUND) { 10862306a36Sopenharmony_ci pr_err("error searching for UART DT node: %d\n", uart_off); 10962306a36Sopenharmony_ci return uart_off; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115"); 11362306a36Sopenharmony_ci if (eth_off < 0) { 11462306a36Sopenharmony_ci pr_err("unable to find ethernet DT node: %d\n", eth_off); 11562306a36Sopenharmony_ci return eth_off; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, eth_off, "interrupt-parent", cpu_phandle); 11962306a36Sopenharmony_ci if (err) { 12062306a36Sopenharmony_ci pr_err("unable to set ethernet interrupt-parent: %d\n", err); 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int); 12562306a36Sopenharmony_ci if (err) { 12662306a36Sopenharmony_ci pr_err("unable to set ethernet interrupts property: %d\n", err); 12762306a36Sopenharmony_ci return err; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci"); 13162306a36Sopenharmony_ci if (ehci_off < 0) { 13262306a36Sopenharmony_ci pr_err("unable to find EHCI DT node: %d\n", ehci_off); 13362306a36Sopenharmony_ci return ehci_off; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, ehci_off, "interrupt-parent", cpu_phandle); 13762306a36Sopenharmony_ci if (err) { 13862306a36Sopenharmony_ci pr_err("unable to set EHCI interrupt-parent: %d\n", err); 13962306a36Sopenharmony_ci return err; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int); 14362306a36Sopenharmony_ci if (err) { 14462306a36Sopenharmony_ci pr_err("unable to set EHCI interrupts property: %d\n", err); 14562306a36Sopenharmony_ci return err; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const struct mips_fdt_fixup sead3_fdt_fixups[] __initconst = { 15262306a36Sopenharmony_ci { yamon_dt_append_cmdline, "append command line" }, 15362306a36Sopenharmony_ci { append_memory, "append memory" }, 15462306a36Sopenharmony_ci { remove_gic, "remove GIC when not present" }, 15562306a36Sopenharmony_ci { yamon_dt_serial_config, "append serial configuration" }, 15662306a36Sopenharmony_ci { }, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic __init const void *sead3_fixup_fdt(const void *fdt, 16062306a36Sopenharmony_ci const void *match_data) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci static unsigned char fdt_buf[16 << 10] __initdata; 16362306a36Sopenharmony_ci int err; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (fdt_check_header(fdt)) 16662306a36Sopenharmony_ci panic("Corrupt DT"); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* if this isn't SEAD3, something went wrong */ 16962306a36Sopenharmony_ci BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3")); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci fw_init_cmdline(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci err = apply_mips_fdt_fixups(fdt_buf, sizeof(fdt_buf), 17462306a36Sopenharmony_ci fdt, sead3_fdt_fixups); 17562306a36Sopenharmony_ci if (err) 17662306a36Sopenharmony_ci panic("Unable to fixup FDT: %d", err); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return fdt_buf; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic __init unsigned int sead3_measure_hpt_freq(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci void __iomem *status_reg = (void __iomem *)0xbf000410; 18462306a36Sopenharmony_ci unsigned int freq, orig, tick = 0; 18562306a36Sopenharmony_ci unsigned long flags; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci local_irq_save(flags); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci orig = readl(status_reg) & 0x2; /* get original sample */ 19062306a36Sopenharmony_ci /* wait for transition */ 19162306a36Sopenharmony_ci while ((readl(status_reg) & 0x2) == orig) 19262306a36Sopenharmony_ci ; 19362306a36Sopenharmony_ci orig = orig ^ 0x2; /* flip the bit */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci write_c0_count(0); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* wait 1 second (the sampling clock transitions every 10ms) */ 19862306a36Sopenharmony_ci while (tick < 100) { 19962306a36Sopenharmony_ci /* wait for transition */ 20062306a36Sopenharmony_ci while ((readl(status_reg) & 0x2) == orig) 20162306a36Sopenharmony_ci ; 20262306a36Sopenharmony_ci orig = orig ^ 0x2; /* flip the bit */ 20362306a36Sopenharmony_ci tick++; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci freq = read_c0_count(); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci local_irq_restore(flags); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return freq; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciextern char __dtb_sead3_begin[]; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciMIPS_MACHINE(sead3) = { 21662306a36Sopenharmony_ci .fdt = __dtb_sead3_begin, 21762306a36Sopenharmony_ci .detect = sead3_detect, 21862306a36Sopenharmony_ci .fixup_fdt = sead3_fixup_fdt, 21962306a36Sopenharmony_ci .measure_hpt_freq = sead3_measure_hpt_freq, 22062306a36Sopenharmony_ci}; 221