aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/xscom-utils/xscom.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/xscom-utils/xscom.c')
-rw-r--r--roms/skiboot/external/xscom-utils/xscom.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/roms/skiboot/external/xscom-utils/xscom.c b/roms/skiboot/external/xscom-utils/xscom.c
new file mode 100644
index 000000000..031f9e193
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/xscom.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Do XSCOMs through linux debugfs interface
+ *
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#define _LARGEFILE64_SOURCE
+#include <sys/mman.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "xscom.h"
+
+#define XSCOM_BASE_PATH "/sys/kernel/debug/powerpc/scom"
+
+struct xscom_chip {
+ struct xscom_chip *next;
+ uint32_t chip_id;
+ int fd;
+};
+static struct xscom_chip *xscom_chips;
+
+void xscom_for_each_chip(void (*cb)(uint32_t chip_id))
+{
+ struct xscom_chip *c;
+
+ for (c = xscom_chips; c; c = c->next)
+ cb(c->chip_id);
+}
+
+static uint32_t xscom_add_chip(const char *base_path, const char *dname)
+{
+ char nbuf[strlen(base_path) + strlen(dname) + 16];
+ struct xscom_chip *chip;
+ int fd;
+
+ snprintf(nbuf, sizeof(nbuf), "%s/%s/access", base_path, dname);
+ fd = open(nbuf, O_RDWR);
+ if (fd < 0) {
+ perror("Failed to open SCOM access file");
+ exit(1);
+ }
+
+ chip = malloc(sizeof(*chip));
+ assert(chip);
+ memset(chip, 0, sizeof(*chip));
+ chip->fd = fd;
+ chip->chip_id = strtoul(dname, NULL, 16);
+ chip->next = xscom_chips;
+ xscom_chips = chip;
+
+ return chip->chip_id;
+}
+
+static bool xscom_check_dirname(const char *n)
+{
+ while(*n) {
+ char c = toupper(*(n++));
+
+ if ((c < 'A' || c > 'Z') &&
+ (c < '0' || c > '9'))
+ return false;
+ }
+ return true;
+}
+
+static uint32_t xscom_scan_chips(const char *base_path)
+{
+ int i, nfiles;
+ struct dirent **filelist;
+ uint32_t lower = 0xffffffff;
+
+ nfiles = scandir(base_path, &filelist, NULL, alphasort);
+ if (nfiles < 0) {
+ perror("Error accessing sysfs scom directory");
+ exit(1);
+ }
+ if (nfiles == 0) {
+ fprintf(stderr, "No SCOM dir found in sysfs\n");
+ exit(1);
+ }
+
+ for (i = 0; i < nfiles; i++) {
+ struct dirent *d = filelist[i];
+ uint32_t id;
+
+ if (d->d_type != DT_DIR)
+ continue;
+ if (!xscom_check_dirname(d->d_name))
+ continue;
+ id = xscom_add_chip(base_path, d->d_name);
+ if (id < lower)
+ lower = id;
+ free(d);
+ }
+
+ free(filelist);
+ return lower;
+}
+
+static struct xscom_chip *xscom_find_chip(uint32_t chip_id)
+{
+ struct xscom_chip *c;
+
+ for (c = xscom_chips; c; c = c->next)
+ if (c->chip_id == chip_id)
+ return c;
+ return NULL;
+}
+
+static uint64_t xscom_mangle_addr(uint64_t addr)
+{
+ uint64_t tmp;
+
+ /*
+ * Shift the top 4 bits (indirect mode) down by 4 bits so we
+ * don't lose going through the debugfs interfaces.
+ */
+ tmp = (addr & 0xf000000000000000) >> 4;
+ addr &= 0x00ffffffffffffff;
+ addr |= tmp;
+
+ /* Shift up by 3 for debugfs */
+ return addr << 3;
+}
+
+int xscom_read(uint32_t chip_id, uint64_t addr, uint64_t *val)
+{
+ struct xscom_chip *c = xscom_find_chip(chip_id);
+ int rc;
+
+ if (!c)
+ return -ENODEV;
+ addr = xscom_mangle_addr(addr);
+ lseek64(c->fd, addr, SEEK_SET);
+ rc = read(c->fd, val, 8);
+ if (rc < 0)
+ return -errno;
+ if (rc != 8)
+ return -EIO;
+ return 0;
+}
+
+int xscom_write(uint32_t chip_id, uint64_t addr, uint64_t val)
+{
+ struct xscom_chip *c = xscom_find_chip(chip_id);
+ int rc;
+
+ if (!c)
+ return -ENODEV;
+ addr = xscom_mangle_addr(addr);
+ lseek64(c->fd, addr, SEEK_SET);
+ rc = write(c->fd, &val, 8);
+ if (rc < 0)
+ return -errno;
+ if (rc != 8)
+ return -EIO;
+ return 0;
+}
+
+int xscom_read_ex(uint32_t ex_target_id, uint64_t addr, uint64_t *val)
+{
+ uint32_t chip_id = ex_target_id >> 4;;
+
+ addr |= (ex_target_id & 0xf) << 24;
+
+ /* XXX TODO: Special wakeup ? */
+
+ return xscom_read(chip_id, addr, val);
+}
+
+int xscom_write_ex(uint32_t ex_target_id, uint64_t addr, uint64_t val)
+{
+ uint32_t chip_id = ex_target_id >> 4;;
+
+ addr |= (ex_target_id & 0xf) << 24;
+
+ /* XXX TODO: Special wakeup ? */
+
+ return xscom_write(chip_id, addr, val);
+}
+
+bool xscom_readable(uint64_t addr)
+{
+ /* Top nibble 9 indicates form 1 indirect, which is write only */
+ if (((addr >> 60) & 0xf) == 9)
+ return false;
+ return true;
+}
+
+uint32_t xscom_init(void)
+{
+ return xscom_scan_chips(XSCOM_BASE_PATH);
+}