/* * linux/drivers/mmc/core/mmc_ops.h * * Copyright 2006-2007 Pierre Ossman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #include #include #include #include #include #include #include "core.h" #include "mmc_ops.h" static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; struct mmc_command cmd; BUG_ON(!host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SELECT_CARD; if (card) { cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; } else { cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; } err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; return MMC_ERR_NONE; } int mmc_select_card(struct mmc_card *card) { BUG_ON(!card); return _mmc_select_card(card->host, card); } int mmc_deselect_cards(struct mmc_host *host) { return _mmc_select_card(host, NULL); } int mmc_go_idle(struct mmc_host *host) { int err; struct mmc_command cmd; mmc_set_chip_select(host, MMC_CS_HIGH); mmc_delay(1); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; err = mmc_wait_for_cmd(host, &cmd, 0); mmc_delay(1); mmc_set_chip_select(host, MMC_CS_DONTCARE); mmc_delay(1); return err; } int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { struct mmc_command cmd; int i, err = 0; BUG_ON(!host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; for (i = 100; i; i--) { err = mmc_wait_for_cmd(host, &cmd, 0); if (err != MMC_ERR_NONE) break; if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) break; err = MMC_ERR_TIMEOUT; mmc_delay(10); } if (rocr) *rocr = cmd.resp[0]; return err; } int mmc_all_send_cid(struct mmc_host *host, u32 *cid) { int err; struct mmc_command cmd; BUG_ON(!host); BUG_ON(!cid); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; memcpy(cid, cmd.resp, sizeof(u32) * 4); return MMC_ERR_NONE; } int mmc_set_relative_addr(struct mmc_card *card) { int err; struct mmc_command cmd; BUG_ON(!card); BUG_ON(!card->host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; return MMC_ERR_NONE; } int mmc_send_csd(struct mmc_card *card, u32 *csd) { int err; struct mmc_command cmd; BUG_ON(!card); BUG_ON(!card->host); BUG_ON(!csd); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_CSD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; memcpy(csd, cmd.resp, sizeof(u32) * 4); return MMC_ERR_NONE; } int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) { struct mmc_request mrq; struct mmc_command cmd; struct mmc_data data; struct scatterlist sg; BUG_ON(!card); BUG_ON(!card->host); BUG_ON(!ext_csd); memset(&mrq, 0, sizeof(struct mmc_request)); memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; cmd.opcode = MMC_SEND_EXT_CSD; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; data.blksz = 512; data.blocks = 1; data.flags = MMC_DATA_READ; data.sg = &sg; data.sg_len = 1; sg_init_one(&sg, ext_csd, 512); mmc_set_data_timeout(&data, card, 0); mmc_wait_for_req(card->host, &mrq); if (cmd.error != MMC_ERR_NONE) return cmd.error; if (data.error != MMC_ERR_NONE) return data.error; return MMC_ERR_NONE; } int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) { int err; struct mmc_command cmd; BUG_ON(!card); BUG_ON(!card->host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; return MMC_ERR_NONE; } int mmc_send_status(struct mmc_card *card, u32 *status) { int err; struct mmc_command cmd; BUG_ON(!card); BUG_ON(!card->host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_STATUS; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err != MMC_ERR_NONE) return err; if (status) *status = cmd.resp[0]; return MMC_ERR_NONE; }