rs5c372.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * Ricoh RS5C372, R222x I2C RTC
  3. *
  4. * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com>
  5. *
  6. * Based on hw/rtc/ds1338.c
  7. *
  8. * SPDX-License-Identifier: GPL-2.0-or-later
  9. */
  10. #include "qemu/osdep.h"
  11. #include "hw/i2c/i2c.h"
  12. #include "hw/qdev-properties.h"
  13. #include "hw/resettable.h"
  14. #include "migration/vmstate.h"
  15. #include "qemu/bcd.h"
  16. #include "qom/object.h"
  17. #include "system/rtc.h"
  18. #include "trace.h"
  19. #define NVRAM_SIZE 0x10
  20. /* Flags definitions */
  21. #define SECONDS_CH 0x80
  22. #define HOURS_PM 0x20
  23. #define CTRL2_24 0x20
  24. #define TYPE_RS5C372 "rs5c372"
  25. OBJECT_DECLARE_SIMPLE_TYPE(RS5C372State, RS5C372)
  26. struct RS5C372State {
  27. I2CSlave parent_obj;
  28. int64_t offset;
  29. uint8_t wday_offset;
  30. uint8_t nvram[NVRAM_SIZE];
  31. uint8_t ptr;
  32. uint8_t tx_format;
  33. bool addr_byte;
  34. };
  35. static void capture_current_time(RS5C372State *s)
  36. {
  37. /*
  38. * Capture the current time into the secondary registers which will be
  39. * actually read by the data transfer operation.
  40. */
  41. struct tm now;
  42. qemu_get_timedate(&now, s->offset);
  43. s->nvram[0] = to_bcd(now.tm_sec);
  44. s->nvram[1] = to_bcd(now.tm_min);
  45. if (s->nvram[0xf] & CTRL2_24) {
  46. s->nvram[2] = to_bcd(now.tm_hour);
  47. } else {
  48. int tmp = now.tm_hour;
  49. if (tmp % 12 == 0) {
  50. tmp += 12;
  51. }
  52. if (tmp <= 12) {
  53. s->nvram[2] = to_bcd(tmp);
  54. } else {
  55. s->nvram[2] = HOURS_PM | to_bcd(tmp - 12);
  56. }
  57. }
  58. s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
  59. s->nvram[4] = to_bcd(now.tm_mday);
  60. s->nvram[5] = to_bcd(now.tm_mon + 1);
  61. s->nvram[6] = to_bcd(now.tm_year - 100);
  62. }
  63. static void inc_regptr(RS5C372State *s)
  64. {
  65. s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
  66. }
  67. static int rs5c372_event(I2CSlave *i2c, enum i2c_event event)
  68. {
  69. RS5C372State *s = RS5C372(i2c);
  70. switch (event) {
  71. case I2C_START_RECV:
  72. /*
  73. * In h/w, capture happens on any START condition, not just a
  74. * START_RECV, but there is no need to actually capture on
  75. * START_SEND, because the guest can't get at that data
  76. * without going through a START_RECV which would overwrite it.
  77. */
  78. capture_current_time(s);
  79. s->ptr = 0xf;
  80. break;
  81. case I2C_START_SEND:
  82. s->addr_byte = true;
  83. break;
  84. default:
  85. break;
  86. }
  87. return 0;
  88. }
  89. static uint8_t rs5c372_recv(I2CSlave *i2c)
  90. {
  91. RS5C372State *s = RS5C372(i2c);
  92. uint8_t res;
  93. res = s->nvram[s->ptr];
  94. trace_rs5c372_recv(s->ptr, res);
  95. inc_regptr(s);
  96. return res;
  97. }
  98. static int rs5c372_send(I2CSlave *i2c, uint8_t data)
  99. {
  100. RS5C372State *s = RS5C372(i2c);
  101. if (s->addr_byte) {
  102. s->ptr = data >> 4;
  103. s->tx_format = data & 0xf;
  104. s->addr_byte = false;
  105. return 0;
  106. }
  107. trace_rs5c372_send(s->ptr, data);
  108. if (s->ptr < 7) {
  109. /* Time register. */
  110. struct tm now;
  111. qemu_get_timedate(&now, s->offset);
  112. switch (s->ptr) {
  113. case 0:
  114. now.tm_sec = from_bcd(data & 0x7f);
  115. break;
  116. case 1:
  117. now.tm_min = from_bcd(data & 0x7f);
  118. break;
  119. case 2:
  120. if (s->nvram[0xf] & CTRL2_24) {
  121. now.tm_hour = from_bcd(data & 0x3f);
  122. } else {
  123. int tmp = from_bcd(data & (HOURS_PM - 1));
  124. if (data & HOURS_PM) {
  125. tmp += 12;
  126. }
  127. if (tmp % 12 == 0) {
  128. tmp -= 12;
  129. }
  130. now.tm_hour = tmp;
  131. }
  132. break;
  133. case 3:
  134. {
  135. /*
  136. * The day field is supposed to contain a value in the range
  137. * 1-7. Otherwise behavior is undefined.
  138. */
  139. int user_wday = (data & 7) - 1;
  140. s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
  141. }
  142. break;
  143. case 4:
  144. now.tm_mday = from_bcd(data & 0x3f);
  145. break;
  146. case 5:
  147. now.tm_mon = from_bcd(data & 0x1f) - 1;
  148. break;
  149. case 6:
  150. now.tm_year = from_bcd(data) + 100;
  151. break;
  152. }
  153. s->offset = qemu_timedate_diff(&now);
  154. } else {
  155. s->nvram[s->ptr] = data;
  156. }
  157. inc_regptr(s);
  158. return 0;
  159. }
  160. static void rs5c372_reset_hold(Object *obj, ResetType type)
  161. {
  162. RS5C372State *s = RS5C372(obj);
  163. /* The clock is running and synchronized with the host */
  164. s->offset = 0;
  165. s->wday_offset = 0;
  166. memset(s->nvram, 0, NVRAM_SIZE);
  167. s->ptr = 0;
  168. s->addr_byte = false;
  169. }
  170. static const VMStateDescription rs5c372_vmstate = {
  171. .name = "rs5c372",
  172. .version_id = 1,
  173. .minimum_version_id = 1,
  174. .fields = (const VMStateField[]) {
  175. VMSTATE_I2C_SLAVE(parent_obj, RS5C372State),
  176. VMSTATE_INT64(offset, RS5C372State),
  177. VMSTATE_UINT8_V(wday_offset, RS5C372State, 2),
  178. VMSTATE_UINT8_ARRAY(nvram, RS5C372State, NVRAM_SIZE),
  179. VMSTATE_UINT8(ptr, RS5C372State),
  180. VMSTATE_UINT8(tx_format, RS5C372State),
  181. VMSTATE_BOOL(addr_byte, RS5C372State),
  182. VMSTATE_END_OF_LIST()
  183. }
  184. };
  185. static void rs5c372_init(Object *obj)
  186. {
  187. qdev_prop_set_uint8(DEVICE(obj), "address", 0x32);
  188. }
  189. static void rs5c372_class_init(ObjectClass *klass, void *data)
  190. {
  191. DeviceClass *dc = DEVICE_CLASS(klass);
  192. I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
  193. ResettableClass *rc = RESETTABLE_CLASS(klass);
  194. k->event = rs5c372_event;
  195. k->recv = rs5c372_recv;
  196. k->send = rs5c372_send;
  197. dc->vmsd = &rs5c372_vmstate;
  198. rc->phases.hold = rs5c372_reset_hold;
  199. }
  200. static const TypeInfo rs5c372_types[] = {
  201. {
  202. .name = TYPE_RS5C372,
  203. .parent = TYPE_I2C_SLAVE,
  204. .instance_size = sizeof(RS5C372State),
  205. .instance_init = rs5c372_init,
  206. .class_init = rs5c372_class_init,
  207. },
  208. };
  209. DEFINE_TYPES(rs5c372_types)