📄 mmc.c
字号:
/* * Allocate a new MMC card, and assign a unique RCA. */static struct mmc_card *mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, u8 sd, unsigned int *frca){ struct mmc_card *card, *c; unsigned int rca = *frca; card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); if (!card) return ERR_PTR(-ENOMEM); mmc_init_card(card, host); memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); card->sd = sd; again: list_for_each_entry(c, &host->cards, node) if (c->rca == rca) { rca++; goto again; } 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; struct mmc_card *card; cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE; mmc_wait_for_cmd(host, &cmd, 0); //MMC_GO_IDLE_STATE resets all cards to bus width 1 list_for_each_entry(card, &host->cards, node) { card->bus_width=1; } mmc_delay(100);}int sd_set_bus_width(struct mmc_host *host, u16 rca, u32 bus_width){ struct mmc_command cmd; int err; cmd.opcode = MMC_ACMD_SD_SET_BUS_WIDTH; cmd.flags = MMC_RSP_R1; switch(bus_width) { case 1: cmd.arg = 00; break; case 4: cmd.arg = 10; break; default: return -EINVAL; } err = mmc_wait_for_acmd(host, rca, &cmd, 3); if(err == MMC_ERR_TIMEOUT) { printk(KERN_ERR "MMC: sd_set_bus_width timed out.\n"); } else if(err == MMC_ERR_BADCRC) { printk(KERN_ERR "MMC: sd_set_bus_width yielded crc error.\n"); } else { DBG("MMC: sd_app_op_cond done.\n"); } return err;}EXPORT_SYMBOL(sd_set_bus_width);static int sd_app_op_cond(struct mmc_host *host, u16 rca, u32 parameter, u32 *response){ struct mmc_command cmd; int err; int retries = 10; cmd.opcode = MMC_ACMD_SD_APP_OP_COND; cmd.arg = parameter; cmd.flags = MMC_RSP_SHORT; DBG("MMC: sd_app_op_cond to %08x\n",parameter); while(retries--) { err = mmc_wait_for_acmd(host, 0, &cmd, 0); if(0 == (cmd.resp[0] & MMC_CARD_BUSY)) { err=MMC_ERR_BUSY; printk(KERN_ERR "MMC: sd_app_op_cond: at least one card is busy - trying again.\n"); mmc_delay(10); //mmc_delay(20); continue; } if(err == MMC_ERR_NONE) { if(response)*response = cmd.resp[0]; break; } } if(err == MMC_ERR_TIMEOUT) { printk(KERN_WARNING "MMC: sd_app_op_cond timed out. Probably no SD-Card here.\n"); } else if(err == MMC_ERR_BUSY) { printk(KERN_ERR "MMC: sd_app_op_cond locked busy. Probably have broken SD-Card.\n"); } else { DBG("MMC: sd_app_op_cond done. Results are: 0x%08x.\n",cmd.resp[0]); } return err;}/* * 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); //mmc_delay(5); host->ios.clock = host->f_min; host->ios.power_mode = MMC_POWER_ON; host->ops->set_ios(host, &host->ios); mmc_delay(2); //mmc_delay(5);}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, u8 sd){ 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_LONG; //HACK! CRC currently not implemented 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, sd, &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); //SD-Cards choose their adresses themselfes (yuck!) if(card->sd) card->rca = (cmd.resp[0] >> 16); }}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_LONG; //HACK: No CRC check as s3c-Core is broken 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){ mmc_power_up(host); mmc_idle_cards(host); // Wake and discover SD-Cards sd_app_op_cond(host, 0, host->ocr_avail, NULL); mmc_discover_cards(host, 1); // Wake and discover MMC-Cards mmc_send_op_cond(host, host->ocr_avail, NULL); mmc_discover_cards(host, 0); /* * 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_delay(500); // ghcstop fix: 20051226, 构 弊繁措肺 肋 登匙.... 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. */ 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){ int ret; ret = mmc_add_host_sysfs(host); if (ret == 0) { mmc_power_off(host); mmc_detect_change(host); } 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_detect_change(host); return 0;}EXPORT_SYMBOL(mmc_resume_host);#endifMODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -