1 /* 2 * Copyright (C) 2016 Imagination Technologies 3 * Author: Paul Burton <paul.burton@imgtec.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 */ 10 11 #define pr_fmt(fmt) "sead3: " fmt 12 13 #include <linux/errno.h> 14 #include <linux/libfdt.h> 15 #include <linux/printk.h> 16 17 #include <asm/fw/fw.h> 18 #include <asm/io.h> 19 #include <asm/machine.h> 20 #include <asm/yamon-dt.h> 21 22 #define SEAD_CONFIG CKSEG1ADDR(0x1b100110) 23 #define SEAD_CONFIG_GIC_PRESENT BIT(1) 24 25 #define MIPS_REVISION CKSEG1ADDR(0x1fc00010) 26 #define MIPS_REVISION_MACHINE (0xf << 4) 27 #define MIPS_REVISION_MACHINE_SEAD3 (0x4 << 4) 28 29 static __init bool sead3_detect(void) 30 { 31 uint32_t rev; 32 33 rev = __raw_readl((void *)MIPS_REVISION); 34 return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3; 35 } 36 37 static __init int remove_gic(void *fdt) 38 { 39 const unsigned int cpu_ehci_int = 2; 40 const unsigned int cpu_uart_int = 4; 41 const unsigned int cpu_eth_int = 6; 42 int gic_off, cpu_off, uart_off, eth_off, ehci_off, err; 43 uint32_t cfg, cpu_phandle; 44 45 /* leave the GIC node intact if a GIC is present */ 46 cfg = __raw_readl((uint32_t *)SEAD_CONFIG); 47 if (cfg & SEAD_CONFIG_GIC_PRESENT) 48 return 0; 49 50 gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic"); 51 if (gic_off < 0) { 52 pr_err("unable to find DT GIC node: %d\n", gic_off); 53 return gic_off; 54 } 55 56 err = fdt_nop_node(fdt, gic_off); 57 if (err) { 58 pr_err("unable to nop GIC node\n"); 59 return err; 60 } 61 62 cpu_off = fdt_node_offset_by_compatible(fdt, -1, 63 "mti,cpu-interrupt-controller"); 64 if (cpu_off < 0) { 65 pr_err("unable to find CPU intc node: %d\n", cpu_off); 66 return cpu_off; 67 } 68 69 cpu_phandle = fdt_get_phandle(fdt, cpu_off); 70 if (!cpu_phandle) { 71 pr_err("unable to get CPU intc phandle\n"); 72 return -EINVAL; 73 } 74 75 err = fdt_setprop_u32(fdt, 0, "interrupt-parent", cpu_phandle); 76 if (err) { 77 pr_err("unable to set root interrupt-parent: %d\n", err); 78 return err; 79 } 80 81 uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a"); 82 while (uart_off >= 0) { 83 err = fdt_setprop_u32(fdt, uart_off, "interrupts", 84 cpu_uart_int); 85 if (err) { 86 pr_err("unable to set UART interrupts property: %d\n", 87 err); 88 return err; 89 } 90 91 uart_off = fdt_node_offset_by_compatible(fdt, uart_off, 92 "ns16550a"); 93 } 94 if (uart_off != -FDT_ERR_NOTFOUND) { 95 pr_err("error searching for UART DT node: %d\n", uart_off); 96 return uart_off; 97 } 98 99 eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115"); 100 if (eth_off < 0) { 101 pr_err("unable to find ethernet DT node: %d\n", eth_off); 102 return eth_off; 103 } 104 105 err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int); 106 if (err) { 107 pr_err("unable to set ethernet interrupts property: %d\n", err); 108 return err; 109 } 110 111 ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci"); 112 if (ehci_off < 0) { 113 pr_err("unable to find EHCI DT node: %d\n", ehci_off); 114 return ehci_off; 115 } 116 117 err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int); 118 if (err) { 119 pr_err("unable to set EHCI interrupts property: %d\n", err); 120 return err; 121 } 122 123 return 0; 124 } 125 126 static __init const void *sead3_fixup_fdt(const void *fdt, 127 const void *match_data) 128 { 129 static unsigned char fdt_buf[16 << 10] __initdata; 130 int err; 131 132 if (fdt_check_header(fdt)) 133 panic("Corrupt DT"); 134 135 /* if this isn't SEAD3, something went wrong */ 136 BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3")); 137 138 fw_init_cmdline(); 139 140 err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf)); 141 if (err) 142 panic("Unable to open FDT: %d", err); 143 144 err = yamon_dt_append_cmdline(fdt_buf); 145 if (err) 146 panic("Unable to patch FDT: %d", err); 147 148 err = yamon_dt_append_memory(fdt_buf); 149 if (err) 150 panic("Unable to patch FDT: %d", err); 151 152 err = remove_gic(fdt_buf); 153 if (err) 154 panic("Unable to patch FDT: %d", err); 155 156 err = yamon_dt_serial_config(fdt_buf); 157 if (err) 158 panic("Unable to patch FDT: %d", err); 159 160 err = fdt_pack(fdt_buf); 161 if (err) 162 panic("Unable to pack FDT: %d\n", err); 163 164 return fdt_buf; 165 } 166 167 static __init unsigned int sead3_measure_hpt_freq(void) 168 { 169 void __iomem *status_reg = (void __iomem *)0xbf000410; 170 unsigned int freq, orig, tick = 0; 171 unsigned long flags; 172 173 local_irq_save(flags); 174 175 orig = readl(status_reg) & 0x2; /* get original sample */ 176 /* wait for transition */ 177 while ((readl(status_reg) & 0x2) == orig) 178 ; 179 orig = orig ^ 0x2; /* flip the bit */ 180 181 write_c0_count(0); 182 183 /* wait 1 second (the sampling clock transitions every 10ms) */ 184 while (tick < 100) { 185 /* wait for transition */ 186 while ((readl(status_reg) & 0x2) == orig) 187 ; 188 orig = orig ^ 0x2; /* flip the bit */ 189 tick++; 190 } 191 192 freq = read_c0_count(); 193 194 local_irq_restore(flags); 195 196 return freq; 197 } 198 199 extern char __dtb_sead3_begin[]; 200 201 MIPS_MACHINE(sead3) = { 202 .fdt = __dtb_sead3_begin, 203 .detect = sead3_detect, 204 .fixup_fdt = sead3_fixup_fdt, 205 .measure_hpt_freq = sead3_measure_hpt_freq, 206 }; 207