Skip to content
Snippets Groups Projects
Commit 801d4d8a authored by Jonas Höppner's avatar Jonas Höppner
Browse files

[CMD][FDT_OVERLAY]: seco_fdt_overlay to overlay dtbo by eeprom or seco_config

This command is a replacement for the environment script with the same
functionality.
The only sub command implemented in 'apply_all', which reads the config
from the eeprom and seco_config and eventually a compiletime default,
creates a list of overlay to apply and applies them to the current
devicetree.
Up to know, the panel and touch is handled by this command.
parent 51a23110
No related branches found
No related tags found
No related merge requests found
...@@ -2377,5 +2377,15 @@ config CMD_SECO_EEPROM_MANAGER ...@@ -2377,5 +2377,15 @@ config CMD_SECO_EEPROM_MANAGER
data in a dedicated I2C EEPROM. It uses a pre-defined data in a dedicated I2C EEPROM. It uses a pre-defined
data-structure with CRC calculation. data-structure with CRC calculation.
config CMD_SECO_FDT_OVERLAY
bool "Enable SECO devicetree overlay commands"
depends on SECO_EEPROM_MANAGER && CMD_FDT
default y
help
The seco_fdt_overlay commands implement the selection of device specific
overlays depending on data from the eeprom, seco_config (if available)
and autodetected features.
endmenu endmenu
...@@ -26,6 +26,7 @@ endif ...@@ -26,6 +26,7 @@ endif
obj-$(CONFIG_CMD_SECO_SYSDATA) += sysdata.o obj-$(CONFIG_CMD_SECO_SYSDATA) += sysdata.o
obj-$(CONFIG_CMD_SECO_EEPROM_MANAGER) += seco_eeprom_manager.o obj-$(CONFIG_CMD_SECO_EEPROM_MANAGER) += seco_eeprom_manager.o
obj-$(CONFIG_CMD_SECO_FDT_OVERLAY) += seco_fdt_overlay.o
# command # command
obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ACPI) += acpi.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2025 SECO
*
* Jonas Höppner <jonas.hoeppner@seco.com>
*/
//#define DEBUG 1
#include <common.h>
#include <command.h>
#include <stdlib.h>
#include <errno.h>
#include <env.h>
#include <linux/libfdt.h>
#include <fdt_support.h>
#include <fs.h>
#include "../board/seco/common/seco_eeprom_manager.h"
#include <mapmem.h>
// These config are supposed to be set in the defconfig if needed
#ifdef CONFIG_DEFAULT_FDT_OVERLAY_PANEL_FILE
static const char *seco_fdt_overlay_default_panel = __stringify(CONFIG_DEFAULT_FDT_OVERLAY_PANEL_FILE);
#else
static const char *seco_fdt_overlay_default_panel = "";
#endif
#ifdef CONFIG_DEFAULT_FDT_OVERLAY_TOUCH_FILE
static const char *seco_fdt_overlay_default_touch = __stringify(CONFIG_DEFAULT_FDT_OVERLAY_TOUCH_FILE);
#else
static const char *seco_fdt_overlay_default_touch = "";
#endif
/* this is from cmd/fdt */
extern struct fdt_header *working_fdt;
static int seco_devconf_nodeoffset = -1;
static int fdt_overlay_addr;
enum fdt_field{
NAME,
COMPATIBLE,
};
#define MAX_OVERLAYS 32
#define MAX_OVERLAY_NAME_LEN 256
static char * overlay_list[MAX_OVERLAYS];
static int overlay_cnt = 0;
/*==============================*/
/* Environment helper functions */
/*==============================*/
static void env_get_int_with_default(int * var, const char * variable,
int default_val, int base)
{
const char * env_str;
env_str = env_get(variable);
if( env_str)
*var = simple_strtoul(env_str, NULL, base);
else
*var = default_val;
}
static int fdt_overlay_add_to_list(const char * overlay)
{
if( overlay_cnt >= (MAX_OVERLAYS - 1)){
log_err("Too many overlays configured.\n");
return -1;
}
debug("%s %d: Adding %s as %d\n", __func__, __LINE__, overlay, overlay_cnt);
if( strlen( overlay ) >= MAX_OVERLAY_NAME_LEN){
log_err("Overlay name too long.\n");
return -1;
}
overlay_list[overlay_cnt] = strndup(overlay, MAX_OVERLAY_NAME_LEN);
overlay_cnt++;
return 0;
}
/* reads overlay (dtbo) names from an environment variable and
returns how many overlays have been added.
*/
static int fdt_overlay_read_from_env( const char * env_var)
{
char * env_str;
char * overlay;
int cnt = 0;
// seco_config
env_str = env_get(env_var);
if( !env_str || strlen(env_str) == 0)
return 0;
/* Seco config has configured something, use it.
Loop over all elements separted by ';' */
overlay = strtok( env_str, ";" );
while( overlay != NULL)
{
debug("%s %d: seco_config overlay: %d %s\n", __func__,
__LINE__, overlay_cnt, overlay);
if( fdt_overlay_add_to_list(overlay) < 0) break;
cnt++;
overlay = strtok( NULL, ";" );
}
return cnt;
}
/* returns the number of applied overlays */
static int fdt_overlay_read_from_eeprom_by_property(const char * prop_name)
{
const char * overlay;
int cnt = 0;
debug("%s %d: %s\n", __func__, __LINE__, prop_name);
/* I wantet to support multiple overlay for one entry,
but it does not work, seems to be an issue in libfdt
*/
while(true)
{
overlay = fdt_stringlist_get( working_fdt,
seco_devconf_nodeoffset, prop_name, cnt, NULL);
if( !overlay) break;
debug("%s %d: seco_eeprom overlay: %d %s\n", __func__,
__LINE__, overlay_cnt, overlay);
if( fdt_overlay_add_to_list(overlay) < 0) break;
cnt++;
}
return cnt;
}
/* returns the number of applied overlays */
static int fdt_overlay_read_panel_from_eeprom(void)
{
int err;
u8 panel_id = 1;
char prop_name[16];
err = seco_eeprom_get_panel_id(&panel_id);
if( err != 0) return 0;
// read the matching devicetree overlay name from loaded devicetree
snprintf(prop_name, 16, "panel-id-%d", panel_id);
return fdt_overlay_read_from_eeprom_by_property(prop_name);
}
/* returns the number of applied overlays */
static int fdt_overlay_read_touch_from_eeprom(void)
{
int err;
u8 touch_id = 1;
char prop_name[16];
err = seco_eeprom_get_touch_id(&touch_id);
if( err != 0) return 0;
// read the matching devicetree overlay name from loaded devicetree
snprintf(prop_name, 16, "touch-id-%d", touch_id);
return fdt_overlay_read_from_eeprom_by_property(prop_name);
}
/*==============================*/
/* fs helper functions */
/*==============================*/
/* This configures the fs subsystem to read from the configured
fdt device and partition.
It reads out the device and partition from the environment,
the type is hardcoded to mmc.
I put it into its one function, because I'm thinking if this
should may be change to automatically select the boot device
and partition, or the A-B booting need to be taken into account.
*/
static int seco_set_fdt_blk_dev( void)
{
static char dev_part_str[8] = "";
int fdt_device_id = -1;
int fdt_partition = -1;
if( dev_part_str[0] == 0){
// Cash this part
env_get_int_with_default(&fdt_device_id, "fdt_device_id",
ENV_SYS_MMC_ENV_DEV, 10);
env_get_int_with_default(&fdt_partition, "fdt_partition",
ENV_SYS_MMC_FDT_PART, 10);
snprintf(dev_part_str, 8, "%d:%d", fdt_device_id, fdt_partition);
}
if (fs_set_blk_dev("mmc", dev_part_str, FS_TYPE_ANY)) {
log_err("Can't set block device\n");
return 1;
}
return 0;
}
/* Function to find one node in the current device tree.
start_offset: Used to iterate through the devicetree,
for example returned by fdt_path_offset
fdt_field: either COMPATIBLE or NAME, nodes field used for comparison.
value: String to compare with
recursive: bool, if false, the search is only done in the current depth
*/
static int fdt_find_node(int start_offset, enum fdt_field fdt_field, const char * value, bool recursive)
{
int start_depth, current_depth;
int node_offset;
const char *node_value;
node_offset = start_offset;
start_depth = fdt_node_depth(working_fdt, node_offset);
current_depth = start_depth;
// loop through the fdt
while (true) {
node_offset = fdt_next_node( working_fdt, node_offset, &current_depth);
if (node_offset < 0 || ( current_depth <= start_depth))
break;
if ( !recursive && current_depth != start_depth + 1)
continue;
if( fdt_field == NAME)
node_value = fdt_get_name(working_fdt, node_offset, NULL);
else if( fdt_field == COMPATIBLE)
node_value = fdt_getprop(working_fdt, node_offset, "compatible", NULL);
debug("Value of %s: %s at offset %d, depth %d, start depth %d\n",
fdt_field == NAME? "name" : "compatible",
node_value, node_offset, current_depth, start_depth);
if( node_value && 0 == strcmp(value, node_value))
return node_offset;
}
debug("Failed to find node with %s '%s' in the current devicetree\n",
fdt_field == NAME? "name" : "compatible", value);
return -1;
}
/* Function to load one file from the default fdt source and applies it to the
current devicetree.
*/
static int seco_fdt_overlay_apply_one( const char * overlay_name)
{
int ret = 0;
loff_t len;
struct fdt_header *blob;
/* method prints messages on error */
if( seco_set_fdt_blk_dev()) return 1;
ret = fs_read(overlay_name, fdt_overlay_addr, 0, 0, &len);
if (ret < 0) {
log_err("Failed to load '%s' %d\n", overlay_name, ret);
goto error;
}
printf("Applying overlay: %s (%d bytes)\n", overlay_name, (int) len);
// Check loaded overlay
blob = map_sysmem(fdt_overlay_addr, 0);
ret = fdt_check_header(blob);
if (ret != 0){
printf( "FDT does not seem to be valid: %d\n", ret);
goto error;
}
// Apply overlay to current devicetree
/* apply method prints messages on error */
ret = fdt_overlay_apply_verbose(working_fdt, blob);
error:
return ret;
}
static int find_devconf_node(void)
{
int node_offset;
node_offset = fdt_path_offset(working_fdt, "/"); // Assume the node is on '/'
if (node_offset < 0) {
/* Not found or something else bad happened. */
printf("libfdt: Failed to find root node. fdt_path_offset() returned %s\n",
fdt_strerror(node_offset));
return 1;
}
debug("Found '/' node add offset %d\n", node_offset);
// Find node with compatible = 'seco,devconf'
node_offset = fdt_find_node(node_offset, COMPATIBLE, "seco,devconf", false);
if( node_offset < 0){
/* This should be quiet in normal use case, as not all variants are
using this implemenation */
debug("Failed to find 'seco,devconf' node in the current devicetree\n");
return -1;
}
// Find subnode named 'overlays'
node_offset = fdt_find_node(node_offset, NAME, "overlays", true);
if( node_offset < 0){
puts("Failed to find overlay in 'seco,devconf' node.\n");
return -1;
}
return node_offset;
}
/* This function reads all configuration (seco_config, eeprom, default)
and creates a list of overlay to apply to the currently loaded devicetree.
The priority is
seco_config (during development) -> eeprom (factory programmed) -> default (build time)
Currently this is done for display (panel) and touch.
Other overlays, like what's additionally configured by seco config is not yet
taken into account.
*/
static int apply_all(void)
{
int err, i;
bool skip_eeprom = false;
// Verify the base fdt is setup correctly
if (!working_fdt) {
puts(
"No FDT memory address configured. Please configure\n"
"the FDT address via \"fdt addr <address>\" command.\n"
"Aborting!\n");
return CMD_RET_FAILURE;
}
err = fdt_check_header(working_fdt);
if (err != 0){
printf( "FDT does not seem to be valid: %d\n", err);
return 1;
}
// Search for the base nodes
seco_devconf_nodeoffset = find_devconf_node();
if( seco_devconf_nodeoffset < 0 )
skip_eeprom = true;
env_get_int_with_default(&fdt_overlay_addr, "fdt_overlay_base_addr",
ENV_FDT_OVERLAY_BASEADDR, 16);
// Initialize the eeprom: ignore errors, we can continue without
if(!skip_eeprom)
if( seco_eeprom_auto_init() < 0)
skip_eeprom = true;
// Read out panel overlays
do{
// seco_config
if( fdt_overlay_read_from_env("fdt_overlay_video_list") > 0 )
break;
// seco eeprom
if(!skip_eeprom)
if( fdt_overlay_read_panel_from_eeprom() > 0 )
break;
// default
if(strlen(seco_fdt_overlay_default_panel) > 0)
fdt_overlay_add_to_list(seco_fdt_overlay_default_panel);
}while(false);
// Read out touch overlays
// seco_config
do{
if(fdt_overlay_read_from_env("fdt_overlay_touch_list") > 0 )
break;
// seco eeprom
if(!skip_eeprom)
if(fdt_overlay_read_touch_from_eeprom() > 0 )
break;
//default
if(strlen(seco_fdt_overlay_default_touch) > 0)
fdt_overlay_add_to_list(seco_fdt_overlay_default_touch);
}while(false);
// Now apply all overlays from the list
for( i = 0; i < overlay_cnt; i++)
seco_fdt_overlay_apply_one(overlay_list[i]);
for( int j = 0; j < overlay_cnt; j++)
{
free(overlay_list[j]);
overlay_list[j] = NULL;
}
return 0;
}
static int do_seco_fdt_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
if (argc < 1)
return CMD_RET_USAGE;
/* 'apply_all' */
if (strncmp(argv[1], "a", 1) == 0) {
return apply_all();
}
return 0;
}
#define CMD_DESC "SECO devicetree overlay handling"
#define CMD_HELP "apply_all: Read and apply all configured overlays.\n"
U_BOOT_CMD(
seco_fdt_overlay, 2, 0, do_seco_fdt_overlay,
CMD_DESC,
CMD_HELP
);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment