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) return err; else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) break; udelay(1000); if (cmd.response[0] & MMC_STATUS_MASK) { mmcdbg("Status Error: 0x%08X\n", cmd.response[0]); return COMM_ERR; } } while (timeout--); if (!timeout) { mmcdbg("Timeout waiting card ready\n"); return TIMEOUT; } return 0; }
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) { mmcdbg("MMC: block number 0x%lx exceeds max(0x%lx)\n", 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)) { mmcdbg("mmc write failed\n"); 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)) { mmcdbg("mmc fail to send stop cmd\n"); 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; }
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) return 0; if (!mmc) return 0; if ((start + blkcnt) > mmc->lba) { mmcdbg("MMC: block number 0x%x exceeds max(0x%x)\n", start + blkcnt, mmc->lba); return 0; } if (mmc_set_blocklen(mmc, mmc->read_bl_len)) return 0; do { cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur) 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; }
struct mmc *find_mmc_device(int dev_num) { if (mmc_devices[dev_num] != NULL) return mmc_devices[dev_num]; mmcdbg("MMC Device %d not found\n", dev_num); return NULL; }
static unsigned long mmc_erase_t(struct mmc *mmc, unsigned long start, unsigned blkcnt) { struct mmc_cmd cmd; unsigned long end; int err, start_cmd, end_cmd; if (mmc->high_capacity) end = start + blkcnt - 1; else { end = (start + blkcnt - 1) * mmc->write_bl_len; start *= mmc->write_bl_len; } if (IS_SD(mmc)) { start_cmd = SD_CMD_ERASE_WR_BLK_START; end_cmd = SD_CMD_ERASE_WR_BLK_END; } else { start_cmd = MMC_CMD_ERASE_GROUP_START; end_cmd = MMC_CMD_ERASE_GROUP_END; } cmd.cmdidx = start_cmd; cmd.cmdarg = start; cmd.resp_type = MMC_RSP_R1; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) goto err_out; cmd.cmdidx = end_cmd; cmd.cmdarg = end; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) goto err_out; cmd.cmdidx = MMC_CMD_ERASE; cmd.cmdarg = SECURE_ERASE; cmd.resp_type = MMC_RSP_R1b; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) goto err_out; return 0; err_out: mmcdbg("mmc erase failed\n"); return err; }
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)) 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)) { mmcdbg("mmc fail to send stop cmd\n"); return 0; } /* Waiting for the ready status */ mmc_send_status(mmc, timeout); } return blkcnt; }
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_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|MMC_MODE_8BIT; 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] & 0xff; //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 & EXT_CSD_CARD_TYPE_HS) { //EXT_CSD_CARD_TYPE_52 if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) { mmcdbg("%s: get ddr OK!\n", __FUNCTION__); mmc->card_caps |= MMC_MODE_DDR_52MHz; mmc->speed_mode = HSDDR52_DDR50; } else mmc->speed_mode = HSSDR52_SDR25; mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; } else { mmc->card_caps |= MMC_MODE_HS; mmc->speed_mode = DS26_SDR12; } return 0; }