2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-06-16 12:47:16 -04:00
/*
* sdhci - brcmstb . c Support for SDHCI on Broadcom BRCMSTB SoC ' s
*
* Copyright ( C ) 2015 Broadcom Corporation
*/
# include <linux/io.h>
# include <linux/mmc/host.h>
# include <linux/module.h>
# include <linux/of.h>
2020-01-13 16:07:02 -05:00
# include <linux/bitops.h>
2020-01-13 16:07:05 -05:00
# include <linux/delay.h>
2016-06-16 12:47:16 -04:00
# include "sdhci-pltfm.h"
2020-01-13 16:07:05 -05:00
# include "cqhci.h"
2016-06-16 12:47:16 -04:00
2020-01-13 16:07:02 -05:00
# define SDHCI_VENDOR 0x78
# define SDHCI_VENDOR_ENHANCED_STRB 0x1
# define BRCMSTB_PRIV_FLAGS_NO_64BIT BIT(0)
# define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT BIT(1)
2020-01-13 16:07:05 -05:00
# define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
2020-01-13 16:07:02 -05:00
struct sdhci_brcmstb_priv {
void __iomem * cfg_regs ;
2020-01-13 16:07:05 -05:00
bool has_cqe ;
2020-01-13 16:07:02 -05:00
} ;
struct brcmstb_match_priv {
void ( * hs400es ) ( struct mmc_host * mmc , struct mmc_ios * ios ) ;
2020-01-13 16:07:05 -05:00
struct sdhci_ops * ops ;
2020-01-13 16:07:02 -05:00
unsigned int flags ;
} ;
static void sdhci_brcmstb_hs400es ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
u32 reg ;
dev_dbg ( mmc_dev ( mmc ) , " %s(): Setting HS400-Enhanced-Strobe mode \n " ,
__func__ ) ;
reg = readl ( host - > ioaddr + SDHCI_VENDOR ) ;
if ( ios - > enhanced_strobe )
reg | = SDHCI_VENDOR_ENHANCED_STRB ;
else
reg & = ~ SDHCI_VENDOR_ENHANCED_STRB ;
writel ( reg , host - > ioaddr + SDHCI_VENDOR ) ;
}
2020-01-13 16:07:05 -05:00
static void sdhci_brcmstb_set_clock ( struct sdhci_host * host , unsigned int clock )
{
u16 clk ;
host - > mmc - > actual_clock = 0 ;
clk = sdhci_calc_clk ( host , clock , & host - > mmc - > actual_clock ) ;
sdhci_writew ( host , clk , SDHCI_CLOCK_CONTROL ) ;
if ( clock = = 0 )
return ;
sdhci_enable_clk ( host , clk ) ;
}
2020-01-13 16:07:06 -05:00
static void sdhci_brcmstb_set_uhs_signaling ( struct sdhci_host * host ,
unsigned int timing )
{
u16 ctrl_2 ;
dev_dbg ( mmc_dev ( host - > mmc ) , " %s: Setting UHS signaling for %d timing \n " ,
__func__ , timing ) ;
ctrl_2 = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
/* Select Bus Speed Mode for host */
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
if ( ( timing = = MMC_TIMING_MMC_HS200 ) | |
( timing = = MMC_TIMING_UHS_SDR104 ) )
ctrl_2 | = SDHCI_CTRL_UHS_SDR104 ;
else if ( timing = = MMC_TIMING_UHS_SDR12 )
ctrl_2 | = SDHCI_CTRL_UHS_SDR12 ;
else if ( timing = = MMC_TIMING_SD_HS | |
timing = = MMC_TIMING_MMC_HS | |
timing = = MMC_TIMING_UHS_SDR25 )
ctrl_2 | = SDHCI_CTRL_UHS_SDR25 ;
else if ( timing = = MMC_TIMING_UHS_SDR50 )
ctrl_2 | = SDHCI_CTRL_UHS_SDR50 ;
else if ( ( timing = = MMC_TIMING_UHS_DDR50 ) | |
( timing = = MMC_TIMING_MMC_DDR52 ) )
ctrl_2 | = SDHCI_CTRL_UHS_DDR50 ;
else if ( timing = = MMC_TIMING_MMC_HS400 )
ctrl_2 | = SDHCI_CTRL_HS400 ; /* Non-standard */
sdhci_writew ( host , ctrl_2 , SDHCI_HOST_CONTROL2 ) ;
}
2020-01-13 16:07:05 -05:00
static void sdhci_brcmstb_dumpregs ( struct mmc_host * mmc )
{
sdhci_dumpregs ( mmc_priv ( mmc ) ) ;
}
static void sdhci_brcmstb_cqe_enable ( struct mmc_host * mmc )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
u32 reg ;
reg = sdhci_readl ( host , SDHCI_PRESENT_STATE ) ;
while ( reg & SDHCI_DATA_AVAILABLE ) {
sdhci_readl ( host , SDHCI_BUFFER ) ;
reg = sdhci_readl ( host , SDHCI_PRESENT_STATE ) ;
}
sdhci_cqe_enable ( mmc ) ;
}
static const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops = {
. enable = sdhci_brcmstb_cqe_enable ,
. disable = sdhci_cqe_disable ,
. dumpregs = sdhci_brcmstb_dumpregs ,
} ;
static struct sdhci_ops sdhci_brcmstb_ops = {
2016-06-16 12:47:16 -04:00
. set_clock = sdhci_set_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = sdhci_reset ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
} ;
2020-01-13 16:07:05 -05:00
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
. set_clock = sdhci_brcmstb_set_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = sdhci_reset ,
2020-01-13 16:07:06 -05:00
. set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling ,
2016-06-16 12:47:16 -04:00
} ;
2020-01-13 16:07:05 -05:00
static struct brcmstb_match_priv match_priv_7425 = {
2020-01-13 16:07:02 -05:00
. flags = BRCMSTB_PRIV_FLAGS_NO_64BIT |
BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT ,
2020-01-13 16:07:05 -05:00
. ops = & sdhci_brcmstb_ops ,
2020-01-13 16:07:02 -05:00
} ;
2020-01-13 16:07:05 -05:00
static struct brcmstb_match_priv match_priv_7445 = {
2020-01-13 16:07:02 -05:00
. flags = BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT ,
2020-01-13 16:07:05 -05:00
. ops = & sdhci_brcmstb_ops ,
2020-01-13 16:07:02 -05:00
} ;
static const struct brcmstb_match_priv match_priv_7216 = {
. hs400es = sdhci_brcmstb_hs400es ,
2020-01-13 16:07:05 -05:00
. ops = & sdhci_brcmstb_ops_7216 ,
2020-01-13 16:07:02 -05:00
} ;
static const struct of_device_id sdhci_brcm_of_match [ ] = {
{ . compatible = " brcm,bcm7425-sdhci " , . data = & match_priv_7425 } ,
{ . compatible = " brcm,bcm7445-sdhci " , . data = & match_priv_7445 } ,
{ . compatible = " brcm,bcm7216-sdhci " , . data = & match_priv_7216 } ,
{ } ,
} ;
2020-01-13 16:07:05 -05:00
static u32 sdhci_brcmstb_cqhci_irq ( struct sdhci_host * host , u32 intmask )
{
int cmd_error = 0 ;
int data_error = 0 ;
if ( ! sdhci_cqe_irq ( host , intmask , & cmd_error , & data_error ) )
return intmask ;
cqhci_irq ( host - > mmc , intmask , cmd_error , data_error ) ;
return 0 ;
}
static int sdhci_brcmstb_add_host ( struct sdhci_host * host ,
struct sdhci_brcmstb_priv * priv )
{
struct cqhci_host * cq_host ;
bool dma64 ;
int ret ;
if ( ! priv - > has_cqe )
return sdhci_add_host ( host ) ;
dev_dbg ( mmc_dev ( host - > mmc ) , " CQE is enabled \n " ) ;
host - > mmc - > caps2 | = MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD ;
ret = sdhci_setup_host ( host ) ;
if ( ret )
return ret ;
cq_host = devm_kzalloc ( mmc_dev ( host - > mmc ) ,
sizeof ( * cq_host ) , GFP_KERNEL ) ;
if ( ! cq_host ) {
ret = - ENOMEM ;
goto cleanup ;
}
cq_host - > mmio = host - > ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR ;
cq_host - > ops = & sdhci_brcmstb_cqhci_ops ;
dma64 = host - > flags & SDHCI_USE_64_BIT_DMA ;
if ( dma64 ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " Using 64 bit DMA \n " ) ;
cq_host - > caps | = CQHCI_TASK_DESC_SZ_128 ;
}
ret = cqhci_init ( cq_host , host - > mmc , dma64 ) ;
if ( ret )
goto cleanup ;
ret = __sdhci_add_host ( host ) ;
if ( ret )
goto cleanup ;
return 0 ;
cleanup :
sdhci_cleanup_host ( host ) ;
return ret ;
}
2016-06-16 12:47:16 -04:00
static int sdhci_brcmstb_probe ( struct platform_device * pdev )
{
2020-01-13 16:07:02 -05:00
const struct brcmstb_match_priv * match_priv ;
2020-01-13 16:07:05 -05:00
struct sdhci_pltfm_data brcmstb_pdata ;
2016-06-16 12:47:16 -04:00
struct sdhci_pltfm_host * pltfm_host ;
2020-01-13 16:07:02 -05:00
const struct of_device_id * match ;
struct sdhci_brcmstb_priv * priv ;
struct sdhci_host * host ;
struct resource * iomem ;
2020-01-13 16:07:05 -05:00
bool has_cqe = false ;
2016-06-16 12:47:16 -04:00
struct clk * clk ;
int res ;
2020-01-13 16:07:02 -05:00
match = of_match_node ( sdhci_brcm_of_match , pdev - > dev . of_node ) ;
match_priv = match - > data ;
2020-01-13 16:07:05 -05:00
dev_dbg ( & pdev - > dev , " Probe found match for %s \n " , match - > compatible ) ;
2020-09-02 21:36:53 +02:00
clk = devm_clk_get_optional ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( clk ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( clk ) ,
" Failed to get clock from Device Tree \n " ) ;
2016-06-16 12:47:16 -04:00
res = clk_prepare_enable ( clk ) ;
if ( res )
return res ;
2020-01-13 16:07:05 -05:00
memset ( & brcmstb_pdata , 0 , sizeof ( brcmstb_pdata ) ) ;
if ( device_property_read_bool ( & pdev - > dev , " supports-cqe " ) ) {
has_cqe = true ;
match_priv - > ops - > irq = sdhci_brcmstb_cqhci_irq ;
}
brcmstb_pdata . ops = match_priv - > ops ;
host = sdhci_pltfm_init ( pdev , & brcmstb_pdata ,
2020-01-13 16:07:02 -05:00
sizeof ( struct sdhci_brcmstb_priv ) ) ;
2016-06-16 12:47:16 -04:00
if ( IS_ERR ( host ) ) {
res = PTR_ERR ( host ) ;
goto err_clk ;
}
2020-01-13 16:07:02 -05:00
pltfm_host = sdhci_priv ( host ) ;
priv = sdhci_pltfm_priv ( pltfm_host ) ;
2020-01-13 16:07:05 -05:00
priv - > has_cqe = has_cqe ;
2020-01-13 16:07:02 -05:00
/* Map in the non-standard CFG registers */
iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
priv - > cfg_regs = devm_ioremap_resource ( & pdev - > dev , iomem ) ;
if ( IS_ERR ( priv - > cfg_regs ) ) {
res = PTR_ERR ( priv - > cfg_regs ) ;
goto err ;
}
2016-06-16 12:47:16 -04:00
sdhci_get_of_property ( pdev ) ;
2018-12-23 21:59:18 +01:00
res = mmc_of_parse ( host - > mmc ) ;
if ( res )
goto err ;
2016-06-16 12:47:16 -04:00
2020-01-13 16:07:02 -05:00
/*
* If the chip has enhanced strobe and it ' s enabled , add
* callback
*/
if ( match_priv - > hs400es & &
( host - > mmc - > caps2 & MMC_CAP2_HS400_ES ) )
host - > mmc_host_ops . hs400_enhanced_strobe = match_priv - > hs400es ;
2016-06-16 12:47:16 -04:00
/*
* Supply the existing CAPS , but clear the UHS modes . This
* will allow these modes to be specified by device tree
* properties through mmc_of_parse ( ) .
*/
host - > caps = sdhci_readl ( host , SDHCI_CAPABILITIES ) ;
2020-01-13 16:07:02 -05:00
if ( match_priv - > flags & BRCMSTB_PRIV_FLAGS_NO_64BIT )
2016-09-09 11:08:40 +09:00
host - > caps & = ~ SDHCI_CAN_64BIT ;
2016-06-16 12:47:16 -04:00
host - > caps1 = sdhci_readl ( host , SDHCI_CAPABILITIES_1 ) ;
host - > caps1 & = ~ ( SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
2020-01-13 16:07:05 -05:00
SDHCI_SUPPORT_DDR50 ) ;
2020-01-13 16:07:02 -05:00
host - > quirks | = SDHCI_QUIRK_MISSING_CAPS ;
if ( match_priv - > flags & BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT )
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2016-06-16 12:47:16 -04:00
2020-01-13 16:07:05 -05:00
res = sdhci_brcmstb_add_host ( host , priv ) ;
2016-06-16 12:47:16 -04:00
if ( res )
goto err ;
pltfm_host - > clk = clk ;
return res ;
err :
sdhci_pltfm_free ( pdev ) ;
err_clk :
clk_disable_unprepare ( clk ) ;
return res ;
}
2020-01-13 16:07:04 -05:00
static void sdhci_brcmstb_shutdown ( struct platform_device * pdev )
{
2021-01-07 17:15:09 -05:00
sdhci_pltfm_suspend ( & pdev - > dev ) ;
2020-01-13 16:07:04 -05:00
}
2016-06-16 12:47:16 -04:00
MODULE_DEVICE_TABLE ( of , sdhci_brcm_of_match ) ;
static struct platform_driver sdhci_brcmstb_driver = {
. driver = {
. name = " sdhci-brcmstb " ,
2020-09-03 16:24:37 -07:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2017-08-23 13:15:02 +09:00
. pm = & sdhci_pltfm_pmops ,
2016-06-16 12:47:16 -04:00
. of_match_table = of_match_ptr ( sdhci_brcm_of_match ) ,
} ,
. probe = sdhci_brcmstb_probe ,
. remove = sdhci_pltfm_unregister ,
2020-01-13 16:07:04 -05:00
. shutdown = sdhci_brcmstb_shutdown ,
2016-06-16 12:47:16 -04:00
} ;
module_platform_driver ( sdhci_brcmstb_driver ) ;
MODULE_DESCRIPTION ( " SDHCI driver for Broadcom BRCMSTB SoCs " ) ;
MODULE_AUTHOR ( " Broadcom " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;