2012-11-24 00:17:34 +04:00
/*
* Secure Digital Host Controller Interface ACPI driver .
*
* Copyright ( c ) 2012 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
*/
# include <linux/init.h>
# include <linux/export.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/ioport.h>
# include <linux/io.h>
# include <linux/dma-mapping.h>
# include <linux/compiler.h>
# include <linux/stddef.h>
# include <linux/bitops.h>
# include <linux/types.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/acpi.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
2013-06-13 12:50:27 +04:00
# include <linux/delay.h>
2012-11-24 00:17:34 +04:00
# include <linux/mmc/host.h>
# include <linux/mmc/pm.h>
2014-03-10 17:02:42 +04:00
# include <linux/mmc/slot-gpio.h>
2012-11-24 00:17:34 +04:00
2016-04-15 14:06:57 +03:00
# ifdef CONFIG_X86
# include <asm/cpu_device_id.h>
2016-06-03 03:19:51 +03:00
# include <asm/intel-family.h>
2016-04-15 14:06:57 +03:00
# include <asm/iosf_mbi.h>
2017-06-21 15:08:39 +03:00
# include <linux/pci.h>
2016-04-15 14:06:57 +03:00
# endif
2012-11-24 00:17:34 +04:00
# include "sdhci.h"
enum {
2014-03-10 17:02:42 +04:00
SDHCI_ACPI_SD_CD = BIT ( 0 ) ,
SDHCI_ACPI_RUNTIME_PM = BIT ( 1 ) ,
SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL = BIT ( 2 ) ,
2012-11-24 00:17:34 +04:00
} ;
struct sdhci_acpi_chip {
const struct sdhci_ops * ops ;
unsigned int quirks ;
unsigned int quirks2 ;
unsigned long caps ;
unsigned int caps2 ;
mmc_pm_flag_t pm_caps ;
} ;
struct sdhci_acpi_slot {
const struct sdhci_acpi_chip * chip ;
unsigned int quirks ;
unsigned int quirks2 ;
unsigned long caps ;
unsigned int caps2 ;
mmc_pm_flag_t pm_caps ;
unsigned int flags ;
2014-10-06 16:23:06 +04:00
int ( * probe_slot ) ( struct platform_device * , const char * , const char * ) ;
2014-09-01 07:35:40 +04:00
int ( * remove_slot ) ( struct platform_device * ) ;
2012-11-24 00:17:34 +04:00
} ;
struct sdhci_acpi_host {
struct sdhci_host * host ;
const struct sdhci_acpi_slot * slot ;
struct platform_device * pdev ;
bool use_runtime_pm ;
} ;
static inline bool sdhci_acpi_flag ( struct sdhci_acpi_host * c , unsigned int flag )
{
return c - > slot & & ( c - > slot - > flags & flag ) ;
}
2013-06-13 12:50:27 +04:00
static void sdhci_acpi_int_hw_reset ( struct sdhci_host * host )
{
u8 reg ;
reg = sdhci_readb ( host , SDHCI_POWER_CONTROL ) ;
reg | = 0x10 ;
sdhci_writeb ( host , reg , SDHCI_POWER_CONTROL ) ;
/* For eMMC, minimum is 1us but give it 9us for good measure */
udelay ( 9 ) ;
reg & = ~ 0x10 ;
sdhci_writeb ( host , reg , SDHCI_POWER_CONTROL ) ;
/* For eMMC, minimum is 200us but give it 300us for good measure */
usleep_range ( 300 , 1000 ) ;
}
2012-11-24 00:17:34 +04:00
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
2014-04-25 15:58:55 +04:00
. set_clock = sdhci_set_clock ,
2014-04-25 15:57:07 +04:00
. set_bus_width = sdhci_set_bus_width ,
2014-04-25 15:57:12 +04:00
. reset = sdhci_reset ,
2014-04-25 15:59:26 +04:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2012-11-24 00:17:34 +04:00
} ;
2013-06-13 12:50:27 +04:00
static const struct sdhci_ops sdhci_acpi_ops_int = {
2014-04-25 15:58:55 +04:00
. set_clock = sdhci_set_clock ,
2014-04-25 15:57:07 +04:00
. set_bus_width = sdhci_set_bus_width ,
2014-04-25 15:57:12 +04:00
. reset = sdhci_reset ,
2014-04-25 15:59:26 +04:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2013-06-13 12:50:27 +04:00
. hw_reset = sdhci_acpi_int_hw_reset ,
} ;
static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
. ops = & sdhci_acpi_ops_int ,
} ;
2016-04-15 14:06:57 +03:00
# ifdef CONFIG_X86
static bool sdhci_acpi_byt ( void )
{
static const struct x86_cpu_id byt [ ] = {
2016-06-03 03:19:51 +03:00
{ X86_VENDOR_INTEL , 6 , INTEL_FAM6_ATOM_SILVERMONT1 } ,
2016-04-15 14:06:57 +03:00
{ }
} ;
return x86_match_cpu ( byt ) ;
}
2017-06-21 15:08:39 +03:00
static bool sdhci_acpi_cht ( void )
{
static const struct x86_cpu_id cht [ ] = {
{ X86_VENDOR_INTEL , 6 , INTEL_FAM6_ATOM_AIRMONT } ,
{ }
} ;
return x86_match_cpu ( cht ) ;
}
2016-04-15 14:06:57 +03:00
# define BYT_IOSF_SCCEP 0x63
# define BYT_IOSF_OCP_NETCTRL0 0x1078
# define BYT_IOSF_OCP_TIMEOUT_BASE GENMASK(10, 8)
static void sdhci_acpi_byt_setting ( struct device * dev )
{
u32 val = 0 ;
if ( ! sdhci_acpi_byt ( ) )
return ;
if ( iosf_mbi_read ( BYT_IOSF_SCCEP , MBI_CR_READ , BYT_IOSF_OCP_NETCTRL0 ,
& val ) ) {
dev_err ( dev , " %s read error \n " , __func__ ) ;
return ;
}
if ( ! ( val & BYT_IOSF_OCP_TIMEOUT_BASE ) )
return ;
val & = ~ BYT_IOSF_OCP_TIMEOUT_BASE ;
if ( iosf_mbi_write ( BYT_IOSF_SCCEP , MBI_CR_WRITE , BYT_IOSF_OCP_NETCTRL0 ,
val ) ) {
dev_err ( dev , " %s write error \n " , __func__ ) ;
return ;
}
dev_dbg ( dev , " %s completed \n " , __func__ ) ;
}
static bool sdhci_acpi_byt_defer ( struct device * dev )
{
if ( ! sdhci_acpi_byt ( ) )
return false ;
if ( ! iosf_mbi_available ( ) )
return true ;
sdhci_acpi_byt_setting ( dev ) ;
return false ;
}
2017-06-21 15:08:39 +03:00
static bool sdhci_acpi_cht_pci_wifi ( unsigned int vendor , unsigned int device ,
unsigned int slot , unsigned int parent_slot )
{
struct pci_dev * dev , * parent , * from = NULL ;
while ( 1 ) {
dev = pci_get_device ( vendor , device , from ) ;
pci_dev_put ( from ) ;
if ( ! dev )
break ;
parent = pci_upstream_bridge ( dev ) ;
if ( ACPI_COMPANION ( & dev - > dev ) & & PCI_SLOT ( dev - > devfn ) = = slot & &
parent & & PCI_SLOT ( parent - > devfn ) = = parent_slot & &
! pci_upstream_bridge ( parent ) ) {
pci_dev_put ( dev ) ;
return true ;
}
from = dev ;
}
return false ;
}
/*
* GPDwin uses PCI wifi which conflicts with SDIO ' s use of
* acpi_device_fix_up_power ( ) on child device nodes . Identifying GPDwin is
* problematic , but since SDIO is only used for wifi , the presence of the PCI
* wifi card in the expected slot with an ACPI companion node , is used to
* indicate that acpi_device_fix_up_power ( ) should be avoided .
*/
static inline bool sdhci_acpi_no_fixup_child_power ( const char * hid ,
const char * uid )
{
return sdhci_acpi_cht ( ) & &
! strcmp ( hid , " 80860F14 " ) & &
! strcmp ( uid , " 2 " ) & &
sdhci_acpi_cht_pci_wifi ( 0x14e4 , 0x43ec , 0 , 28 ) ;
}
2016-04-15 14:06:57 +03:00
# else
static inline void sdhci_acpi_byt_setting ( struct device * dev )
{
}
static inline bool sdhci_acpi_byt_defer ( struct device * dev )
{
return false ;
}
2017-06-21 15:08:39 +03:00
static inline bool sdhci_acpi_no_fixup_child_power ( const char * hid ,
const char * uid )
{
return false ;
}
2016-04-15 14:06:57 +03:00
# endif
2016-02-09 17:12:38 +03:00
static int bxt_get_cd ( struct mmc_host * mmc )
{
int gpio_cd = mmc_gpio_get_cd ( mmc ) ;
struct sdhci_host * host = mmc_priv ( mmc ) ;
unsigned long flags ;
int ret = 0 ;
if ( ! gpio_cd )
return 0 ;
spin_lock_irqsave ( & host - > lock , flags ) ;
if ( host - > flags & SDHCI_DEVICE_DEAD )
goto out ;
ret = ! ! ( sdhci_readl ( host , SDHCI_PRESENT_STATE ) & SDHCI_CARD_PRESENT ) ;
out :
spin_unlock_irqrestore ( & host - > lock , flags ) ;
return ret ;
}
2014-10-06 16:23:06 +04:00
static int sdhci_acpi_emmc_probe_slot ( struct platform_device * pdev ,
const char * hid , const char * uid )
2014-09-01 07:35:40 +04:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host ;
if ( ! c | | ! c - > host )
return 0 ;
host = c - > host ;
2015-01-12 20:37:25 +03:00
/* Platform specific code during emmc probe slot goes here */
2014-09-01 07:35:40 +04:00
2014-10-06 16:23:07 +04:00
if ( hid & & uid & & ! strcmp ( hid , " 80860F14 " ) & & ! strcmp ( uid , " 1 " ) & &
sdhci_readl ( host , SDHCI_CAPABILITIES ) = = 0x446cc8b2 & &
sdhci_readl ( host , SDHCI_CAPABILITIES_1 ) = = 0x00000807 )
host - > timeout_clk = 1000 ; /* 1000 kHz i.e. 1 MHz */
2014-09-01 07:35:40 +04:00
return 0 ;
}
2014-10-06 16:23:06 +04:00
static int sdhci_acpi_sdio_probe_slot ( struct platform_device * pdev ,
const char * hid , const char * uid )
2014-09-01 07:35:40 +04:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host ;
if ( ! c | | ! c - > host )
return 0 ;
host = c - > host ;
2015-01-12 20:37:25 +03:00
/* Platform specific code during sdio probe slot goes here */
2014-09-01 07:35:40 +04:00
return 0 ;
}
2014-10-06 16:23:06 +04:00
static int sdhci_acpi_sd_probe_slot ( struct platform_device * pdev ,
const char * hid , const char * uid )
2014-09-01 07:35:40 +04:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host ;
if ( ! c | | ! c - > host | | ! c - > slot )
return 0 ;
host = c - > host ;
2015-01-12 20:37:25 +03:00
/* Platform specific code during sd probe slot goes here */
2014-09-01 07:35:40 +04:00
2017-03-29 21:16:32 +03:00
if ( hid & & ! strcmp ( hid , " 80865ACA " ) )
2016-02-09 17:12:38 +03:00
host - > mmc_host_ops . get_cd = bxt_get_cd ;
2014-09-01 07:35:40 +04:00
return 0 ;
}
2013-04-26 12:27:22 +04:00
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
2013-06-13 12:50:27 +04:00
. chip = & sdhci_acpi_chip_int ,
2014-07-08 15:11:01 +04:00
. caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
2014-12-01 16:51:19 +03:00
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
2016-08-16 13:44:15 +03:00
MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY ,
2013-04-26 12:27:22 +04:00
. flags = SDHCI_ACPI_RUNTIME_PM ,
2014-12-01 16:51:17 +03:00
. quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC ,
2015-10-21 11:15:46 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC |
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 ,
2014-09-01 07:35:40 +04:00
. probe_slot = sdhci_acpi_emmc_probe_slot ,
2013-04-26 12:27:22 +04:00
} ;
2012-12-11 00:18:48 +04:00
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
2014-12-01 16:51:17 +03:00
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC ,
2012-12-11 00:18:48 +04:00
. quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON ,
2014-12-01 16:51:19 +03:00
. caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
2016-05-20 10:33:48 +03:00
MMC_CAP_WAIT_WHILE_BUSY ,
2012-12-11 00:18:48 +04:00
. flags = SDHCI_ACPI_RUNTIME_PM ,
. pm_caps = MMC_PM_KEEP_POWER ,
2014-09-01 07:35:40 +04:00
. probe_slot = sdhci_acpi_sdio_probe_slot ,
2012-12-11 00:18:48 +04:00
} ;
2013-04-26 12:27:22 +04:00
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
2014-03-10 17:02:42 +04:00
. flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
SDHCI_ACPI_RUNTIME_PM ,
2014-12-01 16:51:17 +03:00
. quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC ,
2014-09-24 11:27:28 +04:00
. quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC ,
2017-03-29 21:16:32 +03:00
. caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM ,
2014-09-01 07:35:40 +04:00
. probe_slot = sdhci_acpi_sd_probe_slot ,
2013-04-26 12:27:22 +04:00
} ;
2016-03-03 19:38:46 +03:00
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = {
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION ,
. quirks2 = SDHCI_QUIRK2_NO_1_8_V ,
. caps = MMC_CAP_NONREMOVABLE ,
} ;
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd = {
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION ,
. caps = MMC_CAP_NONREMOVABLE ,
} ;
2013-04-26 12:27:22 +04:00
struct sdhci_acpi_uid_slot {
const char * hid ;
const char * uid ;
const struct sdhci_acpi_slot * slot ;
} ;
static const struct sdhci_acpi_uid_slot sdhci_acpi_uids [ ] = {
2015-10-21 11:15:46 +03:00
{ " 80865ACA " , NULL , & sdhci_acpi_slot_int_sd } ,
{ " 80865ACC " , NULL , & sdhci_acpi_slot_int_emmc } ,
{ " 80865AD0 " , NULL , & sdhci_acpi_slot_int_sdio } ,
2013-04-26 12:27:22 +04:00
{ " 80860F14 " , " 1 " , & sdhci_acpi_slot_int_emmc } ,
2016-12-01 23:33:03 +03:00
{ " 80860F14 " , " 2 " , & sdhci_acpi_slot_int_sdio } ,
2013-04-26 12:27:22 +04:00
{ " 80860F14 " , " 3 " , & sdhci_acpi_slot_int_sd } ,
2014-03-10 17:02:43 +04:00
{ " 80860F16 " , NULL , & sdhci_acpi_slot_int_sd } ,
2013-04-26 12:27:22 +04:00
{ " INT33BB " , " 2 " , & sdhci_acpi_slot_int_sdio } ,
2014-09-24 11:27:29 +04:00
{ " INT33BB " , " 3 " , & sdhci_acpi_slot_int_sd } ,
2013-04-26 12:27:22 +04:00
{ " INT33C6 " , NULL , & sdhci_acpi_slot_int_sdio } ,
2013-11-12 14:01:33 +04:00
{ " INT3436 " , NULL , & sdhci_acpi_slot_int_sdio } ,
2015-01-05 15:47:57 +03:00
{ " INT344D " , NULL , & sdhci_acpi_slot_int_sdio } ,
2015-09-05 09:49:52 +03:00
{ " PNP0FFF " , " 3 " , & sdhci_acpi_slot_int_sd } ,
2013-04-26 12:27:22 +04:00
{ " PNP0D40 " } ,
2016-03-03 19:38:46 +03:00
{ " QCOM8051 " , NULL , & sdhci_acpi_slot_qcom_sd_3v } ,
{ " QCOM8052 " , NULL , & sdhci_acpi_slot_qcom_sd } ,
2013-04-26 12:27:22 +04:00
{ } ,
} ;
2012-11-24 00:17:34 +04:00
static const struct acpi_device_id sdhci_acpi_ids [ ] = {
2015-10-21 11:15:46 +03:00
{ " 80865ACA " } ,
{ " 80865ACC " } ,
{ " 80865AD0 " } ,
2013-04-26 12:27:22 +04:00
{ " 80860F14 " } ,
2014-03-10 17:02:43 +04:00
{ " 80860F16 " } ,
2013-04-26 12:27:22 +04:00
{ " INT33BB " } ,
{ " INT33C6 " } ,
2013-11-12 14:01:33 +04:00
{ " INT3436 " } ,
2015-01-05 15:47:57 +03:00
{ " INT344D " } ,
2013-04-26 12:27:22 +04:00
{ " PNP0D40 " } ,
2016-03-03 19:38:46 +03:00
{ " QCOM8051 " } ,
{ " QCOM8052 " } ,
2012-11-24 00:17:34 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , sdhci_acpi_ids ) ;
2014-10-06 16:23:05 +04:00
static const struct sdhci_acpi_slot * sdhci_acpi_get_slot ( const char * hid ,
const char * uid )
2012-11-24 00:17:34 +04:00
{
2013-04-26 12:27:22 +04:00
const struct sdhci_acpi_uid_slot * u ;
for ( u = sdhci_acpi_uids ; u - > hid ; u + + ) {
if ( strcmp ( u - > hid , hid ) )
continue ;
if ( ! u - > uid )
return u - > slot ;
if ( uid & & ! strcmp ( u - > uid , uid ) )
return u - > slot ;
}
2012-11-24 00:17:34 +04:00
return NULL ;
}
2012-12-22 03:05:47 +04:00
static int sdhci_acpi_probe ( struct platform_device * pdev )
2012-11-24 00:17:34 +04:00
{
struct device * dev = & pdev - > dev ;
acpi_handle handle = ACPI_HANDLE ( dev ) ;
2016-05-19 16:25:42 +03:00
struct acpi_device * device , * child ;
2012-11-24 00:17:34 +04:00
struct sdhci_acpi_host * c ;
struct sdhci_host * host ;
struct resource * iomem ;
resource_size_t len ;
const char * hid ;
2014-10-06 16:23:05 +04:00
const char * uid ;
2014-01-08 14:40:55 +04:00
int err ;
2012-11-24 00:17:34 +04:00
if ( acpi_bus_get_device ( handle , & device ) )
return - ENODEV ;
2017-06-21 15:08:39 +03:00
hid = acpi_device_hid ( device ) ;
uid = device - > pnp . unique_id ;
2016-05-19 16:25:42 +03:00
/* Power on the SDHCI controller and its children */
acpi_device_fix_up_power ( device ) ;
2017-06-21 15:08:39 +03:00
if ( ! sdhci_acpi_no_fixup_child_power ( hid , uid ) ) {
list_for_each_entry ( child , & device - > children , node )
if ( child - > status . present & & child - > status . enabled )
acpi_device_fix_up_power ( child ) ;
}
2016-05-19 16:25:42 +03:00
2016-04-15 14:06:57 +03:00
if ( sdhci_acpi_byt_defer ( dev ) )
return - EPROBE_DEFER ;
2012-11-24 00:17:34 +04:00
iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! iomem )
return - ENOMEM ;
len = resource_size ( iomem ) ;
if ( len < 0x100 )
dev_err ( dev , " Invalid iomem size! \n " ) ;
if ( ! devm_request_mem_region ( dev , iomem - > start , len , dev_name ( dev ) ) )
return - ENOMEM ;
host = sdhci_alloc_host ( dev , sizeof ( struct sdhci_acpi_host ) ) ;
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
c = sdhci_priv ( host ) ;
c - > host = host ;
2014-10-06 16:23:05 +04:00
c - > slot = sdhci_acpi_get_slot ( hid , uid ) ;
2012-11-24 00:17:34 +04:00
c - > pdev = pdev ;
c - > use_runtime_pm = sdhci_acpi_flag ( c , SDHCI_ACPI_RUNTIME_PM ) ;
platform_set_drvdata ( pdev , c ) ;
host - > hw_name = " ACPI " ;
host - > ops = & sdhci_acpi_ops_dflt ;
host - > irq = platform_get_irq ( pdev , 0 ) ;
host - > ioaddr = devm_ioremap_nocache ( dev , iomem - > start ,
resource_size ( iomem ) ) ;
if ( host - > ioaddr = = NULL ) {
err = - ENOMEM ;
goto err_free ;
}
if ( c - > slot ) {
2014-09-01 07:35:40 +04:00
if ( c - > slot - > probe_slot ) {
2014-10-06 16:23:06 +04:00
err = c - > slot - > probe_slot ( pdev , hid , uid ) ;
2014-09-01 07:35:40 +04:00
if ( err )
goto err_free ;
}
2012-11-24 00:17:34 +04:00
if ( c - > slot - > chip ) {
host - > ops = c - > slot - > chip - > ops ;
host - > quirks | = c - > slot - > chip - > quirks ;
host - > quirks2 | = c - > slot - > chip - > quirks2 ;
host - > mmc - > caps | = c - > slot - > chip - > caps ;
host - > mmc - > caps2 | = c - > slot - > chip - > caps2 ;
host - > mmc - > pm_caps | = c - > slot - > chip - > pm_caps ;
}
host - > quirks | = c - > slot - > quirks ;
host - > quirks2 | = c - > slot - > quirks2 ;
host - > mmc - > caps | = c - > slot - > caps ;
host - > mmc - > caps2 | = c - > slot - > caps2 ;
host - > mmc - > pm_caps | = c - > slot - > pm_caps ;
}
2013-04-04 17:41:06 +04:00
host - > mmc - > caps2 | = MMC_CAP2_NO_PRESCAN_POWERUP ;
2013-05-06 13:17:33 +04:00
if ( sdhci_acpi_flag ( c , SDHCI_ACPI_SD_CD ) ) {
2014-03-10 17:02:42 +04:00
bool v = sdhci_acpi_flag ( c , SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL ) ;
2017-01-18 12:46:18 +03:00
err = mmc_gpiod_request_cd ( host - > mmc , NULL , 0 , v , 0 , NULL ) ;
if ( err ) {
if ( err = = - EPROBE_DEFER )
goto err_free ;
2014-03-10 17:02:42 +04:00
dev_warn ( dev , " failed to setup card detect gpio \n " ) ;
2013-05-06 13:17:33 +04:00
c - > use_runtime_pm = false ;
2014-03-10 17:02:42 +04:00
}
2013-05-06 13:17:33 +04:00
}
2014-03-10 17:02:42 +04:00
err = sdhci_add_host ( host ) ;
if ( err )
goto err_free ;
2012-11-24 00:17:34 +04:00
if ( c - > use_runtime_pm ) {
2013-04-26 12:27:21 +04:00
pm_runtime_set_active ( dev ) ;
2012-11-24 00:17:34 +04:00
pm_suspend_ignore_children ( dev , 1 ) ;
pm_runtime_set_autosuspend_delay ( dev , 50 ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_enable ( dev ) ;
}
2016-01-22 07:35:26 +03:00
device_enable_async_suspend ( dev ) ;
2012-11-24 00:17:34 +04:00
return 0 ;
err_free :
sdhci_free_host ( c - > host ) ;
return err ;
}
2012-12-22 03:05:47 +04:00
static int sdhci_acpi_remove ( struct platform_device * pdev )
2012-11-24 00:17:34 +04:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct device * dev = & pdev - > dev ;
int dead ;
if ( c - > use_runtime_pm ) {
pm_runtime_get_sync ( dev ) ;
pm_runtime_disable ( dev ) ;
pm_runtime_put_noidle ( dev ) ;
}
2014-09-01 07:35:40 +04:00
if ( c - > slot & & c - > slot - > remove_slot )
c - > slot - > remove_slot ( pdev ) ;
2012-11-24 00:17:34 +04:00
dead = ( sdhci_readl ( c - > host , SDHCI_INT_STATUS ) = = ~ 0 ) ;
sdhci_remove_host ( c - > host , dead ) ;
sdhci_free_host ( c - > host ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int sdhci_acpi_suspend ( struct device * dev )
{
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
2017-03-20 20:50:32 +03:00
struct sdhci_host * host = c - > host ;
2012-11-24 00:17:34 +04:00
2017-03-20 20:50:32 +03:00
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
return sdhci_suspend_host ( host ) ;
2012-11-24 00:17:34 +04:00
}
static int sdhci_acpi_resume ( struct device * dev )
{
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
2016-04-15 14:06:57 +03:00
sdhci_acpi_byt_setting ( & c - > pdev - > dev ) ;
2012-11-24 00:17:34 +04:00
return sdhci_resume_host ( c - > host ) ;
}
# endif
2014-12-05 05:05:33 +03:00
# ifdef CONFIG_PM
2012-11-24 00:17:34 +04:00
static int sdhci_acpi_runtime_suspend ( struct device * dev )
{
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
2017-03-20 20:50:32 +03:00
struct sdhci_host * host = c - > host ;
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
2012-11-24 00:17:34 +04:00
2017-03-20 20:50:32 +03:00
return sdhci_runtime_suspend_host ( host ) ;
2012-11-24 00:17:34 +04:00
}
static int sdhci_acpi_runtime_resume ( struct device * dev )
{
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
2016-04-15 14:06:57 +03:00
sdhci_acpi_byt_setting ( & c - > pdev - > dev ) ;
2012-11-24 00:17:34 +04:00
return sdhci_runtime_resume_host ( c - > host ) ;
}
# endif
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
2016-07-27 11:39:52 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( sdhci_acpi_suspend , sdhci_acpi_resume )
2014-08-12 20:14:29 +04:00
SET_RUNTIME_PM_OPS ( sdhci_acpi_runtime_suspend ,
2015-01-02 16:47:00 +03:00
sdhci_acpi_runtime_resume , NULL )
2012-11-24 00:17:34 +04:00
} ;
static struct platform_driver sdhci_acpi_driver = {
. driver = {
. name = " sdhci-acpi " ,
. acpi_match_table = sdhci_acpi_ids ,
. pm = & sdhci_acpi_pm_ops ,
} ,
. probe = sdhci_acpi_probe ,
2012-12-22 03:05:47 +04:00
. remove = sdhci_acpi_remove ,
2012-11-24 00:17:34 +04:00
} ;
module_platform_driver ( sdhci_acpi_driver ) ;
MODULE_DESCRIPTION ( " Secure Digital Host Controller Interface ACPI driver " ) ;
MODULE_AUTHOR ( " Adrian Hunter " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;