npcm7xx_adc.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Nuvoton NPCM7xx ADC Module
  3. *
  4. * Copyright 2020 Google LLC
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * for more details.
  15. */
  16. #include "qemu/osdep.h"
  17. #include "hw/adc/npcm7xx_adc.h"
  18. #include "hw/qdev-clock.h"
  19. #include "hw/qdev-properties.h"
  20. #include "hw/registerfields.h"
  21. #include "migration/vmstate.h"
  22. #include "qemu/log.h"
  23. #include "qemu/module.h"
  24. #include "qemu/timer.h"
  25. #include "qemu/units.h"
  26. #include "trace.h"
  27. REG32(NPCM7XX_ADC_CON, 0x0)
  28. REG32(NPCM7XX_ADC_DATA, 0x4)
  29. /* Register field definitions. */
  30. #define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
  31. #define NPCM7XX_ADC_CON_INT_EN BIT(21)
  32. #define NPCM7XX_ADC_CON_REFSEL BIT(19)
  33. #define NPCM7XX_ADC_CON_INT BIT(18)
  34. #define NPCM7XX_ADC_CON_EN BIT(17)
  35. #define NPCM7XX_ADC_CON_RST BIT(16)
  36. #define NPCM7XX_ADC_CON_CONV BIT(13)
  37. #define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
  38. #define NPCM7XX_ADC_MAX_RESULT 1023
  39. #define NPCM7XX_ADC_DEFAULT_IREF 2000000
  40. #define NPCM7XX_ADC_CONV_CYCLES 20
  41. #define NPCM7XX_ADC_RESET_CYCLES 10
  42. #define NPCM7XX_ADC_R0_INPUT 500000
  43. #define NPCM7XX_ADC_R1_INPUT 1500000
  44. static void npcm7xx_adc_reset(NPCM7xxADCState *s)
  45. {
  46. timer_del(&s->conv_timer);
  47. s->con = 0x000c0001;
  48. s->data = 0x00000000;
  49. }
  50. static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
  51. {
  52. uint32_t result;
  53. result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
  54. if (result > NPCM7XX_ADC_MAX_RESULT) {
  55. result = NPCM7XX_ADC_MAX_RESULT;
  56. }
  57. return result;
  58. }
  59. static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
  60. {
  61. return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
  62. }
  63. static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
  64. uint32_t cycles, uint32_t prescaler)
  65. {
  66. int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  67. int64_t ticks = cycles;
  68. int64_t ns;
  69. ticks *= prescaler;
  70. ns = clock_ticks_to_ns(clk, ticks);
  71. ns += now;
  72. timer_mod(timer, ns);
  73. }
  74. static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
  75. {
  76. uint32_t prescaler = npcm7xx_adc_prescaler(s);
  77. npcm7xx_adc_start_timer(s->clock, &s->conv_timer, NPCM7XX_ADC_CONV_CYCLES,
  78. prescaler);
  79. }
  80. static void npcm7xx_adc_convert_done(void *opaque)
  81. {
  82. NPCM7xxADCState *s = opaque;
  83. uint32_t input = NPCM7XX_ADC_CON_MUX(s->con);
  84. uint32_t ref = (s->con & NPCM7XX_ADC_CON_REFSEL)
  85. ? s->iref : s->vref;
  86. if (input >= NPCM7XX_ADC_NUM_INPUTS) {
  87. qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid input: %u\n",
  88. __func__, input);
  89. return;
  90. }
  91. s->data = npcm7xx_adc_convert(s->adci[input], ref);
  92. if (s->con & NPCM7XX_ADC_CON_INT_EN) {
  93. s->con |= NPCM7XX_ADC_CON_INT;
  94. qemu_irq_raise(s->irq);
  95. }
  96. s->con &= ~NPCM7XX_ADC_CON_CONV;
  97. }
  98. static void npcm7xx_adc_calibrate(NPCM7xxADCState *adc)
  99. {
  100. adc->calibration_r_values[0] = npcm7xx_adc_convert(NPCM7XX_ADC_R0_INPUT,
  101. adc->iref);
  102. adc->calibration_r_values[1] = npcm7xx_adc_convert(NPCM7XX_ADC_R1_INPUT,
  103. adc->iref);
  104. }
  105. static void npcm7xx_adc_write_con(NPCM7xxADCState *s, uint32_t new_con)
  106. {
  107. uint32_t old_con = s->con;
  108. /* Write ADC_INT to 1 to clear it */
  109. if (new_con & NPCM7XX_ADC_CON_INT) {
  110. new_con &= ~NPCM7XX_ADC_CON_INT;
  111. qemu_irq_lower(s->irq);
  112. } else if (old_con & NPCM7XX_ADC_CON_INT) {
  113. new_con |= NPCM7XX_ADC_CON_INT;
  114. }
  115. s->con = new_con;
  116. if (s->con & NPCM7XX_ADC_CON_RST) {
  117. npcm7xx_adc_reset(s);
  118. return;
  119. }
  120. if ((s->con & NPCM7XX_ADC_CON_EN)) {
  121. if (s->con & NPCM7XX_ADC_CON_CONV) {
  122. if (!(old_con & NPCM7XX_ADC_CON_CONV)) {
  123. npcm7xx_adc_start_convert(s);
  124. }
  125. } else {
  126. timer_del(&s->conv_timer);
  127. }
  128. }
  129. }
  130. static uint64_t npcm7xx_adc_read(void *opaque, hwaddr offset, unsigned size)
  131. {
  132. uint64_t value = 0;
  133. NPCM7xxADCState *s = opaque;
  134. switch (offset) {
  135. case A_NPCM7XX_ADC_CON:
  136. value = s->con;
  137. break;
  138. case A_NPCM7XX_ADC_DATA:
  139. value = s->data;
  140. break;
  141. default:
  142. qemu_log_mask(LOG_GUEST_ERROR,
  143. "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
  144. __func__, offset);
  145. break;
  146. }
  147. trace_npcm7xx_adc_read(DEVICE(s)->canonical_path, offset, value);
  148. return value;
  149. }
  150. static void npcm7xx_adc_write(void *opaque, hwaddr offset, uint64_t v,
  151. unsigned size)
  152. {
  153. NPCM7xxADCState *s = opaque;
  154. trace_npcm7xx_adc_write(DEVICE(s)->canonical_path, offset, v);
  155. switch (offset) {
  156. case A_NPCM7XX_ADC_CON:
  157. npcm7xx_adc_write_con(s, v);
  158. break;
  159. case A_NPCM7XX_ADC_DATA:
  160. qemu_log_mask(LOG_GUEST_ERROR,
  161. "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
  162. __func__, offset);
  163. break;
  164. default:
  165. qemu_log_mask(LOG_GUEST_ERROR,
  166. "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
  167. __func__, offset);
  168. break;
  169. }
  170. }
  171. static const struct MemoryRegionOps npcm7xx_adc_ops = {
  172. .read = npcm7xx_adc_read,
  173. .write = npcm7xx_adc_write,
  174. .endianness = DEVICE_LITTLE_ENDIAN,
  175. .valid = {
  176. .min_access_size = 4,
  177. .max_access_size = 4,
  178. .unaligned = false,
  179. },
  180. };
  181. static void npcm7xx_adc_enter_reset(Object *obj, ResetType type)
  182. {
  183. NPCM7xxADCState *s = NPCM7XX_ADC(obj);
  184. npcm7xx_adc_reset(s);
  185. }
  186. static void npcm7xx_adc_hold_reset(Object *obj)
  187. {
  188. NPCM7xxADCState *s = NPCM7XX_ADC(obj);
  189. qemu_irq_lower(s->irq);
  190. }
  191. static void npcm7xx_adc_init(Object *obj)
  192. {
  193. NPCM7xxADCState *s = NPCM7XX_ADC(obj);
  194. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  195. int i;
  196. sysbus_init_irq(sbd, &s->irq);
  197. timer_init_ns(&s->conv_timer, QEMU_CLOCK_VIRTUAL,
  198. npcm7xx_adc_convert_done, s);
  199. memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
  200. TYPE_NPCM7XX_ADC, 4 * KiB);
  201. sysbus_init_mmio(sbd, &s->iomem);
  202. s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
  203. for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
  204. object_property_add_uint32_ptr(obj, "adci[*]",
  205. &s->adci[i], OBJ_PROP_FLAG_READWRITE);
  206. }
  207. object_property_add_uint32_ptr(obj, "vref",
  208. &s->vref, OBJ_PROP_FLAG_WRITE);
  209. npcm7xx_adc_calibrate(s);
  210. }
  211. static const VMStateDescription vmstate_npcm7xx_adc = {
  212. .name = "npcm7xx-adc",
  213. .version_id = 0,
  214. .minimum_version_id = 0,
  215. .fields = (VMStateField[]) {
  216. VMSTATE_TIMER(conv_timer, NPCM7xxADCState),
  217. VMSTATE_UINT32(con, NPCM7xxADCState),
  218. VMSTATE_UINT32(data, NPCM7xxADCState),
  219. VMSTATE_CLOCK(clock, NPCM7xxADCState),
  220. VMSTATE_UINT32_ARRAY(adci, NPCM7xxADCState, NPCM7XX_ADC_NUM_INPUTS),
  221. VMSTATE_UINT32(vref, NPCM7xxADCState),
  222. VMSTATE_UINT32(iref, NPCM7xxADCState),
  223. VMSTATE_UINT16_ARRAY(calibration_r_values, NPCM7xxADCState,
  224. NPCM7XX_ADC_NUM_CALIB),
  225. VMSTATE_END_OF_LIST(),
  226. },
  227. };
  228. static Property npcm7xx_timer_properties[] = {
  229. DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_IREF),
  230. DEFINE_PROP_END_OF_LIST(),
  231. };
  232. static void npcm7xx_adc_class_init(ObjectClass *klass, void *data)
  233. {
  234. ResettableClass *rc = RESETTABLE_CLASS(klass);
  235. DeviceClass *dc = DEVICE_CLASS(klass);
  236. dc->desc = "NPCM7xx ADC Module";
  237. dc->vmsd = &vmstate_npcm7xx_adc;
  238. rc->phases.enter = npcm7xx_adc_enter_reset;
  239. rc->phases.hold = npcm7xx_adc_hold_reset;
  240. device_class_set_props(dc, npcm7xx_timer_properties);
  241. }
  242. static const TypeInfo npcm7xx_adc_info = {
  243. .name = TYPE_NPCM7XX_ADC,
  244. .parent = TYPE_SYS_BUS_DEVICE,
  245. .instance_size = sizeof(NPCM7xxADCState),
  246. .class_init = npcm7xx_adc_class_init,
  247. .instance_init = npcm7xx_adc_init,
  248. };
  249. static void npcm7xx_adc_register_types(void)
  250. {
  251. type_register_static(&npcm7xx_adc_info);
  252. }
  253. type_init(npcm7xx_adc_register_types);