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