diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2018-04-20 10:17:59 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.ibm.com> | 2018-04-19 23:51:01 -0500 |
commit | df98e55e50a34e2b07ac5f3d864b58d1e015e144 (patch) | |
tree | d778a3ea69a878ea7f42a9d17287ad2cf286f8d8 | |
parent | 58b1e05b08d35fcdbb4f21e89561bb4e4c3d58c0 (diff) | |
download | skiboot-df98e55e50a34e2b07ac5f3d864b58d1e015e144.zip skiboot-df98e55e50a34e2b07ac5f3d864b58d1e015e144.tar.gz skiboot-df98e55e50a34e2b07ac5f3d864b58d1e015e144.tar.bz2 |
external: Add "lpc" tool
This is a little front-end to the lpc debugfs files to access
the LPC bus from userspace on the host.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
-rw-r--r-- | external/lpc/Makefile | 6 | ||||
-rw-r--r-- | external/lpc/lpc.c | 187 |
2 files changed, 193 insertions, 0 deletions
diff --git a/external/lpc/Makefile b/external/lpc/Makefile new file mode 100644 index 0000000..81e0b09 --- /dev/null +++ b/external/lpc/Makefile @@ -0,0 +1,6 @@ +all: lpc + +lpc: lpc.c + $(CC) -o $@ $^ + +clean: rm -rf *.[od] lpc diff --git a/external/lpc/lpc.c b/external/lpc/lpc.c new file mode 100644 index 0000000..b8a4fa1 --- /dev/null +++ b/external/lpc/lpc.c @@ -0,0 +1,187 @@ +/* Copyright 2014-2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * imitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <byteswap.h> +#include <stdint.h> +#include <stdbool.h> +#include <getopt.h> +#include <limits.h> +#include <arpa/inet.h> +#include <assert.h> +#include <endian.h> +#include <byteswap.h> + +#define SYSFS_PREFIX "/sys/kernel/debug/powerpc/lpc" + +int main(int argc, char *argv[]) +{ + char path[256]; + char *dot; + char *eq; + int fd, size = 4; + bool do_write = false; + bool big_endian = false; + uint32_t addr, val; + ssize_t rc; + + if (argc < 3) { + printf("Usage: %s <space> <addr>[.lLwWbBd[,size]][=value]\n", argv[0]); + return 0; + } + + eq = strchr(argv[2], '='); + if (eq) { + do_write = true; + val = strtoul(eq + 1, NULL, 0); + *eq = 0; + } + dot = strchr(argv[2], '.'); + if (dot) { + *(dot++) = 0; + switch(*dot) { + case 'L': + big_endian = true; + case 'l': + break; + case 'W': + big_endian = true; + case 'w': + size = 2; + break; + case 'B': + big_endian = true; + case 'b': + size = 1; + break; + default: + fprintf(stderr, "Invalid size specifier\n"); + exit(1); + } + } + addr = strtoul(argv[2], NULL, 0); + + memset(path, 0, sizeof(path)); + snprintf(path, 255, SYSFS_PREFIX "/%s", argv[1]); + fd = open(path, O_RDWR); + if (fd < 0) { + perror("Failed to open sysfs file"); + exit(1); + } + + lseek(fd, addr, SEEK_SET); + if (do_write) { + uint8_t v8; + uint16_t v16; + uint32_t v32; + + switch(size) { + case 1: + val &= 0xff; + v8 = val; + rc = write(fd, &v8, 1); + if (rc != 1) { + perror("Failed to write to LPC"); + exit(1); + } + printf("[%s] W 0x%08x.%c=0x%02x\n", + argv[1], addr, big_endian ? 'B' : 'b', val); + break; + case 2: + val &= 0xffff; +#if __BYTE_ORDER == __LITTLE_ENDIAN + v16 = big_endian ? bswap_16(val) : val; +#else + v16 = big_endian ? val : bswap_16(val); +#endif + rc = write(fd, &v16, 2); + if (rc != 2) { + perror("Failed to write to LPC"); + exit(1); + } + printf("[%s] W 0x%08x.%c=0x%04x\n", + argv[1], addr, big_endian ? 'W' : 'w', val); + break; + default: +#if __BYTE_ORDER == __LITTLE_ENDIAN + v32 = big_endian ? bswap_32(val) : val; +#else + v32 = big_endian ? val : bswap_32(val); +#endif + rc = write(fd, &v32, 4); + if (rc != 4) { + perror("Failed to write to LPC"); + exit(1); + } + printf("[%s] W 0x%08x.%c=0x%08x\n", + argv[1], addr, big_endian ? 'L' : 'l', val); + break; + } + } else { + uint8_t v8; + uint16_t v16; + uint32_t v32; + + switch(size) { + case 1: + rc = read(fd, &v8, 1); + if (rc != 1) { + perror("Failed to read from LPC"); + exit(1); + } + printf("[%s] R 0x%08x.%c=0x%02x\n", argv[1], addr, + big_endian ? 'B' : 'b', v8); + break; + case 2: + rc = read(fd, &v16, 2); + if (rc != 2) { + perror("Failed to read from LPC"); + exit(1); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + v16 = big_endian ? bswap_16(v16) : v16; +#else + v16 = big_endian ? v16 : bswap_16(v16); +#endif + printf("[%s] R 0x%08x.%c=0x%04x\n", argv[1], addr, + big_endian ? 'W' : 'w', v16); + break; + default: + rc = read(fd, &v32, 4); + if (rc != 4) { + perror("Failed to read from LPC"); + exit(1); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + v32 = big_endian ? bswap_32(v32) : v32; +#else + v32 = big_endian ? v32 : bswap_32(v32); +#endif + printf("[%s] R 0x%08x.%c=0x%08x\n", argv[1], addr, + big_endian ? 'L' : 'l', v32); + break; + } + } + return 0; +} |