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 *
hw_nvram_create(const char * name,const device_unit * unit_address,const char * args)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
hw_nvram_init_address(device * me)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, 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 
161 static int
hw_nvram_bcd(int val)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
hw_nvram_update_clock(hw_nvram_device * nvram,cpu * processor)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
hw_nvram_set_clock(hw_nvram_device * nvram,cpu * processor)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
hw_nvram_io_read_buffer(device * me,void * dest,int space,unsigned_word addr,unsigned nr_bytes,cpu * processor,unsigned_word cia)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
hw_nvram_io_write_buffer(device * me,const void * source,int space,unsigned_word addr,unsigned nr_bytes,cpu * processor,unsigned_word cia)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_ */
265