@ -1,7 +1,7 @@
/*
* ACPI support for Intel Lynxpoint LPSS .
*
* Copyright ( C ) 2013 , Intel Corporation
* Copyright ( C ) 2013 , 2014 , Intel Corporation
* Authors : Mika Westerberg < mika . westerberg @ linux . intel . com >
* Rafael J . Wysocki < rafael . j . wysocki @ intel . com >
*
@ -60,6 +60,8 @@ ACPI_MODULE_NAME("acpi_lpss");
# define LPSS_CLK_DIVIDER BIT(2)
# define LPSS_LTR BIT(3)
# define LPSS_SAVE_CTX BIT(4)
# define LPSS_DEV_PROXY BIT(5)
# define LPSS_PROXY_REQ BIT(6)
struct lpss_private_data ;
@ -70,8 +72,10 @@ struct lpss_device_desc {
void ( * setup ) ( struct lpss_private_data * pdata ) ;
} ;
static struct device * proxy_device ;
static struct lpss_device_desc lpss_dma_desc = {
. flags = LPSS_CLK ,
. flags = LPSS_CLK | LPSS_PROXY_REQ ,
} ;
struct lpss_private_data {
@ -146,22 +150,24 @@ static struct lpss_device_desc byt_pwm_dev_desc = {
} ;
static struct lpss_device_desc byt_uart_dev_desc = {
. flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX ,
. flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
LPSS_DEV_PROXY ,
. prv_offset = 0x800 ,
. setup = lpss_uart_setup ,
} ;
static struct lpss_device_desc byt_spi_dev_desc = {
. flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX ,
. flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
LPSS_DEV_PROXY ,
. prv_offset = 0x400 ,
} ;
static struct lpss_device_desc byt_sdio_dev_desc = {
. flags = LPSS_CLK ,
. flags = LPSS_CLK | LPSS_DEV_PROXY ,
} ;
static struct lpss_device_desc byt_i2c_dev_desc = {
. flags = LPSS_CLK | LPSS_SAVE_CTX ,
. flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_DEV_PROXY ,
. prv_offset = 0x800 ,
. setup = byt_i2c_setup ,
} ;
@ -368,6 +374,8 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
adev - > driver_data = pdata ;
pdev = acpi_create_platform_device ( adev ) ;
if ( ! IS_ERR_OR_NULL ( pdev ) ) {
if ( ! proxy_device & & dev_desc - > flags & LPSS_DEV_PROXY )
proxy_device = & pdev - > dev ;
return 1 ;
}
@ -499,14 +507,15 @@ static void acpi_lpss_set_ltr(struct device *dev, s32 val)
/**
* acpi_lpss_save_ctx ( ) - Save the private registers of LPSS device
* @ dev : LPSS device
* @ pdata : pointer to the private data of the LPSS device
*
* Most LPSS devices have private registers which may loose their context when
* the device is powered down . acpi_lpss_save_ctx ( ) saves those registers into
* prv_reg_ctx array .
*/
static void acpi_lpss_save_ctx ( struct device * dev )
static void acpi_lpss_save_ctx ( struct device * dev ,
struct lpss_private_data * pdata )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
unsigned int i ;
for ( i = 0 ; i < LPSS_PRV_REG_COUNT ; i + + ) {
@ -521,12 +530,13 @@ static void acpi_lpss_save_ctx(struct device *dev)
/**
* acpi_lpss_restore_ctx ( ) - Restore the private registers of LPSS device
* @ dev : LPSS device
* @ pdata : pointer to the private data of the LPSS device
*
* Restores the registers that were previously stored with acpi_lpss_save_ctx ( ) .
*/
static void acpi_lpss_restore_ctx ( struct device * dev )
static void acpi_lpss_restore_ctx ( struct device * dev ,
struct lpss_private_data * pdata )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
unsigned int i ;
/*
@ -549,23 +559,31 @@ static void acpi_lpss_restore_ctx(struct device *dev)
# ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late ( struct device * dev )
{
int ret = pm_generic_suspend_late ( dev ) ;
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
int ret ;
ret = pm_generic_suspend_late ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_save_ctx ( dev ) ;
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
acpi_lpss_save_ctx ( dev , pdata ) ;
return acpi_dev_suspend_late ( dev ) ;
}
static int acpi_lpss_resume_early ( struct device * dev )
{
int ret = acpi_dev_resume_early ( dev ) ;
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
int ret ;
ret = acpi_dev_resume_early ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_restore_ctx ( dev ) ;
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
acpi_lpss_restore_ctx ( dev , pdata ) ;
return pm_generic_resume_early ( dev ) ;
}
# endif /* CONFIG_PM_SLEEP */
@ -573,23 +591,44 @@ static int acpi_lpss_resume_early(struct device *dev)
# ifdef CONFIG_PM_RUNTIME
static int acpi_lpss_runtime_suspend ( struct device * dev )
{
int ret = pm_generic_runtime_suspend ( dev ) ;
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
int ret ;
ret = pm_generic_runtime_suspend ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_save_ctx ( dev ) ;
return acpi_dev_runtime_suspend ( dev ) ;
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
acpi_lpss_save_ctx ( dev , pdata ) ;
ret = acpi_dev_runtime_suspend ( dev ) ;
if ( ret )
return ret ;
if ( pdata - > dev_desc - > flags & LPSS_PROXY_REQ & & proxy_device )
return pm_runtime_put_sync_suspend ( proxy_device ) ;
return 0 ;
}
static int acpi_lpss_runtime_resume ( struct device * dev )
{
int ret = acpi_dev_runtime_resume ( dev ) ;
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
int ret ;
if ( pdata - > dev_desc - > flags & LPSS_PROXY_REQ & & proxy_device ) {
ret = pm_runtime_get_sync ( proxy_device ) ;
if ( ret )
return ret ;
}
ret = acpi_dev_runtime_resume ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_restore_ctx ( dev ) ;
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
acpi_lpss_restore_ctx ( dev , pdata ) ;
return pm_generic_runtime_resume ( dev ) ;
}
# endif /* CONFIG_PM_RUNTIME */
@ -631,30 +670,27 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
return 0 ;
pdata = acpi_driver_data ( adev ) ;
if ( ! pdata | | ! pdata - > mmio_base )
if ( ! pdata )
return 0 ;
if ( pdata - > mmio_size < pdata - > dev_desc - > prv_offset + LPSS_LTR_SIZE ) {
if ( pdata - > mmio_base & &
pdata - > mmio_size < pdata - > dev_desc - > prv_offset + LPSS_LTR_SIZE ) {
dev_err ( & pdev - > dev , " MMIO size insufficient to access LTR \n " ) ;
return 0 ;
}
switch ( action ) {
case BUS_NOTIFY_BOUND_DRIVER :
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
pdev - > dev . pm_domain = & acpi_lpss_pm_domain ;
break ;
case BUS_NOTIFY_UNBOUND_DRIVER :
if ( pdata - > dev_desc - > flags & LPSS_SAVE_CTX )
pdev - > dev . pm_domain = NULL ;
break ;
case BUS_NOTIFY_ADD_DEVICE :
pdev - > dev . pm_domain = & acpi_lpss_pm_domain ;
if ( pdata - > dev_desc - > flags & LPSS_LTR )
return sysfs_create_group ( & pdev - > dev . kobj ,
& lpss_attr_group ) ;
break ;
case BUS_NOTIFY_DEL_DEVICE :
if ( pdata - > dev_desc - > flags & LPSS_LTR )
sysfs_remove_group ( & pdev - > dev . kobj , & lpss_attr_group ) ;
pdev - > dev . pm_domain = NULL ;
break ;
default :
break ;
}