target/i386: Fix compiler warnings
[qemu/ar7.git] / hw / exynos4210_sdhci.c
blob13799bf57a8d6dabdd26088cf21ff5c93b35e315
1 /*
2 * Samsung Exynos4210 SD/MMC host controller model
4 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5 * Mitsyanko Igor <i.mitsyanko@samsung.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "qemu/osdep.h"
22 #include "sdhci.h"
24 #define EXYNOS4_SDHC_CAPABILITIES 0x05E80080
25 #define EXYNOS4_SDHC_MAX_BUFSZ 512
27 #define EXYNOS4_SDHC_DEBUG 0
29 #if EXYNOS4_SDHC_DEBUG == 0
30 #define DPRINT_L1(fmt, args...) do { } while (0)
31 #define DPRINT_L2(fmt, args...) do { } while (0)
32 #define ERRPRINT(fmt, args...) do { } while (0)
33 #elif EXYNOS4_SDHC_DEBUG == 1
34 #define DPRINT_L1(fmt, args...) \
35 do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
36 #define DPRINT_L2(fmt, args...) do { } while (0)
37 #define ERRPRINT(fmt, args...) \
38 do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
39 #else
40 #define DPRINT_L1(fmt, args...) \
41 do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
42 #define DPRINT_L2(fmt, args...) \
43 do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
44 #define ERRPRINT(fmt, args...) \
45 do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
46 #endif
49 #define TYPE_EXYNOS4_SDHC "exynos4210.sdhci"
50 #define EXYNOS4_SDHCI(obj) \
51 OBJECT_CHECK(Exynos4SDHCIState, (obj), TYPE_EXYNOS4_SDHC)
53 /* ADMA Error Status Register */
54 #define EXYNOS4_SDHC_FINAL_BLOCK (1 << 10)
55 #define EXYNOS4_SDHC_CONTINUE_REQ (1 << 9)
56 #define EXYNOS4_SDHC_IRQ_STAT (1 << 8)
57 /* Control register 2 */
58 #define EXYNOS4_SDHC_CONTROL2 0x80
59 #define EXYNOS4_SDHC_HWINITFIN (1 << 0)
60 #define EXYNOS4_SDHC_DISBUFRD (1 << 6)
61 #define EXYNOS4_SDHC_SDOPSIGPC (1 << 12)
62 #define EXYNOS4_SDHC_SDINPSIGPC (1 << 3)
63 /* Control register 3 */
64 #define EXYNOS4_SDHC_CONTROL3 0x84
65 /* Control register 4 */
66 #define EXYNOS4_SDHC_CONTROL4 0x8C
67 /* Clock control register */
68 #define EXYNOS4_SDHC_SDCLK_STBL (1 << 3)
70 #define EXYNOS4_SDHC_CMD_USES_DAT(cmd) \
71 (((cmd) & SDHC_CMD_DATA_PRESENT) || \
72 ((cmd) & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY)
74 typedef struct Exynos4SDHCIState {
75 SDHCIState sdhci;
77 uint32_t admaerr;
78 uint32_t control2;
79 uint32_t control3;
80 bool stopped_adma;
81 } Exynos4SDHCIState;
83 static uint8_t sdhci_slotint(SDHCIState *s)
85 return (s->norintsts & s->norintsigen) || (s->errintsts & s->errintsigen) ||
86 ((s->norintsts & SDHC_NIS_INSERT) && (s->wakcon & SDHC_WKUP_ON_INS)) ||
87 ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV));
90 static inline void exynos4210_sdhci_update_irq(SDHCIState *s)
92 qemu_set_irq(s->irq, sdhci_slotint(s));
95 static void exynos4210_sdhci_reset(DeviceState *d)
97 Exynos4SDHCIState *s = EXYNOS4_SDHCI(d);
99 SDHCI_GET_CLASS(d)->reset(SDHCI(d));
100 s->stopped_adma = false;
101 s->admaerr = 0;
102 s->control2 = 0;
103 s->control3 = 0x7F5F3F1F;
106 static void exynos4210_sdhci_start_adma(SDHCIState *sdhci)
108 Exynos4SDHCIState *s = EXYNOS4_SDHCI(sdhci);
109 unsigned int length, n, begin;
110 hwaddr entry_addr;
111 uint32_t addr;
112 uint8_t attributes;
113 const uint16_t block_size = sdhci->blksize & 0x0fff;
114 s->admaerr &=
115 ~(EXYNOS4_SDHC_FINAL_BLOCK | SDHC_ADMAERR_LENGTH_MISMATCH);
117 while (1) {
118 addr = length = attributes = 0;
119 entry_addr = (hwaddr)(sdhci->admasysaddr & 0xFFFFFFFFull);
121 /* fetch next entry from descriptor table */
122 cpu_physical_memory_read(entry_addr + 4, (uint8_t *)(&addr), 4);
123 cpu_physical_memory_read(entry_addr + 2, (uint8_t *)(&length), 2);
124 cpu_physical_memory_read(entry_addr, (uint8_t *)(&attributes), 1);
125 DPRINT_L1("ADMA loop: addr=0x%08x, len=%d, attr=%x\n",
126 addr, length, attributes);
128 if ((attributes & SDHC_ADMA_ATTR_VALID) == 0) {
129 /* Indicate that error occurred in ST_FDS state */
130 s->admaerr &= ~SDHC_ADMAERR_STATE_MASK;
131 s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS;
132 DPRINT_L1("ADMA not valid at addr=0x%lx\n", sdhci->admasysaddr);
134 if (sdhci->errintstsen & SDHC_EISEN_ADMAERR) {
135 sdhci->errintsts |= SDHC_EIS_ADMAERR;
136 sdhci->norintsts |= SDHC_NIS_ERR;
139 exynos4210_sdhci_update_irq(sdhci);
140 break;
143 if (length == 0) {
144 length = 65536;
147 addr &= 0xfffffffc; /* minimum unit of addr is 4 byte */
149 switch (attributes & SDHC_ADMA_ATTR_ACT_MASK) {
150 case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */
151 if (sdhci->trnmod & SDHC_TRNS_READ) {
152 while (length) {
153 if (sdhci->data_count == 0) {
154 for (n = 0; n < block_size; n++) {
155 sdhci->fifo_buffer[n] = sd_read_data(sdhci->card);
158 begin = sdhci->data_count;
159 if ((length + begin) < block_size) {
160 sdhci->data_count = length + begin;
161 length = 0;
162 } else {
163 sdhci->data_count = block_size;
164 length -= block_size - begin;
166 cpu_physical_memory_write(addr, &sdhci->fifo_buffer[begin],
167 sdhci->data_count - begin);
168 addr += sdhci->data_count - begin;
169 if (sdhci->data_count == block_size) {
170 sdhci->data_count = 0;
171 if (sdhci->trnmod & SDHC_TRNS_BLK_CNT_EN) {
172 sdhci->blkcnt--;
173 if (sdhci->blkcnt == 0) {
174 break;
179 } else {
180 while (length) {
181 begin = sdhci->data_count;
182 if ((length + begin) < block_size) {
183 sdhci->data_count = length + begin;
184 length = 0;
185 } else {
186 sdhci->data_count = block_size;
187 length -= block_size - begin;
189 cpu_physical_memory_read(addr,
190 &sdhci->fifo_buffer[begin], sdhci->data_count);
191 addr += sdhci->data_count - begin;
192 if (sdhci->data_count == block_size) {
193 for (n = 0; n < block_size; n++) {
194 sd_write_data(sdhci->card, sdhci->fifo_buffer[n]);
196 sdhci->data_count = 0;
197 if (sdhci->trnmod & SDHC_TRNS_BLK_CNT_EN) {
198 sdhci->blkcnt--;
199 if (sdhci->blkcnt == 0) {
200 break;
206 sdhci->admasysaddr += 8;
207 break;
208 case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
209 sdhci->admasysaddr = addr;
210 DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", sdhci->admasysaddr);
211 break;
212 default:
213 sdhci->admasysaddr += 8;
214 break;
217 /* ADMA transfer terminates if blkcnt == 0 or by END attribute */
218 if (((sdhci->trnmod & SDHC_TRNS_BLK_CNT_EN) && (sdhci->blkcnt == 0)) ||
219 (attributes & SDHC_ADMA_ATTR_END)) {
220 DPRINT_L2("ADMA transfer completed\n");
221 if (length || ((attributes & SDHC_ADMA_ATTR_END) &&
222 (sdhci->trnmod & SDHC_TRNS_BLK_CNT_EN) && sdhci->blkcnt != 0) ||
223 ((sdhci->trnmod & SDHC_TRNS_BLK_CNT_EN) && sdhci->blkcnt == 0 &&
224 (attributes & SDHC_ADMA_ATTR_END) == 0)) {
225 ERRPRINT("ADMA length mismatch\n");
226 s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
227 SDHC_ADMAERR_STATE_ST_TFR;
228 if (sdhci->errintstsen & SDHC_EISEN_ADMAERR) {
229 sdhci->errintsts |= SDHC_EIS_ADMAERR;
230 sdhci->norintsts |= SDHC_NIS_ERR;
233 exynos4210_sdhci_update_irq(sdhci);
236 s->admaerr |= EXYNOS4_SDHC_FINAL_BLOCK;
237 SDHCI_GET_CLASS(sdhci)->end_data_transfer(sdhci);
238 break;
241 if (attributes & SDHC_ADMA_ATTR_INT) {
242 DPRINT_L1("ADMA interrupt: addr=0x%lx\n", sdhci->admasysaddr);
243 s->admaerr |= EXYNOS4_SDHC_IRQ_STAT;
244 s->stopped_adma = true;
245 if (sdhci->norintstsen & SDHC_NISEN_DMA) {
246 sdhci->norintsts |= SDHC_NIS_DMA;
248 exynos4210_sdhci_update_irq(sdhci);
249 break;
254 static bool exynos4210_sdhci_can_issue_command(SDHCIState *sdhci)
256 Exynos4SDHCIState *s = EXYNOS4_SDHCI(sdhci);
258 /* Check that power is supplied and clock is enabled.
259 * If SDOPSIGPC and SDINPSIGPC bits in CONTROL2 register are not set, power
260 * is supplied regardless of the PWRCON register state */
261 if (!SDHC_CLOCK_IS_ON(sdhci->clkcon) || (!(sdhci->pwrcon & SDHC_POWER_ON) &&
262 (s->control2 & (EXYNOS4_SDHC_SDOPSIGPC | EXYNOS4_SDHC_SDINPSIGPC)))) {
263 return false;
266 /* Controller cannot issue a command which uses data line (unless its an
267 * ABORT command) if data line is currently busy */
268 if (((sdhci->prnsts & SDHC_DATA_INHIBIT) || sdhci->stopped_state) &&
269 (EXYNOS4_SDHC_CMD_USES_DAT(sdhci->cmdreg) &&
270 SDHC_COMMAND_TYPE(sdhci->cmdreg) != SDHC_CMD_ABORT)) {
271 return false;
274 return true;
277 static uint64_t
278 exynos4210_sdhci_readfn(void *opaque, hwaddr offset, unsigned size)
280 Exynos4SDHCIState *s = (Exynos4SDHCIState *)opaque;
281 uint32_t ret;
283 switch (offset & ~0x3) {
284 case SDHC_BDATA:
285 /* Buffer data port read can be disabled by CONTROL2 register */
286 if (s->control2 & EXYNOS4_SDHC_DISBUFRD) {
287 ret = 0;
288 } else {
289 ret = SDHCI_GET_CLASS(s)->mem_read(SDHCI(s), offset, size);
291 break;
292 case SDHC_ADMAERR:
293 ret = (s->admaerr >> 8 * (offset - SDHC_ADMAERR)) &
294 ((1 << 8 * size) - 1);
295 break;
296 case EXYNOS4_SDHC_CONTROL2:
297 ret = (s->control2 >> 8 * (offset - EXYNOS4_SDHC_CONTROL2)) &
298 ((1 << 8 * size) - 1);
299 break;
300 case EXYNOS4_SDHC_CONTROL3:
301 ret = (s->control3 >> 8 * (offset - EXYNOS4_SDHC_CONTROL3)) &
302 ((1 << 8 * size) - 1);
303 break;
304 case EXYNOS4_SDHC_CONTROL4:
305 ret = 0;
306 break;
307 default:
308 ret = SDHCI_GET_CLASS(s)->mem_read(SDHCI(s), offset, size);
309 break;
312 DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, offset, ret, ret);
313 return ret;
316 static void exynos4210_sdhci_writefn(void *opaque, hwaddr offset,
317 uint64_t val, unsigned size)
319 Exynos4SDHCIState *s = (Exynos4SDHCIState *)opaque;
320 SDHCIState *sdhci = SDHCI(s);
321 unsigned shift;
323 DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n", size, (uint32_t)offset,
324 (uint32_t)val, (uint32_t)val);
326 switch (offset) {
327 case SDHC_CLKCON:
328 if ((val & SDHC_CLOCK_SDCLK_EN) &&
329 (sdhci->prnsts & SDHC_CARD_PRESENT)) {
330 val |= EXYNOS4_SDHC_SDCLK_STBL;
331 } else {
332 val &= ~EXYNOS4_SDHC_SDCLK_STBL;
334 /* Break out to superclass write to handle the rest of this register */
335 break;
336 case EXYNOS4_SDHC_CONTROL2 ... EXYNOS4_SDHC_CONTROL2 + 3:
337 shift = (offset - EXYNOS4_SDHC_CONTROL2) * 8;
338 s->control2 = (s->control2 & ~(((1 << 8 * size) - 1) << shift)) |
339 (val << shift);
340 return;
341 case EXYNOS4_SDHC_CONTROL3 ... EXYNOS4_SDHC_CONTROL3 + 3:
342 shift = (offset - EXYNOS4_SDHC_CONTROL2) * 8;
343 s->control3 = (s->control3 & ~(((1 << 8 * size) - 1) << shift)) |
344 (val << shift);
345 return;
346 case SDHC_ADMAERR ... SDHC_ADMAERR + 3:
347 if (size == 4 || (size == 2 && offset == SDHC_ADMAERR) ||
348 (size == 1 && offset == (SDHC_ADMAERR + 1))) {
349 uint32_t mask = 0;
351 if (size == 2) {
352 mask = 0xFFFF0000;
353 } else if (size == 1) {
354 mask = 0xFFFF00FF;
355 val <<= 8;
358 s->admaerr = (s->admaerr & (mask | EXYNOS4_SDHC_FINAL_BLOCK |
359 EXYNOS4_SDHC_IRQ_STAT)) | (val & ~(EXYNOS4_SDHC_FINAL_BLOCK |
360 EXYNOS4_SDHC_IRQ_STAT | EXYNOS4_SDHC_CONTINUE_REQ));
361 s->admaerr &= ~(val & EXYNOS4_SDHC_IRQ_STAT);
362 if ((s->stopped_adma) && (val & EXYNOS4_SDHC_CONTINUE_REQ) &&
363 (SDHC_DMA_TYPE(sdhci->hostctl) == SDHC_CTRL_ADMA2_32)) {
364 s->stopped_adma = false;
365 SDHCI_GET_CLASS(sdhci)->do_adma(sdhci);
367 } else {
368 uint32_t mask = (1 << (size * 8)) - 1;
369 shift = 8 * (offset & 0x3);
370 val <<= shift;
371 mask = ~(mask << shift);
372 s->admaerr = (s->admaerr & mask) | val;
374 return;
377 SDHCI_GET_CLASS(s)->mem_write(sdhci, offset, val, size);
380 static const MemoryRegionOps exynos4210_sdhci_mmio_ops = {
381 .read = exynos4210_sdhci_readfn,
382 .write = exynos4210_sdhci_writefn,
383 .valid = {
384 .min_access_size = 1,
385 .max_access_size = 4,
386 .unaligned = false
388 .endianness = DEVICE_LITTLE_ENDIAN,
391 static const VMStateDescription exynos4210_sdhci_vmstate = {
392 .name = "exynos4210.sdhci",
393 .version_id = 1,
394 .minimum_version_id = 1,
395 .fields = (VMStateField[]) {
396 VMSTATE_STRUCT(sdhci, Exynos4SDHCIState, 1, sdhci_vmstate, SDHCIState),
397 VMSTATE_UINT32(admaerr, Exynos4SDHCIState),
398 VMSTATE_UINT32(control2, Exynos4SDHCIState),
399 VMSTATE_UINT32(control3, Exynos4SDHCIState),
400 VMSTATE_BOOL(stopped_adma, Exynos4SDHCIState),
401 VMSTATE_END_OF_LIST()
405 static int exynos4210_sdhci_realize(SysBusDevice *busdev)
407 SDHCIState *sdhci = SDHCI(busdev);
409 qdev_prop_set_uint32(DEVICE(busdev), "capareg", EXYNOS4_SDHC_CAPABILITIES);
410 sdhci->buf_maxsz = EXYNOS4_SDHC_MAX_BUFSZ;
411 sdhci->fifo_buffer = g_malloc0(sdhci->buf_maxsz);
412 sysbus_init_irq(busdev, &sdhci->irq);
413 memory_region_init_io(&sdhci->iomem, &exynos4210_sdhci_mmio_ops,
414 EXYNOS4_SDHCI(sdhci), "exynos4210.sdhci", SDHC_REGISTERS_MAP_SIZE);
415 sysbus_init_mmio(busdev, &sdhci->iomem);
416 return 0;
419 static void exynos4210_sdhci_class_init(ObjectClass *klass, void *data)
421 DeviceClass *dc = DEVICE_CLASS(klass);
422 SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
423 SDHCIClass *k = SDHCI_CLASS(klass);
425 dc->vmsd = &exynos4210_sdhci_vmstate;
426 dc->reset = exynos4210_sdhci_reset;
427 sbdc->init = exynos4210_sdhci_realize;
429 k->can_issue_command = exynos4210_sdhci_can_issue_command;
430 k->do_adma = exynos4210_sdhci_start_adma;
433 static const TypeInfo exynos4210_sdhci_type_info = {
434 .name = TYPE_EXYNOS4_SDHC,
435 .parent = TYPE_SDHCI,
436 .instance_size = sizeof(Exynos4SDHCIState),
437 .class_init = exynos4210_sdhci_class_init,
440 static void exynos4210_sdhci_register_types(void)
442 type_register_static(&exynos4210_sdhci_type_info);
445 type_init(exynos4210_sdhci_register_types)