2010-05-05 13:19:50 +02:00
|
|
|
/*
|
|
|
|
* sconfig, coreboot device tree compiler
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 coresystems GmbH
|
2016-05-07 10:11:14 +02:00
|
|
|
* written by Patrick Georgi <patrick@georgi-clan.de>
|
2010-05-05 13:19:50 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
2012-05-30 16:26:30 +02:00
|
|
|
#include <ctype.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
|
|
|
|
|
|
|
/*
|
|
|
|
* This is intentionally shared between chip and device structure ids because it
|
|
|
|
* is easier to track the order of parsing for chip and device.
|
|
|
|
*/
|
|
|
|
static int count = 0;
|
2010-05-05 13:19:50 +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
|
|
|
|
|
|
|
/* 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",
|
|
|
|
.id = 0,
|
|
|
|
.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
|
|
|
};
|
|
|
|
|
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",
|
|
|
|
.id = 0,
|
|
|
|
/*
|
|
|
|
* 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,
|
2018-06-22 18:19:15 +02:00
|
|
|
.ref_count = 2,
|
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
|
|
|
|
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
|
|
|
|
* exists on on pretty much all AMD chipsets.
|
|
|
|
*/
|
|
|
|
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->id = ++count;
|
|
|
|
instance->chip = chip;
|
|
|
|
instance->next = chip->instance;
|
|
|
|
chip->instance = instance;
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2018-06-22 18:19:15 +02:00
|
|
|
static void delete_chip_instance(struct chip_instance *ins)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ins->ref_count == 0) {
|
|
|
|
printf("ERROR: ref count for chip instance is zero!!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--ins->ref_count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct chip *c = ins->chip;
|
|
|
|
|
|
|
|
/* Get pointer to first instance of the chip. */
|
|
|
|
struct chip_instance *i = c->instance;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If chip instance to be deleted is the first instance, then update
|
|
|
|
* instance pointer of the chip as well.
|
|
|
|
*/
|
|
|
|
if (i == ins) {
|
|
|
|
c->instance = ins->next;
|
|
|
|
free(ins);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through the instances list of the chip to find and remove the
|
|
|
|
* given instance.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
if (i == NULL) {
|
|
|
|
printf("ERROR: chip instance not found!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i->next != ins) {
|
|
|
|
i = i->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
i->next = ins->next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ins);
|
|
|
|
}
|
|
|
|
|
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->id = ++count;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *new_device(struct bus *parent,
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip_instance *chip_instance,
|
2018-05-31 16:52:00 +02:00
|
|
|
const int bustype, const char *devnum,
|
2018-05-31 00:09:09 +02:00
|
|
|
int enabled)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2010-05-05 13:19:50 +02:00
|
|
|
char *tmp;
|
2018-06-03 13:22:17 +02:00
|
|
|
int path_a;
|
|
|
|
int path_b = 0;
|
|
|
|
struct device *new_d;
|
|
|
|
|
|
|
|
path_a = strtol(devnum, &tmp, 16);
|
2010-05-05 13:19:50 +02:00
|
|
|
if (*tmp == '.') {
|
|
|
|
tmp++;
|
2018-06-03 13:22:17 +02:00
|
|
|
path_b = strtol(tmp, NULL, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2018-05-31 18:48:51 +02:00
|
|
|
char *name = S_ALLOC(10);
|
2010-05-05 13:19:50 +02:00
|
|
|
sprintf(name, "_dev%d", new_d->id);
|
|
|
|
new_d->name = name;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
new_d->enabled = enabled;
|
2018-05-31 19:33:16 +02:00
|
|
|
new_d->chip_instance = chip_instance;
|
2018-06-22 18:19:15 +02:00
|
|
|
chip_instance->ref_count++;
|
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;
|
|
|
|
|
|
|
|
case APIC:
|
2010-05-05 13:19:50 +02:00
|
|
|
new_d->path = ".type=DEVICE_PATH_APIC,{.apic={ .apic_id = 0x%x }}";
|
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;
|
|
|
|
|
|
|
|
case IOAPIC:
|
|
|
|
new_d->path = ".type=DEVICE_PATH_IOAPIC,{.ioapic={ .ioapic_id = 0x%x }}";
|
|
|
|
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;
|
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;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
void add_register(struct chip_instance *chip_instance, char *name, char *val)
|
2016-08-06 02:32:18 +02:00
|
|
|
{
|
2018-05-31 18:48:51 +02:00
|
|
|
struct reg *r = S_ALLOC(sizeof(struct reg));
|
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
r->key = name;
|
|
|
|
r->value = val;
|
2018-05-31 19:33:16 +02:00
|
|
|
if (chip_instance->reg) {
|
|
|
|
struct reg *head = chip_instance->reg;
|
2010-05-05 13:19:50 +02:00
|
|
|
// sorting to be equal to sconfig's behaviour
|
|
|
|
int sort = strcmp(r->key, head->key);
|
|
|
|
if (sort == 0) {
|
|
|
|
printf("ERROR: duplicate 'register' key.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2016-08-06 02:32:18 +02:00
|
|
|
if (sort < 0) {
|
2010-05-05 13:19:50 +02:00
|
|
|
r->next = head;
|
2018-05-31 19:33:16 +02:00
|
|
|
chip_instance->reg = r;
|
2010-05-05 13:19:50 +02:00
|
|
|
} else {
|
2016-08-06 02:32:18 +02:00
|
|
|
while ((head->next)
|
|
|
|
&& (strcmp(head->next->key, r->key) < 0))
|
|
|
|
head = head->next;
|
2010-05-05 13:19:50 +02:00
|
|
|
r->next = head->next;
|
|
|
|
head->next = r;
|
|
|
|
}
|
|
|
|
} else {
|
2018-05-31 19:33:16 +02:00
|
|
|
chip_instance->reg = r;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
void add_ioapic_info(struct bus *bus, int apicid, const char *_srcpin,
|
2016-08-06 02:32:18 +02:00
|
|
|
int irqpin)
|
2012-06-21 22:19:48 +02:00
|
|
|
{
|
|
|
|
int srcpin;
|
2018-06-03 13:22:17 +02:00
|
|
|
struct device *dev = bus->dev;
|
2012-06-21 22:19:48 +02:00
|
|
|
|
2016-08-06 02:32:18 +02:00
|
|
|
if (!_srcpin || strlen(_srcpin) < 4 || strncasecmp(_srcpin, "INT", 3) ||
|
|
|
|
_srcpin[3] < 'A' || _srcpin[3] > 'D') {
|
2012-06-21 22:19:48 +02:00
|
|
|
printf("ERROR: malformed ioapic_irq args: %s\n", _srcpin);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
srcpin = _srcpin[3] - 'A';
|
|
|
|
|
2013-02-12 23:17:15 +01:00
|
|
|
if (dev->bustype != PCI && dev->bustype != DOMAIN) {
|
2012-06-21 22:19:48 +02:00
|
|
|
printf("ERROR: ioapic config only allowed for PCI devices\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srcpin > 3) {
|
2012-07-20 12:47:06 +02:00
|
|
|
printf("ERROR: srcpin '%d' invalid\n", srcpin);
|
2012-06-21 22:19:48 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
dev->pci_irq_info[srcpin].ioapic_irq_pin = irqpin;
|
|
|
|
dev->pci_irq_info[srcpin].ioapic_dst_id = apicid;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pass0(FILE *fil, struct device *ptr, struct device *next)
|
|
|
|
{
|
2018-06-11 13:26:45 +02:00
|
|
|
if (ptr == &base_root_dev) {
|
2017-04-17 05:05:36 +02:00
|
|
|
fprintf(fil, "DEVTREE_CONST 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
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "DEVTREE_CONST static struct device %s;\n", ptr->name);
|
2018-06-08 08:36:45 +02:00
|
|
|
if (ptr->res)
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "DEVTREE_CONST 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))
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct bus %s_links[];\n",
|
|
|
|
ptr->name);
|
|
|
|
|
|
|
|
if (next)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fprintf(fil,
|
|
|
|
"DEVTREE_CONST struct device * DEVTREE_CONST last_dev = &%s;\n",
|
|
|
|
ptr->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct resource %s_res[] = {\n", ptr->name);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct bus %s_links[] = {\n",
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
static void pass1(FILE *fil, struct device *ptr, struct device *next)
|
2012-06-21 22:19:48 +02:00
|
|
|
{
|
|
|
|
int pin;
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip_instance *chip_ins = ptr->chip_instance;
|
2018-06-03 13:22:17 +02:00
|
|
|
int has_children = dev_has_children(ptr);
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
if (ptr != &base_root_dev)
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(fil, "static ");
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct device %s = {\n", ptr->name);
|
|
|
|
fprintf(fil, "#if !DEVTREE_EARLY\n");
|
2018-06-23 09:59:31 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ops field is set to default_dev_ops_root only for the root
|
|
|
|
* device. For all other devices, it is set by the driver at runtime.
|
|
|
|
*/
|
|
|
|
if (ptr == &base_root_dev)
|
|
|
|
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);
|
|
|
|
fprintf(fil, "\t.on_mainboard = 1,\n");
|
|
|
|
if (ptr->subsystem_vendor > 0)
|
|
|
|
fprintf(fil, "\t.subsystem_vendor = 0x%04x,\n",
|
|
|
|
ptr->subsystem_vendor);
|
|
|
|
|
|
|
|
for (pin = 0; pin < 4; pin++) {
|
|
|
|
if (ptr->pci_irq_info[pin].ioapic_irq_pin > 0)
|
|
|
|
fprintf(fil,
|
|
|
|
"\t.pci_irq_info[%d].ioapic_irq_pin = %d,\n",
|
|
|
|
pin, ptr->pci_irq_info[pin].ioapic_irq_pin);
|
2011-03-01 20:58:15 +01:00
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
if (ptr->pci_irq_info[pin].ioapic_dst_id > 0)
|
|
|
|
fprintf(fil,
|
|
|
|
"\t.pci_irq_info[%d].ioapic_dst_id = %d,\n",
|
|
|
|
pin, ptr->pci_irq_info[pin].ioapic_dst_id);
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
2018-06-03 13:22:17 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
fprintf(fil, "#if !DEVTREE_EARLY\n");
|
|
|
|
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)
|
|
|
|
fprintf(fil, "\t.next=&%s\n", next->name);
|
|
|
|
fprintf(fil, "};\n");
|
|
|
|
|
|
|
|
emit_resources(fil, ptr);
|
|
|
|
|
|
|
|
if (has_children)
|
|
|
|
emit_dev_links(fil, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
static void walk_device_tree(FILE *fil, struct device *ptr,
|
2018-06-03 13:22:17 +02:00
|
|
|
void (*func)(FILE *, struct device *,
|
|
|
|
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);
|
|
|
|
func(fil, ptr, peek_queue_head(bfs_q_head));
|
|
|
|
}
|
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
|
|
|
|
|
|
|
fprintf(fil, "#include <device/device.h>\n");
|
|
|
|
fprintf(fil, "#include <device/pci.h>\n");
|
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) {
|
2018-05-31 08:46:16 +02:00
|
|
|
fprintf(fil,
|
|
|
|
"__attribute__((weak)) struct chip_operations %s_ops = {};\n",
|
2018-05-31 19:33:16 +02:00
|
|
|
chip->name_underscore);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
fprintf(fil, "DEVTREE_CONST struct %s_config %s_info_%d = {",
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2018-05-31 00:09:09 +02:00
|
|
|
static void emit_chips(FILE *fil)
|
|
|
|
{
|
2018-05-31 19:33:16 +02:00
|
|
|
struct chip *chip = chip_header.next;
|
|
|
|
struct chip_instance *instance;
|
2018-05-31 00:09:09 +02:00
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
emit_chip_headers(fil, chip);
|
2018-05-31 08:46:16 +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;
|
|
|
|
|
2018-05-31 19:33:16 +02:00
|
|
|
instance = chip->instance;
|
|
|
|
while (instance) {
|
|
|
|
emit_chip_instance(fil, instance);
|
|
|
|
instance = instance->next;
|
2010-05-05 13:19:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:22:17 +02:00
|
|
|
static void inherit_subsystem_ids(FILE *file, struct device *dev,
|
|
|
|
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)
|
|
|
|
{
|
2018-06-22 03:44:32 +02:00
|
|
|
printf("usage: sconfig devicetree_file output_file [override_devicetree_file]\n");
|
2016-08-06 02:32:18 +02:00
|
|
|
exit(1);
|
2011-12-05 19:33:55 +01:00
|
|
|
}
|
|
|
|
|
2015-11-24 20:34:16 +01:00
|
|
|
enum {
|
2016-08-06 02:15:06 +02:00
|
|
|
DEVICEFILE_ARG = 1,
|
2018-06-22 03:44:32 +02:00
|
|
|
OUTPUTFILE_ARG,
|
|
|
|
OVERRIDE_DEVICEFILE_ARG,
|
2016-08-06 02:32:18 +02:00
|
|
|
};
|
2015-11-24 20:34:16 +01:00
|
|
|
|
2018-06-22 03:44:32 +02:00
|
|
|
#define MANDATORY_ARG_COUNT 3
|
|
|
|
#define OPTIONAL_ARG_COUNT 1
|
|
|
|
#define TOTAL_ARG_COUNT (MANDATORY_ARG_COUNT + OPTIONAL_ARG_COUNT)
|
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
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk through the override subtree in breadth-first manner starting at node to
|
|
|
|
* see if chip_instance pointer of the node is same as chip_instance pointer of
|
|
|
|
* override parent that is passed into the function. If yes, then update the
|
|
|
|
* chip_instance pointer of the node to chip_instance pointer of the base
|
|
|
|
* parent.
|
|
|
|
*/
|
|
|
|
static void update_chip_pointers(struct device *node,
|
|
|
|
struct chip_instance *base_parent_ci,
|
|
|
|
struct chip_instance *override_parent_ci)
|
|
|
|
{
|
|
|
|
struct queue_entry *q_head = NULL;
|
|
|
|
|
|
|
|
enqueue_tail(&q_head, node);
|
|
|
|
|
|
|
|
while ((node = dequeue_head(&q_head))) {
|
|
|
|
if (node->chip_instance != override_parent_ci)
|
|
|
|
continue;
|
|
|
|
node->chip_instance = base_parent_ci;
|
|
|
|
add_children_to_queue(&q_head, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
|
|
|
if (base_res->type == res->type) {
|
|
|
|
base_res->index = res->index;
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static void update_register(struct chip_instance *c, struct reg *reg)
|
|
|
|
{
|
|
|
|
struct reg *base_reg = c->reg;
|
|
|
|
|
|
|
|
while (base_reg) {
|
|
|
|
if (!strcmp(base_reg->key, reg->key)) {
|
|
|
|
base_reg->value = reg->value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
base_reg = base_reg->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_register(c, reg->key, reg->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
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: |
|
|
|
|
* | | 1. If resource of same type is present in |
|
|
|
|
* | | base device, then index and base of the |
|
|
|
|
* | | resource is copied. |
|
|
|
|
* | | 2. If not, then a new resource is allocated|
|
|
|
|
* | | under the base device using type, index |
|
|
|
|
* | | and base from override res. |
|
|
|
|
* | | |
|
|
|
|
* +-----------------------------------------------------------------+
|
|
|
|
* | | |
|
|
|
|
* | 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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
|
|
|
update_register(base_dev->chip_instance, reg);
|
|
|
|
reg = reg->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete_chip_instance(override_dev->chip_instance);
|
|
|
|
override_dev->chip_instance = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
|
|
|
if (device_match(base_child, override_child))
|
|
|
|
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);
|
|
|
|
/*
|
|
|
|
* Ensure all nodes in override tree pointing to
|
|
|
|
* override parent chip_instance now point to base
|
|
|
|
* parent chip_instance.
|
|
|
|
*/
|
|
|
|
update_chip_pointers(override_child,
|
|
|
|
base_parent->dev->chip_instance,
|
|
|
|
override_parent->dev->chip_instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
override_child = next_child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2018-06-22 03:44:32 +02:00
|
|
|
if ((argc < MANDATORY_ARG_COUNT) || (argc > TOTAL_ARG_COUNT))
|
2018-06-11 13:26:45 +02:00
|
|
|
usage();
|
|
|
|
|
2018-06-22 03:44:32 +02:00
|
|
|
const char *base_devtree = argv[DEVICEFILE_ARG];
|
|
|
|
const char *outputc = argv[OUTPUTFILE_ARG];
|
|
|
|
const char *override_devtree;
|
2018-06-11 13:26:45 +02:00
|
|
|
|
|
|
|
parse_devicetree(base_devtree, &base_root_bus);
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2018-06-22 03:44:32 +02:00
|
|
|
if (argc == TOTAL_ARG_COUNT) {
|
|
|
|
override_devtree = argv[OVERRIDE_DEVICEFILE_ARG];
|
|
|
|
parse_devicetree(override_devtree, &override_root_bus);
|
2018-06-22 18:19:15 +02:00
|
|
|
|
|
|
|
override_devicetree(&base_root_bus, &override_root_bus);
|
2018-06-22 03:44:32 +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);
|
|
|
|
}
|
|
|
|
|
2018-05-31 00:09:09 +02:00
|
|
|
emit_chips(autogen);
|
|
|
|
|
2018-06-11 13:26:45 +02:00
|
|
|
walk_device_tree(autogen, &base_root_dev, inherit_subsystem_ids);
|
2016-08-06 01:40:39 +02:00
|
|
|
fprintf(autogen, "\n/* pass 0 */\n");
|
2018-06-11 13:26:45 +02:00
|
|
|
walk_device_tree(autogen, &base_root_dev, pass0);
|
2018-06-03 13:22:17 +02:00
|
|
|
fprintf(autogen, "\n/* pass 1 */\n");
|
2018-06-11 13:26:45 +02:00
|
|
|
walk_device_tree(autogen, &base_root_dev, pass1);
|
2010-05-05 13:19:50 +02:00
|
|
|
|
2011-12-05 19:33:55 +01:00
|
|
|
fclose(autogen);
|
2010-08-16 20:21:56 +02:00
|
|
|
|
2010-05-05 13:19:50 +02:00
|
|
|
return 0;
|
|
|
|
}
|