mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
5.6 KiB
232 lines
5.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Copyright (C) 2016 Imagination Technologies |
|
* Author: Paul Burton <[email protected]> |
|
*/ |
|
|
|
#define pr_fmt(fmt) "yamon-dt: " fmt |
|
|
|
#include <linux/bug.h> |
|
#include <linux/errno.h> |
|
#include <linux/kernel.h> |
|
#include <linux/libfdt.h> |
|
#include <linux/printk.h> |
|
|
|
#include <asm/fw/fw.h> |
|
#include <asm/yamon-dt.h> |
|
|
|
#define MAX_MEM_ARRAY_ENTRIES 2 |
|
|
|
__init int yamon_dt_append_cmdline(void *fdt) |
|
{ |
|
int err, chosen_off; |
|
|
|
/* find or add chosen node */ |
|
chosen_off = fdt_path_offset(fdt, "/chosen"); |
|
if (chosen_off == -FDT_ERR_NOTFOUND) |
|
chosen_off = fdt_add_subnode(fdt, 0, "chosen"); |
|
if (chosen_off < 0) { |
|
pr_err("Unable to find or add DT chosen node: %d\n", |
|
chosen_off); |
|
return chosen_off; |
|
} |
|
|
|
err = fdt_setprop_string(fdt, chosen_off, "bootargs", fw_getcmdline()); |
|
if (err) { |
|
pr_err("Unable to set bootargs property: %d\n", err); |
|
return err; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static unsigned int __init gen_fdt_mem_array( |
|
const struct yamon_mem_region *regions, |
|
__be32 *mem_array, |
|
unsigned int max_entries, |
|
unsigned long memsize) |
|
{ |
|
const struct yamon_mem_region *mr; |
|
unsigned long size; |
|
unsigned int entries = 0; |
|
|
|
for (mr = regions; mr->size && memsize; ++mr) { |
|
if (entries >= max_entries) { |
|
pr_warn("Number of regions exceeds max %u\n", |
|
max_entries); |
|
break; |
|
} |
|
|
|
/* How much of the remaining RAM fits in the next region? */ |
|
size = min_t(unsigned long, memsize, mr->size); |
|
memsize -= size; |
|
|
|
/* Emit a memory region */ |
|
*(mem_array++) = cpu_to_be32(mr->start); |
|
*(mem_array++) = cpu_to_be32(size); |
|
++entries; |
|
|
|
/* Discard the next mr->discard bytes */ |
|
memsize -= min_t(unsigned long, memsize, mr->discard); |
|
} |
|
return entries; |
|
} |
|
|
|
__init int yamon_dt_append_memory(void *fdt, |
|
const struct yamon_mem_region *regions) |
|
{ |
|
unsigned long phys_memsize, memsize; |
|
__be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; |
|
unsigned int mem_entries; |
|
int i, err, mem_off; |
|
char *var, param_name[10], *var_names[] = { |
|
"ememsize", "memsize", |
|
}; |
|
|
|
/* find memory size from the bootloader environment */ |
|
for (i = 0; i < ARRAY_SIZE(var_names); i++) { |
|
var = fw_getenv(var_names[i]); |
|
if (!var) |
|
continue; |
|
|
|
err = kstrtoul(var, 0, &phys_memsize); |
|
if (!err) |
|
break; |
|
|
|
pr_warn("Failed to read the '%s' env variable '%s'\n", |
|
var_names[i], var); |
|
} |
|
|
|
if (!phys_memsize) { |
|
pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); |
|
phys_memsize = 32 << 20; |
|
} |
|
|
|
/* default to using all available RAM */ |
|
memsize = phys_memsize; |
|
|
|
/* allow the user to override the usable memory */ |
|
for (i = 0; i < ARRAY_SIZE(var_names); i++) { |
|
snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); |
|
var = strstr(arcs_cmdline, param_name); |
|
if (!var) |
|
continue; |
|
|
|
memsize = memparse(var + strlen(param_name), NULL); |
|
} |
|
|
|
/* if the user says there's more RAM than we thought, believe them */ |
|
phys_memsize = max_t(unsigned long, phys_memsize, memsize); |
|
|
|
/* find or add a memory node */ |
|
mem_off = fdt_path_offset(fdt, "/memory"); |
|
if (mem_off == -FDT_ERR_NOTFOUND) |
|
mem_off = fdt_add_subnode(fdt, 0, "memory"); |
|
if (mem_off < 0) { |
|
pr_err("Unable to find or add memory DT node: %d\n", mem_off); |
|
return mem_off; |
|
} |
|
|
|
err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); |
|
if (err) { |
|
pr_err("Unable to set memory node device_type: %d\n", err); |
|
return err; |
|
} |
|
|
|
mem_entries = gen_fdt_mem_array(regions, mem_array, |
|
MAX_MEM_ARRAY_ENTRIES, phys_memsize); |
|
err = fdt_setprop(fdt, mem_off, "reg", |
|
mem_array, mem_entries * 2 * sizeof(mem_array[0])); |
|
if (err) { |
|
pr_err("Unable to set memory regs property: %d\n", err); |
|
return err; |
|
} |
|
|
|
mem_entries = gen_fdt_mem_array(regions, mem_array, |
|
MAX_MEM_ARRAY_ENTRIES, memsize); |
|
err = fdt_setprop(fdt, mem_off, "linux,usable-memory", |
|
mem_array, mem_entries * 2 * sizeof(mem_array[0])); |
|
if (err) { |
|
pr_err("Unable to set linux,usable-memory property: %d\n", err); |
|
return err; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
__init int yamon_dt_serial_config(void *fdt) |
|
{ |
|
const char *yamontty, *mode_var; |
|
char mode_var_name[9], path[20], parity; |
|
unsigned int uart, baud, stop_bits; |
|
bool hw_flow; |
|
int chosen_off, err; |
|
|
|
yamontty = fw_getenv("yamontty"); |
|
if (!yamontty || !strcmp(yamontty, "tty0")) { |
|
uart = 0; |
|
} else if (!strcmp(yamontty, "tty1")) { |
|
uart = 1; |
|
} else { |
|
pr_warn("yamontty environment variable '%s' invalid\n", |
|
yamontty); |
|
uart = 0; |
|
} |
|
|
|
baud = stop_bits = 0; |
|
parity = 0; |
|
hw_flow = false; |
|
|
|
snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart); |
|
mode_var = fw_getenv(mode_var_name); |
|
if (mode_var) { |
|
while (mode_var[0] >= '0' && mode_var[0] <= '9') { |
|
baud *= 10; |
|
baud += mode_var[0] - '0'; |
|
mode_var++; |
|
} |
|
if (mode_var[0] == ',') |
|
mode_var++; |
|
if (mode_var[0]) |
|
parity = mode_var[0]; |
|
if (mode_var[0] == ',') |
|
mode_var++; |
|
if (mode_var[0]) |
|
stop_bits = mode_var[0] - '0'; |
|
if (mode_var[0] == ',') |
|
mode_var++; |
|
if (!strcmp(mode_var, "hw")) |
|
hw_flow = true; |
|
} |
|
|
|
if (!baud) |
|
baud = 38400; |
|
|
|
if (parity != 'e' && parity != 'n' && parity != 'o') |
|
parity = 'n'; |
|
|
|
if (stop_bits != 7 && stop_bits != 8) |
|
stop_bits = 8; |
|
|
|
WARN_ON(snprintf(path, sizeof(path), "serial%u:%u%c%u%s", |
|
uart, baud, parity, stop_bits, |
|
hw_flow ? "r" : "") >= sizeof(path)); |
|
|
|
/* find or add chosen node */ |
|
chosen_off = fdt_path_offset(fdt, "/chosen"); |
|
if (chosen_off == -FDT_ERR_NOTFOUND) |
|
chosen_off = fdt_add_subnode(fdt, 0, "chosen"); |
|
if (chosen_off < 0) { |
|
pr_err("Unable to find or add DT chosen node: %d\n", |
|
chosen_off); |
|
return chosen_off; |
|
} |
|
|
|
err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path); |
|
if (err) { |
|
pr_err("Unable to set stdout-path property: %d\n", err); |
|
return err; |
|
} |
|
|
|
return 0; |
|
}
|
|
|