2013-03-07 02:46:20 +04:00
/*
* ACPI support for Intel Lynxpoint LPSS .
*
* Copyright ( C ) 2013 , Intel Corporation
* Authors : Mika Westerberg < mika . westerberg @ linux . intel . com >
* Rafael J . Wysocki < rafael . j . wysocki @ intel . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/acpi.h>
# include <linux/clk.h>
# include <linux/clkdev.h>
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include <linux/platform_data/clk-lpss.h>
2013-03-07 02:46:28 +04:00
# include <linux/pm_runtime.h>
2014-05-23 17:15:09 +04:00
# include <linux/delay.h>
2013-03-07 02:46:20 +04:00
# include "internal.h"
ACPI_MODULE_NAME ( " acpi_lpss " ) ;
# define LPSS_CLK_SIZE 0x04
2013-03-07 02:46:28 +04:00
# define LPSS_LTR_SIZE 0x18
/* Offsets relative to LPSS_PRIVATE_OFFSET */
2014-05-19 15:42:07 +04:00
# define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16))
2013-03-07 02:46:28 +04:00
# define LPSS_GENERAL 0x08
# define LPSS_GENERAL_LTR_MODE_SW BIT(2)
2013-10-09 10:49:20 +04:00
# define LPSS_GENERAL_UART_RTS_OVRD BIT(3)
2013-03-07 02:46:28 +04:00
# define LPSS_SW_LTR 0x10
# define LPSS_AUTO_LTR 0x14
2014-02-11 03:35:53 +04:00
# define LPSS_LTR_SNOOP_REQ BIT(15)
# define LPSS_LTR_SNOOP_MASK 0x0000FFFF
# define LPSS_LTR_SNOOP_LAT_1US 0x800
# define LPSS_LTR_SNOOP_LAT_32US 0xC00
# define LPSS_LTR_SNOOP_LAT_SHIFT 5
# define LPSS_LTR_SNOOP_LAT_CUTOFF 3000
# define LPSS_LTR_MAX_VAL 0x3FF
2013-06-17 14:25:46 +04:00
# define LPSS_TX_INT 0x20
# define LPSS_TX_INT_MASK BIT(1)
2013-03-07 02:46:20 +04:00
2014-05-23 17:15:09 +04:00
# define LPSS_PRV_REG_COUNT 9
2013-05-13 16:42:44 +04:00
struct lpss_shared_clock {
const char * name ;
unsigned long rate ;
struct clk * clk ;
} ;
2013-06-17 14:25:46 +04:00
struct lpss_private_data ;
2013-03-07 02:46:20 +04:00
struct lpss_device_desc {
bool clk_required ;
2013-05-08 12:55:49 +04:00
const char * clkdev_name ;
2013-03-07 02:46:28 +04:00
bool ltr_required ;
unsigned int prv_offset ;
2013-06-18 17:51:35 +04:00
size_t prv_size_override ;
2014-05-19 15:42:07 +04:00
bool clk_divider ;
2013-05-13 16:42:44 +04:00
bool clk_gate ;
2014-05-23 17:15:09 +04:00
bool save_ctx ;
2013-05-13 16:42:44 +04:00
struct lpss_shared_clock * shared_clock ;
2013-06-17 14:25:46 +04:00
void ( * setup ) ( struct lpss_private_data * pdata ) ;
2013-03-07 02:46:20 +04:00
} ;
2013-05-08 12:55:49 +04:00
static struct lpss_device_desc lpss_dma_desc = {
. clk_required = true ,
. clkdev_name = " hclk " ,
} ;
2013-03-07 02:46:20 +04:00
struct lpss_private_data {
void __iomem * mmio_base ;
resource_size_t mmio_size ;
struct clk * clk ;
const struct lpss_device_desc * dev_desc ;
2014-05-23 17:15:09 +04:00
u32 prv_reg_ctx [ LPSS_PRV_REG_COUNT ] ;
2013-03-07 02:46:20 +04:00
} ;
2013-06-17 14:25:46 +04:00
static void lpss_uart_setup ( struct lpss_private_data * pdata )
{
2013-10-09 10:49:20 +04:00
unsigned int offset ;
2013-06-17 14:25:46 +04:00
u32 reg ;
2013-10-09 10:49:20 +04:00
offset = pdata - > dev_desc - > prv_offset + LPSS_TX_INT ;
reg = readl ( pdata - > mmio_base + offset ) ;
writel ( reg | LPSS_TX_INT_MASK , pdata - > mmio_base + offset ) ;
offset = pdata - > dev_desc - > prv_offset + LPSS_GENERAL ;
reg = readl ( pdata - > mmio_base + offset ) ;
writel ( reg | LPSS_GENERAL_UART_RTS_OVRD , pdata - > mmio_base + offset ) ;
2013-06-17 14:25:46 +04:00
}
2013-03-07 02:46:20 +04:00
static struct lpss_device_desc lpt_dev_desc = {
2014-05-19 15:42:07 +04:00
. clk_required = true ,
. prv_offset = 0x800 ,
. ltr_required = true ,
. clk_divider = true ,
. clk_gate = true ,
} ;
static struct lpss_device_desc lpt_i2c_dev_desc = {
2013-03-07 02:46:20 +04:00
. clk_required = true ,
2013-03-07 02:46:28 +04:00
. prv_offset = 0x800 ,
. ltr_required = true ,
2013-05-13 16:42:44 +04:00
. clk_gate = true ,
2013-03-07 02:46:28 +04:00
} ;
2013-06-17 14:25:46 +04:00
static struct lpss_device_desc lpt_uart_dev_desc = {
. clk_required = true ,
. prv_offset = 0x800 ,
. ltr_required = true ,
2014-05-19 15:42:07 +04:00
. clk_divider = true ,
2013-06-17 14:25:46 +04:00
. clk_gate = true ,
. setup = lpss_uart_setup ,
2013-03-07 02:46:28 +04:00
} ;
static struct lpss_device_desc lpt_sdio_dev_desc = {
. prv_offset = 0x1000 ,
2013-06-18 17:51:35 +04:00
. prv_size_override = 0x1018 ,
2013-03-07 02:46:28 +04:00
. ltr_required = true ,
2013-03-07 02:46:20 +04:00
} ;
2014-02-18 22:24:29 +04:00
static struct lpss_shared_clock pwm_clock = {
. name = " pwm_clk " ,
. rate = 25000000 ,
} ;
static struct lpss_device_desc byt_pwm_dev_desc = {
. clk_required = true ,
2014-05-23 17:15:09 +04:00
. save_ctx = true ,
2014-02-18 22:24:29 +04:00
. shared_clock = & pwm_clock ,
} ;
2013-05-13 16:42:44 +04:00
static struct lpss_device_desc byt_uart_dev_desc = {
. clk_required = true ,
. prv_offset = 0x800 ,
2014-05-19 15:42:07 +04:00
. clk_divider = true ,
2013-05-13 16:42:44 +04:00
. clk_gate = true ,
2014-05-23 17:15:09 +04:00
. save_ctx = true ,
2013-06-17 14:25:46 +04:00
. setup = lpss_uart_setup ,
2013-05-13 16:42:44 +04:00
} ;
static struct lpss_device_desc byt_spi_dev_desc = {
. clk_required = true ,
. prv_offset = 0x400 ,
2014-05-19 15:42:07 +04:00
. clk_divider = true ,
2013-05-13 16:42:44 +04:00
. clk_gate = true ,
2014-05-23 17:15:09 +04:00
. save_ctx = true ,
2013-05-13 16:42:44 +04:00
} ;
static struct lpss_device_desc byt_sdio_dev_desc = {
. clk_required = true ,
} ;
static struct lpss_shared_clock i2c_clock = {
. name = " i2c_clk " ,
. rate = 100000000 ,
} ;
static struct lpss_device_desc byt_i2c_dev_desc = {
. clk_required = true ,
. prv_offset = 0x800 ,
2014-05-23 17:15:09 +04:00
. save_ctx = true ,
2013-05-13 16:42:44 +04:00
. shared_clock = & i2c_clock ,
} ;
2013-03-07 02:46:20 +04:00
static const struct acpi_device_id acpi_lpss_device_ids [ ] = {
2013-05-08 12:55:49 +04:00
/* Generic LPSS devices */
{ " INTL9C60 " , ( unsigned long ) & lpss_dma_desc } ,
2013-03-07 02:46:20 +04:00
/* Lynxpoint LPSS devices */
{ " INT33C0 " , ( unsigned long ) & lpt_dev_desc } ,
{ " INT33C1 " , ( unsigned long ) & lpt_dev_desc } ,
2014-05-19 15:42:07 +04:00
{ " INT33C2 " , ( unsigned long ) & lpt_i2c_dev_desc } ,
{ " INT33C3 " , ( unsigned long ) & lpt_i2c_dev_desc } ,
2013-06-17 14:25:46 +04:00
{ " INT33C4 " , ( unsigned long ) & lpt_uart_dev_desc } ,
{ " INT33C5 " , ( unsigned long ) & lpt_uart_dev_desc } ,
2013-03-07 02:46:28 +04:00
{ " INT33C6 " , ( unsigned long ) & lpt_sdio_dev_desc } ,
2013-03-07 02:46:20 +04:00
{ " INT33C7 " , } ,
2013-05-13 16:42:44 +04:00
/* BayTrail LPSS devices */
2014-02-18 22:24:29 +04:00
{ " 80860F09 " , ( unsigned long ) & byt_pwm_dev_desc } ,
2013-05-13 16:42:44 +04:00
{ " 80860F0A " , ( unsigned long ) & byt_uart_dev_desc } ,
{ " 80860F0E " , ( unsigned long ) & byt_spi_dev_desc } ,
{ " 80860F14 " , ( unsigned long ) & byt_sdio_dev_desc } ,
{ " 80860F41 " , ( unsigned long ) & byt_i2c_dev_desc } ,
{ " INT33B2 " , } ,
2013-11-12 13:48:19 +04:00
{ " INT3430 " , ( unsigned long ) & lpt_dev_desc } ,
{ " INT3431 " , ( unsigned long ) & lpt_dev_desc } ,
2014-05-19 15:42:07 +04:00
{ " INT3432 " , ( unsigned long ) & lpt_i2c_dev_desc } ,
{ " INT3433 " , ( unsigned long ) & lpt_i2c_dev_desc } ,
2013-11-12 13:48:19 +04:00
{ " INT3434 " , ( unsigned long ) & lpt_uart_dev_desc } ,
{ " INT3435 " , ( unsigned long ) & lpt_uart_dev_desc } ,
{ " INT3436 " , ( unsigned long ) & lpt_sdio_dev_desc } ,
{ " INT3437 " , } ,
2013-03-07 02:46:20 +04:00
{ }
} ;
static int is_memory ( struct acpi_resource * res , void * not_used )
{
struct resource r ;
return ! acpi_dev_resource_memory ( res , & r ) ;
}
/* LPSS main clock device. */
static struct platform_device * lpss_clk_dev ;
static inline void lpt_register_clock_device ( void )
{
lpss_clk_dev = platform_device_register_simple ( " clk-lpt " , - 1 , NULL , 0 ) ;
}
static int register_device_clock ( struct acpi_device * adev ,
struct lpss_private_data * pdata )
{
const struct lpss_device_desc * dev_desc = pdata - > dev_desc ;
2013-05-13 16:42:44 +04:00
struct lpss_shared_clock * shared_clock = dev_desc - > shared_clock ;
2014-05-19 15:42:07 +04:00
const char * devname = dev_name ( & adev - > dev ) ;
2013-05-13 16:42:44 +04:00
struct clk * clk = ERR_PTR ( - ENODEV ) ;
2013-05-08 12:55:49 +04:00
struct lpss_clk_data * clk_data ;
2014-05-19 15:42:07 +04:00
const char * parent , * clk_name ;
void __iomem * prv_base ;
2013-03-07 02:46:20 +04:00
if ( ! lpss_clk_dev )
lpt_register_clock_device ( ) ;
2013-05-08 12:55:49 +04:00
clk_data = platform_get_drvdata ( lpss_clk_dev ) ;
if ( ! clk_data )
return - ENODEV ;
if ( dev_desc - > clkdev_name ) {
clk_register_clkdev ( clk_data - > clk , dev_desc - > clkdev_name ,
2014-05-19 15:42:07 +04:00
devname ) ;
2013-05-08 12:55:49 +04:00
return 0 ;
}
if ( ! pdata - > mmio_base
2013-03-07 02:46:28 +04:00
| | pdata - > mmio_size < dev_desc - > prv_offset + LPSS_CLK_SIZE )
2013-03-07 02:46:20 +04:00
return - ENODATA ;
2013-05-13 16:42:44 +04:00
parent = clk_data - > name ;
2014-05-19 15:42:07 +04:00
prv_base = pdata - > mmio_base + dev_desc - > prv_offset ;
2013-05-13 16:42:44 +04:00
if ( shared_clock ) {
clk = shared_clock - > clk ;
if ( ! clk ) {
clk = clk_register_fixed_rate ( NULL , shared_clock - > name ,
" lpss_clk " , 0 ,
shared_clock - > rate ) ;
shared_clock - > clk = clk ;
}
parent = shared_clock - > name ;
}
if ( dev_desc - > clk_gate ) {
2014-05-19 15:42:07 +04:00
clk = clk_register_gate ( NULL , devname , parent , 0 ,
prv_base , 0 , 0 , NULL ) ;
parent = devname ;
}
if ( dev_desc - > clk_divider ) {
/* Prevent division by zero */
if ( ! readl ( prv_base ) )
writel ( LPSS_CLK_DIVIDER_DEF_MASK , prv_base ) ;
clk_name = kasprintf ( GFP_KERNEL , " %s-div " , devname ) ;
if ( ! clk_name )
return - ENOMEM ;
clk = clk_register_fractional_divider ( NULL , clk_name , parent ,
0 , prv_base ,
1 , 15 , 16 , 15 , 0 , NULL ) ;
parent = clk_name ;
clk_name = kasprintf ( GFP_KERNEL , " %s-update " , devname ) ;
if ( ! clk_name ) {
kfree ( parent ) ;
return - ENOMEM ;
}
clk = clk_register_gate ( NULL , clk_name , parent ,
CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE ,
prv_base , 31 , 0 , NULL ) ;
kfree ( parent ) ;
kfree ( clk_name ) ;
2013-05-13 16:42:44 +04:00
}
2013-03-07 02:46:20 +04:00
2013-05-13 16:42:44 +04:00
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
2013-03-07 02:46:20 +04:00
2014-05-19 15:42:07 +04:00
pdata - > clk = clk ;
clk_register_clkdev ( clk , NULL , devname ) ;
2013-03-07 02:46:20 +04:00
return 0 ;
}
static int acpi_lpss_create_device ( struct acpi_device * adev ,
const struct acpi_device_id * id )
{
struct lpss_device_desc * dev_desc ;
struct lpss_private_data * pdata ;
struct resource_list_entry * rentry ;
struct list_head resource_list ;
2014-05-25 16:38:52 +04:00
struct platform_device * pdev ;
2013-03-07 02:46:20 +04:00
int ret ;
dev_desc = ( struct lpss_device_desc * ) id - > driver_data ;
2014-05-25 16:38:52 +04:00
if ( ! dev_desc ) {
pdev = acpi_create_platform_device ( adev ) ;
return IS_ERR_OR_NULL ( pdev ) ? PTR_ERR ( pdev ) : 1 ;
}
2013-03-07 02:46:20 +04:00
pdata = kzalloc ( sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
INIT_LIST_HEAD ( & resource_list ) ;
ret = acpi_dev_get_resources ( adev , & resource_list , is_memory , NULL ) ;
if ( ret < 0 )
goto err_out ;
list_for_each_entry ( rentry , & resource_list , node )
if ( resource_type ( & rentry - > res ) = = IORESOURCE_MEM ) {
2013-06-18 17:51:35 +04:00
if ( dev_desc - > prv_size_override )
pdata - > mmio_size = dev_desc - > prv_size_override ;
else
pdata - > mmio_size = resource_size ( & rentry - > res ) ;
2013-03-07 02:46:20 +04:00
pdata - > mmio_base = ioremap ( rentry - > res . start ,
pdata - > mmio_size ) ;
break ;
}
acpi_dev_free_resource_list ( & resource_list ) ;
2013-09-02 14:30:25 +04:00
pdata - > dev_desc = dev_desc ;
2013-03-07 02:46:20 +04:00
if ( dev_desc - > clk_required ) {
ret = register_device_clock ( adev , pdata ) ;
if ( ret ) {
2013-06-19 02:45:34 +04:00
/* Skip the device, but continue the namespace scan. */
ret = 0 ;
goto err_out ;
2013-03-07 02:46:20 +04:00
}
}
2013-06-19 02:45:34 +04:00
/*
* This works around a known issue in ACPI tables where LPSS devices
* have _PS0 and _PS3 without _PSC ( and no power resources ) , so
* acpi_bus_init_power ( ) will assume that the BIOS has put them into D0 .
*/
ret = acpi_device_fix_up_power ( adev ) ;
if ( ret ) {
/* Skip the device, but continue the namespace scan. */
ret = 0 ;
goto err_out ;
}
2013-06-17 14:25:46 +04:00
if ( dev_desc - > setup )
dev_desc - > setup ( pdata ) ;
2013-03-07 02:46:20 +04:00
adev - > driver_data = pdata ;
2014-05-25 16:38:52 +04:00
pdev = acpi_create_platform_device ( adev ) ;
if ( ! IS_ERR_OR_NULL ( pdev ) ) {
device_enable_async_suspend ( & pdev - > dev ) ;
return 1 ;
}
2013-03-07 02:46:20 +04:00
2014-05-25 16:38:52 +04:00
ret = PTR_ERR ( pdev ) ;
2013-03-07 02:46:20 +04:00
adev - > driver_data = NULL ;
err_out :
kfree ( pdata ) ;
return ret ;
}
2014-02-11 03:35:53 +04:00
static u32 __lpss_reg_read ( struct lpss_private_data * pdata , unsigned int reg )
{
return readl ( pdata - > mmio_base + pdata - > dev_desc - > prv_offset + reg ) ;
}
static void __lpss_reg_write ( u32 val , struct lpss_private_data * pdata ,
unsigned int reg )
{
writel ( val , pdata - > mmio_base + pdata - > dev_desc - > prv_offset + reg ) ;
}
2013-03-07 02:46:28 +04:00
static int lpss_reg_read ( struct device * dev , unsigned int reg , u32 * val )
{
struct acpi_device * adev ;
struct lpss_private_data * pdata ;
unsigned long flags ;
int ret ;
ret = acpi_bus_get_device ( ACPI_HANDLE ( dev ) , & adev ) ;
if ( WARN_ON ( ret ) )
return ret ;
spin_lock_irqsave ( & dev - > power . lock , flags ) ;
if ( pm_runtime_suspended ( dev ) ) {
ret = - EAGAIN ;
goto out ;
}
pdata = acpi_driver_data ( adev ) ;
if ( WARN_ON ( ! pdata | | ! pdata - > mmio_base ) ) {
ret = - ENODEV ;
goto out ;
}
2014-02-11 03:35:53 +04:00
* val = __lpss_reg_read ( pdata , reg ) ;
2013-03-07 02:46:28 +04:00
out :
spin_unlock_irqrestore ( & dev - > power . lock , flags ) ;
return ret ;
}
static ssize_t lpss_ltr_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
u32 ltr_value = 0 ;
unsigned int reg ;
int ret ;
reg = strcmp ( attr - > attr . name , " auto_ltr " ) ? LPSS_SW_LTR : LPSS_AUTO_LTR ;
ret = lpss_reg_read ( dev , reg , & ltr_value ) ;
if ( ret )
return ret ;
return snprintf ( buf , PAGE_SIZE , " %08x \n " , ltr_value ) ;
}
static ssize_t lpss_ltr_mode_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
u32 ltr_mode = 0 ;
char * outstr ;
int ret ;
ret = lpss_reg_read ( dev , LPSS_GENERAL , & ltr_mode ) ;
if ( ret )
return ret ;
outstr = ( ltr_mode & LPSS_GENERAL_LTR_MODE_SW ) ? " sw " : " auto " ;
return sprintf ( buf , " %s \n " , outstr ) ;
}
static DEVICE_ATTR ( auto_ltr , S_IRUSR , lpss_ltr_show , NULL ) ;
static DEVICE_ATTR ( sw_ltr , S_IRUSR , lpss_ltr_show , NULL ) ;
static DEVICE_ATTR ( ltr_mode , S_IRUSR , lpss_ltr_mode_show , NULL ) ;
static struct attribute * lpss_attrs [ ] = {
& dev_attr_auto_ltr . attr ,
& dev_attr_sw_ltr . attr ,
& dev_attr_ltr_mode . attr ,
NULL ,
} ;
static struct attribute_group lpss_attr_group = {
. attrs = lpss_attrs ,
. name = " lpss_ltr " ,
} ;
2014-02-11 03:35:53 +04:00
static void acpi_lpss_set_ltr ( struct device * dev , s32 val )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
u32 ltr_mode , ltr_val ;
ltr_mode = __lpss_reg_read ( pdata , LPSS_GENERAL ) ;
if ( val < 0 ) {
if ( ltr_mode & LPSS_GENERAL_LTR_MODE_SW ) {
ltr_mode & = ~ LPSS_GENERAL_LTR_MODE_SW ;
__lpss_reg_write ( ltr_mode , pdata , LPSS_GENERAL ) ;
}
return ;
}
ltr_val = __lpss_reg_read ( pdata , LPSS_SW_LTR ) & ~ LPSS_LTR_SNOOP_MASK ;
if ( val > = LPSS_LTR_SNOOP_LAT_CUTOFF ) {
ltr_val | = LPSS_LTR_SNOOP_LAT_32US ;
val = LPSS_LTR_MAX_VAL ;
} else if ( val > LPSS_LTR_MAX_VAL ) {
ltr_val | = LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ ;
val > > = LPSS_LTR_SNOOP_LAT_SHIFT ;
} else {
ltr_val | = LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ ;
}
ltr_val | = val ;
__lpss_reg_write ( ltr_val , pdata , LPSS_SW_LTR ) ;
if ( ! ( ltr_mode & LPSS_GENERAL_LTR_MODE_SW ) ) {
ltr_mode | = LPSS_GENERAL_LTR_MODE_SW ;
__lpss_reg_write ( ltr_mode , pdata , LPSS_GENERAL ) ;
}
}
2014-05-23 17:15:09 +04:00
# ifdef CONFIG_PM
/**
* acpi_lpss_save_ctx ( ) - Save the private registers of LPSS device
* @ dev : 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 )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
unsigned int i ;
for ( i = 0 ; i < LPSS_PRV_REG_COUNT ; i + + ) {
unsigned long offset = i * sizeof ( u32 ) ;
pdata - > prv_reg_ctx [ i ] = __lpss_reg_read ( pdata , offset ) ;
dev_dbg ( dev , " saving 0x%08x from LPSS reg at offset 0x%02lx \n " ,
pdata - > prv_reg_ctx [ i ] , offset ) ;
}
}
/**
* acpi_lpss_restore_ctx ( ) - Restore the private registers of LPSS device
* @ dev : LPSS device
*
* Restores the registers that were previously stored with acpi_lpss_save_ctx ( ) .
*/
static void acpi_lpss_restore_ctx ( struct device * dev )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
unsigned int i ;
/*
* The following delay is needed or the subsequent write operations may
* fail . The LPSS devices are actually PCI devices and the PCI spec
* expects 10 ms delay before the device can be accessed after D3 to D0
* transition .
*/
msleep ( 10 ) ;
for ( i = 0 ; i < LPSS_PRV_REG_COUNT ; i + + ) {
unsigned long offset = i * sizeof ( u32 ) ;
__lpss_reg_write ( pdata - > prv_reg_ctx [ i ] , pdata , offset ) ;
dev_dbg ( dev , " restoring 0x%08x to LPSS reg at offset 0x%02lx \n " ,
pdata - > prv_reg_ctx [ i ] , offset ) ;
}
}
# ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late ( struct device * dev )
{
int ret = pm_generic_suspend_late ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_save_ctx ( dev ) ;
return acpi_dev_suspend_late ( dev ) ;
}
static int acpi_lpss_restore_early ( struct device * dev )
{
int ret = acpi_dev_resume_early ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_restore_ctx ( dev ) ;
return pm_generic_resume_early ( dev ) ;
}
# endif /* CONFIG_PM_SLEEP */
# ifdef CONFIG_PM_RUNTIME
static int acpi_lpss_runtime_suspend ( struct device * dev )
{
int ret = pm_generic_runtime_suspend ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_save_ctx ( dev ) ;
return acpi_dev_runtime_suspend ( dev ) ;
}
static int acpi_lpss_runtime_resume ( struct device * dev )
{
int ret = acpi_dev_runtime_resume ( dev ) ;
if ( ret )
return ret ;
acpi_lpss_restore_ctx ( dev ) ;
return pm_generic_runtime_resume ( dev ) ;
}
# endif /* CONFIG_PM_RUNTIME */
# endif /* CONFIG_PM */
static struct dev_pm_domain acpi_lpss_pm_domain = {
. ops = {
# ifdef CONFIG_PM_SLEEP
. suspend_late = acpi_lpss_suspend_late ,
. restore_early = acpi_lpss_restore_early ,
. prepare = acpi_subsys_prepare ,
. complete = acpi_subsys_complete ,
. suspend = acpi_subsys_suspend ,
. resume_early = acpi_subsys_resume_early ,
. freeze = acpi_subsys_freeze ,
. poweroff = acpi_subsys_suspend ,
. poweroff_late = acpi_subsys_suspend_late ,
# endif
# ifdef CONFIG_PM_RUNTIME
. runtime_suspend = acpi_lpss_runtime_suspend ,
. runtime_resume = acpi_lpss_runtime_resume ,
# endif
} ,
} ;
2013-03-07 02:46:28 +04:00
static int acpi_lpss_platform_notify ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct platform_device * pdev = to_platform_device ( data ) ;
struct lpss_private_data * pdata ;
struct acpi_device * adev ;
const struct acpi_device_id * id ;
id = acpi_match_device ( acpi_lpss_device_ids , & pdev - > dev ) ;
if ( ! id | | ! id - > driver_data )
return 0 ;
if ( acpi_bus_get_device ( ACPI_HANDLE ( & pdev - > dev ) , & adev ) )
return 0 ;
pdata = acpi_driver_data ( adev ) ;
2014-05-23 17:15:09 +04:00
if ( ! pdata | | ! pdata - > mmio_base )
2013-03-07 02:46:28 +04:00
return 0 ;
if ( pdata - > mmio_size < pdata - > dev_desc - > prv_offset + LPSS_LTR_SIZE ) {
dev_err ( & pdev - > dev , " MMIO size insufficient to access LTR \n " ) ;
return 0 ;
}
2014-05-23 17:15:09 +04:00
switch ( action ) {
case BUS_NOTIFY_BOUND_DRIVER :
if ( pdata - > dev_desc - > save_ctx )
pdev - > dev . pm_domain = & acpi_lpss_pm_domain ;
break ;
case BUS_NOTIFY_UNBOUND_DRIVER :
if ( pdata - > dev_desc - > save_ctx )
pdev - > dev . pm_domain = NULL ;
break ;
case BUS_NOTIFY_ADD_DEVICE :
if ( pdata - > dev_desc - > ltr_required )
return sysfs_create_group ( & pdev - > dev . kobj ,
& lpss_attr_group ) ;
case BUS_NOTIFY_DEL_DEVICE :
if ( pdata - > dev_desc - > ltr_required )
sysfs_remove_group ( & pdev - > dev . kobj , & lpss_attr_group ) ;
default :
break ;
}
2013-03-07 02:46:28 +04:00
2014-05-23 17:15:09 +04:00
return 0 ;
2013-03-07 02:46:28 +04:00
}
static struct notifier_block acpi_lpss_nb = {
. notifier_call = acpi_lpss_platform_notify ,
} ;
2014-02-11 03:35:53 +04:00
static void acpi_lpss_bind ( struct device * dev )
{
struct lpss_private_data * pdata = acpi_driver_data ( ACPI_COMPANION ( dev ) ) ;
if ( ! pdata | | ! pdata - > mmio_base | | ! pdata - > dev_desc - > ltr_required )
return ;
if ( pdata - > mmio_size > = pdata - > dev_desc - > prv_offset + LPSS_LTR_SIZE )
dev - > power . set_latency_tolerance = acpi_lpss_set_ltr ;
else
dev_err ( dev , " MMIO size insufficient to access LTR \n " ) ;
}
static void acpi_lpss_unbind ( struct device * dev )
{
dev - > power . set_latency_tolerance = NULL ;
}
2013-03-07 02:46:20 +04:00
static struct acpi_scan_handler lpss_handler = {
. ids = acpi_lpss_device_ids ,
. attach = acpi_lpss_create_device ,
2014-02-11 03:35:53 +04:00
. bind = acpi_lpss_bind ,
. unbind = acpi_lpss_unbind ,
2013-03-07 02:46:20 +04:00
} ;
void __init acpi_lpss_init ( void )
{
2013-03-07 02:46:28 +04:00
if ( ! lpt_clk_init ( ) ) {
bus_register_notifier ( & platform_bus_type , & acpi_lpss_nb ) ;
2013-03-07 02:46:20 +04:00
acpi_scan_add_handler ( & lpss_handler ) ;
2013-03-07 02:46:28 +04:00
}
2013-03-07 02:46:20 +04:00
}