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"
36 nvram - non-volatile memory with clock
42 This device implements a small byte addressable non-volatile
43 memory. The top 8 bytes of this memory include a real-time clock.
49 reg = <address> <size> (required)
51 Specify the address/size of this device within its parents address
55 timezone = <integer> (optional)
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.
64 typedef struct _hw_nvram_device
{
66 unsigned sizeof_memory
;
75 unsigned addr_minutes
;
76 unsigned addr_seconds
;
77 unsigned addr_control
;
81 hw_nvram_create(const char *name
,
82 const device_unit
*unit_address
,
85 hw_nvram_device
*nvram
= ZALLOC(hw_nvram_device
);
89 typedef struct _hw_nvram_reg_spec
{
95 hw_nvram_init_address(device
*me
)
97 hw_nvram_device
*nvram
= (hw_nvram_device
*)device_data(me
);
99 /* use the generic init code to attach this device to its parent bus */
100 generic_device_init_address(me
);
102 /* find the first non zero reg property and use that as the device
104 if (nvram
->sizeof_memory
== 0) {
105 reg_property_spec reg
;
108 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
110 unsigned attach_size
;
111 if (device_size_to_attach_size(device_parent(me
),
112 ®
.size
, &attach_size
,
114 nvram
->sizeof_memory
= attach_size
;
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");
124 /* initialize the hw_nvram */
125 if (nvram
->memory
== NULL
) {
126 nvram
->memory
= zalloc(nvram
->sizeof_memory
);
129 memset(nvram
->memory
, 0, nvram
->sizeof_memory
);
131 if (device_find_property(me
, "timezone") == NULL
)
134 nvram
->timezone
= device_find_integer_property(me
, "timezone");
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;
148 hw_nvram_bcd(int val
)
153 return ((val
/ 10) << 4) + (val
% 10);
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 */
164 hw_nvram_update_clock(hw_nvram_device
*nvram
,
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
);
185 hw_nvram_set_clock(hw_nvram_device
*nvram
, cpu
*processor
)
187 error ("fixme - how do I set the localtime\n");
191 hw_nvram_io_read_buffer(device
*me
,
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
;
211 hw_nvram_io_write_buffer(device
*me
,
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
);
229 hw_nvram_update_clock(nvram
, processor
);
230 nvram
->memory
[address
] = data
;
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 */
241 const device_descriptor hw_nvram_device_descriptor
[] = {
242 { "nvram", hw_nvram_create
, &hw_nvram_callbacks
},
246 #endif /* _HW_NVRAM_C_ */