1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include "qemu/osdep.h"
27#include "hw/sysbus.h"
28#include "qemu/log.h"
29
30#include "qemu/bitops.h"
31#include "qapi/qmp/qerror.h"
32#include "sysemu/blockdev.h"
33#include "sysemu/block-backend.h"
34#include "migration/vmstate.h"
35#include "hw/qdev-properties.h"
36
37#include "hw/fdt_generic_util.h"
38
39#include "hw/sd/sd.h"
40#include "hw/sd/sdhci.h"
41#include "qapi/error.h"
42#include "sdhci-internal.h"
43
44#ifndef ZYNQMP_SDHCI_ERR_DEBUG
45#define ZYNQMP_SDHCI_ERR_DEBUG 0
46#endif
47
48#define TYPE_ZYNQMP_SDHCI "xilinx.zynqmp-sdhci"
49
50#define ZYNQMP_SDHCI(obj) \
51 OBJECT_CHECK(ZynqMPSDHCIState, (obj), TYPE_ZYNQMP_SDHCI)
52
53#define ZYNQMP_SDHCI_PARENT_CLASS \
54 object_class_get_parent(object_class_by_name(TYPE_ZYNQMP_SDHCI))
55
56typedef struct ZynqMPSDHCIState {
57
58 SDHCIState parent_obj;
59
60
61 SDState *card;
62 uint8_t drive_index;
63 bool is_mmc;
64} ZynqMPSDHCIState;
65
66static void zynqmp_sdhci_slottype_handler(void *opaque, int n, int level)
67{
68 ZynqMPSDHCIState *s = ZYNQMP_SDHCI(opaque);
69
70 assert(n == 0);
71
72 if (!s->card) {
73
74 return;
75 }
76
77 if (level != s->is_mmc) {
78 qemu_log_mask(LOG_GUEST_ERROR, "WARNING: Inserted %s Card but"
79 " Slot configured as %s\n",
80 s->is_mmc ? "MMC" : "SD",
81 level ? "MMC" : "SD");
82 }
83}
84
85static void zynqmp_sdhci_reset(DeviceState *dev)
86{
87 DeviceClass *dc_parent = DEVICE_CLASS(ZYNQMP_SDHCI_PARENT_CLASS);
88 ZynqMPSDHCIState *s = ZYNQMP_SDHCI(dev);
89 SDHCIState *p = SYSBUS_SDHCI(dev);
90
91 dc_parent->reset(dev);
92 if (s->is_mmc) {
93 p->capareg = deposit64(p->capareg, R_SDHC_CAPAB_SLOT_TYPE_SHIFT,
94 R_SDHC_CAPAB_SLOT_TYPE_LENGTH, 0x01);
95 }
96}
97
98static void zynqmp_sdhci_realize(DeviceState *dev, Error **errp)
99{
100 DeviceClass *dc_parent = DEVICE_CLASS(ZYNQMP_SDHCI_PARENT_CLASS);
101 ZynqMPSDHCIState *s = ZYNQMP_SDHCI(dev);
102 DriveInfo *di_sd, *di_mmc;
103 DeviceState *carddev_sd;
104 static int index_offset = 0;
105
106
107
108
109
110
111
112 if (!s->drive_index) {
113 s->drive_index += index_offset;
114 index_offset++;
115 }
116
117 qdev_prop_set_uint8(dev, "sd-spec-version", 3);
118 qdev_prop_set_uint64(dev, "capareg", 0x280737ec6481);
119 qdev_prop_set_uint8(dev, "uhs", UHS_I);
120 carddev_sd = qdev_new(TYPE_SD_CARD);
121 object_property_add_child(OBJECT(dev), "sd-card",
122 OBJECT(carddev_sd));
123 object_property_set_bool(OBJECT(carddev_sd), "spi", false, &error_fatal);
124
125
126
127
128
129
130
131
132 di_sd = drive_get_by_index(IF_SD , s->drive_index);
133 di_mmc = drive_get_by_index(IF_SD, (s->drive_index + 2));
134
135 if (di_sd && di_mmc) {
136 if (!di_sd->is_default && !di_mmc->is_default) {
137 error_setg(&error_fatal, "Cannot attach both an MMC"
138 " and an SD card into the same slot");
139 }
140 }
141
142 if (di_sd) {
143 qdev_prop_set_drive(carddev_sd, "drive", blk_by_legacy_dinfo(di_sd));
144 object_property_set_bool(OBJECT(carddev_sd), "mmc", false, &error_fatal);
145 }
146
147 if (di_mmc) {
148 qdev_prop_set_uint8(carddev_sd, "spec_version", SD_PHY_SPECv3_01_VERS);
149 qdev_prop_set_drive(carddev_sd, "drive", blk_by_legacy_dinfo(di_mmc));
150 object_property_set_bool(OBJECT(carddev_sd), "mmc", true, &error_fatal);
151 s->is_mmc = true;
152 }
153
154 qdev_realize(carddev_sd,
155 qdev_get_child_bus(DEVICE(dev), "sd-bus"),
156 &error_abort);
157 qdev_init_gpio_in_named(dev, zynqmp_sdhci_slottype_handler, "SLOTTYPE", 1);
158 s->card = SD_CARD(carddev_sd);
159
160 dc_parent->realize(dev, errp);
161}
162
163static Property zynqmp_sdhci_properties[] = {
164 DEFINE_PROP_UINT8("drive-index", ZynqMPSDHCIState, drive_index, 0),
165 DEFINE_PROP_END_OF_LIST(),
166};
167
168static void zynqmp_sdhci_class_init(ObjectClass *klass, void *data)
169{
170 DeviceClass *dc = DEVICE_CLASS(klass);
171
172 dc->realize = zynqmp_sdhci_realize;
173 device_class_set_props(dc, zynqmp_sdhci_properties);
174 dc->reset = zynqmp_sdhci_reset;
175}
176
177static const TypeInfo zynqmp_sdhci_info = {
178 .name = TYPE_ZYNQMP_SDHCI,
179 .parent = TYPE_SYSBUS_SDHCI,
180 .class_init = zynqmp_sdhci_class_init,
181 .instance_size = sizeof(ZynqMPSDHCIState),
182};
183
184static void zynqmp_sdhci_register_types(void)
185{
186 type_register_static(&zynqmp_sdhci_info);
187}
188
189type_init(zynqmp_sdhci_register_types)
190