/*
* $QNXLicenseC:
* Copyright 2007, 2008, QNX Software Systems.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You
* may not reproduce, modify or distribute this software except in
* compliance with the License. You may obtain a copy of the License
* at: http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OF ANY KIND, either express or implied.
*
* This file may contain contributions from others, either as
* contributors under the License or as licensors under other terms.
* Please review this entire file for other proprietary rights or license
* notices, as well as the QNX Development Suite License Guide at
* http://licensing.qnx.com/license-guide/ for other information.
* $
*/
// Module Description: MMC command processing
#include <sim_mmc.h>
static const uint32_t mmcsd_ts_exp[] = { 100, 1000, 10000, 100000, 0, 0, 0, 0 };
static const uint32_t mmcsd_ts_mul[] = { 0, 1000, 1200, 1300, 1500, 2000, 2500, 3000,
3500, 4000, 4500, 5000, 5500, 6000, 7000, 8000 };
#define mmc_sendcmd(h, c, r, f, a) _mmc_sendcmd(h, (c) | ((r) << 8) | ((f) << 16), a)
int _mmc_sendcmd(SIM_HBA *hba, uint32_t cmdflg, uint32_t argument)
{
SIM_MMC_EXT *ext;
mmc_cmd_t cmd;
ext = (SIM_MMC_EXT *)hba->ext;
/* Put the card in idle state */
cmd.argument = argument;
cmd.opcode = cmdflg & 0xFF;
cmd.resp = (cmdflg >> 8) & 0xFF;
cmd.eflags = (cmdflg >> 16) & 0xFFFF;
if (ext->eflags & MMC_EFLAG_PPL)
cmd.eflags |= MMC_CMD_PPL;
ext->mmc_resp[0] = ext->mmc_resp[1] = ext->mmc_resp[2] = ext->mmc_resp[3] = 0;
ext->resp_type = cmd.resp;
return (ext->command(hba, &cmd));
}
static int mmc_send_rwcmd(SIM_HBA *hba, int nbytes, int flag)
{
SIM_MMC_EXT *ext;
uint32_t cmd;
ext = (SIM_MMC_EXT *)hba->ext;
if (nbytes == ext->mmc_blksize) {
cmd = ext->dir == MMC_DIR_IN ? MMC_READ_SINGLE_BLOCK : MMC_WRITE_BLOCK;
ext->stop = MMCSD_STOP_NONE;
} else {
cmd = ext->dir == MMC_DIR_IN ? MMC_READ_MULTIPLE_BLOCK : MMC_WRITE_MULTIPLE_BLOCK;
if (!(ext->hccap & MMC_HCCAP_ACMD12))
ext->stop = MMCSD_STOP_PEND;
else
ext->stop = MMCSD_STOP_AUTO;
flag |= MMC_CMD_DATA_MULTI;
}
if (ext->dir == MMC_DIR_IN)
flag |= MMC_CMD_DATA_IN;
flag |= MMC_CMD_DATA;
/*
* Data is expected
* DON'T set MMC_CMD_INTR flag
*/
if (mmc_sendcmd(hba, cmd, MMC_RSP_R1, flag,
(ext->eflags & MMC_EFLAG_HC) ? ext->blkno : (ext->blkno * ext->mmc_blksize)) != MMC_SUCCESS)
return 0;
return nbytes;
}
int mmc_pio_xfer(SIM_HBA *hba, uint32_t nbytes)
{
SIM_MMC_EXT *ext;
ext = (SIM_MMC_EXT *)hba->ext;
if (ext->setup_pio(hba, nbytes, ext->dir) == nbytes)
return mmc_send_rwcmd(hba, nbytes, 0);
return 0;
}
int mmc_dma_xfer(SIM_HBA *hba, paddr_t paddr, uint32_t nbytes)
{
SIM_MMC_EXT *ext;
ext = (SIM_MMC_EXT *)hba->ext;
if ((nbytes = ext->setup_dma(hba, paddr, nbytes, ext->dir)) > 0)
return mmc_send_rwcmd(hba, nbytes, MMC_CMD_DATA_DMA);
return 0;
}
int mmc_stop_xfer(SIM_HBA *hba)
{
return mmc_sendcmd(hba, MMC_STOP_TRANSMISSION, MMC_RSP_R1B, MMC_CMD_INTR, 0);
}
static void mmc_disp_mmc_cid(uint8_t mmc_prot, mmc_cid_t *cid)
{
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, "MMC CID info:");
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " MID (Manufacturer ID) : %d", cid->mid);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PNM (Product name) : %s", cid->pnm);
if (mmc_prot < 2) {
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " HWR (Hardware revision) : %d", cid->hwr);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " FWR (Hardware revision) : %d", cid->fwr);
} else
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " OID (OEM ID) : %d", cid->oid);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PSN (Product serial number) : %08x", cid->psn);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " MCD (Month code) : %d", cid->mcd);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " YCD (Year code) : %d", cid->ycd);
}
/*
* CSD has to be already parsed
*/
void mmc_parse_mmc_cid(SIM_HBA *hba, mmc_cid_t *cid, uint32_t *resp)
{
SIM_MMC_EXT *ext;
ext = (SIM_MMC_EXT *)hba->ext;
cid->pnm[0] = resp[3];
cid->pnm[1] = resp[2] >> 24;
cid->pnm[2] = resp[2] >> 16;
cid->pnm[3] = resp[2] >> 8;
cid->pnm[4] = resp[2];
cid->pnm[5] = resp[1] >> 24;
if (ext->csd.mmc_csd.mmc_prot < 2) {
cid->mid = resp[3] >> 8;
cid->pnm[6] = resp[1] >> 16;
cid->pnm[7] = 0;
cid->hwr = (resp[1] >> 12) & 0x0F;
cid->fwr = (resp[1] >> 8) & 0x0F;
} else {
cid->mid = resp[3] >> 24;
cid->oid = (resp[3] >> 8) & 0xFFFF;
cid->pnm[6] = 0;
}
cid->psn = ((resp[1] & 0xFFFF) << 16) | (resp[0] >> 16);
cid->mcd = (resp[0] >> 12) & 0x0F;
cid->ycd = ((resp[0] >> 8) & 0x0F) + 1997;
if (hba->verbosity > 1)
mmc_disp_mmc_cid(ext->csd.mmc_csd.mmc_prot, cid);
}
static void mmc_disp_sd_cid(sd_cid_t *cid)
{
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, "SD CID info:");
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " MID (Manufacturer ID) : %d", cid->mid);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " OID (OEM/Application ID) : %s", cid->oid);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PNM (Product name) : %s", cid->pnm);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PRV (Product revision) : %d.%d", cid->prv >> 4, cid->prv & 0x0F);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PSN (Product serial number) : %08x", cid->psn);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " MDT (Manufacturing date) : %d.%d", ((cid->mdt >> 4) & 0xFF) + 2000, cid->mdt & 0x0F);
}
void mmc_parse_sd_cid(SIM_HBA *hba, sd_cid_t *cid, uint32_t *resp)
{
cid->mid = resp[3] >> 24;
cid->oid[0] = (resp[3] >> 8) & 0xFF;
cid->oid[1] = (resp[3] >> 16) & 0xFFFF;
cid->oid[2] = 0;
cid->pnm[0] = resp[3];
cid->pnm[1] = resp[2] >> 24;
cid->pnm[2] = resp[2] >> 16;
cid->pnm[3] = resp[2] >> 8;
cid->pnm[4] = resp[2];
cid->pnm[5] = 0;
cid->prv = resp[1] >> 24;
cid->psn = (resp[1] << 8) | (resp[0] >> 24);
cid->mdt = (resp[0] >> 8) & 0xFFFF;
if (hba->verbosity > 1)
mmc_disp_sd_cid(cid);
}
static void mmc_disp_mmc_csd(mmc_csd_t *csd)
{
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, "MMC CSD info:");
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " CSD_STRUCTURE : %x", csd->csd_structure);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " MMC_PROT : %x", csd->mmc_prot);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " TAAC : %x", csd->taac);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " NSAC : %x", csd->nsac);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " TRAN_SPEED : %x", csd->tran_speed);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " CCC : %x", csd->ccc);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " READ_BL_LEN : %x", csd->read_bl_len);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " READ_BL_PARTIAL : %x", csd->read_bl_partial);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " WRITE_BLK_MISALIGN : %x", csd->write_blk_misalign);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " READ_BLK_MISALIGN : %x", csd->read_blk_misalign);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " DSR_IMP : %x", csd->dsr_imp);
if (csd->csd_structure <= CSD_VERSION_11) {
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " SECTOR_SIZE : %x", csd->erase.mmc_v22.sector_size);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " ERASE_GRP_SIZE : %x", csd->erase.mmc_v22.erase_grp_size);
} else {
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " ERASE_GRP_SIZE : %x", csd->erase.mmc_v31.erase_grp_size);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " ERASE_GRP_MULT : %x", csd->erase.mmc_v31.erase_grp_mult);
}
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " WP_GRP_SIZE : %x", csd->wp_grp_size);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " R2W_FACTOR : %x", csd->r2w_factor);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " WRITE_BL_LEN : %x", csd->write_bl_len);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " WRITE_BL_PARTIAL : %x", csd->write_bl_partial);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " COPY : %x", csd->copy);
slogf(_SLOGC_SIM_MMC, _SLOG_INFO, " PERM_WRITE_PROTECT : %x", csd->perm_