1168404Spjd// SPDX-License-Identifier: GPL-2.0-or-later 2168404Spjd/* 3168404Spjd * Copyright (C) 2016 Imagination Technologies 4168404Spjd * Author: Paul Burton <paul.burton@mips.com> 5168404Spjd */ 6168404Spjd 7168404Spjd#define pr_fmt(fmt) "sead3: " fmt 8168404Spjd 9168404Spjd#include <linux/errno.h> 10168404Spjd#include <linux/libfdt.h> 11168404Spjd#include <linux/printk.h> 12168404Spjd#include <linux/sizes.h> 13168404Spjd 14168404Spjd#include <asm/fw/fw.h> 15168404Spjd#include <asm/io.h> 16168404Spjd#include <asm/machine.h> 17168404Spjd#include <asm/yamon-dt.h> 18168404Spjd 19168404Spjd#define SEAD_CONFIG CKSEG1ADDR(0x1b100110) 20168404Spjd#define SEAD_CONFIG_GIC_PRESENT BIT(1) 21168404Spjd 22219089Spjd#define MIPS_REVISION CKSEG1ADDR(0x1fc00010) 23249195Smm#define MIPS_REVISION_MACHINE (0xf << 4) 24254012Sdelphij#define MIPS_REVISION_MACHINE_SEAD3 (0x4 << 4) 25255750Sdelphij 26168404Spjd/* 27168404Spjd * Maximum 384MB RAM at physical address 0, preceding any I/O. 28168404Spjd */ 29219089Spjdstatic struct yamon_mem_region mem_regions[] __initdata = { 30185029Spjd /* start size */ 31168404Spjd { 0, SZ_256M + SZ_128M }, 32168404Spjd {} 33168404Spjd}; 34168404Spjd 35168404Spjdstatic __init bool sead3_detect(void) 36236155Smm{ 37185029Spjd uint32_t rev; 38168404Spjd 39168404Spjd rev = __raw_readl((void *)MIPS_REVISION); 40168404Spjd return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3; 41168404Spjd} 42168404Spjd 43168404Spjdstatic __init int append_memory(void *fdt) 44168404Spjd{ 45168404Spjd return yamon_dt_append_memory(fdt, mem_regions); 46168404Spjd} 47168404Spjd 48168404Spjdstatic __init int remove_gic(void *fdt) 49168404Spjd{ 50219089Spjd const unsigned int cpu_ehci_int = 2; 51219089Spjd const unsigned int cpu_uart_int = 4; 52219089Spjd const unsigned int cpu_eth_int = 6; 53219089Spjd int gic_off, cpu_off, uart_off, eth_off, ehci_off, err; 54219089Spjd uint32_t cfg, cpu_phandle; 55219089Spjd 56219089Spjd /* leave the GIC node intact if a GIC is present */ 57219089Spjd cfg = __raw_readl((uint32_t *)SEAD_CONFIG); 58219089Spjd if (cfg & SEAD_CONFIG_GIC_PRESENT) 59219089Spjd return 0; 60219089Spjd 61219089Spjd gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic"); 62219089Spjd if (gic_off < 0) { 63219089Spjd pr_err("unable to find DT GIC node: %d\n", gic_off); 64219089Spjd return gic_off; 65219089Spjd } 66219089Spjd 67219089Spjd err = fdt_nop_node(fdt, gic_off); 68219089Spjd if (err) { 69219089Spjd pr_err("unable to nop GIC node\n"); 70219089Spjd return err; 71219089Spjd } 72219089Spjd 73219089Spjd cpu_off = fdt_node_offset_by_compatible(fdt, -1, 74219089Spjd "mti,cpu-interrupt-controller"); 75219089Spjd if (cpu_off < 0) { 76219089Spjd pr_err("unable to find CPU intc node: %d\n", cpu_off); 77219089Spjd return cpu_off; 78219089Spjd } 79219089Spjd 80219089Spjd cpu_phandle = fdt_get_phandle(fdt, cpu_off); 81219089Spjd if (!cpu_phandle) { 82219089Spjd pr_err("unable to get CPU intc phandle\n"); 83219089Spjd return -EINVAL; 84219089Spjd } 85219089Spjd 86219089Spjd uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a"); 87219089Spjd while (uart_off >= 0) { 88219089Spjd err = fdt_setprop_u32(fdt, uart_off, "interrupt-parent", 89219089Spjd cpu_phandle); 90219089Spjd if (err) { 91219089Spjd pr_warn("unable to set UART interrupt-parent: %d\n", 92219089Spjd err); 93219089Spjd return err; 94219089Spjd } 95219089Spjd 96219089Spjd err = fdt_setprop_u32(fdt, uart_off, "interrupts", 97219089Spjd cpu_uart_int); 98219089Spjd if (err) { 99219089Spjd pr_err("unable to set UART interrupts property: %d\n", 100219089Spjd err); 101219089Spjd return err; 102219089Spjd } 103219089Spjd 104219089Spjd uart_off = fdt_node_offset_by_compatible(fdt, uart_off, 105219089Spjd "ns16550a"); 106219089Spjd } 107219089Spjd if (uart_off != -FDT_ERR_NOTFOUND) { 108219089Spjd pr_err("error searching for UART DT node: %d\n", uart_off); 109236155Smm return uart_off; 110236155Smm } 111236155Smm 112236155Smm eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115"); 113236155Smm if (eth_off < 0) { 114236155Smm pr_err("unable to find ethernet DT node: %d\n", eth_off); 115236155Smm return eth_off; 116236155Smm } 117236155Smm 118236155Smm err = fdt_setprop_u32(fdt, eth_off, "interrupt-parent", cpu_phandle); 119236155Smm if (err) { 120236155Smm pr_err("unable to set ethernet interrupt-parent: %d\n", err); 121236155Smm return err; 122236155Smm } 123236155Smm 124236155Smm err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int); 125236155Smm if (err) { 126236155Smm pr_err("unable to set ethernet interrupts property: %d\n", err); 127236155Smm return err; 128236155Smm } 129236155Smm 130236155Smm ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci"); 131236155Smm if (ehci_off < 0) { 132236155Smm pr_err("unable to find EHCI DT node: %d\n", ehci_off); 133236155Smm return ehci_off; 134236155Smm } 135236155Smm 136236155Smm err = fdt_setprop_u32(fdt, ehci_off, "interrupt-parent", cpu_phandle); 137236155Smm if (err) { 138236155Smm pr_err("unable to set EHCI interrupt-parent: %d\n", err); 139254012Sdelphij return err; 140254012Sdelphij } 141254012Sdelphij 142254012Sdelphij err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int); 143254012Sdelphij if (err) { 144254012Sdelphij pr_err("unable to set EHCI interrupts property: %d\n", err); 145254012Sdelphij return err; 146254012Sdelphij } 147254012Sdelphij 148254012Sdelphij return 0; 149168404Spjd} 150236155Smm 151236155Smmstatic const struct mips_fdt_fixup sead3_fdt_fixups[] __initconst = { 152168404Spjd { yamon_dt_append_cmdline, "append command line" }, 153209962Smm { append_memory, "append memory" }, 154168404Spjd { remove_gic, "remove GIC when not present" }, 155254012Sdelphij { yamon_dt_serial_config, "append serial configuration" }, 156254012Sdelphij { }, 157254012Sdelphij}; 158254012Sdelphij 159254012Sdelphijstatic __init const void *sead3_fixup_fdt(const void *fdt, 160254012Sdelphij const void *match_data) 161168404Spjd{ 162185029Spjd static unsigned char fdt_buf[16 << 10] __initdata; 163185029Spjd int err; 164249209Smm 165249209Smm if (fdt_check_header(fdt)) 166254012Sdelphij panic("Corrupt DT"); 167168404Spjd 168168404Spjd /* if this isn't SEAD3, something went wrong */ 169168404Spjd BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3")); 170168404Spjd 171168404Spjd fw_init_cmdline(); 172168404Spjd 173249195Smm err = apply_mips_fdt_fixups(fdt_buf, sizeof(fdt_buf), 174168404Spjd fdt, sead3_fdt_fixups); 175168404Spjd if (err) 176219089Spjd panic("Unable to fixup FDT: %d", err); 177219089Spjd 178219089Spjd return fdt_buf; 179219089Spjd} 180219089Spjd 181219089Spjdstatic __init unsigned int sead3_measure_hpt_freq(void) 182219089Spjd{ 183219089Spjd void __iomem *status_reg = (void __iomem *)0xbf000410; 184219089Spjd unsigned int freq, orig, tick = 0; 185219089Spjd unsigned long flags; 186168404Spjd 187168404Spjd local_irq_save(flags); 188168404Spjd 189168404Spjd orig = readl(status_reg) & 0x2; /* get original sample */ 190168404Spjd /* wait for transition */ 191168404Spjd while ((readl(status_reg) & 0x2) == orig) 192168404Spjd ; 193168404Spjd orig = orig ^ 0x2; /* flip the bit */ 194168404Spjd 195168404Spjd write_c0_count(0); 196168404Spjd 197168404Spjd /* wait 1 second (the sampling clock transitions every 10ms) */ 198168404Spjd while (tick < 100) { 199168404Spjd /* wait for transition */ 200168404Spjd while ((readl(status_reg) & 0x2) == orig) 201168404Spjd ; 202168404Spjd orig = orig ^ 0x2; /* flip the bit */ 203168404Spjd tick++; 204168404Spjd } 205168404Spjd 206168404Spjd freq = read_c0_count(); 207168404Spjd 208249195Smm local_irq_restore(flags); 209168404Spjd 210168404Spjd return freq; 211168404Spjd} 212168404Spjd 213168404Spjdextern char __dtb_sead3_begin[]; 214219089Spjd 215168404SpjdMIPS_MACHINE(sead3) = { 216168404Spjd .fdt = __dtb_sead3_begin, 217168404Spjd .detect = sead3_detect, 218168404Spjd .fixup_fdt = sead3_fixup_fdt, 219168404Spjd .measure_hpt_freq = sead3_measure_hpt_freq, 220168404Spjd}; 221168404Spjd