1 /* This file is part of the program psim.
3 Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
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.
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.
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/>.
24 #ifndef STATIC_INLINE_HW_NVRAM
25 #define STATIC_INLINE_HW_NVRAM STATIC_INLINE
28 #include "device_table.h"
45 nvram - non-volatile memory with clock
51 This device implements a small byte addressable non-volatile
52 memory. The top 8 bytes of this memory include a real-time clock.
58 reg = <address> <size> (required)
60 Specify the address/size of this device within its parents address
64 timezone = <integer> (optional)
66 Adjustment to the hosts current GMT (in seconds) that should be
67 applied when updating the NVRAM's clock. If no timezone is
68 specified, zero (GMT or UCT) is assumed.
73 typedef struct _hw_nvram_device
{
75 unsigned sizeof_memory
;
88 unsigned addr_minutes
;
89 unsigned addr_seconds
;
90 unsigned addr_control
;
94 hw_nvram_create(const char *name
,
95 const device_unit
*unit_address
,
98 hw_nvram_device
*nvram
= ZALLOC(hw_nvram_device
);
102 typedef struct _hw_nvram_reg_spec
{
108 hw_nvram_init_address(device
*me
)
110 hw_nvram_device
*nvram
= (hw_nvram_device
*)device_data(me
);
112 /* use the generic init code to attach this device to its parent bus */
113 generic_device_init_address(me
);
115 /* find the first non zero reg property and use that as the device
117 if (nvram
->sizeof_memory
== 0) {
118 reg_property_spec reg
;
121 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
123 unsigned attach_size
;
124 if (device_size_to_attach_size(device_parent(me
),
125 ®
.size
, &attach_size
,
127 nvram
->sizeof_memory
= attach_size
;
131 if (nvram
->sizeof_memory
== 0)
132 device_error(me
, "reg property must contain a non-zero phys-addr:size tupple");
133 if (nvram
->sizeof_memory
< 8)
134 device_error(me
, "NVRAM must be at least 8 bytes in size");
137 /* initialize the hw_nvram */
138 if (nvram
->memory
== NULL
) {
139 nvram
->memory
= zalloc(nvram
->sizeof_memory
);
142 memset(nvram
->memory
, 0, nvram
->sizeof_memory
);
144 if (device_find_property(me
, "timezone") == NULL
)
147 nvram
->timezone
= device_find_integer_property(me
, "timezone");
149 nvram
->addr_year
= nvram
->sizeof_memory
- 1;
150 nvram
->addr_month
= nvram
->sizeof_memory
- 2;
151 nvram
->addr_date
= nvram
->sizeof_memory
- 3;
152 nvram
->addr_day
= nvram
->sizeof_memory
- 4;
153 nvram
->addr_hour
= nvram
->sizeof_memory
- 5;
154 nvram
->addr_minutes
= nvram
->sizeof_memory
- 6;
155 nvram
->addr_seconds
= nvram
->sizeof_memory
- 7;
156 nvram
->addr_control
= nvram
->sizeof_memory
- 8;
161 hw_nvram_bcd(int val
)
166 return ((val
/ 10) << 4) + (val
% 10);
170 /* If reached an update interval and allowed, update the clock within
171 the hw_nvram. While this function could be implemented using events
172 it isn't on the assumption that the HW_NVRAM will hardly ever be
173 referenced and hence there is little need in keeping the clock
174 continually up-to-date */
177 hw_nvram_update_clock(hw_nvram_device
*nvram
,
181 if (!(nvram
->memory
[nvram
->addr_control
] & 0xc0)) {
182 time_t host_time
= time(NULL
);
183 if (nvram
->host_time
!= host_time
) {
184 time_t nvtime
= host_time
+ nvram
->timezone
;
185 struct tm
*clock
= gmtime(&nvtime
);
186 nvram
->host_time
= host_time
;
187 nvram
->memory
[nvram
->addr_year
] = hw_nvram_bcd(clock
->tm_year
);
188 nvram
->memory
[nvram
->addr_month
] = hw_nvram_bcd(clock
->tm_mon
+ 1);
189 nvram
->memory
[nvram
->addr_date
] = hw_nvram_bcd(clock
->tm_mday
);
190 nvram
->memory
[nvram
->addr_day
] = hw_nvram_bcd(clock
->tm_wday
+ 1);
191 nvram
->memory
[nvram
->addr_hour
] = hw_nvram_bcd(clock
->tm_hour
);
192 nvram
->memory
[nvram
->addr_minutes
] = hw_nvram_bcd(clock
->tm_min
);
193 nvram
->memory
[nvram
->addr_seconds
] = hw_nvram_bcd(clock
->tm_sec
);
197 error("fixme - where do I find out GMT\n");
202 hw_nvram_set_clock(hw_nvram_device
*nvram
, cpu
*processor
)
204 error ("fixme - how do I set the localtime\n");
208 hw_nvram_io_read_buffer(device
*me
,
217 hw_nvram_device
*nvram
= (hw_nvram_device
*)device_data(me
);
218 for (i
= 0; i
< nr_bytes
; i
++) {
219 unsigned address
= (addr
+ i
) % nvram
->sizeof_memory
;
220 unsigned8 data
= nvram
->memory
[address
];
221 hw_nvram_update_clock(nvram
, processor
);
222 ((unsigned8
*)dest
)[i
] = data
;
228 hw_nvram_io_write_buffer(device
*me
,
237 hw_nvram_device
*nvram
= (hw_nvram_device
*)device_data(me
);
238 for (i
= 0; i
< nr_bytes
; i
++) {
239 unsigned address
= (addr
+ i
) % nvram
->sizeof_memory
;
240 unsigned8 data
= ((unsigned8
*)source
)[i
];
241 if (address
== nvram
->addr_control
242 && (data
& 0x80) == 0
243 && (nvram
->memory
[address
] & 0x80) == 0x80)
244 hw_nvram_set_clock(nvram
, processor
);
246 hw_nvram_update_clock(nvram
, processor
);
247 nvram
->memory
[address
] = data
;
252 static device_callbacks
const hw_nvram_callbacks
= {
253 { hw_nvram_init_address
, },
254 { NULL
, }, /* address */
255 { hw_nvram_io_read_buffer
, hw_nvram_io_write_buffer
}, /* IO */
258 const device_descriptor hw_nvram_device_descriptor
[] = {
259 { "nvram", hw_nvram_create
, &hw_nvram_callbacks
},
263 #endif /* _HW_NVRAM_C_ */