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 3 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, see <http://www.gnu.org/licenses/>. 17 18 */ 19 20 21#ifndef _HW_NVRAM_C_ 22#define _HW_NVRAM_C_ 23 24#ifndef STATIC_INLINE_HW_NVRAM 25#define STATIC_INLINE_HW_NVRAM STATIC_INLINE 26#endif 27 28#include "device_table.h" 29 30#include <time.h> 31#include <string.h> 32 33/* DEVICE 34 35 36 nvram - non-volatile memory with clock 37 38 39 DESCRIPTION 40 41 42 This device implements a small byte addressable non-volatile 43 memory. The top 8 bytes of this memory include a real-time clock. 44 45 46 PROPERTIES 47 48 49 reg = <address> <size> (required) 50 51 Specify the address/size of this device within its parents address 52 space. 53 54 55 timezone = <integer> (optional) 56 57 Adjustment to the hosts current GMT (in seconds) that should be 58 applied when updating the NVRAM's clock. If no timezone is 59 specified, zero (GMT or UCT) is assumed. 60 61 62 */ 63 64typedef struct _hw_nvram_device { 65 uint8_t *memory; 66 unsigned sizeof_memory; 67 time_t host_time; 68 unsigned timezone; 69 /* useful */ 70 unsigned addr_year; 71 unsigned addr_month; 72 unsigned addr_date; 73 unsigned addr_day; 74 unsigned addr_hour; 75 unsigned addr_minutes; 76 unsigned addr_seconds; 77 unsigned addr_control; 78} hw_nvram_device; 79 80static void * 81hw_nvram_create(const char *name, 82 const device_unit *unit_address, 83 const char *args) 84{ 85 hw_nvram_device *nvram = ZALLOC(hw_nvram_device); 86 return nvram; 87} 88 89typedef struct _hw_nvram_reg_spec { 90 uint32_t base; 91 uint32_t size; 92} hw_nvram_reg_spec; 93 94static void 95hw_nvram_init_address(device *me) 96{ 97 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 98 99 /* use the generic init code to attach this device to its parent bus */ 100 generic_device_init_address(me); 101 102 /* find the first non zero reg property and use that as the device 103 size */ 104 if (nvram->sizeof_memory == 0) { 105 reg_property_spec reg; 106 int reg_nr; 107 for (reg_nr = 0; 108 device_find_reg_array_property(me, "reg", reg_nr, ®); 109 reg_nr++) { 110 unsigned attach_size; 111 if (device_size_to_attach_size(device_parent(me), 112 ®.size, &attach_size, 113 me)) { 114 nvram->sizeof_memory = attach_size; 115 break; 116 } 117 } 118 if (nvram->sizeof_memory == 0) 119 device_error(me, "reg property must contain a non-zero phys-addr:size tupple"); 120 if (nvram->sizeof_memory < 8) 121 device_error(me, "NVRAM must be at least 8 bytes in size"); 122 } 123 124 /* initialize the hw_nvram */ 125 if (nvram->memory == NULL) { 126 nvram->memory = zalloc(nvram->sizeof_memory); 127 } 128 else 129 memset(nvram->memory, 0, nvram->sizeof_memory); 130 131 if (device_find_property(me, "timezone") == NULL) 132 nvram->timezone = 0; 133 else 134 nvram->timezone = device_find_integer_property(me, "timezone"); 135 136 nvram->addr_year = nvram->sizeof_memory - 1; 137 nvram->addr_month = nvram->sizeof_memory - 2; 138 nvram->addr_date = nvram->sizeof_memory - 3; 139 nvram->addr_day = nvram->sizeof_memory - 4; 140 nvram->addr_hour = nvram->sizeof_memory - 5; 141 nvram->addr_minutes = nvram->sizeof_memory - 6; 142 nvram->addr_seconds = nvram->sizeof_memory - 7; 143 nvram->addr_control = nvram->sizeof_memory - 8; 144 145} 146 147static int 148hw_nvram_bcd(int val) 149{ 150 val = val % 100; 151 if (val < 0) 152 val += 100; 153 return ((val / 10) << 4) + (val % 10); 154} 155 156 157/* If reached an update interval and allowed, update the clock within 158 the hw_nvram. While this function could be implemented using events 159 it isn't on the assumption that the HW_NVRAM will hardly ever be 160 referenced and hence there is little need in keeping the clock 161 continually up-to-date */ 162 163static void 164hw_nvram_update_clock(hw_nvram_device *nvram, 165 cpu *processor) 166{ 167 if (!(nvram->memory[nvram->addr_control] & 0xc0)) { 168 time_t host_time = time(NULL); 169 if (nvram->host_time != host_time) { 170 time_t nvtime = host_time + nvram->timezone; 171 struct tm *clock = gmtime(&nvtime); 172 nvram->host_time = host_time; 173 nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year); 174 nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1); 175 nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday); 176 nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1); 177 nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour); 178 nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min); 179 nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec); 180 } 181 } 182} 183 184static void 185hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor) 186{ 187 error ("fixme - how do I set the localtime\n"); 188} 189 190static unsigned 191hw_nvram_io_read_buffer(device *me, 192 void *dest, 193 int space, 194 unsigned_word addr, 195 unsigned nr_bytes, 196 cpu *processor, 197 unsigned_word cia) 198{ 199 int i; 200 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 201 for (i = 0; i < nr_bytes; i++) { 202 unsigned address = (addr + i) % nvram->sizeof_memory; 203 uint8_t data = nvram->memory[address]; 204 hw_nvram_update_clock(nvram, processor); 205 ((uint8_t*)dest)[i] = data; 206 } 207 return nr_bytes; 208} 209 210static unsigned 211hw_nvram_io_write_buffer(device *me, 212 const void *source, 213 int space, 214 unsigned_word addr, 215 unsigned nr_bytes, 216 cpu *processor, 217 unsigned_word cia) 218{ 219 int i; 220 hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); 221 for (i = 0; i < nr_bytes; i++) { 222 unsigned address = (addr + i) % nvram->sizeof_memory; 223 uint8_t data = ((uint8_t*)source)[i]; 224 if (address == nvram->addr_control 225 && (data & 0x80) == 0 226 && (nvram->memory[address] & 0x80) == 0x80) 227 hw_nvram_set_clock(nvram, processor); 228 else 229 hw_nvram_update_clock(nvram, processor); 230 nvram->memory[address] = data; 231 } 232 return nr_bytes; 233} 234 235static device_callbacks const hw_nvram_callbacks = { 236 { hw_nvram_init_address, }, 237 { NULL, }, /* address */ 238 { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */ 239}; 240 241const device_descriptor hw_nvram_device_descriptor[] = { 242 { "nvram", hw_nvram_create, &hw_nvram_callbacks }, 243 { NULL }, 244}; 245 246#endif /* _HW_NVRAM_C_ */ 247