2 * Aspeed SD Host Controller
3 * Eddie James <eajames@linux.ibm.com>
5 * Copyright (C) 2019 IBM Corp
6 * SPDX-License-Identifier: GPL-2.0-or-later
9 #include "qemu/osdep.h"
11 #include "qemu/error-report.h"
12 #include "hw/sd/aspeed_sdhci.h"
13 #include "qapi/error.h"
15 #include "migration/vmstate.h"
16 #include "hw/qdev-properties.h"
19 #define ASPEED_SDHCI_INFO 0x00
20 #define ASPEED_SDHCI_INFO_SLOT1 (1 << 17)
21 #define ASPEED_SDHCI_INFO_SLOT0 (1 << 16)
22 #define ASPEED_SDHCI_INFO_RESET (1 << 0)
23 #define ASPEED_SDHCI_DEBOUNCE 0x04
24 #define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005
25 #define ASPEED_SDHCI_BUS 0x08
26 #define ASPEED_SDHCI_SDIO_140 0x10
27 #define ASPEED_SDHCI_SDIO_148 0x18
28 #define ASPEED_SDHCI_SDIO_240 0x20
29 #define ASPEED_SDHCI_SDIO_248 0x28
30 #define ASPEED_SDHCI_WP_POL 0xec
31 #define ASPEED_SDHCI_CARD_DET 0xf0
32 #define ASPEED_SDHCI_IRQ_STAT 0xfc
34 #define TO_REG(addr) ((addr) / sizeof(uint32_t))
36 static uint64_t aspeed_sdhci_read(void *opaque
, hwaddr addr
, unsigned int size
)
39 AspeedSDHCIState
*sdhci
= opaque
;
42 case ASPEED_SDHCI_SDIO_140
:
43 val
= (uint32_t)sdhci
->slots
[0].capareg
;
45 case ASPEED_SDHCI_SDIO_148
:
46 val
= (uint32_t)sdhci
->slots
[0].maxcurr
;
48 case ASPEED_SDHCI_SDIO_240
:
49 val
= (uint32_t)sdhci
->slots
[1].capareg
;
51 case ASPEED_SDHCI_SDIO_248
:
52 val
= (uint32_t)sdhci
->slots
[1].maxcurr
;
55 if (addr
< ASPEED_SDHCI_REG_SIZE
) {
56 val
= sdhci
->regs
[TO_REG(addr
)];
58 qemu_log_mask(LOG_GUEST_ERROR
,
59 "%s: Out-of-bounds read at 0x%" HWADDR_PRIx
"\n",
64 trace_aspeed_sdhci_read(addr
, size
, (uint64_t) val
);
69 static void aspeed_sdhci_write(void *opaque
, hwaddr addr
, uint64_t val
,
72 AspeedSDHCIState
*sdhci
= opaque
;
74 trace_aspeed_sdhci_write(addr
, size
, val
);
77 case ASPEED_SDHCI_INFO
:
78 /* The RESET bit automatically clears. */
79 sdhci
->regs
[TO_REG(addr
)] = (uint32_t)val
& ~ASPEED_SDHCI_INFO_RESET
;
81 case ASPEED_SDHCI_SDIO_140
:
82 sdhci
->slots
[0].capareg
= (uint64_t)(uint32_t)val
;
84 case ASPEED_SDHCI_SDIO_148
:
85 sdhci
->slots
[0].maxcurr
= (uint64_t)(uint32_t)val
;
87 case ASPEED_SDHCI_SDIO_240
:
88 sdhci
->slots
[1].capareg
= (uint64_t)(uint32_t)val
;
90 case ASPEED_SDHCI_SDIO_248
:
91 sdhci
->slots
[1].maxcurr
= (uint64_t)(uint32_t)val
;
94 if (addr
< ASPEED_SDHCI_REG_SIZE
) {
95 sdhci
->regs
[TO_REG(addr
)] = (uint32_t)val
;
97 qemu_log_mask(LOG_GUEST_ERROR
,
98 "%s: Out-of-bounds write at 0x%" HWADDR_PRIx
"\n",
104 static const MemoryRegionOps aspeed_sdhci_ops
= {
105 .read
= aspeed_sdhci_read
,
106 .write
= aspeed_sdhci_write
,
107 .endianness
= DEVICE_NATIVE_ENDIAN
,
108 .valid
.min_access_size
= 4,
109 .valid
.max_access_size
= 4,
112 static void aspeed_sdhci_set_irq(void *opaque
, int n
, int level
)
114 AspeedSDHCIState
*sdhci
= opaque
;
117 sdhci
->regs
[TO_REG(ASPEED_SDHCI_IRQ_STAT
)] |= BIT(n
);
119 qemu_irq_raise(sdhci
->irq
);
121 sdhci
->regs
[TO_REG(ASPEED_SDHCI_IRQ_STAT
)] &= ~BIT(n
);
123 qemu_irq_lower(sdhci
->irq
);
127 static void aspeed_sdhci_realize(DeviceState
*dev
, Error
**errp
)
129 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
130 AspeedSDHCIState
*sdhci
= ASPEED_SDHCI(dev
);
132 /* Create input irqs for the slots */
133 qdev_init_gpio_in_named_with_opaque(DEVICE(sbd
), aspeed_sdhci_set_irq
,
134 sdhci
, NULL
, sdhci
->num_slots
);
136 sysbus_init_irq(sbd
, &sdhci
->irq
);
137 memory_region_init_io(&sdhci
->iomem
, OBJECT(sdhci
), &aspeed_sdhci_ops
,
138 sdhci
, TYPE_ASPEED_SDHCI
, 0x1000);
139 sysbus_init_mmio(sbd
, &sdhci
->iomem
);
141 for (int i
= 0; i
< sdhci
->num_slots
; ++i
) {
142 Object
*sdhci_slot
= OBJECT(&sdhci
->slots
[i
]);
143 SysBusDevice
*sbd_slot
= SYS_BUS_DEVICE(&sdhci
->slots
[i
]);
145 if (!object_property_set_int(sdhci_slot
, "sd-spec-version", 2, errp
)) {
149 if (!object_property_set_uint(sdhci_slot
, "capareg",
150 ASPEED_SDHCI_CAPABILITIES
, errp
)) {
154 if (!sysbus_realize(sbd_slot
, errp
)) {
158 sysbus_connect_irq(sbd_slot
, 0, qdev_get_gpio_in(DEVICE(sbd
), i
));
159 memory_region_add_subregion(&sdhci
->iomem
, (i
+ 1) * 0x100,
160 &sdhci
->slots
[i
].iomem
);
164 static void aspeed_sdhci_reset(DeviceState
*dev
)
166 AspeedSDHCIState
*sdhci
= ASPEED_SDHCI(dev
);
168 memset(sdhci
->regs
, 0, ASPEED_SDHCI_REG_SIZE
);
170 sdhci
->regs
[TO_REG(ASPEED_SDHCI_INFO
)] = ASPEED_SDHCI_INFO_SLOT0
;
171 if (sdhci
->num_slots
== 2) {
172 sdhci
->regs
[TO_REG(ASPEED_SDHCI_INFO
)] |= ASPEED_SDHCI_INFO_SLOT1
;
174 sdhci
->regs
[TO_REG(ASPEED_SDHCI_DEBOUNCE
)] = ASPEED_SDHCI_DEBOUNCE_RESET
;
177 static const VMStateDescription vmstate_aspeed_sdhci
= {
178 .name
= TYPE_ASPEED_SDHCI
,
180 .fields
= (const VMStateField
[]) {
181 VMSTATE_UINT32_ARRAY(regs
, AspeedSDHCIState
, ASPEED_SDHCI_NUM_REGS
),
182 VMSTATE_END_OF_LIST(),
186 static Property aspeed_sdhci_properties
[] = {
187 DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState
, num_slots
, 0),
188 DEFINE_PROP_END_OF_LIST(),
191 static void aspeed_sdhci_class_init(ObjectClass
*classp
, void *data
)
193 DeviceClass
*dc
= DEVICE_CLASS(classp
);
195 dc
->realize
= aspeed_sdhci_realize
;
196 device_class_set_legacy_reset(dc
, aspeed_sdhci_reset
);
197 dc
->vmsd
= &vmstate_aspeed_sdhci
;
198 device_class_set_props(dc
, aspeed_sdhci_properties
);
201 static const TypeInfo aspeed_sdhci_types
[] = {
203 .name
= TYPE_ASPEED_SDHCI
,
204 .parent
= TYPE_SYS_BUS_DEVICE
,
205 .instance_size
= sizeof(AspeedSDHCIState
),
206 .class_init
= aspeed_sdhci_class_init
,
210 DEFINE_TYPES(aspeed_sdhci_types
)