2019-05-29 07:17:56 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2011-06-08 17:41:58 +08:00
/*
* Copyright ( C ) 2010 Marvell International Ltd .
* Zhangfei Gao < zhangfei . gao @ marvell . com >
* Kevin Wang < dwang4 @ marvell . com >
* Jun Nie < njun @ marvell . com >
* Qiming Wu < wuqm @ marvell . com >
* Philip Rakity < prakity @ marvell . com >
*/
# include <linux/err.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2011-07-03 15:15:51 -04:00
# include <linux/module.h>
2011-06-08 17:41:58 +08:00
# include <linux/io.h>
# include <linux/mmc/card.h>
# include <linux/mmc/host.h>
2011-06-20 22:11:52 +08:00
# include <linux/platform_data/pxa_sdhci.h>
2011-06-08 17:41:58 +08:00
# include <linux/slab.h>
2012-04-10 22:34:33 -04:00
# include <linux/of.h>
# include <linux/of_device.h>
2023-01-16 11:43:59 -08:00
# include <linux/mmc/sdio.h>
# include <linux/mmc/mmc.h>
2023-01-16 11:44:00 -08:00
# include <linux/pinctrl/consumer.h>
2012-04-10 22:34:33 -04:00
2011-06-08 17:41:58 +08:00
# include "sdhci.h"
# include "sdhci-pltfm.h"
# define SD_FIFO_PARAM 0xe0
# define DIS_PAD_SD_CLK_GATE 0x0400 /* Turn on/off Dynamic SD Clock Gating */
# define CLK_GATE_ON 0x0200 /* Disable/enable Clock Gate */
# define CLK_GATE_CTL 0x0100 /* Clock Gate Control */
# define CLK_GATE_SETTING_BITS (DIS_PAD_SD_CLK_GATE | \
CLK_GATE_ON | CLK_GATE_CTL )
# define SD_CLOCK_BURST_SIZE_SETUP 0xe6
# define SDCLK_SEL_SHIFT 8
# define SDCLK_SEL_MASK 0x3
# define SDCLK_DELAY_SHIFT 10
# define SDCLK_DELAY_MASK 0x3c
# define SD_CE_ATA_2 0xea
# define MMC_CARD 0x1000
# define MMC_WIDTH 0x0100
2023-01-16 11:43:59 -08:00
struct sdhci_pxav2_host {
struct mmc_request * sdio_mrq ;
2023-01-16 11:44:00 -08:00
struct pinctrl * pinctrl ;
struct pinctrl_state * pins_default ;
struct pinctrl_state * pins_cmd_gpio ;
2023-01-16 11:43:59 -08:00
} ;
2014-04-25 12:57:12 +01:00
static void pxav2_reset ( struct sdhci_host * host , u8 mask )
2011-06-08 17:41:58 +08:00
{
struct platform_device * pdev = to_platform_device ( mmc_dev ( host - > mmc ) ) ;
struct sdhci_pxa_platdata * pdata = pdev - > dev . platform_data ;
2014-04-25 12:57:12 +01:00
sdhci_reset ( host , mask ) ;
2011-06-08 17:41:58 +08:00
if ( mask = = SDHCI_RESET_ALL ) {
u16 tmp = 0 ;
/*
* tune timing of read data / command when crc error happen
* no performance impact
*/
2011-09-14 11:29:02 +05:30
if ( pdata & & pdata - > clk_delay_sel = = 1 ) {
2011-06-08 17:41:58 +08:00
tmp = readw ( host - > ioaddr + SD_CLOCK_BURST_SIZE_SETUP ) ;
tmp & = ~ ( SDCLK_DELAY_MASK < < SDCLK_DELAY_SHIFT ) ;
tmp | = ( pdata - > clk_delay_cycles & SDCLK_DELAY_MASK )
< < SDCLK_DELAY_SHIFT ;
tmp & = ~ ( SDCLK_SEL_MASK < < SDCLK_SEL_SHIFT ) ;
tmp | = ( 1 & SDCLK_SEL_MASK ) < < SDCLK_SEL_SHIFT ;
writew ( tmp , host - > ioaddr + SD_CLOCK_BURST_SIZE_SETUP ) ;
}
2011-09-14 11:29:02 +05:30
if ( pdata & & ( pdata - > flags & PXA_FLAG_ENABLE_CLOCK_GATING ) ) {
2011-06-08 17:41:58 +08:00
tmp = readw ( host - > ioaddr + SD_FIFO_PARAM ) ;
tmp & = ~ CLK_GATE_SETTING_BITS ;
writew ( tmp , host - > ioaddr + SD_FIFO_PARAM ) ;
} else {
tmp = readw ( host - > ioaddr + SD_FIFO_PARAM ) ;
tmp & = ~ CLK_GATE_SETTING_BITS ;
tmp | = CLK_GATE_SETTING_BITS ;
writew ( tmp , host - > ioaddr + SD_FIFO_PARAM ) ;
}
}
}
2023-01-16 11:43:56 -08:00
static u16 pxav1_readw ( struct sdhci_host * host , int reg )
{
/* Workaround for data abort exception on SDH2 and SDH4 on PXA168 */
if ( reg = = SDHCI_HOST_VERSION )
return readl ( host - > ioaddr + SDHCI_HOST_VERSION - 2 ) > > 16 ;
return readw ( host - > ioaddr + reg ) ;
}
2023-01-16 11:43:59 -08:00
static u32 pxav1_irq ( struct sdhci_host * host , u32 intmask )
{
struct sdhci_pxav2_host * pxav2_host = sdhci_pltfm_priv ( sdhci_priv ( host ) ) ;
struct mmc_request * sdio_mrq ;
if ( pxav2_host - > sdio_mrq & & ( intmask & SDHCI_INT_CMD_MASK ) ) {
/* The dummy CMD0 for the SDIO workaround just completed */
sdhci_writel ( host , intmask & SDHCI_INT_CMD_MASK , SDHCI_INT_STATUS ) ;
intmask & = ~ SDHCI_INT_CMD_MASK ;
2023-01-16 11:44:00 -08:00
/* Restore MMC function to CMD pin */
if ( pxav2_host - > pinctrl & & pxav2_host - > pins_default )
pinctrl_select_state ( pxav2_host - > pinctrl , pxav2_host - > pins_default ) ;
2023-01-16 11:43:59 -08:00
sdio_mrq = pxav2_host - > sdio_mrq ;
pxav2_host - > sdio_mrq = NULL ;
mmc_request_done ( host - > mmc , sdio_mrq ) ;
}
return intmask ;
}
static void pxav1_request_done ( struct sdhci_host * host , struct mmc_request * mrq )
{
u16 tmp ;
struct sdhci_pxav2_host * pxav2_host ;
/* If this is an SDIO command, perform errata workaround for silicon bug */
if ( mrq - > cmd & & ! mrq - > cmd - > error & &
( mrq - > cmd - > opcode = = SD_IO_RW_DIRECT | |
mrq - > cmd - > opcode = = SD_IO_RW_EXTENDED ) ) {
/* Reset data port */
tmp = readw ( host - > ioaddr + SDHCI_TIMEOUT_CONTROL ) ;
tmp | = 0x400 ;
writew ( tmp , host - > ioaddr + SDHCI_TIMEOUT_CONTROL ) ;
/* Clock is now stopped, so restart it by sending a dummy CMD0 */
pxav2_host = sdhci_pltfm_priv ( sdhci_priv ( host ) ) ;
pxav2_host - > sdio_mrq = mrq ;
2023-01-16 11:44:00 -08:00
/* Set CMD as high output rather than MMC function while we do CMD0 */
if ( pxav2_host - > pinctrl & & pxav2_host - > pins_cmd_gpio )
pinctrl_select_state ( pxav2_host - > pinctrl , pxav2_host - > pins_cmd_gpio ) ;
2023-01-16 11:43:59 -08:00
sdhci_writel ( host , 0 , SDHCI_ARGUMENT ) ;
sdhci_writew ( host , 0 , SDHCI_TRANSFER_MODE ) ;
sdhci_writew ( host , SDHCI_MAKE_CMD ( MMC_GO_IDLE_STATE , SDHCI_CMD_RESP_NONE ) ,
SDHCI_COMMAND ) ;
/* Don't finish this request until the dummy CMD0 finishes */
return ;
}
mmc_request_done ( host - > mmc , mrq ) ;
}
2014-04-25 12:57:07 +01:00
static void pxav2_mmc_set_bus_width ( struct sdhci_host * host , int width )
2011-06-08 17:41:58 +08:00
{
u8 ctrl ;
u16 tmp ;
ctrl = readb ( host - > ioaddr + SDHCI_HOST_CONTROL ) ;
tmp = readw ( host - > ioaddr + SD_CE_ATA_2 ) ;
if ( width = = MMC_BUS_WIDTH_8 ) {
ctrl & = ~ SDHCI_CTRL_4BITBUS ;
tmp | = MMC_CARD | MMC_WIDTH ;
} else {
tmp & = ~ ( MMC_CARD | MMC_WIDTH ) ;
if ( width = = MMC_BUS_WIDTH_4 )
ctrl | = SDHCI_CTRL_4BITBUS ;
else
ctrl & = ~ SDHCI_CTRL_4BITBUS ;
}
writew ( tmp , host - > ioaddr + SD_CE_ATA_2 ) ;
writeb ( ctrl , host - > ioaddr + SDHCI_HOST_CONTROL ) ;
}
2023-01-16 11:43:54 -08:00
struct sdhci_pxa_variant {
const struct sdhci_ops * ops ;
unsigned int extra_quirks ;
} ;
static const struct sdhci_ops pxav1_sdhci_ops = {
2023-01-16 11:43:56 -08:00
. read_w = pxav1_readw ,
2023-01-16 11:43:54 -08:00
. set_clock = sdhci_set_clock ,
2023-01-16 11:43:59 -08:00
. irq = pxav1_irq ,
2023-01-16 11:43:54 -08:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
. set_bus_width = pxav2_mmc_set_bus_width ,
. reset = pxav2_reset ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2023-01-16 11:43:59 -08:00
. request_done = pxav1_request_done ,
2023-01-16 11:43:54 -08:00
} ;
static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = {
. ops = & pxav1_sdhci_ops ,
. extra_quirks = SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_32BIT_DMA_SIZE ,
} ;
2013-03-13 19:26:05 +01:00
static const struct sdhci_ops pxav2_sdhci_ops = {
2014-04-25 12:58:55 +01:00
. set_clock = sdhci_set_clock ,
2013-01-28 19:27:12 +01:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
2014-04-25 12:57:07 +01:00
. set_bus_width = pxav2_mmc_set_bus_width ,
2014-04-25 12:57:12 +01:00
. reset = pxav2_reset ,
2014-04-25 12:59:26 +01:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2011-06-08 17:41:58 +08:00
} ;
2023-01-16 11:43:54 -08:00
static const struct sdhci_pxa_variant pxav2_variant = {
. ops = & pxav2_sdhci_ops ,
} ;
2012-04-10 22:34:33 -04:00
# ifdef CONFIG_OF
static const struct of_device_id sdhci_pxav2_of_match [ ] = {
2023-01-16 11:43:54 -08:00
{ . compatible = " mrvl,pxav1-mmc " , . data = & pxav1_variant , } ,
{ . compatible = " mrvl,pxav2-mmc " , . data = & pxav2_variant , } ,
2012-04-10 22:34:33 -04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sdhci_pxav2_of_match ) ;
static struct sdhci_pxa_platdata * pxav2_get_mmc_pdata ( struct device * dev )
{
struct sdhci_pxa_platdata * pdata ;
struct device_node * np = dev - > of_node ;
u32 bus_width ;
u32 clk_delay_cycles ;
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ;
if ( of_find_property ( np , " non-removable " , NULL ) )
pdata - > flags | = PXA_FLAG_CARD_PERMANENT ;
of_property_read_u32 ( np , " bus-width " , & bus_width ) ;
if ( bus_width = = 8 )
pdata - > flags | = PXA_FLAG_SD_8_BIT_CAPABLE_SLOT ;
of_property_read_u32 ( np , " mrvl,clk-delay-cycles " , & clk_delay_cycles ) ;
if ( clk_delay_cycles > 0 ) {
pdata - > clk_delay_sel = 1 ;
pdata - > clk_delay_cycles = clk_delay_cycles ;
}
return pdata ;
}
# else
static inline struct sdhci_pxa_platdata * pxav2_get_mmc_pdata ( struct device * dev )
{
return NULL ;
}
# endif
2012-11-19 13:23:06 -05:00
static int sdhci_pxav2_probe ( struct platform_device * pdev )
2011-06-08 17:41:58 +08:00
{
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_pxa_platdata * pdata = pdev - > dev . platform_data ;
2023-01-16 11:44:00 -08:00
struct sdhci_pxav2_host * pxav2_host ;
2011-06-08 17:41:58 +08:00
struct device * dev = & pdev - > dev ;
struct sdhci_host * host = NULL ;
2023-01-16 11:43:54 -08:00
const struct sdhci_pxa_variant * variant ;
2012-04-10 22:34:33 -04:00
2011-06-08 17:41:58 +08:00
int ret ;
2023-01-16 11:43:58 -08:00
struct clk * clk , * clk_core ;
2011-06-08 17:41:58 +08:00
2023-01-16 11:44:00 -08:00
host = sdhci_pltfm_init ( pdev , NULL , sizeof ( * pxav2_host ) ) ;
2014-10-21 11:22:33 +02:00
if ( IS_ERR ( host ) )
2011-06-08 17:41:58 +08:00
return PTR_ERR ( host ) ;
2014-10-21 11:22:33 +02:00
2011-06-08 17:41:58 +08:00
pltfm_host = sdhci_priv ( host ) ;
2023-01-16 11:44:00 -08:00
pxav2_host = sdhci_pltfm_priv ( pltfm_host ) ;
2011-06-08 17:41:58 +08:00
2023-01-16 11:43:57 -08:00
clk = devm_clk_get ( dev , " io " ) ;
if ( IS_ERR ( clk ) & & PTR_ERR ( clk ) ! = - EPROBE_DEFER )
clk = devm_clk_get ( dev , NULL ) ;
2011-06-08 17:41:58 +08:00
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
2023-01-16 11:43:57 -08:00
dev_err_probe ( dev , ret , " failed to get io clock \n " ) ;
2017-08-23 13:15:01 +09:00
goto free ;
2011-06-08 17:41:58 +08:00
}
pltfm_host - > clk = clk ;
2017-02-11 01:54:46 +03:00
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
2023-01-16 11:43:57 -08:00
dev_err ( dev , " failed to enable io clock \n " ) ;
2017-08-23 13:15:01 +09:00
goto free ;
2017-02-11 01:54:46 +03:00
}
2011-06-08 17:41:58 +08:00
2023-01-16 11:43:58 -08:00
clk_core = devm_clk_get_optional_enabled ( dev , " core " ) ;
if ( IS_ERR ( clk_core ) ) {
ret = PTR_ERR ( clk_core ) ;
dev_err_probe ( dev , ret , " failed to enable core clock \n " ) ;
goto disable_clk ;
}
2011-06-08 17:41:58 +08:00
host - > quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ;
2023-01-16 11:43:54 -08:00
variant = of_device_get_match_data ( dev ) ;
if ( variant )
2012-04-10 22:34:33 -04:00
pdata = pxav2_get_mmc_pdata ( dev ) ;
2023-01-16 11:43:54 -08:00
else
variant = & pxav2_variant ;
2011-06-08 17:41:58 +08:00
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 ;
}
2023-01-16 11:43:54 -08:00
host - > quirks | = variant - > extra_quirks ;
host - > ops = variant - > ops ;
2011-06-08 17:41:58 +08:00
2023-01-16 11:44:00 -08:00
/* Set up optional pinctrl for PXA168 SDIO IRQ fix */
pxav2_host - > pinctrl = devm_pinctrl_get ( dev ) ;
if ( ! IS_ERR ( pxav2_host - > pinctrl ) ) {
pxav2_host - > pins_cmd_gpio = pinctrl_lookup_state ( pxav2_host - > pinctrl ,
" state_cmd_gpio " ) ;
if ( IS_ERR ( pxav2_host - > pins_cmd_gpio ) )
pxav2_host - > pins_cmd_gpio = NULL ;
pxav2_host - > pins_default = pinctrl_lookup_state ( pxav2_host - > pinctrl ,
" default " ) ;
if ( IS_ERR ( pxav2_host - > pins_default ) )
pxav2_host - > pins_default = NULL ;
} else {
pxav2_host - > pinctrl = NULL ;
}
2011-06-08 17:41:58 +08:00
ret = sdhci_add_host ( host ) ;
2018-05-25 15:15:09 +08:00
if ( ret )
2017-08-23 13:15:01 +09:00
goto disable_clk ;
2011-06-08 17:41:58 +08:00
return 0 ;
2017-08-23 13:15:01 +09:00
disable_clk :
2012-07-31 14:35:25 +08:00
clk_disable_unprepare ( clk ) ;
2017-08-23 13:15:01 +09:00
free :
2011-06-08 17:41:58 +08:00
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static struct platform_driver sdhci_pxav2_driver = {
. driver = {
. name = " sdhci-pxav2 " ,
2020-09-03 16:24:36 -07:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2015-05-05 17:11:54 +08:00
. of_match_table = of_match_ptr ( sdhci_pxav2_of_match ) ,
2016-07-27 13:07:21 +02:00
. pm = & sdhci_pltfm_pmops ,
2011-06-08 17:41:58 +08:00
} ,
. probe = sdhci_pxav2_probe ,
2017-08-23 13:15:01 +09:00
. remove = sdhci_pltfm_unregister ,
2011-06-08 17:41:58 +08:00
} ;
2011-11-26 12:55:43 +08:00
module_platform_driver ( sdhci_pxav2_driver ) ;
2011-06-08 17:41:58 +08:00
MODULE_DESCRIPTION ( " SDHCI driver for pxav2 " ) ;
MODULE_AUTHOR ( " Marvell International Ltd. " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;