rt-thread/bsp/allwinner/libraries/sunxi-hal/hal/source/sdmmc/mmc.c

403 lines
12 KiB
C

/*
* Copyright (C) 2017 ALLWINNERTECH TECHNOLOGY CO., LTD. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of ALLWINNERTECH TECHNOLOGY CO., LTD. nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "hal_def.h"
#include "sdio.h"
#include "sdmmc.h"
#include "hal_sdhost.h"
#include "_sdhost.h"
#include "_mmc.h"
#include "_sd.h"
#ifdef CONFIG_USE_MMC
/*
int32_t mmc_sd_get_csd(struct mmc_card *card) //static
{
struct mmc_command cmd = {0};
uint32_t csd[4] = {0};
cmd.opcode = MMC_SEND_CSD;
cmd.arg = card->rca<<16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
if (mmc_wait_for_cmd(card->host, &cmd)) {
return -1;
}
HAL_Memcpy((void *)csd, (void *)cmd.resp, 16);
//decode CSD reg
card->csd.csd_ver = (csd[3]>>30)&0x3;
card->csd.trans_speed = csd[3]&0xff;
card->csd.read_blk_len = (csd[2]>>16)&0xf;
if (card->type == CT_MMC || card->csd.csd_ver == 0) {
card->csd.c_size_mult = (csd[1]>>15)&0x7;
card->csd.c_size = ((csd[1]>>30)&0x3)|((csd[2]&0x3ff)<<2);
} else {
card->csd.c_size_mult = 0;
card->csd.c_size = ((csd[1]>>16)&0xffff)|((csd[2]&0x3f)<<16);
}
card->csd.cmd_class = (csd[2]>>20)&0xfff;
card->csd.mmc_spec_ver = (csd[3]>>26)&0xf;
return 0;
}
*/
int32_t mmc_send_op_cond(struct mmc_card *card, uint32_t ocr, uint32_t *rocr)
{
struct mmc_command cmd = {0};
uint32_t i = 0;
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
SD_LOGD("%s,%d arg use ocr?\n", __func__, __LINE__);
do {
if (mmc_wait_for_cmd(card->host, &cmd)) {
return -1;
}
cmd.arg = 0x40000000|(cmd.resp[0]&0xFF8080);
#ifndef SYSTEM_SIMULATION
HAL_MSleep(10);
if (++i == 100)
break;
#else
smc_model_powerup_rdy(card->smc_no);
#endif
} while(!(cmd.resp[0] & 0x80000000));
if (!(cmd.resp[0] & 0x80000000)) {
SD_LOGD("Wait card power up ready timeout, i = %d !\n", i);
return -1;
}
cmd.resp[0] &= 0x7fffffff;
HAL_Memcpy((void *)&card->ocr, (void *)&cmd.resp[0], 4);
SD_LOGD("ocr = %08x !!\n", (unsigned int)cmd.resp[0]);
if (card->ocr.high_capacity) /* bit30 */
mmc_card_set_blockaddr(card);
return 0;
}
static int32_t mmc_public_new_rca(struct mmc_card *card)
{
struct mmc_command cmd = {0};
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = 0x1234 << 16; // why 1234 ??
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; //different from SD card;
if (mmc_wait_for_cmd(card->host, &cmd)) {
return -1;
}
card->rca = 0x1234;
SD_LOGD("rca = %04x !!\n", (unsigned int)card->rca);
return 0;
}
int32_t mmc_send_extcsd(struct mmc_card *card)
{
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq;
uint8_t extcsd[512] = {0};
struct scatterlist sg = {0};
sg.len = 512;
sg.buffer = extcsd;
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.data = &data;
data.blksz = 512;
data.sg_len = 1;
data.sg = &sg;
data.flags = MMC_DATA_READ;
mrq.cmd = &cmd;
mrq.data = &data;
if (mmc_wait_for_req(card->host, &mrq)) {
return -1;
}
SD_LOGD("%s,%d %s\n", __func__, __LINE__, "extcsd");
sd_hex_dump_bytes((void *)extcsd, 512);
//decode EXTCSD
card->extcsd.version = extcsd[192];
card->extcsd.card_type = extcsd[196];
card->extcsd.csd_struc = extcsd[194];
card->extcsd.hs_timing = extcsd[185];
card->extcsd.bus_width = extcsd[183];
if (extcsd[160] & MMC_SWITCH_PART_SUPPORT)
card->extcsd.part_config = extcsd[179];
if (card->extcsd.version >= 3) //>=4.3
card->extcsd.boot_bus_cond = extcsd[177];
return 0;
}
static int32_t mmc_switch_buswidth(struct mmc_card *card, uint32_t width)
{
uint8_t set_val;
int32_t ret = -1;
switch (width) {
case MMC_BUS_WIDTH_1:
set_val = MMC_EXT_CSD_BUS_WIDTH_1;
break;
case MMC_BUS_WIDTH_4:
set_val = MMC_EXT_CSD_BUS_WIDTH_4;
break;
case MMC_BUS_WIDTH_8:
set_val = MMC_EXT_CSD_BUS_WIDTH_8;
break;
default:
set_val = MMC_EXT_CSD_BUS_WIDTH_1;
}
ret = mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_BUS_WIDTH, set_val);
if (-1 == ret) {
SD_LOGW("Old-MMC Card with 1 bit data only!!\n");
return -1;
}
SD_LOGD("RS-MMC Card!!\n");
card->bus_width = width;
return 0;
}
int32_t mmc_set_buswidth(struct mmc_card *card, uint32_t width)
{
if (card->type == CT_MMC) {
if (card->csd.mmc_spec_ver < MMC_CSD_SPEC_VER_4) {
card->bus_width = width = MMC_BUS_WIDTH_1;
} else if (mmc_switch_buswidth(card, width)) {
SD_LOGD("Set bus width error, use default 1 bit !!\n");
return -1;
}
} else if (card->type == CT_SDSC1x || card->type == CT_SDSC20 || \
card->type == CT_SDHC20 || card->type == CT_SDXC30) {
if (mmc_app_set_bus_width(card, width)) {
SD_LOGD("Set bus width error, use default 1 bit !!\n");
return -1;
}
} else
return -1;
HAL_SDC_Set_BusWidth(card->host, width);
SD_LOGD("Set bus width type: %d !!\n", (unsigned int)width);
return 0;
}
int32_t mmc_switch_part(struct mmc_card *card, uint32_t part_num)
{
return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_PART_CONF,
(card->extcsd.part_config & ~MMC_SWITCH_PART_ACCESS_MASK)
| (part_num & MMC_SWITCH_PART_ACCESS_MASK));
}
int32_t mmc_switch_boot_part(struct mmc_card *card, uint32_t boot_ack, uint32_t boot_part)
{
return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_PART_CONF,
(card->extcsd.part_config & (~MMC_SWITCH_PART_BOOT_PART_MASK) & (~MMC_SWITCH_PART_BOOT_ACK_MASK))
| ((boot_part << 3) & MMC_SWITCH_PART_BOOT_PART_MASK) | (boot_ack << 6));
}
int32_t mmc_switch_boot_bus_cond(struct mmc_card *card, uint32_t boot_mode, uint32_t rst_bus_cond, uint32_t bus_width)
{
return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_BOOT_BUS_COND,
(card->extcsd.boot_bus_cond &
(~MMC_SWITCH_BOOT_MODE_MASK) &
(~MMC_SWITCH_BOOT_RST_BUS_COND_MASK) &
(~MMC_SWITCH_BOOT_BUS_WIDTH_MASK))
| ((boot_mode << 3) & MMC_SWITCH_BOOT_MODE_MASK)
| ((rst_bus_cond << 2) & MMC_SWITCH_BOOT_RST_BUS_COND_MASK)
| ((bus_width) & MMC_SWITCH_BOOT_BUS_WIDTH_MASK) );
}
int32_t smc_model_set_blkcnt(struct mmc_host *host, uint32_t blkcnt)
{
struct mmc_command cmd = {0};
cmd.opcode = MMC_SET_BLOCK_COUNT;
cmd.arg = blkcnt & 0xffff;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
if (mmc_wait_for_cmd(host, &cmd)) {
return -1;
}
host->blkcnt = blkcnt;
return 0;
}
int32_t sdmmc_stream_write(struct mmc_card *card, uint32_t blk_num, uint32_t blk_size, uint32_t sg_len, struct scatterlist *sg)
{
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq;
uint32_t status = 0;
if (!card || !card->host) {
SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d err", __func__, __LINE__);
return -1;
}
cmd.opcode = MMC_WRITE_SINGLE_BLOCK;
cmd.arg = blk_num;
if (!mmc_card_blockaddr(card))
cmd.arg <<= 9;
cmd.stop = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |MMC_CMD_ADTC;
cmd.data = &data;
data.flags |= MMC_DATA_WRITE | MMC_DATA_STREAM;
data.blksz = blk_size;
data.sg_len = sg_len;
data.sg = sg;
mrq.cmd = &cmd;
mrq.data = &data;
if (mmc_wait_for_req(card->host, &mrq)) {
return -1;
}
/* check busy */
do {
if (HAL_SDC_Is_Busy(card->host))
continue;
mmc_send_status(card, &status);
} while (!(status & 0x100));
return 0;
}
/*
* Starting point for MMC card init.
*/
int mmc_attach_mmc(struct mmc_card *card, struct mmc_host *host)
{
int err;
uint32_t ocr;
uint32_t clk = 400000;
if (!host) {
SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d no host exist!\n", __func__, __LINE__);
return -1;
}
//SD_WARN_ON(!host->claimed);
/* send cmd1 to check MMC */
err = mmc_send_op_cond(card, 0, &ocr);
if (err)
return err;
card->type = CT_MMC;
/* cmd2, send cid */
if (mmc_all_send_cid(host, card->cidno)) {
SD_LOGD("All cards send CID number failed !!\n");
return -1;
} else
SD_LOGD("CID number:%x\n", (unsigned int)card->cidno[0]);
SD_LOGD("%s,%d !!!!!!!@@@@@@@@ called mmc_attach_sd\n", __func__, __LINE__);
/* cmd3, For native busses: get card RCA and quit open drain mode. */
err = mmc_public_new_rca(card);
/* cmd10, get CID register */
if (sdmmc_send_cid(card)) {
SD_LOGW("Card send CID reg failed !!\n");
return -1;
}
/* cmd9, get CSD register */
if (mmc_sd_get_csd(card)) {
SD_LOGW("Card send CSD reg failed !!\n");
return -1;
}
/* cmd7, Select card to standby state, as all following commands rely on that. */
if (mmc_select_card(card, 1)) {
SD_LOGW("mmc_select_card failed !!\n");
return -1;
}
if (card->csd.mmc_spec_ver < MMC_CSD_SPEC_VER_4)
err = 0;
else
err = mmc_send_extcsd(card);
if (err == -1)
return -1;
//sd-acmd6, set buswidth, mmc-cmd6, switch buswidth
if (-1 == sdmmc_set_buswidth(card, 4))
return -1;
mmc_switch_to_high_speed(card);
card->sd_bus_speed = SD_SWITCH_ACCESS_HS_SDR25;
if (card->sd_bus_speed == SD_SWITCH_ACCESS_SDR104)
clk = 208000000;
else if (card->sd_bus_speed == SD_SWITCH_ACCESS_SDR50)
clk = 104000000;
else if (card->sd_bus_speed == SD_SWITCH_ACCESS_HS_SDR25)
clk = 50000000;
else
clk = 25000000;
clk = 50000000;
HAL_SDC_Update_Clk(card->host, clk);
sdmmc_enumerate_card_info(card);
//send tunning pattern
if (card->sd_bus_speed == SD_SWITCH_ACCESS_SDR104 || card->sd_bus_speed == SD_SWITCH_ACCESS_SDR50)
sd_send_tuning_pattern(card);
card->host->card = card;
return err;
}
#endif /* CONFIG_USE_MMC */