aboutsummaryrefslogtreecommitdiff
path: root/sim/ppc/hw_nvram.c
diff options
context:
space:
mode:
authorMichael Meissner <gnu@the-meissners.org>1996-04-17 20:09:36 +0000
committerMichael Meissner <gnu@the-meissners.org>1996-04-17 20:09:36 +0000
commitd4d3c7adbb45714fb2d1ddc7a94627729bf0a82a (patch)
tree4770b119db1d5938bf458b014ea5e16693838448 /sim/ppc/hw_nvram.c
parent221d1e19de5852f4b176484cdbc17f0233702c82 (diff)
downloadfsf-binutils-gdb-d4d3c7adbb45714fb2d1ddc7a94627729bf0a82a.zip
fsf-binutils-gdb-d4d3c7adbb45714fb2d1ddc7a94627729bf0a82a.tar.gz
fsf-binutils-gdb-d4d3c7adbb45714fb2d1ddc7a94627729bf0a82a.tar.bz2
update from Andrew
Diffstat (limited to 'sim/ppc/hw_nvram.c')
-rw-r--r--sim/ppc/hw_nvram.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/sim/ppc/hw_nvram.c b/sim/ppc/hw_nvram.c
new file mode 100644
index 0000000..fbd8e11
--- /dev/null
+++ b/sim/ppc/hw_nvram.c
@@ -0,0 +1,234 @@
+/* This file is part of the program psim.
+
+ Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#ifndef _HW_NVRAM_C_
+#define _HW_NVRAM_C_
+
+#ifndef STATIC_INLINE_HW_NVRAM
+#define STATIC_INLINE_HW_NVRAM STATIC_INLINE
+#endif
+
+#include "device_table.h"
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+/* NVRAM - non-volatile memory with optional clock.
+
+ Description:
+
+ This device implements a small byte addressable non-volatile memory
+ component. The component may include an optional real-time clock
+ at its upper addresses.
+
+ Properties:
+
+ reg = <address> <size>. Determine where the device lives in the
+ parents address space.
+
+ timezone = <integer>. Adjustment to current host's GMT (in secons)
+ that should be applied when updating the NVRAM's clock. */
+
+typedef struct _hw_nvram_device {
+ unsigned8 *memory;
+ unsigned sizeof_memory;
+#ifdef HAVE_TIME_H
+ time_t host_time;
+#else
+ long host_time;
+#endif
+ unsigned timezone;
+ /* useful */
+ unsigned addr_year;
+ unsigned addr_month;
+ unsigned addr_date;
+ unsigned addr_day;
+ unsigned addr_hour;
+ unsigned addr_minutes;
+ unsigned addr_seconds;
+ unsigned addr_control;
+} hw_nvram_device;
+
+static void *
+hw_nvram_create(const char *name,
+ const device_unit *unit_address,
+ const char *args,
+ device *parent)
+{
+ hw_nvram_device *hw_nvram = ZALLOC(hw_nvram_device);
+ return hw_nvram;
+}
+
+typedef struct _hw_nvram_reg_spec {
+ unsigned32 base;
+ unsigned32 size;
+} hw_nvram_reg_spec;
+
+static void
+hw_nvram_init_address(device *me)
+{
+ hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me);
+ const device_property *reg = device_find_array_property(me, "reg");
+ const hw_nvram_reg_spec *spec = reg->array;
+ int nr_entries = reg->sizeof_array / sizeof(*spec);
+
+ if ((reg->sizeof_array % sizeof(*spec)) != 0)
+ error("devices/%s reg property of incorrect size\n", device_name(me));
+ if (nr_entries > 1)
+ error("devices/%s reg property contains multiple specs\n",
+ device_name(me));
+
+ /* initialize the hw_nvram */
+ if (hw_nvram->memory == NULL) {
+ hw_nvram->sizeof_memory = BE2H_4(spec->size);
+ hw_nvram->memory = zalloc(hw_nvram->sizeof_memory);
+ }
+ else
+ memset(hw_nvram->memory, hw_nvram->sizeof_memory, 0);
+
+ hw_nvram->timezone = device_find_integer_property(me, "timezone");
+
+ hw_nvram->addr_year = hw_nvram->sizeof_memory - 1;
+ hw_nvram->addr_month = hw_nvram->sizeof_memory - 2;
+ hw_nvram->addr_date = hw_nvram->sizeof_memory - 3;
+ hw_nvram->addr_day = hw_nvram->sizeof_memory - 4;
+ hw_nvram->addr_hour = hw_nvram->sizeof_memory - 5;
+ hw_nvram->addr_minutes = hw_nvram->sizeof_memory - 6;
+ hw_nvram->addr_seconds = hw_nvram->sizeof_memory - 7;
+ hw_nvram->addr_control = hw_nvram->sizeof_memory - 8;
+
+ device_attach_address(device_parent(me),
+ device_name(me),
+ attach_callback,
+ 0 /*address space*/,
+ BE2H_4(spec->base),
+ hw_nvram->sizeof_memory,
+ access_read_write_exec,
+ me);
+}
+
+static int
+hw_nvram_bcd(int val)
+{
+ return ((val / 10) << 4) + (val % 10);
+}
+
+/* If reached an update interval and allowed, update the clock within
+ the hw_nvram. While this function could be implemented using events
+ it isn't on the assumption that the HW_NVRAM will hardly ever be
+ referenced and hence there is little need in keeping the clock
+ continually up-to-date */
+static void
+hw_nvram_update_clock(hw_nvram_device *hw_nvram, cpu *processor)
+{
+#ifdef HAVE_TIME_H
+ if (!(hw_nvram->memory[hw_nvram->addr_control] & 0xc0)) {
+ time_t host_time = time(NULL);
+ if (hw_nvram->host_time != host_time) {
+ time_t nvtime = hw_nvram->host_time + hw_nvram->timezone;
+ struct tm *clock = gmtime(&nvtime);
+ hw_nvram->host_time = host_time;
+ hw_nvram->memory[hw_nvram->addr_year] = hw_nvram_bcd(clock->tm_year);
+ hw_nvram->memory[hw_nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1);
+ hw_nvram->memory[hw_nvram->addr_date] = hw_nvram_bcd(clock->tm_mday);
+ hw_nvram->memory[hw_nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1);
+ hw_nvram->memory[hw_nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour);
+ hw_nvram->memory[hw_nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min);
+ hw_nvram->memory[hw_nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec);
+ }
+ }
+#else
+ error("fixme - where do I find out GMT\n");
+#endif
+}
+
+static void
+hw_nvram_set_clock(hw_nvram_device *hw_nvram, cpu *processor)
+{
+ error ("fixme - how do I set the localtime\n");
+}
+
+static unsigned
+hw_nvram_io_read_buffer(device *me,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ int i;
+ hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me);
+ for (i = 0; i < nr_bytes; i++) {
+ unsigned address = (addr + i) % hw_nvram->sizeof_memory;
+ unsigned8 data = hw_nvram->memory[address];
+ hw_nvram_update_clock(hw_nvram, processor);
+ ((unsigned8*)dest)[i] = data;
+ }
+ return nr_bytes;
+}
+
+static unsigned
+hw_nvram_io_write_buffer(device *me,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ int i;
+ hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me);
+ for (i = 0; i < nr_bytes; i++) {
+ unsigned address = (addr + i) % hw_nvram->sizeof_memory;
+ unsigned8 data = ((unsigned8*)source)[i];
+ if (address == hw_nvram->addr_control
+ && (data & 0x80) == 0
+ && (hw_nvram->memory[address] & 0x80) == 0x80)
+ hw_nvram_set_clock(hw_nvram, processor);
+ else
+ hw_nvram_update_clock(hw_nvram, processor);
+ hw_nvram->memory[address] = data;
+ }
+ return nr_bytes;
+}
+
+static device_callbacks const hw_nvram_callbacks = {
+ { hw_nvram_init_address, },
+ { NULL, }, /* address */
+ { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */
+};
+
+const device_descriptor hw_nvram_device_descriptor[] = {
+ { "nvram", hw_nvram_create, &hw_nvram_callbacks },
+ { NULL },
+};
+
+#endif /* _HW_NVRAM_C_ */