npcm7xx_sdhci.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * NPCM7xx SD-3.0 / eMMC-4.51 Host Controller
  3. *
  4. * Copyright (c) 2021 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/sd/sdhci.h"
  18. #include "hw/sd/npcm7xx_sdhci.h"
  19. #include "migration/vmstate.h"
  20. #include "sdhci-internal.h"
  21. #include "qemu/log.h"
  22. static uint64_t npcm7xx_sdhci_read(void *opaque, hwaddr addr, unsigned int size)
  23. {
  24. NPCM7xxSDHCIState *s = opaque;
  25. uint64_t val = 0;
  26. switch (addr) {
  27. case NPCM7XX_PRSTVALS_0:
  28. case NPCM7XX_PRSTVALS_1:
  29. case NPCM7XX_PRSTVALS_2:
  30. case NPCM7XX_PRSTVALS_3:
  31. case NPCM7XX_PRSTVALS_4:
  32. case NPCM7XX_PRSTVALS_5:
  33. val = s->regs.prstvals[(addr - NPCM7XX_PRSTVALS_0) / 2];
  34. break;
  35. case NPCM7XX_BOOTTOCTRL:
  36. val = s->regs.boottoctrl;
  37. break;
  38. default:
  39. qemu_log_mask(LOG_GUEST_ERROR, "SDHCI read of nonexistent reg: 0x%02"
  40. HWADDR_PRIx, addr);
  41. break;
  42. }
  43. return val;
  44. }
  45. static void npcm7xx_sdhci_write(void *opaque, hwaddr addr, uint64_t val,
  46. unsigned int size)
  47. {
  48. NPCM7xxSDHCIState *s = opaque;
  49. switch (addr) {
  50. case NPCM7XX_BOOTTOCTRL:
  51. s->regs.boottoctrl = val;
  52. break;
  53. default:
  54. qemu_log_mask(LOG_GUEST_ERROR, "SDHCI write of nonexistent reg: 0x%02"
  55. HWADDR_PRIx, addr);
  56. break;
  57. }
  58. }
  59. static bool npcm7xx_sdhci_check_mem_op(void *opaque, hwaddr addr,
  60. unsigned size, bool is_write,
  61. MemTxAttrs attrs)
  62. {
  63. switch (addr) {
  64. case NPCM7XX_PRSTVALS_0:
  65. case NPCM7XX_PRSTVALS_1:
  66. case NPCM7XX_PRSTVALS_2:
  67. case NPCM7XX_PRSTVALS_3:
  68. case NPCM7XX_PRSTVALS_4:
  69. case NPCM7XX_PRSTVALS_5:
  70. /* RO Word */
  71. return !is_write && size == 2;
  72. case NPCM7XX_BOOTTOCTRL:
  73. /* R/W Dword */
  74. return size == 4;
  75. default:
  76. return false;
  77. }
  78. }
  79. static const MemoryRegionOps npcm7xx_sdhci_ops = {
  80. .read = npcm7xx_sdhci_read,
  81. .write = npcm7xx_sdhci_write,
  82. .endianness = DEVICE_NATIVE_ENDIAN,
  83. .valid = {
  84. .min_access_size = 1,
  85. .max_access_size = 4,
  86. .unaligned = false,
  87. .accepts = npcm7xx_sdhci_check_mem_op,
  88. },
  89. };
  90. static void npcm7xx_sdhci_realize(DeviceState *dev, Error **errp)
  91. {
  92. NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev);
  93. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  94. SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci);
  95. memory_region_init(&s->container, OBJECT(s),
  96. "npcm7xx.sdhci-container", 0x1000);
  97. sysbus_init_mmio(sbd, &s->container);
  98. memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_sdhci_ops, s,
  99. TYPE_NPCM7XX_SDHCI, NPCM7XX_SDHCI_REGSIZE);
  100. memory_region_add_subregion_overlap(&s->container, NPCM7XX_PRSTVALS,
  101. &s->iomem, 1);
  102. sysbus_realize(sbd_sdhci, errp);
  103. memory_region_add_subregion(&s->container, 0,
  104. sysbus_mmio_get_region(sbd_sdhci, 0));
  105. /* propagate irq and "sd-bus" from generic-sdhci */
  106. sysbus_pass_irq(sbd, sbd_sdhci);
  107. s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus");
  108. /* Set the read only preset values. */
  109. memset(s->regs.prstvals, 0, sizeof(s->regs.prstvals));
  110. s->regs.prstvals[0] = NPCM7XX_PRSTVALS_0_RESET;
  111. s->regs.prstvals[1] = NPCM7XX_PRSTVALS_1_RESET;
  112. s->regs.prstvals[3] = NPCM7XX_PRSTVALS_3_RESET;
  113. }
  114. static void npcm7xx_sdhci_reset(DeviceState *dev)
  115. {
  116. NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev);
  117. device_cold_reset(DEVICE(&s->sdhci));
  118. s->regs.boottoctrl = 0;
  119. s->sdhci.prnsts = NPCM7XX_PRSNTS_RESET;
  120. s->sdhci.blkgap = NPCM7XX_BLKGAP_RESET;
  121. s->sdhci.capareg = NPCM7XX_CAPAB_RESET;
  122. s->sdhci.maxcurr = NPCM7XX_MAXCURR_RESET;
  123. s->sdhci.version = NPCM7XX_HCVER_RESET;
  124. }
  125. static const VMStateDescription vmstate_npcm7xx_sdhci = {
  126. .name = TYPE_NPCM7XX_SDHCI,
  127. .version_id = 0,
  128. .fields = (const VMStateField[]) {
  129. VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState),
  130. VMSTATE_END_OF_LIST(),
  131. },
  132. };
  133. static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data)
  134. {
  135. DeviceClass *dc = DEVICE_CLASS(classp);
  136. dc->desc = "NPCM7xx SD/eMMC Host Controller";
  137. dc->realize = npcm7xx_sdhci_realize;
  138. device_class_set_legacy_reset(dc, npcm7xx_sdhci_reset);
  139. dc->vmsd = &vmstate_npcm7xx_sdhci;
  140. }
  141. static void npcm7xx_sdhci_instance_init(Object *obj)
  142. {
  143. NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj);
  144. object_initialize_child(OBJECT(s), TYPE_SYSBUS_SDHCI, &s->sdhci,
  145. TYPE_SYSBUS_SDHCI);
  146. }
  147. static const TypeInfo npcm7xx_sdhci_types[] = {
  148. {
  149. .name = TYPE_NPCM7XX_SDHCI,
  150. .parent = TYPE_SYS_BUS_DEVICE,
  151. .instance_size = sizeof(NPCM7xxSDHCIState),
  152. .instance_init = npcm7xx_sdhci_instance_init,
  153. .class_init = npcm7xx_sdhci_class_init,
  154. },
  155. };
  156. DEFINE_TYPES(npcm7xx_sdhci_types)