hw_nvram.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* This file is part of the program psim.
  2. Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #ifndef _HW_NVRAM_C_
  15. #define _HW_NVRAM_C_
  16. #ifndef STATIC_INLINE_HW_NVRAM
  17. #define STATIC_INLINE_HW_NVRAM STATIC_INLINE
  18. #endif
  19. #include "device_table.h"
  20. #ifdef HAVE_TIME_H
  21. #include <time.h>
  22. #endif
  23. #ifdef HAVE_STRING_H
  24. #include <string.h>
  25. #else
  26. #ifdef HAVE_STRINGS_H
  27. #include <strings.h>
  28. #endif
  29. #endif
  30. /* DEVICE
  31. nvram - non-volatile memory with clock
  32. DESCRIPTION
  33. This device implements a small byte addressable non-volatile
  34. memory. The top 8 bytes of this memory include a real-time clock.
  35. PROPERTIES
  36. reg = <address> <size> (required)
  37. Specify the address/size of this device within its parents address
  38. space.
  39. timezone = <integer> (optional)
  40. Adjustment to the hosts current GMT (in seconds) that should be
  41. applied when updating the NVRAM's clock. If no timezone is
  42. specified, zero (GMT or UCT) is assumed.
  43. */
  44. typedef struct _hw_nvram_device {
  45. unsigned8 *memory;
  46. unsigned sizeof_memory;
  47. #ifdef HAVE_TIME_H
  48. time_t host_time;
  49. #else
  50. long host_time;
  51. #endif
  52. unsigned timezone;
  53. /* useful */
  54. unsigned addr_year;
  55. unsigned addr_month;
  56. unsigned addr_date;
  57. unsigned addr_day;
  58. unsigned addr_hour;
  59. unsigned addr_minutes;
  60. unsigned addr_seconds;
  61. unsigned addr_control;
  62. } hw_nvram_device;
  63. static void *
  64. hw_nvram_create(const char *name,
  65. const device_unit *unit_address,
  66. const char *args)
  67. {
  68. hw_nvram_device *nvram = ZALLOC(hw_nvram_device);
  69. return nvram;
  70. }
  71. typedef struct _hw_nvram_reg_spec {
  72. unsigned32 base;
  73. unsigned32 size;
  74. } hw_nvram_reg_spec;
  75. static void
  76. hw_nvram_init_address(device *me)
  77. {
  78. hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
  79. /* use the generic init code to attach this device to its parent bus */
  80. generic_device_init_address(me);
  81. /* find the first non zero reg property and use that as the device
  82. size */
  83. if (nvram->sizeof_memory == 0) {
  84. reg_property_spec reg;
  85. int reg_nr;
  86. for (reg_nr = 0;
  87. device_find_reg_array_property(me, "reg", reg_nr, &reg);
  88. reg_nr++) {
  89. unsigned attach_size;
  90. if (device_size_to_attach_size(device_parent(me),
  91. &reg.size, &attach_size,
  92. me)) {
  93. nvram->sizeof_memory = attach_size;
  94. break;
  95. }
  96. }
  97. if (nvram->sizeof_memory == 0)
  98. device_error(me, "reg property must contain a non-zero phys-addr:size tupple");
  99. if (nvram->sizeof_memory < 8)
  100. device_error(me, "NVRAM must be at least 8 bytes in size");
  101. }
  102. /* initialize the hw_nvram */
  103. if (nvram->memory == NULL) {
  104. nvram->memory = zalloc(nvram->sizeof_memory);
  105. }
  106. else
  107. memset(nvram->memory, 0, nvram->sizeof_memory);
  108. if (device_find_property(me, "timezone") == NULL)
  109. nvram->timezone = 0;
  110. else
  111. nvram->timezone = device_find_integer_property(me, "timezone");
  112. nvram->addr_year = nvram->sizeof_memory - 1;
  113. nvram->addr_month = nvram->sizeof_memory - 2;
  114. nvram->addr_date = nvram->sizeof_memory - 3;
  115. nvram->addr_day = nvram->sizeof_memory - 4;
  116. nvram->addr_hour = nvram->sizeof_memory - 5;
  117. nvram->addr_minutes = nvram->sizeof_memory - 6;
  118. nvram->addr_seconds = nvram->sizeof_memory - 7;
  119. nvram->addr_control = nvram->sizeof_memory - 8;
  120. }
  121. static int
  122. hw_nvram_bcd(int val)
  123. {
  124. val = val % 100;
  125. if (val < 0)
  126. val += 100;
  127. return ((val / 10) << 4) + (val % 10);
  128. }
  129. /* If reached an update interval and allowed, update the clock within
  130. the hw_nvram. While this function could be implemented using events
  131. it isn't on the assumption that the HW_NVRAM will hardly ever be
  132. referenced and hence there is little need in keeping the clock
  133. continually up-to-date */
  134. static void
  135. hw_nvram_update_clock(hw_nvram_device *nvram,
  136. cpu *processor)
  137. {
  138. #ifdef HAVE_TIME_H
  139. if (!(nvram->memory[nvram->addr_control] & 0xc0)) {
  140. time_t host_time = time(NULL);
  141. if (nvram->host_time != host_time) {
  142. time_t nvtime = host_time + nvram->timezone;
  143. struct tm *clock = gmtime(&nvtime);
  144. nvram->host_time = host_time;
  145. nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year);
  146. nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1);
  147. nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday);
  148. nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1);
  149. nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour);
  150. nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min);
  151. nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec);
  152. }
  153. }
  154. #else
  155. error("fixme - where do I find out GMT\n");
  156. #endif
  157. }
  158. static void
  159. hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor)
  160. {
  161. error ("fixme - how do I set the localtime\n");
  162. }
  163. static unsigned
  164. hw_nvram_io_read_buffer(device *me,
  165. void *dest,
  166. int space,
  167. unsigned_word addr,
  168. unsigned nr_bytes,
  169. cpu *processor,
  170. unsigned_word cia)
  171. {
  172. int i;
  173. hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
  174. for (i = 0; i < nr_bytes; i++) {
  175. unsigned address = (addr + i) % nvram->sizeof_memory;
  176. unsigned8 data = nvram->memory[address];
  177. hw_nvram_update_clock(nvram, processor);
  178. ((unsigned8*)dest)[i] = data;
  179. }
  180. return nr_bytes;
  181. }
  182. static unsigned
  183. hw_nvram_io_write_buffer(device *me,
  184. const void *source,
  185. int space,
  186. unsigned_word addr,
  187. unsigned nr_bytes,
  188. cpu *processor,
  189. unsigned_word cia)
  190. {
  191. int i;
  192. hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
  193. for (i = 0; i < nr_bytes; i++) {
  194. unsigned address = (addr + i) % nvram->sizeof_memory;
  195. unsigned8 data = ((unsigned8*)source)[i];
  196. if (address == nvram->addr_control
  197. && (data & 0x80) == 0
  198. && (nvram->memory[address] & 0x80) == 0x80)
  199. hw_nvram_set_clock(nvram, processor);
  200. else
  201. hw_nvram_update_clock(nvram, processor);
  202. nvram->memory[address] = data;
  203. }
  204. return nr_bytes;
  205. }
  206. static device_callbacks const hw_nvram_callbacks = {
  207. { hw_nvram_init_address, },
  208. { NULL, }, /* address */
  209. { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */
  210. };
  211. const device_descriptor hw_nvram_device_descriptor[] = {
  212. { "nvram", hw_nvram_create, &hw_nvram_callbacks },
  213. { NULL },
  214. };
  215. #endif /* _HW_NVRAM_C_ */