static int omap_mmc_remove(struct platform_device *pdev) { struct mmc_omap_host *host = platform_get_drvdata(pdev); struct resource *res; if (host) { omap_hsmmc_enable_clks(host); mmc_remove_host(host->mmc); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); free_irq(host->irq, host); if (mmc_slot(host).card_detect_irq) free_irq(mmc_slot(host).card_detect_irq, host); flush_scheduled_work(); omap_hsmmc_disable_clks(host); clk_put(host->fclk); clk_put(host->iclk); if (cpu_is_omap2430()) clk_put(host->dbclk); mmc_free_host(host->mmc); iounmap(host->base); } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) release_mem_region(res->start, res->end - res->start + 1); platform_set_drvdata(pdev, NULL); return 0; }
/* * Switch MMC interface voltage ... only relevant for MMC1. * * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver. * The MMC2 transceiver controls are used instead of DAT4..DAT7. * Some chips, like eMMC ones, use internal transceivers. */ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) { u32 reg_val = 0; int ret; if (host->id != OMAP_MMC1_DEVID) return 0; /* Disable the clocks */ omap_hsmmc_disable_clks(host); /* Turn the power off */ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); if (ret != 0) goto err; /* Turn the power ON with given VDD 1.8 or 3.0v */ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); if (ret != 0) goto err; omap_hsmmc_enable_clks(host); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); reg_val = OMAP_HSMMC_READ(host->base, HCTL); /* * If a MMC dual voltage card is detected, the set_ios fn calls * this fn with VDD bit set for 1.8V. Upon card removal from the * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. * * Cope with a bit of slop in the range ... per data sheets: * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, * but recommended values are 1.71V to 1.89V * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max, * but recommended values are 2.7V to 3.3V * * Board setup code shouldn't permit anything very out-of-range. * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the * middle range) but VSIM can't power DAT4..DAT7 at more than 3V. */ if ((1 << vdd) <= MMC_VDD_23_24) reg_val |= SDVS18; else reg_val |= SDVS30; OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); return 0; err: dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); return ret; }
/* * Request function. for read/write operation */ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req) { struct mmc_omap_host *host = mmc_priv(mmc); if (!in_interrupt()) spin_lock_irqsave(&host->irq_lock, host->flags); WARN_ON(host->mrq != NULL); host->mrq = req; del_timer_sync(&host->inact_timer); omap_hsmmc_enable_clks(host); mmc_omap_prepare_data(host, req); mmc_omap_start_command(host, req->cmd, req->data); }
static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) { int ret = 0; struct mmc_omap_host *host = platform_get_drvdata(pdev); if (host && host->suspended) return 0; if (host) { ret = mmc_suspend_host(host->mmc, state); if (ret == 0) { host->suspended = 1; omap_hsmmc_enable_clks(host); OMAP_HSMMC_WRITE(host->base, ISE, 0); OMAP_HSMMC_WRITE(host->base, IE, 0); if (host->pdata->suspend) { ret = host->pdata->suspend(&pdev->dev, host->slot_id); if (ret) dev_dbg(mmc_dev(host->mmc), "Unable to handle MMC board" " level suspend\n"); } if (host->id == OMAP_MMC1_DEVID && !(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS30); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); } omap_hsmmc_disable_clks(host); } } return ret; }
/* * Work Item to notify the core about card insertion/removal */ static void mmc_omap_detect(struct work_struct *work) { struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, mmc_carddetect_work); struct omap_mmc_slot_data *slot = &mmc_slot(host); if (host->suspended) return; omap_hsmmc_enable_clks(host); host->carddetect = slot->card_detect(slot->card_detect_irq); sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (host->carddetect) { mmc_detect_change(host->mmc, (HZ * 200) / 1000); } else { mmc_detect_change(host->mmc, (HZ * 50) / 1000); } }
/* * Request function. for read/write operation */ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req) { struct mmc_omap_host *host = mmc_priv(mmc); WARN_ON(host->mrq != NULL); host->mrq = req; if (host->shutdown) { return; } if (host->inactive) if (host->pdata->set_vdd1_opp) host->pdata->set_vdd1_opp(host->dev, host->max_vdd1_opp); del_timer_sync(&host->inact_timer); host->inactive = 0; omap_hsmmc_enable_clks(host); mmc_omap_prepare_data(host, req); mmc_omap_start_command(host, req->cmd, req->data); }
/* Routine to configure clock values. Exposed API to core */ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_omap_host *host = mmc_priv(mmc); u16 dsor = 0; unsigned long regval; unsigned long timeout; u32 con; del_timer_sync(&host->inact_timer); omap_hsmmc_enable_clks(host); switch (ios->power_mode) { case MMC_POWER_OFF: mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); /* * Reset interface voltage to 3V if it's 1.8V now; * only relevant on MMC-1, the others always use 1.8V. * * REVISIT: If we are able to detect cards after unplugging * a 1.8V card, this code should not be needed. */ if (host->id != OMAP_MMC1_DEVID) break; if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { int vdd = fls(host->mmc->ocr_avail) - 1; if (omap_mmc_switch_opcond(host, vdd) != 0) host->mmc->ios.vdd = vdd; } break; case MMC_POWER_UP: mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); break; } switch (mmc->ios.bus_width) { case MMC_BUS_WIDTH_8: OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) | DW8); break; case MMC_BUS_WIDTH_4: OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) & ~DW8); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); break; case MMC_BUS_WIDTH_1: OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) & ~DW8); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); break; } if (host->id == OMAP_MMC1_DEVID) { /* Only MMC1 can interface at 3V without some flavor * of external transceiver; but they all handle 1.8V. */ if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && (ios->vdd == DUAL_VOLT_OCR_BIT)) { /* * The mmc_select_voltage fn of the core does * not seem to set the power_mode to * MMC_POWER_UP upon recalculating the voltage. * vdd 1.8v. */ if (omap_mmc_switch_opcond(host, ios->vdd) != 0) dev_dbg(mmc_dev(host->mmc), "Switch operation failed\n"); } } if (ios->clock) { dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; if (dsor < 1) dsor = 1; if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) dsor++; if (dsor > 250) dsor = 250; } omap_mmc_stop_clock(host); regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK); regval = regval | (dsor << 6) | (DTO << 16); OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); /* Wait till the ICS bit is set */ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2 && time_before(jiffies, timeout)) msleep(1); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); if (ios->power_mode == MMC_POWER_ON) send_init_stream(host); con = OMAP_HSMMC_READ(host->base, CON); if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) OMAP_HSMMC_WRITE(host->base, CON, con | OD); else OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); omap_hsmmc_disable_clks(host); }
/* * MMC controller IRQ handler */ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) { struct mmc_omap_host *host = dev_id; struct mmc_data *data; int end_cmd = 0, end_trans = 0, status; omap_hsmmc_enable_clks(host); if (host->cmd == NULL && host->data == NULL) { OMAP_HSMMC_WRITE(host->base, STAT, OMAP_HSMMC_READ(host->base, STAT)); return IRQ_HANDLED; } data = host->data; status = OMAP_HSMMC_READ(host->base, STAT); dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); if (status & ERR) { #ifdef CONFIG_MMC_DEBUG mmc_omap_report_irq(host, status); #endif if ((status & CMD_TIMEOUT) || (status & CMD_CRC) || (status & CMD_CEB) || (status & CMD_CIE)) { if (host->cmd) { if (status & CMD_TIMEOUT) { mmc_omap_reset_controller_fsm(host, SRC); host->cmd->error = -ETIMEDOUT; } else { host->cmd->error = -EILSEQ; } end_cmd = 1; } if (host->data) { mmc_dma_cleanup(host, -ETIMEDOUT); mmc_omap_reset_controller_fsm(host, SRD); } } if ((status & DATA_TIMEOUT) || (status & DATA_CRC) || (status & DATA_DEB)) { if (host->data) { if (status & DATA_TIMEOUT) mmc_dma_cleanup(host, -ETIMEDOUT); else mmc_dma_cleanup(host, -EILSEQ); mmc_omap_reset_controller_fsm(host, SRD); end_trans = 1; } } if (status & CARD_ERR) { dev_dbg(mmc_dev(host->mmc), "Ignoring card err CMD%d\n", host->cmd->opcode); if (host->cmd) end_cmd = 1; if (host->data) end_trans = 1; } } OMAP_HSMMC_WRITE(host->base, STAT, status); if (end_cmd || (status & CC)) mmc_omap_cmd_done(host, host->cmd); if (end_trans || (status & TC)) mmc_omap_xfer_done(host, data); /* Perform one dummy read to ensure the previous write actually went through */ status = OMAP_HSMMC_READ(host->base, STAT); return IRQ_HANDLED; }
static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) { int ret = 0; int err = 0; struct mmc_omap_host *host = platform_get_drvdata(pdev); if (host && host->suspended) return 0; if (host) { host->suspended = 1; if (host->card_sleep){ dev_dbg(mmc_dev(host->mmc),"has been in sleep status\n"); } else if (mmc_card_can_sleep(host->mmc)){ err = mmc_card_sleep(host->mmc); if (err){ dev_dbg(mmc_dev(host->mmc),"MMC sleep command CMD5 return error\n"); } else{ host->card_sleep = 1; } } if (host->pdata->suspend) { ret = host->pdata->suspend(&pdev->dev, host->slot_id); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to handle MMC board" " level suspend\n"); host->suspended = 0; return ret; } } cancel_work_sync(&host->mmc_carddetect_work); ret = mmc_suspend_host(host->mmc, state); if (ret == 0) { omap_hsmmc_enable_clks(host); OMAP_HSMMC_WRITE(host->base, ISE, 0); OMAP_HSMMC_WRITE(host->base, IE, 0); if (host->id == OMAP_MMC1_DEVID && !(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS30); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); } omap_hsmmc_disable_clks(host); } else { host->suspended = 0; if (host->pdata->resume) { ret = host->pdata->resume(&pdev->dev, host->slot_id); if (ret) dev_dbg(mmc_dev(host->mmc), "Unmask interrupt failed\n"); } } } return ret; }
static int __init omap_mmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; struct mmc_omap_host *host = NULL; struct resource *res; int ret = 0, irq; u32 hctl, capa; if (pdata == NULL) { dev_err(&pdev->dev, "Platform Data is missing\n"); return -ENXIO; } if (pdata->nr_slots == 0) { dev_err(&pdev->dev, "No Slots\n"); return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (res == NULL || irq < 0) return -ENXIO; res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (res == NULL) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto err; } host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdata; host->dev = &pdev->dev; host->use_dma = 1; host->dev->dma_mask = &pdata->dma_mask; host->dma_ch = -1; host->irq = irq; host->id = pdev->id; host->slot_id = 0; host->mapbase = res->start; host->base = ioremap(host->mapbase, SZ_4K); host->shutdown = 0; host->max_vdd1_opp = pdata->max_vdd1_opp; host->min_vdd1_opp = pdata->min_vdd1_opp; #ifdef CONFIG_HC_Broken_eMMC_ZOOM2 /* * HACK: * The HC eMMC card on Zoom2 is broken. It reports wrong ext_csd * version. This is a hack to make the eMMC card useble on Zoom2. * Without this hack the MMC core fails to detect the correct size * of the card and hence accesses beyond the detected boundary results * in DATA CRC errors. * Make use of the unused bit to indicate the host controller ID to * the MMC core. */ if (host->id == OMAP_MMC2_DEVID) host->mmc->unused = 1; #endif #ifdef CONFIG_MMC_EMBEDDED_SDIO if (pdata->slots[0].embedded_sdio) mmc_set_embedded_sdio_data(mmc, &pdata->slots[0].embedded_sdio->cis, &pdata->slots[0].embedded_sdio->cccr, pdata->slots[0].embedded_sdio->funcs, pdata->slots[0].embedded_sdio->num_funcs); #endif platform_set_drvdata(pdev, host); INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); INIT_WORK(&host->mmc_opp_set_work, mmc_omap_opp_setup); mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 52000000; sema_init(&host->sem, 1); spin_lock_init(&host->clk_lock); init_timer(&host->inact_timer); host->inact_timer.function = omap_hsmmc_inact_timer; host->inact_timer.data = (unsigned long) host; host->clks_enabled = 0; host->off_counter = 0; host->inactive = 0; host->card_sleep = 0; host->iclk = clk_get(&pdev->dev, "mmchs_ick"); if (IS_ERR(host->iclk)) { ret = PTR_ERR(host->iclk); host->iclk = NULL; goto err1; } host->fclk = clk_get(&pdev->dev, "mmchs_fck"); if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; clk_put(host->iclk); goto err1; } if (cpu_is_omap2430()) { host->dbclk_enabled = 0; host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); } if (omap_hsmmc_enable_clks(host) != 0) { clk_put(host->iclk); clk_put(host->fclk); goto err1; } #ifdef CONFIG_MMC_BLOCK_BOUNCE mmc->max_phys_segs = 1; mmc->max_hw_segs = 1; #endif mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; if (mmc_slot(host).wires >= 8) mmc->caps |= MMC_CAP_8_BIT_DATA; else if (pdata->slots[host->slot_id].wires >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; /* Only MMC1 supports 3.0V */ if (host->id == OMAP_MMC1_DEVID) { hctl = SDVS30; capa = VS30 | VS18; } else { hctl = SDVS18; capa = VS18; } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | hctl); OMAP_HSMMC_WRITE(host->base, CAPA, OMAP_HSMMC_READ(host->base, CAPA) | capa); /* Set the controller to AUTO IDLE mode */ OMAP_HSMMC_WRITE(host->base, SYSCONFIG, OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); /* Set SD bus power bit */ OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); /* Request IRQ for MMC operations */ ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, mmc_hostname(mmc), host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); goto err_irq; } /* initialize power supplies, gpios, etc */ if (pdata->init != NULL) { if (pdata->init(&pdev->dev) != 0) { dev_dbg(mmc_dev(host->mmc), "late init error\n"); goto err_irq_cd_init; } } mmc->ocr_avail = mmc_slot(host).ocr_mask; /* Request IRQ for card detect */ if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) { ret = request_irq(mmc_slot(host).card_detect_irq, omap_mmc_cd_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, mmc_hostname(mmc), host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } } #ifdef CONFIG_MMC_EMBEDDED_SDIO else if (mmc_slot(host).register_status_notify) { mmc_slot(host).register_status_notify(omap_hsmmc_status_notify_cb, host); } #endif OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); mmc_add_host(mmc); if (host->pdata->slots[host->slot_id].name != NULL) { ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); if (ret < 0) goto err_slot_name; } if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect && host->pdata->slots[host->slot_id].get_cover_state) { ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) goto err_cover_switch; } return 0; err_cover_switch: device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); err_slot_name: mmc_remove_host(mmc); err_irq_cd: free_irq(mmc_slot(host).card_detect_irq, host); err_irq_cd_init: free_irq(host->irq, host); err_irq: omap_hsmmc_disable_clks(host); clk_put(host->fclk); clk_put(host->iclk); if (cpu_is_omap2430()) clk_put(host->dbclk); err1: iounmap(host->base); err: dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); release_mem_region(res->start, res->end - res->start + 1); if (host) mmc_free_host(mmc); return ret; }