12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 262ca8034SShashidhar Hiremath /* 362ca8034SShashidhar Hiremath * Synopsys DesignWare Multimedia Card Interface driver 462ca8034SShashidhar Hiremath * 562ca8034SShashidhar Hiremath * Copyright (C) 2009 NXP Semiconductors 662ca8034SShashidhar Hiremath * Copyright (C) 2009, 2010 Imagination Technologies Ltd. 762ca8034SShashidhar Hiremath */ 862ca8034SShashidhar Hiremath 9a3e2cd7fSThierry Reding #include <linux/err.h> 1062ca8034SShashidhar Hiremath #include <linux/interrupt.h> 1162ca8034SShashidhar Hiremath #include <linux/module.h> 1262ca8034SShashidhar Hiremath #include <linux/io.h> 1362ca8034SShashidhar Hiremath #include <linux/irq.h> 1462ca8034SShashidhar Hiremath #include <linux/platform_device.h> 15ee2112beSShawn Lin #include <linux/pm_runtime.h> 1662ca8034SShashidhar Hiremath #include <linux/slab.h> 1762ca8034SShashidhar Hiremath #include <linux/mmc/host.h> 1862ca8034SShashidhar Hiremath #include <linux/mmc/mmc.h> 19c91eab4bSThomas Abraham #include <linux/of.h> 20*ef87bd81SDinh Nguyen #include <linux/mfd/altera-sysmgr.h> 21*ef87bd81SDinh Nguyen #include <linux/regmap.h> 22c91eab4bSThomas Abraham 2362ca8034SShashidhar Hiremath #include "dw_mmc.h" 247725a52cSJingoo Han #include "dw_mmc-pltfm.h" 2562ca8034SShashidhar Hiremath 26*ef87bd81SDinh Nguyen #define SOCFPGA_DW_MMC_CLK_PHASE_STEP 45 27*ef87bd81SDinh Nguyen #define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel, reg_shift) \ 28*ef87bd81SDinh Nguyen ((((smplsel) & 0x7) << reg_shift) | (((drvsel) & 0x7) << 0)) 29*ef87bd81SDinh Nguyen 30800d78bfSThomas Abraham int dw_mci_pltfm_register(struct platform_device *pdev, 318e2b36eaSArnd Bergmann const struct dw_mci_drv_data *drv_data) 3262ca8034SShashidhar Hiremath { 3362ca8034SShashidhar Hiremath struct dw_mci *host; 3462ca8034SShashidhar Hiremath struct resource *regs; 3562ca8034SShashidhar Hiremath 36bb8bdc77SThomas Abraham host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); 3762ca8034SShashidhar Hiremath if (!host) 3862ca8034SShashidhar Hiremath return -ENOMEM; 3962ca8034SShashidhar Hiremath 4062ca8034SShashidhar Hiremath host->irq = platform_get_irq(pdev, 0); 41bb8bdc77SThomas Abraham if (host->irq < 0) 42bb8bdc77SThomas Abraham return host->irq; 4362ca8034SShashidhar Hiremath 44800d78bfSThomas Abraham host->drv_data = drv_data; 454a90920cSThomas Abraham host->dev = &pdev->dev; 4662ca8034SShashidhar Hiremath host->irq_flags = 0; 4762ca8034SShashidhar Hiremath host->pdata = pdev->dev.platform_data; 48bcc87666SAndy Shevchenko 49bcc87666SAndy Shevchenko regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 50a3e2cd7fSThierry Reding host->regs = devm_ioremap_resource(&pdev->dev, regs); 51a3e2cd7fSThierry Reding if (IS_ERR(host->regs)) 52a3e2cd7fSThierry Reding return PTR_ERR(host->regs); 53bb8bdc77SThomas Abraham 5445c7a490SJaehoon Chung /* Get registers' physical base address */ 5545c7a490SJaehoon Chung host->phy_regs = regs->start; 5645c7a490SJaehoon Chung 5762ca8034SShashidhar Hiremath platform_set_drvdata(pdev, host); 58dd369800SAndy Shevchenko return dw_mci_probe(host); 5962ca8034SShashidhar Hiremath } 6017403f23SThomas Abraham EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); 6117403f23SThomas Abraham 62ee2112beSShawn Lin const struct dev_pm_ops dw_mci_pltfm_pmops = { 63ee2112beSShawn Lin SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 64ee2112beSShawn Lin pm_runtime_force_resume) 65ee2112beSShawn Lin SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, 66ee2112beSShawn Lin dw_mci_runtime_resume, 67ee2112beSShawn Lin NULL) 68ee2112beSShawn Lin }; 6917403f23SThomas Abraham EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); 7062ca8034SShashidhar Hiremath 71*ef87bd81SDinh Nguyen static int dw_mci_socfpga_priv_init(struct dw_mci *host) 72*ef87bd81SDinh Nguyen { 73*ef87bd81SDinh Nguyen struct device_node *np = host->dev->of_node; 74*ef87bd81SDinh Nguyen struct regmap *sys_mgr_base_addr; 75*ef87bd81SDinh Nguyen u32 clk_phase[2] = {0}, reg_offset, reg_shift; 76*ef87bd81SDinh Nguyen int i, rc, hs_timing; 77*ef87bd81SDinh Nguyen 78*ef87bd81SDinh Nguyen rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0); 79*ef87bd81SDinh Nguyen if (rc < 0) 80*ef87bd81SDinh Nguyen return 0; 81*ef87bd81SDinh Nguyen 82*ef87bd81SDinh Nguyen sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); 83*ef87bd81SDinh Nguyen if (IS_ERR(sys_mgr_base_addr)) { 84*ef87bd81SDinh Nguyen dev_warn(host->dev, "clk-phase-sd-hs was specified, but failed to find altr,sys-mgr regmap!\n"); 85*ef87bd81SDinh Nguyen return 0; 86*ef87bd81SDinh Nguyen } 87*ef87bd81SDinh Nguyen 88*ef87bd81SDinh Nguyen of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset); 89*ef87bd81SDinh Nguyen of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift); 90*ef87bd81SDinh Nguyen 91*ef87bd81SDinh Nguyen for (i = 0; i < ARRAY_SIZE(clk_phase); i++) 92*ef87bd81SDinh Nguyen clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; 93*ef87bd81SDinh Nguyen 94*ef87bd81SDinh Nguyen hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift); 95*ef87bd81SDinh Nguyen regmap_write(sys_mgr_base_addr, reg_offset, hs_timing); 96*ef87bd81SDinh Nguyen 97*ef87bd81SDinh Nguyen return 0; 98*ef87bd81SDinh Nguyen } 99*ef87bd81SDinh Nguyen 100*ef87bd81SDinh Nguyen static const struct dw_mci_drv_data socfpga_drv_data = { 101*ef87bd81SDinh Nguyen .init = dw_mci_socfpga_priv_init, 102*ef87bd81SDinh Nguyen }; 103*ef87bd81SDinh Nguyen 104c91eab4bSThomas Abraham static const struct of_device_id dw_mci_pltfm_match[] = { 105c91eab4bSThomas Abraham { .compatible = "snps,dw-mshc", }, 106*ef87bd81SDinh Nguyen { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data, }, 107aaaaeb7aSJaehoon Chung { .compatible = "img,pistachio-dw-mshc", }, 108c91eab4bSThomas Abraham {}, 109c91eab4bSThomas Abraham }; 110c91eab4bSThomas Abraham MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); 111c91eab4bSThomas Abraham 112b177a530SHeiko Stübner static int dw_mci_pltfm_probe(struct platform_device *pdev) 113b177a530SHeiko Stübner { 114c73e41c8SHeiko Stübner const struct dw_mci_drv_data *drv_data = NULL; 115c73e41c8SHeiko Stübner const struct of_device_id *match; 116c73e41c8SHeiko Stübner 117c73e41c8SHeiko Stübner if (pdev->dev.of_node) { 118c73e41c8SHeiko Stübner match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node); 119c73e41c8SHeiko Stübner drv_data = match->data; 120c73e41c8SHeiko Stübner } 121c73e41c8SHeiko Stübner 122c73e41c8SHeiko Stübner return dw_mci_pltfm_register(pdev, drv_data); 123b177a530SHeiko Stübner } 124b177a530SHeiko Stübner 125b177a530SHeiko Stübner int dw_mci_pltfm_remove(struct platform_device *pdev) 126b177a530SHeiko Stübner { 127b177a530SHeiko Stübner struct dw_mci *host = platform_get_drvdata(pdev); 128b177a530SHeiko Stübner 129b177a530SHeiko Stübner dw_mci_remove(host); 130b177a530SHeiko Stübner return 0; 131b177a530SHeiko Stübner } 132b177a530SHeiko Stübner EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove); 133b177a530SHeiko Stübner 13462ca8034SShashidhar Hiremath static struct platform_driver dw_mci_pltfm_driver = { 13549480cf2SAndy Shevchenko .probe = dw_mci_pltfm_probe, 1364e608e4eSGreg Kroah-Hartman .remove = dw_mci_pltfm_remove, 13762ca8034SShashidhar Hiremath .driver = { 13862ca8034SShashidhar Hiremath .name = "dw_mmc", 13921b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 140cf109bc0SSachin Kamat .of_match_table = dw_mci_pltfm_match, 14162ca8034SShashidhar Hiremath .pm = &dw_mci_pltfm_pmops, 14262ca8034SShashidhar Hiremath }, 14362ca8034SShashidhar Hiremath }; 14462ca8034SShashidhar Hiremath 14549480cf2SAndy Shevchenko module_platform_driver(dw_mci_pltfm_driver); 14662ca8034SShashidhar Hiremath 14762ca8034SShashidhar Hiremath MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); 14862ca8034SShashidhar Hiremath MODULE_AUTHOR("NXP Semiconductor VietNam"); 14962ca8034SShashidhar Hiremath MODULE_AUTHOR("Imagination Technologies Ltd"); 15062ca8034SShashidhar Hiremath MODULE_LICENSE("GPL v2"); 151