static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; if (!pdata) { dev_err(dev, "no device data specified\n"); return -ENOENT; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "no memory specified\n"); return -ENOENT; } host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); sc->clk_io = clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); goto err_io_clk; } /* enable the local io clock and keep it running for the moment. */ clk_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { struct clk *clk; char *name = pdata->clocks[ptr]; if (name == NULL) continue; clk = clk_get(dev, name); if (IS_ERR(clk)) { dev_err(dev, "failed to get clock %s\n", name); continue; } clks++; sc->clk_bus[ptr] = clk; /* * save current clock index to know which clock bus * is used later in overriding functions. */ sc->cur_clk = ptr; clk_enable(clk); dev_info(dev, "clock source %d: %s (%ld Hz)\n", ptr, name, clk_get_rate(clk)); } if (clks == 0) { dev_err(dev, "failed to find any bus clocks\n"); ret = -ENOENT; goto err_no_busclks; } sc->ioarea = request_mem_region(res->start, resource_size(res), mmc_hostname(host->mmc)); if (!sc->ioarea) { dev_err(dev, "failed to reserve register area\n"); ret = -ENXIO; goto err_req_regs; } host->ioaddr = ioremap_nocache(res->start, resource_size(res)); if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; goto err_add_host; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ if (pdata->cfg_gpio) pdata->cfg_gpio(pdev, pdata->max_width); host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #ifndef CONFIG_MMC_SDHCI_S3C_DMA /* we currently see overruns on errors, so disable the SDMA * support as well. */ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; #endif /* CONFIG_MMC_SDHCI_S3C_DMA */ /* It seems we do not get an DATA transfer complete on non-busy * transfers, not sure if this is a problem with this specific * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; #ifdef CONFIG_WIMAX_CMC /* This host supports the Auto CMD12 */ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; #endif if (pdata->cd_type == S3C_SDHCI_CD_NONE) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; /* if vmmc_name is in pdata */ if (pdata->vmmc_name) host->vmmc_name = pdata->vmmc_name; host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ if (pdata->clk_type) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; } /* It supports additional host capabilities if needed */ if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; /* for BCM WIFI */ if (pdata->pm_flags) host->mmc->pm_flags |= pdata->pm_flags; /* To turn on vmmc regulator only if sd card exists, GPIO pin for card detection should be initialized. Moved from sdhci_s3c_setup_card_detect_gpio() function */ if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) { if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { sc->ext_cd_gpio = pdata->ext_cd_gpio; sc->ext_cd_gpio_invert = pdata->ext_cd_gpio_invert; mmc_host_sd_set_present(host->mmc); if (sd_detection_cmd_dev == NULL && sc->ext_cd_gpio) { sd_detection_cmd_dev = device_create(sec_class, NULL, 0, NULL, "sdcard"); if (IS_ERR(sd_detection_cmd_dev)) pr_err("Fail to create sysfs dev\n"); if (device_create_file(sd_detection_cmd_dev, &dev_attr_status) < 0) pr_err("Fail to create sysfs file\n"); dev_set_drvdata(sd_detection_cmd_dev, sc); } #ifdef CONFIG_MIDAS_COMMON /* set TF_EN gpio as OUTPUT */ gpio_request(GPIO_TF_EN, "TF_EN"); gpio_direction_output(GPIO_TF_EN, 1); s3c_gpio_cfgpin(GPIO_TF_EN, S3C_GPIO_SFN(1)); s3c_gpio_setpull(GPIO_TF_EN, S3C_GPIO_PULL_NONE); #endif } else { dev_err(dev, "cannot request gpio for card detect\n"); } } ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } /* if it is set SDHCI_QUIRK_BROKEN_CARD_DETECTION before calling sdhci_add_host, in sdhci_add_host, MMC_CAP_NEEDS_POLL flag will be set. The flag S3C_SDHCI_CD_PERMANENT dose not need to detect a card by polling. */ if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT || \ pdata->cd_type == S3C_SDHCI_CD_GPIO) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; /* The following two methods of card detection might call sdhci_s3c_notify_change() immediately, so they can be called only after sdhci_add_host(). Setup errors are ignored. */ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) { pdata->ext_cd_init(&sdhci_s3c_notify_change); #ifdef CONFIG_MACH_PX if (pdata->ext_pdev) pdata->ext_pdev(pdev); #endif } #ifdef CONFIG_MACH_U1_NA_SPR if (pdata->cd_type == S3C_SDHCI_CD_GPIO && pdata->ext_cd_init) pdata->ext_cd_init(&sdhci_s3c_notify_change); #endif if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) sdhci_s3c_setup_card_detect_gpio(sc); return 0; err_add_host: if (host->ioaddr) iounmap(host->ioaddr); release_mem_region(sc->ioarea->start, resource_size(sc->ioarea)); err_req_regs: for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { clk_disable(sc->clk_bus[ptr]); clk_put(sc->clk_bus[ptr]); } err_no_busclks: clk_disable(sc->clk_io); clk_put(sc->clk_io); err_io_clk: sdhci_free_host(host); return ret; }
static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; if (!pdata) { dev_err(dev, "no device data specified\n"); return -ENOENT; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "no memory specified\n"); return -ENOENT; } host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); sc->clk_io = clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); goto err_io_clk; } /* enable the local io clock and keep it running for the moment. */ clk_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { struct clk *clk; char *name = pdata->clocks[ptr]; if (name == NULL) continue; clk = clk_get(dev, name); if (IS_ERR(clk)) { dev_err(dev, "failed to get clock %s\n", name); continue; } clks++; sc->clk_bus[ptr] = clk; /* * save current clock index to know which clock bus * is used later in overriding functions. */ sc->cur_clk = ptr; clk_enable(clk); dev_info(dev, "clock source %d: %s (%ld Hz)\n", ptr, name, clk_get_rate(clk)); } if (clks == 0) { dev_err(dev, "failed to find any bus clocks\n"); ret = -ENOENT; goto err_no_busclks; } sc->ioarea = request_mem_region(res->start, resource_size(res), mmc_hostname(host->mmc)); if (!sc->ioarea) { dev_err(dev, "failed to reserve register area\n"); ret = -ENXIO; goto err_req_regs; } host->ioaddr = ioremap_nocache(res->start, resource_size(res)); if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; goto err_req_regs; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ if (pdata->cfg_gpio) pdata->cfg_gpio(pdev, pdata->max_width); host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #ifndef CONFIG_MMC_SDHCI_S3C_DMA /* we currently see overruns on errors, so disable the SDMA * support as well. */ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; #endif /* CONFIG_MMC_SDHCI_S3C_DMA */ /* It seems we do not get an DATA transfer complete on non-busy * transfers, not sure if this is a problem with this specific * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; /* This host supports the Auto CMD12 */ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; /* Samsung SoCs need BROKEN_ADMA_ZEROLEN_DESC */ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC; if (pdata->cd_type == S3C_SDHCI_CD_NONE || pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ if (pdata->clk_type) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; } /* It supports additional host capabilities if needed */ if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } /* The following two methods of card detection might call sdhci_s3c_notify_change() immediately, so they can be called only after sdhci_add_host(). Setup errors are ignored. */ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) pdata->ext_cd_init(&sdhci_s3c_notify_change); if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) sdhci_s3c_setup_card_detect_gpio(sc); return 0; err_add_host: release_resource(sc->ioarea); kfree(sc->ioarea); err_req_regs: for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { if (sc->clk_bus[ptr]) { clk_disable(sc->clk_bus[ptr]); clk_put(sc->clk_bus[ptr]); } } err_no_busclks: clk_disable(sc->clk_io); clk_put(sc->clk_io); err_io_clk: sdhci_free_host(host); return ret; }
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, struct sdhci_pltfm_data *pdata) { struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct device_node *np = pdev->dev.of_node; struct resource *iomem; int ret; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { ret = -ENOMEM; goto err; } if (resource_size(iomem) < 0x100) dev_err(&pdev->dev, "Invalid iomem size!\n"); /* Some PCI-based MFD need the parent here */ if (pdev->dev.parent != &platform_bus && !np) host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host)); else host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host)); if (IS_ERR(host)) { ret = PTR_ERR(host); goto err; } pltfm_host = sdhci_priv(host); host->hw_name = dev_name(&pdev->dev); if (pdata && pdata->ops) host->ops = pdata->ops; else host->ops = &sdhci_pltfm_ops; if (pdata) host->quirks = pdata->quirks; host->irq = platform_get_irq(pdev, 0); if (!request_mem_region(iomem->start, resource_size(iomem), mmc_hostname(host->mmc))) { dev_err(&pdev->dev, "cannot request region\n"); ret = -EBUSY; goto err_request; } host->ioaddr = ioremap(iomem->start, resource_size(iomem)); if (!host->ioaddr) { dev_err(&pdev->dev, "failed to remap registers\n"); ret = -ENOMEM; goto err_remap; } platform_set_drvdata(pdev, host); return host; err_remap: release_mem_region(iomem->start, resource_size(iomem)); err_request: sdhci_free_host(host); err: dev_err(&pdev->dev, "%s failed %d\n", __func__, ret); return ERR_PTR(ret); }
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) { struct sdhci_host *host; struct sdio_dev *dev; struct resource *iomem; struct sdio_platform_cfg *hw_cfg = NULL; char devname[MAX_DEV_NAME_SIZE]; int ret = 0; char *emmc_regulator = NULL; #ifdef CONFIG_MACH_BCM_FPGA u32 of_quirks = 0; u32 of_quirks2 = 0; #endif pr_debug("%s: ENTRY\n", __func__); BUG_ON(pdev == NULL); hw_cfg = (struct sdio_platform_cfg *)pdev->dev.platform_data; if (pdev->dev.of_node) { u32 val; const char *prop; if (!pdev->dev.platform_data) hw_cfg = kzalloc(sizeof(struct sdio_platform_cfg), GFP_KERNEL); if (!hw_cfg) { dev_err(&pdev->dev, "unable to allocate mem for private data\n"); ret = -ENOMEM; goto err; } if (of_property_read_u32(pdev->dev.of_node, "id", &val)) { dev_err(&pdev->dev, "id read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->id = val; pdev->id = val; if (of_property_read_u32(pdev->dev.of_node, "data-pullup", &val)) { dev_err(&pdev->dev, "data-pullup read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->data_pullup = val; if (of_property_read_u32(pdev->dev.of_node, "devtype", &val)) { dev_err(&pdev->dev, "devtype read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->devtype = val; if (of_property_read_u32(pdev->dev.of_node, "flags", &val)) { dev_err(&pdev->dev, "flags read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->flags = val; if (of_property_read_string(pdev->dev.of_node, "peri-clk-name", &prop)) { dev_err(&pdev->dev, "peri-clk-name read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->peri_clk_name = (char *)prop; if (of_property_read_string(pdev->dev.of_node, "ahb-clk-name", &prop)) { dev_err(&pdev->dev, "ahb-clk-name read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->ahb_clk_name = (char *)prop; if (of_property_read_string(pdev->dev.of_node, "sleep-clk-name", &prop)) { dev_err(&pdev->dev, "sleep-clk-name read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->sleep_clk_name = (char *)prop; if (of_property_read_u32(pdev->dev.of_node, "peri-clk-rate", &val)) { dev_err(&pdev->dev, "peri-clk-rate read failed in %s\n", __func__); goto err_free_priv_data_mem; } #ifdef CONFIG_MACH_BCM_FPGA if (of_property_read_u32(pdev->dev.of_node, "quirks", &of_quirks)) { pr_info("quirks = 0x%08x foud for the configuration\n", of_quirks); } if (of_property_read_u32(pdev->dev.of_node, "quirks2", &of_quirks2)) { pr_info("quirks2 = 0x%08x foud for the configuration\n", of_quirks2); } #endif hw_cfg->peri_clk_rate = val; if (hw_cfg->devtype == SDIO_DEV_TYPE_SDMMC) { if (of_property_read_string(pdev->dev.of_node, "vddo-regulator-name", &prop)) { dev_err(&pdev->dev, "vddo-regulator-name read "\ "failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->vddo_regulator_name = (char *)prop; if (of_property_read_string(pdev->dev.of_node, "vddsdxc-regulator-name", &prop)) { dev_err(&pdev->dev, "vddsdxc-regulator-name"\ "read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->vddsdxc_regulator_name = (char *)prop; if (of_property_read_u32(pdev->dev.of_node, "cd-gpio", &val)) { dev_err(&pdev->dev, "cd-gpio read failed in %s\n", __func__); hw_cfg->cd_gpio = -1; } hw_cfg->cd_gpio = val; } else if (hw_cfg->devtype == SDIO_DEV_TYPE_EMMC) { if (of_property_read_u32(pdev->dev.of_node, "is-8bit", &val)) { dev_err(&pdev->dev, "is-8bit read failed in %s\n", __func__); goto err_free_priv_data_mem; } hw_cfg->is_8bit = val; if (!(of_property_read_string(pdev->dev.of_node, "vddsdmmc-regulator-name", &prop))) emmc_regulator = (char *)prop; } pdev->dev.platform_data = hw_cfg; } if (!hw_cfg) { dev_err(&pdev->dev, "hw_cfg is NULL\n"); ret = -ENOMEM; goto err; } if (hw_cfg->devtype >= SDIO_DEV_TYPE_MAX) { dev_err(&pdev->dev, "unknown device type\n"); ret = -EFAULT; goto err; } pr_debug("%s: GET PLATFORM RESOURCES\n", __func__); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { ret = -ENOMEM; goto err; } /* Some PCI-based MFD need the parent here */ if (pdev->dev.parent != &platform_bus) host = sdhci_alloc_host(pdev->dev.parent, sizeof(struct sdio_dev)); else host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdio_dev)); if (IS_ERR(host)) { ret = PTR_ERR(host); goto err; } pr_debug("%s: ALLOC HOST\n", __func__); host->hw_name = "bcm_kona_sd"; host->ops = &sdhci_pltfm_ops; host->irq = platform_get_irq(pdev, 0); host->quirks = SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE; #ifdef CONFIG_MACH_RHEA_DALTON2_EB30 host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; #endif #ifdef CONFIG_MACH_BCM_FPGA host->quirks |= of_quirks; host->quirks2 |= of_quirks2; #endif pr_debug("%s: GET IRQ\n", __func__); if (hw_cfg->flags & KONA_SDIO_FLAGS_DEVICE_NON_REMOVABLE) host->mmc->caps |= MMC_CAP_NONREMOVABLE; if (!request_mem_region(iomem->start, resource_size(iomem), mmc_hostname(host->mmc))) { dev_err(&pdev->dev, "cannot request region\n"); ret = -EBUSY; goto err_free_host; } host->ioaddr = ioremap(iomem->start, resource_size(iomem)); if (!host->ioaddr) { dev_err(&pdev->dev, "failed to remap registers\n"); ret = -ENOMEM; goto err_free_mem_region; } pr_debug("%s: MEM and IO REGION OKAY\n", __func__); dev = sdhci_priv(host); dev->dev = &pdev->dev; dev->host = host; dev->devtype = hw_cfg->devtype; dev->cd_gpio = hw_cfg->cd_gpio; host->mmc->parent = dev->dev; if (dev->devtype == SDIO_DEV_TYPE_WIFI) dev->wifi_gpio = &hw_cfg->wifi_gpio; if (dev->devtype == SDIO_DEV_TYPE_EMMC && emmc_regulator) { dev->vdd_sdxc_regulator = regulator_get(NULL, emmc_regulator); if (IS_ERR(dev->vdd_sdxc_regulator)) { dev->vdd_sdxc_regulator = NULL; } } if (dev->devtype == SDIO_DEV_TYPE_EMMC) host->detect_delay = 0; else host->detect_delay = 200; pr_debug("%s: DEV TYPE %x\n", __func__, dev->devtype); gDevs[dev->devtype] = dev; platform_set_drvdata(pdev, dev); snprintf(devname, sizeof(devname), "%s%d", DEV_NAME, pdev->id); /* enable clocks */ #ifdef CONFIG_MACH_BCM2850_FPGA if (clock) { /* clock override */ dev->clk_hz = clock; } else { dev->clk_hz = gClock[dev->devtype]; } #elif defined(CONFIG_MACH_BCM_FPGA) dev->clk_hz = hw_cfg->peri_clk_rate; #else /* peripheral clock */ dev->peri_clk = clk_get(&pdev->dev, hw_cfg->peri_clk_name); if (IS_ERR_OR_NULL(dev->peri_clk)) { ret = -EINVAL; goto err_unset_pltfm; } ret = clk_set_rate(dev->peri_clk, hw_cfg->peri_clk_rate); if (ret) goto err_peri_clk_put; /* sleep clock */ dev->sleep_clk = clk_get(&pdev->dev, hw_cfg->sleep_clk_name); if (IS_ERR_OR_NULL(dev->sleep_clk)) { ret = -EINVAL; goto err_peri_clk_put; } ret = clk_enable(dev->sleep_clk); if (ret) { dev_err(&pdev->dev, "failed to enable sleep clock for %s\n", devname); goto err_sleep_clk_put; } ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) { dev_err(&pdev->dev, "failed to initialize core clock for %s\n", devname); goto err_sleep_clk_disable; } dev->clk_hz = clk_get_rate(dev->peri_clk); #endif #if 0 /*** NANI To check mmc base clock ***/ printk( "[NANI] %s, %d : %s[%s], dev->clk_hz(%d)\n", __FUNCTION__, __LINE__, mmc_hostname(host->mmc), dev_name(mmc_dev(host->mmc)), dev->clk_hz ); if( !strcmp( mmc_hostname(host->mmc), "mmc0" ) ) { // KONA_MST_CLK_SDIO2_CLKGATE(0x3F00135C) // KONA_MST_CLK_SDIO2_DIV(0x3F001A2C) // clock.c - struct bus_clk CLK_NAME(sdio2_ahb) // KONA_MST_CLK_BASE_ADDR : 0x3F001000 // KPM_CLK_MGR_REG_SDIO2_CLKGATE_OFFSET : 0x0000035C // KPM_CLK_MGR_REG_SDIO2_DIV_OFFSET : 0x00000A2C printk( "[NANI] %s, %d : KONA_MST_CLK_SDIO2_CLKGATE == %#x, KONA_MST_CLK_SDIO2_DIV == %#x\n", __FUNCTION__, __LINE__, readl( IO_PHYS_TO_VIRT(0x3F001000) + 0x0000035C ), readl( IO_PHYS_TO_VIRT(0x3F001000) + 0x00000A2C ) ); } else if( !strcmp( mmc_hostname(host->mmc), "mmc2" ) ) { // KONA_MST_CLK_SDIO1_CLKGATE(0x3F001358) // KONA_MST_CLK_SDIO1_DIV(0x3F001A28) // clock.c - struct bus_clk CLK_NAME(sdio1_ahb) // KONA_MST_CLK_BASE_ADDR : 0x3F001000 // KPM_CLK_MGR_REG_SDIO1_CLKGATE_OFFSET : 0x00000358 // KPM_CLK_MGR_REG_SDIO1_DIV_OFFSET : 0x00000A28 printk( "[NANI] %s, %d : KONA_MST_CLK_SDIO1_CLKGATE == %#x, KONA_MST_CLK_SDIO1_DIV == %#x\n", __FUNCTION__, __LINE__, readl( IO_PHYS_TO_VIRT(0x3F001000) + 0x00000358 ), readl( IO_PHYS_TO_VIRT(0x3F001000) + 0x00000A28 ) ); } else { } #endif dev->suspended = 0; if (hw_cfg->vddo_regulator_name) { ret = sdhci_pltfm_regulator_init(dev, hw_cfg->vddo_regulator_name); #ifndef BCM_REGULATOR_SKIP_QUIRK if (ret < 0) goto err_term_clk; #endif } if (hw_cfg->vddsdxc_regulator_name && dev->devtype == SDIO_DEV_TYPE_SDMMC) { ret = sdhci_pltfm_regulator_sdxc_init(dev, hw_cfg->vddsdxc_regulator_name); #ifndef BCM_REGULATOR_SKIP_QUIRK if (ret < 0) goto err_term_clk; #endif } if (sd_detection_cmd_dev == NULL){ sd_detection_cmd_dev = device_create(sec_class, NULL, 0, NULL, "sdcard"); if (IS_ERR(sd_detection_cmd_dev)) pr_err("Fail to create sysfs dev\n"); if (device_create_file(sd_detection_cmd_dev, &dev_attr_status) < 0) pr_err("Fail to create sysfs file\n"); } mutex_init(&dev->regulator_lock); kona_sdio_regulator_power(dev, 1); ret = bcm_kona_sd_reset(dev); if (ret) goto err_term_clk; ret = bcm_kona_sd_init(dev); if (ret) goto err_reset; if (hw_cfg->is_8bit) host->mmc->caps |= MMC_CAP_8_BIT_DATA; /* Note that sdhci_add_host calls --> mmc_add_host, which in turn * checks for the flag MMC_PM_IGNORE_PM_NOTIFY before registering a PM * notifier for the specific instance of SDIO host controller. For * WiFi case, we don't want to get notified, becuase then from there * mmc_power_off is called which will reset the Host registers that * needs to be re-programmed by starting SDIO handsake again. We want * to prevent this in case of WiFi. So enable MMC_PM_IGNORE_PM_NOTIFY * flag, so that notifier never gets registered. */ if (dev->devtype == SDIO_DEV_TYPE_WIFI) { /* The Wireless LAN drivers call the API sdio_get_host_pm_caps * to know the PM capabilities of the driver, which would * return pm_caps. While the internal code decides based on * pm_flags, the pm_caps also should reflect the same. */ host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; host->mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; } #if !defined(CONFIG_MACH_BCM_FPGA_E) /* Enable 1.8V DDR operation for e.MMC */ if (dev->devtype == SDIO_DEV_TYPE_EMMC) host->mmc->caps |= MMC_CAP_1_8V_DDR; #endif /* Don't issue SLEEP command to e.MMC device */ if (dev->devtype == SDIO_DEV_TYPE_EMMC) /*host->mmc->caps2 |= MMC_CAP2_NO_SLEEP_CMD;*/ host->mmc->pm_flags = MMC_PM_KEEP_POWER; /* * This has to be done before sdhci_add_host. * As soon as we add the host, request * starts. If we dont enable this here, the * runtime get and put of sdhci will fallback to * clk_enable and clk_disable which will conflict * with the PM runtime when it gets enabled just * after sdhci_add_host. Now with this, the RPM * calls will fail until RPM is enabled, but things * will work well, as we have clocks enabled till the * probe ends. */ dev->runtime_pm_enabled = 1; ret = sdhci_add_host(host); if (ret) goto err_reset; ret = proc_init(pdev); if (ret) goto err_rm_host; /* Should be done only after sdhci_add_host */ sdhci_pltfm_runtime_pm_init(dev->dev); if (dev->devtype == SDIO_DEV_TYPE_SDMMC) { /* support SD card detect interrupts for insert/removal */ host->mmc->card_detect_cap = true; } /* if device is eMMC, emulate card insert right here */ if (dev->devtype == SDIO_DEV_TYPE_EMMC) { ret = bcm_kona_sd_card_emulate(dev, 1); if (ret) { dev_err(&pdev->dev, "unable to emulate card insertion\n"); goto err_proc_term; } pr_info("%s: card insert emulated!\n", devname); } else if (dev->devtype == SDIO_DEV_TYPE_SDMMC && dev->cd_gpio >= 0) { dev->cd_int_wake_lock_name = kasprintf(GFP_KERNEL, "%s_cd_int", devname); if (!dev->cd_int_wake_lock_name) { dev_err(&pdev->dev, "error allocating mem for wake_lock_name\n"); goto err_proc_term; } wake_lock_init(&dev->cd_int_wake_lock, WAKE_LOCK_SUSPEND, dev->cd_int_wake_lock_name); ret = gpio_request(dev->cd_gpio, "sdio cd"); if (ret < 0) { dev_err(&pdev->dev, "Unable to request GPIO pin %d\n", dev->cd_gpio); goto err_proc_term; } gpio_direction_input(dev->cd_gpio); ret = request_threaded_irq(gpio_to_irq(dev->cd_gpio), NULL, sdhci_pltfm_cd_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, "sdio cd", dev); if (ret) { dev_err(&pdev->dev, "Unable to request card detection irq=%d" " for gpio=%d\n", gpio_to_irq(dev->cd_gpio), dev->cd_gpio); goto err_free_cd_gpio; } /* Set debounce for SD Card detect to maximum value (128ms) * * NOTE-1: If gpio_set_debounce() returns error we still * continue with the default debounce value set. Another reason * for doing this is that on rhea-ray boards the SD Detect GPIO * is on GPIO Expander and gpio_set_debounce() will return error * and if we return error from here, then probe() would fail and * SD detection would always fail. * * NOTE-2: We also give a msleep() of the "debounce" time here * so that we give enough time for the debounce to stabilize * before we read the gpio value in gpio_get_value_cansleep(). */ ret = gpio_set_debounce(dev->cd_gpio, (SD_DETECT_GPIO_DEBOUNCE_128MS * 1000)); if (ret < 0) { dev_err(&pdev->dev, "%s: gpio set debounce failed." "default debounce value assumed\n", __func__); } /* Sleep for 128ms to allow debounce to stabilize */ msleep(SD_DETECT_GPIO_DEBOUNCE_128MS); /* * 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 (gpio_get_value_cansleep(dev->cd_gpio) == 0) { printk("%s - external SD Card is detected by GPIO\n",__func__); bcm_kona_sd_card_emulate(dev, 1); } else { printk("%s - SD Card is Not detected by GPIO-->Disable SDXLDO\n",__func__); /* If card is not present disable the regulator */ kona_sdio_regulator_power(dev, 0); } } /* Force insertion interrupt, in case of no card detect registered. */ if (dev->cd_gpio < 0) bcm_kona_sd_card_emulate(dev, 1); #ifdef CONFIG_BRCM_UNIFIED_DHD_SUPPORT if ((dev->devtype == SDIO_DEV_TYPE_WIFI) && (hw_cfg->register_status_notify != NULL)) { hw_cfg->register_status_notify(kona_sdio_status_notify_cb, host); } pr_debug("%s: CALL BACK IS REGISTERED\n", __func__); #endif atomic_set(&dev->initialized, 1); sdhci_pltfm_clk_enable(dev, 0); pr_info("%s: initialized properly\n", devname); return 0; err_free_cd_gpio: if (dev->devtype == SDIO_DEV_TYPE_SDMMC && dev->cd_gpio >= 0) gpio_free(dev->cd_gpio); err_proc_term: proc_term(pdev); err_rm_host: sdhci_remove_host(host, 0); err_reset: bcm_kona_sd_reset(dev); err_term_clk: sdhci_pltfm_clk_enable(dev, 0); #if !defined(CONFIG_MACH_BCM2850_FPGA) && !defined(CONFIG_MACH_BCM_FPGA) err_sleep_clk_disable: clk_disable(dev->sleep_clk); err_sleep_clk_put: clk_put(dev->sleep_clk); err_peri_clk_put: clk_put(dev->peri_clk); err_unset_pltfm: platform_set_drvdata(pdev, NULL); iounmap(host->ioaddr); #endif err_free_mem_region: release_mem_region(iomem->start, resource_size(iomem)); err_free_host: sdhci_free_host(host); err_free_priv_data_mem: if (pdev->dev.of_node) { ret = -EFAULT; kfree(hw_cfg); } err: pr_err("Probing of sdhci-pltfm %d failed: %d\n", pdev->id, ret); return ret; }
static int sdhci_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *device; struct sdhci_acpi_host *c; struct sdhci_host *host; struct resource *iomem; resource_size_t len; const char *hid; const char *uid; int err; if (acpi_bus_get_device(handle, &device)) return -ENODEV; if (acpi_bus_get_status(device) || !device->status.present) return -ENODEV; hid = acpi_device_hid(device); uid = device->pnp.unique_id; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) return -ENOMEM; len = resource_size(iomem); if (len < 0x100) dev_err(dev, "Invalid iomem size!\n"); if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) return -ENOMEM; host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); if (IS_ERR(host)) return PTR_ERR(host); c = sdhci_priv(host); c->host = host; c->slot = sdhci_acpi_get_slot(hid, uid); c->pdev = pdev; c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); platform_set_drvdata(pdev, c); host->hw_name = "ACPI"; host->ops = &sdhci_acpi_ops_dflt; host->irq = platform_get_irq(pdev, 0); host->ioaddr = devm_ioremap_nocache(dev, iomem->start, resource_size(iomem)); if (host->ioaddr == NULL) { err = -ENOMEM; goto err_free; } if (c->slot) { if (c->slot->probe_slot) { err = c->slot->probe_slot(pdev, hid, uid); if (err) goto err_free; } if (c->slot->chip) { host->ops = c->slot->chip->ops; host->quirks |= c->slot->chip->quirks; host->quirks2 |= c->slot->chip->quirks2; host->mmc->caps |= c->slot->chip->caps; host->mmc->caps2 |= c->slot->chip->caps2; host->mmc->pm_caps |= c->slot->chip->pm_caps; } host->quirks |= c->slot->quirks; host->quirks2 |= c->slot->quirks2; host->mmc->caps |= c->slot->caps; host->mmc->caps2 |= c->slot->caps2; host->mmc->pm_caps |= c->slot->pm_caps; } host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL); if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) { dev_warn(dev, "failed to setup card detect gpio\n"); c->use_runtime_pm = false; } } err = sdhci_add_host(host); if (err) goto err_free; if (c->use_runtime_pm) { pm_runtime_set_active(dev); pm_suspend_ignore_children(dev, 1); pm_runtime_set_autosuspend_delay(dev, 50); pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); } device_enable_async_suspend(dev); return 0; err_free: sdhci_free_host(c->host); return err; }
static int tegra_sdhci_card_detect(struct sdhci_host *sdhost) { struct tegra_sdhci_host *host = sdhci_priv(sdhost); return host->card_present; }
static int sdhci_msm_probe(struct platform_device *pdev) { struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_msm_host *msm_host; struct resource *core_memres; int ret; u16 host_version, core_minor; u32 core_version, caps; u8 core_major; msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); if (!msm_host) return -ENOMEM; msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops; host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); pltfm_host->priv = msm_host; msm_host->mmc = host->mmc; msm_host->pdev = pdev; ret = mmc_of_parse(host->mmc); if (ret) goto pltfm_free; sdhci_get_of_property(pdev); /* Setup SDCC bus voter clock. */ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); if (!IS_ERR(msm_host->bus_clk)) { /* Vote for max. clk rate for max. performance */ ret = clk_set_rate(msm_host->bus_clk, INT_MAX); if (ret) goto pltfm_free; ret = clk_prepare_enable(msm_host->bus_clk); if (ret) goto pltfm_free; } /* Setup main peripheral bus clock */ msm_host->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_host->pclk)) { ret = PTR_ERR(msm_host->pclk); dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret); goto bus_clk_disable; } ret = clk_prepare_enable(msm_host->pclk); if (ret) goto bus_clk_disable; /* Setup SDC MMC clock */ msm_host->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(msm_host->clk)) { ret = PTR_ERR(msm_host->clk); dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret); goto pclk_disable; } /* Vote for maximum clock rate for maximum performance */ ret = clk_set_rate(msm_host->clk, INT_MAX); if (ret) dev_warn(&pdev->dev, "core clock boost failed\n"); ret = clk_prepare_enable(msm_host->clk); if (ret) goto pclk_disable; core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); if (IS_ERR(msm_host->core_mem)) { dev_err(&pdev->dev, "Failed to remap registers\n"); ret = PTR_ERR(msm_host->core_mem); goto clk_disable; } /* Reset the core and Enable SDHC mode */ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) | CORE_SW_RST, msm_host->core_mem + CORE_POWER); /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ usleep_range(1000, 5000); if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) { dev_err(&pdev->dev, "Stuck in reset\n"); ret = -ETIMEDOUT; goto clk_disable; } /* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT)); core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION); core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> CORE_VERSION_MAJOR_SHIFT; core_minor = core_version & CORE_VERSION_MINOR_MASK; dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n", core_version, core_major, core_minor); /* * Support for some capabilities is not advertised by newer * controller versions and must be explicitly enabled. */ if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; writel_relaxed(caps, host->ioaddr + CORE_VENDOR_SPEC_CAPABILITIES0); } ret = sdhci_add_host(host); if (ret) goto clk_disable; return 0; clk_disable: clk_disable_unprepare(msm_host->clk); pclk_disable: clk_disable_unprepare(msm_host->pclk); bus_clk_disable: if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); pltfm_free: sdhci_pltfm_free(pdev); return ret; }
static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); return clk_round_rate(pltfm_host->clk, UINT_MAX); }
static int sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; int ret; struct clk *clk; pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) return -ENOMEM; host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0); if (IS_ERR(host)) return PTR_ERR(host); /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { ret = armada_38x_quirks(pdev, host); if (ret < 0) goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); if (ret < 0) goto err_mbus_win; } pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); goto err_clk_get; } pltfm_host->clk = clk; clk_prepare_enable(clk); match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); if (match) { ret = mmc_of_parse(host->mmc); if (ret) goto err_of_parse; sdhci_get_of_property(pdev); pdata = pxav3_get_mmc_pdata(dev); } else if (pdata) { /* on-chip device */ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) host->mmc->caps |= MMC_CAP_NONREMOVABLE; /* If slot design supports 8 bit data, indicate this to MMC. */ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) host->mmc->caps |= MMC_CAP_8_BIT_DATA; if (pdata->quirks) host->quirks |= pdata->quirks; if (pdata->quirks2) host->quirks2 |= pdata->quirks2; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->host_caps2) host->mmc->caps2 |= pdata->host_caps2; if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; if (gpio_is_valid(pdata->ext_cd_gpio)) { ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio, 0); if (ret) { dev_err(mmc_dev(host->mmc), "failed to allocate card detect gpio\n"); goto err_cd_req; } } } pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "failed to add host\n"); goto err_add_host; } platform_set_drvdata(pdev, host); if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) { device_init_wakeup(&pdev->dev, 1); host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; } else { device_init_wakeup(&pdev->dev, 0); } pm_runtime_put_autosuspend(&pdev->dev); return 0; err_add_host: pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); err_of_parse: err_cd_req: clk_disable_unprepare(clk); err_clk_get: err_mbus_win: sdhci_pltfm_free(pdev); return ret; }
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_tegra_autocal_offsets offsets = tegra_host->autocal_offsets; struct mmc_ios *ios = &host->mmc->ios; bool card_clk_enabled; u16 pdpu; u32 reg; int ret; switch (ios->timing) { case MMC_TIMING_UHS_SDR104: pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104; break; case MMC_TIMING_MMC_HS400: pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400; break; default: if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8; else pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3; } tegra_sdhci_set_pad_autocal_offset(host, pdpu); card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); tegra_sdhci_configure_cal_pad(host, true); reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); usleep_range(1, 2); /* 10 ms timeout */ ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS, reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE), 1000, 10000); tegra_sdhci_configure_cal_pad(host, false); tegra_sdhci_configure_card_clk(host, card_clk_enabled); if (ret) { dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n"); if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) pdpu = offsets.pull_down_1v8_timeout << 8 | offsets.pull_up_1v8_timeout; else pdpu = offsets.pull_down_3v3_timeout << 8 | offsets.pull_up_3v3_timeout; /* Disable automatic calibration and use fixed offsets */ reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); reg &= ~SDHCI_AUTO_CAL_ENABLE; sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); tegra_sdhci_set_pad_autocal_offset(host, pdpu); } }
static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_tegra_autocal_offsets *autocal = &tegra_host->autocal_offsets; int err; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-3v3", &autocal->pull_up_3v3); if (err) autocal->pull_up_3v3 = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-3v3", &autocal->pull_down_3v3); if (err) autocal->pull_down_3v3 = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-1v8", &autocal->pull_up_1v8); if (err) autocal->pull_up_1v8 = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-1v8", &autocal->pull_down_1v8); if (err) autocal->pull_down_1v8 = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-3v3-timeout", &autocal->pull_up_3v3); if (err) autocal->pull_up_3v3_timeout = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-3v3-timeout", &autocal->pull_down_3v3); if (err) autocal->pull_down_3v3_timeout = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-1v8-timeout", &autocal->pull_up_1v8); if (err) autocal->pull_up_1v8_timeout = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-1v8-timeout", &autocal->pull_down_1v8); if (err) autocal->pull_down_1v8_timeout = 0; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-sdr104", &autocal->pull_up_sdr104); if (err) autocal->pull_up_sdr104 = autocal->pull_up_1v8; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-sdr104", &autocal->pull_down_sdr104); if (err) autocal->pull_down_sdr104 = autocal->pull_down_1v8; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-hs400", &autocal->pull_up_hs400); if (err) autocal->pull_up_hs400 = autocal->pull_up_1v8; err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-down-offset-hs400", &autocal->pull_down_hs400); if (err) autocal->pull_down_hs400 = autocal->pull_down_1v8; }
static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct sdhci_tegra_soc_data *soc_data; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_tegra *tegra_host; struct clk *clk; int rc; match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); if (!match) return -EINVAL; soc_data = match->data; host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); tegra_host = sdhci_pltfm_priv(pltfm_host); tegra_host->ddr_signaling = false; tegra_host->pad_calib_required = false; tegra_host->pad_control_available = false; tegra_host->soc_data = soc_data; if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) { rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); if (rc == 0) host->mmc_host_ops.start_signal_voltage_switch = sdhci_tegra_start_signal_voltage_switch; } /* Hook to periodically rerun pad calibration */ if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) host->mmc_host_ops.request = tegra_sdhci_request; host->mmc_host_ops.hs400_enhanced_strobe = tegra_sdhci_hs400_enhanced_strobe; rc = mmc_of_parse(host->mmc); if (rc) goto err_parse_dt; if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; tegra_sdhci_parse_pad_autocal_dt(host); tegra_sdhci_parse_tap_and_trim(host); tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(tegra_host->power_gpio)) { rc = PTR_ERR(tegra_host->power_gpio); goto err_power_req; } clk = devm_clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); goto err_clk_get; } clk_prepare_enable(clk); pltfm_host->clk = clk; tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, "sdhci"); if (IS_ERR(tegra_host->rst)) { rc = PTR_ERR(tegra_host->rst); dev_err(&pdev->dev, "failed to get reset control: %d\n", rc); goto err_rst_get; } rc = reset_control_assert(tegra_host->rst); if (rc) goto err_rst_get; usleep_range(2000, 4000); rc = reset_control_deassert(tegra_host->rst); if (rc) goto err_rst_get; usleep_range(2000, 4000); rc = sdhci_add_host(host); if (rc) goto err_add_host; return 0; err_add_host: reset_control_assert(tegra_host->rst); err_rst_get: clk_disable_unprepare(pltfm_host->clk); err_clk_get: err_power_req: err_parse_dt: sdhci_pltfm_free(pdev); return rc; }
static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data; struct clk *clk; int err; struct pltfm_imx_data *imx_data; clk = clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); return PTR_ERR(clk); } clk_enable(clk); pltfm_host->clk = clk; imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL); if (!imx_data) { clk_disable(pltfm_host->clk); clk_put(pltfm_host->clk); return -ENOMEM; } pltfm_host->priv = imx_data; host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; if (cpu_is_mx25() || cpu_is_mx35()) { /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; /* write_protect can't be routed to controller, use gpio */ sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro; } if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51())) imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; if (boarddata) { err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); if (err) { dev_warn(mmc_dev(host->mmc), "no write-protect pin available!\n"); boarddata->wp_gpio = err; } err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD"); if (err) { dev_warn(mmc_dev(host->mmc), "no card-detect pin available!\n"); goto no_card_detect_pin; } /* i.MX5x has issues to be researched */ if (!cpu_is_mx25() && !cpu_is_mx35()) goto not_supported; err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, mmc_hostname(host->mmc), host); if (err) { dev_warn(mmc_dev(host->mmc), "request irq error\n"); goto no_card_detect_irq; } imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP; /* Now we have a working card_detect again */ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; } return 0; no_card_detect_irq: gpio_free(boarddata->cd_gpio); no_card_detect_pin: boarddata->cd_gpio = err; not_supported: kfree(imx_data); return 0; }
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); return clk_get_rate(pltfm_host->clk) / 256 / 16; }
static int sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata; struct sdhci_s3c_drv_data *drv_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; if (!pdev->dev.platform_data && !pdev->dev.of_node) { dev_err(dev, "no device data specified\n"); return -ENOENT; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; } host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } sc = sdhci_priv(host); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; goto err_pdata_io_clk; } sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev); if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) goto err_pdata_io_clk; } else { memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); sc->ext_cd_gpio = -1; /* invalid gpio number */ } drv_data = sdhci_s3c_get_driver_data(pdev); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; platform_set_drvdata(pdev, host); sc->clk_io = clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); goto err_pdata_io_clk; } /* enable the local io clock and keep it running for the moment. */ clk_prepare_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { struct clk *clk; char name[14]; snprintf(name, 14, "mmc_busclk.%d", ptr); clk = clk_get(dev, name); if (IS_ERR(clk)) continue; clks++; sc->clk_bus[ptr] = clk; /* * save current clock index to know which clock bus * is used later in overriding functions. */ sc->cur_clk = ptr; dev_info(dev, "clock source %d: %s (%ld Hz)\n", ptr, name, clk_get_rate(clk)); } if (clks == 0) { dev_err(dev, "failed to find any bus clocks\n"); ret = -ENOENT; goto err_no_busclks; } #ifndef CONFIG_PM_RUNTIME clk_prepare_enable(sc->clk_bus[sc->cur_clk]); #endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->ioaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->ioaddr)) { ret = PTR_ERR(host->ioaddr); goto err_req_regs; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ if (pdata->cfg_gpio) pdata->cfg_gpio(pdev, pdata->max_width); host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; if (drv_data) host->quirks |= drv_data->sdhci_quirks; #ifndef CONFIG_MMC_SDHCI_S3C_DMA /* we currently see overruns on errors, so disable the SDMA * support as well. */ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; #endif /* CONFIG_MMC_SDHCI_S3C_DMA */ /* It seems we do not get an DATA transfer complete on non-busy * transfers, not sure if this is a problem with this specific * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; /* This host supports the Auto CMD12 */ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; /* Samsung SoCs need BROKEN_ADMA_ZEROLEN_DESC */ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC; if (pdata->cd_type == S3C_SDHCI_CD_NONE || pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; switch (pdata->max_width) { case 8: host->mmc->caps |= MMC_CAP_8_BIT_DATA; case 4: host->mmc->caps |= MMC_CAP_4_BIT_DATA; break; } if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; } /* It supports additional host capabilities if needed */ if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->host_caps2) host->mmc->caps2 |= pdata->host_caps2; pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); goto err_req_regs; } /* The following two methods of card detection might call sdhci_s3c_notify_change() immediately, so they can be called only after sdhci_add_host(). Setup errors are ignored. */ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) pdata->ext_cd_init(&sdhci_s3c_notify_change); if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) sdhci_s3c_setup_card_detect_gpio(sc); #ifdef CONFIG_PM_RUNTIME if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) clk_disable_unprepare(sc->clk_io); #endif return 0; err_req_regs: #ifndef CONFIG_PM_RUNTIME clk_disable_unprepare(sc->clk_bus[sc->cur_clk]); #endif for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { if (sc->clk_bus[ptr]) { clk_put(sc->clk_bus[ptr]); } } err_no_busclks: clk_disable_unprepare(sc->clk_io); clk_put(sc->clk_io); err_pdata_io_clk: sdhci_free_host(host); return ret; }
static int sdhci_pxav2_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; int ret; struct clk *clk; pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) return -ENOMEM; host = sdhci_pltfm_init(pdev, NULL, 0); if (IS_ERR(host)) { kfree(pxa); return PTR_ERR(host); } pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; clk = clk_get(dev, "PXA-SDHCLK"); if (IS_ERR(clk)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); goto err_clk_get; } pltfm_host->clk = clk; clk_prepare_enable(clk); host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev); if (match) { pdata = pxav2_get_mmc_pdata(dev); } if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; host->mmc->caps |= MMC_CAP_NONREMOVABLE; } /* If slot design supports 8 bit data, indicate this to MMC. */ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) host->mmc->caps |= MMC_CAP_8_BIT_DATA; if (pdata->quirks) host->quirks |= pdata->quirks; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; } host->ops = &pxav2_sdhci_ops; ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "failed to add host\n"); goto err_add_host; } platform_set_drvdata(pdev, host); return 0; err_add_host: clk_disable_unprepare(clk); clk_put(clk); err_clk_get: sdhci_pltfm_free(pdev); kfree(pxa); return ret; }
static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct sdhci_tegra_soc_data *soc_data; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_tegra *tegra_host; struct clk *clk; int rc; match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); if (!match) return -EINVAL; soc_data = match->data; host = sdhci_pltfm_init(pdev, soc_data->pdata, 0); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); if (!tegra_host) { dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); rc = -ENOMEM; goto err_alloc_tegra_host; } tegra_host->ddr_signaling = false; tegra_host->soc_data = soc_data; pltfm_host->priv = tegra_host; rc = mmc_of_parse(host->mmc); if (rc) goto err_parse_dt; if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(tegra_host->power_gpio)) { rc = PTR_ERR(tegra_host->power_gpio); goto err_power_req; } clk = devm_clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); goto err_clk_get; } clk_prepare_enable(clk); pltfm_host->clk = clk; rc = sdhci_add_host(host); if (rc) goto err_add_host; return 0; err_add_host: clk_disable_unprepare(pltfm_host->clk); err_clk_get: err_power_req: err_parse_dt: err_alloc_tegra_host: sdhci_pltfm_free(pdev); return rc; }
static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; int ret; struct clk *clk; pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) return -ENOMEM; host = sdhci_pltfm_init(pdev, NULL); if (IS_ERR(host)) { kfree(pxa); return PTR_ERR(host); } pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; clk = clk_get(dev, "PXA-SDHCLK"); if (IS_ERR(clk)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); goto err_clk_get; } pltfm_host->clk = clk; clk_enable(clk); host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_32BIT_ADMA_SIZE; /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; host->mmc->caps |= MMC_CAP_NONREMOVABLE; } /* If slot design supports 8 bit data, indicate this to MMC. */ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) host->mmc->caps |= MMC_CAP_8_BIT_DATA; if (pdata->quirks) host->quirks |= pdata->quirks; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; } host->ops = &pxav3_sdhci_ops; ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "failed to add host\n"); goto err_add_host; } platform_set_drvdata(pdev, host); return 0; err_add_host: clk_disable(clk); clk_put(clk); err_clk_get: sdhci_pltfm_free(pdev); kfree(pxa); return ret; }
static int __devinit tegra_sdhci_probe(struct platform_device *pdev) { int rc; struct tegra_sdhci_platform_data *plat; struct sdhci_host *sdhci; struct tegra_sdhci_host *host; struct resource *res; int irq; void __iomem *ioaddr; plat = pdev->dev.platform_data; if (plat == NULL) return -ENXIO; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) return -ENODEV; irq = res->start; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) return -ENODEV; ioaddr = ioremap(res->start, res->end - res->start); #ifdef CONFIG_ALLOC_MEMORY_EARLY_FOR_SDCARD_DETECTION if (!global_state) { global_state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL); if (global_state == NULL) { printk("[sdcard] failed to allocte memory for struct parsed_partitions...\n"); } else { printk("[sdcard] ok to allocate memory! \n", global_state); } } #endif sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host)); if (IS_ERR(sdhci)) { rc = PTR_ERR(sdhci); goto err_unmap; } host = sdhci_priv(sdhci); host->sdhci = sdhci; host->card_always_on = (plat->power_gpio == -1) ? 1 : 0; host->wp_gpio = plat->wp_gpio; host->cd_gpio = plat->cd_gpio; host->cd_gpio_polarity = plat->cd_gpio_polarity; host->wp_gpio_polarity = plat->wp_gpio_polarity; host->clk = clk_get(&pdev->dev, plat->clk_id); if (IS_ERR(host->clk)) { rc = PTR_ERR(host->clk); goto err_free_host; } rc = clk_enable(host->clk); if (rc != 0) goto err_clkput; host->clk_enabled = 1; sdhci->hw_name = "tegra"; sdhci->ops = &tegra_sdhci_ops; sdhci->irq = irq; sdhci->ioaddr = ioaddr; sdhci->version = SDHCI_SPEC_200; sdhci->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP | SDHCI_QUIRK_BROKEN_WRITE_PROTECT | SDHCI_QUIRK_BROKEN_CTRL_HISPD | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_8_BIT_DATA | SDHCI_QUIRK_NO_VERSION_REG | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_RUNTIME_DISABLE | SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (plat->force_hs != 0) sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE; #ifdef CONFIG_MMC_EMBEDDED_SDIO mmc_set_embedded_sdio_data(sdhci->mmc, &plat->cis, &plat->cccr, plat->funcs, plat->num_funcs); #endif if (host->card_always_on) sdhci->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; rc = sdhci_add_host(sdhci); if (rc) goto err_clk_disable; platform_set_drvdata(pdev, host); if (plat->cd_gpio != -1) { rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, mmc_hostname(sdhci->mmc), sdhci); if (rc) goto err_remove_host; host->card_present = (gpio_get_value(plat->cd_gpio) == host->cd_gpio_polarity); } else if (plat->register_status_notify) { plat->register_status_notify( tegra_sdhci_status_notify_cb, sdhci); } if (plat->cd_gpio == -1) host->card_present = true; if (plat->board_probe) plat->board_probe(pdev->id, sdhci->mmc); printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id, sdhci->irq, sdhci->ioaddr); return 0; err_remove_host: sdhci_remove_host(sdhci, 1); err_clk_disable: clk_disable(host->clk); err_clkput: clk_put(host->clk); err_free_host: if (sdhci) sdhci_free_host(sdhci); err_unmap: iounmap(sdhci->ioaddr); return rc; }
unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); return clk_get_rate(pltfm_host->clk); }
static int sdhci_rpm_enabled(struct sdhci_host *host) { struct sdio_dev *dev = sdhci_priv(host); return sdhci_pltfm_rpm_enabled(dev); }
static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct sdhci_tegra_soc_data *soc_data; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_tegra *tegra_host; struct clk *clk; int rc; match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); if (!match) return -EINVAL; soc_data = match->data; host = sdhci_pltfm_init(pdev, soc_data->pdata); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); if (!tegra_host) { dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); rc = -ENOMEM; goto err_alloc_tegra_host; } tegra_host->soc_data = soc_data; pltfm_host->priv = tegra_host; sdhci_tegra_parse_dt(&pdev->dev, tegra_host); if (gpio_is_valid(tegra_host->power_gpio)) { rc = gpio_request(tegra_host->power_gpio, "sdhci_power"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate power gpio\n"); goto err_power_req; } gpio_direction_output(tegra_host->power_gpio, 1); } if (gpio_is_valid(tegra_host->cd_gpio)) { rc = gpio_request(tegra_host->cd_gpio, "sdhci_cd"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate cd gpio\n"); goto err_cd_req; } gpio_direction_input(tegra_host->cd_gpio); rc = request_irq(gpio_to_irq(tegra_host->cd_gpio), carddetect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, mmc_hostname(host->mmc), host); if (rc) { dev_err(mmc_dev(host->mmc), "request irq error\n"); goto err_cd_irq_req; } } if (gpio_is_valid(tegra_host->wp_gpio)) { rc = gpio_request(tegra_host->wp_gpio, "sdhci_wp"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate wp gpio\n"); goto err_wp_req; } gpio_direction_input(tegra_host->wp_gpio); } clk = clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); goto err_clk_get; } clk_prepare_enable(clk); pltfm_host->clk = clk; if (tegra_host->is_8bit) host->mmc->caps |= MMC_CAP_8_BIT_DATA; rc = sdhci_add_host(host); if (rc) goto err_add_host; return 0; err_add_host: clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: if (gpio_is_valid(tegra_host->wp_gpio)) gpio_free(tegra_host->wp_gpio); err_wp_req: if (gpio_is_valid(tegra_host->cd_gpio)) free_irq(gpio_to_irq(tegra_host->cd_gpio), host); err_cd_irq_req: if (gpio_is_valid(tegra_host->cd_gpio)) gpio_free(tegra_host->cd_gpio); err_cd_req: if (gpio_is_valid(tegra_host->power_gpio)) gpio_free(tegra_host->power_gpio); err_power_req: err_alloc_tegra_host: sdhci_pltfm_free(pdev); return rc; }
static int ext_cd_status(struct sdhci_host *host) { struct sdhci_pxa *pxa = sdhci_priv(host); return ext_cd_val(pxa->pdata->ext_cd_gpio,pxa->pdata->ext_cd_gpio_invert); }
static int __devinit sdhci_tegra_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct tegra_sdhci_platform_data *plat; struct sdhci_host *host; struct clk *clk; int rc; host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); plat = pdev->dev.platform_data; if (plat == NULL) { dev_err(mmc_dev(host->mmc), "missing platform data\n"); rc = -ENXIO; goto err_no_plat; } if (gpio_is_valid(plat->power_gpio)) { rc = gpio_request(plat->power_gpio, "sdhci_power"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate power gpio\n"); goto err_power_req; } tegra_gpio_enable(plat->power_gpio); gpio_direction_output(plat->power_gpio, 1); } if (gpio_is_valid(plat->cd_gpio)) { rc = gpio_request(plat->cd_gpio, "sdhci_cd"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate cd gpio\n"); goto err_cd_req; } tegra_gpio_enable(plat->cd_gpio); gpio_direction_input(plat->cd_gpio); rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, mmc_hostname(host->mmc), host); if (rc) { dev_err(mmc_dev(host->mmc), "request irq error\n"); goto err_cd_irq_req; } } if (gpio_is_valid(plat->wp_gpio)) { rc = gpio_request(plat->wp_gpio, "sdhci_wp"); if (rc) { dev_err(mmc_dev(host->mmc), "failed to allocate wp gpio\n"); goto err_wp_req; } tegra_gpio_enable(plat->wp_gpio); gpio_direction_input(plat->wp_gpio); } clk = clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); goto err_clk_get; } clk_enable(clk); pltfm_host->clk = clk; host->mmc->pm_caps = plat->pm_flags; if (plat->is_8bit) host->mmc->caps |= MMC_CAP_8_BIT_DATA; rc = sdhci_add_host(host); if (rc) goto err_add_host; return 0; err_add_host: clk_disable(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: if (gpio_is_valid(plat->wp_gpio)) { tegra_gpio_disable(plat->wp_gpio); gpio_free(plat->wp_gpio); } err_wp_req: if (gpio_is_valid(plat->cd_gpio)) free_irq(gpio_to_irq(plat->cd_gpio), host); err_cd_irq_req: if (gpio_is_valid(plat->cd_gpio)) { tegra_gpio_disable(plat->cd_gpio); gpio_free(plat->cd_gpio); } err_cd_req: if (gpio_is_valid(plat->power_gpio)) { tegra_gpio_disable(plat->power_gpio); gpio_free(plat->power_gpio); } err_power_req: err_no_plat: sdhci_pltfm_free(pdev); return rc; }
static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host) { struct sdhci_of_host *of_host = sdhci_priv(host); return of_host->clock / 1000; }
static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; printk("WEI: %s and pdev->name---->%s\n",__FUNCTION__,pdev->name); if (!pdata) { dev_err(dev, "no device data specified\n"); return -ENOENT; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "no memory specified\n"); return -ENOENT; } printk("WEI: %s --> sdhci_alloc_host\n",__FUNCTION__); host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; platform_set_drvdata(pdev, host); sc->clk_io = clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); goto err_io_clk; } /* enable the local io clock and keep it running for the moment. */ clk_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { struct clk *clk; char *name = pdata->clocks[ptr]; if (name == NULL) continue; clk = clk_get(dev, name); if (IS_ERR(clk)) { dev_err(dev, "failed to get clock %s\n", name); continue; } clks++; sc->clk_bus[ptr] = clk; clk_enable(clk); dev_info(dev, "clock source %d: %s (%ld Hz)\n", ptr, name, clk_get_rate(clk)); } if (clks == 0) { dev_err(dev, "failed to find any bus clocks\n"); ret = -ENOENT; goto err_no_busclks; } sc->ioarea = request_mem_region(res->start, resource_size(res), mmc_hostname(host->mmc)); if (!sc->ioarea) { dev_err(dev, "failed to reserve register area\n"); ret = -ENXIO; goto err_req_regs; } host->ioaddr = ioremap_nocache(res->start, resource_size(res)); if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; goto err_req_regs; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ if (pdata->cfg_gpio) pdata->cfg_gpio(pdev, pdata->max_width); if (pdata->get_ro) sdhci_s3c_ops.get_ro = sdhci_s3c_get_ro; host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_BROKEN_CARD_PRESENT_BIT; host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; #ifndef CONFIG_MMC_SDHCI_S3C_DMA /* we currently see overruns on errors, so disable the SDMA * support as well. */ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; /* PIO currently has problems with multi-block IO */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; #endif /* CONFIG_MMC_SDHCI_S3C_DMA */ /* It seems we do not get an DATA transfer complete on non-busy * transfers, not sure if this is a problem with this specific * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; if (pdata->host_caps) host->mmc->caps = pdata->host_caps; else host->mmc->caps = 0; /* Set pm_flags for built_in device */ host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; if (pdata->built_in) host->mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; /* to add external irq as a card detect signal */ if (pdata->cfg_ext_cd) { pdata->cfg_ext_cd(); if (pdata->detect_ext_cd()) host->flags |= SDHCI_DEVICE_ALIVE; } /* to configure gpio pin as a card write protection signal */ if (pdata->cfg_wp) pdata->cfg_wp(); ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } /* register external irq here (after all init is done) */ if (pdata->cfg_ext_cd) { ret = request_irq(pdata->ext_cd, sdhci_irq_cd, IRQF_SHARED, mmc_hostname(host->mmc), sc); if(ret) goto err_add_host; } printk("WEI: %s success\n",__FUNCTION__); return 0; err_add_host: printk("WEI: %s --> err_add_host\n",__FUNCTION__); release_resource(sc->ioarea); kfree(sc->ioarea); err_req_regs: printk("WEI: %s --> err_req_regs\n",__FUNCTION__); for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { if (sc->clk_bus[ptr]) { clk_disable(sc->clk_bus[ptr]); clk_put(sc->clk_bus[ptr]); } } err_no_busclks: clk_disable(sc->clk_io); clk_put(sc->clk_io); err_io_clk: sdhci_free_host(host); return ret; }
static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) { return sdhci_priv(host); }
static int sdhci_s3c_resume(struct platform_device *dev) { struct sdhci_host *host = platform_get_drvdata(dev); struct s3c_sdhci_platdata *pdata = dev->dev.platform_data; int ret; sdhci_resume_host(host); if(pdata && pdata->cfg_ext_cd){ ret = request_irq(pdata->ext_cd, sdhci_irq_cd, IRQF_SHARED, mmc_hostname(host->mmc), sdhci_priv(host)); if(ret) return ret; } return 0; }
static unsigned int esdhc_get_min_clock(struct sdhci_host *host) { struct sdhci_of_host *of_host = sdhci_priv(host); return of_host->clock / 256 / 16; }
static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; int ret; struct clk *clk; int qos_class = PM_QOS_CPUIDLE_BLOCK; pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) return -ENOMEM; host = sdhci_pltfm_init(pdev, NULL); if (IS_ERR(host)) { kfree(pxa); return PTR_ERR(host); } pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; clk = clk_get(dev, "PXA-SDHCLK"); if (IS_ERR(clk)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); goto err_clk_get; } pltfm_host->clk = clk; clk_prepare_enable(clk); host->quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_32BIT_ADMA_SIZE | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); if (match) pdata = pxav3_get_mmc_pdata(dev); if (pdata) { pdata->qos_idle.name = mmc_hostname(host->mmc); pm_qos_add_request(&pdata->qos_idle, qos_class, PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); /* If slot design supports 8 bit data, indicate this to MMC. */ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) host->mmc->caps |= MMC_CAP_8_BIT_DATA; if (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING) host->mmc->caps2 |= MMC_CAP2_BUS_AUTO_CLK_GATE; if (pdata->flags & PXA_FLAG_DISABLE_PROBE_CDSCAN) host->mmc->caps2 |= MMC_CAP2_DISABLE_PROBE_CDSCAN; if (pdata->quirks) host->quirks |= pdata->quirks; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->host_caps2) host->mmc->caps2 |= pdata->host_caps2; if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; if (pdata->cd_type != PXA_SDHCI_CD_HOST) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == PXA_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) { ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio); if (ret) { dev_err(mmc_dev(host->mmc), "failed to allocate card detect gpio\n"); goto err_cd_req; } } else if (pdata->cd_type == PXA_SDHCI_CD_PERMANENT) { /* on-chip device */ host->mmc->caps |= MMC_CAP_NONREMOVABLE; } else if (pdata->cd_type == PXA_SDHCI_CD_NONE) host->mmc->caps |= MMC_CAP_NEEDS_POLL; } host->quirks2 = SDHCI_QUIRK2_NO_CURRENT_LIMIT | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_TIMEOUT_DIVIDE_4; host->ops = &pxav3_sdhci_ops; if (pdata && pdata->flags & PXA_FLAG_EN_PM_RUNTIME) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); } #ifdef _MMC_SAFE_ACCESS_ mmc_is_available = 1; #endif ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "failed to add host\n"); if (pdata && pdata->flags & PXA_FLAG_EN_PM_RUNTIME) { pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); } goto err_add_host; } /* remove the caps that supported by the controller but not available * for certain platforms. */ if (pdata && pdata->host_caps_disable) host->mmc->caps &= ~(pdata->host_caps_disable); platform_set_drvdata(pdev, host); if (pdata && pdata->flags & PXA_FLAG_WAKEUP_HOST) { device_init_wakeup(&pdev->dev, 1); host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; } else device_init_wakeup(&pdev->dev, 0); #ifdef CONFIG_SD8XXX_RFKILL if (pdata && pdata->pmmc) *pdata->pmmc = host->mmc; #endif return 0; err_add_host: clk_disable_unprepare(clk); clk_put(clk); if (pdata) pm_qos_remove_request(&pdata->qos_idle); err_cd_req: mmc_gpio_free_cd(host->mmc); err_clk_get: sdhci_pltfm_free(pdev); kfree(pxa); return ret; }