2020-05-08 19:28:13 +02:00
|
|
|
/* sconfig, coreboot device tree compiler */
|
util/: Replace GPLv2 boiler plate with SPDX header
Used commands:
perl -i -p0e 's|\/\*[\s*]*.*is free software[:;][\s*]*you[\s*]*can[\s*]*redistribute[\s*]*it[\s*]*and\/or[\s*]*modify[\s*]*it[\s*]*under[\s*]*the[\s*]*terms[\s*]*of[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*as[\s*]*published[\s*]*by[\s*]*the[\s*]*Free[\s*]*Software[\s*]*Foundation[;,][\s*]*version[\s*]*2[\s*]*of[\s*]*the[\s*]*License.[\s*]*This[\s*]*program[\s*]*is[\s*]*distributed[\s*]*in[\s*]*the[\s*]*hope[\s*]*that[\s*]*it[\s*]*will[\s*]*be[\s*]*useful,[\s*]*but[\s*]*WITHOUT[\s*]*ANY[\s*]*WARRANTY;[\s*]*without[\s*]*even[\s*]*the[\s*]*implied[\s*]*warranty[\s*]*of[\s*]*MERCHANTABILITY[\s*]*or[\s*]*FITNESS[\s*]*FOR[\s*]*A[\s*]*PARTICULAR[\s*]*PURPOSE.[\s*]*See[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*for[\s*]*more[\s*]*details.[\s*]*\*\/|/* SPDX-License-Identifier: GPL-2.0-only */|' $(cat filelist)
perl -i -p0e 's|This[\s*]*program[\s*]*is[\s*]*free[\s*]*software[:;][\s*]*you[\s*]*can[\s*]*redistribute[\s*]*it[\s*]*and/or[\s*]*modify[\s*]*it[\s*]*under[\s*]*the[\s*]*terms[\s*]*of[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*as[\s*]*published[\s*]*by[\s*]*the[\s*]*Free[\s*]*Software[\s*]*Foundation[;,][\s*]*either[\s*]*version[\s*]*2[\s*]*of[\s*]*the[\s*]*License,[\s*]*or[\s*]*.at[\s*]*your[\s*]*option.*[\s*]*any[\s*]*later[\s*]*version.[\s*]*This[\s*]*program[\s*]*is[\s*]*distributed[\s*]*in[\s*]*the[\s*]*hope[\s*]*that[\s*]*it[\s*]*will[\s*]*be[\s*]*useful,[\s*]*but[\s*]*WITHOUT[\s*]*ANY[\s*]*WARRANTY;[\s*]*without[\s*]*even[\s*]*the[\s*]*implied[\s*]*warranty[\s*]*of[\s*]*MERCHANTABILITY[\s*]*or[\s*]*FITNESS[\s*]*FOR[\s*]*A[\s*]*PARTICULAR[\s*]*PURPOSE.[\s*]*See[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*for[\s*]*more[\s*]*details.[\s*]*\*\/|/* SPDX-License-Identifier: GPL-2.0-or-later */|' $(cat filelist)
perl -i -p0e 's|\/\*[\s*]*.*This[\s*#]*program[\s*#]*is[\s*#]*free[\s*#]*software[;:,][\s*#]*you[\s*#]*can[\s*#]*redistribute[\s*#]*it[\s*#]*and/or[\s*#]*modify[\s*#]*it[\s*#]*under[\s*#]*the[\s*#]*terms[\s*#]*of[\s*#]*the[\s*#]*GNU[\s*#]*General[\s*#]*Public[\s*#]*License[\s*#]*as[\s*#]*published[\s*#]*by[\s*#]*the[\s*#]*Free[\s*#]*Software[\s*#]*Foundation[;:,][\s*#]*either[\s*#]*version[\s*#]*3[\s*#]*of[\s*#]*the[\s*#]*License[;:,][\s*#]*or[\s*#]*.at[\s*#]*your[\s*#]*option.*[\s*#]*any[\s*#]*later[\s*#]*version.[\s*#]*This[\s*#]*program[\s*#]*is[\s*#]*distributed[\s*#]*in[\s*#]*the[\s*#]*hope[\s*#]*that[\s*#]*it[\s*#]*will[\s*#]*be[\s*#]*useful[;:,][\s*#]*but[\s*#]*WITHOUT[\s*#]*ANY[\s*#]*WARRANTY[;:,][\s*#]*without[\s*#]*even[\s*#]*the[\s*#]*implied[\s*#]*warranty[\s*#]*of[\s*#]*MERCHANTABILITY[\s*#]*or[\s*#]*FITNESS[\s*#]*FOR[\s*#]*A[\s*#]*PARTICULAR[\s*#]*PURPOSE.[\s*#]*See[\s*#]*the[\s*#]*GNU[\s*#]*General[\s*#]*Public[\s*#]*License[\s*#]*for[\s*#]*more[\s*#]*details.[\s*]*\*\/|/* SPDX-License-Identifier: GPL-3.0-or-later */|' $(cat filelist)
perl -i -p0e 's|(\#\#*)[\w]*.*is free software[:;][\#\s]*you[\#\s]*can[\#\s]*redistribute[\#\s]*it[\#\s]*and\/or[\#\s]*modify[\#\s]*it[\s\#]*under[\s \#]*the[\s\#]*terms[\s\#]*of[\s\#]*the[\s\#]*GNU[\s\#]*General[\s\#]*Public[\s\#]*License[\s\#]*as[\s\#]*published[\s\#]*by[\s\#]*the[\s\#]*Free[\s\#]*Software[\s\#]*Foundation[;,][\s\#]*version[\s\#]*2[\s\#]*of[\s\#]*the[\s\#]*License.*[\s\#]*This[\s\#]*program[\s\#]*is[\s\#]*distributed[\s\#]*in[\s\#]*the[\s\#]*hope[\s\#]*that[\s\#]*it[\s\#]*will[\#\s]*be[\#\s]*useful,[\#\s]*but[\#\s]*WITHOUT[\#\s]*ANY[\#\s]*WARRANTY;[\#\s]*without[\#\s]*even[\#\s]*the[\#\s]*implied[\#\s]*warranty[\#\s]*of[\#\s]*MERCHANTABILITY[\#\s]*or[\#\s]*FITNESS[\#\s]*FOR[\#\s]*A[\#\s]*PARTICULAR[\#\s]*PURPOSE.[\#\s]*See[\#\s]*the[\#\s]*GNU[\#\s]*General[\#\s]*Public[\#\s]*License[\#\s]*for[\#\s]*more[\#\s]*details.\s(#* *\n)*|\1 SPDX-License-Identifier: GPL-2.0-only\n\n|' $(cat filelist)
perl -i -p0e 's|(\#\#*)[\w*]*.*is free software[:;][\s*]*you[\s*]*can[\s*]*redistribute[\s*]*it[\s*]*and\/or[\s*]*modify[\s*]*it[\s*]*under[\s*]*the[\s*]*terms[\s*]*of[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*as[\s*]*published[\s*]*by[\s*]*the[\s*]*Free[\s*]*Software[\s*]*Foundation[;,][\s*]*version[\s*]*2[\s*]*of[\s*]*the[\s*]*License.[\s*]*This[\s*]*program[\s*]*is[\s*]*distributed[\s*]*in[\s*]*the[\s*]*hope[\s*]*that[\s*]*it[\s*]*will[\s*]*be[\s*]*useful,[\s*]*but[\s*]*WITHOUT[\s*]*ANY[\s*]*WARRANTY;[\s*]*without[\s*]*even[\s*]*the[\s*]*implied[\s*]*warranty[\s*]*of[\s*]*MERCHANTABILITY[\s*]*or[\s*]*FITNESS[\s*]*FOR[\s*]*A[\s*]*PARTICULAR[\s*]*PURPOSE.[\s*]*See[\s*]*the[\s*]*GNU[\s*]*General[\s*]*Public[\s*]*License[\s*]*for[\s*]*more[\s*]*details.\s(#* *\n)*|\1 SPDX-License-Identifier: GPL-2.0-only\n\n|' $(cat filelist)
Change-Id: I1008a63b804f355a916221ac994701d7584f60ff
Signed-off-by: Patrick Georgi <pgeorgi@google.com>
Signed-off-by: Elyes HAOUAS <ehaouas@noos.fr>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41177
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-05-08 20:48:04 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2020-09-23 23:36:30 +02:00
|
|
|
#include <assert.h>
|
2012-05-30 16:26:30 +02:00
|
|
|
#include <ctype.h>
|
2020-06-09 20:20:29 +02:00
|
|
|
#include <getopt.h>
|
2020-10-01 23:41:31 +02:00
|
|
|
#include <inttypes.h>
|
2020-09-23 23:36:30 +02:00
|
|
|
#include <libgen.h>
|
2020-10-01 23:41:31 +02:00
|
|
|
/* stat.h needs to be included before commonlib/helpers.h to avoid errors.*/
|
2019-07-11 12:47:19 +02:00
|
|
|
#include <sys/stat.h>
|
2019-03-15 16:05:19 +01:00
|
|
|
#include <commonlib/helpers.h>
|
2020-05-16 00:39:08 +02:00
|
|
|
#include <stdint.h>
|
2010-05-05 13:19:50 +02:00
|
|
|
#include "sconfig.h"
|
|
|
|
#include "sconfig.tab.h"
|
|
|
|
|
2010-07-15 17:59:07 +02:00
|
|
|
extern int linenum;
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Maintains list of all the unique chip structures for the board.
|
|
|
|
* This is shared across base and override device trees since we need to
|
|
|
|
* generate headers for all chips added by both the trees.
|
|
|
|
*/
|
2018-05-31 19:33:16 +02:00
|
|
|
static struct chip chip_header;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2012-03-18 19:19:28 +01:00
|
|
|
typedef enum {
|
|
|
|
UNSLASH,
|
|
|
|
SPLIT_1ST,
|
|
|
|
TO_LOWER,
|
|
|
|
TO_UPPER,
|
|
|
|
} translate_t;
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
/*
|
|
|
|
* Mainboard is assumed to have a root device whose bus is the parent of all the
|
|
|
|
* devices that are added by parsing the devicetree file. This device has a
|
|
|
|
* mainboard chip instance associated with it.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* +------------------------+ +----------------------+
|
2018-06-11 13:26:45 +02:00
|
|
|
* | Root device | | Mainboard |
|
|
|
|
* +---------+ (base_root_dev) +--------------->+ instance +
|
2018-06-03 13:22:17 +02:00
|
|
|
* | | | chip_instance | (mainboard_instance)|
|
|
|
|
* | +------------------------+ | |
|
|
|
|
* | | +----------------------+
|
|
|
|
* | | bus |
|
|
|
|
* | parent v |
|
|
|
|
* | +-------------------+ |
|
|
|
|
* | | Root bus | |
|
2018-06-11 13:26:45 +02:00
|
|
|
* +----------->+ (base_root_bus) | |
|
2018-06-03 13:22:17 +02:00
|
|
|
* | | |
|
|
|
|
* +-------------------+ |
|
|
|
|
* | |
|
|
|
|
* | children | chip
|
|
|
|
* v |
|
|
|
|
* X |
|
|
|
|
* (new devices will |
|
|
|
|
* be added here as |
|
|
|
|
* children) |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* +-------+----------+
|
|
|
|
* | |
|
|
|
|
* | Mainboard chip +----------->X (new chips will be
|
|
|
|
* | (mainboard_chip) | added here)
|
|
|
|
* | |
|
|
|
|
* +------------------+
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
2018-06-22 03:44:32 +02:00
|
|
|
|
|
|
|
/* Root device of primary tree. */
|
2018-06-11 13:26:45 +02:00
|
|
|
static struct device base_root_dev;
|
2018-06-22 03:44:32 +02:00
|
|
|
|
2020-07-30 01:28:43 +02:00
|
|
|
/* Root device of chipset tree. */
|
|
|
|
static struct device chipset_root_dev;
|
|
|
|
|
2018-06-22 03:44:32 +02:00
|
|
|
/* Root device of override tree (if applicable). */
|
|
|
|
static struct device override_root_dev;
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
static struct chip_instance mainboard_instance;
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
static struct bus base_root_bus = {
|
2018-06-03 13:22:17 +02:00
|
|
|
.id = 0,
|
2018-06-11 13:26:45 +02:00
|
|
|
.dev = &base_root_dev,
|
2018-06-03 13:22:17 +02:00
|
|
|
};
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
static struct device base_root_dev = {
|
2018-06-03 13:22:17 +02:00
|
|
|
.name = "dev_root",
|
|
|
|
.chip_instance = &mainboard_instance,
|
|
|
|
.path = " .type = DEVICE_PATH_ROOT ",
|
2018-06-11 13:26:45 +02:00
|
|
|
.parent = &base_root_bus,
|
2018-06-03 13:22:17 +02:00
|
|
|
.enabled = 1,
|
2018-06-11 13:26:45 +02:00
|
|
|
.bus = &base_root_bus,
|
2018-06-03 13:22:17 +02:00
|
|
|
};
|
|
|
|
|
2020-07-30 01:28:43 +02:00
|
|
|
static struct bus chipset_root_bus = {
|
|
|
|
.id = 0,
|
|
|
|
.dev = &chipset_root_dev,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct device chipset_root_dev = {
|
|
|
|
.name = "chipset_root",
|
|
|
|
.chip_instance = &mainboard_instance,
|
|
|
|
.path = " .type = DEVICE_PATH_ROOT ",
|
|
|
|
.parent = &chipset_root_bus,
|
|
|
|
.enabled = 1,
|
|
|
|
.bus = &chipset_root_bus,
|
|
|
|
};
|
|
|
|
|
2018-06-22 03:44:32 +02:00
|
|
|
static struct bus override_root_bus = {
|
|
|
|
.id = 0,
|
|
|
|
.dev = &override_root_dev,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct device override_root_dev = {
|
|
|
|
.name = "override_root",
|
|
|
|
/*
|
|
|
|
* Override tree root device points to the same mainboard chip instance
|
|
|
|
* as the base tree root device. It should not cause any side-effects
|
|
|
|
* since the mainboard chip instance pointer in override tree will just
|
|
|
|
* be ignored.
|
|
|
|
*/
|
|
|
|
.chip_instance = &mainboard_instance,
|
|
|
|
.path = " .type = DEVICE_PATH_ROOT ",
|
|
|
|
.parent = &override_root_bus,
|
|
|
|
.enabled = 1,
|
|
|
|
.bus = &override_root_bus,
|
|
|
|
};
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
static struct chip mainboard_chip = {
|
2010-05-05 13:19:50 +02:00
|
|
|
.name = "mainboard",
|
|
|
|
.name_underscore = "mainboard",
|
2018-05-31 19:33:16 +02:00
|
|
|
.instance = &mainboard_instance,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct chip_instance mainboard_instance = {
|
|
|
|
.id = 0,
|
|
|
|
.chip = &mainboard_chip,
|
2010-05-05 13:19:50 +02:00
|
|
|
};
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
/* This is the parent of all devices added by parsing the devicetree file. */
|
2018-06-11 13:26:45 +02:00
|
|
|
struct bus *root_parent;
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
struct queue_entry {
|
2018-05-31 00:09:09 +02:00
|
|
|
void *data;
|
2018-06-03 19:42:49 +02:00
|
|
|
struct queue_entry *next;
|
|
|
|
struct queue_entry *prev;
|
|
|
|
};
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2022-08-06 19:02:59 +02:00
|
|
|
/* Global list of all `struct device_operations` identifiers to declare. */
|
|
|
|
static struct identifier *device_operations;
|
|
|
|
|
2018-05-31 18:48:51 +02:00
|
|
|
#define S_ALLOC(_s) s_alloc(__func__, _s)
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2018-05-31 18:48:51 +02:00
|
|
|
static void *s_alloc(const char *f, size_t s)
|
|
|
|
{
|
|
|
|
void *data = calloc(1, s);
|
|
|
|
if (!data) {
|
2018-06-08 07:02:08 +02:00
|
|
|
fprintf(stderr, "%s: Failed to alloc mem!\n", f);
|
2018-05-31 00:09:09 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
2018-05-31 18:48:51 +02:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
static struct queue_entry *new_queue_entry(void *data)
|
2018-05-31 18:48:51 +02:00
|
|
|
{
|
2018-06-03 19:42:49 +02:00
|
|
|
struct queue_entry *e = S_ALLOC(sizeof(*e));
|
2018-05-31 00:09:09 +02:00
|
|
|
|
|
|
|
e->data = data;
|
|
|
|
e->next = e->prev = e;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
static void enqueue_tail(struct queue_entry **q_head, void *data)
|
2018-05-31 00:09:09 +02:00
|
|
|
{
|
2018-06-03 19:42:49 +02:00
|
|
|
struct queue_entry *tmp = new_queue_entry(data);
|
|
|
|
struct queue_entry *q = *q_head;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
|
|
|
if (!q) {
|
2018-06-03 19:42:49 +02:00
|
|
|
*q_head = tmp;
|
2018-05-31 00:09:09 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->prev->next = tmp;
|
|
|
|
tmp->prev = q->prev;
|
|
|
|
q->prev = tmp;
|
|
|
|
tmp->next = q;
|
|
|
|
}
|
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
static void *dequeue_tail(struct queue_entry **q_head)
|
2018-05-31 00:09:09 +02:00
|
|
|
{
|
2018-06-03 19:42:49 +02:00
|
|
|
struct queue_entry *q = *q_head;
|
|
|
|
struct queue_entry *tmp;
|
2018-05-31 00:09:09 +02:00
|
|
|
void *data;
|
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
if (!q)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tmp = q->prev;
|
|
|
|
|
2018-05-31 00:09:09 +02:00
|
|
|
if (tmp == q)
|
2018-06-03 19:42:49 +02:00
|
|
|
*q_head = NULL;
|
2018-05-31 00:09:09 +02:00
|
|
|
else {
|
|
|
|
tmp->prev->next = q;
|
|
|
|
q->prev = tmp->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = tmp->data;
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2018-06-03 19:42:49 +02:00
|
|
|
static void *dequeue_head(struct queue_entry **q_head)
|
|
|
|
{
|
|
|
|
struct queue_entry *q = *q_head;
|
|
|
|
struct queue_entry *tmp = q;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!q)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (q->next == q)
|
|
|
|
*q_head = NULL;
|
|
|
|
else {
|
|
|
|
q->next->prev = q->prev;
|
|
|
|
q->prev->next = q->next;
|
|
|
|
*q_head = q->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = tmp->data;
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *peek_queue_head(struct queue_entry *q_head)
|
|
|
|
{
|
|
|
|
if (!q_head)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return q_head->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct queue_entry *chip_q_head;
|
|
|
|
|
|
|
|
void chip_enqueue_tail(void *data)
|
|
|
|
{
|
|
|
|
enqueue_tail(&chip_q_head, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *chip_dequeue_tail(void)
|
|
|
|
{
|
|
|
|
return dequeue_tail(&chip_q_head);
|
|
|
|
}
|
|
|
|
|
2016-08-06 02:32:18 +02:00
|
|
|
int yywrap(void)
|
|
|
|
{
|
2010-05-05 13:19:50 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-08-06 02:32:18 +02:00
|
|
|
void yyerror(char const *str)
|
2010-05-05 13:19:50 +02:00
|
|
|
{
|
2010-08-16 20:21:56 +02:00
|
|
|
extern char *yytext;
|
2016-08-06 02:32:18 +02:00
|
|
|
fprintf(stderr, "line %d: %s: %s\n", linenum + 1, yytext, str);
|
2010-08-16 20:21:56 +02:00
|
|
|
exit(1);
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
|
2016-08-06 02:32:18 +02:00
|
|
|
char *translate_name(const char *str, translate_t mode)
|
2011-12-05 19:33:55 +01:00
|
|
|
{
|
2012-03-18 19:19:28 +01:00
|
|
|
char *b, *c;
|
|
|
|
b = c = strdup(str);
|
|
|
|
while (c && *c) {
|
|
|
|
if ((mode == SPLIT_1ST) && (*c == '/')) {
|
|
|
|
*c = 0;
|
|
|
|
break;
|
|
|
|
}
|
2016-08-06 02:32:18 +02:00
|
|
|
if (*c == '/')
|
|
|
|
*c = '_';
|
|
|
|
if (*c == '-')
|
|
|
|
*c = '_';
|
2012-03-18 19:19:28 +01:00
|
|
|
if (mode == TO_UPPER)
|
2011-12-05 19:33:55 +01:00
|
|
|
*c = toupper(*c);
|
2012-03-18 19:19:28 +01:00
|
|
|
if (mode == TO_LOWER)
|
|
|
|
*c = tolower(*c);
|
|
|
|
c++;
|
2011-12-05 19:33:55 +01:00
|
|
|
}
|
2012-03-18 19:19:28 +01:00
|
|
|
return b;
|
2011-12-05 19:33:55 +01:00
|
|
|
}
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
static struct chip *get_chip(char *path)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip *h = &chip_header;
|
|
|
|
|
|
|
|
while (h->next) {
|
|
|
|
int result = strcmp(path, h->next->name);
|
|
|
|
if (result == 0)
|
|
|
|
return h->next;
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
h = h->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chip *new_chip = S_ALLOC(sizeof(struct chip));
|
|
|
|
new_chip->next = h->next;
|
|
|
|
h->next = new_chip;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
new_chip->chiph_exists = 1;
|
|
|
|
new_chip->name = path;
|
2018-05-31 19:33:16 +02:00
|
|
|
new_chip->name_underscore = translate_name(path, UNSLASH);
|
2010-05-05 13:19:50 +02:00
|
|
|
|
|
|
|
struct stat st;
|
2018-05-31 18:48:51 +02:00
|
|
|
char *chip_h = S_ALLOC(strlen(path) + 18);
|
2011-10-14 21:41:46 +02:00
|
|
|
sprintf(chip_h, "src/%s", path);
|
|
|
|
if ((stat(chip_h, &st) == -1) && (errno == ENOENT)) {
|
2015-11-13 09:39:22 +01:00
|
|
|
/* root_complex gets away without a separate directory, but
|
2023-02-04 12:20:37 +01:00
|
|
|
* exists on pretty much all AMD chipsets.
|
2015-11-13 09:39:22 +01:00
|
|
|
*/
|
|
|
|
if (!strstr(path, "/root_complex")) {
|
2013-06-11 16:00:11 +02:00
|
|
|
fprintf(stderr, "ERROR: Chip component %s does not exist.\n",
|
|
|
|
path);
|
|
|
|
exit(1);
|
|
|
|
}
|
2011-10-14 21:41:46 +02:00
|
|
|
}
|
|
|
|
|
2016-08-06 01:40:39 +02:00
|
|
|
sprintf(chip_h, "src/%s/chip.h", path);
|
2011-12-05 19:33:55 +01:00
|
|
|
|
2016-08-06 01:40:39 +02:00
|
|
|
if ((stat(chip_h, &st) == -1) && (errno == ENOENT))
|
2016-08-06 02:32:18 +02:00
|
|
|
new_chip->chiph_exists = 0;
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2014-08-03 15:51:19 +02:00
|
|
|
free(chip_h);
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
return new_chip;
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip_instance *new_chip_instance(char *path)
|
|
|
|
{
|
|
|
|
struct chip *chip = get_chip(path);
|
|
|
|
struct chip_instance *instance = S_ALLOC(sizeof(*instance));
|
|
|
|
|
|
|
|
instance->chip = chip;
|
|
|
|
instance->next = chip->instance;
|
|
|
|
chip->instance = instance;
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
/* List of fw_config fields added during parsing. */
|
|
|
|
static struct fw_config_field *fw_config_fields;
|
|
|
|
|
|
|
|
static struct fw_config_option *find_fw_config_option(struct fw_config_field *field,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct fw_config_option *option = field->options;
|
|
|
|
|
|
|
|
while (option && option->name) {
|
|
|
|
if (!strcmp(option->name, name))
|
|
|
|
return option;
|
|
|
|
option = option->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct fw_config_field *find_fw_config_field(const char *name)
|
|
|
|
{
|
|
|
|
struct fw_config_field *field = fw_config_fields;
|
|
|
|
|
|
|
|
while (field && field->name) {
|
|
|
|
if (!strcmp(field->name, name))
|
|
|
|
return field;
|
|
|
|
field = field->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct fw_config_field *get_fw_config_field(const char *name)
|
|
|
|
{
|
|
|
|
struct fw_config_field *field = find_fw_config_field(name);
|
|
|
|
|
|
|
|
/* Fail if the field does not exist, new fields must be added with a mask. */
|
|
|
|
if (!field) {
|
|
|
|
printf("ERROR: fw_config field not found: %s\n", name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void append_fw_config_field(struct fw_config_field *add)
|
|
|
|
{
|
|
|
|
struct fw_config_field *field = fw_config_fields;
|
|
|
|
|
|
|
|
if (!fw_config_fields) {
|
|
|
|
fw_config_fields = add;
|
|
|
|
} else {
|
|
|
|
while (field && field->next)
|
|
|
|
field = field->next;
|
|
|
|
field->next = add;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
void append_fw_config_bits(struct fw_config_field_bits **bits,
|
|
|
|
unsigned int start_bit, unsigned int end_bit)
|
2020-05-16 00:39:08 +02:00
|
|
|
{
|
2021-04-28 22:03:07 +02:00
|
|
|
struct fw_config_field_bits *new_bits = S_ALLOC(sizeof(*new_bits));
|
|
|
|
new_bits->start_bit = start_bit;
|
|
|
|
new_bits->end_bit = end_bit;
|
|
|
|
new_bits->next = NULL;
|
2020-05-16 00:39:08 +02:00
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
if (*bits == NULL) {
|
|
|
|
*bits = new_bits;
|
|
|
|
return;
|
2020-05-16 00:39:08 +02:00
|
|
|
}
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
struct fw_config_field_bits *tmp = *bits;
|
|
|
|
while (tmp->next)
|
|
|
|
tmp = tmp->next;
|
|
|
|
|
|
|
|
tmp->next = new_bits;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fw_config_masks_overlap(struct fw_config_field *existing,
|
|
|
|
unsigned int start_bit, unsigned int end_bit)
|
|
|
|
{
|
|
|
|
struct fw_config_field_bits *bits = existing->bits;
|
|
|
|
while (bits) {
|
|
|
|
if (start_bit <= bits->end_bit && end_bit >= bits->start_bit) {
|
|
|
|
printf("ERROR: fw_config field [%u-%u] overlaps %s[%u-%u]\n",
|
|
|
|
start_bit, end_bit,
|
|
|
|
existing->name, bits->start_bit, bits->end_bit);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
bits = bits->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct fw_config_field *new_fw_config_field(const char *name, struct fw_config_field_bits *bits)
|
|
|
|
{
|
|
|
|
struct fw_config_field *field = find_fw_config_field(name);
|
|
|
|
struct fw_config_field_bits *tmp;
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
/* Don't allow re-defining a field, only adding new fields. */
|
|
|
|
if (field) {
|
2021-04-28 22:03:07 +02:00
|
|
|
printf("ERROR: fw_config field %s already exists\n", name);
|
2020-05-16 00:39:08 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
/* Check that each field is within 64 bits. */
|
|
|
|
tmp = bits;
|
|
|
|
while (tmp) {
|
|
|
|
if (tmp->start_bit > tmp->end_bit || tmp->end_bit > 63) {
|
2021-05-04 18:08:10 +02:00
|
|
|
printf("ERROR: fw_config field %s has invalid range %u-%u\n", name,
|
2021-04-28 22:03:07 +02:00
|
|
|
tmp->start_bit, tmp->end_bit);
|
2020-05-16 00:39:08 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
2021-04-28 22:03:07 +02:00
|
|
|
|
|
|
|
/* Check for overlap with an existing field. */
|
|
|
|
struct fw_config_field *existing = fw_config_fields;
|
|
|
|
while (existing) {
|
|
|
|
if (fw_config_masks_overlap(existing, tmp->start_bit, tmp->end_bit))
|
|
|
|
exit(1);
|
|
|
|
existing = existing->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = tmp->next;
|
2020-05-16 00:39:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
field = S_ALLOC(sizeof(*field));
|
|
|
|
field->name = name;
|
2021-04-28 22:03:07 +02:00
|
|
|
field->bits = bits;
|
2020-05-16 00:39:08 +02:00
|
|
|
append_fw_config_field(field);
|
|
|
|
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void append_fw_config_option_to_field(struct fw_config_field *field,
|
|
|
|
struct fw_config_option *add)
|
|
|
|
{
|
|
|
|
struct fw_config_option *option = field->options;
|
|
|
|
|
|
|
|
if (!option) {
|
|
|
|
field->options = add;
|
|
|
|
} else {
|
|
|
|
while (option && option->next)
|
|
|
|
option = option->next;
|
|
|
|
option->next = add;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
static uint64_t calc_max_field_value(const struct fw_config_field *field)
|
|
|
|
{
|
|
|
|
unsigned int bit_count = 0;
|
|
|
|
|
|
|
|
const struct fw_config_field_bits *bits = field->bits;
|
|
|
|
while (bits) {
|
|
|
|
bit_count += 1 + bits->end_bit - bits->start_bit;
|
|
|
|
bits = bits->next;
|
|
|
|
};
|
|
|
|
|
|
|
|
return (1ull << bit_count) - 1ull;
|
|
|
|
}
|
|
|
|
|
2020-10-01 23:41:31 +02:00
|
|
|
void add_fw_config_option(struct fw_config_field *field, const char *name, uint64_t value)
|
2020-05-16 00:39:08 +02:00
|
|
|
{
|
|
|
|
struct fw_config_option *option;
|
|
|
|
|
|
|
|
/* Check that option value fits within field mask. */
|
2021-04-28 22:03:07 +02:00
|
|
|
uint64_t field_max_value = calc_max_field_value(field);
|
2020-05-16 00:39:08 +02:00
|
|
|
if (value > field_max_value) {
|
2020-10-01 23:41:31 +02:00
|
|
|
printf("ERROR: fw_config option %s:%s value %" PRIx64 " larger than field max %"
|
|
|
|
PRIx64 "\n",
|
2020-05-16 00:39:08 +02:00
|
|
|
field->name, name, value, field_max_value);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for existing option with this name or value. */
|
|
|
|
option = field->options;
|
|
|
|
while (option) {
|
|
|
|
if (!strcmp(option->name, name)) {
|
|
|
|
printf("ERROR: fw_config option name %s:%s already exists\n",
|
|
|
|
field->name, name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Compare values. */
|
|
|
|
if (value == option->value) {
|
2020-10-01 23:41:31 +02:00
|
|
|
printf("ERROR: fw_config option %s:%s[%" PRIx64 "] redefined as %s\n",
|
2020-05-16 00:39:08 +02:00
|
|
|
field->name, option->name, value, name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
option = option->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
option = S_ALLOC(sizeof(*option));
|
|
|
|
option->name = name;
|
|
|
|
option->value = value;
|
|
|
|
|
|
|
|
/* Add option to the current field. */
|
|
|
|
append_fw_config_option_to_field(field, option);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void append_fw_config_probe_to_dev(struct device *dev, struct fw_config_probe *add)
|
|
|
|
{
|
|
|
|
struct fw_config_probe *probe = dev->probe;
|
|
|
|
|
|
|
|
if (!probe) {
|
|
|
|
dev->probe = add;
|
|
|
|
} else {
|
|
|
|
while (probe && probe->next)
|
|
|
|
probe = probe->next;
|
|
|
|
probe->next = add;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 08:23:23 +02:00
|
|
|
static int check_probe_exists(struct fw_config_probe *probe, const char *field,
|
|
|
|
const char *option)
|
2020-05-16 00:39:08 +02:00
|
|
|
{
|
|
|
|
while (probe) {
|
|
|
|
if (!strcmp(probe->field, field) && !strcmp(probe->option, option)) {
|
2021-08-24 08:23:23 +02:00
|
|
|
return 1;
|
2020-05-16 00:39:08 +02:00
|
|
|
}
|
|
|
|
probe = probe->next;
|
|
|
|
}
|
|
|
|
|
2021-08-24 08:23:23 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_fw_config_probe(struct bus *bus, const char *field, const char *option)
|
|
|
|
{
|
|
|
|
struct fw_config_probe *probe;
|
|
|
|
|
|
|
|
if (check_probe_exists(bus->dev->probe, field, option)) {
|
|
|
|
printf("ERROR: fw_config probe %s:%s already exists\n", field, option);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
probe = S_ALLOC(sizeof(*probe));
|
|
|
|
probe->field = field;
|
|
|
|
probe->option = option;
|
|
|
|
|
|
|
|
append_fw_config_probe_to_dev(bus->dev, probe);
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
static uint64_t compute_fw_config_mask(const struct fw_config_field_bits *bits)
|
|
|
|
{
|
|
|
|
uint64_t mask = 0;
|
|
|
|
|
|
|
|
while (bits) {
|
|
|
|
/* Compute mask from start and end bit. */
|
|
|
|
uint64_t tmp = ((1ull << (1ull + bits->end_bit - bits->start_bit)) - 1ull);
|
|
|
|
tmp <<= bits->start_bit;
|
|
|
|
mask |= tmp;
|
|
|
|
bits = bits->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int bits_width(const struct fw_config_field_bits *bits)
|
|
|
|
{
|
|
|
|
return 1 + bits->end_bit - bits->start_bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t calc_option_value(const struct fw_config_field *field,
|
|
|
|
const struct fw_config_option *option)
|
|
|
|
{
|
|
|
|
uint64_t value = 0;
|
|
|
|
uint64_t original = option->value;
|
|
|
|
|
|
|
|
struct fw_config_field_bits *bits = field->bits;
|
|
|
|
while (bits) {
|
|
|
|
const unsigned int width = bits_width(bits);
|
|
|
|
const uint64_t orig_mask = (1ull << width) - 1ull;
|
|
|
|
const uint64_t orig = (original & orig_mask);
|
|
|
|
value |= (orig << bits->start_bit);
|
|
|
|
|
|
|
|
original >>= width;
|
|
|
|
bits = bits->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
static void emit_fw_config(FILE *fil)
|
|
|
|
{
|
|
|
|
struct fw_config_field *field = fw_config_fields;
|
|
|
|
|
|
|
|
if (!field)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (field) {
|
|
|
|
struct fw_config_option *option = field->options;
|
2020-10-01 23:41:31 +02:00
|
|
|
uint64_t mask;
|
2020-05-16 00:39:08 +02:00
|
|
|
|
|
|
|
fprintf(fil, "#define FW_CONFIG_FIELD_%s_NAME \"%s\"\n",
|
|
|
|
field->name, field->name);
|
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
mask = compute_fw_config_mask(field->bits);
|
2020-10-01 23:41:31 +02:00
|
|
|
fprintf(fil, "#define FW_CONFIG_FIELD_%s_MASK 0x%" PRIx64 "\n",
|
2020-05-16 00:39:08 +02:00
|
|
|
field->name, mask);
|
|
|
|
|
|
|
|
while (option) {
|
2021-04-28 22:03:07 +02:00
|
|
|
const uint64_t value = calc_option_value(field, option);
|
2020-05-16 00:39:08 +02:00
|
|
|
fprintf(fil, "#define FW_CONFIG_FIELD_%s_OPTION_%s_NAME \"%s\"\n",
|
|
|
|
field->name, option->name, option->name);
|
2020-10-01 23:41:31 +02:00
|
|
|
fprintf(fil, "#define FW_CONFIG_FIELD_%s_OPTION_%s_VALUE 0x%"
|
2021-04-28 22:03:07 +02:00
|
|
|
PRIx64 "\n", field->name, option->name, value);
|
2020-05-16 00:39:08 +02:00
|
|
|
option = option->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
field = field->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fil, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int emit_fw_config_probe(FILE *fil, struct device *dev)
|
|
|
|
{
|
|
|
|
struct fw_config_probe *probe = dev->probe;
|
|
|
|
|
|
|
|
fprintf(fil, "STORAGE struct fw_config %s_probe_list[] = {\n", dev->name);
|
|
|
|
|
|
|
|
while (probe) {
|
|
|
|
/* Find matching field. */
|
|
|
|
struct fw_config_field *field;
|
|
|
|
struct fw_config_option *option;
|
2020-10-01 23:41:31 +02:00
|
|
|
uint64_t mask, value;
|
2020-05-16 00:39:08 +02:00
|
|
|
|
|
|
|
field = find_fw_config_field(probe->field);
|
|
|
|
if (!field) {
|
|
|
|
printf("ERROR: fw_config_probe field %s not found\n", probe->field);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
option = find_fw_config_option(field, probe->option);
|
|
|
|
if (!option) {
|
|
|
|
printf("ERROR: fw_config_probe field %s option %s not found\n",
|
|
|
|
probe->field, probe->option);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill out the probe structure with values from emit_fw_config(). */
|
|
|
|
fprintf(fil, "\t{\n");
|
|
|
|
fprintf(fil, "\t\t.field_name = FW_CONFIG_FIELD_%s_NAME,\n", probe->field);
|
|
|
|
fprintf(fil, "\t\t.option_name = FW_CONFIG_FIELD_%s_OPTION_%s_NAME,\n",
|
|
|
|
probe->field, probe->option);
|
|
|
|
fprintf(fil, "\t\t.mask = FW_CONFIG_FIELD_%s_MASK,\n", probe->field);
|
|
|
|
fprintf(fil, "\t\t.value = FW_CONFIG_FIELD_%s_OPTION_%s_VALUE,\n",
|
|
|
|
probe->field, probe->option);
|
|
|
|
fprintf(fil, "\t},\n");
|
|
|
|
|
|
|
|
probe = probe->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add empty entry to mark end of list. */
|
|
|
|
fprintf(fil, "\t{ }\n};\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-06 19:02:59 +02:00
|
|
|
/* Enqueue identifier to list with head `*it`, if not already present. */
|
|
|
|
void add_identifier(struct identifier **it, const char *id)
|
|
|
|
{
|
|
|
|
for (; *it != NULL; it = &(*it)->next) {
|
|
|
|
if (!strcmp((*it)->id, id))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*it = S_ALLOC(sizeof(**it));
|
|
|
|
(*it)->id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_device_ops(struct bus *bus, char *ops_id)
|
|
|
|
{
|
|
|
|
if (bus->dev->ops_id) {
|
|
|
|
printf("ERROR: Device operations may only be specified once,\n"
|
|
|
|
" found '%s', '%s'.\n", bus->dev->ops_id, ops_id);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_identifier(&device_operations, ops_id);
|
|
|
|
bus->dev->ops_id = ops_id;
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
/*
|
|
|
|
* Allocate a new bus for the provided device.
|
|
|
|
* - If this is the first bus being allocated under this device, then its id
|
|
|
|
* is set to 0 and bus and last_bus are pointed to the newly allocated bus.
|
|
|
|
* - If this is not the first bus under this device, then its id is set to 1
|
|
|
|
* plus the id of last bus and newly allocated bus is added to the list of
|
|
|
|
* buses under the device. last_bus is updated to point to the newly
|
|
|
|
* allocated bus.
|
|
|
|
*/
|
|
|
|
static void alloc_bus(struct device *dev)
|
|
|
|
{
|
|
|
|
struct bus *bus = S_ALLOC(sizeof(*bus));
|
|
|
|
|
|
|
|
bus->dev = dev;
|
|
|
|
|
|
|
|
if (dev->last_bus == NULL) {
|
|
|
|
bus->id = 0;
|
|
|
|
dev->bus = bus;
|
|
|
|
} else {
|
|
|
|
bus->id = dev->last_bus->id + 1;
|
|
|
|
dev->last_bus->next_bus = bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->last_bus = bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a new device under the given parent. This function allocates a new
|
|
|
|
* device structure under the provided parent bus and allocates a bus structure
|
|
|
|
* under the newly allocated device.
|
|
|
|
*/
|
|
|
|
static struct device *alloc_dev(struct bus *parent)
|
|
|
|
{
|
|
|
|
struct device *dev = S_ALLOC(sizeof(*dev));
|
|
|
|
|
|
|
|
dev->parent = parent;
|
|
|
|
dev->subsystem_vendor = -1;
|
|
|
|
dev->subsystem_device = -1;
|
|
|
|
|
|
|
|
alloc_bus(dev);
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function scans the children of given bus to see if any device matches
|
|
|
|
* the new device that is requested.
|
|
|
|
*
|
|
|
|
* Returns pointer to the node if found, else NULL.
|
|
|
|
*/
|
|
|
|
static struct device *get_dev(struct bus *parent, int path_a, int path_b,
|
|
|
|
int bustype, struct chip_instance *chip_instance)
|
|
|
|
{
|
|
|
|
struct device *child = parent->children;
|
|
|
|
|
|
|
|
while (child) {
|
|
|
|
if ((child->path_a == path_a) && (child->path_b == path_b) &&
|
|
|
|
(child->bustype == bustype) &&
|
|
|
|
(child->chip_instance == chip_instance))
|
|
|
|
return child;
|
|
|
|
|
|
|
|
child = child->sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Add given node as child of the provided parent. If this is the first child of
|
|
|
|
* the parent, update parent->children pointer as well.
|
|
|
|
*/
|
|
|
|
static void set_new_child(struct bus *parent, struct device *child)
|
|
|
|
{
|
|
|
|
struct device *c = parent->children;
|
|
|
|
if (c) {
|
|
|
|
while (c->sibling)
|
|
|
|
c = c->sibling;
|
|
|
|
c->sibling = child;
|
|
|
|
} else
|
|
|
|
parent->children = child;
|
|
|
|
|
|
|
|
child->sibling = NULL;
|
|
|
|
child->parent = parent;
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:20:07 +02:00
|
|
|
static const struct device *find_alias(const struct device *const parent,
|
|
|
|
const char *const alias)
|
|
|
|
{
|
|
|
|
if (parent->alias && !strcmp(parent->alias, alias))
|
|
|
|
return parent;
|
|
|
|
|
|
|
|
const struct bus *bus;
|
|
|
|
for (bus = parent->bus; bus; bus = bus->next_bus) {
|
|
|
|
const struct device *child;
|
|
|
|
for (child = bus->children; child; child = child->sibling) {
|
|
|
|
const struct device *const ret = find_alias(child, alias);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-07-30 01:28:43 +02:00
|
|
|
static struct device *new_device_with_path(struct bus *parent,
|
|
|
|
struct chip_instance *chip_instance,
|
|
|
|
const int bustype, int path_a, int path_b,
|
|
|
|
char *alias, int status)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *new_d;
|
|
|
|
|
|
|
|
/* If device is found under parent, no need to allocate new device. */
|
|
|
|
new_d = get_dev(parent, path_a, path_b, bustype, chip_instance);
|
|
|
|
if (new_d) {
|
|
|
|
alloc_bus(new_d);
|
|
|
|
return new_d;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
new_d = alloc_dev(parent);
|
|
|
|
|
|
|
|
new_d->bustype = bustype;
|
|
|
|
|
|
|
|
new_d->path_a = path_a;
|
|
|
|
new_d->path_b = path_b;
|
2020-06-03 19:20:07 +02:00
|
|
|
new_d->alias = alias;
|
2018-06-03 13:22:17 +02:00
|
|
|
|
2018-09-10 04:51:26 +02:00
|
|
|
new_d->enabled = status & 0x01;
|
|
|
|
new_d->hidden = (status >> 1) & 0x01;
|
Add configurable ramstage support for minimal PCI scanning
This CL has changes that allow us to enable a configurable
ramstage, and one change that allows us to minimize PCI
scanning. Minimal scanning is a frequently requested feature.
To enable it, we add two new variables to src/Kconfig
CONFIGURABLE_RAMSTAGE
is the overall variable controlling other options for minimizing the
ramstage.
MINIMAL_PCI_SCANNING is how we indicate we wish to enable minimal
PCI scanning.
Some devices must be scanned in all cases, such as 0:0.0.
To indicate which devices we must scan, we add a new mandatory
keyword to sconfig
It is used in place of on, off, or hidden, and indicates
a device is enabled and mandatory. Mandatory
devices are always scanned. When MINIMAL_PCI_SCANNING is enabled,
ONLY mandatory devices are scanned.
We further add support in src/device/pci_device.c to manage
both MINIMAL_PCI_SCANNING and mandatory devices.
Finally, to show how this works in practice, we add mandatory
keywords to 3 devices on the qemu-q35.
TEST=
1. This is tested and working on the qemu-q35 target.
2. On CML-Hatch
Before CL:
Total Boot time: ~685ms
After CL:
Total Boot time: ~615ms
Change-Id: I2073d9f8e9297c2b02530821ebb634ea2a5c758e
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36221
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jeremy Soller <jeremy@system76.com>
2019-10-22 04:02:24 +02:00
|
|
|
new_d->mandatory = (status >> 2) & 0x01;
|
2018-05-31 19:33:16 +02:00
|
|
|
new_d->chip_instance = chip_instance;
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
set_new_child(parent, new_d);
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2018-05-31 16:52:00 +02:00
|
|
|
switch (bustype) {
|
2012-06-21 22:19:48 +02:00
|
|
|
case PCI:
|
2010-05-05 13:19:50 +02:00
|
|
|
new_d->path = ".type=DEVICE_PATH_PCI,{.pci={ .devfn = PCI_DEVFN(0x%x,%d)}}";
|
2012-06-21 22:19:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PNP:
|
2010-05-05 13:19:50 +02:00
|
|
|
new_d->path = ".type=DEVICE_PATH_PNP,{.pnp={ .port = 0x%x, .device = 0x%x }}";
|
2012-06-21 22:19:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case I2C:
|
2016-05-08 04:49:37 +02:00
|
|
|
new_d->path = ".type=DEVICE_PATH_I2C,{.i2c={ .device = 0x%x, .mode_10bit = %d }}";
|
2012-06-21 22:19:48 +02:00
|
|
|
break;
|
|
|
|
|
2013-02-13 00:20:54 +01:00
|
|
|
case CPU_CLUSTER:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_CPU_CLUSTER,{.cpu_cluster={ .cluster = 0x%x }}";
|
2012-06-21 22:19:48 +02:00
|
|
|
break;
|
|
|
|
|
2014-09-03 19:40:15 +02:00
|
|
|
case CPU:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_CPU,{.cpu={ .id = 0x%x }}";
|
|
|
|
break;
|
|
|
|
|
2013-02-12 23:17:15 +01:00
|
|
|
case DOMAIN:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_DOMAIN,{.domain={ .domain = 0x%x }}";
|
2012-06-21 22:19:48 +02:00
|
|
|
break;
|
|
|
|
|
2016-05-08 05:01:34 +02:00
|
|
|
case GENERIC:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_GENERIC,{.generic={ .id = 0x%x, .subid = 0x%x }}";
|
|
|
|
break;
|
2017-02-11 09:50:38 +01:00
|
|
|
|
|
|
|
case SPI:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_SPI,{.spi={ .cs = 0x%x }}";
|
|
|
|
break;
|
|
|
|
|
2018-05-07 23:18:13 +02:00
|
|
|
case USB:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_USB,{.usb={ .port_type = %d, .port_id = %d }}";
|
|
|
|
break;
|
|
|
|
|
2018-01-18 01:36:30 +01:00
|
|
|
case MMIO:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_MMIO,{.mmio={ .addr = 0x%x }}";
|
|
|
|
break;
|
2020-05-06 19:47:04 +02:00
|
|
|
|
2020-12-11 21:26:02 +01:00
|
|
|
case GPIO:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_GPIO,{.gpio={ .id = 0x%x }}";
|
|
|
|
break;
|
2022-11-02 15:57:10 +01:00
|
|
|
|
|
|
|
case MDIO:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_MDIO,{.mdio={ .addr = 0x%x }}";
|
|
|
|
break;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
2018-01-18 01:36:30 +01:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
return new_d;
|
|
|
|
}
|
|
|
|
|
2020-07-30 01:28:43 +02:00
|
|
|
struct device *new_device_reference(struct bus *parent,
|
|
|
|
struct chip_instance *chip_instance,
|
|
|
|
const char *reference, int status)
|
|
|
|
{
|
|
|
|
const struct device *dev = find_alias(&base_root_dev, reference);
|
|
|
|
|
|
|
|
if (!dev) {
|
|
|
|
printf("ERROR: Unable to find device reference %s\n", reference);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_device_with_path(parent, chip_instance, dev->bustype, dev->path_a,
|
|
|
|
dev->path_b, NULL, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct device *new_device_raw(struct bus *parent,
|
|
|
|
struct chip_instance *chip_instance,
|
|
|
|
const int bustype, const char *devnum,
|
|
|
|
char *alias, int status)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
int path_a;
|
|
|
|
int path_b = 0;
|
|
|
|
|
|
|
|
/* Check for alias name conflicts. */
|
|
|
|
if (alias && find_alias(root_parent->dev, alias)) {
|
|
|
|
printf("ERROR: Alias already exists: %s\n", alias);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
path_a = strtol(devnum, &tmp, 16);
|
|
|
|
if (*tmp == '.') {
|
|
|
|
tmp++;
|
|
|
|
path_b = strtol(tmp, NULL, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_device_with_path(parent, chip_instance, bustype, path_a, path_b, alias,
|
|
|
|
status);
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
static void new_resource(struct device *dev, int type, int index, int base)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-05-31 18:48:51 +02:00
|
|
|
struct resource *r = S_ALLOC(sizeof(struct resource));
|
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
r->type = type;
|
|
|
|
r->index = index;
|
|
|
|
r->base = base;
|
2010-05-05 14:05:25 +02:00
|
|
|
if (dev->res) {
|
|
|
|
struct resource *head = dev->res;
|
2016-08-06 02:32:18 +02:00
|
|
|
while (head->next)
|
|
|
|
head = head->next;
|
2010-05-05 13:19:50 +02:00
|
|
|
head->next = r;
|
|
|
|
} else {
|
2010-05-05 14:05:25 +02:00
|
|
|
dev->res = r;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
void add_resource(struct bus *bus, int type, int index, int base)
|
|
|
|
{
|
|
|
|
new_resource(bus->dev, type, index, base);
|
|
|
|
}
|
|
|
|
|
2020-05-26 22:13:09 +02:00
|
|
|
static void add_reg(struct reg **const head, char *const name, char *const val)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2020-05-26 22:13:09 +02:00
|
|
|
struct reg *const r = S_ALLOC(sizeof(struct reg));
|
|
|
|
struct reg *prev = NULL;
|
|
|
|
struct reg *cur;
|
2018-05-31 18:48:51 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
r->key = name;
|
|
|
|
r->value = val;
|
2020-05-26 22:13:09 +02:00
|
|
|
|
|
|
|
for (cur = *head; cur != NULL; prev = cur, cur = cur->next) {
|
|
|
|
const int sort = strcmp(r->key, cur->key);
|
2010-05-05 13:19:50 +02:00
|
|
|
if (sort == 0) {
|
2020-11-02 18:08:14 +01:00
|
|
|
printf("ERROR: duplicate 'register' key '%s'.\n", r->key);
|
2010-05-05 13:19:50 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
2020-05-26 22:13:09 +02:00
|
|
|
if (sort < 0)
|
|
|
|
break;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
2020-05-26 22:13:09 +02:00
|
|
|
r->next = cur;
|
|
|
|
if (prev)
|
|
|
|
prev->next = r;
|
|
|
|
else
|
|
|
|
*head = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_register(struct chip_instance *chip_instance, char *name, char *val)
|
|
|
|
{
|
|
|
|
add_reg(&chip_instance->reg, name, val);
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
|
2020-06-03 19:20:07 +02:00
|
|
|
void add_reference(struct chip_instance *const chip_instance,
|
|
|
|
char *const name, char *const alias)
|
|
|
|
{
|
|
|
|
add_reg(&chip_instance->ref, name, alias);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_reference(struct chip_instance *const chip_instance,
|
|
|
|
char *const name, char *const alias)
|
|
|
|
{
|
|
|
|
const struct device *const dev = find_alias(&base_root_dev, alias);
|
|
|
|
if (!dev) {
|
|
|
|
printf("ERROR: Cannot find device alias '%s'.\n", alias);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *const ref_name = S_ALLOC(strlen(dev->name) + 2);
|
|
|
|
sprintf(ref_name, "&%s", dev->name);
|
|
|
|
add_register(chip_instance, name, ref_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_references(FILE *file, FILE *head, struct device *dev,
|
|
|
|
struct device *next)
|
|
|
|
{
|
|
|
|
struct reg *ref;
|
|
|
|
|
|
|
|
for (ref = dev->chip_instance->ref; ref; ref = ref->next)
|
|
|
|
set_reference(dev->chip_instance, ref->key, ref->value);
|
|
|
|
}
|
|
|
|
|
2019-04-12 14:42:17 +02:00
|
|
|
void add_slot_desc(struct bus *bus, char *type, char *length, char *designation,
|
|
|
|
char *data_width)
|
|
|
|
{
|
|
|
|
struct device *dev = bus->dev;
|
|
|
|
|
|
|
|
if (dev->bustype != PCI && dev->bustype != DOMAIN) {
|
|
|
|
printf("ERROR: 'slot_type' only allowed for PCI devices\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->smbios_slot_type = type;
|
|
|
|
dev->smbios_slot_length = length;
|
|
|
|
dev->smbios_slot_data_width = data_width;
|
|
|
|
dev->smbios_slot_designation = designation;
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:51:40 +02:00
|
|
|
void add_smbios_dev_info(struct bus *bus, long instance_id, const char *refdes)
|
|
|
|
{
|
|
|
|
struct device *dev = bus->dev;
|
|
|
|
|
|
|
|
if (dev->bustype != PCI && dev->bustype != DOMAIN) {
|
|
|
|
printf("ERROR: 'dev_info' only allowed for PCI devices\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instance_id < 0 || instance_id > UINT8_MAX) {
|
|
|
|
printf("ERROR: SMBIOS dev info instance ID '%ld' out of range\n", instance_id);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->smbios_instance_id_valid = 1;
|
|
|
|
dev->smbios_instance_id = (unsigned int)instance_id;
|
|
|
|
dev->smbios_refdes = refdes;
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
void add_pci_subsystem_ids(struct bus *bus, int vendor, int device,
|
2016-08-06 02:32:18 +02:00
|
|
|
int inherit)
|
2011-03-01 20:58:15 +01:00
|
|
|
{
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *dev = bus->dev;
|
|
|
|
|
2013-02-12 23:17:15 +01:00
|
|
|
if (dev->bustype != PCI && dev->bustype != DOMAIN) {
|
2011-03-01 20:58:15 +01:00
|
|
|
printf("ERROR: 'subsystem' only allowed for PCI devices\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->subsystem_vendor = vendor;
|
|
|
|
dev->subsystem_device = device;
|
|
|
|
dev->inherit_subsystem = inherit;
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
static int dev_has_children(struct device *dev)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-06-03 13:22:17 +02:00
|
|
|
struct bus *bus = dev->bus;
|
|
|
|
|
|
|
|
while (bus) {
|
|
|
|
if (bus->children)
|
|
|
|
return 1;
|
|
|
|
bus = bus->next_bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
static void pass0(FILE *fil, FILE *head, struct device *ptr, struct device *next)
|
2018-06-03 13:22:17 +02:00
|
|
|
{
|
2020-05-03 00:34:42 +02:00
|
|
|
static int dev_id;
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
if (ptr == &base_root_dev) {
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct bus %s_links[];\n",
|
2016-08-06 02:32:18 +02:00
|
|
|
ptr->name);
|
2018-06-03 13:22:17 +02:00
|
|
|
return;
|
|
|
|
}
|
Make the device tree available in the rom stage
We thought about two ways to do this change. The way we decided to try
was to
1. drop all ops from devices in romstage
2. constify all devices in romstage (make them read-only) so we can
compile static.c into romstage
3. the device tree "devices" can be used to read configuration from
the device tree (and nothing else, really)
4. the device tree devices are accessed through struct device * in
romstage only. device_t stays the typedef to int in romstage
5. Use the same static.c file in ramstage and romstage
We declare structs as follows:
ROMSTAGE_CONST struct bus dev_root_links[];
ROMSTAGE_CONST is const in romstage and empty in ramstage; This
forces all of the device tree into the text area.
So a struct looks like this:
static ROMSTAGE_CONST struct device _dev21 = {
#ifndef __PRE_RAM__
.ops = 0,
#endif
.bus = &_dev7_links[0],
.path = {.type=DEVICE_PATH_PCI,{.pci={ .devfn = PCI_DEVFN(0x1c,3)}}},
.enabled = 0,
.on_mainboard = 1,
.subsystem_vendor = 0x1ae0,
.subsystem_device = 0xc000,
.link_list = NULL,
.sibling = &_dev22,
#ifndef __PRE_RAM__
.chip_ops = &southbridge_intel_bd82x6x_ops,
#endif
.chip_info = &southbridge_intel_bd82x6x_info_10,
.next=&_dev22
};
Change-Id: I722454d8d3c40baf7df989f5a6891f6ba7db5727
Signed-off-by: Ronald G. Minnich <rminnich@chromium.org>
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Reviewed-on: http://review.coreboot.org/1398
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
2012-08-01 01:47:25 +02:00
|
|
|
|
2021-09-17 01:05:56 +02:00
|
|
|
char *name;
|
|
|
|
|
|
|
|
if (ptr->alias) {
|
|
|
|
name = S_ALLOC(6 + strlen(ptr->alias));
|
|
|
|
sprintf(name, "_dev_%s", ptr->alias);
|
|
|
|
} else {
|
|
|
|
name = S_ALLOC(11);
|
|
|
|
sprintf(name, "_dev_%d", dev_id++);
|
|
|
|
}
|
|
|
|
|
2020-05-03 00:34:42 +02:00
|
|
|
ptr->name = name;
|
|
|
|
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct device %s;\n", ptr->name);
|
2018-06-08 08:36:45 +02:00
|
|
|
if (ptr->res)
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct resource %s_res[];\n",
|
2016-08-06 02:32:18 +02:00
|
|
|
ptr->name);
|
2018-06-03 13:22:17 +02:00
|
|
|
if (dev_has_children(ptr))
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct bus %s_links[];\n",
|
2018-06-03 13:22:17 +02:00
|
|
|
ptr->name);
|
|
|
|
|
|
|
|
if (next)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fprintf(fil,
|
|
|
|
"DEVTREE_CONST struct device * DEVTREE_CONST last_dev = &%s;\n",
|
|
|
|
ptr->name);
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:55:25 +02:00
|
|
|
static void emit_smbios_data(FILE *fil, struct device *ptr)
|
|
|
|
{
|
|
|
|
fprintf(fil, "#if !DEVTREE_EARLY\n");
|
|
|
|
fprintf(fil, "#if CONFIG(GENERATE_SMBIOS_TABLES)\n");
|
|
|
|
|
|
|
|
/* SMBIOS types start at 1, if zero it hasn't been set */
|
|
|
|
if (ptr->smbios_slot_type)
|
|
|
|
fprintf(fil, "\t.smbios_slot_type = %s,\n",
|
|
|
|
ptr->smbios_slot_type);
|
|
|
|
if (ptr->smbios_slot_data_width)
|
|
|
|
fprintf(fil, "\t.smbios_slot_data_width = %s,\n",
|
|
|
|
ptr->smbios_slot_data_width);
|
|
|
|
if (ptr->smbios_slot_designation)
|
|
|
|
fprintf(fil, "\t.smbios_slot_designation = \"%s\",\n",
|
|
|
|
ptr->smbios_slot_designation);
|
|
|
|
if (ptr->smbios_slot_length)
|
|
|
|
fprintf(fil, "\t.smbios_slot_length = %s,\n",
|
|
|
|
ptr->smbios_slot_length);
|
|
|
|
|
2021-09-03 16:51:40 +02:00
|
|
|
/* Fill in SMBIOS type41 fields */
|
|
|
|
if (ptr->smbios_instance_id_valid) {
|
|
|
|
fprintf(fil, "\t.smbios_instance_id_valid = true,\n");
|
|
|
|
fprintf(fil, "\t.smbios_instance_id = %u,\n", ptr->smbios_instance_id);
|
|
|
|
if (ptr->smbios_refdes)
|
|
|
|
fprintf(fil, "\t.smbios_refdes = \"%s\",\n", ptr->smbios_refdes);
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:55:25 +02:00
|
|
|
fprintf(fil, "#endif\n");
|
|
|
|
fprintf(fil, "#endif\n");
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
static void emit_resources(FILE *fil, struct device *ptr)
|
|
|
|
{
|
2018-06-08 08:36:45 +02:00
|
|
|
if (ptr->res == NULL)
|
2018-06-03 13:22:17 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
int i = 1;
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct resource %s_res[] = {\n", ptr->name);
|
2018-06-03 13:22:17 +02:00
|
|
|
struct resource *r = ptr->res;
|
|
|
|
while (r) {
|
|
|
|
fprintf(fil,
|
|
|
|
"\t\t{ .flags=IORESOURCE_FIXED | IORESOURCE_ASSIGNED | IORESOURCE_");
|
|
|
|
if (r->type == IRQ)
|
|
|
|
fprintf(fil, "IRQ");
|
|
|
|
if (r->type == DRQ)
|
|
|
|
fprintf(fil, "DRQ");
|
|
|
|
if (r->type == IO)
|
|
|
|
fprintf(fil, "IO");
|
|
|
|
fprintf(fil, ", .index=0x%x, .base=0x%x,", r->index,
|
|
|
|
r->base);
|
|
|
|
if (r->next)
|
|
|
|
fprintf(fil, ".next=&%s_res[%d]},\n", ptr->name,
|
|
|
|
i++);
|
|
|
|
else
|
|
|
|
fprintf(fil, ".next=NULL },\n");
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fil, "\t };\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void emit_bus(FILE *fil, struct bus *bus)
|
|
|
|
{
|
|
|
|
fprintf(fil, "\t\t[%d] = {\n", bus->id);
|
|
|
|
fprintf(fil, "\t\t\t.link_num = %d,\n", bus->id);
|
|
|
|
fprintf(fil, "\t\t\t.dev = &%s,\n", bus->dev->name);
|
|
|
|
if (bus->children)
|
|
|
|
fprintf(fil, "\t\t\t.children = &%s,\n", bus->children->name);
|
|
|
|
|
|
|
|
if (bus->next_bus)
|
|
|
|
fprintf(fil, "\t\t\t.next=&%s_links[%d],\n", bus->dev->name,
|
|
|
|
bus->id + 1);
|
|
|
|
else
|
|
|
|
fprintf(fil, "\t\t\t.next = NULL,\n");
|
|
|
|
fprintf(fil, "\t\t},\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void emit_dev_links(FILE *fil, struct device *ptr)
|
|
|
|
{
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct bus %s_links[] = {\n",
|
2018-06-03 13:22:17 +02:00
|
|
|
ptr->name);
|
|
|
|
|
|
|
|
struct bus *bus = ptr->bus;
|
|
|
|
|
|
|
|
while (bus) {
|
|
|
|
emit_bus(fil, bus);
|
|
|
|
bus = bus->next_bus;
|
2010-05-21 16:33:48 +02:00
|
|
|
}
|
2018-06-03 13:22:17 +02:00
|
|
|
|
|
|
|
fprintf(fil, "\t};\n");
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
|
2021-01-07 07:36:59 +01:00
|
|
|
static struct chip_instance *get_chip_instance(const struct device *dev)
|
2012-06-21 22:19:48 +02:00
|
|
|
{
|
2021-01-07 07:36:59 +01:00
|
|
|
struct chip_instance *chip_ins = dev->chip_instance;
|
util/sconfig: Drop use of ref_count for chip_instance
chip_instance structure currently uses a ref_count to determine how
many devices hold reference to that instance. If the count drops to
zero, then it is assumed that the chip instance is a duplicate in
override tree and has a similar instance that is already overriden in
base device tree.
ref_count is currently decremented whenever a device in override tree
matches the one in base device tree and the registers from the
override tree instance are copied over to the base tree instance. On
the other hand, if a device in override tree does not match any device
in base tree under a given parent, then the device is added to base
tree and all the devices in its subtree that hold pointers to its
parent chip instance are updated to point to the parent's chip
instance in base tree. This is done as part of update_chip_pointers.
However, there are a couple of issues that this suffers from:
a) If a device is present only in override tree and it does not have
its own chip (i.e. pointing to parent's chip instance), then it
results in sconfig emiiting parent's chip instance (which can be the
SoC chip instance) in static.c even though it is unused. This is
because update_chip_pointers() does not call delete_chip_instance()
before reassigning the chip instance pointer.
b) If a device is added under root device only in the override tree
and it does not have its own chip instance (i.e. uses SoC chip
instance), then it results in sconfig emitting a copy of the SoC chip
instance and setting that as chip_ops for this new device in the
override tree.
In order to fix the above issues, this change drops the ref_count
field from chip_instance structure and instead adds a forwarding
pointer `base_chip_instance`. This is setup as per the following
rules:
1. If the instance belongs to base devicetree, base_chip_instance is
set to NULL.
2. If the instance belongs to override tree, then it is set to its
corresponding chip instance in base tree (if present), else set to
NULL.
State of base_chip_instance is then used when emitting chips and
devices using the following rules:
1. If a chip_instance has non-NULL base_chip_instance, then that chip
instance is not emitted to static.c
2. When emitting chip_ops for a device, base_chip_instance is used to
determine the correct chip instance name to emit.
BUG=b:155549176
TEST=Verified that the static.c file generated for base/override tree
combination is correct when new devices without chips are added only
to override tree.
Change-Id: Idbb5b34f49bf874da3f30ebb6a6a0e2d8d091fe5
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41007
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-05-03 01:05:29 +02:00
|
|
|
/*
|
|
|
|
* If the chip instance of device has base_chip_instance pointer set, then follow that
|
|
|
|
* to update the chip instance for current device.
|
|
|
|
*/
|
|
|
|
if (chip_ins->base_chip_instance)
|
|
|
|
chip_ins = chip_ins->base_chip_instance;
|
|
|
|
|
2021-01-07 07:36:59 +01:00
|
|
|
return chip_ins;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pass1(FILE *fil, FILE *head, struct device *ptr, struct device *next)
|
|
|
|
{
|
|
|
|
struct chip_instance *chip_ins = get_chip_instance(ptr);
|
|
|
|
int has_children = dev_has_children(ptr);
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
/* Emit probe structures. */
|
|
|
|
if (ptr->probe && (emit_fw_config_probe(fil, ptr) < 0)) {
|
2020-09-23 23:36:30 +02:00
|
|
|
if (head)
|
|
|
|
fclose(head);
|
2020-05-16 00:39:08 +02:00
|
|
|
fclose(fil);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-03-15 09:04:11 +01:00
|
|
|
if (ptr == &base_root_dev)
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct device %s = {\n", ptr->name);
|
|
|
|
else
|
|
|
|
fprintf(fil, "STORAGE struct device %s = {\n", ptr->name);
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "#if !DEVTREE_EARLY\n");
|
2018-06-23 09:59:31 +02:00
|
|
|
|
|
|
|
/*
|
2022-08-06 19:02:59 +02:00
|
|
|
* ops field can be set in the devicetree. If unspecified, it is set
|
|
|
|
* to default_dev_ops_root only for the root device, other devices
|
|
|
|
* get it set by the driver at runtime.
|
2018-06-23 09:59:31 +02:00
|
|
|
*/
|
2022-08-06 19:02:59 +02:00
|
|
|
if (ptr->ops_id)
|
|
|
|
fprintf(fil, "\t.ops = &%s,\n", ptr->ops_id);
|
|
|
|
else if (ptr == &base_root_dev)
|
2018-06-23 09:59:31 +02:00
|
|
|
fprintf(fil, "\t.ops = &default_dev_ops_root,\n");
|
|
|
|
else
|
|
|
|
fprintf(fil, "\t.ops = NULL,\n");
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "#endif\n");
|
|
|
|
fprintf(fil, "\t.bus = &%s_links[%d],\n", ptr->parent->dev->name,
|
|
|
|
ptr->parent->id);
|
|
|
|
fprintf(fil, "\t.path = {");
|
|
|
|
fprintf(fil, ptr->path, ptr->path_a, ptr->path_b);
|
|
|
|
fprintf(fil, "},\n");
|
|
|
|
fprintf(fil, "\t.enabled = %d,\n", ptr->enabled);
|
2018-09-10 04:51:26 +02:00
|
|
|
fprintf(fil, "\t.hidden = %d,\n", ptr->hidden);
|
Add configurable ramstage support for minimal PCI scanning
This CL has changes that allow us to enable a configurable
ramstage, and one change that allows us to minimize PCI
scanning. Minimal scanning is a frequently requested feature.
To enable it, we add two new variables to src/Kconfig
CONFIGURABLE_RAMSTAGE
is the overall variable controlling other options for minimizing the
ramstage.
MINIMAL_PCI_SCANNING is how we indicate we wish to enable minimal
PCI scanning.
Some devices must be scanned in all cases, such as 0:0.0.
To indicate which devices we must scan, we add a new mandatory
keyword to sconfig
It is used in place of on, off, or hidden, and indicates
a device is enabled and mandatory. Mandatory
devices are always scanned. When MINIMAL_PCI_SCANNING is enabled,
ONLY mandatory devices are scanned.
We further add support in src/device/pci_device.c to manage
both MINIMAL_PCI_SCANNING and mandatory devices.
Finally, to show how this works in practice, we add mandatory
keywords to 3 devices on the qemu-q35.
TEST=
1. This is tested and working on the qemu-q35 target.
2. On CML-Hatch
Before CL:
Total Boot time: ~685ms
After CL:
Total Boot time: ~615ms
Change-Id: I2073d9f8e9297c2b02530821ebb634ea2a5c758e
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36221
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jeremy Soller <jeremy@system76.com>
2019-10-22 04:02:24 +02:00
|
|
|
fprintf(fil, "\t.mandatory = %d,\n", ptr->mandatory);
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "\t.on_mainboard = 1,\n");
|
|
|
|
if (ptr->subsystem_vendor > 0)
|
|
|
|
fprintf(fil, "\t.subsystem_vendor = 0x%04x,\n",
|
|
|
|
ptr->subsystem_vendor);
|
|
|
|
|
|
|
|
if (ptr->subsystem_device > 0)
|
|
|
|
fprintf(fil, "\t.subsystem_device = 0x%04x,\n",
|
|
|
|
ptr->subsystem_device);
|
|
|
|
|
2018-06-08 08:36:45 +02:00
|
|
|
if (ptr->res) {
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "\t.resource_list = &%s_res[0],\n",
|
2016-08-06 02:32:18 +02:00
|
|
|
ptr->name);
|
2010-05-21 16:33:48 +02:00
|
|
|
}
|
2018-06-03 13:22:17 +02:00
|
|
|
if (has_children)
|
|
|
|
fprintf(fil, "\t.link_list = &%s_links[0],\n",
|
2016-08-06 02:32:18 +02:00
|
|
|
ptr->name);
|
2018-06-03 13:22:17 +02:00
|
|
|
else
|
|
|
|
fprintf(fil, "\t.link_list = NULL,\n");
|
|
|
|
if (ptr->sibling)
|
|
|
|
fprintf(fil, "\t.sibling = &%s,\n", ptr->sibling->name);
|
2020-03-26 19:29:35 +01:00
|
|
|
else
|
|
|
|
fprintf(fil, "\t.sibling = NULL,\n");
|
2020-05-16 00:39:08 +02:00
|
|
|
if (ptr->probe)
|
|
|
|
fprintf(fil, "\t.probe_list = %s_probe_list,\n", ptr->name);
|
2021-05-22 16:36:58 +02:00
|
|
|
fprintf(fil, "#if !DEVTREE_EARLY\n");
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "\t.chip_ops = &%s_ops,\n",
|
|
|
|
chip_ins->chip->name_underscore);
|
|
|
|
if (chip_ins == &mainboard_instance)
|
|
|
|
fprintf(fil, "\t.name = mainboard_name,\n");
|
|
|
|
fprintf(fil, "#endif\n");
|
|
|
|
if (chip_ins->chip->chiph_exists)
|
|
|
|
fprintf(fil, "\t.chip_info = &%s_info_%d,\n",
|
|
|
|
chip_ins->chip->name_underscore, chip_ins->id);
|
|
|
|
if (next)
|
2019-04-12 14:42:17 +02:00
|
|
|
fprintf(fil, "\t.next=&%s,\n", next->name);
|
2021-09-03 16:55:25 +02:00
|
|
|
|
|
|
|
emit_smbios_data(fil, ptr);
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "};\n");
|
|
|
|
|
|
|
|
emit_resources(fil, ptr);
|
|
|
|
|
|
|
|
if (has_children)
|
|
|
|
emit_dev_links(fil, ptr);
|
|
|
|
}
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
static void expose_device_names(FILE *fil, FILE *head, struct device *ptr, struct device *next)
|
2019-03-15 16:44:23 +01:00
|
|
|
{
|
2021-01-07 07:36:59 +01:00
|
|
|
struct chip_instance *chip_ins = get_chip_instance(ptr);
|
|
|
|
|
2019-03-15 16:44:23 +01:00
|
|
|
/* Only devices on root bus here. */
|
2019-09-20 12:05:51 +02:00
|
|
|
if (ptr->bustype == PCI && ptr->parent->dev->bustype == DOMAIN) {
|
2021-01-08 18:54:54 +01:00
|
|
|
fprintf(head, "extern DEVTREE_CONST struct device *const __pci_0_%02x_%d;\n",
|
2019-09-20 12:05:51 +02:00
|
|
|
ptr->path_a, ptr->path_b);
|
2021-01-08 18:54:54 +01:00
|
|
|
fprintf(fil, "DEVTREE_CONST struct device *const __pci_0_%02x_%d = &%s;\n",
|
2019-03-15 16:44:23 +01:00
|
|
|
ptr->path_a, ptr->path_b, ptr->name);
|
2021-01-07 07:36:59 +01:00
|
|
|
|
|
|
|
if (chip_ins->chip->chiph_exists) {
|
|
|
|
fprintf(head, "extern DEVTREE_CONST void *const __pci_0_%02x_%d_config;\n",
|
|
|
|
ptr->path_a, ptr->path_b);
|
|
|
|
fprintf(fil, "DEVTREE_CONST void *const __pci_0_%02x_%d_config = &%s_info_%d;\n",
|
|
|
|
ptr->path_a, ptr->path_b, chip_ins->chip->name_underscore, chip_ins->id);
|
|
|
|
}
|
2019-09-20 12:05:51 +02:00
|
|
|
}
|
2019-03-15 16:44:23 +01:00
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
if (ptr->bustype == PNP) {
|
2021-01-08 18:54:54 +01:00
|
|
|
fprintf(head, "extern DEVTREE_CONST struct device *const __pnp_%04x_%02x;\n",
|
2019-09-20 12:05:51 +02:00
|
|
|
ptr->path_a, ptr->path_b);
|
2021-01-08 18:54:54 +01:00
|
|
|
fprintf(fil, "DEVTREE_CONST struct device *const __pnp_%04x_%02x = &%s;\n",
|
2019-03-15 16:44:23 +01:00
|
|
|
ptr->path_a, ptr->path_b, ptr->name);
|
2019-09-20 12:05:51 +02:00
|
|
|
}
|
2021-09-17 01:05:56 +02:00
|
|
|
|
|
|
|
if (ptr->alias) {
|
|
|
|
fprintf(head, "extern DEVTREE_CONST struct device *const %s_ptr;\n", ptr->name);
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct device *const %s_ptr = &%s;\n",
|
|
|
|
ptr->name, ptr->name);
|
|
|
|
}
|
2019-03-15 16:44:23 +01:00
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
static void add_siblings_to_queue(struct queue_entry **bfs_q_head,
|
|
|
|
struct device *d)
|
|
|
|
{
|
|
|
|
while (d) {
|
|
|
|
enqueue_tail(bfs_q_head, d);
|
|
|
|
d = d->sibling;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_children_to_queue(struct queue_entry **bfs_q_head,
|
|
|
|
struct device *d)
|
|
|
|
{
|
|
|
|
struct bus *bus = d->bus;
|
|
|
|
|
|
|
|
while (bus) {
|
|
|
|
if (bus->children)
|
|
|
|
add_siblings_to_queue(bfs_q_head, bus->children);
|
|
|
|
bus = bus->next_bus;
|
2010-06-10 00:41:35 +02:00
|
|
|
}
|
2018-05-31 00:09:09 +02:00
|
|
|
}
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
static void walk_device_tree(FILE *fil, FILE *head, struct device *ptr,
|
|
|
|
void (*func)(FILE *, FILE *, struct device *,
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *))
|
2018-05-31 00:09:09 +02:00
|
|
|
{
|
2018-06-03 13:22:17 +02:00
|
|
|
struct queue_entry *bfs_q_head = NULL;
|
|
|
|
|
|
|
|
enqueue_tail(&bfs_q_head, ptr);
|
|
|
|
|
|
|
|
while ((ptr = dequeue_head(&bfs_q_head))) {
|
|
|
|
add_children_to_queue(&bfs_q_head, ptr);
|
2019-09-20 12:05:51 +02:00
|
|
|
func(fil, head, ptr, peek_queue_head(bfs_q_head));
|
2018-06-03 13:22:17 +02:00
|
|
|
}
|
2018-05-31 00:09:09 +02:00
|
|
|
}
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
static void emit_chip_headers(FILE *fil, struct chip *chip)
|
2018-05-31 08:46:16 +02:00
|
|
|
{
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip *tmp = chip;
|
2018-05-31 08:46:16 +02:00
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
while (chip) {
|
|
|
|
if (chip->chiph_exists)
|
|
|
|
fprintf(fil, "#include \"%s/chip.h\"\n", chip->name);
|
|
|
|
chip = chip->next;
|
2018-05-31 08:46:16 +02:00
|
|
|
}
|
|
|
|
fprintf(fil, "\n#if !DEVTREE_EARLY\n");
|
|
|
|
fprintf(fil,
|
|
|
|
"__attribute__((weak)) struct chip_operations mainboard_ops = {};\n");
|
2018-05-31 19:33:16 +02:00
|
|
|
|
|
|
|
chip = tmp;
|
|
|
|
while (chip) {
|
2019-08-17 05:28:40 +02:00
|
|
|
/* A lot of cpus do not define chip_operations at all, and the ones
|
|
|
|
that do only initialise .name. */
|
|
|
|
if (strstr(chip->name_underscore, "cpu_") == chip->name_underscore) {
|
|
|
|
fprintf(fil,
|
|
|
|
"__attribute__((weak)) struct chip_operations %s_ops = {};\n",
|
|
|
|
chip->name_underscore);
|
|
|
|
} else {
|
|
|
|
fprintf(fil, "extern struct chip_operations %s_ops;\n",
|
|
|
|
chip->name_underscore);
|
|
|
|
}
|
2018-05-31 19:33:16 +02:00
|
|
|
chip = chip->next;
|
2018-05-31 08:46:16 +02:00
|
|
|
}
|
|
|
|
fprintf(fil, "#endif\n");
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
static void emit_chip_instance(FILE *fil, struct chip_instance *instance)
|
|
|
|
{
|
2019-03-15 09:04:11 +01:00
|
|
|
fprintf(fil, "STORAGE struct %s_config %s_info_%d = {",
|
2018-05-31 19:33:16 +02:00
|
|
|
instance->chip->name_underscore,
|
|
|
|
instance->chip->name_underscore,
|
|
|
|
instance->id);
|
|
|
|
|
|
|
|
if (instance->reg) {
|
|
|
|
fprintf(fil, "\n");
|
|
|
|
struct reg *r = instance->reg;
|
|
|
|
while (r) {
|
|
|
|
fprintf(fil, "\t.%s = %s,\n", r->key, r->value);
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(fil, "};\n\n");
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:20:07 +02:00
|
|
|
static void emit_chip_configs(FILE *fil)
|
2018-05-31 00:09:09 +02:00
|
|
|
{
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip *chip = chip_header.next;
|
|
|
|
struct chip_instance *instance;
|
2020-05-03 00:51:02 +02:00
|
|
|
int chip_id;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
for (; chip; chip = chip->next) {
|
2018-05-31 00:09:09 +02:00
|
|
|
if (!chip->chiph_exists)
|
|
|
|
continue;
|
|
|
|
|
2020-05-03 00:51:02 +02:00
|
|
|
chip_id = 1;
|
2018-05-31 19:33:16 +02:00
|
|
|
instance = chip->instance;
|
|
|
|
while (instance) {
|
util/sconfig: Drop use of ref_count for chip_instance
chip_instance structure currently uses a ref_count to determine how
many devices hold reference to that instance. If the count drops to
zero, then it is assumed that the chip instance is a duplicate in
override tree and has a similar instance that is already overriden in
base device tree.
ref_count is currently decremented whenever a device in override tree
matches the one in base device tree and the registers from the
override tree instance are copied over to the base tree instance. On
the other hand, if a device in override tree does not match any device
in base tree under a given parent, then the device is added to base
tree and all the devices in its subtree that hold pointers to its
parent chip instance are updated to point to the parent's chip
instance in base tree. This is done as part of update_chip_pointers.
However, there are a couple of issues that this suffers from:
a) If a device is present only in override tree and it does not have
its own chip (i.e. pointing to parent's chip instance), then it
results in sconfig emiiting parent's chip instance (which can be the
SoC chip instance) in static.c even though it is unused. This is
because update_chip_pointers() does not call delete_chip_instance()
before reassigning the chip instance pointer.
b) If a device is added under root device only in the override tree
and it does not have its own chip instance (i.e. uses SoC chip
instance), then it results in sconfig emitting a copy of the SoC chip
instance and setting that as chip_ops for this new device in the
override tree.
In order to fix the above issues, this change drops the ref_count
field from chip_instance structure and instead adds a forwarding
pointer `base_chip_instance`. This is setup as per the following
rules:
1. If the instance belongs to base devicetree, base_chip_instance is
set to NULL.
2. If the instance belongs to override tree, then it is set to its
corresponding chip instance in base tree (if present), else set to
NULL.
State of base_chip_instance is then used when emitting chips and
devices using the following rules:
1. If a chip_instance has non-NULL base_chip_instance, then that chip
instance is not emitted to static.c
2. When emitting chip_ops for a device, base_chip_instance is used to
determine the correct chip instance name to emit.
BUG=b:155549176
TEST=Verified that the static.c file generated for base/override tree
combination is correct when new devices without chips are added only
to override tree.
Change-Id: Idbb5b34f49bf874da3f30ebb6a6a0e2d8d091fe5
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41007
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-05-03 01:05:29 +02:00
|
|
|
/*
|
|
|
|
* Emit this chip instance only if there is no forwarding pointer to the
|
|
|
|
* base tree chip instance.
|
|
|
|
*/
|
|
|
|
if (instance->base_chip_instance == NULL) {
|
|
|
|
instance->id = chip_id++;
|
|
|
|
emit_chip_instance(fil, instance);
|
|
|
|
}
|
2018-05-31 19:33:16 +02:00
|
|
|
instance = instance->next;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-06 19:02:59 +02:00
|
|
|
static void emit_identifiers(FILE *fil, const char *decl, const struct identifier *it)
|
|
|
|
{
|
|
|
|
for (; it != NULL; it = it->next)
|
|
|
|
fprintf(fil, "extern %s %s;\n", decl, it->id);
|
|
|
|
}
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
static void inherit_subsystem_ids(FILE *file, FILE *head, struct device *dev,
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *next)
|
2011-03-01 20:58:15 +01:00
|
|
|
{
|
|
|
|
struct device *p;
|
|
|
|
|
|
|
|
if (dev->subsystem_vendor != -1 && dev->subsystem_device != -1) {
|
|
|
|
/* user already gave us a subsystem vendor/device */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
for (p = dev; p && p->parent->dev != p; p = p->parent->dev) {
|
2011-03-01 20:58:15 +01:00
|
|
|
|
2013-02-12 23:17:15 +01:00
|
|
|
if (p->bustype != PCI && p->bustype != DOMAIN)
|
2011-03-01 20:58:15 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (p->inherit_subsystem) {
|
|
|
|
dev->subsystem_vendor = p->subsystem_vendor;
|
|
|
|
dev->subsystem_device = p->subsystem_device;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-05 19:33:55 +01:00
|
|
|
static void usage(void)
|
|
|
|
{
|
2020-06-09 20:20:29 +02:00
|
|
|
printf("usage: sconfig <options>\n");
|
|
|
|
printf(" -c | --output_c : Path to output static.c file (required)\n");
|
|
|
|
printf(" -r | --output_h : Path to header static.h file (required)\n");
|
2020-09-23 23:36:30 +02:00
|
|
|
printf(" -d | --output_d : Path to header static_devices.h file (required)\n");
|
|
|
|
printf(" -f | --output_f : Path to header static_fw_config.h file (required)\n");
|
2020-06-09 20:20:29 +02:00
|
|
|
printf(" -m | --mainboard_devtree : Path to mainboard devicetree file (required)\n");
|
|
|
|
printf(" -o | --override_devtree : Path to override devicetree file (optional)\n");
|
2020-07-30 01:28:43 +02:00
|
|
|
printf(" -p | --chipset_devtree : Path to chipset/SOC devicetree file (optional)\n");
|
2020-09-23 23:36:30 +02:00
|
|
|
|
2016-08-06 02:32:18 +02:00
|
|
|
exit(1);
|
2011-12-05 19:33:55 +01:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
static void parse_devicetree(const char *file, struct bus *parent)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-06-11 13:26:45 +02:00
|
|
|
FILE *filec = fopen(file, "r");
|
2010-08-16 20:21:56 +02:00
|
|
|
if (!filec) {
|
|
|
|
perror(NULL);
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2010-08-16 20:21:56 +02:00
|
|
|
yyrestart(filec);
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
root_parent = parent;
|
|
|
|
linenum = 0;
|
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
yyparse();
|
2010-08-16 20:21:56 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
fclose(filec);
|
2018-06-11 13:26:45 +02:00
|
|
|
}
|
|
|
|
|
2021-08-24 08:23:23 +02:00
|
|
|
static int device_probe_count(struct fw_config_probe *probe)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
while (probe) {
|
|
|
|
probe = probe->next;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When overriding devices, use the following rules:
|
|
|
|
* 1. If probe count matches and:
|
|
|
|
* a. Entire probe list matches for both devices -> Same device, override.
|
|
|
|
* b. No probe entries match -> Different devices, do not override.
|
|
|
|
* c. Partial list matches -> Bad device tree entries, fail build.
|
|
|
|
*
|
|
|
|
* 2. If probe counts do not match and:
|
|
|
|
* a. No probe entries match -> Different devices, do not override.
|
|
|
|
* b. Partial list matches -> Bad device tree entries, fail build.
|
|
|
|
*/
|
|
|
|
static int device_probes_match(struct device *a, struct device *b)
|
|
|
|
{
|
|
|
|
struct fw_config_probe *a_probe = a->probe;
|
|
|
|
struct fw_config_probe *b_probe = b->probe;
|
|
|
|
int a_probe_count = device_probe_count(a_probe);
|
|
|
|
int b_probe_count = device_probe_count(b_probe);
|
|
|
|
int match_count = 0;
|
|
|
|
|
|
|
|
while (a_probe) {
|
|
|
|
if (check_probe_exists(b_probe, a_probe->field, a_probe->option))
|
|
|
|
match_count++;
|
|
|
|
a_probe = a_probe->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((a_probe_count == b_probe_count) && (a_probe_count == match_count))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (match_count) {
|
|
|
|
printf("ERROR: devices with overlapping probes: ");
|
|
|
|
printf(a->path, a->path_a, a->path_b);
|
|
|
|
printf(b->path, b->path_a, b->path_b);
|
|
|
|
printf("\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Match device nodes from base and override tree to see if they are the same
|
|
|
|
* node.
|
|
|
|
*/
|
|
|
|
static int device_match(struct device *a, struct device *b)
|
|
|
|
{
|
|
|
|
return ((a->path_a == b->path_a) &&
|
|
|
|
(a->path_b == b->path_b) &&
|
|
|
|
(a->bustype == b->bustype) &&
|
|
|
|
(a->chip_instance->chip ==
|
|
|
|
b->chip_instance->chip));
|
|
|
|
}
|
|
|
|
|
2019-11-21 11:16:12 +01:00
|
|
|
/*
|
|
|
|
* Match resource nodes from base and override tree to see if they are the same
|
|
|
|
* node.
|
|
|
|
*/
|
|
|
|
static int res_match(struct resource *a, struct resource *b)
|
|
|
|
{
|
|
|
|
return ((a->type == b->type) &&
|
|
|
|
(a->index == b->index));
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Add resource to device. If resource is already present, then update its base
|
|
|
|
* and index. If not, then add a new resource to the device.
|
|
|
|
*/
|
|
|
|
static void update_resource(struct device *dev, struct resource *res)
|
|
|
|
{
|
|
|
|
struct resource *base_res = dev->res;
|
|
|
|
|
|
|
|
while (base_res) {
|
2019-11-21 11:16:12 +01:00
|
|
|
if (res_match(base_res, res)) {
|
2018-06-22 18:19:15 +02:00
|
|
|
base_res->base = res->base;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
base_res = base_res->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_resource(dev, res->type, res->index, res->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add register to chip instance. If register is already present, then update
|
|
|
|
* its value. If not, then add a new register to the chip instance.
|
|
|
|
*/
|
2020-06-03 19:20:07 +02:00
|
|
|
static void update_register(struct reg **const head, struct reg *reg)
|
2018-06-22 18:19:15 +02:00
|
|
|
{
|
2020-06-03 19:20:07 +02:00
|
|
|
struct reg *base_reg = *head;
|
2018-06-22 18:19:15 +02:00
|
|
|
|
|
|
|
while (base_reg) {
|
|
|
|
if (!strcmp(base_reg->key, reg->key)) {
|
|
|
|
base_reg->value = reg->value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
base_reg = base_reg->next;
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:20:07 +02:00
|
|
|
add_reg(head, reg->key, reg->value);
|
2018-06-22 18:19:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void override_devicetree(struct bus *base_parent,
|
|
|
|
struct bus *override_parent);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the base device properties using the properties of override device. In
|
|
|
|
* addition to that, call override_devicetree for all the buses under the
|
|
|
|
* override device.
|
|
|
|
*
|
|
|
|
* Override Rules:
|
|
|
|
* +--------------------+--------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* |struct device member| Rule |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | id | Unchanged. This is used to generate device |
|
|
|
|
* | | structure name in static.c. So, no need to |
|
|
|
|
* | | override. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | enabled | Copy enabled state from override device. |
|
|
|
|
* | | This allows variants to override device |
|
|
|
|
* | | state. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | subsystem_vendor | Copy from override device only if any one |
|
|
|
|
* | subsystem_device | of the ids is non-zero. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | inherit_subsystem | Copy from override device only if it is |
|
|
|
|
* | | non-zero. This allows variant to only |
|
|
|
|
* | | enable inherit flag for a device. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | path | Unchanged since these are same for both |
|
|
|
|
* | path_a | base and override device (Used for |
|
|
|
|
* | path_b | matching devices). |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | bustype | Unchanged since this is same for both base |
|
|
|
|
* | | and override device (User for matching |
|
|
|
|
* | | devices). |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | pci_irq_info | Unchanged. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | parent | Unchanged. This is meaningful only within |
|
|
|
|
* | sibling | the parse tree, hence not being copied. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | res | Each resource that is present in override |
|
|
|
|
* | | device is copied over to base device: |
|
2019-11-21 11:16:12 +01:00
|
|
|
* | | 1. If resource of same type and index is |
|
|
|
|
* | | present in base device, then base of |
|
|
|
|
* | | the resource is copied. |
|
2018-06-22 18:19:15 +02:00
|
|
|
* | | 2. If not, then a new resource is allocated|
|
|
|
|
* | | under the base device using type, index |
|
|
|
|
* | | and base from override res. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
2020-06-03 19:20:07 +02:00
|
|
|
* | ref | Each reference that is present in override |
|
|
|
|
* | | device is copied over to base device with |
|
|
|
|
* | | the same rules as registers. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | alias | Base device alias is copied to override. |
|
|
|
|
* | | Override devices cannot change/remove an |
|
|
|
|
* | | existing alias, but they can add an alias |
|
|
|
|
* | | if one does not exist. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
2021-01-20 07:40:05 +01:00
|
|
|
* | smbios_slot info | Copy SMBIOS slot information from override.|
|
|
|
|
* | | This allows variants to override PCI(e) |
|
|
|
|
* | | slot information in SMBIOS tables. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
2018-06-22 18:19:15 +02:00
|
|
|
* | chip_instance | Each register of chip_instance is copied |
|
|
|
|
* | | over from override device to base device: |
|
|
|
|
* | | 1. If register with same key is present in |
|
|
|
|
* | | base device, then value of the register |
|
|
|
|
* | | is copied. |
|
|
|
|
* | | 2. If not, then a new register is allocated|
|
|
|
|
* | | under the base chip_instance using key |
|
|
|
|
* | | and value from override register. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | bus | Recursively call override_devicetree on |
|
|
|
|
* | last_bus | each bus of override device. It is assumed |
|
|
|
|
* | | that bus with id X under base device |
|
|
|
|
* | | to bus with id X under override device. If |
|
|
|
|
* | | override device has more buses than base |
|
|
|
|
* | | device, then new buses are allocated under |
|
|
|
|
* | | base device. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
static void update_device(struct device *base_dev, struct device *override_dev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Copy the enabled state of override device to base device. This allows
|
|
|
|
* override tree to enable or disable a particular device.
|
|
|
|
*/
|
|
|
|
base_dev->enabled = override_dev->enabled;
|
|
|
|
|
2020-10-28 22:22:34 +01:00
|
|
|
/*
|
|
|
|
* Copy the hidden state of override device to base device. This allows
|
|
|
|
* override tree to hide or unhide a particular device.
|
|
|
|
*/
|
|
|
|
base_dev->hidden = override_dev->hidden;
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Copy subsystem vendor and device ids from override device to base
|
|
|
|
* device only if the ids are non-zero in override device. Else, honor
|
|
|
|
* the values in base device.
|
|
|
|
*/
|
|
|
|
if (override_dev->subsystem_vendor ||
|
|
|
|
override_dev->subsystem_device) {
|
|
|
|
base_dev->subsystem_vendor = override_dev->subsystem_vendor;
|
|
|
|
base_dev->subsystem_device = override_dev->subsystem_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy value of inherity_subsystem from override device to base device
|
|
|
|
* only if it is non-zero in override device. This allows override
|
|
|
|
* tree to only enable inhert flag for a device.
|
|
|
|
*/
|
|
|
|
if (override_dev->inherit_subsystem)
|
|
|
|
base_dev->inherit_subsystem = override_dev->inherit_subsystem;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy resources of override device to base device.
|
|
|
|
* 1. If resource is already present in base device, then index and base
|
|
|
|
* of the resource will be copied over.
|
|
|
|
* 2. If resource is not already present in base device, a new resource
|
|
|
|
* will be allocated.
|
|
|
|
*/
|
|
|
|
struct resource *res = override_dev->res;
|
|
|
|
while (res) {
|
|
|
|
update_resource(base_dev, res);
|
|
|
|
res = res->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy registers of override chip instance to base chip instance.
|
|
|
|
* 1. If register key is already present in base chip instance, then
|
|
|
|
* value for the register is copied over.
|
|
|
|
* 2. If register key is not already present in base chip instance, then
|
|
|
|
* a new register will be allocated.
|
|
|
|
*/
|
|
|
|
struct reg *reg = override_dev->chip_instance->reg;
|
|
|
|
while (reg) {
|
2020-06-03 19:20:07 +02:00
|
|
|
update_register(&base_dev->chip_instance->reg, reg);
|
2018-06-22 18:19:15 +02:00
|
|
|
reg = reg->next;
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:20:07 +02:00
|
|
|
/* Copy references just as with registers. */
|
|
|
|
reg = override_dev->chip_instance->ref;
|
|
|
|
while (reg) {
|
|
|
|
update_register(&base_dev->chip_instance->ref, reg);
|
|
|
|
reg = reg->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for alias name conflicts. */
|
|
|
|
if (override_dev->alias && find_alias(&base_root_dev, override_dev->alias)) {
|
|
|
|
printf("ERROR: alias already exists: %s\n", override_dev->alias);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy alias from base device.
|
|
|
|
*
|
|
|
|
* Override devices cannot change/remove an existing alias,
|
|
|
|
* but they can add an alias to a device if one does not exist yet.
|
|
|
|
*/
|
|
|
|
if (base_dev->alias)
|
|
|
|
override_dev->alias = base_dev->alias;
|
|
|
|
else
|
|
|
|
base_dev->alias = override_dev->alias;
|
|
|
|
|
2020-05-16 00:39:08 +02:00
|
|
|
/*
|
|
|
|
* Use probe list from override device in place of base device, in order
|
|
|
|
* to allow an override to remove a probe from the base device.
|
|
|
|
*/
|
|
|
|
base_dev->probe = override_dev->probe;
|
|
|
|
|
2021-01-20 07:40:05 +01:00
|
|
|
/* Copy SMBIOS slot information from base device */
|
|
|
|
base_dev->smbios_slot_type = override_dev->smbios_slot_type;
|
|
|
|
base_dev->smbios_slot_length = override_dev->smbios_slot_length;
|
|
|
|
base_dev->smbios_slot_data_width = override_dev->smbios_slot_data_width;
|
|
|
|
base_dev->smbios_slot_designation = override_dev->smbios_slot_designation;
|
|
|
|
|
util/sconfig: Drop use of ref_count for chip_instance
chip_instance structure currently uses a ref_count to determine how
many devices hold reference to that instance. If the count drops to
zero, then it is assumed that the chip instance is a duplicate in
override tree and has a similar instance that is already overriden in
base device tree.
ref_count is currently decremented whenever a device in override tree
matches the one in base device tree and the registers from the
override tree instance are copied over to the base tree instance. On
the other hand, if a device in override tree does not match any device
in base tree under a given parent, then the device is added to base
tree and all the devices in its subtree that hold pointers to its
parent chip instance are updated to point to the parent's chip
instance in base tree. This is done as part of update_chip_pointers.
However, there are a couple of issues that this suffers from:
a) If a device is present only in override tree and it does not have
its own chip (i.e. pointing to parent's chip instance), then it
results in sconfig emiiting parent's chip instance (which can be the
SoC chip instance) in static.c even though it is unused. This is
because update_chip_pointers() does not call delete_chip_instance()
before reassigning the chip instance pointer.
b) If a device is added under root device only in the override tree
and it does not have its own chip instance (i.e. uses SoC chip
instance), then it results in sconfig emitting a copy of the SoC chip
instance and setting that as chip_ops for this new device in the
override tree.
In order to fix the above issues, this change drops the ref_count
field from chip_instance structure and instead adds a forwarding
pointer `base_chip_instance`. This is setup as per the following
rules:
1. If the instance belongs to base devicetree, base_chip_instance is
set to NULL.
2. If the instance belongs to override tree, then it is set to its
corresponding chip instance in base tree (if present), else set to
NULL.
State of base_chip_instance is then used when emitting chips and
devices using the following rules:
1. If a chip_instance has non-NULL base_chip_instance, then that chip
instance is not emitted to static.c
2. When emitting chip_ops for a device, base_chip_instance is used to
determine the correct chip instance name to emit.
BUG=b:155549176
TEST=Verified that the static.c file generated for base/override tree
combination is correct when new devices without chips are added only
to override tree.
Change-Id: Idbb5b34f49bf874da3f30ebb6a6a0e2d8d091fe5
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41007
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-05-03 01:05:29 +02:00
|
|
|
/*
|
|
|
|
* Update base_chip_instance member in chip instance of override tree to forward it to
|
|
|
|
* the chip instance in base tree.
|
|
|
|
*/
|
2021-02-18 04:07:18 +01:00
|
|
|
override_dev->chip_instance->base_chip_instance = get_chip_instance(base_dev);
|
util/sconfig: Drop use of ref_count for chip_instance
chip_instance structure currently uses a ref_count to determine how
many devices hold reference to that instance. If the count drops to
zero, then it is assumed that the chip instance is a duplicate in
override tree and has a similar instance that is already overriden in
base device tree.
ref_count is currently decremented whenever a device in override tree
matches the one in base device tree and the registers from the
override tree instance are copied over to the base tree instance. On
the other hand, if a device in override tree does not match any device
in base tree under a given parent, then the device is added to base
tree and all the devices in its subtree that hold pointers to its
parent chip instance are updated to point to the parent's chip
instance in base tree. This is done as part of update_chip_pointers.
However, there are a couple of issues that this suffers from:
a) If a device is present only in override tree and it does not have
its own chip (i.e. pointing to parent's chip instance), then it
results in sconfig emiiting parent's chip instance (which can be the
SoC chip instance) in static.c even though it is unused. This is
because update_chip_pointers() does not call delete_chip_instance()
before reassigning the chip instance pointer.
b) If a device is added under root device only in the override tree
and it does not have its own chip instance (i.e. uses SoC chip
instance), then it results in sconfig emitting a copy of the SoC chip
instance and setting that as chip_ops for this new device in the
override tree.
In order to fix the above issues, this change drops the ref_count
field from chip_instance structure and instead adds a forwarding
pointer `base_chip_instance`. This is setup as per the following
rules:
1. If the instance belongs to base devicetree, base_chip_instance is
set to NULL.
2. If the instance belongs to override tree, then it is set to its
corresponding chip instance in base tree (if present), else set to
NULL.
State of base_chip_instance is then used when emitting chips and
devices using the following rules:
1. If a chip_instance has non-NULL base_chip_instance, then that chip
instance is not emitted to static.c
2. When emitting chip_ops for a device, base_chip_instance is used to
determine the correct chip instance name to emit.
BUG=b:155549176
TEST=Verified that the static.c file generated for base/override tree
combination is correct when new devices without chips are added only
to override tree.
Change-Id: Idbb5b34f49bf874da3f30ebb6a6a0e2d8d091fe5
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41007
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-05-03 01:05:29 +02:00
|
|
|
|
2022-08-06 19:02:59 +02:00
|
|
|
/* Allow to override the ops of a device */
|
|
|
|
if (override_dev->ops_id)
|
|
|
|
base_dev->ops_id = override_dev->ops_id;
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
/*
|
|
|
|
* Now that the device properties are all copied over, look at each bus
|
|
|
|
* of the override device and run override_devicetree in a recursive
|
|
|
|
* manner. The assumption here is that first bus of override device
|
|
|
|
* corresponds to first bus of base device and so on. If base device has
|
|
|
|
* lesser buses than override tree, then new buses are allocated for it.
|
|
|
|
*/
|
|
|
|
struct bus *override_bus = override_dev->bus;
|
|
|
|
struct bus *base_bus = base_dev->bus;
|
|
|
|
|
|
|
|
while (override_bus) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have more buses in override tree device, then allocate
|
|
|
|
* a new bus for the base tree device as well.
|
|
|
|
*/
|
|
|
|
if (!base_bus) {
|
|
|
|
alloc_bus(base_dev);
|
|
|
|
base_bus = base_dev->last_bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
override_devicetree(base_dev->bus, override_dev->bus);
|
|
|
|
|
|
|
|
override_bus = override_bus->next_bus;
|
|
|
|
base_bus = base_bus->next_bus;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform copy of device and properties from override parent to base parent.
|
|
|
|
* This function walks through the override tree in a depth-first manner
|
|
|
|
* performing following actions:
|
|
|
|
* 1. If matching device is found in base tree, then copy the properties of
|
|
|
|
* override device to base tree device. Call override_devicetree recursively on
|
|
|
|
* the bus of override device.
|
|
|
|
* 2. If matching device is not found in base tree, then set override tree
|
|
|
|
* device as new child of base_parent and update the chip pointers in override
|
|
|
|
* device subtree to ensure the nodes do not point to override tree chip
|
|
|
|
* instance.
|
|
|
|
*/
|
|
|
|
static void override_devicetree(struct bus *base_parent,
|
|
|
|
struct bus *override_parent)
|
|
|
|
{
|
|
|
|
struct device *base_child;
|
|
|
|
struct device *override_child = override_parent->children;
|
|
|
|
struct device *next_child;
|
|
|
|
|
|
|
|
while (override_child) {
|
|
|
|
|
|
|
|
/* Look for a matching device in base tree. */
|
|
|
|
for (base_child = base_parent->children;
|
|
|
|
base_child; base_child = base_child->sibling) {
|
2021-08-24 08:23:23 +02:00
|
|
|
if (!device_match(base_child, override_child))
|
|
|
|
continue;
|
|
|
|
/* If base device has no probe statement, nothing else to compare. */
|
|
|
|
if (base_child->probe == NULL)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* If base device has probe statements, ensure that all probe conditions
|
|
|
|
* match for base and override device.
|
|
|
|
*/
|
|
|
|
if (device_probes_match(base_child, override_child))
|
2018-06-22 18:19:15 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
next_child = override_child->sibling;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If matching device is found, copy properties of
|
|
|
|
* override_child to base_child.
|
|
|
|
*/
|
|
|
|
if (base_child)
|
|
|
|
update_device(base_child, override_child);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* If matching device is not found, set override_child
|
|
|
|
* as a new child of base_parent.
|
|
|
|
*/
|
|
|
|
set_new_child(base_parent, override_child);
|
|
|
|
}
|
|
|
|
|
|
|
|
override_child = next_child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 20:24:10 +02:00
|
|
|
static void parse_override_devicetree(const char *file, struct device *dev)
|
|
|
|
{
|
|
|
|
parse_devicetree(file, dev->bus);
|
|
|
|
|
|
|
|
if (!dev_has_children(dev)) {
|
|
|
|
fprintf(stderr, "ERROR: Override tree needs at least one device!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
override_devicetree(&base_root_bus, dev->bus);
|
|
|
|
}
|
|
|
|
|
2020-09-23 23:36:30 +02:00
|
|
|
static void generate_outputh(FILE *f, const char *fw_conf_header, const char *device_header)
|
|
|
|
{
|
|
|
|
fprintf(f, "#ifndef __STATIC_DEVICE_TREE_H\n");
|
|
|
|
fprintf(f, "#define __STATIC_DEVICE_TREE_H\n\n");
|
|
|
|
|
|
|
|
fprintf(f, "#include <%s>\n", fw_conf_header);
|
|
|
|
fprintf(f, "#include <%s>\n\n", device_header);
|
|
|
|
|
|
|
|
fprintf(f, "\n#endif /* __STATIC_DEVICE_TREE_H */\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void generate_outputc(FILE *f, const char *static_header)
|
|
|
|
{
|
2021-09-14 22:09:20 +02:00
|
|
|
fprintf(f, "#include <boot/coreboot_tables.h>\n");
|
2020-09-23 23:36:30 +02:00
|
|
|
fprintf(f, "#include <device/device.h>\n");
|
|
|
|
fprintf(f, "#include <device/pci.h>\n");
|
|
|
|
fprintf(f, "#include <fw_config.h>\n");
|
|
|
|
fprintf(f, "#include <%s>\n", static_header);
|
|
|
|
emit_chip_headers(f, chip_header.next);
|
2022-08-06 19:02:59 +02:00
|
|
|
emit_identifiers(f, "struct device_operations", device_operations);
|
2022-07-08 10:53:21 +02:00
|
|
|
fprintf(f, "\n#define STORAGE static __maybe_unused DEVTREE_CONST\n\n");
|
2020-09-23 23:36:30 +02:00
|
|
|
|
|
|
|
walk_device_tree(NULL, NULL, &base_root_dev, inherit_subsystem_ids);
|
|
|
|
fprintf(f, "\n/* pass 0 */\n");
|
|
|
|
walk_device_tree(f, NULL, &base_root_dev, pass0);
|
|
|
|
walk_device_tree(NULL, NULL, &base_root_dev, update_references);
|
|
|
|
fprintf(f, "\n/* chip configs */\n");
|
|
|
|
emit_chip_configs(f);
|
|
|
|
fprintf(f, "\n/* pass 1 */\n");
|
|
|
|
walk_device_tree(f, NULL, &base_root_dev, pass1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void generate_outputd(FILE *gen, FILE *dev)
|
|
|
|
{
|
|
|
|
fprintf(dev, "#ifndef __STATIC_DEVICES_H\n");
|
|
|
|
fprintf(dev, "#define __STATIC_DEVICES_H\n\n");
|
|
|
|
fprintf(dev, "#include <device/device.h>\n\n");
|
|
|
|
fprintf(dev, "/* expose_device_names */\n");
|
|
|
|
walk_device_tree(gen, dev, &base_root_dev, expose_device_names);
|
|
|
|
fprintf(dev, "\n#endif /* __STATIC_DEVICE_NAMES_H */\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void generate_outputf(FILE *f)
|
|
|
|
{
|
|
|
|
fprintf(f, "#ifndef __STATIC_FW_CONFIG_H\n");
|
|
|
|
fprintf(f, "#define __STATIC_FW_CONFIG_H\n\n");
|
|
|
|
emit_fw_config(f);
|
|
|
|
fprintf(f, "\n#endif /* __STATIC_FW_CONFIG_H */\n");
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2020-06-09 20:20:29 +02:00
|
|
|
static const struct option long_options[] = {
|
|
|
|
{ "mainboard_devtree", 1, NULL, 'm' },
|
|
|
|
{ "override_devtree", 1, NULL, 'o' },
|
2020-07-30 01:28:43 +02:00
|
|
|
{ "chipset_devtree", 1, NULL, 'p' },
|
2020-06-09 20:20:29 +02:00
|
|
|
{ "output_c", 1, NULL, 'c' },
|
|
|
|
{ "output_h", 1, NULL, 'r' },
|
2020-09-23 23:36:30 +02:00
|
|
|
{ "output_d", 1, NULL, 'd' },
|
|
|
|
{ "output_f", 1, NULL, 'f' },
|
2020-06-09 20:20:29 +02:00
|
|
|
{ "help", 1, NULL, 'h' },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
const char *override_devtree = NULL;
|
|
|
|
const char *base_devtree = NULL;
|
2020-07-30 01:28:43 +02:00
|
|
|
const char *chipset_devtree = NULL;
|
2020-06-09 20:20:29 +02:00
|
|
|
const char *outputc = NULL;
|
|
|
|
const char *outputh = NULL;
|
2020-09-23 23:36:30 +02:00
|
|
|
const char *outputd = NULL;
|
|
|
|
const char *outputf = NULL;
|
2020-06-09 20:20:29 +02:00
|
|
|
int opt, option_index;
|
|
|
|
|
2020-09-23 23:36:30 +02:00
|
|
|
while ((opt = getopt_long(argc, argv, "m:o:p:c:r:d:f:h", long_options,
|
2020-06-09 20:20:29 +02:00
|
|
|
&option_index)) != EOF) {
|
|
|
|
switch (opt) {
|
|
|
|
case 'm':
|
|
|
|
base_devtree = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
override_devtree = strdup(optarg);
|
|
|
|
break;
|
2020-07-30 01:28:43 +02:00
|
|
|
case 'p':
|
|
|
|
chipset_devtree = strdup(optarg);
|
|
|
|
break;
|
2020-06-09 20:20:29 +02:00
|
|
|
case 'c':
|
|
|
|
outputc = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
outputh = strdup(optarg);
|
|
|
|
break;
|
2020-09-23 23:36:30 +02:00
|
|
|
case 'd':
|
|
|
|
outputd = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
outputf = strdup(optarg);
|
|
|
|
break;
|
2020-06-09 20:20:29 +02:00
|
|
|
case 'h':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
2018-06-11 13:26:45 +02:00
|
|
|
|
2020-09-23 23:36:30 +02:00
|
|
|
if (!base_devtree || !outputc || !outputh || !outputd || !outputf)
|
2020-06-09 20:20:29 +02:00
|
|
|
usage();
|
2018-06-11 13:26:45 +02:00
|
|
|
|
2020-07-30 01:28:43 +02:00
|
|
|
if (chipset_devtree) {
|
|
|
|
/* Use the chipset devicetree as the base, then override
|
|
|
|
with the mainboard "base" devicetree. */
|
|
|
|
parse_devicetree(chipset_devtree, &base_root_bus);
|
|
|
|
parse_override_devicetree(base_devtree, &chipset_root_dev);
|
|
|
|
} else {
|
|
|
|
parse_devicetree(base_devtree, &base_root_bus);
|
|
|
|
}
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2020-06-09 20:24:10 +02:00
|
|
|
if (override_devtree)
|
|
|
|
parse_override_devicetree(override_devtree, &override_root_dev);
|
2018-06-22 03:44:32 +02:00
|
|
|
|
2021-04-28 22:03:07 +02:00
|
|
|
|
2011-12-05 19:33:55 +01:00
|
|
|
FILE *autogen = fopen(outputc, "w");
|
|
|
|
if (!autogen) {
|
2016-08-06 02:32:18 +02:00
|
|
|
fprintf(stderr, "Could not open file '%s' for writing: ",
|
|
|
|
outputc);
|
2010-08-16 20:21:56 +02:00
|
|
|
perror(NULL);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
FILE *autohead = fopen(outputh, "w");
|
|
|
|
if (!autohead) {
|
|
|
|
fprintf(stderr, "Could not open file '%s' for writing: ", outputh);
|
|
|
|
perror(NULL);
|
|
|
|
fclose(autogen);
|
|
|
|
exit(1);
|
|
|
|
}
|
2020-09-23 23:36:30 +02:00
|
|
|
|
|
|
|
FILE *autodev = fopen(outputd, "w");
|
|
|
|
if (!autodev) {
|
|
|
|
fprintf(stderr, "Could not open file '%s' for writing: ", outputd);
|
|
|
|
perror(NULL);
|
|
|
|
fclose(autogen);
|
|
|
|
fclose(autohead);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *autofwconf = fopen(outputf, "w");
|
|
|
|
if (!autofwconf) {
|
|
|
|
fprintf(stderr, "Could not open file '%s' for writing: ", outputf);
|
|
|
|
perror(NULL);
|
|
|
|
fclose(autogen);
|
|
|
|
fclose(autohead);
|
|
|
|
fclose(autodev);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *f = strdup(outputf);
|
|
|
|
assert(f);
|
|
|
|
char *d = strdup(outputd);
|
|
|
|
assert(d);
|
|
|
|
char *h = strdup(outputh);
|
|
|
|
assert(h);
|
|
|
|
|
|
|
|
const char *fw_conf_header = basename(f);
|
|
|
|
const char *device_header = basename(d);
|
|
|
|
const char *static_header = basename(h);
|
|
|
|
|
|
|
|
generate_outputh(autohead, fw_conf_header, device_header);
|
|
|
|
generate_outputc(autogen, static_header);
|
|
|
|
generate_outputd(autogen, autodev);
|
|
|
|
generate_outputf(autofwconf);
|
|
|
|
|
2019-09-20 12:05:51 +02:00
|
|
|
fclose(autohead);
|
2011-12-05 19:33:55 +01:00
|
|
|
fclose(autogen);
|
2020-09-23 23:36:30 +02:00
|
|
|
fclose(autodev);
|
|
|
|
fclose(autofwconf);
|
|
|
|
free(f);
|
|
|
|
free(d);
|
|
|
|
free(h);
|
2010-08-16 20:21:56 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
return 0;
|
|
|
|
}
|