/* * Card detection callback from host. */ static void mmc_detect(struct mmc_host *host) { int err; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); /* * Just check if our card has been removed. */ err = mmc_send_status(host->card, NULL); mmc_release_host(host); if (err) { mmc_remove(host); mmc_claim_host(host); mmc_detach_bus(host); mmc_release_host(host); } }
/* * Card detection - card is alive. */ static int mmc_alive(struct mmc_host *host) { return mmc_send_status(host->card, NULL); }
/** * __mmc_switch - modify EXT_CSD register * @card: the MMC card associated with the data transfer * @set: cmd set values * @index: EXT_CSD register index * @value: value to program into EXT_CSD register * @timeout_ms: timeout (ms) for operation performed by register write, * timeout of zero implies maximum possible timeout * @use_busy_signal: use the busy signal as response type * * Modifies the EXT_CSD register for selected card. */ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, bool use_busy_signal) { int err; struct mmc_command cmd = {0}; unsigned long timeout; u32 status; BUG_ON(!card); BUG_ON(!card->host); cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; cmd.flags = MMC_CMD_AC; if (use_busy_signal) cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; else cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; cmd.cmd_timeout_ms = timeout_ms; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) return err; /* No need to check card status in case of unblocking command */ if (!use_busy_signal) return 0; /* Must check status to be sure of no errors */ timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { err = mmc_send_status(card, &status); if (err) { if (err == -EILSEQ && index == EXT_CSD_HS_TIMING) { pr_warn("%s: CMD13 error after switching timing\n" "%s: this error can be ignored...\n", mmc_hostname(card->host), mmc_hostname(card->host)); return 0; } else return err; } if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; /* Timeout if the device never leaves the program state. */ if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", mmc_hostname(card->host), __func__); return -ETIMEDOUT; } } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(card->host)) { if (status & R1_SPI_ILLEGAL_COMMAND) return -EBADMSG; } else { if (status & 0xFDFFA000) pr_warning("%s: unexpected status %#x after " "switch", mmc_hostname(card->host), status); if (status & R1_SWITCH_ERROR) return -EBADMSG; } return 0; }
/* * This needs to be called with host claimed * @part: GPP partition part ID, should be 1/2/3/4. * @addr: GPP write group unit */ int mmc_set_user_wp(struct mmc_card *card, unsigned int part, unsigned int wpg) { struct mmc_command cmd = {0}; int err = 0; u32 status = 0; if (!card) return -ENODEV; mmc_claim_host(card->host); /* * enable WP to partitions * set bit2 of ext_csd[171], permanent write protect */ err = mmc_switch_bits(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USER_WP, EXT_CSD_PERMANENT_WP, card->ext_csd.generic_cmd6_time, true, true); if (err) { pr_err("%s: enable permanent write protect err %d!\n", __func__, err); mmc_release_host(card->host); return err; } err = mmc_switch_part(card, part); if (err) goto switchback; cmd.opcode = MMC_SET_WRITE_PROT; cmd.arg = wpg * card->ext_csd.wpg_sz; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) { pr_err("%s: failed to set addr 0x%x write protected, err %d\n", __func__, cmd.arg, err); goto out; } /* Must check status to be sure of no errors */ do { err = mmc_send_status(card, &status); if (err) { pr_err("%s: card status get err %d, status 0x%x\n", __func__, err, status); goto out; } if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(card->host)) { if (status & R1_SPI_ILLEGAL_COMMAND) { pr_err("%s: error card status 0x%x\n", __func__, status); err = -EILSEQ; goto out; } } else { if (status & 0xFDFFA000) pr_warn("%s: unexpected status %#x after switch", __func__, status); if (status & R1_SWITCH_ERROR) { pr_err("%s: card switch error, status 0x%x\n", __func__, status); err = -EIO; goto out; } if (status & R1_OUT_OF_RANGE) { pr_err("%s: addr out of range, status 0x%x\n", __func__, status); err = -EINVAL; } } out: err = mmc_switch_part(card, EXT_CSD_PART_CONFIG_ACC_USER); if (err) { pr_warn("%s: switch to USER partition failed!\n", __func__); WARN_ON(err); } switchback: /* * clear bit2 of ext_csd[171], permanent write protect */ err = mmc_switch_bits(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USER_WP, EXT_CSD_PERMANENT_WP, card->ext_csd.generic_cmd6_time, true, false); if (err) { pr_err("%s: clear write protect err %d!\n", __func__, err); } mmc_release_host(card->host); return err; }
/* * @part: GPP partition part number * @addr: GPP write group */ int mmc_wp_status(struct mmc_card *card, unsigned int part, unsigned int addr, u8 *wp_status) { struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct mmc_request mrq = {0}; struct scatterlist sg; u32 status = 0; int err = 0; u8 *rbuf = NULL; if (!card) return -ENODEV; if (!card->ext_csd.gpp_sz[part - EXT_CSD_PART_CONFIG_ACC_GP0]) { pr_err("%s: doesn't have GPP%d\n", __func__, part - 3); return -ENODEV; } rbuf = kzalloc(8, GFP_KERNEL); if (rbuf == NULL) { pr_err("%s: no memory\n", __func__); return -ENOMEM; } cmd.opcode = MMC_SEND_WRITE_PROT_TYPE; cmd.arg = addr * card->ext_csd.wpg_sz; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; data.sg = &sg; data.sg_len = 1; data.blksz = 8; data.blocks = 1; data.flags = MMC_DATA_READ; sg_init_one(data.sg, rbuf, 8); mrq.data = &data; mrq.cmd = &cmd; mmc_claim_host(card->host); mmc_set_data_timeout(&data, card); err = mmc_switch_part(card, part); if (err) { mmc_release_host(card->host); dev_err(mmc_dev(card->host), "%s: swith error %d\n", __func__, err); goto out; } mmc_wait_for_req(card->host, &mrq); if (cmd.error) { dev_err(mmc_dev(card->host), "%s: cmd error %d\n", __func__, cmd.error); } if (data.error) { dev_err(mmc_dev(card->host), "%s: data error %d\n", __func__, data.error); } /* Must check status to be sure of no errors */ do { err = mmc_send_status(card, &status); if (err) { pr_err("%s: get card status err %d, status 0x%x\n", __func__, err, status); goto out; } if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(card->host)) { if (status & R1_SPI_ILLEGAL_COMMAND) { pr_err("%s: error card status 0x%x\n", __func__, status); goto out; } } else { if (status & 0xFDFFA000) pr_warn("%s: unexpected status %#x after switch", __func__, status); if (status & R1_SWITCH_ERROR) { pr_err("%s: card switch error, status 0x%x\n", __func__, status); } if (status & R1_OUT_OF_RANGE) { pr_err("%s: addr out of range, status 0x%x\n", __func__, status); goto out; } } mmc_switch_part(card, EXT_CSD_PART_CONFIG_ACC_USER); mmc_release_host(card->host); sg_copy_from_buffer(data.sg, 1, rbuf, 8); /* * the first write protect group type is in the last two * bits in the last byte read from the device. */ *wp_status = rbuf[7] & 0x3; kfree(rbuf); return 0; out: kfree(rbuf); return -EPERM; }
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, bool use_busy_signal, bool ignore_timeout) { int err; struct mmc_command cmd = {0}; unsigned long timeout; u32 status; BUG_ON(!card); BUG_ON(!card->host); cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; cmd.flags = MMC_CMD_AC; if (use_busy_signal) cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; else cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; cmd.cmd_timeout_ms = timeout_ms; cmd.ignore_timeout = ignore_timeout; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) return err; if (!use_busy_signal) return 0; timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { err = mmc_send_status(card, &status); if (err) return err; if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", mmc_hostname(card->host), __func__); return -ETIMEDOUT; } } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(card->host)) { if (status & R1_SPI_ILLEGAL_COMMAND) return -EBADMSG; } else { if (status & 0xFDFFA000) pr_warning("%s: unexpected status %#x after " "switch", mmc_hostname(card->host), status); if (status & R1_SWITCH_ERROR) return -EBADMSG; } return 0; }
static int mmc_queue_thread(void *d) { struct mmc_queue *mq = d; struct request_queue *q = mq->queue; struct request *req; //ruanmeisi_20100603 int issue_ret = 0; #ifdef CONFIG_MMC_PERF_PROFILING ktime_t start, diff; struct mmc_host *host = mq->card->host; unsigned long bytes_xfer; #endif current->flags |= PF_MEMALLOC; down(&mq->thread_sem); do { req = NULL; //ruanmeisi_20100603 if (kthread_should_stop()) { remove_all_req(mq); break; } //end spin_lock_irq(q->queue_lock); set_current_state(TASK_INTERRUPTIBLE); if (!blk_queue_plugged(q)) req = blk_fetch_request(q); mq->req = req; spin_unlock_irq(q->queue_lock); if (!req) { if (kthread_should_stop()) { set_current_state(TASK_RUNNING); break; } up(&mq->thread_sem); schedule(); down(&mq->thread_sem); continue; } set_current_state(TASK_RUNNING); #ifdef CONFIG_MMC_AUTO_SUSPEND mmc_auto_suspend(mq->card->host, 0); #endif #ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME if (mq->check_status) { struct mmc_command cmd; int retries = 3; do { int err; cmd.opcode = MMC_SEND_STATUS; cmd.arg = mq->card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; mmc_claim_host(mq->card->host); err = mmc_wait_for_cmd(mq->card->host, &cmd, 5); mmc_release_host(mq->card->host); if (err) { printk(KERN_ERR "%s: failed to get status (%d)\n", __func__, err); msleep(5); retries--; continue; } printk(KERN_DEBUG "%s: status 0x%.8x\n", __func__, cmd.resp[0]); } while (retries && (!(cmd.resp[0] & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(cmd.resp[0]) == 7))); mq->check_status = 0; } #endif //ruanmeisi_20100529 #ifdef CONFIG_MMC_PERF_PROFILING bytes_xfer = blk_rq_bytes(req); if (rq_data_dir(req) == READ) { start = ktime_get(); issue_ret = mq->issue_fn(mq, req); diff = ktime_sub(ktime_get(), start); host->perf.rbytes_mmcq += bytes_xfer; host->perf.rtime_mmcq = ktime_add(host->perf.rtime_mmcq, diff); } else { start = ktime_get(); issue_ret = mq->issue_fn(mq, req); diff = ktime_sub(ktime_get(), start); host->perf.wbytes_mmcq += bytes_xfer; host->perf.wtime_mmcq = ktime_add(host->perf.wtime_mmcq, diff); } #else issue_ret = mq->issue_fn(mq, req); #endif //ruanmeisi if (0 == issue_ret) { int err; mmc_claim_host(mq->card->host); err = mmc_send_status(mq->card, NULL); mmc_release_host(mq->card->host); if (err) { printk(KERN_ERR "rms:%s: failed to get status (%d) maybe the card is removed\n", __func__, err); //sdcard is removed? mmc_detect_change(mq->card->host, 0); msleep(500); //set_current_state(TASK_INTERRUPTIBLE); //schedule_timeout(HZ / 2); continue; } } } while (1); up(&mq->thread_sem); return 0; }
/** * __mmc_switch - modify EXT_CSD register * @card: the MMC card associated with the data transfer * @set: cmd set values * @index: EXT_CSD register index * @value: value to program into EXT_CSD register * @timeout_ms: timeout (ms) for operation performed by register write, * timeout of zero implies maximum possible timeout * @use_busy_signal: use the busy signal as response type * * Modifies the EXT_CSD register for selected card. */ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, bool use_busy_signal) { int err; struct mmc_command cmd = {0}; unsigned int ignore; unsigned long timeout; u32 status; int retry_times = 3; BUG_ON(!card); BUG_ON(!card->host); cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; cmd.flags = MMC_CMD_AC; if (use_busy_signal) cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; else cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; cmd.cmd_timeout_ms = timeout_ms; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) return err; /* No need to check card status in case of unblocking command */ if (!use_busy_signal) return 0; retry: /* Must check status to be sure of no errors */ timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); ignore = (index == EXT_CSD_HS_TIMING) ? MMC_RSP_CRC : 0; do { err = mmc_send_status(card, &status, ignore); if (err) return err; if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; /* Timeout if the device never leaves the program state. */ if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", mmc_hostname(card->host), __func__); return -ETIMEDOUT; } } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(card->host)) { if (status & R1_SPI_ILLEGAL_COMMAND) return -EBADMSG; } else { if (status & 0xFDFFA000) pr_warning("%s: unexpected status %#x after " "switch", mmc_hostname(card->host), status); if (status & R1_SWITCH_ERROR) { /* Means Set HS200 bit and Driver Strength value in the HS_TIMING [185]*/ if (((value & 0x3) == MMC_HS_TIMING_HS200) && retry_times) { mmc_set_timing(card->host, MMC_TIMING_MMC_HS200); retry_times--; pr_err("%s: Card set to HS200 status read should be retry! \n", mmc_hostname(card->host)); goto retry; } return -EBADMSG; } } return 0; }
int mmc_write_prot_off_part(const char *part) { int rc, fd; struct mmc_part_info info; char device[PATH_MAX], *test; __u8 extcsd[512]; __u32 rca, wp_grp_sz; uint32_t i, wpst, wped; bool is_sharp = is_sharp_dev(); rc = mmc_part_info_get(part, &info); if (rc < 0) return -1; strncpy(device, info.path, sizeof(device)); test = strstr(device, "mmcblk"); if (test) { test += 6; while (isdigit(*test)) test++; *test = '\0'; } fd = open(device, O_RDWR); if (fd < 0) return -1; rc = mmc_read_extcsd(fd, extcsd); if (rc) { close(fd); return -1; } rc = mmc_get_wp_grp_sz(extcsd, &wp_grp_sz); if (rc) { close(fd); return -1; } wpst = info.offset & ~(wp_grp_sz - 1); wped = (info.offset + info.size) & ~(wp_grp_sz - 1); rc = mmc_send_rca(fd, &rca); if (rc) { // close(fd); // return -1; rca = 0x0001; } rc = mmc_set_class_6_ctrl(fd, 0); if (rc) { close(fd); return -1; } for (i = wpst; i < wped; i += wp_grp_sz) { __u32 status = 0; if (is_sharp && mmc_sh_pre_clr_wp(fd)) { // printf("failed mmc_sh_pre_clr_wp, %d\n", i); break; } // clear 1 group rc = mmc_clr_write_prot(fd, i); if (rc) { // printf("failed mmc_clr_write_prot, %d\n", i); break; } rc = mmc_send_status(fd, rca, &status); if (rc) { // printf("failed mmc_send_status, %d\n", i); break; } } close(fd); return i == wped ? 0 : -1; }
int mmc_write_prot_off() { int rc, fd; char device[PATH_MAX]; __u8 extcsd[512]; __u32 rca, card_sz, wp_grp_sz; __u32 i, loop; bool is_sharp = is_sharp_dev(); rc = mmc_get_dev("system", device); if (rc) return -1; fd = open(device, O_RDWR); if (fd < 0) return -1; rc = mmc_read_extcsd(fd, extcsd); if (rc) { close(fd); return -1; } rc = mmc_get_card_sz(extcsd, &card_sz); if (rc) { close(fd); return -1; } rc = mmc_get_wp_grp_sz(extcsd, &wp_grp_sz); if (rc) { close(fd); return -1; } rc = mmc_send_rca(fd, &rca); if (rc) { // close(fd); // return -1; rca = 0x0001; } rc = mmc_set_class_6_ctrl(fd, 0); if (rc) { close(fd); return -1; } loop = card_sz / wp_grp_sz; for (i = 0; i < loop; i++) { __u32 status = 0; if (is_sharp && mmc_sh_pre_clr_wp(fd)) { // printf("failed mmc_sh_pre_clr_wp, %d\n", i * wp_grp_sz); break; } // clear 1 group rc = mmc_clr_write_prot(fd, i * wp_grp_sz); if (rc) { // printf("failed mmc_clr_write_prot, %d\n", i * wp_grp_sz); break; } rc = mmc_send_status(fd, rca, &status); if (rc) { // printf("failed mmc_send_status, %d\n", i * wp_grp_sz); break; } } close(fd); return i == loop ? 0 : -1; }
/* * Handle the detection and initialisation of a card. * * In the case of a resume, "oldcard" will contain the card * we're trying to reinitialise. */ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; int err, count; u32 cid[4]; unsigned int max_dtr; u32 status; BUG_ON(!host); WARN_ON(!host->claimed); /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle * state. We wait 1ms to give cards time to * respond. */ mmc_go_idle(host); /* The extra bit indicates that we support high capacity */ err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); if (err) goto err; /* * For SPI, enable CRC as appropriate. */ if (mmc_host_is_spi(host)) { err = mmc_spi_set_crc(host, use_spi_crc); if (err) goto err; } /* * Fetch CID from card. */ if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else err = mmc_all_send_cid(host, cid); if (err) goto err; if (oldcard) { if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { err = -ENOENT; goto err; } card = oldcard; } else { /* * Allocate card structure. */ card = mmc_alloc_card(host, &mmc_type); if (IS_ERR(card)) { err = PTR_ERR(card); goto err; } card->type = MMC_TYPE_MMC; card->rca = 1; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } /* * For native busses: set card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { err = mmc_set_relative_addr(card); if (err) goto free_card; mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } if (!oldcard) { /* * Fetch CSD from card. */ err = mmc_send_csd(card, card->raw_csd); if (err) goto free_card; err = mmc_decode_csd(card); if (err) goto free_card; err = mmc_decode_cid(card); if (err) goto free_card; } /* * Select card, as all following commands rely on that. */ if (!mmc_host_is_spi(host)) { err = mmc_select_card(card); if (err) goto free_card; } if (!oldcard) { /* * Fetch and process extended CSD. */ err = mmc_read_ext_csd(card); if (err) goto free_card; } /* * Activate high speed (if supported) */ if ((card->ext_csd.hs_max_dtr != 0) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if (err) goto free_card; mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_MMC_HS); } count = 0; do { err = mmc_send_status(card, &status); if (err) goto free_card; if (status & R1_READY_FOR_DATA) break; else mdelay(1); } while (++count < WAIT_CMD6_MAX); /* * Compute bus speed. */ max_dtr = (unsigned int)-1; if (mmc_card_highspeed(card)) { if (max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; } else if (max_dtr > card->csd.max_dtr) { max_dtr = card->csd.max_dtr; } mmc_set_clock(host, max_dtr); /* * Activate wide bus (if supported). */ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { unsigned ext_csd_bit, bus_width; if (host->caps & MMC_CAP_8_BIT_DATA) { ext_csd_bit = EXT_CSD_BUS_WIDTH_8; bus_width = MMC_BUS_WIDTH_8; } else { ext_csd_bit = EXT_CSD_BUS_WIDTH_4; bus_width = MMC_BUS_WIDTH_4; } err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bit); if (err) goto free_card; mmc_set_bus_width(card->host, bus_width); count = 0; do { err = mmc_send_status(card, &status); if (err) goto free_card; if (status & R1_READY_FOR_DATA) break; else mdelay(1); } while (++count < WAIT_CMD6_MAX); } if (!oldcard) host->card = card; return 0; free_card: if (!oldcard) mmc_remove_card(card); err: return err; }
int mmc_test_mem_card(struct mmc_test_config *cfg) { int id, count, forever; int ret, chk_result, tid = 0, result = 0; unsigned int chunks, chunk_blks, left_blks, pass = 0, fail = 0; unsigned int total_blks; unsigned int i, j; unsigned int blksz; unsigned int clkhz; char pattern = 0; char *buf; unsigned long blknr; struct mmc_host *host; struct mmc_card *card; id = cfg->id; count = cfg->count; buf = cfg->buf; blknr = cfg->blknr; blksz = cfg->blksz; chk_result = cfg->chk_result; chunk_blks = cfg->chunk_blks; total_blks = (cfg->total_size + blksz - 1) / blksz; forever = (count == -1) ? 1 : 0; host = mmc_get_host(id); card = mmc_get_card(id); while (forever || count--) { printf("[TST] ==============================================\n"); printf("[TST] BEGIN: %d/%d, No Stop(%d)\n", (cfg->count != -1) ? cfg->count - count : 0, (cfg->count != -1) ? cfg->count : 0, forever); printf("[TST] ----------------------------------------------\n"); printf("[TST] Mode : %d\n", cfg->mode); printf("[TST] Clock : %d kHz\n", cfg->clock / 1000); printf("[TST] BusWidth: %d bits\n", cfg->buswidth); printf("[TST] BurstSz : %d bytes\n", 0x1 << cfg->burstsz); printf("[TST] BlkAddr : %xh\n", blknr); printf("[TST] BlkSize : %dbytes\n", blksz); printf("[TST] TstBlks : %d\n", total_blks); #if defined(BB_MT6575) printf("[TST] AutoCMD : 12(%d), 23(%d)\n", (cfg->autocmd & MSDC_AUTOCMD12) ? 1 : 0, (cfg->autocmd & MSDC_AUTOCMD23) ? 1 : 0); #endif printf("[TST] ----------------------------------------------\n"); if (mmc_init_host(host, id) != 0) { result = -__LINE__; goto failure; } if (mmc_init_card(host, card) != 0) { result = -__LINE__; goto failure; } #if defined(BB_MT6575) msdc_set_dma(host, (u8)cfg->burstsz, (u32)cfg->flags); msdc_set_autocmd(host, cfg->autocmd, 1); #endif /* change uhs-1 mode */ #if 0 if (mmc_card_uhs1(card)) { if (mmc_switch_uhs1(host, card, cfg->uhsmode) != 0) { result = -__LINE__; goto failure; } } #endif /* change clock */ if (cfg->clock) { clkhz = card->maxhz < cfg->clock ? card->maxhz : cfg->clock; mmc_set_clock(host, mmc_card_ddr(card), clkhz); } if (mmc_card_sd(card) && cfg->buswidth == HOST_BUS_WIDTH_8) { printf("[TST] SD card doesn't support 8-bit bus width (SKIP)\n"); result = MMC_ERR_NONE; } if (mmc_set_bus_width(host, card, cfg->buswidth) != 0) { result = -__LINE__; goto failure; } /* cmd16 is illegal while card is in ddr mode */ if (!(mmc_card_mmc(card) && mmc_card_ddr(card))) { if (mmc_set_blk_length(host, blksz) != 0) { result = -__LINE__; goto failure; } } #if defined(BB_MT6575) if (cfg->piobits) { printf("[TST] PIO bits: %d\n", cfg->piobits); msdc_set_pio_bits(host, cfg->piobits); } #endif tid = result = 0; if (mmc_erase_start(card, blknr * blksz) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (mmc_erase_end(card, (blknr + total_blks) * blksz) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (mmc_erase(card, MMC_ERASE_NORMAL) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } printf("[TST] 0x%x - 0x%x Erased\n", blknr * blksz, (blknr + total_blks) * blksz); mmc_send_status(host, card, &status); if (cfg->tst_single) { /* single block write */ for (i = 0; i < total_blks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz); ret = mmc_block_write(id, blknr + i, 1, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("test single block write failed (%d)\n", i); result = -__LINE__; goto failure; } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test single block write\n"); if (result) break; /* single block read */ for (i = 0; i < total_blks && !result; i++) { pattern = (i + count) % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + i, 1, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < blksz; j++) { if (buf[j] != pattern) { result = -__LINE__; goto failure; } } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test single block read\n"); if (result) { printf("[SD%d]\t\tread back pattern(0x%.2x) failed\n", id, pattern); goto failure; } } mmc_send_status(host, card, &status); if (cfg->tst_multiple) { /* multiple block write */ chunks = total_blks / chunk_blks; left_blks = total_blks % chunk_blks; for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz * chunk_blks); ret = mmc_block_write(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } } if (!result && left_blks) { pattern = (i + count) % 256; memset(buf, pattern, blksz * left_blks); ret = mmc_block_write(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block write\n"); if (result) goto failure; /* multiple block read */ for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("[SD%d]\t\tread %d blks failed(ret = %d blks)\n", host->id, chunk_blks, ret); result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < chunk_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; printf("[SD%d]\t\t%xh = %x (!= %x)\n", host->id, blknr + i * chunk_blks + j, buf[j], pattern); goto failure; } } } if (!result && left_blks) { pattern = i % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("[SD%d]\t\tread %d blks failed(ret = %d blks)\n", host->id, left_blks, ret); result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < left_blks * blksz; j++) { if (buf[j] == pattern) continue; printf("[SD%d]\t\t%xh = %x (!= %x)\n", host->id, blknr + chunks * chunk_blks + j, buf[j], pattern); result = -__LINE__; goto failure; } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block read\n"); if (result) goto failure; } mmc_send_status(host, card, &status); if (cfg->tst_interleave) { /* multiple block write */ chunks = total_blks / chunk_blks; left_blks = total_blks % chunk_blks; for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz * chunk_blks); ret = mmc_block_write(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz * chunk_blks); ret = mmc_block_read(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < chunk_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; goto failure; } } } if (!result && left_blks) { pattern = (i + count) % 256; memset(buf, pattern, blksz * left_blks); ret = mmc_block_write(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz * left_blks); ret = mmc_block_read(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; break; } if (chk_result) { for (j = 0; j < left_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; goto failure; } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block interleave write-read\n"); if (result) goto failure; } if (cfg->desc) { printf("[TST] ----------------------------------------------\n"); printf("[TST] Report - %s \n", cfg->desc); printf("[TST] ----------------------------------------------\n"); } mmc_prof_dump(id); failure: if (result) { printf("[SD%d] mmc test failed (%d)\n", host->id, result); fail++; } else { pass++; } printf("[TST] ----------------------------------------------\n"); printf("[TST] Test Result: TOTAL(%d/%d), PASS(%d), FAIL(%d) \n", cfg->count - count, cfg->count, pass, fail); printf("[TST] ----------------------------------------------\n"); //mdelay(1000); } return result; }
// extern struct partition partitions[]; static ssize_t mmc_wr_prot_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct mmc_card *card = filp->private_data; unsigned int wp_group_size; unsigned int set_clear_wp, status; int ret, i; unsigned int addr = 0; unsigned int init_addr = 0; unsigned int loop_count = 0; unsigned int size = 0; char *cmd_buffer; // struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct emmc_partition *p_emmc_partition; pr_info("[HW]: eMMC protect driver built on %s @ %s\n", __DATE__, __TIME__); if( ubuf == NULL){ pr_info("[HW]:%s: NULL pointer \n", __func__); return -1; } cmd_buffer = kmalloc(sizeof(char)*cnt, GFP_KERNEL); if (cmd_buffer == NULL) { return -ENOMEM; } memset(cmd_buffer, 0, sizeof(char)*cnt); if(copy_from_user(cmd_buffer, ubuf, cnt)){ kfree(cmd_buffer); return -EFAULT; } pr_info("[HW]:%s: input arg = %s, cnt = %d \n", __func__, cmd_buffer, cnt); if(strncmp(cmd_buffer, "disable_prot", strlen("disable_prot")) == 0){ set_clear_wp = 0; }else if(strncmp(cmd_buffer, "enable_prot", strlen("enable_prot")) == 0){ set_clear_wp = 1; } else{ kfree(cmd_buffer); return -1; } // mrq.cmd = &cmd; // mrq.data = &data; wp_group_size =(512 * 1024) * card->ext_csd.raw_hc_erase_gap_size * card->ext_csd.raw_hc_erase_grp_size / 512; p_emmc_partition = g_emmc_partition; for(i = 0; i < MAX_EMMC_PARTITION_NUM; i++){ if(p_emmc_partition->flags == 0) break; if(strcmp(p_emmc_partition->name, "system") == 0){ addr = (unsigned int)(p_emmc_partition->start); size = (unsigned int)(p_emmc_partition->size_sectors); pr_info("[HW]:%s: partitionname = %s \n", __func__, p_emmc_partition->name); pr_info("[HW]:%s: partition start from = 0x%08x \n", __func__, addr); pr_info("[HW]:%s: partition size = 0x%08x \n", __func__, size); break; } p_emmc_partition++; } if(strcmp(p_emmc_partition->name, "") == 0){ pr_info("[HW]:%s: can not find partition system \n", __func__); kfree(cmd_buffer); return -1; } pr_info("[HW]:%s: card->ext_csd.raw_hc_erase_gap_size = 0x%02x, card->ext_csd.raw_hc_erase_grp_size = 0x%02x \n", __func__, \ card->ext_csd.raw_hc_erase_gap_size, card->ext_csd.raw_hc_erase_grp_size); pr_info("[HW]:%s, size = 0x%08x, wp_group_size = 0x%08x, unit is block \n", \ __func__, size, wp_group_size); if (wp_group_size == 0) { pr_info("[HW]:%s:invalid wp_group_size=0x%08x.", __func__, wp_group_size); kfree(cmd_buffer); return -2; } init_addr = addr; if(addr % wp_group_size == 0){ }else{ addr = (addr / wp_group_size) * wp_group_size + wp_group_size; pr_info("[HW]:%s: setting start area is not muti size of wp_group_size\n", __func__); } loop_count = (init_addr + size - addr) / wp_group_size; pr_info("[HW]:%s:prot_start_sec_addr = 0x%08x \n", __func__, addr); pr_info("[HW]:%s:loop_count = %x \n", __func__, loop_count); cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; if (set_clear_wp){ cmd.opcode = MMC_SET_WRITE_PROT; }else{ cmd.opcode = MMC_CLR_WRITE_PROT; } for (i = 0; i < loop_count; i++) { /* Sending CMD28 for each WP group size address is in sectors already */ cmd.arg = addr + (i * wp_group_size); pr_info("[HW:%s:loop_count = %d, cmd.arg = 0x%08x, cmd.opcode = %d, \n", __func__, i, cmd.arg, cmd.opcode); mmc_claim_host(card->host); ret = mmc_wait_for_cmd(card->host, &cmd, 3); mmc_release_host(card->host); if (ret) { pr_err("[HW]:%s:mmc_wait_for_cmd return err = %d \n", __func__, ret); kfree(cmd_buffer); return -3; } /* Sending CMD13 to check card status */ do { mmc_claim_host(card->host); ret = mmc_send_status(card, &status); mmc_release_host(card->host); if (R1_CURRENT_STATE(status) == R1_STATE_TRAN) break; }while ((!ret) && (R1_CURRENT_STATE(status) == R1_STATE_PRG)); if (ret) { pr_err("[HW]:%s: mmc_send_status return err = %d \n", __func__, ret); kfree(cmd_buffer); return -4; } } pr_info("[HW]: %s: end sector = 0x%08x \n", __func__, size + init_addr); pr_info("[HW]: %s: size = 0x%08x \n", __func__, size); kfree(cmd_buffer); return size; }