blob: d6b0708d7a6f437a3e5b2fec0b38a1b484fd59f6 [file] [log] [blame]
Paul Burtonb6d5e472016-08-26 15:17:34 +01001/*
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-dtshim: " fmt
12
13#include <linux/errno.h>
14#include <linux/libfdt.h>
15#include <linux/printk.h>
16
Paul Burtonc11e3b42016-08-26 15:17:35 +010017#include <asm/fw/fw.h>
Paul Burtonb6d5e472016-08-26 15:17:34 +010018#include <asm/io.h>
19
20#define SEAD_CONFIG CKSEG1ADDR(0x1b100110)
21#define SEAD_CONFIG_GIC_PRESENT BIT(1)
22
23static unsigned char fdt_buf[16 << 10] __initdata;
24
Paul Burtonfd4d7402016-08-26 15:17:45 +010025static int append_memory(void *fdt)
26{
27 unsigned long phys_memsize, memsize;
28 __be32 mem_array[2];
29 int err, mem_off;
30 char *var;
31
32 /* find memory size from the bootloader environment */
33 var = fw_getenv("memsize");
34 if (var) {
35 err = kstrtoul(var, 0, &phys_memsize);
36 if (err) {
37 pr_err("Failed to read memsize env variable '%s'\n",
38 var);
39 return -EINVAL;
40 }
41 } else {
42 pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n");
43 phys_memsize = 32 << 20;
44 }
45
46 /* default to using all available RAM */
47 memsize = phys_memsize;
48
49 /* allow the user to override the usable memory */
50 var = strstr(arcs_cmdline, "memsize=");
51 if (var)
52 memsize = memparse(var + strlen("memsize="), NULL);
53
54 /* if the user says there's more RAM than we thought, believe them */
55 phys_memsize = max_t(unsigned long, phys_memsize, memsize);
56
57 /* find or add a memory node */
58 mem_off = fdt_path_offset(fdt, "/memory");
59 if (mem_off == -FDT_ERR_NOTFOUND)
60 mem_off = fdt_add_subnode(fdt, 0, "memory");
61 if (mem_off < 0) {
62 pr_err("Unable to find or add memory DT node: %d\n", mem_off);
63 return mem_off;
64 }
65
66 err = fdt_setprop_string(fdt, mem_off, "device_type", "memory");
67 if (err) {
68 pr_err("Unable to set memory node device_type: %d\n", err);
69 return err;
70 }
71
72 mem_array[0] = 0;
73 mem_array[1] = cpu_to_be32(phys_memsize);
74 err = fdt_setprop(fdt, mem_off, "reg", mem_array, sizeof(mem_array));
75 if (err) {
76 pr_err("Unable to set memory regs property: %d\n", err);
77 return err;
78 }
79
80 mem_array[0] = 0;
81 mem_array[1] = cpu_to_be32(memsize);
82 err = fdt_setprop(fdt, mem_off, "linux,usable-memory",
83 mem_array, sizeof(mem_array));
84 if (err) {
85 pr_err("Unable to set linux,usable-memory property: %d\n", err);
86 return err;
87 }
88
89 return 0;
90}
91
Paul Burtonb6d5e472016-08-26 15:17:34 +010092static int remove_gic(void *fdt)
93{
Paul Burton7afd2a52016-08-26 15:17:38 +010094 const unsigned int cpu_ehci_int = 2;
Paul Burtonc11e3b42016-08-26 15:17:35 +010095 const unsigned int cpu_uart_int = 4;
Paul Burtona34e9382016-08-26 15:17:37 +010096 const unsigned int cpu_eth_int = 6;
Paul Burton7afd2a52016-08-26 15:17:38 +010097 int gic_off, cpu_off, uart_off, eth_off, ehci_off, err;
Paul Burtonb6d5e472016-08-26 15:17:34 +010098 uint32_t cfg, cpu_phandle;
99
100 /* leave the GIC node intact if a GIC is present */
101 cfg = __raw_readl((uint32_t *)SEAD_CONFIG);
102 if (cfg & SEAD_CONFIG_GIC_PRESENT)
103 return 0;
104
105 gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
106 if (gic_off < 0) {
107 pr_err("unable to find DT GIC node: %d\n", gic_off);
108 return gic_off;
109 }
110
111 err = fdt_nop_node(fdt, gic_off);
112 if (err) {
113 pr_err("unable to nop GIC node\n");
114 return err;
115 }
116
117 cpu_off = fdt_node_offset_by_compatible(fdt, -1,
118 "mti,cpu-interrupt-controller");
119 if (cpu_off < 0) {
120 pr_err("unable to find CPU intc node: %d\n", cpu_off);
121 return cpu_off;
122 }
123
124 cpu_phandle = fdt_get_phandle(fdt, cpu_off);
125 if (!cpu_phandle) {
126 pr_err("unable to get CPU intc phandle\n");
127 return -EINVAL;
128 }
129
130 err = fdt_setprop_u32(fdt, 0, "interrupt-parent", cpu_phandle);
131 if (err) {
132 pr_err("unable to set root interrupt-parent: %d\n", err);
133 return err;
134 }
135
Paul Burtonc11e3b42016-08-26 15:17:35 +0100136 uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a");
137 while (uart_off >= 0) {
138 err = fdt_setprop_u32(fdt, uart_off, "interrupts",
139 cpu_uart_int);
140 if (err) {
141 pr_err("unable to set UART interrupts property: %d\n",
142 err);
143 return err;
144 }
145
146 uart_off = fdt_node_offset_by_compatible(fdt, uart_off,
147 "ns16550a");
148 }
149 if (uart_off != -FDT_ERR_NOTFOUND) {
150 pr_err("error searching for UART DT node: %d\n", uart_off);
151 return uart_off;
152 }
153
Paul Burtona34e9382016-08-26 15:17:37 +0100154 eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115");
155 if (eth_off < 0) {
156 pr_err("unable to find ethernet DT node: %d\n", eth_off);
157 return eth_off;
158 }
159
160 err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int);
161 if (err) {
162 pr_err("unable to set ethernet interrupts property: %d\n", err);
163 return err;
164 }
165
Paul Burton7afd2a52016-08-26 15:17:38 +0100166 ehci_off = fdt_node_offset_by_compatible(fdt, -1, "mti,sead3-ehci");
167 if (ehci_off < 0) {
168 pr_err("unable to find EHCI DT node: %d\n", ehci_off);
169 return ehci_off;
170 }
171
172 err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int);
173 if (err) {
174 pr_err("unable to set EHCI interrupts property: %d\n", err);
175 return err;
176 }
177
Paul Burtonc11e3b42016-08-26 15:17:35 +0100178 return 0;
179}
180
181static int serial_config(void *fdt)
182{
183 const char *yamontty, *mode_var;
184 char mode_var_name[9], path[18], parity;
185 unsigned int uart, baud, stop_bits;
186 bool hw_flow;
187 int chosen_off, err;
188
189 yamontty = fw_getenv("yamontty");
190 if (!yamontty || !strcmp(yamontty, "tty0")) {
191 uart = 0;
192 } else if (!strcmp(yamontty, "tty1")) {
193 uart = 1;
194 } else {
195 pr_warn("yamontty environment variable '%s' invalid\n",
196 yamontty);
197 uart = 0;
198 }
199
200 baud = stop_bits = 0;
201 parity = 0;
202 hw_flow = false;
203
204 snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart);
205 mode_var = fw_getenv(mode_var_name);
206 if (mode_var) {
207 while (mode_var[0] >= '0' && mode_var[0] <= '9') {
208 baud *= 10;
209 baud += mode_var[0] - '0';
210 mode_var++;
211 }
212 if (mode_var[0] == ',')
213 mode_var++;
214 if (mode_var[0])
215 parity = mode_var[0];
216 if (mode_var[0] == ',')
217 mode_var++;
218 if (mode_var[0])
219 stop_bits = mode_var[0] - '0';
220 if (mode_var[0] == ',')
221 mode_var++;
222 if (!strcmp(mode_var, "hw"))
223 hw_flow = true;
224 }
225
226 if (!baud)
227 baud = 38400;
228
229 if (parity != 'e' && parity != 'n' && parity != 'o')
230 parity = 'n';
231
232 if (stop_bits != 7 && stop_bits != 8)
233 stop_bits = 8;
234
235 WARN_ON(snprintf(path, sizeof(path), "uart%u:%u%c%u%s",
236 uart, baud, parity, stop_bits,
237 hw_flow ? "r" : "") >= sizeof(path));
238
239 /* find or add chosen node */
240 chosen_off = fdt_path_offset(fdt, "/chosen");
241 if (chosen_off == -FDT_ERR_NOTFOUND)
242 chosen_off = fdt_path_offset(fdt, "/chosen@0");
243 if (chosen_off == -FDT_ERR_NOTFOUND)
244 chosen_off = fdt_add_subnode(fdt, 0, "chosen");
245 if (chosen_off < 0) {
246 pr_err("Unable to find or add DT chosen node: %d\n",
247 chosen_off);
248 return chosen_off;
249 }
250
251 err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path);
252 if (err) {
253 pr_err("Unable to set stdout-path property: %d\n", err);
254 return err;
255 }
256
Paul Burtonb6d5e472016-08-26 15:17:34 +0100257 return 0;
258}
259
260void __init *sead3_dt_shim(void *fdt)
261{
262 int err;
263
264 if (fdt_check_header(fdt))
265 panic("Corrupt DT");
266
267 /* if this isn't SEAD3, leave the DT alone */
268 if (fdt_node_check_compatible(fdt, 0, "mti,sead-3"))
269 return fdt;
270
271 err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf));
272 if (err)
273 panic("Unable to open FDT: %d", err);
274
Paul Burtonfd4d7402016-08-26 15:17:45 +0100275 err = append_memory(fdt_buf);
276 if (err)
277 panic("Unable to patch FDT: %d", err);
278
Paul Burtonb6d5e472016-08-26 15:17:34 +0100279 err = remove_gic(fdt_buf);
280 if (err)
281 panic("Unable to patch FDT: %d", err);
282
Paul Burtonc11e3b42016-08-26 15:17:35 +0100283 err = serial_config(fdt_buf);
284 if (err)
285 panic("Unable to patch FDT: %d", err);
286
Paul Burtonb6d5e472016-08-26 15:17:34 +0100287 err = fdt_pack(fdt_buf);
288 if (err)
289 panic("Unable to pack FDT: %d\n", err);
290
291 return fdt_buf;
292}