xref: /openbmc/linux/arch/mips/generic/board-sead3.c (revision 571b7e69f7f775c531ffaf73ae476b1e46150f41)
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