loader/u-boot: Use FDT serial info to create uart

* drop my fdt tests
* we have to call fdt parsing code *after* cpu_init (why?)
* pass fdt pointer to all FDT support calls to avoid confusion
  once we get into the kernel land
* look for PL011 compatible uart and use it
* Add some saftey checks to serial putc code to avoid null*
* fdt_node_check_compatible returns 0 on success not 1
* fdt_get_device_reg needs to add the SOC base to the result
* fdt_get_device_reg might need to add the second range cell
  instead of reg?
This commit is contained in:
Alexander von Gluck IV 2015-03-28 15:52:16 -05:00
parent 3d02d66b03
commit c6a4fee579
8 changed files with 126 additions and 66 deletions

View File

@ -0,0 +1,19 @@
/*
* Copyright 2012-2015, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors
* Alexander von Gluck IV, kallisti5@unixzen.com
*/
#ifndef __FDT_SERIAL_H
#define __FDT_SERIAL_H
#include <KernelExport.h>
#include <arch/generic/debug_uart.h>
DebugUART * debug_uart_from_fdt(const void *fdt);
#endif /*__FDT_SERIAL_H*/

View File

@ -13,11 +13,12 @@
void dump_fdt(const void *fdt);
status_t fdt_get_cell_count(int node, int32 &addressCells, int32 &sizeCells);
status_t fdt_get_cell_count(const void* fdt, int node,
int32 &addressCells, int32 &sizeCells);
phys_addr_t fdt_get_device_reg(int node);
phys_addr_t fdt_get_device_reg_byname(const char* name);
phys_addr_t fdt_get_device_reg_byalias(const char* alias);
phys_addr_t fdt_get_device_reg(const void* fdt, int node);
phys_addr_t fdt_get_device_reg_byname(const void* fdt, const char* name);
phys_addr_t fdt_get_device_reg_byalias(const void* fdt, const char* alias);
#endif /*__FDT_SUPPORT_H*/

View File

@ -600,7 +600,7 @@ find_physical_memory_ranges(uint64 &total)
int32 regAddressCells = 1;
int32 regSizeCells = 1;
fdt_get_cell_count(node, regAddressCells, regSizeCells);
fdt_get_cell_count(gFDT, node, regAddressCells, regSizeCells);
prop = fdt_getprop(gFDT, node, "reg", &len);
if (prop == NULL) {
@ -671,10 +671,6 @@ mmu_init(void)
dprintf("total physical memory = %" B_PRId64 "MB\n", total / (1024 * 1024));
}
// XXX: A simple test.
fdt_get_device_reg_byname("/soc/gpio");
fdt_get_device_reg_byalias("gpio");
// see if subpages are disabled
if (mmu_read_C1() & (1 << 23))
sSmallPageType = ARM_MMU_L2_TYPE_SMALLNEW;

View File

@ -2,17 +2,23 @@
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2012, Alexander von Gluck, kallisti5@unixzen.com
* Copyright 2012-2015, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Alexander von Gluck IV, kallisti5@unixzen.com
*/
#include "serial.h"
#include <debug_uart_8250.h>
#include <arch/generic/debug_uart_8250.h>
#if defined(__ARM__)
#include <arch_uart_pl011.h>
#include <arch/arm/arch_uart_pl011.h>
#endif
#include <board_config.h>
#include <boot/platform.h>
#include <arch/cpu.h>
@ -26,8 +32,8 @@ extern "C" {
#include <libfdt_env.h>
};
#include "fdt_serial.h"
extern "C" DebugUART *debug_uart_from_fdt(const void *fdt);
DebugUART* gUART;
@ -39,6 +45,9 @@ static uint32 sBufferPosition;
static void
serial_putc(char c)
{
if (gUART == NULL || sSerialEnabled <= 0)
return;
gUART->PutChar(c);
}
@ -46,6 +55,9 @@ serial_putc(char c)
extern "C" int
serial_getc(bool wait)
{
if (gUART == NULL || sSerialEnabled <= 0)
return 0;
return gUART->GetChar(wait);
}
@ -112,16 +124,16 @@ serial_init(const void *fdt)
// first try with hints from the FDT
gUART = debug_uart_from_fdt(fdt);
#ifdef BOARD_UART_DEBUG
// fallback to hardcoded board UART
// fallback to known board UARTs
#if defined(BOARD_UART_DEBUG) && defined(BOARD_UART_CLOCK)
if (gUART == NULL) {
#if defined(BOARD_UART_PL011)
#ifdef BOARD_UART_PL011
gUART = arch_get_uart_pl011(BOARD_UART_DEBUG, BOARD_UART_CLOCK);
#else
gUART = arch_get_uart_8250(BOARD_UART_DEBUG, BOARD_UART_CLOCK);
#endif
}
#endif
#endif
if (gUART == NULL)
return;

View File

@ -214,16 +214,19 @@ start_gen(int argc, const char **argv, struct image_header *uimage, void *fdt)
gFDT = args.platform.fdt_data;
}
// We have to cpu_init *before* calling FDT functions
cpu_init();
#if defined(__ARM__)
arch_mailbox_init();
#endif
serial_init(gFDT);
console_init();
serial_init(gFDT);
// initialize the OpenFirmware wrapper
of_init(NULL);
cpu_init();
// if we get passed an FDT, check /chosen for initrd and bootargs
if (gFDT != NULL) {
int node = fdt_path_offset(gFDT, "/chosen");

View File

@ -11,9 +11,7 @@
#include <arch/arm/reg.h>
#include <arch/generic/debug_uart.h>
#include <arch/arm/arch_uart_pl011.h>
//#include <board_config.h>
#include <new>
//#include <target/debugconfig.h>
#define PL01x_DR 0x00 // Data read or written

View File

@ -1,17 +1,26 @@
/*
* Copyright 2012, François Revol, revol@free.fr.
* Distributed under the terms of the MIT License.
*
* Authors:
* François Revol, revol@free.fr
* Alexander von Gluck IV, kallisti5@unixzen.com
*/
#include "fdt_serial.h"
#include <KernelExport.h>
#include <ByteOrder.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/cdefs.h>
#include <arch/generic/debug_uart.h>
#include <arch/generic/debug_uart_8250.h>
#ifdef __ARM__
#include <arch/arm/arch_uart_pl011.h>
#endif
extern "C" {
#include <fdt.h>
#include <libfdt.h>
@ -21,14 +30,13 @@ extern "C" {
#include "fdt_support.h"
extern "C" DebugUART *debug_uart_from_fdt(const void *fdt);
// If we dprintf before the UART is initalized there will be no output
DebugUART *
DebugUART*
debug_uart_from_fdt(const void *fdt)
{
const char *name;
//const char *type;
int node;
int len;
phys_addr_t regs;
@ -52,36 +60,45 @@ debug_uart_from_fdt(const void *fdt)
return NULL;
node = fdt_path_offset(fdt, name);
//dprintf("serial: using '%s', node %d\n", name, node);
if (node < 0)
return NULL;
// determine the MMIO address
regs = fdt_get_device_reg(node);
regs = fdt_get_device_reg(fdt, node);
if (regs == 0)
return NULL;
dprintf("serial: using '%s', node %d @ %" B_PRIxPHYSADDR "\n",
name, node, regs);
// get the UART clock rate
prop = fdt_getprop(fdt, node, "clock-frequency", &len);
if (prop && len == 4) {
clock = fdt32_to_cpu(*(uint32_t *)prop);
//dprintf("serial: clock %ld\n", clock);
dprintf("serial: clock %ld\n", clock);
}
// get current speed (XXX: not yet passed over)
prop = fdt_getprop(fdt, node, "current-speed", &len);
if (prop && len == 4) {
speed = fdt32_to_cpu(*(uint32_t *)prop);
//dprintf("serial: speed %ld\n", speed);
dprintf("serial: speed %ld\n", speed);
}
if (fdt_node_check_compatible(fdt, node, "ns16550a") == 1
|| fdt_node_check_compatible(fdt, node, "ns16550") == 1) {
// fdt_node_check_compatible returns 0 on match.
if (fdt_node_check_compatible(fdt, node, "ns16550a") == 0
|| fdt_node_check_compatible(fdt, node, "ns16550") == 0) {
dprintf("serial: Found 8250 serial UART!\n");
uart = arch_get_uart_8250(regs, clock);
//dprintf("serial: using 8250\n");
// XXX:assume speed is already set
(void)speed;
#ifdef __ARM__
} else if (fdt_node_check_compatible(fdt, node, "arm,pl011") == 0
|| fdt_node_check_compatible(fdt, node, "arm,primecell") == 0) {
dprintf("serial: Found pl011 serial UART!\n");
uart = arch_get_uart_pl011(regs, clock);
#endif
} else {
// TODO: handle more UART types
return NULL;
@ -89,4 +106,3 @@ debug_uart_from_fdt(const void *fdt)
return uart;
}

View File

@ -133,7 +133,7 @@ void dump_fdt(const void *fdt)
static uint64
fdt_get_range_offset(int32 node)
fdt_get_range_offset(const void* fdt, int32 node)
{
// Obtain the offset of the device by searching
// for the first ranges start in parents.
@ -141,18 +141,18 @@ fdt_get_range_offset(int32 node)
// It could be possible that there are multiple
// offset ranges in several parents + children.
// Lets hope that no system designer is that insane.
int depth = fdt_node_depth(gFDT, node);
int depth = fdt_node_depth(fdt, node);
int32 examineNode = node;
uint64 pathOffset = 0x0;
while (depth > 0) {
int len;
const void* prop;
prop = fdt_getprop(gFDT, examineNode, "ranges", &len);
prop = fdt_getprop(fdt, examineNode, "ranges", &len);
if (prop) {
int32 regAddressCells = 1;
int32 regSizeCells = 1;
fdt_get_cell_count(examineNode, regAddressCells, regSizeCells);
fdt_get_cell_count(fdt, examineNode, regAddressCells, regSizeCells);
const uint32 *p = (const uint32 *)prop;
// All we are interested in is the start offset
@ -162,19 +162,20 @@ fdt_get_range_offset(int32 node)
pathOffset = fdt32_to_cpu(*(uint32_t *)p);
break;
}
int32 parentNode = fdt_parent_offset(gFDT, examineNode);
depth = fdt_node_depth(gFDT, parentNode);
int32 parentNode = fdt_parent_offset(fdt, examineNode);
depth = fdt_node_depth(fdt, parentNode);
examineNode = parentNode;
}
dprintf("%s: range offset: 0x%" B_PRIx64 "\n", __func__, pathOffset);
TRACE("%s: range offset: 0x%" B_PRIx64 "\n", __func__, pathOffset);
return pathOffset;
}
status_t
fdt_get_cell_count(int node, int32 &addressCells, int32 &sizeCells)
fdt_get_cell_count(const void* fdt, int node,
int32 &addressCells, int32 &sizeCells)
{
// It would be nice if libfdt provided this.
@ -182,7 +183,7 @@ fdt_get_cell_count(int node, int32 &addressCells, int32 &sizeCells)
// #address-cells and #size-cells matches the number of 32-bit 'cells'
// representing the length of the base address and size fields
// TODO: assert !gFDT || !pathOffset?
// TODO: assert !fdt || !pathOffset?
int len;
if (node < 0) {
@ -191,10 +192,10 @@ fdt_get_cell_count(int node, int32 &addressCells, int32 &sizeCells)
}
const void *prop;
prop = fdt_getprop(gFDT, node, "#address-cells", &len);
prop = fdt_getprop(fdt, node, "#address-cells", &len);
if (prop && len == sizeof(uint32))
addressCells = fdt32_to_cpu(*(uint32_t *)prop);
prop = fdt_getprop(gFDT, node, "#size-cells", &len);
prop = fdt_getprop(fdt, node, "#size-cells", &len);
if (prop && len == sizeof(uint32))
sizeCells = fdt32_to_cpu(*(uint32_t *)prop);
@ -211,18 +212,18 @@ fdt_get_cell_count(int node, int32 &addressCells, int32 &sizeCells)
phys_addr_t
fdt_get_device_reg(int node)
fdt_get_device_reg(const void* fdt, int node)
{
const void *prop;
int len;
int32 regAddressCells = 1;
int32 regSizeCells = 1;
fdt_get_cell_count(node, regAddressCells, regSizeCells);
fdt_get_cell_count(fdt, node, regAddressCells, regSizeCells);
// TODO: check for virtual-reg, and don't -= fdt_get_range_offset?
prop = fdt_getprop(gFDT, node, "reg", &len);
prop = fdt_getprop(fdt, node, "reg", &len);
if (!prop) {
dprintf("%s: reg property not found on node in FDT!\n", __func__);
@ -231,41 +232,55 @@ fdt_get_device_reg(int node)
const uint32 *p = (const uint32 *)prop;
uint64 baseDevice = 0x0;
uint64 size = 0x0;
// soc base address cells
if (regAddressCells == 2)
baseDevice = fdt64_to_cpu(*(uint64_t *)p);
else
baseDevice = fdt32_to_cpu(*(uint32_t *)p);
p += regAddressCells;
//p += regAddressCells;
// size (atm, we don't pass this back :-\)
if (regSizeCells == 2)
size = fdt64_to_cpu(*(uint64_t *)p);
else if (regSizeCells == 1)
size = fdt32_to_cpu(*(uint32_t *)p);
// we can have 0 size cells
//p += regSizeCells;
// subtract the range offset (X) on the parent node (ranges = X Y Z)
baseDevice -= fdt_get_range_offset(fdt, node);
baseDevice -= fdt_get_range_offset(node);
// find the start of the parent (X) and add to base (regs = X Y)
int parentNode = fdt_parent_offset(fdt, node);
if (!parentNode)
return baseDevice;
fdt_get_cell_count(fdt, parentNode, regAddressCells, regSizeCells);
prop = fdt_getprop(fdt, parentNode, "reg", &len);
if (!prop)
return baseDevice;
p = (const uint32 *)prop;
uint64 parentReg = 0x0;
// soc base address cells
if (regAddressCells == 2)
parentReg = fdt64_to_cpu(*(uint64_t *)p);
else
parentReg = fdt32_to_cpu(*(uint32_t *)p);
// add parent reg base to property
baseDevice += parentReg;
return baseDevice;
}
phys_addr_t
fdt_get_device_reg_byname(const char* name)
fdt_get_device_reg_byname(const void* fdt, const char* name)
{
// Find device in FDT
int node = fdt_path_offset(gFDT, name);
int node = fdt_path_offset(fdt, name);
if (node < 0) {
dprintf("%s: %s not found in FDT!\n", __func__, name);
return 0;
}
addr_t deviceReg = fdt_get_device_reg(node);
addr_t deviceReg = fdt_get_device_reg(fdt, node);
if (deviceReg > 0) {
//TRACE("%s: %s found @ 0x%" B_PRIx64 " , size: 0x%" B_PRIx64 "\n",
// __func__, name, deviceReg, size);
@ -280,15 +295,15 @@ fdt_get_device_reg_byname(const char* name)
phys_addr_t
fdt_get_device_reg_byalias(const char* alias)
fdt_get_device_reg_byalias(const void* fdt, const char* alias)
{
const char* name = fdt_get_alias(gFDT, alias);
const char* name = fdt_get_alias(fdt, alias);
if (name == NULL) {
dprintf("%s: No alias found for %s!\n", __func__, alias);
return 0;
}
phys_addr_t deviceReg = fdt_get_device_reg_byname(name);
phys_addr_t deviceReg = fdt_get_device_reg_byname(fdt, name);
return deviceReg;
}