int mmc_send_status(struct mmc *mmc, int timeout) { struct mmc_cmd cmd; int err; cmd.cmdidx = MMC_CMD_SEND_STATUS; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; cmd.flags = 0; do { err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d Send status failed\n",mmc->control_num); return err; } else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) break; __msdelay(1); if (cmd.response[0] & MMC_STATUS_MASK) { mmcinfo("mmc %d Status Error: 0x%08X\n",mmc->control_num, cmd.response[0]); return COMM_ERR; } } while (timeout--); if (!timeout) { mmcinfo("mmc %d Timeout waiting card ready\n",mmc->control_num); return TIMEOUT; } return 0; }
int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) { struct mmc_cmd cmd; int timeout = 1000; int ret; cmd.cmdidx = MMC_CMD_SWITCH; cmd.resp_type = MMC_RSP_R1b; cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8); cmd.flags = 0; ret = mmc_send_cmd(mmc, &cmd, NULL); if(ret){ mmcinfo("mmc %d switch failed\n",mmc->control_num); } /* for re-update sample phase */ ret = mmc_update_phase(mmc); if (ret) { mmcinfo("mmc_switch: update clock failed after send cmd6\n"); return ret; } /* Waiting for the ready status */ mmc_send_status(mmc, timeout); return ret; }
static unsigned long mmc_write_blocks(struct mmc *mmc, unsigned long start, unsigned blkcnt, const void*src) { struct mmc_cmd cmd; struct mmc_data data; int timeout = 1000; if ((start + blkcnt) > mmc->lba) { mmcinfo("mmc %d: block number 0x%lx exceeds max(0x%lx)\n",mmc->control_num, start + blkcnt, mmc->lba); return 0; } if (blkcnt > 1) cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; else cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; if (mmc->high_capacity) cmd.cmdarg = start; else cmd.cmdarg = start * mmc->write_bl_len; cmd.resp_type = MMC_RSP_R1; cmd.flags = 0; data.b.src = src; data.blocks = blkcnt; data.blocksize = mmc->write_bl_len; data.flags = MMC_DATA_WRITE; if (mmc_send_cmd(mmc, &cmd, &data)) { mmcinfo("mmc %d mmc write failed\n",mmc->control_num); return 0; } /* SPI multiblock writes terminate using a special * token, not a STOP_TRANSMISSION request. */ if (!mmc_host_is_spi(mmc) && blkcnt > 1) { cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; cmd.flags = 0; if (mmc_send_cmd(mmc, &cmd, NULL)) { mmcinfo("mmc %d fail to send stop cmd\n",mmc->control_num); return 0; } } /* Waiting for the ready status */ mmc_send_status(mmc, timeout); return blkcnt; }
static int mmc_config_clock(struct mmc *mmc, unsigned clk) { struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv; unsigned rval = readl(&mmchost->reg->clkcr); // unsigned int clkdiv = 0; /* Disable Clock */ rval &= ~(1 << 16); writel(rval, &mmchost->reg->clkcr); if(mmc_update_clk(mmc)){ mmcinfo("mmc %d disable clock failed\n",mmchost ->mmc_no); return -1; } // clkdiv = mmchost->mclk/clk/2; //disable mclk first writel(0,mmchost->mclkbase); mmcdbg("mmc %d mclkbase %x\n",mmchost ->mmc_no,readl(mmchost->mclkbase)); if (clk <=400000) { mmchost->mclk = 400000; writel(0x0002000f, mmchost->mclkbase); mmcdbg("mmc %d mclkbase%x\n",mmchost ->mmc_no,readl(mmchost->mclkbase)); } else { mmchost->mclk = 12000000; writel(0x00000001, mmchost->mclkbase); mmcdbg("mmc %d mclkbase%x\n",mmchost ->mmc_no,readl(mmchost->mclkbase)); } //re-enable mclk writel(readl(mmchost->mclkbase)|(1<<31),mmchost->mclkbase); mmcdbg("mmc %d mclkbase%x\n",mmchost ->mmc_no,readl(mmchost->mclkbase)); /* * CLKCREG[7:0]: divider * CLKCREG[16]: on/off * CLKCREG[17]: power save */ /* Change Divider Factor */ rval &= ~(0xFF); writel(rval, &mmchost->reg->clkcr); if(mmc_update_clk(mmc)){ mmcinfo("mmc %d Change Divider Factor failed\n",mmchost ->mmc_no); return -1; } /* Re-enable Clock */ rval |= (3 << 16); writel(rval, &mmchost->reg->clkcr); if(mmc_update_clk(mmc)){ mmcinfo("mmc %d re-enable clock failed\n",mmchost ->mmc_no); return -1; } return 0; }
static int mmc_resource_init(int sdc_no) { struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no]; mmcdbg("init mmc %d resource\n", sdc_no); mmchost->reg = (struct sunxi_mmc *)(MMC_REG_BASE + sdc_no * 0x1000); mmchost->database = (u32)mmchost->reg + MMC_REG_FIFO_OS; #ifdef CONFIG_ARCH_SUN9IW1P1 mmchost->commreg = MMC_REG_COMM_BASE+sdc_no*4; mmchost->hclkbase = 0x06000400+0x180; mmchost->hclkrst = 0x06000400+0x1a0; #else mmchost->hclkbase = CCMU_HCLKGATE0_BASE; #if !defined(CONFIG_ARCH_SUN5I) && !defined(CONFIG_ARCH_SUN7I) mmchost->hclkrst = CCMU_HCLKRST0_BASE; #endif #endif if (sdc_no == 0) mmchost->mclkbase = CCMU_MMC0_CLK_BASE; else if (sdc_no == 2) mmchost->mclkbase = CCMU_MMC2_CLK_BASE; else { mmcinfo("Wrong mmc NO.: %d\n", sdc_no); return -1; } mmchost->mmc_no = sdc_no; return 0; }
int mmc_berase(int dev_num, unsigned long start, unsigned blkcnt) { int err = 0; struct mmc *mmc = find_mmc_device(dev_num); // unsigned blk = 0, blk_r = 0; void* src = (void*)0x41000000; if (!mmc) return -1; memset(src, 0, 512*blkcnt); mmcinfo("erase blk %d ~ %d\n", start, start + blkcnt - 1); err = mmc_bwrite(dev_num, start, blkcnt, src); return err; /* if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) mmcdbg("\n\nCaution! Your devices Erase group is 0x%x\n" "The erase range would be change to 0x%x~0x%x\n\n", mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), ((start + blkcnt + mmc->erase_grp_size) & ~(mmc->erase_grp_size - 1)) - 1); while (blk < blkcnt) { blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? mmc->erase_grp_size : (blkcnt - blk); err = mmc_erase_t(mmc, start + blk, blk_r); if (err) break; blk += blk_r; } return blk; */ }
int mmc_startup(struct mmc *mmc) { int err; u32 mult, freq; u64 cmult, csize, capacity; struct mmc_cmd cmd; char ext_csd[512]; int timeout = 1000; char *spd_name[] = {"DS26/SDR12", "HSSDR52/SDR25", "HSDDR52/DDR50", "HS200/SDR104", "HS400"}; /* Put the Card in Identify Mode */ cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = 0; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d Put the Card in Identify Mode failed\n",mmc->control_num); return err; } memcpy(mmc->cid, cmd.response, 16); /* * For MMC cards, set the Relative Address. * For SD cards, get the Relatvie Address. * This also puts the cards into Standby State */ if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; cmd.cmdarg = mmc->rca << 16; cmd.resp_type = MMC_RSP_R6; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d send rca failed\n",mmc->control_num); return err; } if (IS_SD(mmc)) mmc->rca = (cmd.response[0] >> 16) & 0xffff; }
int mmc_read_blocks(struct mmc *mmc, void *dst, unsigned long start, unsigned blkcnt) { struct mmc_cmd cmd; struct mmc_data data; int timeout = 1000; if (blkcnt > 1) cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; else cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; if (mmc->high_capacity) cmd.cmdarg = start; else cmd.cmdarg = start * mmc->read_bl_len; cmd.resp_type = MMC_RSP_R1; cmd.flags = 0; data.b.dest = dst; data.blocks = blkcnt; data.blocksize = mmc->read_bl_len; data.flags = MMC_DATA_READ; if (mmc_send_cmd(mmc, &cmd, &data)){ mmcinfo("mmc %d read blcok failed\n",mmc->control_num); return 0; } if (blkcnt > 1) { cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; cmd.flags = 0; if (mmc_send_cmd(mmc, &cmd, NULL)) { mmcinfo("mmc %d fail to send stop cmd\n",mmc->control_num); return 0; } /* Waiting for the ready status */ mmc_send_status(mmc, timeout); } return blkcnt; }
struct mmc *find_mmc_device(int dev_num) { if (mmc_devices[dev_num] != NULL) return mmc_devices[dev_num]; mmcinfo("MMC Device %d not found\n", dev_num); return NULL; }
unsigned long mmc_bread(int dev_num, unsigned long start, unsigned blkcnt, void *dst) { unsigned cur, blocks_todo = blkcnt; struct mmc *mmc = find_mmc_device(dev_num); if (blkcnt == 0){ mmcinfo("mmc %d blkcnt should not be 0\n",mmc->control_num); return 0; } if (!mmc){ mmcinfo("Can not find mmc dev %d\n",dev_num); return 0; } if ((start + blkcnt) > mmc->lba) { mmcinfo("mmc %d: block number 0x%x exceeds max(0x%x)\n",mmc->control_num, start + blkcnt, mmc->lba); return 0; } if (mmc_set_blocklen(mmc, mmc->read_bl_len)){ mmcinfo("mmc %d Set block len failed\n",mmc->control_num); return 0; } do { cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur){ mmcinfo("mmc %d block read failed\n",mmc->control_num); return 0; } blocks_todo -= cur; start += cur; // dst += cur * mmc->read_bl_len; dst = (char*)dst + cur * mmc->read_bl_len; } while (blocks_todo > 0); return blkcnt; }
unsigned long mmc_bwrite(int dev_num, unsigned long start, unsigned blkcnt, const void*src) { unsigned cur, blocks_todo = blkcnt; struct mmc *mmc = find_mmc_device(dev_num); if (blkcnt == 0){ mmcinfo("mmc %d blkcnt should not be 0\n",dev_num); return 0; } if (!mmc){ mmcinfo("Can not found device %d\n",dev_num); return 0; } if (mmc_set_blocklen(mmc, mmc->write_bl_len)){ mmcinfo("mmc %d set block len failed\n",mmc->control_num); // sunxi_mmc_exit(dev_num); // if(sunxi_mmc_init(dev_num,4)<0){ // mmcinfo("re init failed\n"); // return 0; // } return 0; } do { cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; if(mmc_write_blocks(mmc, start, cur, src) != cur){ mmcinfo("mmc %d write block failed\n",mmc->control_num); return 0; } blocks_todo -= cur; start += cur; // src += cur * mmc->write_bl_len; src = (char*)src + cur * mmc->write_bl_len; } while (blocks_todo > 0); return blkcnt; }
static int mmc_update_clk(struct mmc *mmc) { struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv; unsigned int cmd; int timeout = 0xfffff; cmd = (1U << 31) | (1 << 21) | (1 << 13); writel(cmd, &mmchost->reg->cmd); while((readl(&mmchost->reg->cmd)&0x80000000) && timeout--); if (timeout<0){ mmcinfo("mmc %d update clk failed\n",mmchost ->mmc_no); dumphex32("mmc", (char*)mmchost->reg, 0x100); return -1; } writel(readl(&mmchost->reg->rint), &mmchost->reg->rint); return 0; }
static void mmc_set_ios(struct mmc *mmc) { struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv; mmcdbg("mmc %d ios: bus: %d, clock: %d\n",mmchost ->mmc_no, mmc->bus_width, mmc->clock); if (mmc->clock && mmc_config_clock(mmc, mmc->clock)) { mmcinfo("[mmc]: " "*** update clock failed\n"); mmchost->fatal_err = 1; return; } /* Change bus width */ if (mmc->bus_width == 8) writel(2, &mmchost->reg->width); else if (mmc->bus_width == 4) writel(1, &mmchost->reg->width); else writel(0, &mmchost->reg->width); }
static int mmc_resource_init(int sdc_no) { struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no]; mmcdbg("init mmc %d resource\n", sdc_no); mmchost->reg = (struct sunxi_mmc *)(MMC_REG_BASE + sdc_no * 0x1000); mmchost->database = (u32)mmchost->reg + MMC_REG_FIFO_OS; mmchost->hclkbase = CCMU_HCLKGATE0_BASE; mmchost->hclkrst = CCMU_BUS_SOFT_RST_REG0; if (sdc_no == 0) mmchost->mclkbase = CCMU_MMC0_CLK_BASE; else if (sdc_no == 2) mmchost->mclkbase = CCMU_MMC2_CLK_BASE; else { mmcinfo("Wrong mmc NO.: %d\n", sdc_no); return -1; } mmchost->mmc_no = sdc_no; return 0; }
int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) { struct mmc_cmd cmd; struct mmc_data data; int err; /* Get the Card Status Register */ cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; cmd.flags = 0; data.b.dest = ext_csd; data.blocks = 1; data.blocksize = 512; data.flags = MMC_DATA_READ; err = mmc_send_cmd(mmc, &cmd, &data); if(err) mmcinfo("mmc %d send ext csd failed\n",mmc->control_num); return err; }
int mmc_go_idle(struct mmc* mmc) { struct mmc_cmd cmd; int err; __msdelay(1); cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_NONE; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d go idle failed\n",mmc->control_num); return err; } __msdelay(2); return 0; }
int sd_change_freq(struct mmc *mmc) { int err; struct mmc_cmd cmd; u32 scr[2]; u32 switch_status[16]; struct mmc_data data; int timeout; mmc->card_caps = 0; if (mmc_host_is_spi(mmc)) return 0; /* Read the SCR to find out if this card supports higher speeds */ cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d Send app cmd failed\n",mmc->control_num); return err; } cmd.cmdidx = SD_CMD_APP_SEND_SCR; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; cmd.flags = 0; timeout = 3; retry_scr: data.b.dest = (char *)&scr; data.blocksize = 8; data.blocks = 1; data.flags = MMC_DATA_READ; err = mmc_send_cmd(mmc, &cmd, &data); if (err) { if (timeout--) goto retry_scr; mmcinfo("mmc %d Send scr failed\n",mmc->control_num); return err; } mmc->scr[0] = __be32_to_cpu(scr[0]); mmc->scr[1] = __be32_to_cpu(scr[1]); switch ((mmc->scr[0] >> 24) & 0xf) { case 0: mmc->version = SD_VERSION_1_0; break; case 1: mmc->version = SD_VERSION_1_10; break; case 2: mmc->version = SD_VERSION_2; break; default: mmc->version = SD_VERSION_1_0; break; } if (mmc->scr[0] & SD_DATA_4BIT) mmc->card_caps |= MMC_MODE_4BIT; /* Version 1.0 doesn't support switching */ if (mmc->version == SD_VERSION_1_0) return 0; timeout = 4; while (timeout--) { err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, (u8 *)&switch_status); if (err){ mmcinfo("mmc %d Check high speed status faild\n",mmc->control_num); return err; } /* The high-speed function is busy. Try again */ if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) break; } /* If high-speed isn't supported, we return */ if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) return 0; err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)&switch_status); if (err){ mmcinfo("mmc %d switch to high speed failed\n",mmc->control_num); return err; } if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) mmc->card_caps |= MMC_MODE_HS; return 0; }
int mmc_change_freq(struct mmc *mmc) { char ext_csd[512]; char cardtype; int err; int retry = 5; mmc->card_caps = 0; if (mmc_host_is_spi(mmc)) return 0; /* Only version 4 supports high-speed */ if (mmc->version < MMC_VERSION_4) return 0; mmc->card_caps |= MMC_MODE_4BIT; err = mmc_send_ext_csd(mmc, ext_csd); if (err){ mmcinfo("mmc %d get ext csd failed\n",mmc->control_num); return err; } cardtype = ext_csd[196] & 0xf; //retry for Toshiba emmc,for the first time Toshiba emmc change to HS //it will return response crc err,so retry do{ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if(!err){ break; } mmcinfo("retry mmc switch(cmd6)\n"); }while(retry--); if (err){ mmcinfo("mmc %d change to hs failed\n",mmc->control_num); return err; } /* Now check to see that it worked */ err = mmc_send_ext_csd(mmc, ext_csd); if (err){ mmcinfo("mmc %d send ext csd faild\n",mmc->control_num); return err; } /* No high-speed support */ if (!ext_csd[185]) return 0; /* High Speed is set, there are two types: 52MHz and 26MHz */ if (cardtype & MMC_HS_52MHZ) mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; else mmc->card_caps |= MMC_MODE_HS; return 0; }
int mmc_send_op_cond(struct mmc *mmc) { int timeout = 10000; struct mmc_cmd cmd; int err; /* Some cards seem to need this */ mmc_go_idle(mmc); /* Asking to the card its capabilities */ cmd.cmdidx = MMC_CMD_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; cmd.cmdarg = 0x40ff8000;//foresee cmd.flags = 0; //mmcinfo("mmc send op cond arg not zero !!!\n"); err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d send op cond failed\n",mmc->control_num); return err; } __msdelay(1); do { cmd.cmdidx = MMC_CMD_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : (mmc->voltages & (cmd.response[0] & OCR_VOLTAGE_MASK)) | (cmd.response[0] & OCR_ACCESS_MODE)); if (mmc->host_caps & MMC_MODE_HC) cmd.cmdarg |= OCR_HCS; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d send op cond failed\n",mmc->control_num); return err; } __msdelay(1); } while (!(cmd.response[0] & OCR_BUSY) && timeout--); if (timeout <= 0){ mmcinfo("mmc %d wait for mmc init failed\n",mmc->control_num); return UNUSABLE_ERR; } if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ cmd.cmdidx = MMC_CMD_SPI_READ_OCR; cmd.resp_type = MMC_RSP_R3; cmd.cmdarg = 0; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) return err; } mmc->version = MMC_VERSION_UNKNOWN; mmc->ocr = cmd.response[0]; mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); mmc->rca = 1; return 0; }
int sd_send_op_cond(struct mmc *mmc) { int timeout = 1000; int err; struct mmc_cmd cmd; do { cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d send app cmd failed\n",mmc->control_num); return err; } cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; /* * Most cards do not answer if some reserved bits * in the ocr are set. However, Some controller * can set bit 7 (reserved for low voltages), but * how to manage low voltages SD card is not yet * specified. */ cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : (mmc->voltages & 0xff8000); if (mmc->version == SD_VERSION_2) cmd.cmdarg |= OCR_HCS; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d send cmd41 failed\n",mmc->control_num); return err; } __msdelay(1); } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--); if (timeout <= 0){ mmcinfo("mmc %d wait card init failed\n",mmc->control_num); return UNUSABLE_ERR; } if (mmc->version != SD_VERSION_2) mmc->version = SD_VERSION_1_0; if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ cmd.cmdidx = MMC_CMD_SPI_READ_OCR; cmd.resp_type = MMC_RSP_R3; cmd.cmdarg = 0; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err){ mmcinfo("mmc %d spi read ocr failed\n",mmc->control_num); return err; } } mmc->ocr = cmd.response[0]; mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); mmc->rca = 0; return 0; }