xref: /netbsd-src/external/gpl3/gdb/dist/sim/ppc/hw_nvram.c (revision 4b169a6ba595ae283ca507b26b15fdff40495b1c)
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 
64 typedef 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 
80 static void *
hw_nvram_create(const char * name,const device_unit * unit_address,const char * args)81 hw_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 
89 typedef struct _hw_nvram_reg_spec {
90   uint32_t base;
91   uint32_t size;
92 } hw_nvram_reg_spec;
93 
94 static void
hw_nvram_init_address(device * me)95 hw_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, &reg);
109 	 reg_nr++) {
110       unsigned attach_size;
111       if (device_size_to_attach_size(device_parent(me),
112 				     &reg.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 
147 static int
hw_nvram_bcd(int val)148 hw_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 
163 static void
hw_nvram_update_clock(hw_nvram_device * nvram,cpu * processor)164 hw_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 
184 static void
hw_nvram_set_clock(hw_nvram_device * nvram,cpu * processor)185 hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor)
186 {
187   error ("fixme - how do I set the localtime\n");
188 }
189 
190 static unsigned
hw_nvram_io_read_buffer(device * me,void * dest,int space,unsigned_word addr,unsigned nr_bytes,cpu * processor,unsigned_word cia)191 hw_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 
210 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)211 hw_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 
235 static 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 
241 const device_descriptor hw_nvram_device_descriptor[] = {
242   { "nvram", hw_nvram_create, &hw_nvram_callbacks },
243   { NULL },
244 };
245 
246 #endif /* _HW_NVRAM_C_ */
247