int msmsdcc_probe(struct platform_device *pdev) { struct mmc_platform_data *plat = pdev->dev.platform_data; struct msmsdcc_host *host; struct mmc_host *mmc; struct resource *irqres = NULL; struct resource *memres = NULL; struct resource *dmares = NULL; int ret; /* must have platform data */ if (!plat) { printk(KERN_ERR "%s: Platform data not available\n", __func__); ret = -EINVAL; goto out; } if (pdev->id < 1 || pdev->id > 4) return -EINVAL; if (pdev->resource == NULL || pdev->num_resources < 2) { printk(KERN_ERR "%s: Invalid resource\n", __func__); return -ENXIO; } memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irqres || !memres) { printk(KERN_ERR "%s: Invalid resource\n", __func__); return -ENXIO; } /* * Setup our host structure */ mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto out; } host = mmc_priv(mmc); host->pdev_id = pdev->id; host->plat = plat; host->mmc = mmc; host->cmdpoll = 1; host->base = ioremap(memres->start, PAGE_SIZE); if (!host->base) { ret = -ENOMEM; goto out; } host->irqres = irqres; host->memres = memres; host->dmares = dmares; spin_lock_init(&host->lock); #ifdef CONFIG_MMC_EMBEDDED_SDIO if (plat->embedded_sdio) mmc_set_embedded_sdio_data(mmc, &plat->embedded_sdio->cis, &plat->embedded_sdio->cccr, plat->embedded_sdio->funcs, plat->embedded_sdio->num_funcs); #endif #ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ INIT_WORK(&host->resume_task, do_resume_work); #endif /* * Setup DMA */ msmsdcc_init_dma(host); /* * Setup main peripheral bus clock */ host->pclk = clk_get(&pdev->dev, "sdc_pclk"); if (IS_ERR(host->pclk)) { ret = PTR_ERR(host->pclk); printk(KERN_ERR "%s: failed to get pclock (%d)\n", __func__, ret); goto host_free; } ret = clk_enable(host->pclk); if (ret) goto pclk_put; host->pclk_rate = clk_get_rate(host->pclk); /* * Setup SDC MMC clock */ host->clk = clk_get(&pdev->dev, "sdc_clk"); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); printk(KERN_ERR "%s: failed to get clock (%d)\n", __func__, ret); goto pclk_disable; } ret = clk_enable(host->clk); if (ret) goto clk_put; ret = clk_set_rate(host->clk, msmsdcc_fmin); if (ret) { printk(KERN_ERR "%s: Clock rate set failed (%d)\n", __func__, ret); goto clk_disable; } host->clk_rate = clk_get_rate(host->clk); host->clks_on = 1; /* * Setup MMC host structure */ mmc->ops = &msmsdcc_ops; mmc->f_min = msmsdcc_fmin; mmc->f_max = msmsdcc_fmax; mmc->ocr_avail = plat->ocr_mask; if (msmsdcc_4bit) mmc->caps |= MMC_CAP_4_BIT_DATA; if (msmsdcc_sdioirq) mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; mmc->max_phys_segs = NR_SG; mmc->max_hw_segs = NR_SG; mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ mmc->max_blk_count = 65536; mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */ mmc->max_seg_size = mmc->max_req_size; writel(0, host->base + MMCIMASK0); writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */ writel(MCI_IRQENABLE, host->base + MMCIMASK0); host->saved_irq0mask = MCI_IRQENABLE; /* * Setup card detect change */ memset(&host->timer, 0, sizeof(host->timer)); if (plat->register_status_notify) { plat->register_status_notify(msmsdcc_status_notify_cb, host); } else if (!plat->status) printk(KERN_ERR "%s: No card detect facilities available\n", mmc_hostname(mmc)); else { init_timer(&host->timer); host->timer.data = (unsigned long)host; host->timer.function = msmsdcc_check_status; host->timer.expires = jiffies + HZ; add_timer(&host->timer); } if (plat->status) { host->oldstat = host->plat->status(mmc_dev(host->mmc)); host->eject = !host->oldstat; } /* * Setup a command timer. We currently need this due to * some 'strange' timeout / error handling situations. */ init_timer(&host->command_timer); host->command_timer.data = (unsigned long) host; host->command_timer.function = msmsdcc_command_expired; ret = request_irq(irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) goto stat_irq_free; ret = request_irq(irqres->end, msmsdcc_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); if (ret) goto cmd_irq_free; mmc_set_drvdata(pdev, mmc); mmc_claim_host(mmc); printk(KERN_INFO "%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", mmc_hostname(mmc), (unsigned long long)memres->start, (unsigned int) irqres->start, (unsigned int) host->stat_irq, host->dma.channel); printk(KERN_INFO "%s: 4 bit data mode %s\n", mmc_hostname(mmc), (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled")); printk(KERN_INFO "%s: MMC clock %u -> %u Hz, PCLK %u Hz\n", mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate); printk(KERN_INFO "%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject); printk(KERN_INFO "%s: Power save feature enable = %d\n", mmc_hostname(mmc), msmsdcc_pwrsave); if (host->dma.channel != -1) { printk(KERN_INFO "%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n", mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr); printk(KERN_INFO "%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n", mmc_hostname(mmc), host->dma.cmd_busaddr, host->dma.cmdptr_busaddr); } else printk(KERN_INFO "%s: PIO transfer enabled\n", mmc_hostname(mmc)); if (host->timer.function) printk(KERN_INFO "%s: Polling status mode enabled\n", mmc_hostname(mmc)); #if defined(CONFIG_DEBUG_FS) msmsdcc_dbg_createhost(host); #endif return 0; cmd_irq_free: free_irq(irqres->start, host); stat_irq_free: if (host->stat_irq) free_irq(host->stat_irq, host); clk_disable: clk_disable(host->clk); clk_put: clk_put(host->clk); pclk_disable: clk_disable(host->pclk); pclk_put: clk_put(host->pclk); host_free: mmc_free_host(mmc); out: return ret; }
static int msmsdcc_probe(struct platform_device *pdev) { struct mmc_platform_data *plat = pdev->dev.platform_data; struct msmsdcc_host *host; struct mmc_host *mmc; struct resource *irqres = NULL; struct resource *memres = NULL; struct resource *dmares = NULL; int ret; int i; /* must have platform data */ if (!plat) { pr_err("%s: Platform data not available\n", __func__); ret = -EINVAL; goto out; } if (pdev->id < 1 || pdev->id > 4) return -EINVAL; if (pdev->resource == NULL || pdev->num_resources < 3) { pr_err("%s: Invalid resource\n", __func__); return -ENXIO; } for (i = 0; i < pdev->num_resources; i++) { if (pdev->resource[i].flags & IORESOURCE_MEM) memres = &pdev->resource[i]; if (pdev->resource[i].flags & IORESOURCE_IRQ) irqres = &pdev->resource[i]; if (pdev->resource[i].flags & IORESOURCE_DMA) dmares = &pdev->resource[i]; } if (!irqres || !memres) { pr_err("%s: Invalid resource\n", __func__); return -ENXIO; } /* * Setup our host structure */ mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto out; } host = mmc_priv(mmc); host->pdev_id = pdev->id; host->plat = plat; host->mmc = mmc; host->curr.cmd = NULL; host->base = ioremap(memres->start, PAGE_SIZE); if (!host->base) { ret = -ENOMEM; goto host_free; } host->irqres = irqres; host->memres = memres; host->dmares = dmares; spin_lock_init(&host->lock); #ifdef CONFIG_MMC_EMBEDDED_SDIO if (plat->embedded_sdio) mmc_set_embedded_sdio_data(mmc, &plat->embedded_sdio->cis, &plat->embedded_sdio->cccr, plat->embedded_sdio->funcs, plat->embedded_sdio->num_funcs); #endif #ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ INIT_WORK(&host->resume_task, do_resume_work); #endif tasklet_init(&host->dma_tlet, msmsdcc_dma_complete_tlet, (unsigned long)host); /* * Setup DMA */ ret = msmsdcc_init_dma(host); if (ret) goto ioremap_free; /* * Setup main peripheral bus clock */ host->pclk = clk_get(&pdev->dev, "sdc_pclk"); if (IS_ERR(host->pclk)) { ret = PTR_ERR(host->pclk); goto dma_free; } ret = clk_enable(host->pclk); if (ret) goto pclk_put; host->pclk_rate = clk_get_rate(host->pclk); /* * Setup SDC MMC clock */ host->clk = clk_get(&pdev->dev, "sdc_clk"); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); goto pclk_disable; } ret = clk_enable(host->clk); if (ret) goto clk_put; ret = clk_set_rate(host->clk, msmsdcc_fmin); if (ret) { pr_err("%s: Clock rate set failed (%d)\n", __func__, ret); goto clk_disable; } host->clk_rate = clk_get_rate(host->clk); host->clks_on = 1; /* * Setup MMC host structure */ mmc->ops = &msmsdcc_ops; mmc->f_min = msmsdcc_fmin; mmc->f_max = msmsdcc_fmax; mmc->ocr_avail = plat->ocr_mask; mmc->caps |= plat->mmc_bus_width; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; #ifdef CONFIG_MMC_MSM_SDIO_SUPPORT mmc->caps |= MMC_CAP_SDIO_IRQ; #endif mmc->max_phys_segs = NR_SG; mmc->max_hw_segs = NR_SG; mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ mmc->max_blk_count = 65536; mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */ mmc->max_seg_size = mmc->max_req_size; writel(0, host->base + MMCIMASK0); writel(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR); /* Delay needed (MMCIMASK0 was just written above) */ msmsdcc_delay(host); writel(MCI_IRQENABLE, host->base + MMCIMASK0); host->mci_irqenable = MCI_IRQENABLE; /* * Setup card detect change */ if (plat->status_irq) { ret = request_irq(plat->status_irq, msmsdcc_platform_status_irq, IRQF_SHARED | plat->irq_flags, DRIVER_NAME " (slot)", host); if (ret) { pr_err("Unable to get slot IRQ %d (%d)\n", plat->status_irq, ret); goto clk_disable; } } else if (plat->register_status_notify) { plat->register_status_notify(msmsdcc_status_notify_cb, host); } else if (!plat->status) pr_err("%s: No card detect facilities available\n", mmc_hostname(mmc)); if (plat->status) { host->oldstat = host->plat->status(mmc_dev(host->mmc)); host->eject = !host->oldstat; } if (host->plat->sdiowakeup_irq) { ret = request_irq(plat->sdiowakeup_irq, msmsdcc_platform_sdiowakeup_irq, IRQF_SHARED | IRQF_TRIGGER_FALLING, DRIVER_NAME "sdiowakeup", host); if (ret) { pr_err("Unable to get sdio wakeup IRQ %d (%d)\n", plat->sdiowakeup_irq, ret); goto platform_irq_free; } else { set_irq_wake(host->plat->sdiowakeup_irq, 1); disable_irq(host->plat->sdiowakeup_irq); } } ret = request_irq(irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) goto sdiowakeup_irq_free; ret = request_irq(irqres->end, msmsdcc_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); if (ret) goto irq_free; mmc_set_drvdata(pdev, mmc); mmc_add_host(mmc); #ifdef CONFIG_HAS_EARLYSUSPEND host->early_suspend.suspend = msmsdcc_early_suspend; host->early_suspend.resume = msmsdcc_late_resume; host->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; register_early_suspend(&host->early_suspend); #endif pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", mmc_hostname(mmc), (unsigned long long)memres->start, (unsigned int) irqres->start, (unsigned int) plat->status_irq, host->dma.channel); pr_info("%s: 8 bit data mode %s\n", mmc_hostname(mmc), (mmc->caps & MMC_CAP_8_BIT_DATA ? "enabled" : "disabled")); pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc), (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled")); pr_info("%s: polling status mode %s\n", mmc_hostname(mmc), (mmc->caps & MMC_CAP_NEEDS_POLL ? "enabled" : "disabled")); pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n", mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate); pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject); pr_info("%s: Power save feature enable = %d\n", mmc_hostname(mmc), msmsdcc_pwrsave); if (host->dma.channel != -1) { pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n", mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr); pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n", mmc_hostname(mmc), host->dma.cmd_busaddr, host->dma.cmdptr_busaddr); } else pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc)); #if defined(CONFIG_DEBUG_FS) msmsdcc_dbg_createhost(host); #endif if (!plat->status_irq) { ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); if (ret) goto irq_free; } return 0; irq_free: free_irq(irqres->start, host); sdiowakeup_irq_free: if (plat->sdiowakeup_irq) { set_irq_wake(host->plat->sdiowakeup_irq, 0); free_irq(plat->sdiowakeup_irq, host); } platform_irq_free: if (plat->status_irq) free_irq(plat->status_irq, host); clk_disable: clk_disable(host->clk); clk_put: clk_put(host->clk); pclk_disable: clk_disable(host->pclk); pclk_put: clk_put(host->pclk); dma_free: dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata), host->dma.nc, host->dma.nc_busaddr); ioremap_free: iounmap(host->base); host_free: mmc_free_host(mmc); out: return ret; }