2013-03-21 16:27:19 +08:00
/*
* SDHCI support for SiRF primaII and marco SoCs
*
* Copyright ( c ) 2011 Cambridge Silicon Radio Limited , a CSR plc group company .
*
* Licensed under GPLv2 or later .
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/mmc/host.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_gpio.h>
# include <linux/mmc/slot-gpio.h>
# include "sdhci-pltfm.h"
2014-12-04 20:09:20 +08:00
# define SDHCI_CLK_DELAY_SETTING 0x4C
2014-08-26 10:50:42 +08:00
# define SDHCI_SIRF_8BITBUS BIT(3)
2015-04-27 08:15:13 +00:00
# define SIRF_TUNING_COUNT 16384
2014-08-26 10:50:42 +08:00
2013-03-21 16:27:19 +08:00
struct sdhci_sirf_priv {
int gpio_cd ;
} ;
2014-08-26 10:50:42 +08:00
static void sdhci_sirf_set_bus_width ( struct sdhci_host * host , int width )
{
u8 ctrl ;
ctrl = sdhci_readb ( host , SDHCI_HOST_CONTROL ) ;
ctrl & = ~ ( SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS ) ;
/*
* CSR atlas7 and prima2 SD host version is not 3.0
* 8 bit - width enable bit of CSR SD hosts is 3 ,
* while stardard hosts use bit 5
*/
if ( width = = MMC_BUS_WIDTH_8 )
ctrl | = SDHCI_SIRF_8BITBUS ;
else if ( width = = MMC_BUS_WIDTH_4 )
ctrl | = SDHCI_CTRL_4BITBUS ;
sdhci_writeb ( host , ctrl , SDHCI_HOST_CONTROL ) ;
}
2015-04-27 08:15:14 +00:00
static u32 sdhci_sirf_readl_le ( struct sdhci_host * host , int reg )
{
u32 val = readl ( host - > ioaddr + reg ) ;
if ( unlikely ( ( reg = = SDHCI_CAPABILITIES_1 ) & &
( host - > mmc - > caps & MMC_CAP_UHS_SDR50 ) ) ) {
/* fake CAP_1 register */
2015-10-04 12:04:13 +00:00
val = SDHCI_SUPPORT_DDR50 |
SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING ;
2015-04-27 08:15:14 +00:00
}
if ( unlikely ( reg = = SDHCI_SLOT_INT_STATUS ) ) {
u32 prss = val ;
/* fake chips as V3.0 host conreoller */
prss & = ~ ( 0xFF < < 16 ) ;
val = prss | ( SDHCI_SPEC_300 < < 16 ) ;
}
return val ;
}
static u16 sdhci_sirf_readw_le ( struct sdhci_host * host , int reg )
{
u16 ret = 0 ;
ret = readw ( host - > ioaddr + reg ) ;
if ( unlikely ( reg = = SDHCI_HOST_VERSION ) ) {
ret = readw ( host - > ioaddr + SDHCI_HOST_VERSION ) ;
ret | = SDHCI_SPEC_300 ;
}
return ret ;
}
2014-12-04 20:09:20 +08:00
static int sdhci_sirf_execute_tuning ( struct sdhci_host * host , u32 opcode )
{
int tuning_seq_cnt = 3 ;
2015-04-27 08:15:13 +00:00
int phase ;
2014-12-04 20:09:20 +08:00
u8 tuned_phase_cnt = 0 ;
2015-02-15 23:43:51 +08:00
int rc = 0 , longest_range = 0 ;
2014-12-04 20:09:20 +08:00
int start = - 1 , end = 0 , tuning_value = - 1 , range = 0 ;
u16 clock_setting ;
struct mmc_host * mmc = host - > mmc ;
clock_setting = sdhci_readw ( host , SDHCI_CLK_DELAY_SETTING ) ;
clock_setting & = ~ 0x3fff ;
retry :
phase = 0 ;
2015-04-27 08:15:13 +00:00
tuned_phase_cnt = 0 ;
2014-12-04 20:09:20 +08:00
do {
sdhci_writel ( host ,
2015-02-15 23:43:51 +08:00
clock_setting | phase ,
2014-12-04 20:09:20 +08:00
SDHCI_CLK_DELAY_SETTING ) ;
2015-10-27 14:24:28 +08:00
if ( ! mmc_send_tuning ( mmc , opcode , NULL ) ) {
2014-12-04 20:09:20 +08:00
/* Tuning is successful at this tuning point */
2015-04-27 08:15:13 +00:00
tuned_phase_cnt + + ;
2014-12-04 20:09:20 +08:00
dev_dbg ( mmc_dev ( mmc ) , " %s: Found good phase = %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
if ( start = = - 1 )
start = phase ;
end = phase ;
range + + ;
if ( phase = = ( SIRF_TUNING_COUNT - 1 )
& & range > longest_range )
tuning_value = ( start + end ) / 2 ;
} else {
dev_dbg ( mmc_dev ( mmc ) , " %s: Found bad phase = %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
if ( range > longest_range ) {
tuning_value = ( start + end ) / 2 ;
longest_range = range ;
}
start = - 1 ;
end = range = 0 ;
}
2015-04-27 08:15:13 +00:00
} while ( + + phase < SIRF_TUNING_COUNT ) ;
2014-12-04 20:09:20 +08:00
if ( tuned_phase_cnt & & tuning_value > 0 ) {
/*
* Finally set the selected phase in delay
* line hw block .
*/
phase = tuning_value ;
sdhci_writel ( host ,
2015-02-15 23:43:51 +08:00
clock_setting | phase ,
2014-12-04 20:09:20 +08:00
SDHCI_CLK_DELAY_SETTING ) ;
dev_dbg ( mmc_dev ( mmc ) , " %s: Setting the tuning phase to %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
} else {
if ( - - tuning_seq_cnt )
goto retry ;
/* Tuning failed */
dev_dbg ( mmc_dev ( mmc ) , " %s: No tuning point found \n " ,
mmc_hostname ( mmc ) ) ;
rc = - EIO ;
}
return rc ;
}
2017-08-07 11:50:42 +02:00
static const struct sdhci_ops sdhci_sirf_ops = {
2015-04-27 08:15:14 +00:00
. read_l = sdhci_sirf_readl_le ,
. read_w = sdhci_sirf_readw_le ,
2014-12-04 20:09:20 +08:00
. platform_execute_tuning = sdhci_sirf_execute_tuning ,
2014-04-25 12:58:55 +01:00
. set_clock = sdhci_set_clock ,
2015-02-27 15:47:28 +08:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
2014-08-26 10:50:42 +08:00
. set_bus_width = sdhci_sirf_set_bus_width ,
2014-04-25 12:57:12 +01:00
. reset = sdhci_reset ,
2014-04-25 12:59:26 +01:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2013-03-21 16:27:19 +08:00
} ;
2017-08-07 11:50:42 +02:00
static const struct sdhci_pltfm_data sdhci_sirf_pdata = {
2013-03-21 16:27:19 +08:00
. ops = & sdhci_sirf_ops ,
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
2015-08-12 06:59:33 +00:00
SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS ,
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
2013-03-21 16:27:19 +08:00
} ;
static int sdhci_sirf_probe ( struct platform_device * pdev )
{
struct sdhci_host * host ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_sirf_priv * priv ;
2013-06-27 11:17:25 -04:00
struct clk * clk ;
int gpio_cd ;
2013-03-21 16:27:19 +08:00
int ret ;
2013-06-27 11:17:25 -04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( clk ) ) {
2013-03-21 16:27:19 +08:00
dev_err ( & pdev - > dev , " unable to get clock " ) ;
2013-06-27 11:17:25 -04:00
return PTR_ERR ( clk ) ;
2013-03-21 16:27:19 +08:00
}
2013-06-27 11:17:25 -04:00
if ( pdev - > dev . of_node )
gpio_cd = of_get_named_gpio ( pdev - > dev . of_node , " cd-gpios " , 0 ) ;
else
gpio_cd = - EINVAL ;
2013-03-21 16:27:19 +08:00
2013-06-27 11:17:25 -04:00
host = sdhci_pltfm_init ( pdev , & sdhci_sirf_pdata , sizeof ( struct sdhci_sirf_priv ) ) ;
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
2013-03-21 16:27:19 +08:00
pltfm_host = sdhci_priv ( host ) ;
2015-02-27 15:47:28 +08:00
pltfm_host - > clk = clk ;
2013-06-27 11:17:25 -04:00
priv = sdhci_pltfm_priv ( pltfm_host ) ;
priv - > gpio_cd = gpio_cd ;
2013-03-21 16:27:19 +08:00
sdhci_get_of_property ( pdev ) ;
2015-02-27 15:47:28 +08:00
ret = clk_prepare_enable ( pltfm_host - > clk ) ;
2013-06-27 11:17:25 -04:00
if ( ret )
goto err_clk_prepare ;
2013-03-21 16:27:19 +08:00
ret = sdhci_add_host ( host ) ;
if ( ret )
goto err_sdhci_add ;
/*
* We must request the IRQ after sdhci_add_host ( ) , as the tasklet only
* gets setup in sdhci_add_host ( ) and we oops .
*/
if ( gpio_is_valid ( priv - > gpio_cd ) ) {
2013-08-08 12:38:31 +02:00
ret = mmc_gpio_request_cd ( host - > mmc , priv - > gpio_cd , 0 ) ;
2013-03-21 16:27:19 +08:00
if ( ret ) {
dev_err ( & pdev - > dev , " card detect irq request failed: %d \n " ,
ret ) ;
goto err_request_cd ;
}
mmc: don't request CD IRQ until mmc_start_host()
As soon as the CD IRQ is requested, it can trigger, since it's an
externally controlled event. If it does, delayed_work host->detect will
be scheduled.
Many host controller probe()s are roughly structured as:
*_probe() {
host = sdhci_pltfm_init();
mmc_of_parse(host->mmc);
rc = sdhci_add_host(host);
if (rc) {
sdhci_pltfm_free();
return rc;
}
In 3.17, CD IRQs can are enabled quite early via *_probe() ->
mmc_of_parse() -> mmc_gpio_request_cd() -> mmc_gpiod_request_cd_irq().
Note that in linux-next, mmc_of_parse() calls mmc_gpio*d*_request_cd()
rather than mmc_gpio_request_cd(), and mmc_gpio*d*_request_cd() doesn't
call mmc_gpiod_request_cd_irq(). However, this issue still exists if
mmc_gpio_request_cd() is called directly before mmc_start_host().
sdhci_add_host() may fail part way through (e.g. due to deferred
probe for a vmmc regulator), and sdhci_pltfm_free() does nothing to
unrequest the CD IRQ nor cancel the delayed_work. sdhci_pltfm_free() is
coded to assume that if sdhci_add_host() failed, then the delayed_work
cannot (or should not) have been triggered.
This can lead to the following with CONFIG_DEBUG_OBJECTS_* enabled, when
kfree(host) is eventually called inside sdhci_pltfm_free():
WARNING: CPU: 2 PID: 6 at lib/debugobjects.c:263 debug_print_object+0x8c/0xb4()
ODEBUG: free active (active state 0) object type: timer_list hint: delayed_work_timer_fn+0x0/0x18
The object being complained about is host->detect.
There's no need to request the CD IRQ so early; mmc_start_host() already
requests it. For most SDHCI hosts at least, the typical call path that
does this is: *_probe() -> sdhci_add_host() -> mmc_add_host() ->
mmc_start_host(). Therefore, remove the call to mmc_gpiod_request_cd_irq()
from mmc_gpio_request_cd(). This also matches mmc_gpio*d*_request_cd(),
which already doesn't call mmc_gpiod_request_cd_irq().
However, some host controller drivers call mmc_gpio_request_cd() after
mmc_start_host() has already been called, and assume that this will also
call mmc_gpiod_request_cd_irq(). Update those drivers to explicitly call
mmc_gpiod_request_cd_irq() themselves. Ideally, these drivers should be
modified to move their call to mmc_gpio_request_cd() before their call
to mmc_add_host(). However that's too large a change for stable.
This solves the problem (eliminates the kernel error message above),
since it guarantees that the IRQ can't trigger before mmc_start_host()
is called.
The critical point here is that once sdhci_add_host() calls
mmc_add_host() -> mmc_start_host(), sdhci_add_host() is coded not to
fail. In other words, if there's a chance that mmc_start_host() may have
been called, and CD IRQs triggered, and the delayed_work scheduled,
sdhci_add_host() won't fail, and so cleanup is no longer via
sdhci_pltfm_free() (which doesn't free the IRQ or cancel the work queue)
but instead must be via sdhci_remove_host(), which calls mmc_remove_host()
-> mmc_stop_host(), which does free the IRQ and cancel the work queue.
CC: Russell King <linux@arm.linux.org.uk>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: <stable@vger.kernel.org> # v3.15+
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2014-09-22 09:57:42 -06:00
mmc_gpiod_request_cd_irq ( host - > mmc ) ;
2013-03-21 16:27:19 +08:00
}
return 0 ;
err_request_cd :
sdhci_remove_host ( host , 0 ) ;
err_sdhci_add :
2015-02-27 15:47:28 +08:00
clk_disable_unprepare ( pltfm_host - > clk ) ;
2013-06-27 11:17:25 -04:00
err_clk_prepare :
2013-03-21 16:27:19 +08:00
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static const struct of_device_id sdhci_sirf_of_match [ ] = {
{ . compatible = " sirf,prima2-sdhc " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_sirf_of_match ) ;
static struct platform_driver sdhci_sirf_driver = {
. driver = {
. name = " sdhci-sirf " ,
. of_match_table = sdhci_sirf_of_match ,
2017-08-23 13:15:02 +09:00
. pm = & sdhci_pltfm_pmops ,
2013-03-21 16:27:19 +08:00
} ,
. probe = sdhci_sirf_probe ,
2015-02-27 15:47:31 +08:00
. remove = sdhci_pltfm_unregister ,
2013-03-21 16:27:19 +08:00
} ;
module_platform_driver ( sdhci_sirf_driver ) ;
MODULE_DESCRIPTION ( " SDHCI driver for SiRFprimaII/SiRFmarco " ) ;
MODULE_AUTHOR ( " Barry Song <21cnbao@gmail.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;