📄 mmc.c
字号:
cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE; mmc_wait_for_cmd(host, &cmd, 0); mmc_delay(1);}/* * Apply power to the MMC stack. */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.power_mode = MMC_POWER_UP; 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.power_mode = MMC_POWER_OFF; 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;}/* * 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", host->host_name, 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; 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 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; mmc_power_up(host); mmc_idle_cards(host); 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.) */ 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);}/** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. * * 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){ 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 = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (host) { memset(host, 0, sizeof(struct mmc_host) + extra); spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_LIST_HEAD(&host->cards); INIT_WORK(&host->detect, mmc_rescan, host); host->dev = dev; /* * By default, hosts do not support SGIO or large requests. * They have to set these according to their abilities. */ 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; } return host;}EXPORT_SYMBOL(mmc_alloc_host);/** * mmc_add_host - initialise host hardware * @host: mmc host */int mmc_add_host(struct mmc_host *host){ static unsigned int host_num; snprintf(host->host_name, sizeof(host->host_name), "mmc%d", host_num++); mmc_power_off(host); mmc_detect_change(host); return 0;}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);}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(); kfree(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, u32 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_detect_change(host); return 0;}EXPORT_SYMBOL(mmc_resume_host);#endifMODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -