1/* This file is part of the program psim. 2 3 Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 21 22#ifndef _HW_NVRAM_C_ 23#define _HW_NVRAM_C_ 24 25#ifndef STATIC_INLINE_HW_NVRAM 26#define STATIC_INLINE_HW_NVRAM STATIC_INLINE 27#endif 28 29#include "device_table.h" 30 31#ifdef HAVE_TIME_H 32#include <time.h> 33#endif 34 35#ifdef HAVE_STRING_H 36#include <string.h> 37#else 38#ifdef HAVE_STRINGS_H 39#include <strings.h> 40#endif 41#endif 42 43/* DEVICE 44 45 46 nvram - non-volatile memory with clock 47 48 49 DESCRIPTION 50 51 52 This device implements a small byte addressable non-volatile 53 memory. The top 8 bytes of this memory include a real-time clock. 54 55 56 PROPERTIES 57 58 59 reg = <address> <size> (required) 60 61 Specify the address/size of this device within its parents address 62 space. 63 64 65 timezone = <integer> (optional) 66 67 Adjustment to the hosts current GMT (in seconds) that should be 68 applied when updating the NVRAM's clock. If no timezone is 69 specified, zero (GMT or UCT) is assumed. 70 71 72 */ 73 74typedef struct _hw_nvram_device { 75 unsigned8 *memory; 76 unsigned sizeof_memory; 77#ifdef HAVE_TIME_H 78 time_t host_time; 79#else 80 long host_time; 81#endif 82 unsigned timezone; 83 /* useful */ 84 unsigned addr_year; 85 unsigned addr_month; 86 unsigned addr_date; 87 unsigned addr_day; 88 unsigned addr_hour; 89 unsigned addr_minutes; 90 unsigned addr_seconds; 91 unsigned addr_control; 92} hw_nvram_device; 93 94static void * 95hw_nvram_create(const char *name, 96 const device_unit *unit_address, 97 const char *args) 98{ 99 hw_nvram_device *nvram = ZALLOC(hw_nvram_device); 100 return nvram; 101} 102 103typedef struct _hw_nvram_reg_spec { 104 unsigned32 base; 105 unsigned32 size; 106} hw_nvram_reg_spec; 107 108static void 109hw_nvram_init_address(device *me) 110{ 111 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 112 113 /* use the generic init code to attach this device to its parent bus */ 114 generic_device_init_address(me); 115 116 /* find the first non zero reg property and use that as the device 117 size */ 118 if (nvram->sizeof_memory == 0) { 119 reg_property_spec reg; 120 int reg_nr; 121 for (reg_nr = 0; 122 device_find_reg_array_property(me, "reg", reg_nr, ®); 123 reg_nr++) { 124 unsigned attach_size; 125 if (device_size_to_attach_size(device_parent(me), 126 ®.size, &attach_size, 127 me)) { 128 nvram->sizeof_memory = attach_size; 129 break; 130 } 131 } 132 if (nvram->sizeof_memory == 0) 133 device_error(me, "reg property must contain a non-zero phys-addr:size tupple"); 134 if (nvram->sizeof_memory < 8) 135 device_error(me, "NVRAM must be at least 8 bytes in size"); 136 } 137 138 /* initialize the hw_nvram */ 139 if (nvram->memory == NULL) { 140 nvram->memory = zalloc(nvram->sizeof_memory); 141 } 142 else 143 memset(nvram->memory, 0, nvram->sizeof_memory); 144 145 if (device_find_property(me, "timezone") == NULL) 146 nvram->timezone = 0; 147 else 148 nvram->timezone = device_find_integer_property(me, "timezone"); 149 150 nvram->addr_year = nvram->sizeof_memory - 1; 151 nvram->addr_month = nvram->sizeof_memory - 2; 152 nvram->addr_date = nvram->sizeof_memory - 3; 153 nvram->addr_day = nvram->sizeof_memory - 4; 154 nvram->addr_hour = nvram->sizeof_memory - 5; 155 nvram->addr_minutes = nvram->sizeof_memory - 6; 156 nvram->addr_seconds = nvram->sizeof_memory - 7; 157 nvram->addr_control = nvram->sizeof_memory - 8; 158 159} 160 161static int 162hw_nvram_bcd(int val) 163{ 164 val = val % 100; 165 if (val < 0) 166 val += 100; 167 return ((val / 10) << 4) + (val % 10); 168} 169 170 171/* If reached an update interval and allowed, update the clock within 172 the hw_nvram. While this function could be implemented using events 173 it isn't on the assumption that the HW_NVRAM will hardly ever be 174 referenced and hence there is little need in keeping the clock 175 continually up-to-date */ 176 177static void 178hw_nvram_update_clock(hw_nvram_device *nvram, 179 cpu *processor) 180{ 181#ifdef HAVE_TIME_H 182 if (!(nvram->memory[nvram->addr_control] & 0xc0)) { 183 time_t host_time = time(NULL); 184 if (nvram->host_time != host_time) { 185 time_t nvtime = host_time + nvram->timezone; 186 struct tm *clock = gmtime(&nvtime); 187 nvram->host_time = host_time; 188 nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year); 189 nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1); 190 nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday); 191 nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1); 192 nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour); 193 nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min); 194 nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec); 195 } 196 } 197#else 198 error("fixme - where do I find out GMT\n"); 199#endif 200} 201 202static void 203hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor) 204{ 205 error ("fixme - how do I set the localtime\n"); 206} 207 208static unsigned 209hw_nvram_io_read_buffer(device *me, 210 void *dest, 211 int space, 212 unsigned_word addr, 213 unsigned nr_bytes, 214 cpu *processor, 215 unsigned_word cia) 216{ 217 int i; 218 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 219 for (i = 0; i < nr_bytes; i++) { 220 unsigned address = (addr + i) % nvram->sizeof_memory; 221 unsigned8 data = nvram->memory[address]; 222 hw_nvram_update_clock(nvram, processor); 223 ((unsigned8*)dest)[i] = data; 224 } 225 return nr_bytes; 226} 227 228static unsigned 229hw_nvram_io_write_buffer(device *me, 230 const void *source, 231 int space, 232 unsigned_word addr, 233 unsigned nr_bytes, 234 cpu *processor, 235 unsigned_word cia) 236{ 237 int i; 238 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 239 for (i = 0; i < nr_bytes; i++) { 240 unsigned address = (addr + i) % nvram->sizeof_memory; 241 unsigned8 data = ((unsigned8*)source)[i]; 242 if (address == nvram->addr_control 243 && (data & 0x80) == 0 244 && (nvram->memory[address] & 0x80) == 0x80) 245 hw_nvram_set_clock(nvram, processor); 246 else 247 hw_nvram_update_clock(nvram, processor); 248 nvram->memory[address] = data; 249 } 250 return nr_bytes; 251} 252 253static device_callbacks const hw_nvram_callbacks = { 254 { hw_nvram_init_address, }, 255 { NULL, }, /* address */ 256 { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */ 257}; 258 259const device_descriptor hw_nvram_device_descriptor[] = { 260 { "nvram", hw_nvram_create, &hw_nvram_callbacks }, 261 { NULL }, 262}; 263 264#endif /* _HW_NVRAM_C_ */ 265