blob: 2295d2defef1c185eebd407c32223331e2d0b3c7 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include "qemu_file.h"
13#include "goldfish_device.h"
14#include "mmc.h"
15#include "sd.h"
16#include "block.h"
17
18enum {
19 /* status register */
20 MMC_INT_STATUS = 0x00,
21 /* set this to enable IRQ */
22 MMC_INT_ENABLE = 0x04,
23 /* set this to specify buffer address */
24 MMC_SET_BUFFER = 0x08,
25
26 /* MMC command number */
27 MMC_CMD = 0x0C,
28
29 /* MMC argument */
30 MMC_ARG = 0x10,
31
32 /* MMC response (or R2 bits 0 - 31) */
33 MMC_RESP_0 = 0x14,
34
35 /* MMC R2 response bits 32 - 63 */
36 MMC_RESP_1 = 0x18,
37
38 /* MMC R2 response bits 64 - 95 */
39 MMC_RESP_2 = 0x1C,
40
41 /* MMC R2 response bits 96 - 127 */
42 MMC_RESP_3 = 0x20,
43
44 MMC_BLOCK_LENGTH = 0x24,
45 MMC_BLOCK_COUNT = 0x28,
46
47 /* MMC state flags */
48 MMC_STATE = 0x2C,
49
50 /* MMC_INT_STATUS bits */
51
52 MMC_STAT_END_OF_CMD = 1U << 0,
53 MMC_STAT_END_OF_DATA = 1U << 1,
54 MMC_STAT_STATE_CHANGE = 1U << 2,
55
56 /* MMC_STATE bits */
57 MMC_STATE_INSERTED = 1U << 0,
58 MMC_STATE_READ_ONLY = 1U << 1,
59};
60
61
62struct goldfish_mmc_state {
63 struct goldfish_device dev;
64 BlockDriverState *bs;
65 // pointer to our buffer
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070066 uint32_t buffer_address;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080067 // offsets for read and write operations
68 uint32_t read_offset, write_offset;
69 // buffer status flags
70 uint32_t int_status;
71 // irq enable mask for int_status
72 uint32_t int_enable;
73
74 // MMC command argument
75 uint32_t arg;
76 uint32_t resp[4];
77
78 uint32_t block_length;
79 uint32_t block_count;
80 int is_SDHC;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070081
82 uint8_t* buf;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080083};
84
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070085#define GOLDFISH_MMC_SAVE_VERSION 2
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080086#define QFIELD_STRUCT struct goldfish_mmc_state
87QFIELD_BEGIN(goldfish_mmc_fields)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070088 QFIELD_INT32(buffer_address),
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080089 QFIELD_INT32(read_offset),
90 QFIELD_INT32(write_offset),
91 QFIELD_INT32(int_status),
92 QFIELD_INT32(int_enable),
93 QFIELD_INT32(arg),
94 QFIELD_INT32(resp[0]),
95 QFIELD_INT32(resp[1]),
96 QFIELD_INT32(resp[2]),
97 QFIELD_INT32(resp[3]),
98 QFIELD_INT32(block_length),
99 QFIELD_INT32(block_count),
100 QFIELD_INT32(is_SDHC),
101QFIELD_END
102
103static void goldfish_mmc_save(QEMUFile* f, void* opaque)
104{
105 struct goldfish_mmc_state* s = opaque;
106
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800107 qemu_put_struct(f, goldfish_mmc_fields, s);
108}
109
110static int goldfish_mmc_load(QEMUFile* f, void* opaque, int version_id)
111{
112 struct goldfish_mmc_state* s = opaque;
113
114 if (version_id != GOLDFISH_MMC_SAVE_VERSION)
115 return -1;
116
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800117 return qemu_get_struct(f, goldfish_mmc_fields, s);
118}
119
120struct mmc_opcode {
121 const char* name;
122 int cmd;
123} mmc_opcodes[] = {
124 { "MMC_GO_IDLE_STATE", 0 },
125 { "MMC_SEND_OP_COND", 1 },
126 { "MMC_ALL_SEND_CID", 2 },
127 { "MMC_SET_RELATIVE_ADDR", 3 },
128 { "MMC_SET_DSR", 4 },
129 { "MMC_SWITCH", 6 },
130 { "MMC_SELECT_CARD", 7 },
131 { "MMC_SEND_EXT_CSD", 8 },
132 { "MMC_SEND_CSD", 9 },
133 { "MMC_SEND_CID", 10 },
134 { "MMC_READ_DAT_UNTIL_STOP", 11 },
135 { "MMC_STOP_TRANSMISSION", 12 },
136 { "MMC_SEND_STATUS", 13 },
137 { "MMC_GO_INACTIVE_STATE", 15 },
138 { "MMC_SET_BLOCKLEN", 16 },
139 { "MMC_READ_SINGLE_BLOCK", 17 },
140 { "MMC_READ_MULTIPLE_BLOCK", 18 },
141 { "MMC_WRITE_DAT_UNTIL_STOP", 20 },
142 { "MMC_SET_BLOCK_COUNT", 23 },
143 { "MMC_WRITE_BLOCK", 24 },
144 { "MMC_WRITE_MULTIPLE_BLOCK", 25 },
145 { "MMC_PROGRAM_CID", 26 },
146 { "MMC_PROGRAM_CSD", 27 },
147 { "MMC_SET_WRITE_PROT", 28 },
148 { "MMC_CLR_WRITE_PROT", 29 },
149 { "MMC_SEND_WRITE_PROT", 30 },
150 { "MMC_ERASE_GROUP_START", 35 },
151 { "MMC_ERASE_GROUP_END", 36 },
152 { "MMC_ERASE", 38 },
153 { "MMC_FAST_IO", 39 },
154 { "MMC_GO_IRQ_STATE", 40 },
155 { "MMC_LOCK_UNLOCK", 42 },
156 { "MMC_APP_CMD", 55 },
157 { "MMC_GEN_CMD", 56 },
158 { "SD_APP_OP_COND", 41 },
159 { "SD_APP_SEND_SCR", 51 },
160 { "UNKNOWN", -1 }
161};
162
163#if 0
164static const char* get_command_name(int command)
165{
166 struct mmc_opcode* opcode = mmc_opcodes;
167
168 while (opcode->cmd != command && opcode->cmd != -1) opcode++;
169 return opcode->name;
170}
171#endif
172
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700173static int goldfish_mmc_bdrv_read(struct goldfish_mmc_state *s,
174 int64_t sector_number,
175 target_phys_addr_t dst_address,
176 int num_sectors)
177{
178 int ret;
179
180 while (num_sectors > 0) {
181 ret = bdrv_read(s->bs, sector_number, s->buf, 1);
182 if (ret < 0)
183 return ret;
184
185 cpu_physical_memory_write(dst_address, s->buf, 512);
186 dst_address += 512;
187 num_sectors -= 1;
188 sector_number += 1;
189 }
190 return 0;
191}
192
193static int goldfish_mmc_bdrv_write(struct goldfish_mmc_state *s,
194 int64_t sector_number,
195 target_phys_addr_t dst_address,
196 int num_sectors)
197{
198 int ret;
199
200 while (num_sectors > 0) {
201 cpu_physical_memory_read(dst_address, s->buf, 512);
202
203 ret = bdrv_write(s->bs, sector_number, s->buf, 1);
204 if (ret < 0)
205 return ret;
206
207 dst_address += 512;
208 num_sectors -= 1;
209 sector_number += 1;
210 }
211 return 0;
212}
213
214
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800215static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd, uint32_t arg)
216{
217 int result;
218 int new_status = MMC_STAT_END_OF_CMD;
219 int opcode = cmd & 63;
220
221// fprintf(stderr, "goldfish_mmc_do_command opcode: %s (0x%04X), arg: %d\n", get_command_name(opcode), cmd, arg);
222
223 s->resp[0] = 0;
224 s->resp[1] = 0;
225 s->resp[2] = 0;
226 s->resp[3] = 0;
227
228#define SET_R1_CURRENT_STATE(s) ((s << 9) & 0x00001E00) /* sx, b (4 bits) */
229
230 switch (opcode) {
231 case MMC_SEND_CSD: {
232 int64_t sector_count = 0;
233 uint64_t capacity;
234 uint8_t exponent;
235 uint32_t m;
236
237 bdrv_get_geometry(s->bs, (uint64_t*)&sector_count);
238 capacity = sector_count * 512;
239 if (capacity > 2147483648U) {
240 // if storages is > 2 gig, then emulate SDHC card
241 s->is_SDHC = 1;
242
243 // CSD bits borrowed from a real SDHC card, with capacity bits zeroed out
244 s->resp[3] = 0x400E0032;
245 s->resp[2] = 0x5B590000;
246 s->resp[1] = 0x00007F80;
247 s->resp[0] = 0x0A4040DF;
248
249 // stuff in the real capacity
250 // m = UNSTUFF_BITS(resp, 48, 22);
251 m = (uint32_t)(capacity / (512*1024)) - 1;
252 // m must fit into 22 bits
253 if (m & 0xFFC00000) {
254 fprintf(stderr, "SD card too big (%lld bytes). Maximum SDHC card size is 128 gigabytes.\n", capacity);
255 abort();
256 }
257
258 // low 16 bits go in high end of resp[1]
259 s->resp[1] |= ((m & 0x0000FFFF) << 16);
260 // high 6 bits go in low end of resp[2]
261 s->resp[2] |= (m >> 16);
262 } else {
263 // emulate standard SD card
264 s->is_SDHC = 0;
265
266 // CSD bits borrowed from a real SD card, with capacity bits zeroed out
267 s->resp[3] = 0x00260032;
268 s->resp[2] = 0x5F5A8000;
269 s->resp[1] = 0x3EF84FFF;
270 s->resp[0] = 0x928040CB;
271
272 // stuff in the real capacity
273 // e = UNSTUFF_BITS(resp, 47, 3);
274 // m = UNSTUFF_BITS(resp, 62, 12);
275 // csd->capacity = (1 + m) << (e + 2);
276 // need to reverse the formula and calculate e and m
277 exponent = 0;
278 capacity = sector_count * 512;
279 if (capacity > 2147483648U) {
280 fprintf(stderr, "SD card too big (%lld bytes). Maximum SD card size is 2 gigabytes.\n", capacity);
281 abort();
282 }
283 capacity >>= 10; // convert to Kbytes
284 while (capacity > 4096) {
285 // (capacity - 1) must fit into 12 bits
286 exponent++;
287 capacity >>= 1;
288 }
289 capacity -= 1;
290 exponent -= 2;
291 if (exponent > 7)
292 cpu_abort(cpu_single_env, "exponent %d too big\n", exponent);
293
294 s->resp[2] |= (((uint32_t)capacity >> 2) & 0x3FF); // high 10 bits to bottom of resp[2]
295 s->resp[1] |= (((uint32_t)capacity & 3) << 30); // low 2 bits to top of resp[1]
296 s->resp[1] |= (exponent << (47 - 32));
297 }
298 break;
299 }
300
301 case MMC_SEND_EXT_CSD:
302 s->resp[0] = arg;
303 break;
304
305 case MMC_APP_CMD:
306 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
307 break;
308
309 case SD_APP_OP_COND:
310 s->resp[0] = 0x80FF8000;
311 break;
312
313 case SD_APP_SEND_SCR:
314 {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700315#if 1 /* this code is actually endian-safe */
316 const uint8_t scr[8] = "\x02\x25\x00\x00\x00\x00\x00\x00";
317#else /* this original code wasn't */
318 uint32_t scr[2];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800319 scr[0] = 0x00002502;
320 scr[1] = 0x00000000;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700321#endif
322 cpu_physical_memory_write(s->buffer_address, (uint8_t*)scr, 8);
323
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800324 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
325 new_status |= MMC_STAT_END_OF_DATA;
326 break;
327 }
328 case MMC_SET_RELATIVE_ADDR:
329 s->resp[0] = -518519520;
330 break;
331
332 case MMC_ALL_SEND_CID:
333 s->resp[3] = 55788627;
334 s->resp[2] = 1429221959;
335 s->resp[1] = -2147479692;
336 s->resp[0] = -436179883;
337 break;
338
339 case MMC_SELECT_CARD:
340 s->resp[0] = SET_R1_CURRENT_STATE(3) | R1_READY_FOR_DATA; // 1792
341 break;
342
343 case MMC_SWITCH:
344 if (arg == 0x00FFFFF1 || arg == 0x80FFFFF1) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700345 uint8_t buff0[64];
346 memset(buff0, 0, sizeof buff0);
347 buff0[13] = 2;
348 cpu_physical_memory_write(s->buffer_address, buff0, sizeof buff0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800349 new_status |= MMC_STAT_END_OF_DATA;
350 }
351 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
352 break;
353
354 case MMC_SET_BLOCKLEN:
355 s->block_length = arg;
356 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
357 break;
358
359 case MMC_READ_SINGLE_BLOCK:
360 s->block_count = 1;
361 // fall through
362 case MMC_READ_MULTIPLE_BLOCK: {
363 if (s->is_SDHC) {
364 // arg is block offset
365 } else {
366 // arg is byte offset
367 if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when reading\n", arg);
368 arg /= s->block_length;
369 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700370 result = goldfish_mmc_bdrv_read(s, arg, s->buffer_address, s->block_count);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800371 new_status |= MMC_STAT_END_OF_DATA;
372 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
373 break;
374 }
375
376 case MMC_WRITE_BLOCK:
377 s->block_count = 1;
378 // fall through
379 case MMC_WRITE_MULTIPLE_BLOCK: {
380 if (s->is_SDHC) {
381 // arg is block offset
382 } else {
383 // arg is byte offset
384 if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when writing\n", arg);
385 arg /= s->block_length;
386 }
387 // arg is byte offset
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700388 result = goldfish_mmc_bdrv_write(s, arg, s->buffer_address, s->block_count);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800389// bdrv_flush(s->bs);
390 new_status |= MMC_STAT_END_OF_DATA;
391 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
392 break;
393 }
394
395 case MMC_STOP_TRANSMISSION:
396 s->resp[0] = SET_R1_CURRENT_STATE(5) | R1_READY_FOR_DATA; // 2816
397 break;
398
399 case MMC_SEND_STATUS:
400 s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
401 break;
402 }
403
404 s->int_status |= new_status;
405
406 if ((s->int_status & s->int_enable)) {
407 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
408 }
409}
410
411static uint32_t goldfish_mmc_read(void *opaque, target_phys_addr_t offset)
412{
413 uint32_t ret;
414 struct goldfish_mmc_state *s = opaque;
415
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800416 switch(offset) {
417 case MMC_INT_STATUS:
418 // return current buffer status flags
419 return s->int_status & s->int_enable;
420 case MMC_RESP_0:
421 return s->resp[0];
422 case MMC_RESP_1:
423 return s->resp[1];
424 case MMC_RESP_2:
425 return s->resp[2];
426 case MMC_RESP_3:
427 return s->resp[3];
428 case MMC_STATE: {
429 ret = MMC_STATE_INSERTED;
430 if (bdrv_is_read_only(s->bs)) {
431 ret |= MMC_STATE_READ_ONLY;
432 }
433 return ret;
434 }
435 default:
436 cpu_abort(cpu_single_env, "goldfish_mmc_read: Bad offset %x\n", offset);
437 return 0;
438 }
439}
440
441static void goldfish_mmc_write(void *opaque, target_phys_addr_t offset, uint32_t val)
442{
443 struct goldfish_mmc_state *s = opaque;
444 int status, old_status;
445
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800446 switch(offset) {
447
448 case MMC_INT_STATUS:
449 status = s->int_status;
450 old_status = status;
451 status &= ~val;
452 s->int_status = status;
453 if(status != old_status) {
454 goldfish_device_set_irq(&s->dev, 0, status);
455 }
456 break;
457
458 case MMC_INT_ENABLE:
459 /* enable buffer interrupts */
460 s->int_enable = val;
461 s->int_status = 0;
462 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
463 break;
464 case MMC_SET_BUFFER:
465 /* save pointer to buffer 1 */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700466 s->buffer_address = val;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800467 break;
468 case MMC_CMD:
469 goldfish_mmc_do_command(s, val, s->arg);
470 break;
471 case MMC_ARG:
472 s->arg = val;
473 break;
474 case MMC_BLOCK_LENGTH:
475 s->block_length = val + 1;
476 break;
477 case MMC_BLOCK_COUNT:
478 s->block_count = val + 1;
479 break;
480
481 default:
482 cpu_abort (cpu_single_env, "goldfish_mmc_write: Bad offset %x\n", offset);
483 }
484}
485
486static CPUReadMemoryFunc *goldfish_mmc_readfn[] = {
487 goldfish_mmc_read,
488 goldfish_mmc_read,
489 goldfish_mmc_read
490};
491
492static CPUWriteMemoryFunc *goldfish_mmc_writefn[] = {
493 goldfish_mmc_write,
494 goldfish_mmc_write,
495 goldfish_mmc_write
496};
497
498void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs)
499{
500 struct goldfish_mmc_state *s;
501
502 s = (struct goldfish_mmc_state *)qemu_mallocz(sizeof(*s));
503 s->dev.name = "goldfish_mmc";
504 s->dev.id = id;
505 s->dev.base = base;
506 s->dev.size = 0x1000;
507 s->dev.irq_count = 1;
508 s->bs = bs;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700509 s->buf = qemu_memalign(512,512);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800510
511 goldfish_device_add(&s->dev, goldfish_mmc_readfn, goldfish_mmc_writefn, s);
512
513 register_savevm( "goldfish_mmc", 0, GOLDFISH_MMC_SAVE_VERSION,
514 goldfish_mmc_save, goldfish_mmc_load, s);
515}
516