📄 mmc.c.svn-base
字号:
card->rca = rca; *frca = rca; return card;}/* * Tell attached cards to go to IDLE state */static void mmc_idle_cards(struct mmc_host *host){ struct mmc_command cmd; host->ios.chip_select = MMC_CS_HIGH; host->ops->set_ios(host, &host->ios); mmc_delay(1); cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE; mmc_wait_for_cmd(host, &cmd, 0); mmc_delay(1); host->ios.chip_select = MMC_CS_DONTCARE; host->ops->set_ios(host, &host->ios); mmc_delay(1);}/* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. * We then wait a bit for the power to stabilise. Finally, * enable the bus drivers and clock to the card. * * We must _NOT_ enable the clock prior to power stablising. * * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */static void mmc_power_up(struct mmc_host *host){ int bit = fls(host->ocr_avail) - 1; host->ios.vdd = bit; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios); mmc_delay(1); host->ios.clock = host->f_min; host->ios.power_mode = MMC_POWER_ON; host->ops->set_ios(host, &host->ios); mmc_delay(2);}static void mmc_power_off(struct mmc_host *host){ host->ios.clock = 0; host->ios.vdd = 0; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios);}static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr){ struct mmc_command cmd; int i, err = 0; cmd.opcode = MMC_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3; for (i = 100; i; i--) { err = mmc_wait_for_cmd(host, &cmd, 0); if (err != MMC_ERR_NONE) break; if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) break; err = MMC_ERR_TIMEOUT; mmc_delay(10); } if (rocr) *rocr = cmd.resp[0]; return err;}static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr){ struct mmc_command cmd; int i, err = 0; cmd.opcode = SD_APP_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3; for (i = 100; i; i--) { err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) break; if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) break; err = MMC_ERR_TIMEOUT; mmc_delay(10); } if (rocr) *rocr = cmd.resp[0]; return err;}/* * Discover cards by requesting their CID. If this command * times out, it is not an error; there are no further cards * to be discovered. Add new cards to the list. * * Create a mmc_card entry for each discovered card, assigning * it an RCA, and save the raw CID for decoding later. */static void mmc_discover_cards(struct mmc_host *host){ struct mmc_card *card; unsigned int first_rca = 1, err; while (1) { struct mmc_command cmd; cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err == MMC_ERR_TIMEOUT) { err = MMC_ERR_NONE; break; } if (err != MMC_ERR_NONE) { printk(KERN_ERR "%s: error requesting CID: %d\n", mmc_hostname(host), err); break; } card = mmc_find_card(host, cmd.resp); if (!card) { card = mmc_alloc_card(host, cmd.resp, &first_rca); if (IS_ERR(card)) { err = PTR_ERR(card); break; } list_add(&card->node, &host->cards); } card->state &= ~MMC_STATE_DEAD; if (host->mode == MMC_MODE_SD) { mmc_card_set_sd(card); cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) mmc_card_set_dead(card); else { card->rca = cmd.resp[0] >> 16; if (!host->ops->get_ro) { #if 0 printk(KERN_WARNING "%s: host does not " "support reading read-only " "switch. assuming write-enable.\n", mmc_hostname(host)); #endif } else { if (host->ops->get_ro(host)) mmc_card_set_readonly(card); } } } else { cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) mmc_card_set_dead(card); } }}static void mmc_read_csds(struct mmc_host *host){ struct mmc_card *card; list_for_each_entry(card, &host->cards, node) { struct mmc_command cmd; int err; if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) continue; cmd.opcode = MMC_SEND_CSD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R2; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) { mmc_card_set_dead(card); continue; } memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); mmc_decode_csd(card); mmc_decode_cid(card); }}static void mmc_read_scrs(struct mmc_host *host){ int err; struct mmc_card *card; struct mmc_request mrq; struct mmc_command cmd; struct mmc_data data; struct scatterlist sg; list_for_each_entry(card, &host->cards, node) { if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) continue; if (!mmc_card_sd(card)) continue; err = mmc_select_card(host, card); if (err != MMC_ERR_NONE) { mmc_card_set_dead(card); continue; } memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_APP_CMD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(host, &cmd, 0); if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { mmc_card_set_dead(card); continue; } memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_APP_SEND_SCR; cmd.arg = 0; cmd.flags = MMC_RSP_R1; memset(&data, 0, sizeof(struct mmc_data)); data.timeout_ns = card->csd.tacc_ns * 10; data.timeout_clks = card->csd.tacc_clks * 10; data.blksz_bits = 3; data.blocks = 1; data.flags = MMC_DATA_READ; data.sg = &sg; data.sg_len = 1; memset(&mrq, 0, sizeof(struct mmc_request)); mrq.cmd = &cmd; mrq.data = &data; sg_init_one(&sg, (u8*)card->raw_scr, 8); mmc_wait_for_req(host, &mrq); if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { mmc_card_set_dead(card); continue; } card->raw_scr[0] = ntohl(card->raw_scr[0]); card->raw_scr[1] = ntohl(card->raw_scr[1]); mmc_decode_scr(card); } mmc_deselect_cards(host);}static unsigned int mmc_calculate_clock(struct mmc_host *host){ struct mmc_card *card; unsigned int max_dtr = host->f_max; list_for_each_entry(card, &host->cards, node) if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) max_dtr = card->csd.max_dtr; DBG("MMC: selected %d.%03dMHz transfer rate\n", max_dtr / 1000000, (max_dtr / 1000) % 1000); return max_dtr;}/* * Check whether cards we already know about are still present. * We do this by requesting status, and checking whether a card * responds. * * A request for status does not cause a state change in data * transfer mode. */static void mmc_check_cards(struct mmc_host *host){ struct list_head *l, *n; mmc_deselect_cards(host); list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); struct mmc_command cmd; int err; cmd.opcode = MMC_SEND_STATUS; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err == MMC_ERR_NONE) continue; mmc_card_set_dead(card); }}static void mmc_setup(struct mmc_host *host){ if (host->ios.power_mode != MMC_POWER_ON) { int err; u32 ocr; host->mode = MMC_MODE_SD; mmc_power_up(host); mmc_idle_cards(host); err = mmc_send_app_op_cond(host, 0, &ocr); /* * If we fail to detect any SD cards then try * searching for MMC cards. */ if (err != MMC_ERR_NONE) { host->mode = MMC_MODE_MMC; err = mmc_send_op_cond(host, 0, &ocr); if (err != MMC_ERR_NONE) return; } host->ocr = mmc_select_voltage(host, ocr); /* * 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. */ if (host->ocr) mmc_idle_cards(host); } else { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.clock = host->f_min; host->ops->set_ios(host, &host->ios); /* * We should remember the OCR mask from the existing * cards, and detect the new cards OCR mask, combine * the two and re-select the VDD. However, if we do * change VDD, we should do an idle, and then do a * full re-initialisation. We would need to notify * drivers so that they can re-setup the cards as * well, while keeping their queues at bay. * * For the moment, we take the easy way out - if the * new cards don't like our currently selected VDD, * they drop off the bus. */ } if (host->ocr == 0) return; /* * Send the selected OCR multiple times... until the cards * all get the idea that they should be ready for CMD2. * (My SanDisk card seems to need this.) */ if (host->mode == MMC_MODE_SD) mmc_send_app_op_cond(host, host->ocr, NULL); else mmc_send_op_cond(host, host->ocr, NULL); mmc_discover_cards(host); /* * Ok, now switch to push-pull mode. */ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ops->set_ios(host, &host->ios); mmc_read_csds(host); if (host->mode == MMC_MODE_SD) mmc_read_scrs(host);}/** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. * @delay: optional delay to wait before detection (jiffies) * * All we know is that card(s) have been inserted or removed * from the socket(s). We don't know which socket or cards. */void mmc_detect_change(struct mmc_host *host, unsigned long delay){ if (delay) schedule_delayed_work(&host->detect, delay); else schedule_work(&host->detect);}EXPORT_SYMBOL(mmc_detect_change);static void mmc_rescan(void *data){ struct mmc_host *host = data; struct list_head *l, *n; mmc_claim_host(host); if (host->ios.power_mode == MMC_POWER_ON) mmc_check_cards(host); mmc_setup(host); if (!list_empty(&host->cards)) { /* * (Re-)calculate the fastest clock rate which the * attached cards and the host support. */ host->ios.clock = mmc_calculate_clock(host); host->ops->set_ios(host, &host->ios); } mmc_release_host(host); list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); /* * If this is a new and good card, register it. */ if (!mmc_card_present(card) && !mmc_card_dead(card)) { if (mmc_register_card(card)) mmc_card_set_dead(card); else mmc_card_set_present(card); } /* * If this card is dead, destroy it. */ if (mmc_card_dead(card)) { list_del(&card->node); mmc_remove_card(card); } } /* * If we discover that there are no cards on the * bus, turn off the clock and power down. */ if (list_empty(&host->cards)) mmc_power_off(host);}/** * mmc_alloc_host - initialise the per-host structure. * @extra: sizeof private data structure * @dev: pointer to host device model structure * * Initialise the per-host structure. */struct mmc_host *mmc_alloc_host(int extra, struct device *dev){ struct mmc_host *host; host = mmc_alloc_host_sysfs(extra, dev); if (host) { spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_LIST_HEAD(&host->cards); INIT_WORK(&host->detect, mmc_rescan, host); /* * By default, hosts do not support SGIO or large requests. * They have to set these according to their abilities. */ #ifdef CONFIG_MMC_JADE host->max_hw_segs = 16; host->max_phys_segs = 16; host->max_sectors =64; host->max_seg_size = PAGE_CACHE_SIZE; #else host->max_hw_segs = 1; host->max_phys_segs = 1; host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9); host->max_seg_size = PAGE_CACHE_SIZE; #endif } return host;}EXPORT_SYMBOL(mmc_alloc_host);/** * mmc_add_host - initialise host hardware * @host: mmc host */int mmc_add_host(struct mmc_host *host){ int ret; ret = mmc_add_host_sysfs(host); if (ret == 0) { mmc_power_off(host); mmc_detect_change(host, 0); } return ret;}EXPORT_SYMBOL(mmc_add_host);/** * mmc_remove_host - remove host hardware * @host: mmc host * * Unregister and remove all cards associated with this host, * and power down the MMC bus. */void mmc_remove_host(struct mmc_host *host){ struct list_head *l, *n; list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); mmc_remove_card(card); } mmc_power_off(host); mmc_remove_host_sysfs(host);}EXPORT_SYMBOL(mmc_remove_host);/** * mmc_free_host - free the host structure * @host: mmc host * * Free the host once all references to it have been dropped. */void mmc_free_host(struct mmc_host *host){ flush_scheduled_work(); mmc_free_host_sysfs(host);}EXPORT_SYMBOL(mmc_free_host);#ifdef CONFIG_PM/** * mmc_suspend_host - suspend a host * @host: mmc host * @state: suspend mode (PM_SUSPEND_xxx) */int mmc_suspend_host(struct mmc_host *host, pm_message_t state){ mmc_claim_host(host); mmc_deselect_cards(host); mmc_power_off(host); mmc_release_host(host); return 0;}EXPORT_SYMBOL(mmc_suspend_host);/** * mmc_resume_host - resume a previously suspended host * @host: mmc host */int mmc_resume_host(struct mmc_host *host){ mmc_rescan(host); return 0;}EXPORT_SYMBOL(mmc_resume_host);#endifMODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -