/* This file is part of the program psim. Copyright (C) 1994-1996, Andrew Cagney 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 #endif #ifdef HAVE_STRING_H #include #else #ifdef HAVE_STRINGS_H #include #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 =
. Determine where the device lives in the parents address space. timezone = . 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_ */