mirror of
https://git.proxmox.com/git/qemu
synced 2025-06-15 21:36:32 +00:00
hw/sd.c: Correct handling of APP_CMD status bit
Fix some bugs in our implementation of the APP_CMD status bit: * the response to an ACMD should have APP_CMD set, not cleared * if an illegal ACMD is sent then the next command should be handled as a normal command This requires that we split "card is expecting an ACMD" from the state of the APP_CMD status bit (the latter indicates both "expecting ACMD" and "that was an ACMD"). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Andrzej Zaborowski <andrew.zaborowski@intel.com>
This commit is contained in:
parent
10a412dab3
commit
1d06cb7ab9
24
hw/sd.c
24
hw/sd.c
@ -92,6 +92,10 @@ struct SDState {
|
|||||||
|
|
||||||
int spi;
|
int spi;
|
||||||
int current_cmd;
|
int current_cmd;
|
||||||
|
/* True if we will handle the next command as an ACMD. Note that this does
|
||||||
|
* *not* track the APP_CMD status bit!
|
||||||
|
*/
|
||||||
|
int expecting_acmd;
|
||||||
int blk_written;
|
int blk_written;
|
||||||
uint64_t data_start;
|
uint64_t data_start;
|
||||||
uint32_t data_offset;
|
uint32_t data_offset;
|
||||||
@ -341,8 +345,8 @@ static int sd_req_crc_validate(SDRequest *req)
|
|||||||
static void sd_response_r1_make(SDState *sd, uint8_t *response)
|
static void sd_response_r1_make(SDState *sd, uint8_t *response)
|
||||||
{
|
{
|
||||||
uint32_t status = sd->card_status;
|
uint32_t status = sd->card_status;
|
||||||
/* Clear the "clear on read" status bits (except APP_CMD) */
|
/* Clear the "clear on read" status bits */
|
||||||
sd->card_status &= ~CARD_STATUS_C | APP_CMD;
|
sd->card_status &= ~CARD_STATUS_C;
|
||||||
|
|
||||||
response[0] = (status >> 24) & 0xff;
|
response[0] = (status >> 24) & 0xff;
|
||||||
response[1] = (status >> 16) & 0xff;
|
response[1] = (status >> 16) & 0xff;
|
||||||
@ -608,6 +612,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||||||
uint32_t rca = 0x0000;
|
uint32_t rca = 0x0000;
|
||||||
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
|
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
|
||||||
|
|
||||||
|
/* Not interpreting this as an app command */
|
||||||
|
sd->card_status &= ~APP_CMD;
|
||||||
|
|
||||||
if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
|
if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
|
||||||
rca = req.arg >> 16;
|
rca = req.arg >> 16;
|
||||||
|
|
||||||
@ -1116,6 +1123,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||||||
if (sd->rca != rca)
|
if (sd->rca != rca)
|
||||||
return sd_r0;
|
return sd_r0;
|
||||||
|
|
||||||
|
sd->expecting_acmd = 1;
|
||||||
sd->card_status |= APP_CMD;
|
sd->card_status |= APP_CMD;
|
||||||
return sd_r1;
|
return sd_r1;
|
||||||
|
|
||||||
@ -1155,6 +1163,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
|
|||||||
SDRequest req)
|
SDRequest req)
|
||||||
{
|
{
|
||||||
DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
|
DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
|
||||||
|
sd->card_status |= APP_CMD;
|
||||||
switch (req.cmd) {
|
switch (req.cmd) {
|
||||||
case 6: /* ACMD6: SET_BUS_WIDTH */
|
case 6: /* ACMD6: SET_BUS_WIDTH */
|
||||||
switch (sd->state) {
|
switch (sd->state) {
|
||||||
@ -1251,7 +1260,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
/* Fall back to standard commands. */
|
/* Fall back to standard commands. */
|
||||||
sd->card_status &= ~APP_CMD;
|
|
||||||
return sd_normal_command(sd, req);
|
return sd_normal_command(sd, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1269,7 +1277,7 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req)
|
|||||||
* ACMD41 and ACMD42
|
* ACMD41 and ACMD42
|
||||||
* Anything else provokes an "illegal command" response.
|
* Anything else provokes an "illegal command" response.
|
||||||
*/
|
*/
|
||||||
if (sd->card_status & APP_CMD) {
|
if (sd->expecting_acmd) {
|
||||||
return req->cmd == 41 || req->cmd == 42;
|
return req->cmd == 41 || req->cmd == 42;
|
||||||
}
|
}
|
||||||
if (req->cmd == 16 || req->cmd == 55) {
|
if (req->cmd == 16 || req->cmd == 55) {
|
||||||
@ -1297,6 +1305,7 @@ int sd_do_command(SDState *sd, SDRequest *req,
|
|||||||
if (sd->card_status & CARD_IS_LOCKED) {
|
if (sd->card_status & CARD_IS_LOCKED) {
|
||||||
if (!cmd_valid_while_locked(sd, req)) {
|
if (!cmd_valid_while_locked(sd, req)) {
|
||||||
sd->card_status |= ILLEGAL_COMMAND;
|
sd->card_status |= ILLEGAL_COMMAND;
|
||||||
|
sd->expecting_acmd = 0;
|
||||||
fprintf(stderr, "SD: Card is locked\n");
|
fprintf(stderr, "SD: Card is locked\n");
|
||||||
rtype = sd_illegal;
|
rtype = sd_illegal;
|
||||||
goto send_response;
|
goto send_response;
|
||||||
@ -1306,11 +1315,12 @@ int sd_do_command(SDState *sd, SDRequest *req,
|
|||||||
last_state = sd->state;
|
last_state = sd->state;
|
||||||
sd_set_mode(sd);
|
sd_set_mode(sd);
|
||||||
|
|
||||||
if (sd->card_status & APP_CMD) {
|
if (sd->expecting_acmd) {
|
||||||
|
sd->expecting_acmd = 0;
|
||||||
rtype = sd_app_command(sd, *req);
|
rtype = sd_app_command(sd, *req);
|
||||||
sd->card_status &= ~APP_CMD;
|
} else {
|
||||||
} else
|
|
||||||
rtype = sd_normal_command(sd, *req);
|
rtype = sd_normal_command(sd, *req);
|
||||||
|
}
|
||||||
|
|
||||||
if (rtype == sd_illegal) {
|
if (rtype == sd_illegal) {
|
||||||
sd->card_status |= ILLEGAL_COMMAND;
|
sd->card_status |= ILLEGAL_COMMAND;
|
||||||
|
Loading…
Reference in New Issue
Block a user