/* * Software emulation of the SD card insertion/removal. Set insert=1 for insert * and insert=0 for removal. The card detection is done by GPIO. For Broadcom * IP to function properly the bit 0 of CORESTAT register needs to be set/reset * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet. */ static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert) { struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv); u32 val; /* * Back-to-Back register write needs a delay of min 10uS. * Back-to-Back writes to same register needs delay when SD bus clock * is very low w.r.t AHB clock, mainly during boot-time and during card * insert-removal. * We keep 20uS */ mutex_lock(&kona_dev->write_lock); udelay(20); val = sdhci_readl(host, KONA_SDHOST_CORESTAT); if (insert) { int ret; ret = mmc_gpio_get_ro(host->mmc); if (ret >= 0) val = (val & ~KONA_SDHOST_WP) | ((ret) ? KONA_SDHOST_WP : 0); val |= KONA_SDHOST_CD_SW; sdhci_writel(host, val, KONA_SDHOST_CORESTAT); } else { val &= ~KONA_SDHOST_CD_SW; sdhci_writel(host, val, KONA_SDHOST_CORESTAT); } mutex_unlock(&kona_dev->write_lock); return 0; }
static int sdhci_bcm_kona_probe(struct platform_device *pdev) { struct sdhci_bcm_kona_dev *kona_dev = NULL; struct sdhci_pltfm_host *pltfm_priv; struct device *dev = &pdev->dev; struct sdhci_host *host; int ret; ret = 0; host = sdhci_pltfm_init(pdev, &sdhci_pltfm_data_kona, sizeof(*kona_dev)); if (IS_ERR(host)) return PTR_ERR(host); dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr); pltfm_priv = sdhci_priv(host); kona_dev = sdhci_pltfm_priv(pltfm_priv); mutex_init(&kona_dev->write_lock); mmc_of_parse(host->mmc); if (!host->mmc->f_max) { dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n"); ret = -ENXIO; goto err_pltfm_free; } /* Get and enable the external clock */ kona_dev->external_clk = devm_clk_get(dev, NULL); if (IS_ERR(kona_dev->external_clk)) { dev_err(dev, "Failed to get external clock\n"); ret = PTR_ERR(kona_dev->external_clk); goto err_pltfm_free; } if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) { dev_err(dev, "Failed to set rate external clock\n"); goto err_pltfm_free; } if (clk_prepare_enable(kona_dev->external_clk) != 0) { dev_err(dev, "Failed to enable external clock\n"); goto err_pltfm_free; } dev_dbg(dev, "non-removable=%c\n", (host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N'); dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n", (mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N', (mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N'); if (host->mmc->caps & MMC_CAP_NONREMOVABLE) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; dev_dbg(dev, "is_8bit=%c\n", (host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N'); ret = sdhci_bcm_kona_sd_reset(host); if (ret) goto err_clk_disable; sdhci_bcm_kona_sd_init(host); ret = sdhci_add_host(host); if (ret) { dev_err(dev, "Failed sdhci_add_host\n"); goto err_reset; } /* if device is eMMC, emulate card insert right here */ if (host->mmc->caps & MMC_CAP_NONREMOVABLE) { ret = sdhci_bcm_kona_sd_card_emulate(host, 1); if (ret) { dev_err(dev, "unable to emulate card insertion\n"); goto err_remove_host; } } /* * Since the card detection GPIO interrupt is configured to be * edge sensitive, check the initial GPIO value here, emulate * only if the card is present */ if (mmc_gpio_get_cd(host->mmc) > 0) sdhci_bcm_kona_sd_card_emulate(host, 1); dev_dbg(dev, "initialized properly\n"); return 0; err_remove_host: sdhci_remove_host(host, 0); err_reset: sdhci_bcm_kona_sd_reset(host); err_clk_disable: clk_disable_unprepare(kona_dev->external_clk); err_pltfm_free: sdhci_pltfm_free(pdev); dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret); return ret; }
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) { return mmc_gpio_get_ro(host->mmc); }