]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/ppc/hw_nvram.c
Initial creation of sourceware repository
[thirdparty/binutils-gdb.git] / sim / ppc / hw_nvram.c
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
74 typedef 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
94 static void *
95 hw_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
103 typedef struct _hw_nvram_reg_spec {
104 unsigned32 base;
105 unsigned32 size;
106 } hw_nvram_reg_spec;
107
108 static void
109 hw_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, &reg);
123 reg_nr++) {
124 unsigned attach_size;
125 if (device_size_to_attach_size(device_parent(me),
126 &reg.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, nvram->sizeof_memory, 0);
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
161 static int
162 hw_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
177 static void
178 hw_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
202 static void
203 hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor)
204 {
205 error ("fixme - how do I set the localtime\n");
206 }
207
208 static unsigned
209 hw_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
228 static unsigned
229 hw_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
253 static 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
259 const device_descriptor hw_nvram_device_descriptor[] = {
260 { "nvram", hw_nvram_create, &hw_nvram_callbacks },
261 { NULL },
262 };
263
264 #endif /* _HW_NVRAM_C_ */