You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
6.6 KiB
352 lines
6.6 KiB
/*
|
|
* Copyright © 2015 Intel Corporation
|
|
*
|
|
* 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 (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <regex.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "intel_reg_spec.h"
|
|
|
|
static const struct port_desc port_descs[] = {
|
|
{
|
|
.name = "mmio",
|
|
.port = PORT_MMIO,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "portio-vga",
|
|
.port = PORT_PORTIO_VGA,
|
|
.stride = 1,
|
|
},
|
|
{
|
|
.name = "mmio-vga",
|
|
.port = PORT_MMIO_VGA,
|
|
.stride = 1,
|
|
},
|
|
{
|
|
.name = "bunit",
|
|
.port = PORT_BUNIT,
|
|
.stride = 1,
|
|
},
|
|
{
|
|
.name = "punit",
|
|
.port = PORT_PUNIT,
|
|
.stride = 1,
|
|
},
|
|
{
|
|
.name = "nc",
|
|
.port = PORT_NC,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "dpio",
|
|
.port = PORT_DPIO,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "gpio-nc",
|
|
.port = PORT_GPIO_NC,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "gpio_nc",
|
|
.port = PORT_GPIO_NC,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "cck",
|
|
.port = PORT_CCK,
|
|
.stride = 1,
|
|
},
|
|
{
|
|
.name = "ccu",
|
|
.port = PORT_CCU,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "dpio2",
|
|
.port = PORT_DPIO2,
|
|
.stride = 4,
|
|
},
|
|
{
|
|
.name = "flisdsi",
|
|
.port = PORT_FLISDSI,
|
|
.stride = 1,
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Parse port desc of the form (PORTNAME|PORTNUM|MMIO-OFFSET) into reg. NULL or
|
|
* zero length s is regarded as MMIO.
|
|
*/
|
|
int parse_port_desc(struct reg *reg, const char *s)
|
|
{
|
|
enum port_addr port = PORT_NONE;
|
|
int i;
|
|
|
|
if (s && *s) {
|
|
/* See if port is specified by number. */
|
|
char *endp;
|
|
unsigned long n = strtoul(s, &endp, 16);
|
|
if (endp > s && *endp == 0) {
|
|
if (n > PORT_MAX) {
|
|
/* Not a sideband port, assume MMIO offset. */
|
|
port = PORT_MMIO;
|
|
reg->mmio_offset = n;
|
|
} else {
|
|
port = n;
|
|
reg->mmio_offset = 0;
|
|
}
|
|
} else {
|
|
reg->mmio_offset = 0;
|
|
}
|
|
} else {
|
|
/* No port, default to searching for MMIO. */
|
|
port = PORT_MMIO;
|
|
reg->mmio_offset = 0;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(port_descs); i++) {
|
|
if ((port != PORT_NONE && port_descs[i].port == port) ||
|
|
(s && strcasecmp(s, port_descs[i].name) == 0)) {
|
|
reg->port_desc = port_descs[i];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static const char *skip_space(const char *line)
|
|
{
|
|
while (*line && isspace(*line))
|
|
line++;
|
|
|
|
return line;
|
|
}
|
|
|
|
static bool ignore_line(const char *line)
|
|
{
|
|
line = skip_space(line);
|
|
|
|
switch (*line) {
|
|
case '\0':
|
|
case '#':
|
|
case ';':
|
|
return true;
|
|
case '/':
|
|
return *(line + 1) == '/';
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static char *include_file(const char *line, const char *source)
|
|
{
|
|
char *filename, *p;
|
|
|
|
line = skip_space(line);
|
|
if (*line == '(')
|
|
return NULL;
|
|
|
|
/* this'll be plenty */
|
|
filename = malloc(strlen(source) + strlen(line) + 1);
|
|
if (!filename)
|
|
return NULL;
|
|
|
|
p = strrchr(source, '/');
|
|
if (p && *line != '/') {
|
|
int len = p - source + 1;
|
|
|
|
memcpy(filename, source, len);
|
|
strcpy(filename + len, line);
|
|
} else {
|
|
strcpy(filename, line);
|
|
}
|
|
|
|
p = strchr(filename, '\n');
|
|
if (p)
|
|
*p = '\0';
|
|
|
|
return filename;
|
|
}
|
|
|
|
#define SPC "[[:space:]]*"
|
|
#define SEP SPC "," SPC
|
|
#define BEG "^" SPC "\\(" SPC
|
|
#define END SPC "\\)" SPC "$"
|
|
#define VALUE "([[:print:]]*)"
|
|
#define QVALUE "'" VALUE "'"
|
|
#define REGEXP BEG QVALUE SEP QVALUE SEP QVALUE END
|
|
|
|
static int parse_line(struct reg *reg, const char *line)
|
|
{
|
|
static regex_t regex;
|
|
static bool initialized = false;
|
|
regmatch_t match[4];
|
|
int i, ret;
|
|
|
|
if (!initialized) {
|
|
if (regcomp (®ex, REGEXP, REG_EXTENDED)) {
|
|
fprintf(stderr, "regcomp %s\n", REGEXP);
|
|
return -1;
|
|
}
|
|
initialized = true;
|
|
}
|
|
|
|
ret = regexec(®ex, line, ARRAY_SIZE(match), match, 0);
|
|
if (ret)
|
|
ret = -1;
|
|
|
|
for (i = 1; i < ARRAY_SIZE(match) && ret == 0; i++) {
|
|
char *p, *e;
|
|
|
|
p = strndup(line + match[i].rm_so,
|
|
match[i].rm_eo - match[i].rm_so);
|
|
|
|
if (i == 1) {
|
|
reg->name = p;
|
|
} else if (i == 2) {
|
|
reg->addr = strtoul(p, &e, 16);
|
|
free(p);
|
|
if (*e)
|
|
ret = -1;
|
|
} else if (i == 3) {
|
|
ret = parse_port_desc(reg, p);
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
free(reg->name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t parse_file(struct reg **regs, size_t *nregs,
|
|
ssize_t index, const char *filename)
|
|
{
|
|
FILE *file;
|
|
char *line = NULL, *include;
|
|
size_t linesize = 0;
|
|
int lineno = 0, r;
|
|
ssize_t ret = -1;
|
|
|
|
file = fopen(filename, "r");
|
|
if (!file) {
|
|
fprintf(stderr, "Error: fopen '%s': %s\n",
|
|
filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
while (getline(&line, &linesize, file) != -1) {
|
|
struct reg reg = {};
|
|
|
|
lineno++;
|
|
|
|
if (ignore_line(line))
|
|
continue;
|
|
|
|
include = include_file(line, filename);
|
|
if (include) {
|
|
index = parse_file(regs, nregs, index, include);
|
|
free(include);
|
|
if (index < 0) {
|
|
fprintf(stderr, "Error: %s:%d: %s",
|
|
filename, lineno, line);
|
|
goto out;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
r = parse_line(®, line);
|
|
if (r < 0) {
|
|
fprintf(stderr, "Error: %s:%d: %s",
|
|
filename, lineno, line);
|
|
goto out;
|
|
} else if (r) {
|
|
continue;
|
|
}
|
|
|
|
if (!*regs || index >= *nregs) {
|
|
if (!*regs)
|
|
*nregs = 64;
|
|
else
|
|
*nregs *= 2;
|
|
|
|
*regs = recalloc(*regs, *nregs, sizeof(**regs));
|
|
if (!*regs) {
|
|
fprintf(stderr, "Error: %s\n", strerror(ENOMEM));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
(*regs)[index++] = reg;
|
|
}
|
|
|
|
ret = index;
|
|
|
|
out:
|
|
free(line);
|
|
fclose(file);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Get register definitions from file.
|
|
*/
|
|
ssize_t intel_reg_spec_file(struct reg **regs, const char *file)
|
|
{
|
|
size_t nregs = 0;
|
|
*regs = NULL;
|
|
|
|
return parse_file(regs, &nregs, 0, file);
|
|
}
|
|
|
|
/*
|
|
* Free the memory allocated for register definitions.
|
|
*/
|
|
void intel_reg_spec_free(struct reg *regs, size_t n)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
free(regs[i].name);
|
|
}
|
|
free(regs);
|
|
}
|
|
|
|
void intel_reg_spec_print_ports(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(port_descs); i++)
|
|
printf("%s%s", i == 0 ? "" : ", ", port_descs[i].name);
|
|
}
|