/* * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * author: Jerome Glisse */ /* rdb format : * REG 0x00028c60 0x00000104 0x00000001 32 0 8 60 CB_COLOR_BASE * F0 F1 F2 F3 F4 F5 F6 F7 F8 * * register: * F0: REG for register * F1: register offset * F2: domain (io domain could be mmio, pio, or any special domain) * F3: block (gpu block like cp engine, or some sub 3d block) * F4: register size in bits * F5: base index (for reg repeating base index for instance 0 here gave * CB_COLOR_BASE0 at offset 0x28c60 but 4 gave * CB_COLOR_BASE4 at offset 0x28c60) * F6: repeat number of time this register is repeated from base index * to base index + F6 * F7: stride number of byte separating 2 consecutive register * F8: register name */ #include #include #include #include #include "rdb.h" #define ID_FROM_CHAR(a, b, c) ((a) | ((b) << 8) | ((c) << 16)) static char *read_value(char *line, unsigned *value) { char *next; *value = strtoul(line, &next, 0); if (next == line) { return NULL; } return next; } static int rdb_read_block(struct rdb *rdb, char *line) { struct rdb_block *block, *tmp; block = malloc(sizeof(*block)); if (block == NULL) { return -ENOMEM; } block->name = NULL; block->description = NULL; line = read_value(&line[4], &block->id); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } block->name = strdup(line); if (block->name == NULL) { free(block); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&rdb->blocks)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &rdb->blocks, list) { if (block->id >= tmp->id) { list_add(&block->list, &tmp->list); return 0; } } list_add(&block->list, &rdb->blocks); return 0; } else { list_add_tail(&block->list, &rdb->blocks); return 0; } return -EINVAL; } static int rdb_read_domain(struct rdb *rdb, char *line) { struct rdb_domain *domain, *tmp; domain = malloc(sizeof(*domain)); if (domain == NULL) { return -ENOMEM; } domain->name = NULL; domain->description = NULL; line = read_value(&line[4], &domain->id); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } domain->name = strdup(line); if (domain->name == NULL) { free(domain); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&rdb->domains)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &rdb->domains, list) { if (domain->id >= tmp->id) { list_add(&domain->list, &tmp->list); return 0; } } list_add(&domain->list, &rdb->domains); return 0; } else { list_add_tail(&domain->list, &rdb->domains); return 0; } return -EINVAL; } static int rdb_read_field(struct rdb_reg *reg, char *line, struct rdb_field **last) { struct rdb_field *field, *tmp; field = malloc(sizeof(*field)); if (field == NULL) { return -ENOMEM; } field->name = NULL; field->description = NULL; list_init_head(&field->values); *last = field; line = read_value(&line[4], &field->shift); if (line == NULL) { return -EINVAL; } line = read_value(line, &field->mask); if (line == NULL) { return -EINVAL; } line = read_value(line, &field->dvalue); if (line == NULL) { return -EINVAL; } line = read_value(line, &field->rw); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } field->name = strdup(line); if (field->name == NULL) { free(field); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(®->fields)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, ®->fields, list) { if (field->shift >= tmp->shift) { list_add(&field->list, &tmp->list); return 0; } } list_add(&field->list, ®->fields); return 0; } else { list_add_tail(&field->list, ®->fields); return 0; } return -EINVAL; } static int rdb_read_it(struct rdb *rdb, char *line) { struct rdb_it *it, *tmp; it = malloc(sizeof(*it)); if (it == NULL) { return -ENOMEM; } it->name = NULL; it->description = NULL; line = read_value(&line[4], &it->it); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } it->name = strdup(line); if (it->name == NULL) { free(it); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&rdb->its)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &rdb->its, list) { if (it->it >= tmp->it) { list_add(&it->list, &tmp->list); return 0; } } list_add(&it->list, &rdb->its); return 0; } else { list_add_tail(&it->list, &rdb->its); return 0; } return -EINVAL; } static int rdb_read_range(struct rdb *rdb, char *line) { struct rdb_range *range, *tmp; range = malloc(sizeof(*range)); if (range == NULL) { return -ENOMEM; } range->name = NULL; range->description = NULL; line = read_value(&line[4], &range->domain); if (line == NULL) { return -EINVAL; } line = read_value(line, &range->soffset); if (line == NULL) { return -EINVAL; } line = read_value(line, &range->eoffset); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } range->name = strdup(line); if (range->name == NULL) { free(range); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&rdb->ranges)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &rdb->ranges, list) { if (range->soffset >= tmp->soffset) { list_add(&range->list, &tmp->list); return 0; } } list_add(&range->list, &rdb->ranges); return 0; } else { list_add_tail(&range->list, &rdb->ranges); return 0; } return -EINVAL; } static int rdb_read_reg(struct rdb *rdb, char *line, struct rdb_reg **last) { struct rdb_reg *reg, *tmp; reg = malloc(sizeof(*reg)); if (reg == NULL) { return -ENOMEM; } reg->name = NULL; reg->description = NULL; list_init_head(®->fields); *last = reg; line = read_value(&line[4], ®->offset); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->domain); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->block); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->size); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->base); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->nrepeat); if (line == NULL) { return -EINVAL; } line = read_value(line, ®->stride); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } reg->name = strdup(line); if (reg->name == NULL) { free(reg); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&rdb->regs)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &rdb->regs, list) { if (reg->domain < tmp->domain) { continue; } if (reg->domain == tmp->domain) { if (reg->offset >= tmp->offset) { list_add(®->list, &tmp->list); return 0; } } else { list_add(®->list, &tmp->list); return 0; } } list_add(®->list, &rdb->regs); return 0; } else { list_add_tail(®->list, &rdb->regs); return 0; } return -EINVAL; } static int rdb_read_value(struct rdb_field *field, char *line) { struct rdb_value *value, *tmp; value = malloc(sizeof(*value)); if (value == NULL) { return -ENOMEM; } value->name = NULL; value->description = NULL; line = read_value(&line[4], &value->value); if (line == NULL) { return -EINVAL; } while (*line == ' ') { line++; } value->name = strdup(line); if (value->name == NULL) { free(value); return -ENOMEM; } /* always keep things shorted */ if (!list_is_empty(&field->values)) { /* assume best case where things are already shorted */ LIST_FOR_EACH_ENTRY_REV(tmp, &field->values, list) { if (value->value >= tmp->value) { list_add(&value->list, &tmp->list); return 0; } } list_add(&value->list, &field->values); return 0; } else { list_add_tail(&value->list, &field->values); return 0; } return -EINVAL; } static void rdb_write_block(FILE *file, struct rdb_block *block) { fprintf(file, "BLK 0x%08x %s\n", block->id, block->name); } static void rdb_write_domain(FILE *file, struct rdb_domain *domain) { fprintf(file, "DOM 0x%08x %s\n", domain->id, domain->name); } static void rdb_write_field(FILE *file, struct rdb_field *field) { fprintf(file, "FLD %2d 0x%08x 0x%08x %1d %s\n", field->shift, field->mask, field->dvalue, field->rw, field->name); } static void rdb_write_it(FILE *file, struct rdb_it *it) { fprintf(file, "IT 0x%08x %s\n", it->it, it->name); } static void rdb_write_range(FILE *file, struct rdb_range *range) { fprintf(file, "RNG 0x%08x 0x%08x 0x%08x %s\n", range->domain, range->soffset, range->eoffset, range->name); } static void rdb_write_reg(FILE *file, struct rdb_reg *reg) { fprintf(file, "REG 0x%08x 0x%08x 0x%08x %2d %4d %4d %4d %s\n", reg->offset, reg->domain, reg->block, reg->size, reg->base, reg->nrepeat, reg->stride, reg->name); } static void rdb_write_value(FILE *file, struct rdb_value *value) { fprintf(file, "VAL 0x%08x %s\n", value->value, value->name); } const struct rdb_reg *rdb_find_reg(struct rdb *rdb, unsigned domain, unsigned offset) { struct rdb_reg *reg; LIST_FOR_EACH_ENTRY (reg, &rdb->regs, list) { if (domain < reg->domain) { /* everything being shorted, that means we didn't found * the register in the requested domain */ return NULL; } if (domain == reg->domain && reg->offset == offset) { return reg; } } return NULL; } const struct rdb_reg *rdb_find_reg_any_domain(struct rdb *rdb, unsigned offset) { struct rdb_reg *reg; LIST_FOR_EACH_ENTRY (reg, &rdb->regs, list) { if (reg->offset == offset) { return reg; } } return NULL; } void rdb_init(struct rdb *rdb) { list_init_head(&rdb->blocks); list_init_head(&rdb->domains); list_init_head(&rdb->its); list_init_head(&rdb->ranges); list_init_head(&rdb->regs); } int rdb_read(struct rdb *rdb, FILE *file) { struct rdb_field *lfield = NULL; struct rdb_reg *lreg = NULL; char line[128]; unsigned id; int r; while (!feof(file)) { if (fgets(line, sizeof(line), file) == NULL) { continue; } /* remove newline */ r = strlen(line); if (line[r - 1] == '\n') { line[r - 1] = 0; } id = *((unsigned*)line) & 0x00ffffff; switch (id) { case ID_FROM_CHAR('R', 'N', 'G'): r = rdb_read_range(rdb, line); break; case ID_FROM_CHAR('B', 'L', 'K'): r = rdb_read_block(rdb, line); break; case ID_FROM_CHAR('D', 'O', 'M'): r = rdb_read_domain(rdb, line); break; case ID_FROM_CHAR('V', 'A', 'L'): if (lfield == NULL) { return -EINVAL; } r = rdb_read_value(lfield, line); break; case ID_FROM_CHAR('I', 'T', ' '): r = rdb_read_it(rdb, line); break; case ID_FROM_CHAR('F', 'L', 'D'): if (lreg == NULL) { return -EINVAL; } r = rdb_read_field(lreg, line, &lfield); break; case ID_FROM_CHAR('R', 'E', 'G'): r = rdb_read_reg(rdb, line, &lreg); break; default: return -EINVAL; } if (r) { return r; } } return 0; } void rdb_write(FILE *file, struct rdb *rdb) { struct rdb_block *block; struct rdb_domain *domain; struct rdb_value *value; struct rdb_field *field; struct rdb_it *it; struct rdb_reg *reg; struct rdb_range *range; LIST_FOR_EACH_ENTRY (block, &rdb->blocks, list) { rdb_write_block(file, block); } LIST_FOR_EACH_ENTRY (domain, &rdb->domains, list) { rdb_write_domain(file, domain); } LIST_FOR_EACH_ENTRY (it, &rdb->its, list) { rdb_write_it(file, it); } LIST_FOR_EACH_ENTRY (range, &rdb->ranges, list) { rdb_write_range(file, range); } LIST_FOR_EACH_ENTRY (reg, &rdb->regs, list) { rdb_write_reg(file, reg); LIST_FOR_EACH_ENTRY (field, ®->fields, list) { rdb_write_field(file, field); LIST_FOR_EACH_ENTRY (value, &field->values, list) { rdb_write_value(file, value); } } } }