2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-01-13 16:04:57 +05:30
/*
* Synopsys DesignWare Multimedia Card Interface driver
*
* Copyright ( C ) 2009 NXP Semiconductors
* Copyright ( C ) 2009 , 2010 Imagination Technologies Ltd .
*/
2013-01-21 11:09:11 +01:00
# include <linux/err.h>
2012-01-13 16:04:57 +05:30
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/platform_device.h>
2016-10-12 10:56:46 +08:00
# include <linux/pm_runtime.h>
2012-01-13 16:04:57 +05:30
# include <linux/slab.h>
# include <linux/mmc/host.h>
# include <linux/mmc/mmc.h>
2012-09-17 18:16:40 +00:00
# include <linux/of.h>
2022-11-14 17:02:15 -06:00
# include <linux/mfd/altera-sysmgr.h>
# include <linux/regmap.h>
2012-09-17 18:16:40 +00:00
2012-01-13 16:04:57 +05:30
# include "dw_mmc.h"
2013-08-06 10:54:07 +09:00
# include "dw_mmc-pltfm.h"
2012-01-13 16:04:57 +05:30
2022-11-14 17:02:15 -06:00
# define SOCFPGA_DW_MMC_CLK_PHASE_STEP 45
# define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel, reg_shift) \
( ( ( ( smplsel ) & 0x7 ) < < reg_shift ) | ( ( ( drvsel ) & 0x7 ) < < 0 ) )
2012-09-17 18:16:42 +00:00
int dw_mci_pltfm_register ( struct platform_device * pdev ,
2013-06-05 12:24:10 +03:00
const struct dw_mci_drv_data * drv_data )
2012-01-13 16:04:57 +05:30
{
struct dw_mci * host ;
struct resource * regs ;
2012-09-17 18:16:36 +00:00
host = devm_kzalloc ( & pdev - > dev , sizeof ( struct dw_mci ) , GFP_KERNEL ) ;
2012-01-13 16:04:57 +05:30
if ( ! host )
return - ENOMEM ;
host - > irq = platform_get_irq ( pdev , 0 ) ;
2012-09-17 18:16:36 +00:00
if ( host - > irq < 0 )
return host - > irq ;
2012-01-13 16:04:57 +05:30
2012-09-17 18:16:42 +00:00
host - > drv_data = drv_data ;
2012-09-17 18:16:35 +00:00
host - > dev = & pdev - > dev ;
2012-01-13 16:04:57 +05:30
host - > irq_flags = 0 ;
host - > pdata = pdev - > dev . platform_data ;
2013-06-05 12:24:10 +03:00
2023-03-15 13:22:31 +08:00
host - > regs = devm_platform_get_and_ioremap_resource ( pdev , 0 , & regs ) ;
2013-01-21 11:09:11 +01:00
if ( IS_ERR ( host - > regs ) )
return PTR_ERR ( host - > regs ) ;
2012-09-17 18:16:36 +00:00
2016-10-21 19:57:57 +09:00
/* Get registers' physical base address */
host - > phy_regs = regs - > start ;
2012-01-13 16:04:57 +05:30
platform_set_drvdata ( pdev , host ) ;
2013-06-05 12:24:11 +03:00
return dw_mci_probe ( host ) ;
2012-01-13 16:04:57 +05:30
}
2012-09-17 18:16:41 +00:00
EXPORT_SYMBOL_GPL ( dw_mci_pltfm_register ) ;
2016-10-12 10:56:46 +08:00
const struct dev_pm_ops dw_mci_pltfm_pmops = {
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
SET_RUNTIME_PM_OPS ( dw_mci_runtime_suspend ,
dw_mci_runtime_resume ,
NULL )
} ;
2012-09-17 18:16:41 +00:00
EXPORT_SYMBOL_GPL ( dw_mci_pltfm_pmops ) ;
2012-01-13 16:04:57 +05:30
2022-11-14 17:02:15 -06:00
static int dw_mci_socfpga_priv_init ( struct dw_mci * host )
{
struct device_node * np = host - > dev - > of_node ;
struct regmap * sys_mgr_base_addr ;
u32 clk_phase [ 2 ] = { 0 } , reg_offset , reg_shift ;
int i , rc , hs_timing ;
rc = of_property_read_variable_u32_array ( np , " clk-phase-sd-hs " , & clk_phase [ 0 ] , 2 , 0 ) ;
if ( rc < 0 )
return 0 ;
sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle ( np , " altr,sysmgr-syscon " ) ;
if ( IS_ERR ( sys_mgr_base_addr ) ) {
dev_warn ( host - > dev , " clk-phase-sd-hs was specified, but failed to find altr,sys-mgr regmap! \n " ) ;
return 0 ;
}
of_property_read_u32_index ( np , " altr,sysmgr-syscon " , 1 , & reg_offset ) ;
of_property_read_u32_index ( np , " altr,sysmgr-syscon " , 2 , & reg_shift ) ;
for ( i = 0 ; i < ARRAY_SIZE ( clk_phase ) ; i + + )
clk_phase [ i ] / = SOCFPGA_DW_MMC_CLK_PHASE_STEP ;
hs_timing = SYSMGR_SDMMC_CTRL_SET ( clk_phase [ 0 ] , clk_phase [ 1 ] , reg_shift ) ;
regmap_write ( sys_mgr_base_addr , reg_offset , hs_timing ) ;
return 0 ;
}
static const struct dw_mci_drv_data socfpga_drv_data = {
. init = dw_mci_socfpga_priv_init ,
} ;
2012-09-17 18:16:40 +00:00
static const struct of_device_id dw_mci_pltfm_match [ ] = {
{ . compatible = " snps,dw-mshc " , } ,
2022-11-14 17:02:15 -06:00
{ . compatible = " altr,socfpga-dw-mshc " , . data = & socfpga_drv_data , } ,
2016-01-21 11:01:06 +09:00
{ . compatible = " img,pistachio-dw-mshc " , } ,
2012-09-17 18:16:40 +00:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_mci_pltfm_match ) ;
2013-06-13 10:57:01 +02:00
static int dw_mci_pltfm_probe ( struct platform_device * pdev )
{
2013-06-27 11:55:35 -04:00
const struct dw_mci_drv_data * drv_data = NULL ;
const struct of_device_id * match ;
if ( pdev - > dev . of_node ) {
match = of_match_node ( dw_mci_pltfm_match , pdev - > dev . of_node ) ;
drv_data = match - > data ;
}
return dw_mci_pltfm_register ( pdev , drv_data ) ;
2013-06-13 10:57:01 +02:00
}
2023-05-06 00:15:06 +02:00
void dw_mci_pltfm_remove ( struct platform_device * pdev )
2013-06-13 10:57:01 +02:00
{
struct dw_mci * host = platform_get_drvdata ( pdev ) ;
dw_mci_remove ( host ) ;
}
EXPORT_SYMBOL_GPL ( dw_mci_pltfm_remove ) ;
2012-01-13 16:04:57 +05:30
static struct platform_driver dw_mci_pltfm_driver = {
2012-10-03 16:26:37 +03:00
. probe = dw_mci_pltfm_probe ,
2023-05-06 00:15:06 +02:00
. remove_new = dw_mci_pltfm_remove ,
2012-01-13 16:04:57 +05:30
. driver = {
. name = " dw_mmc " ,
2020-09-03 16:24:36 -07:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2014-02-25 15:18:23 +05:30
. of_match_table = dw_mci_pltfm_match ,
2012-01-13 16:04:57 +05:30
. pm = & dw_mci_pltfm_pmops ,
} ,
} ;
2012-10-03 16:26:37 +03:00
module_platform_driver ( dw_mci_pltfm_driver ) ;
2012-01-13 16:04:57 +05:30
MODULE_DESCRIPTION ( " DW Multimedia Card Interface driver " ) ;
MODULE_AUTHOR ( " NXP Semiconductor VietNam " ) ;
MODULE_AUTHOR ( " Imagination Technologies Ltd " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;