2019-05-30 02:57:49 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-11-24 00:17:34 +04:00
/*
* Secure Digital Host Controller Interface ACPI driver .
*
* Copyright ( c ) 2012 , Intel Corporation .
*/
2020-10-27 11:46:12 +03:00
# include <linux/bitfield.h>
2012-11-24 00:17:34 +04:00
# 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>
2020-03-16 21:47:52 +03:00
# include <linux/dmi.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 ;
2017-10-19 13:41:45 +03:00
size_t priv_size ;
2019-10-01 17:27:24 +03:00
int ( * probe_slot ) ( struct platform_device * , struct acpi_device * ) ;
2014-09-01 07:35:40 +04:00
int ( * remove_slot ) ( struct platform_device * ) ;
2018-08-16 07:48:42 +03:00
int ( * free_slot ) ( struct platform_device * pdev ) ;
2017-12-08 16:04:58 +03:00
int ( * setup_host ) ( struct platform_device * pdev ) ;
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 ;
2020-03-16 21:47:52 +03:00
bool is_intel ;
bool reset_signal_volt_on_suspend ;
2020-02-27 01:31:25 +03:00
unsigned long private [ ] ____cacheline_aligned ;
2012-11-24 00:17:34 +04:00
} ;
2020-03-16 21:47:52 +03:00
enum {
DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP = BIT ( 0 ) ,
2020-03-16 21:47:53 +03:00
DMI_QUIRK_SD_NO_WRITE_PROTECT = BIT ( 1 ) ,
2020-03-16 21:47:52 +03:00
} ;
2017-10-19 13:41:45 +03:00
static inline void * sdhci_acpi_priv ( struct sdhci_acpi_host * c )
{
return ( void * ) c - > private ;
}
2012-11-24 00:17:34 +04:00
static inline bool sdhci_acpi_flag ( struct sdhci_acpi_host * c , unsigned int flag )
{
return c - > slot & & ( c - > slot - > flags & flag ) ;
}
2017-12-08 16:08:18 +03:00
# define INTEL_DSM_HS_CAPS_SDR25 BIT(0)
# define INTEL_DSM_HS_CAPS_DDR50 BIT(1)
# define INTEL_DSM_HS_CAPS_SDR50 BIT(2)
# define INTEL_DSM_HS_CAPS_SDR104 BIT(3)
2017-10-19 13:41:46 +03:00
enum {
INTEL_DSM_FNS = 0 ,
INTEL_DSM_V18_SWITCH = 3 ,
INTEL_DSM_V33_SWITCH = 4 ,
2017-12-08 16:08:18 +03:00
INTEL_DSM_HS_CAPS = 8 ,
2017-10-19 13:41:46 +03:00
} ;
struct intel_host {
u32 dsm_fns ;
2017-12-08 16:08:18 +03:00
u32 hs_caps ;
2017-10-19 13:41:46 +03:00
} ;
static const guid_t intel_dsm_guid =
GUID_INIT ( 0xF6C13EA5 , 0x65CD , 0x461F ,
0xAB , 0x7A , 0x29 , 0xF7 , 0xE8 , 0xD5 , 0xBD , 0x61 ) ;
static int __intel_dsm ( struct intel_host * intel_host , struct device * dev ,
unsigned int fn , u32 * result )
{
union acpi_object * obj ;
int err = 0 ;
obj = acpi_evaluate_dsm ( ACPI_HANDLE ( dev ) , & intel_dsm_guid , 0 , fn , NULL ) ;
if ( ! obj )
return - EOPNOTSUPP ;
if ( obj - > type = = ACPI_TYPE_INTEGER ) {
* result = obj - > integer . value ;
} else if ( obj - > type = = ACPI_TYPE_BUFFER & & obj - > buffer . length > 0 ) {
size_t len = min_t ( size_t , obj - > buffer . length , 4 ) ;
* result = 0 ;
memcpy ( result , obj - > buffer . pointer , len ) ;
} else {
dev_err ( dev , " %s DSM fn %u obj->type %d obj->buffer.length %d \n " ,
__func__ , fn , obj - > type , obj - > buffer . length ) ;
err = - EINVAL ;
}
ACPI_FREE ( obj ) ;
return err ;
}
static int intel_dsm ( struct intel_host * intel_host , struct device * dev ,
unsigned int fn , u32 * result )
{
if ( fn > 31 | | ! ( intel_host - > dsm_fns & ( 1 < < fn ) ) )
return - EOPNOTSUPP ;
return __intel_dsm ( intel_host , dev , fn , result ) ;
}
static void intel_dsm_init ( struct intel_host * intel_host , struct device * dev ,
struct mmc_host * mmc )
{
int err ;
2017-12-08 16:08:18 +03:00
intel_host - > hs_caps = ~ 0 ;
2017-10-19 13:41:46 +03:00
err = __intel_dsm ( intel_host , dev , INTEL_DSM_FNS , & intel_host - > dsm_fns ) ;
if ( err ) {
pr_debug ( " %s: DSM not supported, error %d \n " ,
mmc_hostname ( mmc ) , err ) ;
return ;
}
pr_debug ( " %s: DSM function mask %#x \n " ,
mmc_hostname ( mmc ) , intel_host - > dsm_fns ) ;
2017-12-08 16:08:18 +03:00
intel_dsm ( intel_host , dev , INTEL_DSM_HS_CAPS , & intel_host - > hs_caps ) ;
2017-10-19 13:41:46 +03:00
}
static int intel_start_signal_voltage_switch ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
struct device * dev = mmc_dev ( mmc ) ;
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
struct intel_host * intel_host = sdhci_acpi_priv ( c ) ;
unsigned int fn ;
u32 result = 0 ;
int err ;
err = sdhci_start_signal_voltage_switch ( mmc , ios ) ;
if ( err )
return err ;
switch ( ios - > signal_voltage ) {
case MMC_SIGNAL_VOLTAGE_330 :
fn = INTEL_DSM_V33_SWITCH ;
break ;
case MMC_SIGNAL_VOLTAGE_180 :
fn = INTEL_DSM_V18_SWITCH ;
break ;
default :
return 0 ;
}
err = intel_dsm ( intel_host , dev , fn , & result ) ;
pr_debug ( " %s: %s DSM fn %u error %d result %u \n " ,
mmc_hostname ( mmc ) , __func__ , fn , err , result ) ;
return 0 ;
}
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 [ ] = {
2020-03-20 16:14:01 +03:00
X86_MATCH_INTEL_FAM6_MODEL ( ATOM_SILVERMONT , NULL ) ,
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 [ ] = {
2020-03-20 16:14:01 +03:00
X86_MATCH_INTEL_FAM6_MODEL ( ATOM_AIRMONT , NULL ) ,
2017-06-21 15:08:39 +03:00
{ }
} ;
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 .
*/
2019-10-01 17:27:24 +03:00
static inline bool sdhci_acpi_no_fixup_child_power ( struct acpi_device * adev )
2017-06-21 15:08:39 +03:00
{
return sdhci_acpi_cht ( ) & &
2019-10-01 17:27:24 +03:00
acpi_dev_hid_uid_match ( adev , " 80860F14 " , " 2 " ) & &
2017-06-21 15:08:39 +03:00
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 ;
}
2019-10-01 17:27:24 +03:00
static inline bool sdhci_acpi_no_fixup_child_power ( struct acpi_device * adev )
2017-06-21 15:08:39 +03:00
{
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 ;
}
2019-10-01 17:27:24 +03:00
static int intel_probe_slot ( struct platform_device * pdev , struct acpi_device * adev )
2014-09-01 07:35:40 +04:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
2017-10-19 13:41:46 +03:00
struct intel_host * intel_host = sdhci_acpi_priv ( c ) ;
2017-10-19 13:41:44 +03:00
struct sdhci_host * host = c - > host ;
2014-09-01 07:35:40 +04:00
2019-10-01 17:27:24 +03:00
if ( acpi_dev_hid_uid_match ( adev , " 80860F14 " , " 1 " ) & &
2014-10-06 16:23:07 +04:00
sdhci_readl ( host , SDHCI_CAPABILITIES ) = = 0x446cc8b2 & &
sdhci_readl ( host , SDHCI_CAPABILITIES_1 ) = = 0x00000807 )
host - > timeout_clk = 1000 ; /* 1000 kHz i.e. 1 MHz */
2019-10-01 17:27:24 +03:00
if ( acpi_dev_hid_uid_match ( adev , " 80865ACA " , NULL ) )
2016-02-09 17:12:38 +03:00
host - > mmc_host_ops . get_cd = bxt_get_cd ;
2017-10-19 13:41:46 +03:00
intel_dsm_init ( intel_host , & pdev - > dev , host - > mmc ) ;
host - > mmc_host_ops . start_signal_voltage_switch =
intel_start_signal_voltage_switch ;
2020-03-16 21:47:52 +03:00
c - > is_intel = true ;
2014-09-01 07:35:40 +04:00
return 0 ;
}
2017-12-08 16:08:18 +03:00
static int intel_setup_host ( struct platform_device * pdev )
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct intel_host * intel_host = sdhci_acpi_priv ( c ) ;
if ( ! ( intel_host - > hs_caps & INTEL_DSM_HS_CAPS_SDR25 ) )
c - > host - > mmc - > caps & = ~ MMC_CAP_UHS_SDR25 ;
if ( ! ( intel_host - > hs_caps & INTEL_DSM_HS_CAPS_SDR50 ) )
c - > host - > mmc - > caps & = ~ MMC_CAP_UHS_SDR50 ;
if ( ! ( intel_host - > hs_caps & INTEL_DSM_HS_CAPS_DDR50 ) )
c - > host - > mmc - > caps & = ~ MMC_CAP_UHS_DDR50 ;
if ( ! ( intel_host - > hs_caps & INTEL_DSM_HS_CAPS_SDR104 ) )
c - > host - > mmc - > caps & = ~ MMC_CAP_UHS_SDR104 ;
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 ,
2018-12-11 16:10:44 +03:00
. quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED ,
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 ,
2017-10-19 13:41:44 +03:00
. probe_slot = intel_probe_slot ,
2017-12-08 16:08:18 +03:00
. setup_host = intel_setup_host ,
2017-10-19 13:41:46 +03:00
. priv_size = sizeof ( struct intel_host ) ,
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 |
2018-12-11 16:10:44 +03:00
SDHCI_QUIRK_NO_LED |
2014-12-01 16:51:17 +03:00
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 ,
2017-10-19 13:41:44 +03:00
. probe_slot = intel_probe_slot ,
2017-12-08 16:08:18 +03:00
. setup_host = intel_setup_host ,
2017-10-19 13:41:46 +03:00
. priv_size = sizeof ( struct intel_host ) ,
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 ,
2018-12-11 16:10:44 +03:00
. quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED ,
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 ,
2017-10-19 13:41:44 +03:00
. probe_slot = intel_probe_slot ,
2017-12-08 16:08:18 +03:00
. setup_host = intel_setup_host ,
2017-10-19 13:41:46 +03:00
. priv_size = sizeof ( struct intel_host ) ,
2013-04-26 12:27:22 +04:00
} ;
2018-08-16 07:48:43 +03:00
# define VENDOR_SPECIFIC_PWRCTL_CLEAR_REG 0x1a8
# define VENDOR_SPECIFIC_PWRCTL_CTL_REG 0x1ac
static irqreturn_t sdhci_acpi_qcom_handler ( int irq , void * ptr )
{
struct sdhci_host * host = ptr ;
sdhci_writel ( host , 0x3 , VENDOR_SPECIFIC_PWRCTL_CLEAR_REG ) ;
sdhci_writel ( host , 0x1 , VENDOR_SPECIFIC_PWRCTL_CTL_REG ) ;
return IRQ_HANDLED ;
}
2019-10-01 17:27:24 +03:00
static int qcom_probe_slot ( struct platform_device * pdev , struct acpi_device * adev )
2018-08-16 07:48:43 +03:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host = c - > host ;
int * irq = sdhci_acpi_priv ( c ) ;
* irq = - EINVAL ;
2019-10-01 17:27:24 +03:00
if ( ! acpi_dev_hid_uid_match ( adev , " QCOM8051 " , NULL ) )
2018-08-16 07:48:43 +03:00
return 0 ;
* irq = platform_get_irq ( pdev , 1 ) ;
if ( * irq < 0 )
return 0 ;
return request_threaded_irq ( * irq , NULL , sdhci_acpi_qcom_handler ,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH ,
" sdhci_qcom " , host ) ;
}
static int qcom_free_slot ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host = c - > host ;
struct acpi_device * adev ;
int * irq = sdhci_acpi_priv ( c ) ;
adev = ACPI_COMPANION ( dev ) ;
if ( ! adev )
return - ENODEV ;
2019-10-01 17:27:24 +03:00
if ( ! acpi_dev_hid_uid_match ( adev , " QCOM8051 " , NULL ) )
2018-08-16 07:48:43 +03:00
return 0 ;
if ( * irq < 0 )
return 0 ;
free_irq ( * irq , host ) ;
return 0 ;
}
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 ,
2018-08-16 07:48:43 +03:00
. priv_size = sizeof ( int ) ,
. probe_slot = qcom_probe_slot ,
. free_slot = qcom_free_slot ,
2016-03-03 19:38:46 +03:00
} ;
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd = {
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION ,
. caps = MMC_CAP_NONREMOVABLE ,
} ;
2020-08-19 22:00:19 +03:00
struct amd_sdhci_host {
bool tuned_clock ;
bool dll_enabled ;
} ;
2017-12-01 13:08:52 +03:00
/* AMD sdhci reset dll register. */
# define SDHCI_AMD_RESET_DLL_REGISTER 0x908
static int amd_select_drive_strength ( struct mmc_card * card ,
unsigned int max_dtr , int host_drv ,
2020-10-27 11:46:12 +03:00
int card_drv , int * host_driver_strength )
2017-12-01 13:08:52 +03:00
{
2020-10-27 11:46:12 +03:00
struct sdhci_host * host = mmc_priv ( card - > host ) ;
u16 preset , preset_driver_strength ;
/*
* This method is only called by mmc_select_hs200 so we only need to
* read from the HS200 ( SDR104 ) preset register .
*
* Firmware that has " invalid/default " presets return a driver strength
* of A . This matches the previously hard coded value .
*/
preset = sdhci_readw ( host , SDHCI_PRESET_FOR_SDR104 ) ;
preset_driver_strength = FIELD_GET ( SDHCI_PRESET_DRV_MASK , preset ) ;
/*
* We want the controller driver strength to match the card ' s driver
* strength so they have similar rise / fall times .
*
* The controller driver strength set by this method is sticky for all
* timings after this method is called . This unfortunately means that
* while HS400 tuning is in progress we end up with mismatched driver
* strengths between the controller and the card . HS400 tuning requires
* switching from HS400 - > DDR52 - > HS - > HS200 - > HS400 . So the driver mismatch
* happens while in DDR52 and HS modes . This has not been observed to
* cause problems . Enabling presets would fix this issue .
*/
* host_driver_strength = preset_driver_strength ;
/*
* The resulting card driver strength is only set when switching the
* card ' s timing to HS200 or HS400 . The card will use the default driver
* strength ( B ) for any other mode .
*/
return preset_driver_strength ;
2017-12-01 13:08:52 +03:00
}
2020-09-01 00:10:32 +03:00
static void sdhci_acpi_amd_hs400_dll ( struct sdhci_host * host , bool enable )
2017-12-01 13:08:52 +03:00
{
2020-09-01 00:10:32 +03:00
struct sdhci_acpi_host * acpi_host = sdhci_priv ( host ) ;
struct amd_sdhci_host * amd_host = sdhci_acpi_priv ( acpi_host ) ;
2017-12-01 13:08:52 +03:00
/* AMD Platform requires dll setting */
sdhci_writel ( host , 0x40003210 , SDHCI_AMD_RESET_DLL_REGISTER ) ;
usleep_range ( 10 , 20 ) ;
2020-09-01 00:10:32 +03:00
if ( enable )
sdhci_writel ( host , 0x40033210 , SDHCI_AMD_RESET_DLL_REGISTER ) ;
amd_host - > dll_enabled = enable ;
2017-12-01 13:08:52 +03:00
}
/*
2020-08-19 22:00:19 +03:00
* The initialization sequence for HS400 is :
* HS - > HS200 - > Perform Tuning - > HS - > HS400
*
* The re - tuning sequence is :
* HS400 - > DDR52 - > HS - > HS200 - > Perform Tuning - > HS - > HS400
*
* The AMD eMMC Controller can only use the tuned clock while in HS200 and HS400
* mode . If we switch to a different mode , we need to disable the tuned clock .
* If we have previously performed tuning and switch back to HS200 or
* HS400 , we can re - enable the tuned clock .
*
2017-12-01 13:08:52 +03:00
*/
static void amd_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
2020-08-19 22:00:19 +03:00
struct sdhci_acpi_host * acpi_host = sdhci_priv ( host ) ;
struct amd_sdhci_host * amd_host = sdhci_acpi_priv ( acpi_host ) ;
2017-12-01 13:08:52 +03:00
unsigned int old_timing = host - > timing ;
2020-08-19 22:00:19 +03:00
u16 val ;
2017-12-01 13:08:52 +03:00
sdhci_set_ios ( mmc , ios ) ;
2020-08-19 22:00:19 +03:00
if ( old_timing ! = host - > timing & & amd_host - > tuned_clock ) {
if ( host - > timing = = MMC_TIMING_MMC_HS400 | |
host - > timing = = MMC_TIMING_MMC_HS200 ) {
val = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
val | = SDHCI_CTRL_TUNED_CLK ;
sdhci_writew ( host , val , SDHCI_HOST_CONTROL2 ) ;
} else {
val = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
val & = ~ SDHCI_CTRL_TUNED_CLK ;
sdhci_writew ( host , val , SDHCI_HOST_CONTROL2 ) ;
}
/* DLL is only required for HS400 */
if ( host - > timing = = MMC_TIMING_MMC_HS400 & &
2020-09-01 00:10:32 +03:00
! amd_host - > dll_enabled )
sdhci_acpi_amd_hs400_dll ( host , true ) ;
2017-12-01 13:08:52 +03:00
}
}
2020-08-19 22:00:19 +03:00
static int amd_sdhci_execute_tuning ( struct mmc_host * mmc , u32 opcode )
{
int err ;
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_acpi_host * acpi_host = sdhci_priv ( host ) ;
struct amd_sdhci_host * amd_host = sdhci_acpi_priv ( acpi_host ) ;
amd_host - > tuned_clock = false ;
err = sdhci_execute_tuning ( mmc , opcode ) ;
if ( ! err & & ! host - > tuning_err )
amd_host - > tuned_clock = true ;
return err ;
}
2020-09-01 00:10:32 +03:00
static void amd_sdhci_reset ( struct sdhci_host * host , u8 mask )
{
struct sdhci_acpi_host * acpi_host = sdhci_priv ( host ) ;
struct amd_sdhci_host * amd_host = sdhci_acpi_priv ( acpi_host ) ;
if ( mask & SDHCI_RESET_ALL ) {
amd_host - > tuned_clock = false ;
sdhci_acpi_amd_hs400_dll ( host , false ) ;
}
sdhci_reset ( host , mask ) ;
}
2017-12-01 13:08:52 +03:00
static const struct sdhci_ops sdhci_acpi_ops_amd = {
. set_clock = sdhci_set_clock ,
. set_bus_width = sdhci_set_bus_width ,
2020-09-01 00:10:32 +03:00
. reset = amd_sdhci_reset ,
2017-12-01 13:08:52 +03:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
} ;
static const struct sdhci_acpi_chip sdhci_acpi_chip_amd = {
. ops = & sdhci_acpi_ops_amd ,
} ;
static int sdhci_acpi_emmc_amd_probe_slot ( struct platform_device * pdev ,
2019-10-01 17:27:24 +03:00
struct acpi_device * adev )
2017-12-01 13:08:52 +03:00
{
struct sdhci_acpi_host * c = platform_get_drvdata ( pdev ) ;
struct sdhci_host * host = c - > host ;
sdhci_read_caps ( host ) ;
if ( host - > caps1 & SDHCI_SUPPORT_DDR50 )
host - > mmc - > caps = MMC_CAP_1_8V_DDR ;
if ( ( host - > caps1 & SDHCI_SUPPORT_SDR104 ) & &
( host - > mmc - > caps & MMC_CAP_1_8V_DDR ) )
host - > mmc - > caps2 = MMC_CAP2_HS400_1_8V ;
2020-09-29 00:59:20 +03:00
/*
* There are two types of presets out in the wild :
* 1 ) Default / broken presets .
* These presets have two sets of problems :
* a ) The clock divisor for SDR12 , SDR25 , and SDR50 is too small .
* This results in clock frequencies that are 2 x higher than
* acceptable . i . e . , SDR12 = 25 MHz , SDR25 = 50 MHz , SDR50 =
* 100 MHz . x
* b ) The HS200 and HS400 driver strengths don ' t match .
* By default , the SDR104 preset register has a driver strength of
* A , but the ( internal ) HS400 preset register has a driver
* strength of B . As part of initializing HS400 , HS200 tuning
* needs to be performed . Having different driver strengths
* between tuning and operation is wrong . It results in different
* rise / fall times that lead to incorrect sampling .
* 2 ) Firmware with properly initialized presets .
* These presets have proper clock divisors . i . e . , SDR12 = > 12 MHz ,
* SDR25 = > 25 MHz , SDR50 = > 50 MHz . Additionally the HS200 and
* HS400 preset driver strengths match .
*
* Enabling presets for HS400 doesn ' t work for the following reasons :
* 1 ) sdhci_set_ios has a hard coded list of timings that are used
* to determine if presets should be enabled .
* 2 ) sdhci_get_preset_value is using a non - standard register to
* read out HS400 presets . The AMD controller doesn ' t support this
* non - standard register . In fact , it doesn ' t expose the HS400
* preset register anywhere in the SDHCI memory map . This results
* in reading a garbage value and using the wrong presets .
*
* Since HS400 and HS200 presets must be identical , we could
* instead use the the SDR104 preset register .
*
* If the above issues are resolved we could remove this quirk for
* firmware that that has valid presets ( i . e . , SDR12 < = 12 MHz ) .
*/
host - > quirks2 | = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ;
2017-12-01 13:08:52 +03:00
host - > mmc_host_ops . select_drive_strength = amd_select_drive_strength ;
host - > mmc_host_ops . set_ios = amd_set_ios ;
2020-08-19 22:00:19 +03:00
host - > mmc_host_ops . execute_tuning = amd_sdhci_execute_tuning ;
2017-12-01 13:08:52 +03:00
return 0 ;
}
static const struct sdhci_acpi_slot sdhci_acpi_slot_amd_emmc = {
2020-05-09 01:54:21 +03:00
. chip = & sdhci_acpi_chip_amd ,
. caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE ,
. quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE ,
. quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA ,
2017-12-01 13:08:52 +03:00
. probe_slot = sdhci_acpi_emmc_amd_probe_slot ,
2020-08-19 22:00:19 +03:00
. priv_size = sizeof ( struct amd_sdhci_host ) ,
2017-12-01 13:08:52 +03:00
} ;
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 } ,
2017-12-01 13:08:52 +03:00
{ " AMDI0040 " , NULL , & sdhci_acpi_slot_amd_emmc } ,
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 " } ,
2017-12-01 13:08:52 +03:00
{ " AMDI0040 " } ,
2012-11-24 00:17:34 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , sdhci_acpi_ids ) ;
2020-03-16 21:47:52 +03:00
static const struct dmi_system_id sdhci_acpi_quirks [ ] = {
{
/*
* The Lenovo Miix 320 - 10 ICR has a bug in the _PS0 method of
* the SHC1 ACPI device , this bug causes it to reprogram the
* wrong LDO ( DLDO3 ) to 1.8 V if 1.8 V modes are used and the
* card is ( runtime ) suspended + resumed . DLDO3 is used for
* the LCD and setting it to 1.8 V causes the LCD to go black .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " LENOVO " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " Lenovo MIIX 320-10ICR " ) ,
} ,
. driver_data = ( void * ) DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP ,
} ,
2020-03-16 21:47:53 +03:00
{
/*
* The Acer Aspire Switch 10 ( SW5 - 012 ) microSD slot always
* reports the card being write - protected even though microSD
* cards do not have a write - protect switch at all .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire SW5-012 " ) ,
} ,
. driver_data = ( void * ) DMI_QUIRK_SD_NO_WRITE_PROTECT ,
} ,
2020-03-16 21:47:52 +03:00
{ } /* Terminating entry */
} ;
2019-10-01 17:27:24 +03:00
static const struct sdhci_acpi_slot * sdhci_acpi_get_slot ( struct acpi_device * adev )
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 + + ) {
2019-10-01 17:27:24 +03:00
if ( acpi_dev_hid_uid_match ( adev , u - > hid , u - > uid ) )
2013-04-26 12:27:22 +04:00
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 ;
2017-10-19 13:41:45 +03:00
const struct sdhci_acpi_slot * slot ;
2016-05-19 16:25:42 +03:00
struct acpi_device * device , * child ;
2020-03-16 21:47:52 +03:00
const struct dmi_system_id * id ;
2012-11-24 00:17:34 +04:00
struct sdhci_acpi_host * c ;
struct sdhci_host * host ;
struct resource * iomem ;
resource_size_t len ;
2017-10-19 13:41:45 +03:00
size_t priv_size ;
2020-03-16 21:47:52 +03:00
int quirks = 0 ;
2014-01-08 14:40:55 +04:00
int err ;
2012-11-24 00:17:34 +04:00
2017-07-24 17:59:58 +03:00
device = ACPI_COMPANION ( dev ) ;
if ( ! device )
2012-11-24 00:17:34 +04:00
return - ENODEV ;
2020-03-16 21:47:52 +03:00
id = dmi_first_match ( sdhci_acpi_quirks ) ;
if ( id )
quirks = ( long ) id - > driver_data ;
2019-10-01 17:27:24 +03:00
slot = sdhci_acpi_get_slot ( device ) ;
2017-10-19 13:41:45 +03:00
2016-05-19 16:25:42 +03:00
/* Power on the SDHCI controller and its children */
acpi_device_fix_up_power ( device ) ;
2019-10-01 17:27:24 +03:00
if ( ! sdhci_acpi_no_fixup_child_power ( device ) ) {
2017-06-21 15:08:39 +03:00
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 ;
2017-10-19 13:41:45 +03:00
priv_size = slot ? slot - > priv_size : 0 ;
host = sdhci_alloc_host ( dev , sizeof ( struct sdhci_acpi_host ) + priv_size ) ;
2012-11-24 00:17:34 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
c = sdhci_priv ( host ) ;
c - > host = host ;
2017-10-19 13:41:45 +03:00
c - > slot = slot ;
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 ) ;
2018-03-21 12:49:40 +03:00
if ( host - > irq < 0 ) {
2017-11-19 07:52:45 +03:00
err = - EINVAL ;
goto err_free ;
}
2012-11-24 00:17:34 +04:00
2020-01-06 11:43:50 +03:00
host - > ioaddr = devm_ioremap ( dev , iomem - > start ,
2012-11-24 00:17:34 +04:00
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 ) {
2019-10-01 17:27:24 +03:00
err = c - > slot - > probe_slot ( pdev , device ) ;
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 ) ;
2019-12-11 05:40:56 +03:00
err = mmc_gpiod_request_cd ( host - > mmc , NULL , 0 , v , 0 ) ;
2017-01-18 12:46:18 +03:00
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
}
2020-03-16 21:47:52 +03:00
if ( quirks & DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP )
c - > reset_signal_volt_on_suspend = true ;
2020-03-16 21:47:53 +03:00
if ( quirks & DMI_QUIRK_SD_NO_WRITE_PROTECT )
host - > mmc - > caps2 | = MMC_CAP2_NO_WRITE_PROTECT ;
2013-05-06 13:17:33 +04:00
}
2017-12-08 16:04:58 +03:00
err = sdhci_setup_host ( host ) ;
2014-03-10 17:02:42 +04:00
if ( err )
goto err_free ;
2017-12-08 16:04:58 +03:00
if ( c - > slot & & c - > slot - > setup_host ) {
err = c - > slot - > setup_host ( pdev ) ;
if ( err )
goto err_cleanup ;
}
err = __sdhci_add_host ( host ) ;
if ( err )
goto err_cleanup ;
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 ;
2017-12-08 16:04:58 +03:00
err_cleanup :
sdhci_cleanup_host ( c - > host ) ;
2012-11-24 00:17:34 +04:00
err_free :
2018-08-16 07:48:42 +03:00
if ( c - > slot & & c - > slot - > free_slot )
c - > slot - > free_slot ( pdev ) ;
2012-11-24 00:17:34 +04:00
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 ) ;
2018-08-16 07:48:42 +03:00
if ( c - > slot & & c - > slot - > free_slot )
c - > slot - > free_slot ( pdev ) ;
2012-11-24 00:17:34 +04:00
sdhci_free_host ( c - > host ) ;
return 0 ;
}
2020-03-16 21:47:52 +03:00
static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed (
struct device * dev )
{
struct sdhci_acpi_host * c = dev_get_drvdata ( dev ) ;
struct sdhci_host * host = c - > host ;
if ( c - > is_intel & & c - > reset_signal_volt_on_suspend & &
host - > mmc - > ios . signal_voltage ! = MMC_SIGNAL_VOLTAGE_330 ) {
struct intel_host * intel_host = sdhci_acpi_priv ( c ) ;
unsigned int fn = INTEL_DSM_V33_SWITCH ;
u32 result = 0 ;
intel_dsm ( intel_host , dev , fn , & result ) ;
}
}
2012-11-24 00:17:34 +04:00
# 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 ;
2020-03-16 21:47:52 +03:00
int ret ;
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 ) ;
2020-03-16 21:47:52 +03:00
ret = sdhci_suspend_host ( host ) ;
if ( ret )
return ret ;
sdhci_acpi_reset_signal_voltage_if_needed ( dev ) ;
return 0 ;
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 ;
2020-03-16 21:47:52 +03:00
int ret ;
2017-03-20 20:50:32 +03:00
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
2012-11-24 00:17:34 +04:00
2020-03-16 21:47:52 +03:00
ret = sdhci_runtime_suspend_host ( host ) ;
if ( ret )
return ret ;
sdhci_acpi_reset_signal_voltage_if_needed ( dev ) ;
return 0 ;
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 ) ;
2019-07-25 06:14:22 +03:00
return sdhci_runtime_resume_host ( c - > host , 0 ) ;
2012-11-24 00:17:34 +04:00
}
# 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 " ,
2020-09-04 02:24:36 +03:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2012-11-24 00:17:34 +04:00
. 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 " ) ;