Initial import of amiga-nvram

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..536b35c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+
+CC = $(CROSS_COMPILE)gcc
+
+CFLAGS = -Wall -Werror -O3 -fomit-frame-pointer -fno-strict-aliasing
+
+.PHONY:		all clean
+
+
+all:		amiga-nvram
+
+amiga-nvram:	amiga-nvram.c
+		$(CC) $(CFLAGS) -o amiga-nvram amiga-nvram.c
+
+clean:
+		$(RM) amiga-nvram
+
diff --git a/amiga-nvram.c b/amiga-nvram.c
new file mode 100644
index 0000000..e71612a
--- /dev/null
+++ b/amiga-nvram.c
@@ -0,0 +1,243 @@
+/*
+ *  Amiga NVRAM tool
+ *
+ *  (C) Copyright 2009 Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define NVRAM_FILE	"/sys/devices/platform/rtc-rp5c01/nvram"
+
+
+static const char *program_name;
+
+static const char *opt_nvram = NVRAM_FILE;
+static _Bool opt_checksum;
+static _Bool opt_quiet;
+
+struct nvram {
+	union {
+		uint32_t words[3];
+		uint8_t bytes[12];
+		struct {
+			uint32_t amiga;
+			uint32_t amix;
+			uint32_t shared;
+		} bits;
+	};
+	uint8_t checksum;
+} __attribute__ ((packed));
+
+static struct nvram nvram, backup;
+
+
+static void msg(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+static void msg(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (!opt_quiet) {
+		va_start(ap, fmt);
+		vprintf(fmt, ap);
+		va_end(ap);
+	}
+}
+
+static int nvram_read(void)
+{
+	int file;
+	ssize_t n;
+
+	file = open(opt_nvram, O_RDONLY);
+	if (file < 0) {
+		perror("Cannot open NVRAM for reading");
+		return file;
+	}
+
+	n = read(file, &nvram, sizeof(nvram));
+	if (n < 0) {
+		perror("Cannot read NVRAM");
+		return n;
+	}
+	if (n < sizeof(nvram.bytes)) {
+		fprintf(stderr, "Could read only %zu NVRAM bytes\n", n);
+		return -1;
+	}
+	if (n < sizeof(nvram)) {
+		fprintf(stderr,
+			"Warning: Could not read checksum, assuming zero\n");
+		nvram.checksum = 0;
+		return 0;
+	}
+
+	close(file);
+
+	memcpy(&backup, &nvram, sizeof(nvram));
+	return 0;
+}
+
+
+static int nvram_write(void)
+{
+	int file;
+	ssize_t n;
+
+	file = open(opt_nvram, O_WRONLY);
+	if (file < 0) {
+		perror("Cannot open NVRAM for writing");
+		return file;
+	}
+
+	n = write(file, &nvram, sizeof(nvram));
+	if (n < 0) {
+		perror("Cannot write NVRAM");
+		return n;
+	}
+	if (n < sizeof(nvram)) {
+		fprintf(stderr, "Could write only %zu NVRAM bytes\n", n);
+		return -1;
+	}
+
+	close(file);
+	return 0;
+}
+
+
+static const uint8_t table[16] = {
+	0x00, 0x57, 0xae, 0xf9, 0x0b, 0x5c, 0xa5, 0xf2,
+	0x16, 0x41, 0xb8, 0x7f, 0x1d, 0x4a, 0xb3, 0xe4
+};
+
+static uint8_t checksum_nibble(uint8_t sum, uint8_t nibble)
+{
+#if 0
+	nibble = sum ^ (nibble << 4);
+	nibble = (nibble >> 4) | (nibble << 4);
+	sum = (nibble & 0xf0) ^ table[nibble & 0x0f];
+	return sum;
+#else
+	uint8_t index = (sum >> 4) ^ nibble;
+	sum = (sum << 4) ^ table[index];
+	return sum;
+#endif
+}
+
+static uint8_t nvram_checksum(void)
+{
+	unsigned int i;
+	uint8_t sum = 0xff;
+
+	for (i = 0; i < sizeof(nvram.bytes); i++) {
+		sum = checksum_nibble(sum, nvram.bytes[i] >> 4);
+		sum = checksum_nibble(sum, nvram.bytes[i] & 0x0f);
+	}
+	return sum;
+}
+
+
+uint32_t be32_to_cpu(uint32_t x)
+{
+	uint8_t bytes[4];
+
+	*(uint32_t *)bytes = x;
+	return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
+}
+
+static void nvram_dump(void)
+{
+	printf("Amiga bits:  0x%08x\n", be32_to_cpu(nvram.bits.amiga));
+	printf("AMIX bits:   0x%08x\n", be32_to_cpu(nvram.bits.amix));
+	printf("Shared bits: 0x%08x\n", be32_to_cpu(nvram.bits.shared));
+}
+
+
+static void usage(void) __attribute__ ((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"\n"
+		"%s: [options]\n\n"
+		"Valid options are:\n"
+		"    -c, --checksum   Recalculate checksum\n"
+		"    -f, --file file  Specify NVRAM file (default: %s)\n"
+		"    -h, --help       Display this usage information\n"
+		"    -q, --quiet      Enable quiet mode\n"
+		"\n",
+		program_name, opt_nvram);
+	exit(1);
+}
+
+
+int main(int argc, char *argv[])
+{
+	program_name = argv[0];
+	int error;
+	uint8_t sum;
+
+	while (argc > 1) {
+		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+			usage();
+		else if (!strcmp(argv[1], "-c") ||
+			   !strcmp(argv[1], "--checksum")) {
+			opt_checksum = 1;
+			argv++;
+			argc--;
+		} else if (!strcmp(argv[1], "-f") ||
+			 !strcmp(argv[1], "--file")) {
+			if (argc <= 2)
+				usage();
+			opt_nvram = argv[2];
+			argv += 2;
+			argc -= 2;
+		} else if (!strcmp(argv[1], "-q") ||
+			   !strcmp(argv[1], "--quiet")) {
+			opt_quiet = 1;
+			argv++;
+			argc--;
+		} else
+			usage();
+	}
+
+	error = nvram_read();
+	if (error)
+		exit(-1);
+
+	sum = nvram_checksum();
+
+	if (!opt_quiet) {
+		nvram_dump();
+		printf("Checksum:          0x%02x ", nvram.checksum);
+		if (sum == nvram.checksum)
+			puts("[correct]");
+		else
+			printf("[not correct, should be 0x%02x]\n", sum);
+	}
+
+	if (opt_checksum && sum != nvram.checksum) {
+		msg("Correcting checksum\n");
+		nvram.checksum = sum;
+	}
+
+	if (memcmp(&nvram, &backup, sizeof(nvram))) {
+		msg("Saving NVRAM\n");
+		error = nvram_write();
+		if (error)
+			exit(-1);
+	}
+
+	exit(0);
+}
+