summaryrefslogtreecommitdiff
path: root/tools/intel_reg_spec.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/intel_reg_spec.c')
-rw-r--r--tools/intel_reg_spec.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/tools/intel_reg_spec.c b/tools/intel_reg_spec.c
new file mode 100644
index 00000000..6b7e30c1
--- /dev/null
+++ b/tools/intel_reg_spec.c
@@ -0,0 +1,345 @@
+/*
+ * 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 = 4,
+ },
+ {
+ .name = "mmio-vga",
+ .port = PORT_MMIO_VGA,
+ .stride = 4,
+ },
+ {
+ .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 (&regex, REGEXP, REG_EXTENDED)) {
+ fprintf(stderr, "regcomp %s\n", REGEXP);
+ return -1;
+ }
+ initialized = true;
+ }
+
+ memset(reg, 0, sizeof(*reg));
+
+ ret = regexec(&regex, 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(&reg, 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);
+}